From c1af83d239a7c518b58bf333cf7aa54d3bc27d6b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 25 Oct 2019 21:06:08 +0300 Subject: [PATCH 0001/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 80 ++++ dbms/src/Dictionaries/SSDCacheDictionary.h | 365 ++++++++++++++++++ .../src/Dictionaries/SSDCacheDictionary.inc.h | 45 +++ 3 files changed, 490 insertions(+) create mode 100644 dbms/src/Dictionaries/SSDCacheDictionary.cpp create mode 100644 dbms/src/Dictionaries/SSDCacheDictionary.h create mode 100644 dbms/src/Dictionaries/SSDCacheDictionary.inc.h diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp new file mode 100644 index 000000000000..15a160fcd2ff --- /dev/null +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -0,0 +1,80 @@ +#include "SSDCacheDictionary.h" + +#include + +namespace DB +{ + +BlockFile::BlockFile(size_t file_id, const std::string & file_name, const Block & header, size_t buffer_size) + : id(file_id), file_name(file_name), buffer_size(buffer_size), out_file(file_name, buffer_size), in_file(file_name), header(header), buffer(header.cloneEmptyColumns()) +{ +} + +void BlockFile::appendBlock(const Block & block) +{ + size_t bytes = 0; + const auto new_columns = block.getColumns(); + if (new_columns.size() != buffer.size()) + { + throw Exception("Wrong size of block in BlockFile::appendBlock(). It's a bug.", ErrorCodes::TYPE_MISMATCH); + } + + const auto id_column = typeid_cast(new_columns.front().get()); + if (!id_column) + throw Exception{"id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; + + size_t start_size = buffer.front()->size(); + for (size_t i = 0; i < header.columns(); ++i) + { + buffer[i]->insertRangeFrom(*new_columns[i], 0, new_columns[i]->size()); + bytes += buffer[i]->byteSize(); + } + + const auto & ids = id_column->getData(); + for (size_t i = 0; i < new_columns.size(); ++i) + { + key_to_file_offset[ids[i]] = start_size + i; + } + + if (bytes >= buffer_size) + { + flush(); + } +} + +void BlockFile::flush() +{ + const auto id_column = typeid_cast(buffer.front().get()); + if (!id_column) + throw Exception{"id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; + const auto & ids = id_column->getData(); + + key_to_file_offset[ids[0]] = out_file.getPositionInFile() + (1ULL << FILE_OFFSET_SIZE); + size_t prev_size = 0; + for (size_t row = 0; row < buffer.front()->size(); ++row) + { + key_to_file_offset[ids[row]] = key_to_file_offset[ids[row ? row - 1 : 0]] + prev_size; + prev_size = 0; + for (size_t col = 0; col < header.columns(); ++col) + { + const auto & column = buffer[col]; + const auto & type = header.getByPosition(col).type; + type->serializeBinary(*column, row, out_file); + if (type->getTypeId() != TypeIndex::String) { + prev_size += column->sizeOfValueIfFixed(); + } else { + prev_size += column->getDataAt(row).size + sizeof(UInt64); + } + } + } + + if (out_file.hasPendingData()) { + out_file.sync(); + } + + buffer = header.cloneEmptyColumns(); +} + + + +} diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h new file mode 100644 index 000000000000..cd913142321b --- /dev/null +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -0,0 +1,365 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DictionaryStructure.h" +#include "IDictionary.h" +#include "IDictionarySource.h" +#include +#include + + +namespace DB +{ + +constexpr size_t OFFSET_MASK = ~0xffff000000000000; +constexpr size_t FILE_ID_SIZE = 16; +constexpr size_t FILE_OFFSET_SIZE = sizeof(size_t) * 8 - FILE_ID_SIZE; + + +class SSDCacheDictionary; + +class BlockFile +{ +public: + using Offset = size_t; + using Offsets = std::vector; + + BlockFile(size_t file_id, const std::string & file_name, const Block & header, size_t buffer_size = 4 * 1024 * 1024); + + void appendBlock(const Block & block); + + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + + template + void getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const; + + // TODO:: getString + +private: + void flush(); + + size_t id; + std::string file_name; + size_t buffer_size; + + WriteBufferFromFile out_file; // 4MB + mutable ReadBufferFromFile in_file; // ssd page size TODO:: adaptive buffer (read two if there less than pagesize bytes) + + /// Block structure: Key, (Default + TTL), Attr1, Attr2, ... + Block header; + + std::unordered_map key_to_file_offset; + MutableColumns buffer; +}; + + +class BlockFilesController +{ + BlockFilesController(const std::string & path) : path(path) { + } + + void appendBlock(const Block& block) { + file->appendBlock(block); + } + + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + + template + void getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const { + file->getValue(column, ids, out, not_found); + } + + // getString(); + +private: + const std::string path; + std::unique_ptr file; +}; + + +class SSDCacheDictionary final : public IDictionary +{ +public: + SSDCacheDictionary( + const std::string & name_, + const DictionaryStructure & dict_struct_, + DictionarySourcePtr source_ptr_, + const DictionaryLifetime dict_lifetime_, + const size_t size_); + + std::string getName() const override { return name; } + + std::string getTypeName() const override { return "SSDCache"; } + + size_t getBytesAllocated() const override { return bytes_allocated + (string_arena ? string_arena->size() : 0); } // TODO: ? + + size_t getQueryCount() const override { return query_count.load(std::memory_order_relaxed); } + + double getHitRate() const override + { + return static_cast(hit_count.load(std::memory_order_acquire)) / query_count.load(std::memory_order_relaxed); + } + + size_t getElementCount() const override { return element_count.load(std::memory_order_relaxed); } + + double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / size; } // TODO: fix + + bool isCached() const override { return true; } + + std::shared_ptr clone() const override + { + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, size); + } + + const IDictionarySource * getSource() const override { return source_ptr.get(); } + + const DictionaryLifetime & getLifetime() const override { return dict_lifetime; } + + const DictionaryStructure & getStructure() const override { return dict_struct; } + + bool isInjective(const std::string & attribute_name) const override + { + return dict_struct.attributes[&getAttribute(attribute_name) - attributes.data()].injective; + } + + bool hasHierarchy() const override { return hierarchical_attribute; } + + void toParent(const PaddedPODArray & ids, PaddedPODArray & out) const override; + + void isInVectorVector( + const PaddedPODArray & child_ids, const PaddedPODArray & ancestor_ids, PaddedPODArray & out) const override; + void isInVectorConstant(const PaddedPODArray & child_ids, const Key ancestor_id, PaddedPODArray & out) const override; + void isInConstantVector(const Key child_id, const PaddedPODArray & ancestor_ids, PaddedPODArray & out) const override; + + std::exception_ptr getLastException() const override; + + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + +#define DECLARE(TYPE) \ + void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const; + +#define DECLARE(TYPE) \ + void get##TYPE( \ + const std::string & attribute_name, \ + const PaddedPODArray & ids, \ + const PaddedPODArray & def, \ + ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void + getString(const std::string & attribute_name, const PaddedPODArray & ids, const ColumnString * const def, ColumnString * const out) + const; + +#define DECLARE(TYPE) \ +void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void getString(const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const; + + void has(const PaddedPODArray & ids, PaddedPODArray & out) const override; + + BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override; + +private: + template + using ContainerType = Value[]; + template + using ContainerPtrType = std::unique_ptr>; + + struct CellMetadata final + { + using time_point_t = std::chrono::system_clock::time_point; + using time_point_rep_t = time_point_t::rep; + using time_point_urep_t = std::make_unsigned_t; + + static constexpr UInt64 EXPIRES_AT_MASK = std::numeric_limits::max(); + static constexpr UInt64 IS_DEFAULT_MASK = ~EXPIRES_AT_MASK; + + UInt64 id; + /// Stores both expiration time and `is_default` flag in the most significant bit + time_point_urep_t data; + + /// Sets expiration time, resets `is_default` flag to false + time_point_t expiresAt() const { return ext::safe_bit_cast(data & EXPIRES_AT_MASK); } + void setExpiresAt(const time_point_t & t) { data = ext::safe_bit_cast(t); } + + bool isDefault() const { return (data & IS_DEFAULT_MASK) == IS_DEFAULT_MASK; } + void setDefault() { data |= IS_DEFAULT_MASK; } + }; + + struct Attribute final + { + AttributeUnderlyingType type; + std::variant< + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Int8, + Int16, + Int32, + Int64, + Decimal32, + Decimal64, + Decimal128, + Float32, + Float64, + String> + null_values; + std::variant< + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType, + ContainerPtrType> + arrays; + }; + + void createAttributes(); + + Attribute createAttributeWithType(const AttributeUnderlyingType type, const Field & null_value); + + template + void getItemsNumberImpl( + Attribute & attribute, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; + + template + void getItemsString(Attribute & attribute, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; + + template + void update(const std::vector & requested_ids, PresentIdHandler && on_cell_updated, AbsentIdHandler && on_id_not_found) const; + + PaddedPODArray getCachedIds() const; + + bool isEmptyCell(const UInt64 idx) const; + + size_t getCellIdx(const Key id) const; + + void setDefaultAttributeValue(Attribute & attribute, const Key idx) const; + + void setAttributeValue(Attribute & attribute, const Key idx, const Field & value) const; + + Attribute & getAttribute(const std::string & attribute_name) const; + + struct FindResult + { + const size_t cell_idx; + const bool valid; + const bool outdated; + }; + + FindResult findCellIdx(const Key & id, const CellMetadata::time_point_t now) const; + + template + void isInImpl(const PaddedPODArray & child_ids, const AncestorType & ancestor_ids, PaddedPODArray & out) const; + + const std::string name; + const DictionaryStructure dict_struct; + mutable DictionarySourcePtr source_ptr; + const DictionaryLifetime dict_lifetime; + Logger * const log; + + mutable std::shared_mutex rw_lock; + + /// Actual size will be increased to match power of 2 + const size_t size; + + /// all bits to 1 mask (size - 1) (0b1000 - 1 = 0b111) + const size_t size_overlap_mask; + + /// Max tries to find cell, overlaped with mask: if size = 16 and start_cell=10: will try cells: 10,11,12,13,14,15,0,1,2,3 + static constexpr size_t max_collision_length = 10; + + const size_t zero_cell_idx{getCellIdx(0)}; + std::map attribute_index_by_name; + mutable std::vector attributes; + mutable std::vector cells; + Attribute * hierarchical_attribute = nullptr; + std::unique_ptr string_arena; + + mutable std::exception_ptr last_exception; + mutable size_t error_count = 0; + mutable std::chrono::system_clock::time_point backoff_end_time; + + mutable pcg64 rnd_engine; + + mutable size_t bytes_allocated = 0; + mutable std::atomic element_count{0}; + mutable std::atomic hit_count{0}; + mutable std::atomic query_count{0}; +}; + +} + + +#include "SSDCacheDictionary.inc.h" \ No newline at end of file diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.inc.h b/dbms/src/Dictionaries/SSDCacheDictionary.inc.h new file mode 100644 index 000000000000..73e94cb5b6d3 --- /dev/null +++ b/dbms/src/Dictionaries/SSDCacheDictionary.inc.h @@ -0,0 +1,45 @@ +#pragma once + +namespace DB { + +template +void BlockFile::getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const +{ + std::vector> offsets; + offsets.reserve(ids.size()); + + for (size_t i = 0; i < ids.size(); ++i) + { + auto it = key_to_file_offset.find(ids[i]); + if (it != std::end(key_to_file_offset)) + { + offsets.emplace_back(it->second, i); + } + else + { + not_found.push_back(i); + } + } + std::sort(std::begin(offsets), std::end(offsets)); + + Field field; + for (const auto & [offset, index] : offsets) + { + if (offset & OFFSET_MASK) + { + in_file.seek(offset && !OFFSET_MASK); + for (size_t col = 0; col < column; ++col) + { + const auto & type = header.getByPosition(column).type; + type->deserializeBinary(field, in_file); + } + } + else + { + buffer[column]->get(offset, field); + } + out[index] = DB::get(field); + } +} + +} \ No newline at end of file From 443a5ca9c13a41faef4e04b42c6f3b83bc0f2f43 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 1 Jan 2020 20:40:46 +0300 Subject: [PATCH 0002/1102] changes --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 283 ++++++++++++++++++- dbms/src/Dictionaries/SSDCacheDictionary.h | 201 +++++-------- 2 files changed, 344 insertions(+), 140 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 15a160fcd2ff..44ca8b8bbe7e 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -1,20 +1,36 @@ #include "SSDCacheDictionary.h" #include +#include +#include +#include +#include +#include +#include +#include namespace DB { -BlockFile::BlockFile(size_t file_id, const std::string & file_name, const Block & header, size_t buffer_size) - : id(file_id), file_name(file_name), buffer_size(buffer_size), out_file(file_name, buffer_size), in_file(file_name), header(header), buffer(header.cloneEmptyColumns()) +namespace ErrorCodes { + extern const int TYPE_MISMATCH; + extern const int BAD_ARGUMENTS; + extern const int UNSUPPORTED_METHOD; + extern const int LOGICAL_ERROR; + extern const int TOO_SMALL_BUFFER_SIZE; } -void BlockFile::appendBlock(const Block & block) +CachePartition::CachePartition(const std::string & file_name, const Block & header, size_t buffer_size) + : file_name(file_name), buffer_size(buffer_size), out_file(file_name, buffer_size), header(header), buffer(header.cloneEmptyColumns()) +{ +} + +void CachePartition::appendBlock(const Block & block) { size_t bytes = 0; const auto new_columns = block.getColumns(); - if (new_columns.size() != buffer.size()) + if (new_columns.size() != header.columns()) { throw Exception("Wrong size of block in BlockFile::appendBlock(). It's a bug.", ErrorCodes::TYPE_MISMATCH); } @@ -42,7 +58,7 @@ void BlockFile::appendBlock(const Block & block) } } -void BlockFile::flush() +void CachePartition::flush() { const auto id_column = typeid_cast(buffer.front().get()); if (!id_column) @@ -75,6 +91,263 @@ void BlockFile::flush() buffer = header.cloneEmptyColumns(); } +SSDCacheDictionary::SSDCacheDictionary( + const std::string & name_, + const DictionaryStructure & dict_struct_, + DictionarySourcePtr source_ptr_, + const DictionaryLifetime dict_lifetime_, + const std::string & path, + const size_t partition_max_size) + : name(name_) + , dict_struct(dict_struct_) + , source_ptr(std::move(source_ptr_)) + , dict_lifetime(dict_lifetime_) + , storage(path, partition_max_size) +{ + if (!this->source_ptr->supportsSelectiveLoad()) + throw Exception{name + ": source cannot be used with CacheDictionary", ErrorCodes::UNSUPPORTED_METHOD}; + + createAttributes(); +} + +#define DECLARE(TYPE) \ + void SSDCacheDictionary::get##TYPE( \ + const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ +\ + const auto null_value = std::get(attributes[index].null_value); \ +\ + getItemsNumberImpl( \ + attribute_name, \ + ids, \ + out, \ + [&](const size_t) { return null_value; }); \ + } + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +#define DECLARE(TYPE) \ + void SSDCacheDictionary::get##TYPE( \ + const std::string & attribute_name, \ + const PaddedPODArray & ids, \ + const PaddedPODArray & def, \ + ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ +\ + getItemsNumberImpl( \ + attribute_name, \ + ids, \ + out, \ + [&](const size_t row) { return def[row]; }); \ + } + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +#define DECLARE(TYPE) \ + void SSDCacheDictionary::get##TYPE( \ + const std::string & attribute_name, \ + const PaddedPODArray & ids, \ + const TYPE def, \ + ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ +\ + getItemsNumberImpl( \ + attribute_name, \ + ids, \ + out, \ + [&](const size_t) { return def; }); \ + } + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +template +void SSDCacheDictionary::getItemsNumberImpl( + const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const +{ + std::unordered_map> not_found_ids; + storage.getValue(attribute_name, ids, out, not_found_ids); + if (not_found_ids.empty()) + return; + + std::vector required_ids(not_found_ids.size()); + std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](auto & pair) { return pair.first; }); + + update( + required_ids, + [&](const auto id, const auto & attribute_value) + { + for (const size_t row : not_found_ids[id]) + out[row] = static_cast(attribute_value); + }, + [&](const auto id) + { + for (const size_t row : not_found_ids[id]) + out[row] = get_default(row); + }); +} + +void SSDCacheDictionary::getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const +{ + auto & attribute = getAttribute(attribute_name); + checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + + const auto null_value = StringRef{std::get(attribute.null_value)}; + + getItemsString(attribute_name, ids, out, [&](const size_t) { return null_value; }); +} + +void SSDCacheDictionary::getString( + const std::string & attribute_name, const PaddedPODArray & ids, const ColumnString * const def, ColumnString * const out) const +{ + auto & attribute = getAttribute(attribute_name); + checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + + getItemsString(attribute_name, ids, out, [&](const size_t row) { return def->getDataAt(row); }); +} + +void SSDCacheDictionary::getString( + const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const +{ + auto & attribute = getAttribute(attribute_name); + checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + + getItemsString(attribute_name, ids, out, [&](const size_t) { return StringRef{def}; }); +} + +template +void SSDCacheDictionary::getItemsString(const std::string & attribute_name, const PaddedPODArray & ids, + ColumnString * out, DefaultGetter && get_default) const +{ + UNUSED(attribute_name); + UNUSED(ids); + UNUSED(out); + UNUSED(get_default); +} + +size_t SSDCacheDictionary::getAttributeIndex(const std::string & attr_name) const +{ + auto it = attribute_index_by_name.find(attr_name); + if (it == std::end(attribute_index_by_name)) + throw Exception{"Attribute `" + name + "` does not exist.", ErrorCodes::BAD_ARGUMENTS}; + return it->second; +} + +SSDCacheDictionary::Attribute & SSDCacheDictionary::getAttribute(const std::string & attr_name) +{ + return attributes[getAttributeIndex(attr_name)]; +} + +const SSDCacheDictionary::Attribute & SSDCacheDictionary::getAttribute(const std::string & attr_name) const +{ + return attributes[getAttributeIndex(attr_name)]; +} + +template +SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value) +{ + Attribute attr{type, {}}; + attr.null_value = static_cast(null_value.get>()); + bytes_allocated += sizeof(T); + return attr; +} + +template <> +SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value) +{ + Attribute attr{type, {}}; + attr.null_value = null_value.get(); + bytes_allocated += sizeof(StringRef); + //if (!string_arena) + // string_arena = std::make_unique(); + return attr; +} + +SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithType(const AttributeUnderlyingType type, const Field & null_value) +{ + switch (type) + { +#define DISPATCH(TYPE) \ +case AttributeUnderlyingType::ut##TYPE: \ + return createAttributeWithTypeImpl(type, null_value); + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + DISPATCH(String) +#undef DISPATCH + } +} + +void SSDCacheDictionary::createAttributes() +{ + attributes.resize(dict_struct.attributes.size()); + for (size_t i = 0; i < dict_struct.attributes.size(); ++i) + { + const auto & attribute = dict_struct.attributes[i]; + + attribute_index_by_name.emplace(attribute.name, i); + attributes.push_back(createAttributeWithType(attribute.type, attribute.null_value)); + + if (attribute.hierarchical) + throw Exception{name + ": hierarchical attributes not supported for dictionary of type " + getTypeName(), + ErrorCodes::TYPE_MISMATCH}; + } +} } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index cd913142321b..04777bd2587e 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -29,33 +29,32 @@ constexpr size_t FILE_OFFSET_SIZE = sizeof(size_t) * 8 - FILE_ID_SIZE; class SSDCacheDictionary; -class BlockFile +class CachePartition { public: using Offset = size_t; using Offsets = std::vector; - BlockFile(size_t file_id, const std::string & file_name, const Block & header, size_t buffer_size = 4 * 1024 * 1024); + CachePartition(const std::string & file_name, const Block & header = {}, size_t buffer_size = 4 * 1024 * 1024); void appendBlock(const Block & block); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; - template - void getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const; + template + void getValue(const std::string & attribute_name, const PaddedPODArray & ids, + ResultArrayType & out, std::unordered_map> & not_found) const; // TODO:: getString private: void flush(); - size_t id; std::string file_name; size_t buffer_size; WriteBufferFromFile out_file; // 4MB - mutable ReadBufferFromFile in_file; // ssd page size TODO:: adaptive buffer (read two if there less than pagesize bytes) /// Block structure: Key, (Default + TTL), Attr1, Attr2, ... Block header; @@ -65,28 +64,38 @@ class BlockFile }; -class BlockFilesController +class CacheStorage { - BlockFilesController(const std::string & path) : path(path) { + CacheStorage(const std::string & path_, size_t partition_max_size_) + : path(path_) + , partition_max_size(partition_max_size_) + { + partition = std::make_unique(path); } - void appendBlock(const Block& block) { - file->appendBlock(block); + void appendBlock(const Block& block) + { + partition->appendBlock(block); } template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; - template - void getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const { - file->getValue(column, ids, out, not_found); + template + void getValue(const std::string & attribute_name, const PaddedPODArray & ids, + ResultArrayType & out, std::unordered_map> & not_found) const + { + partition->getValue(attribute_name, ids, out, not_found); } // getString(); + //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; + private: const std::string path; - std::unique_ptr file; + const size_t partition_max_size; + std::unique_ptr partition; }; @@ -98,13 +107,14 @@ class SSDCacheDictionary final : public IDictionary const DictionaryStructure & dict_struct_, DictionarySourcePtr source_ptr_, const DictionaryLifetime dict_lifetime_, - const size_t size_); + const std::string & path, + const size_t partition_max_size_); std::string getName() const override { return name; } std::string getTypeName() const override { return "SSDCache"; } - size_t getBytesAllocated() const override { return bytes_allocated + (string_arena ? string_arena->size() : 0); } // TODO: ? + size_t getBytesAllocated() const override { return 0; } // TODO: ? size_t getQueryCount() const override { return query_count.load(std::memory_order_relaxed); } @@ -115,13 +125,13 @@ class SSDCacheDictionary final : public IDictionary size_t getElementCount() const override { return element_count.load(std::memory_order_relaxed); } - double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / size; } // TODO: fix + double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / max_size; } // TODO: fix - bool isCached() const override { return true; } + bool supportUpdates() const override { return true; } std::shared_ptr clone() const override { - return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, size); + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, max_size); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -132,19 +142,14 @@ class SSDCacheDictionary final : public IDictionary bool isInjective(const std::string & attribute_name) const override { - return dict_struct.attributes[&getAttribute(attribute_name) - attributes.data()].injective; + return dict_struct.attributes[getAttributeIndex(attribute_name)].injective; } - bool hasHierarchy() const override { return hierarchical_attribute; } - - void toParent(const PaddedPODArray & ids, PaddedPODArray & out) const override; + bool hasHierarchy() const override { return false; } - void isInVectorVector( - const PaddedPODArray & child_ids, const PaddedPODArray & ancestor_ids, PaddedPODArray & out) const override; - void isInVectorConstant(const PaddedPODArray & child_ids, const Key ancestor_id, PaddedPODArray & out) const override; - void isInConstantVector(const Key child_id, const PaddedPODArray & ancestor_ids, PaddedPODArray & out) const override; + void toParent(const PaddedPODArray & /* ids */, PaddedPODArray & /* out */ ) const override {} - std::exception_ptr getLastException() const override; + std::exception_ptr getLastException() const override { return last_exception; } template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -196,7 +201,7 @@ class SSDCacheDictionary final : public IDictionary const; #define DECLARE(TYPE) \ -void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, ResultArrayType & out) const; + void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, const TYPE def, ResultArrayType & out) const; DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -220,139 +225,65 @@ void get##TYPE(const std::string & attribute_name, const PaddedPODArray & i BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override; private: - template - using ContainerType = Value[]; - template - using ContainerPtrType = std::unique_ptr>; - - struct CellMetadata final - { - using time_point_t = std::chrono::system_clock::time_point; - using time_point_rep_t = time_point_t::rep; - using time_point_urep_t = std::make_unsigned_t; - - static constexpr UInt64 EXPIRES_AT_MASK = std::numeric_limits::max(); - static constexpr UInt64 IS_DEFAULT_MASK = ~EXPIRES_AT_MASK; - - UInt64 id; - /// Stores both expiration time and `is_default` flag in the most significant bit - time_point_urep_t data; - - /// Sets expiration time, resets `is_default` flag to false - time_point_t expiresAt() const { return ext::safe_bit_cast(data & EXPIRES_AT_MASK); } - void setExpiresAt(const time_point_t & t) { data = ext::safe_bit_cast(t); } - - bool isDefault() const { return (data & IS_DEFAULT_MASK) == IS_DEFAULT_MASK; } - void setDefault() { data |= IS_DEFAULT_MASK; } - }; - - struct Attribute final + struct Attribute { AttributeUnderlyingType type; std::variant< - UInt8, - UInt16, - UInt32, - UInt64, - UInt128, - Int8, - Int16, - Int32, - Int64, - Decimal32, - Decimal64, - Decimal128, - Float32, - Float64, - String> - null_values; - std::variant< - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType, - ContainerPtrType> - arrays; + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Int8, + Int16, + Int32, + Int64, + Decimal32, + Decimal64, + Decimal128, + Float32, + Float64, + String> null_value; }; + using Attributes = std::vector; - void createAttributes(); + size_t getAttributeIndex(const std::string & attr_name) const; + Attribute & getAttribute(const std::string & attr_name); + const Attribute & getAttribute(const std::string & attr_name) const; + template + Attribute createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value); Attribute createAttributeWithType(const AttributeUnderlyingType type, const Field & null_value); + void createAttributes(); template void getItemsNumberImpl( - Attribute & attribute, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; - + const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; template - void getItemsString(Attribute & attribute, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; + void getItemsString(const std::string & attribute_name, const PaddedPODArray & ids, + ColumnString * out, DefaultGetter && get_default) const; template - void update(const std::vector & requested_ids, PresentIdHandler && on_cell_updated, AbsentIdHandler && on_id_not_found) const; - - PaddedPODArray getCachedIds() const; - - bool isEmptyCell(const UInt64 idx) const; - - size_t getCellIdx(const Key id) const; - - void setDefaultAttributeValue(Attribute & attribute, const Key idx) const; - - void setAttributeValue(Attribute & attribute, const Key idx, const Field & value) const; - - Attribute & getAttribute(const std::string & attribute_name) const; - - struct FindResult - { - const size_t cell_idx; - const bool valid; - const bool outdated; - }; - - FindResult findCellIdx(const Key & id, const CellMetadata::time_point_t now) const; - - template - void isInImpl(const PaddedPODArray & child_ids, const AncestorType & ancestor_ids, PaddedPODArray & out) const; - + void update(const std::vector & requested_ids, PresentIdHandler && on_updated, + AbsentIdHandler && on_id_not_found) const; + const std::string name; const DictionaryStructure dict_struct; mutable DictionarySourcePtr source_ptr; const DictionaryLifetime dict_lifetime; + + CacheStorage storage; Logger * const log; mutable std::shared_mutex rw_lock; - /// Actual size will be increased to match power of 2 - const size_t size; - - /// all bits to 1 mask (size - 1) (0b1000 - 1 = 0b111) - const size_t size_overlap_mask; - - /// Max tries to find cell, overlaped with mask: if size = 16 and start_cell=10: will try cells: 10,11,12,13,14,15,0,1,2,3 - static constexpr size_t max_collision_length = 10; - - const size_t zero_cell_idx{getCellIdx(0)}; std::map attribute_index_by_name; - mutable std::vector attributes; - mutable std::vector cells; - Attribute * hierarchical_attribute = nullptr; - std::unique_ptr string_arena; + Attributes attributes; mutable std::exception_ptr last_exception; mutable size_t error_count = 0; mutable std::chrono::system_clock::time_point backoff_end_time; - mutable pcg64 rnd_engine; - mutable size_t bytes_allocated = 0; mutable std::atomic element_count{0}; mutable std::atomic hit_count{0}; From 3bbb73e37caeb9764d5761dabac31f183aac3e1a Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 2 Jan 2020 22:33:19 +0300 Subject: [PATCH 0003/1102] update --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 160 ++++++++++++++++++- dbms/src/Dictionaries/SSDCacheDictionary.h | 105 +++++++----- 2 files changed, 222 insertions(+), 43 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 44ca8b8bbe7e..61f3933f178b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -1,14 +1,34 @@ #include "SSDCacheDictionary.h" #include -#include #include +#include +#include #include #include #include #include #include +namespace ProfileEvents +{ + extern const Event DictCacheKeysRequested; + extern const Event DictCacheKeysRequestedMiss; + extern const Event DictCacheKeysRequestedFound; + extern const Event DictCacheKeysExpired; + extern const Event DictCacheKeysNotFound; + extern const Event DictCacheKeysHit; + extern const Event DictCacheRequestTimeNs; + extern const Event DictCacheRequests; + extern const Event DictCacheLockWriteNs; + extern const Event DictCacheLockReadNs; +} + +namespace CurrentMetrics +{ + extern const Metric DictCacheRequests; +} + namespace DB { @@ -21,8 +41,8 @@ namespace ErrorCodes extern const int TOO_SMALL_BUFFER_SIZE; } -CachePartition::CachePartition(const std::string & file_name, const Block & header, size_t buffer_size) - : file_name(file_name), buffer_size(buffer_size), out_file(file_name, buffer_size), header(header), buffer(header.cloneEmptyColumns()) +CachePartition::CachePartition(CacheStorage & storage_, const size_t file_id_, const size_t max_size_, const size_t buffer_size_) + : storage(storage_), file_id(file_id_), max_size(max_size_), buffer_size(buffer_size_) { } @@ -91,6 +111,124 @@ void CachePartition::flush() buffer = header.cloneEmptyColumns(); } +template +std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, + PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found) +{ + CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequested, requested_ids.size()); + + std::unordered_map remaining_ids{requested_ids.size()}; + for (const auto id : requested_ids) + remaining_ids.insert({id, 0}); + + const auto now = std::chrono::system_clock::now(); + + const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; + + if (now > backoff_end_time) + { + try + { + if (update_error_count) + { + /// Recover after error: we have to clone the source here because + /// it could keep connections which should be reset after error. + source_ptr = source_ptr->clone(); + } + + Stopwatch watch; + auto stream = source_ptr->loadIds(requested_ids); + stream->readPrefix(); + + while (const auto block = stream->read()) + { + const auto id_column = typeid_cast(block.safeGetByPosition(0).column.get()); + if (!id_column) + throw Exception{"Id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; + + const auto & ids = id_column->getData(); + + /// cache column pointers + const auto column_ptrs = ext::map( + ext::range(0, dictionary.getAttributes().size()), + [&block](size_t i) { return block.safeGetByPosition(i + 1).column.get(); }); + + for (const auto i : ext::range(0, ids.size())) + { + const auto id = ids[i]; + + on_updated(id, i, column_ptrs); + /// mark corresponding id as found + remaining_ids[id] = 1; + } + + /// TODO: Add TTL to block + partitions[0]->appendBlock(block); + } + + stream->readSuffix(); + + update_error_count = 0; + last_update_exception = std::exception_ptr{}; + backoff_end_time = std::chrono::system_clock::time_point{}; + + ProfileEvents::increment(ProfileEvents::DictCacheRequestTimeNs, watch.elapsed()); + } + catch (...) + { + ++update_error_count; + last_update_exception = std::current_exception(); + backoff_end_time = now + std::chrono::seconds(calculateDurationWithBackoff(rnd_engine, update_error_count)); + + tryLogException(last_update_exception, log, "Could not update cache dictionary '" + dictionary.getName() + + "', next update is scheduled at " + ext::to_string(backoff_end_time)); + } + } + + size_t not_found_num = 0, found_num = 0; + + /// Check which ids have not been found and require setting null_value + auto mutable_columns = header.cloneEmptyColumns(); + for (const auto & id_found_pair : remaining_ids) + { + if (id_found_pair.second) + { + ++found_num; + continue; + } + ++not_found_num; + + const auto id = id_found_pair.first; + + if (update_error_count) + { + /// TODO: юзать старые значения. + + /// We don't have expired data for that `id` so all we can do is to rethrow `last_exception`. + std::rethrow_exception(last_update_exception); + } + + /// TODO: Add TTL + + /// Set null_value for each attribute + const auto & attributes = dictionary.getAttributes(); + for (size_t i = 0; i < attributes.size(); ++i) + { + const auto & attribute = attributes[i]; + mutable_columns[i].insert(attribute.null_value); + } + + /// inform caller that the cell has not been found + on_id_not_found(id); + } + partitions[0]->appendBlock(header.cloneWithColumns(std::move(mutable_columns))); + + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); + ProfileEvents::increment(ProfileEvents::DictCacheRequests); +} + SSDCacheDictionary::SSDCacheDictionary( const std::string & name_, const DictionaryStructure & dict_struct_, @@ -102,7 +240,7 @@ SSDCacheDictionary::SSDCacheDictionary( , dict_struct(dict_struct_) , source_ptr(std::move(source_ptr_)) , dict_lifetime(dict_lifetime_) - , storage(path, partition_max_size) + , storage(*this, path, 1, partition_max_size) { if (!this->source_ptr->supportsSelectiveLoad()) throw Exception{name + ": source cannot be used with CacheDictionary", ErrorCodes::UNSUPPORTED_METHOD}; @@ -209,6 +347,8 @@ template void SSDCacheDictionary::getItemsNumberImpl( const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const { + const auto attribute_index = getAttributeIndex(attribute_index); + std::unordered_map> not_found_ids; storage.getValue(attribute_name, ids, out, not_found_ids); if (not_found_ids.empty()) @@ -217,12 +357,13 @@ void SSDCacheDictionary::getItemsNumberImpl( std::vector required_ids(not_found_ids.size()); std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](auto & pair) { return pair.first; }); - update( + storage.update( + source_ptr, required_ids, - [&](const auto id, const auto & attribute_value) + [&](const auto id, const auto row, const auto & attributes) { for (const size_t row : not_found_ids[id]) - out[row] = static_cast(attribute_value); + out[row] = static_cast(attributes[attribute_index][row]); }, [&](const auto id) { @@ -287,6 +428,11 @@ const SSDCacheDictionary::Attribute & SSDCacheDictionary::getAttribute(const std return attributes[getAttributeIndex(attr_name)]; } +const SSDCacheDictionary::Attributes & SSDCacheDictionary::getAttributes() const +{ + return attributes; +} + template SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value) { diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 04777bd2587e..6ece329ba874 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -22,12 +22,8 @@ namespace DB { -constexpr size_t OFFSET_MASK = ~0xffff000000000000; -constexpr size_t FILE_ID_SIZE = 16; -constexpr size_t FILE_OFFSET_SIZE = sizeof(size_t) * 8 - FILE_ID_SIZE; - - class SSDCacheDictionary; +class CacheStorage; class CachePartition { @@ -35,7 +31,7 @@ class CachePartition using Offset = size_t; using Offsets = std::vector; - CachePartition(const std::string & file_name, const Block & header = {}, size_t buffer_size = 4 * 1024 * 1024); + CachePartition(CacheStorage & storage, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); void appendBlock(const Block & block); @@ -48,34 +44,46 @@ class CachePartition // TODO:: getString + /// 0 -- not found + /// 1 -- good + /// 2 -- expired + void has(const PaddedPODArray & ids, ResultArrayType & out) const; + private: void flush(); - std::string file_name; - size_t buffer_size; - WriteBufferFromFile out_file; // 4MB + CacheStorage & storage; - /// Block structure: Key, (Default + TTL), Attr1, Attr2, ... - Block header; + size_t file_id; + size_t max_size; + size_t buffer_size; + + //mutable std::shared_mutex rw_lock; + int index_fd; + int data_fd; std::unordered_map key_to_file_offset; MutableColumns buffer; + + mutable std::atomic element_count{0}; }; +using CachePartitionPtr = std::unique_ptr; + class CacheStorage { - CacheStorage(const std::string & path_, size_t partition_max_size_) - : path(path_) - , partition_max_size(partition_max_size_) - { - partition = std::make_unique(path); - } + using Key = IDictionary::Key; - void appendBlock(const Block& block) + CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) + : dictionary(dictionary_) + , path(path_) + , partition_max_size(partition_max_size_) + , log(&Poco::Logger::get("CacheStorage")) { - partition->appendBlock(block); + for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) + partitions.emplace_back(std::make_unique(partition_id, partition_max_size)); } template @@ -85,17 +93,42 @@ class CacheStorage void getValue(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - partition->getValue(attribute_name, ids, out, not_found); + partitions[0]->getValue(attribute_name, ids, out, not_found); } // getString(); + template + std::exception_ptr update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, + PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found); + //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; + std::exception_ptr getLastException() const { return last_update_exception; } + private: + SSDCacheDictionary & dictionary; + + /// Block structure: Key, (Default + TTL), Attr1, Attr2, ... + const Block header; const std::string path; const size_t partition_max_size; - std::unique_ptr partition; + std::vector partitions; + + Logger * const log; + + mutable pcg64 rnd_engine; + + mutable std::shared_mutex rw_lock; + + mutable std::exception_ptr last_update_exception; + mutable size_t update_error_count = 0; + mutable std::chrono::system_clock::time_point backoff_end_time; + + // stats + mutable std::atomic element_count{0}; + mutable std::atomic hit_count{0}; + mutable std::atomic query_count{0}; }; @@ -125,13 +158,13 @@ class SSDCacheDictionary final : public IDictionary size_t getElementCount() const override { return element_count.load(std::memory_order_relaxed); } - double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / max_size; } // TODO: fix + double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / partition_max_size; } // TODO: fix bool supportUpdates() const override { return true; } std::shared_ptr clone() const override { - return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, max_size); + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, partition_max_size); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -149,7 +182,7 @@ class SSDCacheDictionary final : public IDictionary void toParent(const PaddedPODArray & /* ids */, PaddedPODArray & /* out */ ) const override {} - std::exception_ptr getLastException() const override { return last_exception; } + std::exception_ptr getLastException() const override { return storage.getLastException(); } template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -222,9 +255,16 @@ class SSDCacheDictionary final : public IDictionary void has(const PaddedPODArray & ids, PaddedPODArray & out) const override; - BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override; + BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override // TODO + { + UNUSED(column_names); + UNUSED(max_block_size); + return nullptr; + } private: + friend class CacheStorage; + struct Attribute { AttributeUnderlyingType type; @@ -250,6 +290,7 @@ class SSDCacheDictionary final : public IDictionary size_t getAttributeIndex(const std::string & attr_name) const; Attribute & getAttribute(const std::string & attr_name); const Attribute & getAttribute(const std::string & attr_name) const; + const Attributes & getAttributes() const; template Attribute createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value); @@ -262,28 +303,20 @@ class SSDCacheDictionary final : public IDictionary template void getItemsString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; - - template - void update(const std::vector & requested_ids, PresentIdHandler && on_updated, - AbsentIdHandler && on_id_not_found) const; const std::string name; const DictionaryStructure dict_struct; mutable DictionarySourcePtr source_ptr; const DictionaryLifetime dict_lifetime; - CacheStorage storage; + const std::string path; + const size_t partition_max_size; + mutable CacheStorage storage; Logger * const log; - mutable std::shared_mutex rw_lock; - std::map attribute_index_by_name; Attributes attributes; - mutable std::exception_ptr last_exception; - mutable size_t error_count = 0; - mutable std::chrono::system_clock::time_point backoff_end_time; - mutable size_t bytes_allocated = 0; mutable std::atomic element_count{0}; mutable std::atomic hit_count{0}; From b55d8dd348c26406965dd318c47e37ec705ab847 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 3 Jan 2020 22:52:07 +0300 Subject: [PATCH 0004/1102] update, changed block -> attrs --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 259 ++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 78 ++++-- .../src/Dictionaries/SSDCacheDictionary.inc.h | 4 +- 3 files changed, 271 insertions(+), 70 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 61f3933f178b..4d0cbc3f0757 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -1,5 +1,6 @@ #include "SSDCacheDictionary.h" +#include #include #include #include @@ -41,35 +42,66 @@ namespace ErrorCodes extern const int TOO_SMALL_BUFFER_SIZE; } -CachePartition::CachePartition(CacheStorage & storage_, const size_t file_id_, const size_t max_size_, const size_t buffer_size_) - : storage(storage_), file_id(file_id_), max_size(max_size_), buffer_size(buffer_size_) +namespace { + constexpr size_t INMEMORY = (1ULL << 63ULL); + const std::string BIN_FILE_EXT = ".bin"; + const std::string IND_FILE_EXT = ".idx"; } -void CachePartition::appendBlock(const Block & block) +CachePartition::CachePartition(const std::vector & structure, const std::string & dir_path, + const size_t file_id_, const size_t max_size_, const size_t buffer_size_) + : file_id(file_id_), max_size(max_size_), buffer_size(buffer_size_), path(dir_path + "/" + std::to_string(file_id)) { - size_t bytes = 0; - const auto new_columns = block.getColumns(); - if (new_columns.size() != header.columns()) + for (const auto & type : structure) { - throw Exception("Wrong size of block in BlockFile::appendBlock(). It's a bug.", ErrorCodes::TYPE_MISMATCH); + switch (type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + buffer.emplace_back(type, std::vector()); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } } +} - const auto id_column = typeid_cast(new_columns.front().get()); - if (!id_column) - throw Exception{"id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; +void CachePartition::appendBlock(const Attributes & new_columns) +{ + if (new_columns.size() != buffer.size()) + throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; + + const auto & id_column = std::get>(new_columns.front().values); size_t start_size = buffer.front()->size(); - for (size_t i = 0; i < header.columns(); ++i) + for (size_t i = 0; i < buffer.size(); ++i) { - buffer[i]->insertRangeFrom(*new_columns[i], 0, new_columns[i]->size()); + appendValuesToBufferAttribute(buffer[i], new_columns[i]); bytes += buffer[i]->byteSize(); } - const auto & ids = id_column->getData(); - for (size_t i = 0; i < new_columns.size(); ++i) + for (size_t i = 0; i < id_column.size(); ++i) { - key_to_file_offset[ids[i]] = start_size + i; + key_to_file_offset[id_column[i]] = (start_size + i) | INMEMORY; } if (bytes >= buffer_size) @@ -78,14 +110,57 @@ void CachePartition::appendBlock(const Block & block) } } +void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribute & from) +{ + switch (to.type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + auto &to_values = std::get>(to.values); \ + auto &from_values = std::get>(from.values); \ + size_t prev_size = to_values.size(); \ + to_values.resize(to_values.size() + from_values.size()); \ + memcpy(to_values.data() + prev_size * sizeof(TYPE), from_values.data(), from_values.size() * sizeof(TYPE)); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } +} + void CachePartition::flush() { + if (!write_data_buffer) + { + write_data_buffer = std::make_unique(path + BIN_FILE_EXT, buffer_size, O_RDWR | O_CREAT | O_TRUNC); + // TODO: не перетирать + seek в конец файла + } + const auto id_column = typeid_cast(buffer.front().get()); if (!id_column) throw Exception{"id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; const auto & ids = id_column->getData(); - key_to_file_offset[ids[0]] = out_file.getPositionInFile() + (1ULL << FILE_OFFSET_SIZE); + key_to_file_offset[ids[0]] = write_data_buffer->getPositionInFile(); size_t prev_size = 0; for (size_t row = 0; row < buffer.front()->size(); ++row) { @@ -95,7 +170,7 @@ void CachePartition::flush() { const auto & column = buffer[col]; const auto & type = header.getByPosition(col).type; - type->serializeBinary(*column, row, out_file); + type->serializeBinary(*column, row, *write_data_buffer); if (type->getTypeId() != TypeIndex::String) { prev_size += column->sizeOfValueIfFixed(); } else { @@ -104,13 +179,27 @@ void CachePartition::flush() } } - if (out_file.hasPendingData()) { - out_file.sync(); - } + write_data_buffer->sync(); buffer = header.cloneEmptyColumns(); } +template +void CachePartition::getValue(const std::string & attribute_name, const PaddedPODArray & ids, + ResultArrayType & out, std::unordered_map> & not_found) const +{ + UNUSED(attribute_name); + UNUSED(ids); + UNUSED(out); + UNUSED(not_found); +} + +void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const +{ + UNUSED(ids); + UNUSED(out); +} + template std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found) @@ -143,28 +232,18 @@ std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const while (const auto block = stream->read()) { - const auto id_column = typeid_cast(block.safeGetByPosition(0).column.get()); - if (!id_column) - throw Exception{"Id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; - - const auto & ids = id_column->getData(); - - /// cache column pointers - const auto column_ptrs = ext::map( - ext::range(0, dictionary.getAttributes().size()), - [&block](size_t i) { return block.safeGetByPosition(i + 1).column.get(); }); + const auto new_attributes = createAttributesFromBlock(block); + const auto & ids = std::get>(new_attributes.front().values); for (const auto i : ext::range(0, ids.size())) { - const auto id = ids[i]; - - on_updated(id, i, column_ptrs); /// mark corresponding id as found - remaining_ids[id] = 1; + on_updated(ids[i], i, new_attributes); + remaining_ids[ids[i]] = 1; } /// TODO: Add TTL to block - partitions[0]->appendBlock(block); + partitions[0]->appendBlock(new_attributes); } stream->readSuffix(); @@ -189,7 +268,40 @@ std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const size_t not_found_num = 0, found_num = 0; /// Check which ids have not been found and require setting null_value - auto mutable_columns = header.cloneEmptyColumns(); + CachePartition::Attributes new_attributes; + { + /// TODO: create attributes from structure + for (const auto & attribute : dictionary.getAttributes()) + { + switch (attribute.type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + new_attributes.emplace_back(attribute.type, std::vector()); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } + } + } for (const auto & id_found_pair : remaining_ids) { if (id_found_pair.second) @@ -216,31 +328,81 @@ std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const for (size_t i = 0; i < attributes.size(); ++i) { const auto & attribute = attributes[i]; - mutable_columns[i].insert(attribute.null_value); + // TODO : append null + (attribute.null_value); } /// inform caller that the cell has not been found on_id_not_found(id); } - partitions[0]->appendBlock(header.cloneWithColumns(std::move(mutable_columns))); + partitions[0]->appendBlock(new_attributes); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); ProfileEvents::increment(ProfileEvents::DictCacheRequests); } +CachePartition::Attributes CacheStorage::createAttributesFromBlock(const Block & block) +{ + CachePartition::Attributes attributes; + + const auto & structure = dictionary.getAttributes(); + const auto columns = block.getColumns(); + for (size_t i = 0; i < structure.size(); ++i) + { + const auto & column = columns[i]; + switch (structure[i].type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + std::vector values(column->size()); \ + const auto raw_data = column->getRawData(); \ + memcpy(values.data(), raw_data.data, raw_data.size); \ + attributes.emplace_back(structure[i].type, std::move(values)); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } + } + + return attributes; +} + SSDCacheDictionary::SSDCacheDictionary( const std::string & name_, const DictionaryStructure & dict_struct_, DictionarySourcePtr source_ptr_, const DictionaryLifetime dict_lifetime_, - const std::string & path, - const size_t partition_max_size) + const std::string & path_, + const size_t partition_max_size_) : name(name_) , dict_struct(dict_struct_) , source_ptr(std::move(source_ptr_)) , dict_lifetime(dict_lifetime_) + , path(path_) + , partition_max_size(partition_max_size_) , storage(*this, path, 1, partition_max_size) + , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) throw Exception{name + ": source cannot be used with CacheDictionary", ErrorCodes::UNSUPPORTED_METHOD}; @@ -347,10 +509,10 @@ template void SSDCacheDictionary::getItemsNumberImpl( const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const { - const auto attribute_index = getAttributeIndex(attribute_index); + const auto attribute_index = getAttributeIndex(attribute_name); std::unordered_map> not_found_ids; - storage.getValue(attribute_name, ids, out, not_found_ids); + storage.getValue(attribute_name, ids, out, not_found_ids); if (not_found_ids.empty()) return; @@ -360,10 +522,15 @@ void SSDCacheDictionary::getItemsNumberImpl( storage.update( source_ptr, required_ids, - [&](const auto id, const auto row, const auto & attributes) + [&](const auto id, const auto row, const auto & new_attributes) { - for (const size_t row : not_found_ids[id]) - out[row] = static_cast(attributes[attribute_index][row]); + Field field; + for (const size_t out_row : not_found_ids[id]) + { + new_attributes[attribute_index] + ->get(row, field); + out[out_row] = field.get(); + } }, [&](const auto id) { @@ -488,7 +655,7 @@ void SSDCacheDictionary::createAttributes() const auto & attribute = dict_struct.attributes[i]; attribute_index_by_name.emplace(attribute.name, i); - attributes.push_back(createAttributeWithType(attribute.type, attribute.null_value)); + attributes.push_back(createAttributeWithType(attribute.underlying_type, attribute.null_value)); if (attribute.hierarchical) throw Exception{name + ": hierarchical attributes not supported for dictionary of type " + getTypeName(), diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 6ece329ba874..ff6bfd10d8c7 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -15,9 +15,7 @@ #include "DictionaryStructure.h" #include "IDictionary.h" #include "IDictionarySource.h" -#include -#include - +#include namespace DB { @@ -31,9 +29,8 @@ class CachePartition using Offset = size_t; using Offsets = std::vector; - CachePartition(CacheStorage & storage, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); - - void appendBlock(const Block & block); + CachePartition(const std::vector & structure, const std::string & dir_path, + const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -49,22 +46,55 @@ class CachePartition /// 2 -- expired void has(const PaddedPODArray & ids, ResultArrayType & out) const; + struct Attribute + { + template + using Container = std::vector; + + AttributeUnderlyingType type; + std::variant< + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container> values; + }; + using Attributes = std::vector; + + + // Key, (Metadata), attributes + void appendBlock(const Attributes & new_columns); + private: void flush(); - - CacheStorage & storage; + void appendValuesToBufferAttribute(Attribute & to, const Attribute & from); size_t file_id; size_t max_size; size_t buffer_size; + std::string path; //mutable std::shared_mutex rw_lock; - int index_fd; + //int index_fd; int data_fd; + std::unique_ptr write_data_buffer; std::unordered_map key_to_file_offset; - MutableColumns buffer; + + Attributes buffer; + //MutableColumns buffer; + size_t bytes = 0; mutable std::atomic element_count{0}; }; @@ -74,6 +104,7 @@ using CachePartitionPtr = std::unique_ptr; class CacheStorage { +public: using Key = IDictionary::Key; CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) @@ -83,17 +114,17 @@ class CacheStorage , log(&Poco::Logger::get("CacheStorage")) { for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) - partitions.emplace_back(std::make_unique(partition_id, partition_max_size)); + partitions.emplace_back(std::make_unique(*this, partition_id, partition_max_size, path_)); } template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; - template + template void getValue(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - partitions[0]->getValue(attribute_name, ids, out, not_found); + partitions[0]->getValue(attribute_name, ids, out, not_found); } // getString(); @@ -106,11 +137,15 @@ class CacheStorage std::exception_ptr getLastException() const { return last_update_exception; } + const std::string & getPath() const { return path; } + private: + CachePartition::Attributes createAttributesFromBlock(const Block & block); + SSDCacheDictionary & dictionary; - /// Block structure: Key, (Default + TTL), Attr1, Attr2, ... - const Block header; + // Block structure: Key, (Default + TTL), Attr1, Attr2, ... + // const Block header; const std::string path; const size_t partition_max_size; std::vector partitions; @@ -164,7 +199,7 @@ class SSDCacheDictionary final : public IDictionary std::shared_ptr clone() const override { - return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, partition_max_size); + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, partition_max_size); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -253,7 +288,7 @@ class SSDCacheDictionary final : public IDictionary void getString(const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const; - void has(const PaddedPODArray & ids, PaddedPODArray & out) const override; + void has(const PaddedPODArray & /* ids */, PaddedPODArray & /* out */) const override {} // TODO BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override // TODO { @@ -262,9 +297,6 @@ class SSDCacheDictionary final : public IDictionary return nullptr; } -private: - friend class CacheStorage; - struct Attribute { AttributeUnderlyingType type; @@ -287,10 +319,12 @@ class SSDCacheDictionary final : public IDictionary }; using Attributes = std::vector; + const Attributes & getAttributes() const; + +private: size_t getAttributeIndex(const std::string & attr_name) const; Attribute & getAttribute(const std::string & attr_name); const Attribute & getAttribute(const std::string & attr_name) const; - const Attributes & getAttributes() const; template Attribute createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value); @@ -315,7 +349,7 @@ class SSDCacheDictionary final : public IDictionary Logger * const log; std::map attribute_index_by_name; - Attributes attributes; + Attributes attributes; // TODO: move to storage mutable size_t bytes_allocated = 0; mutable std::atomic element_count{0}; diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.inc.h b/dbms/src/Dictionaries/SSDCacheDictionary.inc.h index 73e94cb5b6d3..c3d113035094 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.inc.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.inc.h @@ -1,7 +1,7 @@ #pragma once namespace DB { - +/* template void BlockFile::getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const { @@ -41,5 +41,5 @@ void BlockFile::getValue(size_t column, const PaddedPODArray & ids, Resu out[index] = DB::get(field); } } - +*/ } \ No newline at end of file From 5dccab304f456435e4a340811118f710feeca41e Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 4 Jan 2020 18:04:16 +0300 Subject: [PATCH 0005/1102] compl --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 166 ++++++++++++++----- dbms/src/Dictionaries/SSDCacheDictionary.h | 35 ++-- 2 files changed, 147 insertions(+), 54 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 4d0cbc3f0757..afa473c0becf 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,9 @@ CachePartition::CachePartition(const std::vector & stru { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - buffer.emplace_back(type, std::vector()); \ + buffer.emplace_back(); \ + buffer.back().type = type; \ + buffer.back().values = std::vector(); \ break; DISPATCH(UInt8) @@ -90,21 +93,21 @@ void CachePartition::appendBlock(const Attributes & new_columns) if (new_columns.size() != buffer.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; - const auto & id_column = std::get>(new_columns.front().values); + const auto & ids = std::get>(new_columns.front().values); - size_t start_size = buffer.front()->size(); + size_t start_size = ids.size(); for (size_t i = 0; i < buffer.size(); ++i) { appendValuesToBufferAttribute(buffer[i], new_columns[i]); - bytes += buffer[i]->byteSize(); + //bytes += buffer[i]->byteSize(); } - for (size_t i = 0; i < id_column.size(); ++i) + for (size_t i = 0; i < ids.size(); ++i) { - key_to_file_offset[id_column[i]] = (start_size + i) | INMEMORY; + key_to_file_offset[ids[i]] = (start_size + i) | INMEMORY; } - if (bytes >= buffer_size) + //if (bytes >= buffer_size) { flush(); } @@ -155,33 +158,67 @@ void CachePartition::flush() // TODO: не перетирать + seek в конец файла } - const auto id_column = typeid_cast(buffer.front().get()); - if (!id_column) - throw Exception{"id column has type different from UInt64.", ErrorCodes::TYPE_MISMATCH}; - const auto & ids = id_column->getData(); + const auto & ids = std::get>(buffer.front().values); + + std::vector offsets; - key_to_file_offset[ids[0]] = write_data_buffer->getPositionInFile(); size_t prev_size = 0; - for (size_t row = 0; row < buffer.front()->size(); ++row) + for (size_t row = 0; row < ids.size(); ++row) { - key_to_file_offset[ids[row]] = key_to_file_offset[ids[row ? row - 1 : 0]] + prev_size; + offsets.push_back((offsets.empty() ? write_data_buffer->getPositionInFile() : offsets.back()) + prev_size); prev_size = 0; - for (size_t col = 0; col < header.columns(); ++col) + + for (size_t col = 0; col < buffer.size(); ++col) { - const auto & column = buffer[col]; - const auto & type = header.getByPosition(col).type; - type->serializeBinary(*column, row, *write_data_buffer); - if (type->getTypeId() != TypeIndex::String) { - prev_size += column->sizeOfValueIfFixed(); - } else { - prev_size += column->getDataAt(row).size + sizeof(UInt64); + const auto & attribute = buffer[col]; + + switch (attribute.type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + const auto & values = std::get>(attribute.values); \ + writeBinary(values[row], *static_cast(write_data_buffer.get())); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; } } } - write_data_buffer->sync(); - buffer = header.cloneEmptyColumns(); + /// commit changes in index + for (size_t row = 0; row < ids.size(); ++row) + { + key_to_file_offset[ids[row]] = offsets[row]; + } + + /// clear buffer + for (auto & attribute : buffer) + { + std::visit([](auto & attr) { + attr.clear(); + }, attribute.values); + } } template @@ -200,8 +237,23 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType structure; + for (const auto & item : dictionary.getStructure().attributes) + { + structure.push_back(item.underlying_type); + } + for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) + partitions.emplace_back(std::make_unique(structure, path_, partition_id, partition_max_size)); +} + template -std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, +void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found) { CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; @@ -277,7 +329,9 @@ std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - new_attributes.emplace_back(attribute.type, std::vector()); \ + new_attributes.emplace_back(); \ + new_attributes.back().type = attribute.type; \ + new_attributes.back().values = std::vector(); \ break; DISPATCH(UInt8) @@ -328,8 +382,38 @@ std::exception_ptr CacheStorage::update(DictionarySourcePtr & source_ptr, const for (size_t i = 0; i < attributes.size(); ++i) { const auto & attribute = attributes[i]; - // TODO : append null - (attribute.null_value); + // append null + switch (attribute.type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + auto & to_values = std::get>(new_attributes[i].values); \ + auto & null_value = std::get(attribute.null_value); \ + to_values.push_back(null_value); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } } /// inform caller that the cell has not been found @@ -359,7 +443,9 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock(const Block & std::vector values(column->size()); \ const auto raw_data = column->getRawData(); \ memcpy(values.data(), raw_data.data, raw_data.size); \ - attributes.emplace_back(structure[i].type, std::move(values)); \ + attributes.emplace_back(); \ + attributes.back().type = structure[i].type; \ + attributes.back().values = std::move(values); \ } \ break; @@ -416,14 +502,12 @@ SSDCacheDictionary::SSDCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ -\ const auto null_value = std::get(attributes[index].null_value); \ -\ getItemsNumberImpl( \ - attribute_name, \ - ids, \ - out, \ - [&](const size_t) { return null_value; }); \ + attribute_name, \ + ids, \ + out, \ + [&](const size_t) { return null_value; }); \ } DECLARE(UInt8) DECLARE(UInt16) @@ -450,7 +534,6 @@ SSDCacheDictionary::SSDCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ -\ getItemsNumberImpl( \ attribute_name, \ ids, \ @@ -522,15 +605,9 @@ void SSDCacheDictionary::getItemsNumberImpl( storage.update( source_ptr, required_ids, - [&](const auto id, const auto row, const auto & new_attributes) - { - Field field; + [&](const auto id, const auto row, const auto & new_attributes) { for (const size_t out_row : not_found_ids[id]) - { - new_attributes[attribute_index] - ->get(row, field); - out[out_row] = field.get(); - } + out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const auto id) { @@ -645,6 +722,7 @@ case AttributeUnderlyingType::ut##TYPE: \ DISPATCH(String) #undef DISPATCH } + throw Exception{"Unknown attribute type: " + std::to_string(static_cast(type)), ErrorCodes::TYPE_MISMATCH}; } void SSDCacheDictionary::createAttributes() diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index ff6bfd10d8c7..b5a2712dd054 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -23,6 +23,27 @@ namespace DB class SSDCacheDictionary; class CacheStorage; +/*class SimpleSerializer +{ +public: + bool block() const { return false; } + + template + size_t estimateSizeNumber(T number) const; + + size_t estimateSizeString(const String & str) const; + + template + ssize_t writeNumber(T number, WriteBuffer & buffer); + + ssize_t writeString(const String & str, WriteBuffer & buffer); + + template + ssize_t readNumber(T number, WriteBuffer & buffer); + + ssize_t readString(const String & str, WriteBuffer & buffer); +};*/ + class CachePartition { public: @@ -107,15 +128,8 @@ class CacheStorage public: using Key = IDictionary::Key; - CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) - : dictionary(dictionary_) - , path(path_) - , partition_max_size(partition_max_size_) - , log(&Poco::Logger::get("CacheStorage")) - { - for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) - partitions.emplace_back(std::make_unique(*this, partition_id, partition_max_size, path_)); - } + CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, + const size_t partitions_count_, const size_t partition_max_size_); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -130,7 +144,7 @@ class CacheStorage // getString(); template - std::exception_ptr update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, + void update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found); //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; @@ -319,6 +333,7 @@ class SSDCacheDictionary final : public IDictionary }; using Attributes = std::vector; + /// переместить const Attributes & getAttributes() const; private: From 55125cd5ac9819b0255bafb611befb0551a15c7f Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 5 Jan 2020 16:59:49 +0300 Subject: [PATCH 0006/1102] create + refactoring --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 177 +++++++++++------- dbms/src/Dictionaries/SSDCacheDictionary.h | 22 ++- .../getDictionaryConfigurationFromAST.cpp | 26 ++- .../src/Dictionaries/registerDictionaries.cpp | 1 + dbms/src/Dictionaries/registerDictionaries.h | 1 + .../Functions/FunctionsExternalDictionaries.h | 3 + dbms/src/Parsers/ASTDictionary.cpp | 18 +- dbms/src/Parsers/ASTDictionary.h | 2 +- dbms/src/Parsers/ParserDictionary.cpp | 14 +- 9 files changed, 163 insertions(+), 101 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index afa473c0becf..90c8057034c7 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "DictionaryFactory.h" #include #include #include @@ -50,35 +51,38 @@ namespace const std::string IND_FILE_EXT = ".idx"; } -CachePartition::CachePartition(const std::vector & structure, const std::string & dir_path, - const size_t file_id_, const size_t max_size_, const size_t buffer_size_) +CachePartition::CachePartition( + const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure, + const std::string & dir_path, const size_t file_id_, const size_t max_size_, const size_t buffer_size_) : file_id(file_id_), max_size(max_size_), buffer_size(buffer_size_), path(dir_path + "/" + std::to_string(file_id)) { - for (const auto & type : structure) + keys_buffer.type = AttributeUnderlyingType::utUInt64; + keys_buffer.values = std::vector(); + for (const auto & type : attributes_structure) { switch (type) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - buffer.emplace_back(); \ - buffer.back().type = type; \ - buffer.back().values = std::vector(); \ + attributes_buffer.emplace_back(); \ + attributes_buffer.back().type = type; \ + attributes_buffer.back().values = std::vector(); \ break; - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) #undef DISPATCH case AttributeUnderlyingType::utString: @@ -88,17 +92,19 @@ CachePartition::CachePartition(const std::vector & stru } } -void CachePartition::appendBlock(const Attributes & new_columns) +void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes) { - if (new_columns.size() != buffer.size()) + if (new_attributes.size() != attributes_buffer.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; - const auto & ids = std::get>(new_columns.front().values); + const auto & ids = std::get>(new_keys.values); size_t start_size = ids.size(); - for (size_t i = 0; i < buffer.size(); ++i) + + appendValuesToBufferAttribute(keys_buffer, new_keys); + for (size_t i = 0; i < attributes_buffer.size(); ++i) { - appendValuesToBufferAttribute(buffer[i], new_columns[i]); + appendValuesToBufferAttribute(attributes_buffer[i], new_attributes[i]); //bytes += buffer[i]->byteSize(); } @@ -158,7 +164,7 @@ void CachePartition::flush() // TODO: не перетирать + seek в конец файла } - const auto & ids = std::get>(buffer.front().values); + const auto & ids = std::get>(keys_buffer.values); std::vector offsets; @@ -168,9 +174,9 @@ void CachePartition::flush() offsets.push_back((offsets.empty() ? write_data_buffer->getPositionInFile() : offsets.back()) + prev_size); prev_size = 0; - for (size_t col = 0; col < buffer.size(); ++col) + for (size_t col = 0; col < attributes_buffer.size(); ++col) { - const auto & attribute = buffer[col]; + const auto & attribute = attributes_buffer[col]; switch (attribute.type) { @@ -208,24 +214,19 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) - { key_to_file_offset[ids[row]] = offsets[row]; - } /// clear buffer - for (auto & attribute : buffer) - { - std::visit([](auto & attr) { - attr.clear(); - }, attribute.values); - } + std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); + for (auto & attribute : attributes_buffer) + std::visit([](auto & attr) { attr.clear(); }, attribute.values); } template -void CachePartition::getValue(const std::string & attribute_name, const PaddedPODArray & ids, +void CachePartition::getValue(size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - UNUSED(attribute_name); + UNUSED(attribute_index); UNUSED(ids); UNUSED(out); UNUSED(not_found); @@ -249,7 +250,7 @@ CacheStorage::CacheStorage(SSDCacheDictionary & dictionary_, const std::string & structure.push_back(item.underlying_type); } for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) - partitions.emplace_back(std::make_unique(structure, path_, partition_id, partition_max_size)); + partitions.emplace_back(std::make_unique(AttributeUnderlyingType::utUInt64, structure, path_, partition_id, partition_max_size)); } template @@ -284,8 +285,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorread()) { - const auto new_attributes = createAttributesFromBlock(block); - const auto & ids = std::get>(new_attributes.front().values); + const auto new_keys = createAttributesFromBlock(block, 0, 1).front(); + const auto new_attributes = createAttributesFromBlock(block, 1); + const auto & ids = std::get>(new_keys.values); for (const auto i : ext::range(0, ids.size())) { @@ -295,7 +297,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_attributes); + partitions[0]->appendBlock(new_keys, new_attributes); } stream->readSuffix(); @@ -320,6 +322,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); CachePartition::Attributes new_attributes; { /// TODO: create attributes from structure @@ -377,6 +382,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); + /// Set null_value for each attribute const auto & attributes = dictionary.getAttributes(); for (size_t i = 0; i < attributes.size(); ++i) @@ -419,20 +427,23 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_attributes); + partitions[0]->appendBlock(new_keys, new_attributes); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); ProfileEvents::increment(ProfileEvents::DictCacheRequests); } -CachePartition::Attributes CacheStorage::createAttributesFromBlock(const Block & block) +CachePartition::Attributes CacheStorage::createAttributesFromBlock(const Block & block, const size_t begin, size_t end) { CachePartition::Attributes attributes; const auto & structure = dictionary.getAttributes(); + if (end == static_cast(-1)) + end = structure.size(); + const auto columns = block.getColumns(); - for (size_t i = 0; i < structure.size(); ++i) + for (size_t i = begin; i < end; ++i) { const auto & column = columns[i]; switch (structure[i].type) @@ -504,11 +515,12 @@ SSDCacheDictionary::SSDCacheDictionary( checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ const auto null_value = std::get(attributes[index].null_value); \ getItemsNumberImpl( \ - attribute_name, \ + index, \ ids, \ out, \ [&](const size_t) { return null_value; }); \ } + DECLARE(UInt8) DECLARE(UInt16) DECLARE(UInt32) @@ -535,7 +547,7 @@ SSDCacheDictionary::SSDCacheDictionary( const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ getItemsNumberImpl( \ - attribute_name, \ + index, \ ids, \ out, \ [&](const size_t row) { return def[row]; }); \ @@ -565,9 +577,8 @@ SSDCacheDictionary::SSDCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ -\ getItemsNumberImpl( \ - attribute_name, \ + index, \ ids, \ out, \ [&](const size_t) { return def; }); \ @@ -590,17 +601,15 @@ SSDCacheDictionary::SSDCacheDictionary( template void SSDCacheDictionary::getItemsNumberImpl( - const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const + const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const { - const auto attribute_index = getAttributeIndex(attribute_name); - std::unordered_map> not_found_ids; - storage.getValue(attribute_name, ids, out, not_found_ids); + storage.getValue(attribute_index, ids, out, not_found_ids); if (not_found_ids.empty()) return; std::vector required_ids(not_found_ids.size()); - std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](auto & pair) { return pair.first; }); + std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](const auto & pair) { return pair.first; }); storage.update( source_ptr, @@ -609,7 +618,7 @@ void SSDCacheDictionary::getItemsNumberImpl( for (const size_t out_row : not_found_ids[id]) out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, - [&](const auto id) + [&](const size_t id) { for (const size_t row : not_found_ids[id]) out[row] = get_default(row); @@ -618,37 +627,37 @@ void SSDCacheDictionary::getItemsNumberImpl( void SSDCacheDictionary::getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const { - auto & attribute = getAttribute(attribute_name); - checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); - const auto null_value = StringRef{std::get(attribute.null_value)}; + const auto null_value = StringRef{std::get(attributes[index].null_value)}; - getItemsString(attribute_name, ids, out, [&](const size_t) { return null_value; }); + getItemsString(index, ids, out, [&](const size_t) { return null_value; }); } void SSDCacheDictionary::getString( const std::string & attribute_name, const PaddedPODArray & ids, const ColumnString * const def, ColumnString * const out) const { - auto & attribute = getAttribute(attribute_name); - checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); - getItemsString(attribute_name, ids, out, [&](const size_t row) { return def->getDataAt(row); }); + getItemsString(index, ids, out, [&](const size_t row) { return def->getDataAt(row); }); } void SSDCacheDictionary::getString( const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const { - auto & attribute = getAttribute(attribute_name); - checkAttributeType(name, attribute_name, attribute.type, AttributeUnderlyingType::utString); + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); - getItemsString(attribute_name, ids, out, [&](const size_t) { return StringRef{def}; }); + getItemsString(index, ids, out, [&](const size_t) { return StringRef{def}; }); } template -void SSDCacheDictionary::getItemsString(const std::string & attribute_name, const PaddedPODArray & ids, +void SSDCacheDictionary::getItemsString(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const { - UNUSED(attribute_name); + UNUSED(attribute_index); UNUSED(ids); UNUSED(out); UNUSED(get_default); @@ -727,7 +736,7 @@ case AttributeUnderlyingType::ut##TYPE: \ void SSDCacheDictionary::createAttributes() { - attributes.resize(dict_struct.attributes.size()); + attributes.reserve(dict_struct.attributes.size()); for (size_t i = 0; i < dict_struct.attributes.size(); ++i) { const auto & attribute = dict_struct.attributes[i]; @@ -741,4 +750,36 @@ void SSDCacheDictionary::createAttributes() } } +void registerDictionarySSDCache(DictionaryFactory & factory) +{ + auto create_layout = [=](const std::string & name, + const DictionaryStructure & dict_struct, + const Poco::Util::AbstractConfiguration & config, + const std::string & config_prefix, + DictionarySourcePtr source_ptr) -> DictionaryPtr + { + if (dict_struct.key) + throw Exception{"'key' is not supported for dictionary of layout 'cache'", ErrorCodes::UNSUPPORTED_METHOD}; + + if (dict_struct.range_min || dict_struct.range_max) + throw Exception{name + + ": elements .structure.range_min and .structure.range_max should be defined only " + "for a dictionary of layout 'range_hashed'", + ErrorCodes::BAD_ARGUMENTS}; + const auto & layout_prefix = config_prefix + ".layout"; + const auto max_partition_size = config.getInt(layout_prefix + ".ssd.max_partition_size"); + if (max_partition_size == 0) + throw Exception{name + ": dictionary of layout 'cache' cannot have 0 cells", ErrorCodes::TOO_SMALL_BUFFER_SIZE}; + + const auto path = config.getString(layout_prefix + ".ssd.path"); + if (path.empty()) + throw Exception{name + ": dictionary of layout 'cache' cannot have empty path", + ErrorCodes::BAD_ARGUMENTS}; + + const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; + return std::make_unique(name, dict_struct, std::move(source_ptr), dict_lifetime, path, max_partition_size); + }; + factory.registerLayout("ssd", create_layout, false); +} + } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index b5a2712dd054..9df0b2597e21 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -50,14 +50,15 @@ class CachePartition using Offset = size_t; using Offsets = std::vector; - CachePartition(const std::vector & structure, const std::string & dir_path, - const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); + CachePartition( + const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, + const std::string & dir_path, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; template - void getValue(const std::string & attribute_name, const PaddedPODArray & ids, + void getValue(size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const; // TODO:: getString @@ -94,7 +95,7 @@ class CachePartition // Key, (Metadata), attributes - void appendBlock(const Attributes & new_columns); + void appendBlock(const Attribute & new_keys, const Attributes & new_attributes); private: void flush(); @@ -113,7 +114,8 @@ class CachePartition std::unique_ptr write_data_buffer; std::unordered_map key_to_file_offset; - Attributes buffer; + Attribute keys_buffer; + Attributes attributes_buffer; //MutableColumns buffer; size_t bytes = 0; @@ -135,10 +137,10 @@ class CacheStorage using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; template - void getValue(const std::string & attribute_name, const PaddedPODArray & ids, + void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - partitions[0]->getValue(attribute_name, ids, out, not_found); + partitions[0]->getValue(attribute_index, ids, out, not_found); } // getString(); @@ -154,7 +156,7 @@ class CacheStorage const std::string & getPath() const { return path; } private: - CachePartition::Attributes createAttributesFromBlock(const Block & block); + CachePartition::Attributes createAttributesFromBlock(const Block & block, const size_t begin = 0, size_t end = -1); SSDCacheDictionary & dictionary; @@ -348,9 +350,9 @@ class SSDCacheDictionary final : public IDictionary template void getItemsNumberImpl( - const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; + const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; template - void getItemsString(const std::string & attribute_name, const PaddedPODArray & ids, + void getItemsString(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; const std::string name; diff --git a/dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp b/dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp index f0e49bcc4ac0..fcecc8f2bbba 100644 --- a/dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp +++ b/dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace DB { @@ -95,13 +96,22 @@ void buildLayoutConfiguration( root->appendChild(layout_element); AutoPtr layout_type_element(doc->createElement(layout->layout_type)); layout_element->appendChild(layout_type_element); - if (layout->parameter.has_value()) + for (const auto & param : layout->parameters) { - const auto & param = layout->parameter; - AutoPtr layout_type_parameter_element(doc->createElement(param->first)); - const ASTLiteral & literal = param->second->as(); - AutoPtr value(doc->createTextNode(toString(literal.value.get()))); - layout_type_parameter_element->appendChild(value); + AutoPtr layout_type_parameter_element(doc->createElement(param.first)); + const ASTLiteral & literal = param.second->as(); + Field::dispatch([&](auto & value) + { + if constexpr (std::is_same_v, UInt64> || std::is_same_v, String>) + { + AutoPtr value_to_append(doc->createTextNode(toString(value))); + layout_type_parameter_element->appendChild(value_to_append); + } + else + { + throw DB::Exception{"Wrong type of layout argument.", ErrorCodes::BAD_ARGUMENTS}; + } + }, literal.value); layout_type_element->appendChild(layout_type_parameter_element); } } @@ -458,6 +468,10 @@ DictionaryConfigurationPtr getDictionaryConfigurationFromAST(const ASTCreateQuer buildRangeConfiguration(xml_document, structure_element, query.dictionary->range, all_attr_names_and_types); conf->load(xml_document); + + std::ostringstream ss; + conf->save(ss); + Poco::Logger::get("xml config:").information(ss.str()); return conf; } diff --git a/dbms/src/Dictionaries/registerDictionaries.cpp b/dbms/src/Dictionaries/registerDictionaries.cpp index 4ebaae041166..47e755a3c3d1 100644 --- a/dbms/src/Dictionaries/registerDictionaries.cpp +++ b/dbms/src/Dictionaries/registerDictionaries.cpp @@ -29,6 +29,7 @@ void registerDictionaries() registerDictionaryFlat(factory); registerDictionaryHashed(factory); registerDictionaryCache(factory); + registerDictionarySSDCache(factory); } } diff --git a/dbms/src/Dictionaries/registerDictionaries.h b/dbms/src/Dictionaries/registerDictionaries.h index 3f2e730b5e3c..5fa4a734ec32 100644 --- a/dbms/src/Dictionaries/registerDictionaries.h +++ b/dbms/src/Dictionaries/registerDictionaries.h @@ -24,6 +24,7 @@ void registerDictionaryTrie(DictionaryFactory & factory); void registerDictionaryFlat(DictionaryFactory & factory); void registerDictionaryHashed(DictionaryFactory & factory); void registerDictionaryCache(DictionaryFactory & factory); +void registerDictionarySSDCache(DictionaryFactory & factory); void registerDictionaries(); } diff --git a/dbms/src/Functions/FunctionsExternalDictionaries.h b/dbms/src/Functions/FunctionsExternalDictionaries.h index 33cb05e2e7b7..8542bc00f930 100644 --- a/dbms/src/Functions/FunctionsExternalDictionaries.h +++ b/dbms/src/Functions/FunctionsExternalDictionaries.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -839,6 +840,7 @@ class FunctionDictGet final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && + !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && @@ -1103,6 +1105,7 @@ class FunctionDictGetOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && + !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr)) diff --git a/dbms/src/Parsers/ASTDictionary.cpp b/dbms/src/Parsers/ASTDictionary.cpp index ec750acff315..37e73166a33b 100644 --- a/dbms/src/Parsers/ASTDictionary.cpp +++ b/dbms/src/Parsers/ASTDictionary.cpp @@ -66,10 +66,10 @@ ASTPtr ASTDictionaryLayout::clone() const auto res = std::make_shared(*this); res->children.clear(); res->layout_type = layout_type; - if (parameter.has_value()) + for (const auto & parameter : parameters) { - res->parameter.emplace(parameter->first, nullptr); - res->set(res->parameter->second, parameter->second->clone()); + res->parameters.emplace_back(parameter.first, nullptr); + res->set(res->parameters.back().second, parameter.second->clone()); } return res; } @@ -88,14 +88,18 @@ void ASTDictionaryLayout::formatImpl(const FormatSettings & settings, << (settings.hilite ? hilite_none : ""); settings.ostr << "("; - if (parameter) + bool first = true; + for (const auto & parameter : parameters) { - settings.ostr << (settings.hilite ? hilite_keyword : "") - << Poco::toUpper(parameter->first) + settings.ostr << (first ? "" : " ") + << (settings.hilite ? hilite_keyword : "") + << Poco::toUpper(parameter.first) << (settings.hilite ? hilite_none : "") << " "; - parameter->second->formatImpl(settings, state, expected); + parameter.second->formatImpl(settings, state, expected); + + first = false; } settings.ostr << ")"; settings.ostr << ")"; diff --git a/dbms/src/Parsers/ASTDictionary.h b/dbms/src/Parsers/ASTDictionary.h index e146162cbdf0..0113afe5d009 100644 --- a/dbms/src/Parsers/ASTDictionary.h +++ b/dbms/src/Parsers/ASTDictionary.h @@ -32,7 +32,7 @@ class ASTDictionaryLayout : public IAST /// flat, cache, hashed, etc. String layout_type; /// optional parameter (size_in_cells) - std::optional parameter; + std::vector parameters; String getID(char) const override { return "Dictionary layout"; } diff --git a/dbms/src/Parsers/ParserDictionary.cpp b/dbms/src/Parsers/ParserDictionary.cpp index ca9c2ad031a0..f79df2d31b1a 100644 --- a/dbms/src/Parsers/ParserDictionary.cpp +++ b/dbms/src/Parsers/ParserDictionary.cpp @@ -123,21 +123,17 @@ bool ParserDictionaryLayout::parseImpl(Pos & pos, ASTPtr & node, Expected & expe res->layout_type = func.name; const ASTExpressionList & type_expr_list = func.elements->as(); - /// there are no layout with more than 1 parameter - if (type_expr_list.children.size() > 1) - return false; - - if (type_expr_list.children.size() == 1) + for (const auto & child : type_expr_list.children) { - const ASTPair * pair = dynamic_cast(type_expr_list.children.at(0).get()); + const ASTPair * pair = dynamic_cast(child.get()); if (pair == nullptr) return false; const ASTLiteral * literal = dynamic_cast(pair->second.get()); - if (literal == nullptr || literal->value.getType() != Field::Types::UInt64) + if (literal == nullptr || (literal->value.getType() != Field::Types::UInt64 && literal->value.getType() != Field::Types::String)) return false; - res->parameter.emplace(pair->first, nullptr); - res->set(res->parameter->second, literal->clone()); + res->parameters.emplace_back(pair->first, nullptr); + res->set(res->parameters.back().second, literal->clone()); } node = res; From 57d9e3820f24e47eb0d15496d918e99376b336ce Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 5 Jan 2020 20:05:49 +0300 Subject: [PATCH 0007/1102] fixed update --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 27 ++++++++++---------- dbms/src/Dictionaries/SSDCacheDictionary.h | 3 ++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 90c8057034c7..c65668e8dc52 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -115,7 +115,7 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & //if (bytes >= buffer_size) { - flush(); + //flush(); } } @@ -227,9 +227,11 @@ void CachePartition::getValue(size_t attribute_index, const PaddedPODArray & out, std::unordered_map> & not_found) const { UNUSED(attribute_index); - UNUSED(ids); UNUSED(out); - UNUSED(not_found); + for (size_t i = 0; i < ids.size(); ++i) + { + not_found[ids[i]].push_back(i); + } } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const @@ -285,8 +287,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorread()) { - const auto new_keys = createAttributesFromBlock(block, 0, 1).front(); - const auto new_attributes = createAttributesFromBlock(block, 1); + const auto new_keys = createAttributesFromBlock(block, { AttributeUnderlyingType::utUInt64 }).front(); + const auto new_attributes = createAttributesFromBlock( + block, ext::map(dictionary.getAttributes(), [](const auto & attribute) { return attribute.type; })); const auto & ids = std::get>(new_keys.values); for (const auto i : ext::range(0, ids.size())) @@ -434,19 +437,16 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & structure) { CachePartition::Attributes attributes; - const auto & structure = dictionary.getAttributes(); - if (end == static_cast(-1)) - end = structure.size(); - const auto columns = block.getColumns(); - for (size_t i = begin; i < end; ++i) + for (size_t i = 0; i < structure.size(); ++i) { const auto & column = columns[i]; - switch (structure[i].type) + switch (structure[i]) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ @@ -455,7 +455,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock(const Block & const auto raw_data = column->getRawData(); \ memcpy(values.data(), raw_data.data, raw_data.size); \ attributes.emplace_back(); \ - attributes.back().type = structure[i].type; \ + attributes.back().type = structure[i]; \ attributes.back().values = std::move(values); \ } \ break; @@ -615,6 +615,7 @@ void SSDCacheDictionary::getItemsNumberImpl( source_ptr, required_ids, [&](const auto id, const auto row, const auto & new_attributes) { + Poco::Logger::get("update:").information(std::to_string(id) + " " + std::to_string(row)); for (const size_t out_row : not_found_ids[id]) out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 9df0b2597e21..e7fb12af15fd 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -156,7 +156,8 @@ class CacheStorage const std::string & getPath() const { return path; } private: - CachePartition::Attributes createAttributesFromBlock(const Block & block, const size_t begin = 0, size_t end = -1); + CachePartition::Attributes createAttributesFromBlock( + const Block & block, const std::vector & structure); SSDCacheDictionary & dictionary; From f3b00e6c8c6ec7f03beddd8a085067fc93f8fdf3 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 5 Jan 2020 23:31:25 +0300 Subject: [PATCH 0008/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 64 ++++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 14 ++++- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index c65668e8dc52..3fc0db308993 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -46,7 +46,8 @@ namespace ErrorCodes namespace { - constexpr size_t INMEMORY = (1ULL << 63ULL); + constexpr size_t IN_MEMORY = (1ULL << 63ULL); + constexpr size_t NOT_FOUND = -1; const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; } @@ -99,7 +100,7 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & const auto & ids = std::get>(new_keys.values); - size_t start_size = ids.size(); + const size_t start_size = std::visit([](const auto & values) { return values.size(); }, keys_buffer.values); appendValuesToBufferAttribute(keys_buffer, new_keys); for (size_t i = 0; i < attributes_buffer.size(); ++i) @@ -109,14 +110,10 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & } for (size_t i = 0; i < ids.size(); ++i) - { - key_to_file_offset[ids[i]] = (start_size + i) | INMEMORY; - } + key_to_file_offset[ids[i]] = (start_size + i) | IN_MEMORY; //if (bytes >= buffer_size) - { //flush(); - } } void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribute & from) @@ -223,23 +220,63 @@ void CachePartition::flush() } template -void CachePartition::getValue(size_t attribute_index, const PaddedPODArray & ids, +void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - UNUSED(attribute_index); - UNUSED(out); + PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { - not_found[ids[i]].push_back(i); + auto it = key_to_file_offset.find(ids[i]); + if (it != std::end(key_to_file_offset)) + { + Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i])); + indices[i] = it->second; + } + else + { + Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i])); + indices[i] = NOT_FOUND; + not_found[ids[i]].push_back(i); + } + + + getValueFromMemory(attribute_index, indices, out); + getValueFromStorage(attribute_index, indices, out); } } -void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const +template +void CachePartition::getValueFromMemory( + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { - UNUSED(ids); + const auto & attribute = std::get>(attributes_buffer[attribute_index].values); + for (size_t i = 0; i < indices.size(); ++i) + { + const auto & index = indices[i]; + if (index != NOT_FOUND && (index & IN_MEMORY)) + { + out[i] = attribute[index ^ IN_MEMORY]; + if constexpr (std::is_same_v) + Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(out[i]) + " --- " + std::to_string(index ^ IN_MEMORY)); + } + } +} + +template +void CachePartition::getValueFromStorage( + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const +{ + UNUSED(attribute_index); + UNUSED(indices); UNUSED(out); } +void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const +{ + for (size_t i = 0; i < ids.size(); ++i) + out[i] = static_cast(key_to_file_offset.find(ids[i]) != std::end(key_to_file_offset)); +} + CacheStorage::CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) : dictionary(dictionary_) , path(path_) @@ -259,6 +296,7 @@ template void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found) { + Poco::Logger::get("cachestorage").information("update"); CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; ProfileEvents::increment(ProfileEvents::DictCacheKeysRequested, requested_ids.size()); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index e7fb12af15fd..a02562a04800 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -54,11 +54,15 @@ class CachePartition const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, const std::string & dir_path, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); + ~CachePartition() { + Poco::Logger::get("cachepartition").information("DESTROY"); + } + template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; template - void getValue(size_t attribute_index, const PaddedPODArray & ids, + void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const; // TODO:: getString @@ -102,6 +106,14 @@ class CachePartition void appendValuesToBufferAttribute(Attribute & to, const Attribute & from); + template + void getValueFromMemory( + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + + template + void getValueFromStorage( + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + size_t file_id; size_t max_size; size_t buffer_size; From 2c521628daf27f4bfed16cae21832ca9b47e0cfe Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 6 Jan 2020 23:38:32 +0300 Subject: [PATCH 0009/1102] some fixes --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 319 +++++++++++++++++-- dbms/src/Dictionaries/SSDCacheDictionary.h | 59 +++- 2 files changed, 337 insertions(+), 41 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 3fc0db308993..c98082f327aa 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -7,11 +7,14 @@ #include #include #include "DictionaryFactory.h" +#include +#include #include #include #include #include #include +#include namespace ProfileEvents { @@ -25,6 +28,7 @@ namespace ProfileEvents extern const Event DictCacheRequests; extern const Event DictCacheLockWriteNs; extern const Event DictCacheLockReadNs; + extern const Event FileOpen; } namespace CurrentMetrics @@ -42,16 +46,93 @@ namespace ErrorCodes extern const int UNSUPPORTED_METHOD; extern const int LOGICAL_ERROR; extern const int TOO_SMALL_BUFFER_SIZE; + extern const int FILE_DOESNT_EXIST; + extern const int CANNOT_OPEN_FILE; + extern const int CANNOT_IO_SUBMIT; + extern const int CANNOT_IO_GETEVENTS; } namespace { - constexpr size_t IN_MEMORY = (1ULL << 63ULL); + constexpr size_t MAX_KEYS_TO_READ_ONCE = 128; + constexpr size_t SSD_BLOCK_SIZE = 4096; + constexpr size_t READ_BUFFER_ALIGNMENT = 0; + constexpr size_t MAX_ATTRIBUTES_SIZE = 1024; + + static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); + static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; + + constexpr size_t KEY_IN_MEMORY_BIT = 63; + constexpr size_t KEY_IN_MEMORY = (1ULL << KEY_IN_MEMORY_BIT); + constexpr size_t BLOCK_INDEX_BITS = 32; + constexpr size_t INDEX_IN_BLOCK_BITS = 16; + constexpr size_t INDEX_IN_BLOCK_MASK = (1ULL << INDEX_IN_BLOCK_BITS) - 1; + constexpr size_t BLOCK_INDEX_MASK = ((1ULL << (BLOCK_INDEX_BITS + INDEX_IN_BLOCK_BITS)) - 1) ^ INDEX_IN_BLOCK_MASK; + constexpr size_t NOT_FOUND = -1; + const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; } +CachePartition::KeyMetadata::time_point_t CachePartition::KeyMetadata::expiresAt() const +{ + return ext::safe_bit_cast(data & KEY_METADATA_EXPIRES_AT_MASK); +} +void CachePartition::KeyMetadata::setExpiresAt(const time_point_t & t) +{ + data = ext::safe_bit_cast(t); +} + +bool CachePartition::KeyMetadata::isDefault() const +{ + return (data & KEY_METADATA_IS_DEFAULT_MASK) == KEY_METADATA_IS_DEFAULT_MASK; +} +void CachePartition::KeyMetadata::setDefault() +{ + data |= KEY_METADATA_IS_DEFAULT_MASK; +} + +bool CachePartition::Index::inMemory() const +{ + return (index & KEY_IN_MEMORY) == KEY_IN_MEMORY; +} + +bool CachePartition::Index::exists() const +{ + return index != NOT_FOUND; +} + +void CachePartition::Index::setNotExists() +{ + index = NOT_FOUND; +} + +void CachePartition::Index::setInMemory(const bool in_memory) +{ + index = (index & ~KEY_IN_MEMORY) | (static_cast(in_memory) << KEY_IN_MEMORY_BIT); +} + +size_t CachePartition::Index::getAddressInBlock() const +{ + return index & INDEX_IN_BLOCK_MASK; +} + +void CachePartition::Index::setAddressInBlock(const size_t address_in_block) +{ + index = (index & ~INDEX_IN_BLOCK_MASK) | address_in_block; +} + +size_t CachePartition::Index::getBlockId() const +{ + return (index & BLOCK_INDEX_MASK) >> INDEX_IN_BLOCK_BITS; +} + +void CachePartition::Index::setBlockId(const size_t block_id) +{ + index = (index & ~BLOCK_INDEX_MASK) | (block_id << INDEX_IN_BLOCK_BITS); +} + CachePartition::CachePartition( const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure, const std::string & dir_path, const size_t file_id_, const size_t max_size_, const size_t buffer_size_) @@ -91,6 +172,22 @@ CachePartition::CachePartition( break; } } + + { + ProfileEvents::increment(ProfileEvents::FileOpen); + + const std::string filename = path + BIN_FILE_EXT; + read_fd = ::open(filename.c_str(), O_RDONLY | O_DIRECT); + if (read_fd == -1) + { + auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE; + throwFromErrnoWithPath("Cannot open file " + filename, filename, error_code); + } + } +} + +CachePartition::~CachePartition() { + ::close(read_fd); } void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes) @@ -110,10 +207,12 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & } for (size_t i = 0; i < ids.size(); ++i) - key_to_file_offset[ids[i]] = (start_size + i) | IN_MEMORY; - + { + key_to_metadata[ids[i]].index.setInMemory(true); + key_to_metadata[ids[i]].index.setAddressInBlock(start_size + i); + } //if (bytes >= buffer_size) - //flush(); + //flush(); } void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribute & from) @@ -127,7 +226,7 @@ void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribu auto &from_values = std::get>(from.values); \ size_t prev_size = to_values.size(); \ to_values.resize(to_values.size() + from_values.size()); \ - memcpy(to_values.data() + prev_size * sizeof(TYPE), from_values.data(), from_values.size() * sizeof(TYPE)); \ + memcpy(&to_values[prev_size], &from_values[0], from_values.size() * sizeof(TYPE)); \ } \ break; @@ -162,6 +261,10 @@ void CachePartition::flush() } const auto & ids = std::get>(keys_buffer.values); + if (ids.empty()) + return; + + Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!!"); std::vector offsets; @@ -211,7 +314,12 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) - key_to_file_offset[ids[row]] = offsets[row]; + { + key_to_metadata[ids[row]].index.setInMemory(false); + key_to_metadata[ids[row]].index.setBlockId(current_block_id); + key_to_metadata[ids[row]].index.setAddressInBlock(offsets[row]); + Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_metadata[ids[row]].index.index)); + } /// clear buffer std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); @@ -223,20 +331,20 @@ template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { - PaddedPODArray indices(ids.size()); + PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { - auto it = key_to_file_offset.find(ids[i]); - if (it != std::end(key_to_file_offset)) + auto it = key_to_metadata.find(ids[i]); + if (it == std::end(key_to_metadata)) // TODO: check expired { - Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i])); - indices[i] = it->second; + Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i])); + indices[i].setNotExists(); + not_found[ids[i]].push_back(i); } else { - Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i])); - indices[i] = NOT_FOUND; - not_found[ids[i]].push_back(i); + Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i])); + indices[i] = it->second.index; } @@ -247,34 +355,184 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray template void CachePartition::getValueFromMemory( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { const auto & attribute = std::get>(attributes_buffer[attribute_index].values); for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; - if (index != NOT_FOUND && (index & IN_MEMORY)) + if (index.exists() && index.inMemory()) { - out[i] = attribute[index ^ IN_MEMORY]; + out[i] = attribute[index.getAddressInBlock()]; if constexpr (std::is_same_v) - Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(out[i]) + " --- " + std::to_string(index ^ IN_MEMORY)); + Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(out[i]) + " --- " + std::to_string(index.getAddressInBlock())); } } } template void CachePartition::getValueFromStorage( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { - UNUSED(attribute_index); - UNUSED(indices); - UNUSED(out); + std::vector> index_to_out; + for (size_t i = 0; i < indices.size(); ++i) + { + const auto & index = indices[i]; + if (index.exists() && !index.inMemory()) + index_to_out.emplace_back(index.getAddressInBlock(), i); + } + if (index_to_out.empty()) + return; + + std::sort(std::begin(index_to_out), std::end(index_to_out)); + + DB::Memory read_buffer(MAX_ATTRIBUTES_SIZE * index_to_out.size(), READ_BUFFER_ALIGNMENT); + + std::vector requests(index_to_out.size()); + memset(requests.data(), 0, requests.size() * sizeof(requests.front())); + std::vector pointers(index_to_out.size()); + for (size_t i = 0; i < index_to_out.size(); ++i) + { +#if defined(__FreeBSD__) + request.aio.aio_lio_opcode = LIO_READ; + request.aio.aio_fildes = read_fd; + request.aio.aio_buf = reinterpret_cast(read_buffer.data() + i * MAX_ATTRIBUTES_SIZE); + request.aio.aio_nbytes = MAX_ATTRIBUTES_SIZE; + request.aio.aio_offset = index_to_out[i].first; + request.aio_data = i; +#else + requests[i].aio_lio_opcode = IOCB_CMD_PREAD; + requests[i].aio_fildes = read_fd; + requests[i].aio_buf = reinterpret_cast(read_buffer.data()) + i * MAX_ATTRIBUTES_SIZE; + requests[i].aio_nbytes = MAX_ATTRIBUTES_SIZE; + requests[i].aio_offset = index_to_out[i].first; + requests[i].aio_data = i; +#endif + + Poco::Logger::get("requests:").information(); + pointers[i] = &requests[i]; + } + Poco::Logger::get("requests:").information(std::to_string(requests.size())); + + //const auto pointers = ext::map( + // std::begin(requests), std::end(requests), [](const iocb & request) { return &request; }); + + AIOContext context(MAX_KEYS_TO_READ_ONCE); + + std::vector events(index_to_out.size()); + + for (size_t i = 0; i < index_to_out.size(); i += MAX_KEYS_TO_READ_ONCE) + { + size_t to_push = std::min(MAX_KEYS_TO_READ_ONCE, index_to_out.size() - i); + size_t push_index = i; + int pushed = 0; + while (to_push > 0 && (pushed = io_submit(context.ctx, to_push, pointers.data() + push_index)) < 0) + { + if (errno != EINTR) + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + to_push -= pushed; + push_index += pushed; + pushed = 0; + } + + size_t to_get = std::min(MAX_KEYS_TO_READ_ONCE, index_to_out.size() - i); + size_t got_index = i; + int got = 0; + while (to_get > 0 && (got = io_getevents(context.ctx, to_get, to_get, events.data() + got_index, NULL)) < 0) + { + if (errno != EINTR) + throwFromErrno("io_getevents: Failed to get an event from asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); + to_get -= got; + got_index += got; + got = 0; + } + } + + //std::sort(std::begin(events), std::end(events), [](const auto & lhs, const auto & rhs) { return lhs.data < rhs.data; }); + for (const auto & event : events) + { + Poco::Logger::get("Read:").information("ito: f:" + std::to_string(index_to_out[event.data].first) + " s:" + std::to_string(index_to_out[event.data].second)); + Poco::Logger::get("Read:").information("data: " + std::to_string(event.data) + " res: " + std::to_string(event.res)); + DB::ReadBufferFromMemory buf(read_buffer.data() + event.data * MAX_ATTRIBUTES_SIZE, event.res); + + for (size_t i = 0; i < attribute_index; ++i) + { + switch (attributes_buffer[i].type) + { + #define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + TYPE tmp; \ + readBinary(tmp, buf); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + #undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } + } + + switch (attributes_buffer[attribute_index].type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + readBinary(out[index_to_out[event.data].second], buf); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } + } } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const { for (size_t i = 0; i < ids.size(); ++i) - out[i] = static_cast(key_to_file_offset.find(ids[i]) != std::end(key_to_file_offset)); + { + auto it = key_to_metadata.find(ids[i]); + if (it == std::end(key_to_metadata)) + { + out[i] = 0; + } + else + { + out[i] = it->second.isDefault(); + } + } } CacheStorage::CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) @@ -325,9 +583,10 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorread()) { - const auto new_keys = createAttributesFromBlock(block, { AttributeUnderlyingType::utUInt64 }).front(); + const auto new_keys = createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front(); const auto new_attributes = createAttributesFromBlock( - block, ext::map(dictionary.getAttributes(), [](const auto & attribute) { return attribute.type; })); + block, 1, ext::map(dictionary.getAttributes(), [](const auto & attribute) { return attribute.type; })); + const auto & ids = std::get>(new_keys.values); for (const auto i : ext::range(0, ids.size())) @@ -468,7 +727,8 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes); + if (not_found_num) + partitions[0]->appendBlock(new_keys, new_attributes); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); @@ -476,14 +736,14 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & structure) + const Block & block, const size_t begin_column, const std::vector & structure) { CachePartition::Attributes attributes; const auto columns = block.getColumns(); for (size_t i = 0; i < structure.size(); ++i) { - const auto & column = columns[i]; + const auto & column = columns[i + begin_column]; switch (structure[i]) { #define DISPATCH(TYPE) \ @@ -491,7 +751,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( { \ std::vector values(column->size()); \ const auto raw_data = column->getRawData(); \ - memcpy(values.data(), raw_data.data, raw_data.size); \ + memcpy(&values[0], raw_data.data, raw_data.size * sizeof(TYPE)); \ attributes.emplace_back(); \ attributes.back().type = structure[i]; \ attributes.back().values = std::move(values); \ @@ -653,7 +913,6 @@ void SSDCacheDictionary::getItemsNumberImpl( source_ptr, required_ids, [&](const auto id, const auto row, const auto & new_attributes) { - Poco::Logger::get("update:").information(std::to_string(id) + " " + std::to_string(row)); for (const size_t out_row : not_found_ids[id]) out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index a02562a04800..03baf91d7e15 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -52,11 +52,9 @@ class CachePartition CachePartition( const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, - const std::string & dir_path, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024 * 1024); + const std::string & dir_path, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024); - ~CachePartition() { - Poco::Logger::get("cachepartition").information("DESTROY"); - } + ~CachePartition(); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -97,22 +95,41 @@ class CachePartition }; using Attributes = std::vector; - // Key, (Metadata), attributes void appendBlock(const Attribute & new_keys, const Attributes & new_attributes); private: + struct Index final + { + bool inMemory() const; + void setInMemory(const bool in_memory); + + bool exists() const; + void setNotExists(); + + size_t getAddressInBlock() const; + void setAddressInBlock(const size_t address_in_block); + + size_t getBlockId() const; + void setBlockId(const size_t block_id); + + bool operator< (const Index & rhs) const { return index < rhs.index; } + + /// Stores `is_in_memory` flag, block id, address in uncompressed block + size_t index = 0; + }; + void flush(); void appendValuesToBufferAttribute(Attribute & to, const Attribute & from); template void getValueFromMemory( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; template void getValueFromStorage( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; size_t file_id; size_t max_size; @@ -121,15 +138,35 @@ class CachePartition //mutable std::shared_mutex rw_lock; //int index_fd; - int data_fd; + mutable int read_fd = -1; std::unique_ptr write_data_buffer; - std::unordered_map key_to_file_offset; + + struct KeyMetadata final + { + using time_point_t = std::chrono::system_clock::time_point; + using time_point_rep_t = time_point_t::rep; + using time_point_urep_t = std::make_unsigned_t; + + time_point_t expiresAt() const; + void setExpiresAt(const time_point_t & t); + + bool isDefault() const; + void setDefault(); + + Index index{}; + /// Stores both expiration time and `is_default` flag in the most significant bit + time_point_urep_t data = 0; + }; + + std::unordered_map key_to_metadata; Attribute keys_buffer; Attributes attributes_buffer; //MutableColumns buffer; size_t bytes = 0; + size_t current_block_id = 0; + size_t current_address_in_block = 0; mutable std::atomic element_count{0}; }; @@ -169,7 +206,7 @@ class CacheStorage private: CachePartition::Attributes createAttributesFromBlock( - const Block & block, const std::vector & structure); + const Block & block, const size_t begin_column, const std::vector & structure); SSDCacheDictionary & dictionary; @@ -224,7 +261,7 @@ class SSDCacheDictionary final : public IDictionary double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / partition_max_size; } // TODO: fix - bool supportUpdates() const override { return true; } + bool supportUpdates() const override { return false; } std::shared_ptr clone() const override { From b62ac3aa8052444d32bd41d9af2afc0388109fbe Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 7 Jan 2020 14:26:52 +0300 Subject: [PATCH 0010/1102] change buffer --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 170 ++++++++++++------- dbms/src/Dictionaries/SSDCacheDictionary.h | 18 +- 2 files changed, 122 insertions(+), 66 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index c98082f327aa..7b881fc6415b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -55,8 +55,8 @@ namespace ErrorCodes namespace { constexpr size_t MAX_KEYS_TO_READ_ONCE = 128; - constexpr size_t SSD_BLOCK_SIZE = 4096; - constexpr size_t READ_BUFFER_ALIGNMENT = 0; + constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t MAX_ATTRIBUTES_SIZE = 1024; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); @@ -135,8 +135,8 @@ void CachePartition::Index::setBlockId(const size_t block_id) CachePartition::CachePartition( const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure, - const std::string & dir_path, const size_t file_id_, const size_t max_size_, const size_t buffer_size_) - : file_id(file_id_), max_size(max_size_), buffer_size(buffer_size_), path(dir_path + "/" + std::to_string(file_id)) + const std::string & dir_path, const size_t file_id_, const size_t max_size_) + : file_id(file_id_), max_size(max_size_), path(dir_path + "/" + std::to_string(file_id)), memory(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT) { keys_buffer.type = AttributeUnderlyingType::utUInt64; keys_buffer.values = std::vector(); @@ -186,7 +186,8 @@ CachePartition::CachePartition( } } -CachePartition::~CachePartition() { +CachePartition::~CachePartition() +{ ::close(read_fd); } @@ -197,25 +198,64 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & const auto & ids = std::get>(new_keys.values); - const size_t start_size = std::visit([](const auto & values) { return values.size(); }, keys_buffer.values); + appendValuesToAttribute(keys_buffer, new_keys); - appendValuesToBufferAttribute(keys_buffer, new_keys); - for (size_t i = 0; i < attributes_buffer.size(); ++i) - { - appendValuesToBufferAttribute(attributes_buffer[i], new_attributes[i]); - //bytes += buffer[i]->byteSize(); - } + if (!write_buffer) + write_buffer.emplace(memory.data(), memory.size()); - for (size_t i = 0; i < ids.size(); ++i) + for (size_t index = 0; index < ids.size();) { - key_to_metadata[ids[i]].index.setInMemory(true); - key_to_metadata[ids[i]].index.setAddressInBlock(start_size + i); + auto & key_index = key_to_metadata[ids[index]].index; + key_index.setInMemory(true); + key_index.setBlockId(current_memory_block_id); + key_index.setAddressInBlock(write_buffer->offset()); + + for (const auto & attribute : new_attributes) + { + // TODO:: переделать через столбцы + getDataAt + switch (attribute.type) { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + if (sizeof(TYPE) > write_buffer->available()) \ + { \ + flush(); \ + continue; \ + } \ + else \ + { \ + const auto & values = std::get>(attribute.values); \ + writeBinary(values[index], *write_buffer); \ + } \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } + } + ++index; } - //if (bytes >= buffer_size) - //flush(); } -void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribute & from) +size_t CachePartition::appendValuesToAttribute(Attribute & to, const Attribute & from) { switch (to.type) { @@ -227,6 +267,7 @@ void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribu size_t prev_size = to_values.size(); \ to_values.resize(to_values.size() + from_values.size()); \ memcpy(&to_values[prev_size], &from_values[0], from_values.size() * sizeof(TYPE)); \ + return from_values.size() * sizeof(TYPE); \ } \ break; @@ -250,13 +291,14 @@ void CachePartition::appendValuesToBufferAttribute(Attribute & to, const Attribu // TODO: string support break; } + throw Exception{"Unknown attribute type: " + std::to_string(static_cast(to.type)), ErrorCodes::TYPE_MISMATCH}; } void CachePartition::flush() { if (!write_data_buffer) { - write_data_buffer = std::make_unique(path + BIN_FILE_EXT, buffer_size, O_RDWR | O_CREAT | O_TRUNC); + //write_data_buffer = std::make_unique(path + BIN_FILE_EXT, buffer_size, O_RDWR | O_CREAT | O_TRUNC); // TODO: не перетирать + seek в конец файла } @@ -316,7 +358,7 @@ void CachePartition::flush() for (size_t row = 0; row < ids.size(); ++row) { key_to_metadata[ids[row]].index.setInMemory(false); - key_to_metadata[ids[row]].index.setBlockId(current_block_id); + key_to_metadata[ids[row]].index.setBlockId(current_file_block_id); key_to_metadata[ids[row]].index.setAddressInBlock(offsets[row]); Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_metadata[ids[row]].index.index)); } @@ -357,15 +399,18 @@ template void CachePartition::getValueFromMemory( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { - const auto & attribute = std::get>(attributes_buffer[attribute_index].values); + //const auto & attribute = std::get>(attributes_buffer[attribute_index].values); for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; if (index.exists() && index.inMemory()) { - out[i] = attribute[index.getAddressInBlock()]; - if constexpr (std::is_same_v) - Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(out[i]) + " --- " + std::to_string(index.getAddressInBlock())); + const size_t offset = index.getAddressInBlock(); + + Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(i) + " --- " + std::to_string(offset)); + + ReadBufferFromMemory read_buffer(memory.data() + offset, memory.size() - offset); + readValueFromBuffer(attribute_index, out[i], read_buffer); } } } @@ -386,7 +431,7 @@ void CachePartition::getValueFromStorage( std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(MAX_ATTRIBUTES_SIZE * index_to_out.size(), READ_BUFFER_ALIGNMENT); + DB::Memory read_buffer(MAX_ATTRIBUTES_SIZE * index_to_out.size(), BUFFER_ALIGNMENT); std::vector requests(index_to_out.size()); memset(requests.data(), 0, requests.size() * sizeof(requests.front())); @@ -453,49 +498,26 @@ void CachePartition::getValueFromStorage( { Poco::Logger::get("Read:").information("ito: f:" + std::to_string(index_to_out[event.data].first) + " s:" + std::to_string(index_to_out[event.data].second)); Poco::Logger::get("Read:").information("data: " + std::to_string(event.data) + " res: " + std::to_string(event.res)); + DB::ReadBufferFromMemory buf(read_buffer.data() + event.data * MAX_ATTRIBUTES_SIZE, event.res); + readValueFromBuffer(attribute_index, out[index_to_out[event.data].second], buf); + } +} - for (size_t i = 0; i < attribute_index; ++i) +template +void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const +{ + for (size_t i = 0; i < attribute_index; ++i) + { + switch (attributes_buffer[i].type) { - switch (attributes_buffer[i].type) - { - #define DISPATCH(TYPE) \ +#define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ - TYPE tmp; \ - readBinary(tmp, buf); \ + buf.ignore(sizeof(TYPE)); \ } \ break; - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) - #undef DISPATCH - - case AttributeUnderlyingType::utString: - // TODO: string support - break; - } - } - - switch (attributes_buffer[attribute_index].type) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - readBinary(out[index_to_out[event.data].second], buf); \ - break; - DISPATCH(UInt8) DISPATCH(UInt16) DISPATCH(UInt32) @@ -517,6 +539,34 @@ void CachePartition::getValueFromStorage( break; } } + + switch (attributes_buffer[attribute_index].type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + readBinary(dst, buf); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + // TODO: string support + break; + } } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 03baf91d7e15..61185774ad27 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -52,7 +52,7 @@ class CachePartition CachePartition( const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, - const std::string & dir_path, const size_t file_id, const size_t max_size, const size_t buffer_size = 4 * 1024); + const std::string & dir_path, const size_t file_id, const size_t max_size); ~CachePartition(); @@ -121,7 +121,7 @@ class CachePartition void flush(); - void appendValuesToBufferAttribute(Attribute & to, const Attribute & from); + size_t appendValuesToAttribute(Attribute & to, const Attribute & from); template void getValueFromMemory( @@ -131,9 +131,12 @@ class CachePartition void getValueFromStorage( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + template + void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; + size_t file_id; size_t max_size; - size_t buffer_size; + //size_t buffer_size; std::string path; //mutable std::shared_mutex rw_lock; @@ -164,9 +167,12 @@ class CachePartition Attribute keys_buffer; Attributes attributes_buffer; //MutableColumns buffer; - size_t bytes = 0; - size_t current_block_id = 0; - size_t current_address_in_block = 0; + + DB::Memory<> memory; + std::optional write_buffer; + + size_t current_memory_block_id = 0; + size_t current_file_block_id = 0; mutable std::atomic element_count{0}; }; From dbb565f34a8fff82c6249727092062893db9fd4d Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 7 Jan 2020 17:59:03 +0300 Subject: [PATCH 0011/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 128 +++++++++++-------- dbms/src/Dictionaries/SSDCacheDictionary.h | 2 - 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 7b881fc6415b..af958207b57b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "DictionaryFactory.h" #include @@ -29,11 +30,14 @@ namespace ProfileEvents extern const Event DictCacheLockWriteNs; extern const Event DictCacheLockReadNs; extern const Event FileOpen; + extern const Event WriteBufferAIOWrite; + extern const Event WriteBufferAIOWriteBytes; } namespace CurrentMetrics { extern const Metric DictCacheRequests; + extern const Metric Write; } namespace DB @@ -50,6 +54,8 @@ namespace ErrorCodes extern const int CANNOT_OPEN_FILE; extern const int CANNOT_IO_SUBMIT; extern const int CANNOT_IO_GETEVENTS; + extern const int AIO_WRITE_ERROR; + extern const int CANNOT_FSYNC; } namespace @@ -57,7 +63,7 @@ namespace constexpr size_t MAX_KEYS_TO_READ_ONCE = 128; constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; - constexpr size_t MAX_ATTRIBUTES_SIZE = 1024; + //constexpr size_t MAX_ATTRIBUTES_SIZE = 1024; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -177,7 +183,7 @@ CachePartition::CachePartition( ProfileEvents::increment(ProfileEvents::FileOpen); const std::string filename = path + BIN_FILE_EXT; - read_fd = ::open(filename.c_str(), O_RDONLY | O_DIRECT); + read_fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, 0666); if (read_fd == -1) { auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE; @@ -201,7 +207,7 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & appendValuesToAttribute(keys_buffer, new_keys); if (!write_buffer) - write_buffer.emplace(memory.data(), memory.size()); + write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); for (size_t index = 0; index < ids.size();) { @@ -213,13 +219,15 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & for (const auto & attribute : new_attributes) { // TODO:: переделать через столбцы + getDataAt - switch (attribute.type) { + switch (attribute.type) + { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ if (sizeof(TYPE) > write_buffer->available()) \ { \ flush(); \ + write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); \ continue; \ } \ else \ @@ -251,6 +259,9 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & break; } } + + flush(); + write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); ++index; } } @@ -296,73 +307,80 @@ size_t CachePartition::appendValuesToAttribute(Attribute & to, const Attribute & void CachePartition::flush() { - if (!write_data_buffer) - { - //write_data_buffer = std::make_unique(path + BIN_FILE_EXT, buffer_size, O_RDWR | O_CREAT | O_TRUNC); - // TODO: не перетирать + seek в конец файла - } - + write_buffer.reset(); const auto & ids = std::get>(keys_buffer.values); if (ids.empty()) return; Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!!"); - std::vector offsets; + AIOContext aio_context{1}; - size_t prev_size = 0; - for (size_t row = 0; row < ids.size(); ++row) - { - offsets.push_back((offsets.empty() ? write_data_buffer->getPositionInFile() : offsets.back()) + prev_size); - prev_size = 0; + iocb write_request; + memset(&write_request, 0, sizeof(write_request)); + iocb * write_request_ptr{&write_request}; - for (size_t col = 0; col < attributes_buffer.size(); ++col) - { - const auto & attribute = attributes_buffer[col]; +#if defined(__FreeBSD__) + write_request.aio.aio_lio_opcode = LIO_WRITE; + write_request.aio.aio_fildes = fd; + write_request.aio.aio_buf = reinterpret_cast(buffer_begin); + write_request.aio.aio_nbytes = region_aligned_size; + write_request.aio.aio_offset = region_aligned_begin; +#else + write_request.aio_lio_opcode = IOCB_CMD_PWRITE; + write_request.aio_fildes = read_fd; + write_request.aio_buf = reinterpret_cast(memory.data()); + write_request.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; + write_request.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE * current_file_block_id; +#endif - switch (attribute.type) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - { \ - const auto & values = std::get>(attribute.values); \ - writeBinary(values[row], *static_cast(write_data_buffer.get())); \ - } \ - break; + Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH + while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) + { + if (errno != EINTR) + throw Exception("Cannot submit request for asynchronous IO on file " + path + BIN_FILE_EXT, ErrorCodes::CANNOT_IO_SUBMIT); + } - case AttributeUnderlyingType::utString: - // TODO: string support - break; - } - } + CurrentMetrics::Increment metric_increment_write{CurrentMetrics::Write}; + + io_event event; + while (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) < 0) + { + if (errno != EINTR) + throw Exception("Failed to wait for asynchronous IO completion on file " + path + BIN_FILE_EXT, ErrorCodes::CANNOT_IO_GETEVENTS); } - write_data_buffer->sync(); + + // Unpoison the memory returned from an uninstrumented system function. + __msan_unpoison(&event, sizeof(event)); + + ssize_t bytes_written; +#if defined(__FreeBSD__) + bytes_written = aio_return(reinterpret_cast(event.udata)); +#else + bytes_written = event.res; +#endif + + ProfileEvents::increment(ProfileEvents::WriteBufferAIOWrite); + ProfileEvents::increment(ProfileEvents::WriteBufferAIOWriteBytes, bytes_written); + + if (bytes_written != static_cast(write_request.aio_nbytes)) + throw Exception("Not all data was written for asynchronous IO on file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(bytes_written), ErrorCodes::AIO_WRITE_ERROR); + + int res = ::fsync(read_fd); + if (res == -1) + throwFromErrnoWithPath("Cannot fsync " + path, path, ErrorCodes::CANNOT_FSYNC); /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) { key_to_metadata[ids[row]].index.setInMemory(false); key_to_metadata[ids[row]].index.setBlockId(current_file_block_id); - key_to_metadata[ids[row]].index.setAddressInBlock(offsets[row]); Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_metadata[ids[row]].index.index)); } + ++current_file_block_id; + /// clear buffer std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); for (auto & attribute : attributes_buffer) @@ -409,7 +427,7 @@ void CachePartition::getValueFromMemory( Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(i) + " --- " + std::to_string(offset)); - ReadBufferFromMemory read_buffer(memory.data() + offset, memory.size() - offset); + ReadBufferFromMemory read_buffer(memory.data() + offset, SSD_BLOCK_SIZE - offset); readValueFromBuffer(attribute_index, out[i], read_buffer); } } @@ -431,7 +449,7 @@ void CachePartition::getValueFromStorage( std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(MAX_ATTRIBUTES_SIZE * index_to_out.size(), BUFFER_ALIGNMENT); + DB::Memory read_buffer(SSD_BLOCK_SIZE * index_to_out.size(), BUFFER_ALIGNMENT); std::vector requests(index_to_out.size()); memset(requests.data(), 0, requests.size() * sizeof(requests.front())); @@ -448,8 +466,8 @@ void CachePartition::getValueFromStorage( #else requests[i].aio_lio_opcode = IOCB_CMD_PREAD; requests[i].aio_fildes = read_fd; - requests[i].aio_buf = reinterpret_cast(read_buffer.data()) + i * MAX_ATTRIBUTES_SIZE; - requests[i].aio_nbytes = MAX_ATTRIBUTES_SIZE; + requests[i].aio_buf = reinterpret_cast(read_buffer.data()) + i * SSD_BLOCK_SIZE; + requests[i].aio_nbytes = SSD_BLOCK_SIZE; requests[i].aio_offset = index_to_out[i].first; requests[i].aio_data = i; #endif @@ -499,7 +517,7 @@ void CachePartition::getValueFromStorage( Poco::Logger::get("Read:").information("ito: f:" + std::to_string(index_to_out[event.data].first) + " s:" + std::to_string(index_to_out[event.data].second)); Poco::Logger::get("Read:").information("data: " + std::to_string(event.data) + " res: " + std::to_string(event.res)); - DB::ReadBufferFromMemory buf(read_buffer.data() + event.data * MAX_ATTRIBUTES_SIZE, event.res); + DB::ReadBufferFromMemory buf(read_buffer.data() + event.data * SSD_BLOCK_SIZE, event.res); readValueFromBuffer(attribute_index, out[index_to_out[event.data].second], buf); } } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 61185774ad27..c851905b821e 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -143,8 +143,6 @@ class CachePartition //int index_fd; mutable int read_fd = -1; - std::unique_ptr write_data_buffer; - struct KeyMetadata final { using time_point_t = std::chrono::system_clock::time_point; From 2e10fe5878aff2774055b5db9cb4f90180d10c38 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 7 Jan 2020 20:55:32 +0300 Subject: [PATCH 0012/1102] aio read --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 149 +++++++++++-------- dbms/src/Dictionaries/SSDCacheDictionary.h | 4 +- 2 files changed, 86 insertions(+), 67 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index af958207b57b..857b93fbc0f6 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace ProfileEvents { @@ -60,10 +61,10 @@ namespace ErrorCodes namespace { - constexpr size_t MAX_KEYS_TO_READ_ONCE = 128; constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; - //constexpr size_t MAX_ATTRIBUTES_SIZE = 1024; + + constexpr size_t MAX_BLOCKS_TO_KEEP_IN_MEMORY = 16; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -183,8 +184,8 @@ CachePartition::CachePartition( ProfileEvents::increment(ProfileEvents::FileOpen); const std::string filename = path + BIN_FILE_EXT; - read_fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, 0666); - if (read_fd == -1) + fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, 0666); + if (fd == -1) { auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE; throwFromErrnoWithPath("Cannot open file " + filename, filename, error_code); @@ -194,7 +195,7 @@ CachePartition::CachePartition( CachePartition::~CachePartition() { - ::close(read_fd); + ::close(fd); } void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes) @@ -260,8 +261,6 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & } } - flush(); - write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); ++index; } } @@ -316,19 +315,18 @@ void CachePartition::flush() AIOContext aio_context{1}; - iocb write_request; - memset(&write_request, 0, sizeof(write_request)); + iocb write_request{}; iocb * write_request_ptr{&write_request}; #if defined(__FreeBSD__) write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; - write_request.aio.aio_buf = reinterpret_cast(buffer_begin); - write_request.aio.aio_nbytes = region_aligned_size; - write_request.aio.aio_offset = region_aligned_begin; + write_request.aio.aio_buf = reinterpret_cast(memory.data()); + write_request.aio.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; + write_request.aio.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; - write_request.aio_fildes = read_fd; + write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory.data()); write_request.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; write_request.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE * current_file_block_id; @@ -367,9 +365,8 @@ void CachePartition::flush() if (bytes_written != static_cast(write_request.aio_nbytes)) throw Exception("Not all data was written for asynchronous IO on file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(bytes_written), ErrorCodes::AIO_WRITE_ERROR); - int res = ::fsync(read_fd); - if (res == -1) - throwFromErrnoWithPath("Cannot fsync " + path, path, ErrorCodes::CANNOT_FSYNC); + if (::fsync(fd) < 0) + throwFromErrnoWithPath("Cannot fsync " + path + BIN_FILE_EXT, path + BIN_FILE_EXT, ErrorCodes::CANNOT_FSYNC); /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) @@ -417,7 +414,6 @@ template void CachePartition::getValueFromMemory( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { - //const auto & attribute = std::get>(attributes_buffer[attribute_index].values); for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; @@ -437,88 +433,113 @@ template void CachePartition::getValueFromStorage( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const { - std::vector> index_to_out; + std::vector> index_to_out; for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; if (index.exists() && !index.inMemory()) - index_to_out.emplace_back(index.getAddressInBlock(), i); + index_to_out.emplace_back(index, i); } if (index_to_out.empty()) return; + /// sort by (block_id, offset_in_block) std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(SSD_BLOCK_SIZE * index_to_out.size(), BUFFER_ALIGNMENT); + DB::Memory read_buffer(SSD_BLOCK_SIZE * MAX_BLOCKS_TO_KEEP_IN_MEMORY, BUFFER_ALIGNMENT); - std::vector requests(index_to_out.size()); - memset(requests.data(), 0, requests.size() * sizeof(requests.front())); - std::vector pointers(index_to_out.size()); + std::vector requests; + std::vector pointers; + std::vector> blocks_to_indices; + requests.reserve(index_to_out.size()); + pointers.reserve(index_to_out.size()); + blocks_to_indices.reserve(index_to_out.size()); for (size_t i = 0; i < index_to_out.size(); ++i) { + if (!requests.empty() && + static_cast(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE) + { + blocks_to_indices.back().push_back(i); + continue; + } + + iocb request{}; #if defined(__FreeBSD__) request.aio.aio_lio_opcode = LIO_READ; - request.aio.aio_fildes = read_fd; - request.aio.aio_buf = reinterpret_cast(read_buffer.data() + i * MAX_ATTRIBUTES_SIZE); - request.aio.aio_nbytes = MAX_ATTRIBUTES_SIZE; + request.aio.aio_fildes = fd; + request.aio.aio_buf = reinterpret_cast( + reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY)); + request.aio.aio_nbytes = SSD_BLOCK_SIZE; request.aio.aio_offset = index_to_out[i].first; request.aio_data = i; #else - requests[i].aio_lio_opcode = IOCB_CMD_PREAD; - requests[i].aio_fildes = read_fd; - requests[i].aio_buf = reinterpret_cast(read_buffer.data()) + i * SSD_BLOCK_SIZE; - requests[i].aio_nbytes = SSD_BLOCK_SIZE; - requests[i].aio_offset = index_to_out[i].first; - requests[i].aio_data = i; + request.aio_lio_opcode = IOCB_CMD_PREAD; + request.aio_fildes = fd; + request.aio_buf = reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY); + request.aio_nbytes = SSD_BLOCK_SIZE; + request.aio_offset = index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE; + request.aio_data = i; #endif + requests.push_back(request); + pointers.push_back(&requests.back()); - Poco::Logger::get("requests:").information(); - pointers[i] = &requests[i]; + blocks_to_indices.emplace_back(); + blocks_to_indices.back().push_back(i); } - Poco::Logger::get("requests:").information(std::to_string(requests.size())); - //const auto pointers = ext::map( - // std::begin(requests), std::end(requests), [](const iocb & request) { return &request; }); + Poco::Logger::get("requests:").information(std::to_string(requests.size())); - AIOContext context(MAX_KEYS_TO_READ_ONCE); + AIOContext aio_context(MAX_BLOCKS_TO_KEEP_IN_MEMORY); - std::vector events(index_to_out.size()); + std::vector processed(requests.size(), false); + std::vector events(requests.size()); - for (size_t i = 0; i < index_to_out.size(); i += MAX_KEYS_TO_READ_ONCE) + size_t to_push = 0; + size_t to_pop = 0; + while (to_pop < requests.size()) { - size_t to_push = std::min(MAX_KEYS_TO_READ_ONCE, index_to_out.size() - i); - size_t push_index = i; - int pushed = 0; - while (to_push > 0 && (pushed = io_submit(context.ctx, to_push, pointers.data() + push_index)) < 0) + /// get io tasks from previous iteration + size_t popped = 0; + while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) < 0) { if (errno != EINTR) throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); - to_push -= pushed; - push_index += pushed; - pushed = 0; } - size_t to_get = std::min(MAX_KEYS_TO_READ_ONCE, index_to_out.size() - i); - size_t got_index = i; - int got = 0; - while (to_get > 0 && (got = io_getevents(context.ctx, to_get, to_get, events.data() + got_index, NULL)) < 0) + for (size_t i = to_pop; i < to_pop + popped; ++i) { - if (errno != EINTR) - throwFromErrno("io_getevents: Failed to get an event from asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); - to_get -= got; - got_index += got; - got = 0; + const auto request_id = events[i].data; + const auto & request = requests[request_id]; + if (events[i].res != static_cast(request.aio_nbytes)) + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(events[i].res), ErrorCodes::AIO_WRITE_ERROR); + + for (const size_t idx : blocks_to_indices[request_id]) + { + const auto & [file_index, out_index] = index_to_out[idx]; + DB::ReadBufferFromMemory buf( + reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), + SSD_BLOCK_SIZE - file_index.getAddressInBlock()); + readValueFromBuffer(attribute_index, out[out_index], buf); + + Poco::Logger::get("kek").information(std::to_string(file_index.getAddressInBlock()) + " " + std::to_string(file_index.getBlockId())); + } + + processed[request_id] = true; } - } - //std::sort(std::begin(events), std::end(events), [](const auto & lhs, const auto & rhs) { return lhs.data < rhs.data; }); - for (const auto & event : events) - { - Poco::Logger::get("Read:").information("ito: f:" + std::to_string(index_to_out[event.data].first) + " s:" + std::to_string(index_to_out[event.data].second)); - Poco::Logger::get("Read:").information("data: " + std::to_string(event.data) + " res: " + std::to_string(event.res)); + while (to_pop < requests.size() && processed[to_pop]) + ++to_pop; - DB::ReadBufferFromMemory buf(read_buffer.data() + event.data * SSD_BLOCK_SIZE, event.res); - readValueFromBuffer(attribute_index, out[index_to_out[event.data].second], buf); + /// add new io tasks + const size_t new_tasks_count = std::min(MAX_BLOCKS_TO_KEEP_IN_MEMORY - (to_push - to_pop), requests.size() - to_push); + + size_t pushed = 0; + while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) + { + if (errno != EINTR) + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + } + to_push += pushed; } } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index c851905b821e..5f8017158e60 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -140,8 +140,7 @@ class CachePartition std::string path; //mutable std::shared_mutex rw_lock; - //int index_fd; - mutable int read_fd = -1; + int fd = -1; struct KeyMetadata final { @@ -164,7 +163,6 @@ class CachePartition Attribute keys_buffer; Attributes attributes_buffer; - //MutableColumns buffer; DB::Memory<> memory; std::optional write_buffer; From 297b8aa7ab2778d4e48578070810059e4d0e784f Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 7 Jan 2020 22:18:24 +0300 Subject: [PATCH 0013/1102] fix read --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 31 +++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 857b93fbc0f6..a237a438b3b8 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -204,8 +204,9 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; const auto & ids = std::get>(new_keys.values); + auto & ids_buffer = std::get>(keys_buffer.values); - appendValuesToAttribute(keys_buffer, new_keys); + //appendValuesToAttribute(keys_buffer, new_keys); if (!write_buffer) write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); @@ -217,6 +218,8 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & key_index.setBlockId(current_memory_block_id); key_index.setAddressInBlock(write_buffer->offset()); + bool flushed = false; + for (const auto & attribute : new_attributes) { // TODO:: переделать через столбцы + getDataAt @@ -228,7 +231,7 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & if (sizeof(TYPE) > write_buffer->available()) \ { \ flush(); \ - write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); \ + flushed = true; \ continue; \ } \ else \ @@ -261,7 +264,13 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & } } - ++index; + if (!flushed) + { + ids_buffer.push_back(ids[index]); + ++index; + } + else + write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); } } @@ -388,26 +397,26 @@ template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found) const { + Poco::Logger::get("IDS:").information(std::to_string(ids.size())); PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { auto it = key_to_metadata.find(ids[i]); if (it == std::end(key_to_metadata)) // TODO: check expired { - Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i])); indices[i].setNotExists(); not_found[ids[i]].push_back(i); + Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i]) + " " + std::to_string(indices[i].index)); } else { - Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i])); indices[i] = it->second.index; + Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i]) + " " + std::to_string(indices[i].index)); } - - - getValueFromMemory(attribute_index, indices, out); - getValueFromStorage(attribute_index, indices, out); } + + getValueFromMemory(attribute_index, indices, out); + getValueFromStorage(attribute_index, indices, out); } template @@ -442,6 +451,8 @@ void CachePartition::getValueFromStorage( } if (index_to_out.empty()) return; + for (const auto & [index1, index2] : index_to_out) + Poco::Logger::get("FROM STORAGE:").information(std::to_string(index2) + " ## " + std::to_string(index1.getBlockId()) + " " + std::to_string(index1.getAddressInBlock())); /// sort by (block_id, offset_in_block) std::sort(std::begin(index_to_out), std::end(index_to_out)); @@ -478,7 +489,7 @@ void CachePartition::getValueFromStorage( request.aio_buf = reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY); request.aio_nbytes = SSD_BLOCK_SIZE; request.aio_offset = index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE; - request.aio_data = i; + request.aio_data = requests.size(); #endif requests.push_back(request); pointers.push_back(&requests.back()); From 05622f2bee73b269e20f469809edb36d1c91da2e Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 15:40:29 +0300 Subject: [PATCH 0014/1102] has and ttl --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 121 +++++++++++++----- dbms/src/Dictionaries/SSDCacheDictionary.h | 62 +++++---- .../Functions/FunctionsExternalDictionaries.h | 1 + 3 files changed, 132 insertions(+), 52 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index a237a438b3b8..6ca85ff23e71 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -82,20 +82,20 @@ namespace const std::string IND_FILE_EXT = ".idx"; } -CachePartition::KeyMetadata::time_point_t CachePartition::KeyMetadata::expiresAt() const +CachePartition::Metadata::time_point_t CachePartition::Metadata::expiresAt() const { return ext::safe_bit_cast(data & KEY_METADATA_EXPIRES_AT_MASK); } -void CachePartition::KeyMetadata::setExpiresAt(const time_point_t & t) +void CachePartition::Metadata::setExpiresAt(const time_point_t & t) { data = ext::safe_bit_cast(t); } -bool CachePartition::KeyMetadata::isDefault() const +bool CachePartition::Metadata::isDefault() const { return (data & KEY_METADATA_IS_DEFAULT_MASK) == KEY_METADATA_IS_DEFAULT_MASK; } -void CachePartition::KeyMetadata::setDefault() +void CachePartition::Metadata::setDefault() { data |= KEY_METADATA_IS_DEFAULT_MASK; } @@ -198,7 +198,7 @@ CachePartition::~CachePartition() ::close(fd); } -void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes) +void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata) { if (new_attributes.size() != attributes_buffer.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; @@ -206,17 +206,16 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & const auto & ids = std::get>(new_keys.values); auto & ids_buffer = std::get>(keys_buffer.values); - //appendValuesToAttribute(keys_buffer, new_keys); - if (!write_buffer) write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); for (size_t index = 0; index < ids.size();) { - auto & key_index = key_to_metadata[ids[index]].index; - key_index.setInMemory(true); - key_index.setBlockId(current_memory_block_id); - key_index.setAddressInBlock(write_buffer->offset()); + auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; + index_and_metadata.index.setInMemory(true); + index_and_metadata.index.setBlockId(current_memory_block_id); + index_and_metadata.index.setAddressInBlock(write_buffer->offset()); + index_and_metadata.metadata = metadata[index]; bool flushed = false; @@ -332,7 +331,7 @@ void CachePartition::flush() write_request.aio.aio_fildes = fd; write_request.aio.aio_buf = reinterpret_cast(memory.data()); write_request.aio.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; - write_request.aio.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE; + write_request.aio.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE * current_file_block_id; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; @@ -380,9 +379,9 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) { - key_to_metadata[ids[row]].index.setInMemory(false); - key_to_metadata[ids[row]].index.setBlockId(current_file_block_id); - Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_metadata[ids[row]].index.index)); + key_to_index_and_metadata[ids[row]].index.setInMemory(false); + key_to_index_and_metadata[ids[row]].index.setBlockId(current_file_block_id); + Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_index_and_metadata[ids[row]].index.index)); } ++current_file_block_id; @@ -395,23 +394,28 @@ void CachePartition::flush() template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::unordered_map> & not_found) const + ResultArrayType & out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const { Poco::Logger::get("IDS:").information(std::to_string(ids.size())); PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { - auto it = key_to_metadata.find(ids[i]); - if (it == std::end(key_to_metadata)) // TODO: check expired + auto it = key_to_index_and_metadata.find(ids[i]); + if (it == std::end(key_to_index_and_metadata)) + { + indices[i].setNotExists(); + not_found[ids[i]].push_back(i); + } + else if (it->second.metadata.expiresAt() <= now) { indices[i].setNotExists(); not_found[ids[i]].push_back(i); - Poco::Logger::get("part:").information("NOT FOUND " + std::to_string(ids[i]) + " " + std::to_string(indices[i].index)); + markExpired(it); } else { indices[i] = it->second.index; - Poco::Logger::get("part:").information("HIT " + std::to_string(ids[i]) + " " + std::to_string(indices[i].index)); } } @@ -482,7 +486,7 @@ void CachePartition::getValueFromStorage( reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY)); request.aio.aio_nbytes = SSD_BLOCK_SIZE; request.aio.aio_offset = index_to_out[i].first; - request.aio_data = i; + request.aio_data = requests.size(); #else request.aio_lio_opcode = IOCB_CMD_PREAD; request.aio_fildes = fd; @@ -619,22 +623,38 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst } } -void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out) const +template +void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, + std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { for (size_t i = 0; i < ids.size(); ++i) { - auto it = key_to_metadata.find(ids[i]); - if (it == std::end(key_to_metadata)) + auto it = key_to_index_and_metadata.find(ids[i]); + + if (it == std::end(key_to_index_and_metadata)) { - out[i] = 0; + not_found[ids[i]].push_back(i); + } + else if (it->second.metadata.expiresAt() <= now) + { + not_found[ids[i]].push_back(i); + markExpired(it); } else { - out[i] = it->second.isDefault(); + Poco::Logger::get("not expired").information("expires at " + std::to_string(std::chrono::system_clock::to_time_t(it->second.metadata.expiresAt())) + " now: " + std::to_string(std::chrono::system_clock::to_time_t(now))); + out[i] = !it->second.metadata.isDefault(); } } } +template +void CachePartition::markExpired(const Iterator & it) const +{ + Poco::Logger::get("markExpired").information("expired: " + std::to_string(it->first)); + key_to_index_and_metadata.erase(it); +} + CacheStorage::CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) : dictionary(dictionary_) , path(path_) @@ -689,15 +709,20 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values); + std::vector metadata(ids.size()); + const auto & dict_lifetime = dictionary.getLifetime(); + for (const auto i : ext::range(0, ids.size())) { + std::uniform_int_distribution distribution{dict_lifetime.min_sec, dict_lifetime.max_sec}; + metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); /// mark corresponding id as found on_updated(ids[i], i, new_attributes); remaining_ids[ids[i]] = 1; } /// TODO: Add TTL to block - partitions[0]->appendBlock(new_keys, new_attributes); + partitions[0]->appendBlock(new_keys, new_attributes, metadata); } stream->readSuffix(); @@ -761,6 +786,10 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector metadata; + const auto & dict_lifetime = dictionary.getLifetime(); + for (const auto & id_found_pair : remaining_ids) { if (id_found_pair.second) @@ -785,6 +814,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); + std::uniform_int_distribution distribution{dict_lifetime.min_sec, dict_lifetime.max_sec}; + metadata.emplace_back(); + metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); + metadata.back().setDefault(); + /// Set null_value for each attribute const auto & attributes = dictionary.getAttributes(); for (size_t i = 0; i < attributes.size(); ++i) @@ -827,8 +861,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes); + partitions[0]->appendBlock(new_keys, new_attributes, metadata); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); @@ -1001,8 +1036,10 @@ template void SSDCacheDictionary::getItemsNumberImpl( const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const { + const auto now = std::chrono::system_clock::now(); + std::unordered_map> not_found_ids; - storage.getValue(attribute_index, ids, out, not_found_ids); + storage.getValue(attribute_index, ids, out, not_found_ids, now); if (not_found_ids.empty()) return; @@ -1061,6 +1098,32 @@ void SSDCacheDictionary::getItemsString(const size_t attribute_index, const Padd UNUSED(get_default); } +void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray & out) const +{ + const auto now = std::chrono::system_clock::now(); + + std::unordered_map> not_found_ids; + storage.has(ids, out, not_found_ids, now); + if (not_found_ids.empty()) + return; + + std::vector required_ids(not_found_ids.size()); + std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](const auto & pair) { return pair.first; }); + + storage.update( + source_ptr, + required_ids, + [&](const auto id, const auto, const auto &) { + for (const size_t out_row : not_found_ids[id]) + out[out_row] = true; + }, + [&](const size_t id) + { + for (const size_t row : not_found_ids[id]) + out[row] = false; + }); +} + size_t SSDCacheDictionary::getAttributeIndex(const std::string & attr_name) const { auto it = attribute_index_by_name.find(attr_name); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 5f8017158e60..f77171b20987 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -47,6 +47,22 @@ class CacheStorage; class CachePartition { public: + struct Metadata final + { + using time_point_t = std::chrono::system_clock::time_point; + using time_point_rep_t = time_point_t::rep; + using time_point_urep_t = std::make_unsigned_t; + + time_point_t expiresAt() const; + void setExpiresAt(const time_point_t & t); + + bool isDefault() const; + void setDefault(); + + /// Stores both expiration time and `is_default` flag in the most significant bit + time_point_urep_t data = 0; + }; + using Offset = size_t; using Offsets = std::vector; @@ -61,14 +77,14 @@ class CachePartition template void getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::unordered_map> & not_found) const; + ResultArrayType & out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const; // TODO:: getString - /// 0 -- not found - /// 1 -- good - /// 2 -- expired - void has(const PaddedPODArray & ids, ResultArrayType & out) const; + template + void has(const PaddedPODArray & ids, ResultArrayType & out, + std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; struct Attribute { @@ -96,7 +112,7 @@ class CachePartition using Attributes = std::vector; // Key, (Metadata), attributes - void appendBlock(const Attribute & new_keys, const Attributes & new_attributes); + void appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata); private: struct Index final @@ -134,6 +150,9 @@ class CachePartition template void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; + template + void markExpired(const Iterator & it) const; + size_t file_id; size_t max_size; //size_t buffer_size; @@ -142,24 +161,13 @@ class CachePartition //mutable std::shared_mutex rw_lock; int fd = -1; - struct KeyMetadata final + struct IndexAndMetadata final { - using time_point_t = std::chrono::system_clock::time_point; - using time_point_rep_t = time_point_t::rep; - using time_point_urep_t = std::make_unsigned_t; - - time_point_t expiresAt() const; - void setExpiresAt(const time_point_t & t); - - bool isDefault() const; - void setDefault(); - Index index{}; - /// Stores both expiration time and `is_default` flag in the most significant bit - time_point_urep_t data = 0; + Metadata metadata{}; }; - std::unordered_map key_to_metadata; + mutable std::unordered_map key_to_index_and_metadata; Attribute keys_buffer; Attributes attributes_buffer; @@ -189,13 +197,21 @@ class CacheStorage template void getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::unordered_map> & not_found) const + ResultArrayType & out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const { - partitions[0]->getValue(attribute_index, ids, out, not_found); + partitions[0]->getValue(attribute_index, ids, out, not_found, now); } // getString(); + template + void has(const PaddedPODArray & ids, ResultArrayType & out, + std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const + { + partitions[0]->has(ids, out, not_found, now); + } + template void update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found); @@ -356,7 +372,7 @@ class SSDCacheDictionary final : public IDictionary void getString(const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const; - void has(const PaddedPODArray & /* ids */, PaddedPODArray & /* out */) const override {} // TODO + void has(const PaddedPODArray & ids, PaddedPODArray & out) const override; BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override // TODO { diff --git a/dbms/src/Functions/FunctionsExternalDictionaries.h b/dbms/src/Functions/FunctionsExternalDictionaries.h index 8542bc00f930..746b894e154c 100644 --- a/dbms/src/Functions/FunctionsExternalDictionaries.h +++ b/dbms/src/Functions/FunctionsExternalDictionaries.h @@ -137,6 +137,7 @@ class FunctionDictHas final : public IFunction if (!executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && + !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr)) From ddaf23d4e3885e3466467834d7a26542527e72fe Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 17:09:56 +0300 Subject: [PATCH 0015/1102] test --- .../01053_ssd_dictionary.reference | 17 ++++ .../0_stateless/01053_ssd_dictionary.sql | 78 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference create mode 100644 dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference new file mode 100644 index 000000000000..b85ff6488b3a --- /dev/null +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -0,0 +1,17 @@ +UPDATE DICTIONARY +118 +VALUE FROM DISK +-100 +VALUE FROM RAM BUFFER +8 +VALUES FROM DISK AND RAM BUFFER +118 +VALUES NOT FROM TABLE +0 -1 +DUPLICATE KEYS +1 -100 +2 4 +3 -1 +3 -1 +2 4 +1 -100 diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql new file mode 100644 index 000000000000..f14ce5bdd91d --- /dev/null +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -0,0 +1,78 @@ +SET send_logs_level = 'none'; + +DROP DATABASE IF EXISTS database_for_dict; + +CREATE DATABASE database_for_dict Engine = Ordinary; + +DROP TABLE IF EXISTS database_for_dict.table_for_dict; + +CREATE TABLE database_for_dict.table_for_dict +( + id UInt64, + a UInt64, + b Int32 +) +ENGINE = MergeTree() +ORDER BY id; + +INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100), (2, 3, 4), (5, 6, 7), (10, 9, 8); + +DROP TABLE IF EXISTS database_for_dict.keys_table; + +CREATE TABLE database_for_dict.keys_table +( + id UInt64 +) +ENGINE = MergeTree() +ORDER BY id; + +INSERT INTO database_for_dict.keys_table VALUES (1); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370; +INSERT INTO database_for_dict.keys_table VALUES (2); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370, 370; +INSERT INTO database_for_dict.keys_table VALUES (5); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 700, 370; +INSERT INTO database_for_dict.keys_table VALUES (10); + +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +CREATE DICTIONARY database_for_dict.ssd_dict +( + id UInt64, + a UInt64 DEFAULT 0, + b Int32 DEFAULT -1 +) +PRIMARY KEY id +SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) +LIFETIME(MIN 1000 MAX 2000) +LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/1')); + +SELECT 'UPDATE DICTIONARY'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; + +SELECT 'VALUE FROM DISK'; +-- -100 +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); + +SELECT 'VALUE FROM RAM BUFFER'; +-- 8 +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(10)); + +SELECT 'VALUES FROM DISK AND RAM BUFFER'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; + +SELECT 'VALUES NOT FROM TABLE'; +-- 0 -1 +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)); + + +SELECT 'DUPLICATE KEYS'; +SELECT arrayJoin([1, 2, 3, 3, 2, 1]) AS id, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(id)); + +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +DROP TABLE IF EXISTS database_for_dict.table_for_dict; + +DROP DATABASE IF EXISTS database_for_dict; From 81c9d66c7be81faf1a24b68ee0b28d88952de309 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 17:14:19 +0300 Subject: [PATCH 0016/1102] fix --- dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index f14ce5bdd91d..232f17691a42 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -23,8 +23,7 @@ CREATE TABLE database_for_dict.keys_table ( id UInt64 ) -ENGINE = MergeTree() -ORDER BY id; +ENGINE = StripeLog(); INSERT INTO database_for_dict.keys_table VALUES (1); INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370; From 371c3f819e5aaba9673a34f41af5086e53998481 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 17:21:18 +0300 Subject: [PATCH 0017/1102] read with one thread --- .../01053_ssd_dictionary.reference | 4 ++++ .../0_stateless/01053_ssd_dictionary.sql | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index b85ff6488b3a..d7a2b13a8ce0 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -15,3 +15,7 @@ DUPLICATE KEYS 3 -1 2 4 1 -100 +UPDATE DICTIONARY (max_threads=1) +118 +VALUES FROM DISK AND RAM BUFFER (max_threads=1) +118 diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 232f17691a42..520c11817763 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -66,12 +66,32 @@ SELECT 'VALUES NOT FROM TABLE'; -- 0 -1 SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)); - SELECT 'DUPLICATE KEYS'; SELECT arrayJoin([1, 2, 3, 3, 2, 1]) AS id, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(id)); DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; +CREATE DICTIONARY database_for_dict.ssd_dict +( + id UInt64, + a UInt64 DEFAULT 0, + b Int32 DEFAULT -1 +) +PRIMARY KEY id +SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) +LIFETIME(MIN 1000 MAX 2000) +LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/1')); + +SELECT 'UPDATE DICTIONARY (max_threads=1)'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table SETTINGS max_threads=1; + +SELECT 'VALUES FROM DISK AND RAM BUFFER (max_threads=1)'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table SETTINGS max_threads=1; + +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + DROP TABLE IF EXISTS database_for_dict.table_for_dict; DROP DATABASE IF EXISTS database_for_dict; From 4a65b1b345136045987aca6af4948042822ebe56 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 17:25:58 +0300 Subject: [PATCH 0018/1102] test for dict with MT --- .../01053_ssd_dictionary.reference | 4 +-- .../0_stateless/01053_ssd_dictionary.sql | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index d7a2b13a8ce0..cda8dc267c5f 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -15,7 +15,7 @@ DUPLICATE KEYS 3 -1 2 4 1 -100 -UPDATE DICTIONARY (max_threads=1) +UPDATE DICTIONARY (MT) 118 -VALUES FROM DISK AND RAM BUFFER (max_threads=1) +VALUES FROM DISK AND RAM BUFFER (MT) 118 diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 520c11817763..ce33187c5b01 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -71,6 +71,26 @@ SELECT arrayJoin([1, 2, 3, 3, 2, 1]) AS id, dictGetInt32('database_for_dict.ssd_ DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; +DROP TABLE IF EXISTS database_for_dict.keys_table; + +CREATE TABLE database_for_dict.keys_table +( + id UInt64 +) +ENGINE = MergeTree() +ORDER BY id; + +INSERT INTO database_for_dict.keys_table VALUES (1); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370; +INSERT INTO database_for_dict.keys_table VALUES (2); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370, 370; +INSERT INTO database_for_dict.keys_table VALUES (5); +INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 700, 370; +INSERT INTO database_for_dict.keys_table VALUES (10); + +-- one block +OPTIMIZE TABLE database_for_dict.keys_table; + CREATE DICTIONARY database_for_dict.ssd_dict ( id UInt64, @@ -82,13 +102,13 @@ SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dic LIFETIME(MIN 1000 MAX 2000) LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/1')); -SELECT 'UPDATE DICTIONARY (max_threads=1)'; +SELECT 'UPDATE DICTIONARY (MT)'; -- 118 -SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table SETTINGS max_threads=1; +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; -SELECT 'VALUES FROM DISK AND RAM BUFFER (max_threads=1)'; +SELECT 'VALUES FROM DISK AND RAM BUFFER (MT)'; -- 118 -SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table SETTINGS max_threads=1; +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; From d968bf66b7b86024101780c59c7f1ed044b9e02e Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 20:10:37 +0300 Subject: [PATCH 0019/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 28 +++++++------------ .../0_stateless/01053_ssd_dictionary.sql | 3 -- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 6ca85ff23e71..c598d03c96ad 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -64,7 +64,7 @@ namespace constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; - constexpr size_t MAX_BLOCKS_TO_KEEP_IN_MEMORY = 16; + constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -330,14 +330,14 @@ void CachePartition::flush() write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; write_request.aio.aio_buf = reinterpret_cast(memory.data()); - write_request.aio.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; - write_request.aio.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE * current_file_block_id; + write_request.aio.aio_nbytes = SSD_BLOCK_SIZE; + write_request.aio.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory.data()); - write_request.aio_nbytes = DEFAULT_AIO_FILE_BLOCK_SIZE; - write_request.aio_offset = DEFAULT_AIO_FILE_BLOCK_SIZE * current_file_block_id; + write_request.aio_nbytes = SSD_BLOCK_SIZE; + write_request.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; #endif Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); @@ -381,7 +381,6 @@ void CachePartition::flush() { key_to_index_and_metadata[ids[row]].index.setInMemory(false); key_to_index_and_metadata[ids[row]].index.setBlockId(current_file_block_id); - Poco::Logger::get("INDEX:").information("NEW MAP: " + std::to_string(ids[row]) + " -> " + std::to_string(key_to_index_and_metadata[ids[row]].index.index)); } ++current_file_block_id; @@ -434,8 +433,6 @@ void CachePartition::getValueFromMemory( { const size_t offset = index.getAddressInBlock(); - Poco::Logger::get("part:").information("GET FROM MEMORY " + std::to_string(i) + " --- " + std::to_string(offset)); - ReadBufferFromMemory read_buffer(memory.data() + offset, SSD_BLOCK_SIZE - offset); readValueFromBuffer(attribute_index, out[i], read_buffer); } @@ -455,13 +452,11 @@ void CachePartition::getValueFromStorage( } if (index_to_out.empty()) return; - for (const auto & [index1, index2] : index_to_out) - Poco::Logger::get("FROM STORAGE:").information(std::to_string(index2) + " ## " + std::to_string(index1.getBlockId()) + " " + std::to_string(index1.getAddressInBlock())); /// sort by (block_id, offset_in_block) std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(SSD_BLOCK_SIZE * MAX_BLOCKS_TO_KEEP_IN_MEMORY, BUFFER_ALIGNMENT); + DB::Memory read_buffer(SSD_BLOCK_SIZE * READ_BUFFER_SIZE_BLOCKS, BUFFER_ALIGNMENT); std::vector requests; std::vector pointers; @@ -483,14 +478,14 @@ void CachePartition::getValueFromStorage( request.aio.aio_lio_opcode = LIO_READ; request.aio.aio_fildes = fd; request.aio.aio_buf = reinterpret_cast( - reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY)); + reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS)); request.aio.aio_nbytes = SSD_BLOCK_SIZE; request.aio.aio_offset = index_to_out[i].first; request.aio_data = requests.size(); #else request.aio_lio_opcode = IOCB_CMD_PREAD; request.aio_fildes = fd; - request.aio_buf = reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (i % MAX_BLOCKS_TO_KEEP_IN_MEMORY); + request.aio_buf = reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS); request.aio_nbytes = SSD_BLOCK_SIZE; request.aio_offset = index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE; request.aio_data = requests.size(); @@ -504,7 +499,7 @@ void CachePartition::getValueFromStorage( Poco::Logger::get("requests:").information(std::to_string(requests.size())); - AIOContext aio_context(MAX_BLOCKS_TO_KEEP_IN_MEMORY); + AIOContext aio_context(READ_BUFFER_SIZE_BLOCKS); std::vector processed(requests.size(), false); std::vector events(requests.size()); @@ -535,8 +530,6 @@ void CachePartition::getValueFromStorage( reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), SSD_BLOCK_SIZE - file_index.getAddressInBlock()); readValueFromBuffer(attribute_index, out[out_index], buf); - - Poco::Logger::get("kek").information(std::to_string(file_index.getAddressInBlock()) + " " + std::to_string(file_index.getBlockId())); } processed[request_id] = true; @@ -546,7 +539,7 @@ void CachePartition::getValueFromStorage( ++to_pop; /// add new io tasks - const size_t new_tasks_count = std::min(MAX_BLOCKS_TO_KEEP_IN_MEMORY - (to_push - to_pop), requests.size() - to_push); + const size_t new_tasks_count = std::min(READ_BUFFER_SIZE_BLOCKS - (to_push - to_pop), requests.size() - to_push); size_t pushed = 0; while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) @@ -642,7 +635,6 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayTypesecond.metadata.expiresAt())) + " now: " + std::to_string(std::chrono::system_clock::to_time_t(now))); out[i] = !it->second.metadata.isDefault(); } } diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index ce33187c5b01..e526df908a58 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -88,9 +88,6 @@ INSERT INTO database_for_dict.keys_table VALUES (5); INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 700, 370; INSERT INTO database_for_dict.keys_table VALUES (10); --- one block -OPTIMIZE TABLE database_for_dict.keys_table; - CREATE DICTIONARY database_for_dict.ssd_dict ( id UInt64, From ce29a3cc000aaba6c6e14514afcc3b5dbf983b1e Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 20:52:13 +0300 Subject: [PATCH 0020/1102] remove unused attributes --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 58 +++++--------------- dbms/src/Dictionaries/SSDCacheDictionary.h | 5 +- 2 files changed, 15 insertions(+), 48 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index c598d03c96ad..123149297316 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -61,10 +61,11 @@ namespace ErrorCodes namespace { - constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; - constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры + constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры - constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; + constexpr size_t AIO_MAX_SIMULTANIOUS_REQUESTS = 32; + constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; // TODO: в параметры static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -76,7 +77,7 @@ namespace constexpr size_t INDEX_IN_BLOCK_MASK = (1ULL << INDEX_IN_BLOCK_BITS) - 1; constexpr size_t BLOCK_INDEX_MASK = ((1ULL << (BLOCK_INDEX_BITS + INDEX_IN_BLOCK_BITS)) - 1) ^ INDEX_IN_BLOCK_MASK; - constexpr size_t NOT_FOUND = -1; + constexpr size_t NOT_EXISTS = -1; const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; @@ -107,12 +108,12 @@ bool CachePartition::Index::inMemory() const bool CachePartition::Index::exists() const { - return index != NOT_FOUND; + return index != NOT_EXISTS; } void CachePartition::Index::setNotExists() { - index = NOT_FOUND; + index = NOT_EXISTS; } void CachePartition::Index::setInMemory(const bool in_memory) @@ -141,44 +142,13 @@ void CachePartition::Index::setBlockId(const size_t block_id) } CachePartition::CachePartition( - const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure, + const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure_, const std::string & dir_path, const size_t file_id_, const size_t max_size_) - : file_id(file_id_), max_size(max_size_), path(dir_path + "/" + std::to_string(file_id)), memory(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT) + : file_id(file_id_), max_size(max_size_), path(dir_path + "/" + std::to_string(file_id)) + , attributes_structure(attributes_structure_), memory(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT) { keys_buffer.type = AttributeUnderlyingType::utUInt64; keys_buffer.values = std::vector(); - for (const auto & type : attributes_structure) - { - switch (type) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - attributes_buffer.emplace_back(); \ - attributes_buffer.back().type = type; \ - attributes_buffer.back().values = std::vector(); \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - // TODO: string support - break; - } - } { ProfileEvents::increment(ProfileEvents::FileOpen); @@ -200,7 +170,7 @@ CachePartition::~CachePartition() void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata) { - if (new_attributes.size() != attributes_buffer.size()) + if (new_attributes.size() != attributes_structure.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; const auto & ids = std::get>(new_keys.values); @@ -387,8 +357,6 @@ void CachePartition::flush() /// clear buffer std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); - for (auto & attribute : attributes_buffer) - std::visit([](auto & attr) { attr.clear(); }, attribute.values); } template @@ -556,7 +524,7 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst { for (size_t i = 0; i < attribute_index; ++i) { - switch (attributes_buffer[i].type) + switch (attributes_structure[i]) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ @@ -587,7 +555,7 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst } } - switch (attributes_buffer[attribute_index].type) + switch (attributes_structure[attribute_index]) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index f77171b20987..87d5564d3383 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -155,7 +155,6 @@ class CachePartition size_t file_id; size_t max_size; - //size_t buffer_size; std::string path; //mutable std::shared_mutex rw_lock; @@ -170,7 +169,7 @@ class CachePartition mutable std::unordered_map key_to_index_and_metadata; Attribute keys_buffer; - Attributes attributes_buffer; + const std::vector attributes_structure; DB::Memory<> memory; std::optional write_buffer; @@ -178,7 +177,7 @@ class CachePartition size_t current_memory_block_id = 0; size_t current_file_block_id = 0; - mutable std::atomic element_count{0}; + // mutable std::atomic element_count{0}; }; using CachePartitionPtr = std::unique_ptr; From fc94ffe84e7c15f16db093dd9335cdbfd09efbad Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 8 Jan 2020 22:41:05 +0300 Subject: [PATCH 0021/1102] some refactoring --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 108 ++++++++----------- dbms/src/Dictionaries/SSDCacheDictionary.h | 68 ++++++------ 2 files changed, 76 insertions(+), 100 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 123149297316..dcc8c9fdf340 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -364,7 +364,6 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { - Poco::Logger::get("IDS:").information(std::to_string(ids.size())); PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { @@ -465,8 +464,6 @@ void CachePartition::getValueFromStorage( blocks_to_indices.back().push_back(i); } - Poco::Logger::get("requests:").information(std::to_string(requests.size())); - AIOContext aio_context(READ_BUFFER_SIZE_BLOCKS); std::vector processed(requests.size(), false); @@ -611,30 +608,27 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType void CachePartition::markExpired(const Iterator & it) const { - Poco::Logger::get("markExpired").information("expired: " + std::to_string(it->first)); key_to_index_and_metadata.erase(it); } -CacheStorage::CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) - : dictionary(dictionary_) +CacheStorage::CacheStorage( + const Attributes & attributes_structure_, const std::string & path_, + const size_t partitions_count_, const size_t partition_max_size_) + : attributes_structure(attributes_structure_) , path(path_) , partition_max_size(partition_max_size_) , log(&Poco::Logger::get("CacheStorage")) { - std::vector structure; - for (const auto & item : dictionary.getStructure().attributes) - { - structure.push_back(item.underlying_type); - } for (size_t partition_id = 0; partition_id < partitions_count_; ++partition_id) - partitions.emplace_back(std::make_unique(AttributeUnderlyingType::utUInt64, structure, path_, partition_id, partition_max_size)); + partitions.emplace_back(std::make_unique(AttributeUnderlyingType::utUInt64, + attributes_structure, path_, partition_id, partition_max_size)); } template void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, - PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found) + PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, + const DictionaryLifetime lifetime, const std::vector & null_values) { - Poco::Logger::get("cachestorage").information("update"); CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; ProfileEvents::increment(ProfileEvents::DictCacheKeysRequested, requested_ids.size()); @@ -664,17 +658,15 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorread()) { const auto new_keys = createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front(); - const auto new_attributes = createAttributesFromBlock( - block, 1, ext::map(dictionary.getAttributes(), [](const auto & attribute) { return attribute.type; })); + const auto new_attributes = createAttributesFromBlock(block, 1, attributes_structure); const auto & ids = std::get>(new_keys.values); std::vector metadata(ids.size()); - const auto & dict_lifetime = dictionary.getLifetime(); for (const auto i : ext::range(0, ids.size())) { - std::uniform_int_distribution distribution{dict_lifetime.min_sec, dict_lifetime.max_sec}; + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); /// mark corresponding id as found on_updated(ids[i], i, new_attributes); @@ -699,8 +691,8 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); \ break; @@ -748,7 +740,6 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector metadata; - const auto & dict_lifetime = dictionary.getLifetime(); for (const auto & id_found_pair : remaining_ids) { @@ -774,24 +765,23 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); - std::uniform_int_distribution distribution{dict_lifetime.min_sec, dict_lifetime.max_sec}; + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata.emplace_back(); metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); metadata.back().setDefault(); /// Set null_value for each attribute - const auto & attributes = dictionary.getAttributes(); - for (size_t i = 0; i < attributes.size(); ++i) + for (size_t i = 0; i < attributes_structure.size(); ++i) { - const auto & attribute = attributes[i]; + const auto & attribute = attributes_structure[i]; // append null - switch (attribute.type) + switch (attribute) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ auto & to_values = std::get>(new_attributes[i].values); \ - auto & null_value = std::get(attribute.null_value); \ + auto & null_value = std::get(null_values[i]); \ to_values.push_back(null_value); \ } \ break; @@ -891,7 +881,8 @@ SSDCacheDictionary::SSDCacheDictionary( , dict_lifetime(dict_lifetime_) , path(path_) , partition_max_size(partition_max_size_) - , storage(*this, path, 1, partition_max_size) + , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), + path, 1, partition_max_size) , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) @@ -906,7 +897,7 @@ SSDCacheDictionary::SSDCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ - const auto null_value = std::get(attributes[index].null_value); \ + const auto null_value = std::get(null_values[index]); \ getItemsNumberImpl( \ index, \ ids, \ @@ -1017,15 +1008,17 @@ void SSDCacheDictionary::getItemsNumberImpl( { for (const size_t row : not_found_ids[id]) out[row] = get_default(row); - }); + }, + getLifetime(), + null_values); } void SSDCacheDictionary::getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const { const auto index = getAttributeIndex(attribute_name); - checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); - const auto null_value = StringRef{std::get(attributes[index].null_value)}; + const auto null_value = StringRef{std::get(null_values[index])}; getItemsString(index, ids, out, [&](const size_t) { return null_value; }); } @@ -1034,7 +1027,7 @@ void SSDCacheDictionary::getString( const std::string & attribute_name, const PaddedPODArray & ids, const ColumnString * const def, ColumnString * const out) const { const auto index = getAttributeIndex(attribute_name); - checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); getItemsString(index, ids, out, [&](const size_t row) { return def->getDataAt(row); }); } @@ -1043,7 +1036,7 @@ void SSDCacheDictionary::getString( const std::string & attribute_name, const PaddedPODArray & ids, const String & def, ColumnString * const out) const { const auto index = getAttributeIndex(attribute_name); - checkAttributeType(name, attribute_name, attributes[index].type, AttributeUnderlyingType::utString); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); getItemsString(index, ids, out, [&](const size_t) { return StringRef{def}; }); } @@ -1081,7 +1074,9 @@ void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArraysecond; } -SSDCacheDictionary::Attribute & SSDCacheDictionary::getAttribute(const std::string & attr_name) -{ - return attributes[getAttributeIndex(attr_name)]; -} - -const SSDCacheDictionary::Attribute & SSDCacheDictionary::getAttribute(const std::string & attr_name) const -{ - return attributes[getAttributeIndex(attr_name)]; -} - -const SSDCacheDictionary::Attributes & SSDCacheDictionary::getAttributes() const -{ - return attributes; -} - template -SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value) +AttributeValueVariant SSDCacheDictionary::createAttributeNullValueWithTypeImpl(const Field & null_value) { - Attribute attr{type, {}}; - attr.null_value = static_cast(null_value.get>()); + AttributeValueVariant var_null_value = static_cast(null_value.get>()); bytes_allocated += sizeof(T); - return attr; + return var_null_value; } template <> -SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value) +AttributeValueVariant SSDCacheDictionary::createAttributeNullValueWithTypeImpl(const Field & null_value) { - Attribute attr{type, {}}; - attr.null_value = null_value.get(); + AttributeValueVariant var_null_value = null_value.get(); bytes_allocated += sizeof(StringRef); //if (!string_arena) // string_arena = std::make_unique(); - return attr; + return var_null_value; } -SSDCacheDictionary::Attribute SSDCacheDictionary::createAttributeWithType(const AttributeUnderlyingType type, const Field & null_value) +AttributeValueVariant SSDCacheDictionary::createAttributeNullValueWithType(const AttributeUnderlyingType type, const Field & null_value) { switch (type) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - return createAttributeWithTypeImpl(type, null_value); + return createAttributeNullValueWithTypeImpl(null_value); DISPATCH(UInt8) DISPATCH(UInt16) @@ -1157,13 +1135,13 @@ case AttributeUnderlyingType::ut##TYPE: \ void SSDCacheDictionary::createAttributes() { - attributes.reserve(dict_struct.attributes.size()); + null_values.reserve(dict_struct.attributes.size()); for (size_t i = 0; i < dict_struct.attributes.size(); ++i) { const auto & attribute = dict_struct.attributes[i]; attribute_index_by_name.emplace(attribute.name, i); - attributes.push_back(createAttributeWithType(attribute.underlying_type, attribute.null_value)); + null_values.push_back(createAttributeNullValueWithType(attribute.underlying_type, attribute.null_value)); if (attribute.hierarchical) throw Exception{name + ": hierarchical attributes not supported for dictionary of type " + getTypeName(), diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 87d5564d3383..07f5f511b6c1 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -44,6 +44,23 @@ class CacheStorage; ssize_t readString(const String & str, WriteBuffer & buffer); };*/ +using AttributeValueVariant = std::variant< + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Int8, + Int16, + Int32, + Int64, + Decimal32, + Decimal64, + Decimal128, + Float32, + Float64, + String>; + class CachePartition { public: @@ -186,9 +203,10 @@ using CachePartitionPtr = std::unique_ptr; class CacheStorage { public: + using Attributes = std::vector; using Key = IDictionary::Key; - CacheStorage(SSDCacheDictionary & dictionary_, const std::string & path_, + CacheStorage(const Attributes & attributes_structure_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_); template @@ -213,7 +231,8 @@ class CacheStorage template void update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, - PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found); + PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, + const DictionaryLifetime lifetime, const std::vector & null_values); //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; @@ -225,10 +244,8 @@ class CacheStorage CachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); - SSDCacheDictionary & dictionary; + const Attributes attributes_structure; - // Block structure: Key, (Default + TTL), Attr1, Attr2, ... - // const Block header; const std::string path; const size_t partition_max_size; std::vector partitions; @@ -244,6 +261,8 @@ class CacheStorage mutable std::chrono::system_clock::time_point backoff_end_time; // stats + mutable size_t bytes_allocated = 0; + mutable std::atomic element_count{0}; mutable std::atomic hit_count{0}; mutable std::atomic query_count{0}; @@ -380,39 +399,18 @@ class SSDCacheDictionary final : public IDictionary return nullptr; } +private: + size_t getAttributeIndex(const std::string & attr_name) const; + struct Attribute { AttributeUnderlyingType type; - std::variant< - UInt8, - UInt16, - UInt32, - UInt64, - UInt128, - Int8, - Int16, - Int32, - Int64, - Decimal32, - Decimal64, - Decimal128, - Float32, - Float64, - String> null_value; + AttributeValueVariant null_value; }; - using Attributes = std::vector; - - /// переместить - const Attributes & getAttributes() const; - -private: - size_t getAttributeIndex(const std::string & attr_name) const; - Attribute & getAttribute(const std::string & attr_name); - const Attribute & getAttribute(const std::string & attr_name) const; template - Attribute createAttributeWithTypeImpl(const AttributeUnderlyingType type, const Field & null_value); - Attribute createAttributeWithType(const AttributeUnderlyingType type, const Field & null_value); + AttributeValueVariant createAttributeNullValueWithTypeImpl(const Field & null_value); + AttributeValueVariant createAttributeNullValueWithType(const AttributeUnderlyingType type, const Field & null_value); void createAttributes(); template @@ -429,11 +427,11 @@ class SSDCacheDictionary final : public IDictionary const std::string path; const size_t partition_max_size; - mutable CacheStorage storage; - Logger * const log; std::map attribute_index_by_name; - Attributes attributes; // TODO: move to storage + std::vector null_values; + mutable CacheStorage storage; + Logger * const log; mutable size_t bytes_allocated = 0; mutable std::atomic element_count{0}; From 07ffe96932c04e33e77b912d6d28d6e4543ede7b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 9 Jan 2020 22:34:03 +0300 Subject: [PATCH 0022/1102] locks --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 16 +++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 37 ++++---------------- 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index dcc8c9fdf340..9aba99157a38 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -165,11 +165,13 @@ CachePartition::CachePartition( CachePartition::~CachePartition() { + std::unique_lock lock(rw_lock); ::close(fd); } void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata) { + std::unique_lock lock(rw_lock); if (new_attributes.size() != attributes_structure.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; @@ -364,6 +366,7 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { + std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { @@ -377,7 +380,6 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray { indices[i].setNotExists(); not_found[ids[i]].push_back(i); - markExpired(it); } else { @@ -459,7 +461,6 @@ void CachePartition::getValueFromStorage( #endif requests.push_back(request); pointers.push_back(&requests.back()); - blocks_to_indices.emplace_back(); blocks_to_indices.back().push_back(i); } @@ -585,6 +586,7 @@ template void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { + std::shared_lock lock(rw_lock); for (size_t i = 0; i < ids.size(); ++i) { auto it = key_to_index_and_metadata.find(ids[i]); @@ -596,7 +598,6 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayTypesecond.metadata.expiresAt() <= now) { not_found[ids[i]].push_back(i); - markExpired(it); } else { @@ -605,12 +606,6 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType -void CachePartition::markExpired(const Iterator & it) const -{ - key_to_index_and_metadata.erase(it); -} - CacheStorage::CacheStorage( const Attributes & attributes_structure_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_) @@ -673,7 +668,6 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes, metadata); } @@ -760,8 +754,6 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 07f5f511b6c1..af61f7be9cbd 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -23,27 +23,6 @@ namespace DB class SSDCacheDictionary; class CacheStorage; -/*class SimpleSerializer -{ -public: - bool block() const { return false; } - - template - size_t estimateSizeNumber(T number) const; - - size_t estimateSizeString(const String & str) const; - - template - ssize_t writeNumber(T number, WriteBuffer & buffer); - - ssize_t writeString(const String & str, WriteBuffer & buffer); - - template - ssize_t readNumber(T number, WriteBuffer & buffer); - - ssize_t readString(const String & str, WriteBuffer & buffer); -};*/ - using AttributeValueVariant = std::variant< UInt8, UInt16, @@ -131,6 +110,7 @@ class CachePartition // Key, (Metadata), attributes void appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata); + void flush(); private: struct Index final { @@ -152,8 +132,6 @@ class CachePartition size_t index = 0; }; - void flush(); - size_t appendValuesToAttribute(Attribute & to, const Attribute & from); template @@ -167,14 +145,12 @@ class CachePartition template void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; - template - void markExpired(const Iterator & it) const; - size_t file_id; size_t max_size; std::string path; - //mutable std::shared_mutex rw_lock; + mutable std::shared_mutex rw_lock; + int fd = -1; struct IndexAndMetadata final @@ -197,7 +173,7 @@ class CachePartition // mutable std::atomic element_count{0}; }; -using CachePartitionPtr = std::unique_ptr; +using CachePartitionPtr = std::shared_ptr; class CacheStorage @@ -226,6 +202,7 @@ class CacheStorage void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { + //for (auto & partition : partitions) partitions[0]->has(ids, out, not_found, now); } @@ -248,14 +225,14 @@ class CacheStorage const std::string path; const size_t partition_max_size; + + mutable std::shared_mutex rw_lock; std::vector partitions; Logger * const log; mutable pcg64 rnd_engine; - mutable std::shared_mutex rw_lock; - mutable std::exception_ptr last_update_exception; mutable size_t update_error_count = 0; mutable std::chrono::system_clock::time_point backoff_end_time; From 75f750833b7ff13be61a382218cbbdcdc1b97a9b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 10 Jan 2020 21:01:23 +0300 Subject: [PATCH 0023/1102] opt table --- dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index e526df908a58..045b55b73a37 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -88,6 +88,8 @@ INSERT INTO database_for_dict.keys_table VALUES (5); INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 700, 370; INSERT INTO database_for_dict.keys_table VALUES (10); +OPTIMIZE TABLE database_for_dict.keys_table; + CREATE DICTIONARY database_for_dict.ssd_dict ( id UInt64, From 2e10d93cab95b028470bed5be1df09706861620a Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 10 Jan 2020 22:19:03 +0300 Subject: [PATCH 0024/1102] some refactoring --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 61 ++++---------------- dbms/src/Dictionaries/SSDCacheDictionary.h | 12 +--- 2 files changed, 13 insertions(+), 60 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 9aba99157a38..52c9db98bbb3 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -148,7 +148,7 @@ CachePartition::CachePartition( , attributes_structure(attributes_structure_), memory(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT) { keys_buffer.type = AttributeUnderlyingType::utUInt64; - keys_buffer.values = std::vector(); + keys_buffer.values = PaddedPODArray(); { ProfileEvents::increment(ProfileEvents::FileOpen); @@ -169,7 +169,7 @@ CachePartition::~CachePartition() ::close(fd); } -void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata) +void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata) { std::unique_lock lock(rw_lock); if (new_attributes.size() != attributes_structure.size()) @@ -245,45 +245,6 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & } } -size_t CachePartition::appendValuesToAttribute(Attribute & to, const Attribute & from) -{ - switch (to.type) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - { \ - auto &to_values = std::get>(to.values); \ - auto &from_values = std::get>(from.values); \ - size_t prev_size = to_values.size(); \ - to_values.resize(to_values.size() + from_values.size()); \ - memcpy(&to_values[prev_size], &from_values[0], from_values.size() * sizeof(TYPE)); \ - return from_values.size() * sizeof(TYPE); \ - } \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - // TODO: string support - break; - } - throw Exception{"Unknown attribute type: " + std::to_string(static_cast(to.type)), ErrorCodes::TYPE_MISMATCH}; -} - void CachePartition::flush() { write_buffer.reset(); @@ -652,12 +613,12 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorread()) { - const auto new_keys = createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front(); + const auto new_keys = std::move(createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front()); const auto new_attributes = createAttributesFromBlock(block, 1, attributes_structure); const auto & ids = std::get>(new_keys.values); - std::vector metadata(ids.size()); + PaddedPODArray metadata(ids.size()); for (const auto i : ext::range(0, ids.size())) { @@ -695,7 +656,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); + new_keys.values = PaddedPODArray(); CachePartition::Attributes new_attributes; { /// TODO: create attributes from structure @@ -707,7 +668,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); \ + new_attributes.back().values = PaddedPODArray(); \ break; DISPATCH(UInt8) @@ -733,7 +694,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector metadata; + PaddedPODArray metadata; for (const auto & id_found_pair : remaining_ids) { @@ -755,7 +716,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); + std::get>(new_keys.values).push_back(id); std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata.emplace_back(); @@ -772,7 +733,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_attributes[i].values); \ + auto & to_values = std::get>(new_attributes[i].values); \ auto & null_value = std::get(null_values[i]); \ to_values.push_back(null_value); \ } \ @@ -826,7 +787,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ - std::vector values(column->size()); \ + PaddedPODArray values(column->size()); \ const auto raw_data = column->getRawData(); \ memcpy(&values[0], raw_data.data, raw_data.size * sizeof(TYPE)); \ attributes.emplace_back(); \ @@ -994,7 +955,7 @@ void SSDCacheDictionary::getItemsNumberImpl( required_ids, [&](const auto id, const auto row, const auto & new_attributes) { for (const size_t out_row : not_found_ids[id]) - out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; + out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const size_t id) { diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index af61f7be9cbd..53b2ef8624a5 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -85,7 +85,7 @@ class CachePartition struct Attribute { template - using Container = std::vector; + using Container = PaddedPODArray; AttributeUnderlyingType type; std::variant< @@ -108,7 +108,7 @@ class CachePartition using Attributes = std::vector; // Key, (Metadata), attributes - void appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const std::vector & metadata); + void appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata); void flush(); private: @@ -132,8 +132,6 @@ class CachePartition size_t index = 0; }; - size_t appendValuesToAttribute(Attribute & to, const Attribute & from); - template void getValueFromMemory( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; @@ -379,12 +377,6 @@ class SSDCacheDictionary final : public IDictionary private: size_t getAttributeIndex(const std::string & attr_name) const; - struct Attribute - { - AttributeUnderlyingType type; - AttributeValueVariant null_value; - }; - template AttributeValueVariant createAttributeNullValueWithTypeImpl(const Field & null_value); AttributeValueVariant createAttributeNullValueWithType(const AttributeUnderlyingType type, const Field & null_value); From 15afa5240a4404ef7ed66b4be05fbbf4b1af42d7 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 10 Jan 2020 22:21:48 +0300 Subject: [PATCH 0025/1102] rm unused file --- .../src/Dictionaries/SSDCacheDictionary.inc.h | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 dbms/src/Dictionaries/SSDCacheDictionary.inc.h diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.inc.h b/dbms/src/Dictionaries/SSDCacheDictionary.inc.h deleted file mode 100644 index c3d113035094..000000000000 --- a/dbms/src/Dictionaries/SSDCacheDictionary.inc.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -namespace DB { -/* -template -void BlockFile::getValue(size_t column, const PaddedPODArray & ids, ResultArrayType & out, PaddedPODArray & not_found) const -{ - std::vector> offsets; - offsets.reserve(ids.size()); - - for (size_t i = 0; i < ids.size(); ++i) - { - auto it = key_to_file_offset.find(ids[i]); - if (it != std::end(key_to_file_offset)) - { - offsets.emplace_back(it->second, i); - } - else - { - not_found.push_back(i); - } - } - std::sort(std::begin(offsets), std::end(offsets)); - - Field field; - for (const auto & [offset, index] : offsets) - { - if (offset & OFFSET_MASK) - { - in_file.seek(offset && !OFFSET_MASK); - for (size_t col = 0; col < column; ++col) - { - const auto & type = header.getByPosition(column).type; - type->deserializeBinary(field, in_file); - } - } - else - { - buffer[column]->get(offset, field); - } - out[index] = DB::get(field); - } -} -*/ -} \ No newline at end of file From 50a68c41a89af89d8c3f158823e2102ad4f267c5 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 10 Jan 2020 23:08:57 +0300 Subject: [PATCH 0026/1102] fixes --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 2 +- dbms/src/Dictionaries/SSDCacheDictionary.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 52c9db98bbb3..e9b77c325c50 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -64,7 +64,7 @@ namespace constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры - constexpr size_t AIO_MAX_SIMULTANIOUS_REQUESTS = 32; + constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; // TODO: в параметры static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 53b2ef8624a5..faea7f7a350f 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -409,6 +409,3 @@ class SSDCacheDictionary final : public IDictionary }; } - - -#include "SSDCacheDictionary.inc.h" \ No newline at end of file From 0e2080c7de456379884d2f9f25591543ac00c701 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 10:20:48 +0300 Subject: [PATCH 0027/1102] preallocate --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index e9b77c325c50..b673c13fe4f6 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -64,6 +64,9 @@ namespace constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры + constexpr size_t FILE_SIZE_TO_PREALLOCATE = 1 * 1024 * 1024 * 1024; + constexpr size_t FILE_SIZE_IN_BLOCKS = FILE_SIZE_TO_PREALLOCATE / SSD_BLOCK_SIZE; + constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; // TODO: в параметры @@ -81,6 +84,15 @@ namespace const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; + + int preallocateDiskSpace(int fd, size_t len) + { + #if defined(__FreeBSD__) + return posix_fallocate(fd, 0, len); + #else + return fallocate(fd, 0, 0, len); + #endif + } } CachePartition::Metadata::time_point_t CachePartition::Metadata::expiresAt() const @@ -160,6 +172,11 @@ CachePartition::CachePartition( auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE; throwFromErrnoWithPath("Cannot open file " + filename, filename, error_code); } + + if (preallocateDiskSpace(fd, FILE_SIZE_TO_PREALLOCATE) < 0) + { + throwFromErrnoWithPath("Cannot preallocate space for the file " + filename, filename, ErrorCodes::CANNOT_ALLOCATE_MEMORY); + } } } From ee1e8cbdb35cc9192ff9b58044ff7a42c24acd39 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 14:19:12 +0300 Subject: [PATCH 0028/1102] read multipartition --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 43 ++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 72 +++++++++++--------- 2 files changed, 62 insertions(+), 53 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index b673c13fe4f6..8619051ee48a 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -64,8 +64,8 @@ namespace constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры - constexpr size_t FILE_SIZE_TO_PREALLOCATE = 1 * 1024 * 1024 * 1024; - constexpr size_t FILE_SIZE_IN_BLOCKS = FILE_SIZE_TO_PREALLOCATE / SSD_BLOCK_SIZE; + constexpr size_t FILE_SIZE_IN_BLOCKS = 16; + constexpr size_t FILE_SIZE_TO_PREALLOCATE = FILE_SIZE_IN_BLOCKS * SSD_BLOCK_SIZE; constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; // TODO: в параметры @@ -186,9 +186,12 @@ CachePartition::~CachePartition() ::close(fd); } -void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata) +size_t CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata) { std::unique_lock lock(rw_lock); + if (current_file_block_id >= FILE_SIZE_IN_BLOCKS) + return 0; + if (new_attributes.size() != attributes_structure.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; @@ -257,9 +260,12 @@ void CachePartition::appendBlock(const Attribute & new_keys, const Attributes & ids_buffer.push_back(ids[index]); ++index; } - else + else if (current_file_block_id < FILE_SIZE_IN_BLOCKS) write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); + else + return index; // End of current file. } + return ids.size(); } void CachePartition::flush() @@ -339,29 +345,28 @@ void CachePartition::flush() std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); } -template +template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::unordered_map> & not_found, + ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { - auto it = key_to_index_and_metadata.find(ids[i]); - if (it == std::end(key_to_index_and_metadata)) + if (found[i]) { indices[i].setNotExists(); - not_found[ids[i]].push_back(i); } - else if (it->second.metadata.expiresAt() <= now) + else if (auto it = key_to_index_and_metadata.find(ids[i]); + it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) { - indices[i].setNotExists(); - not_found[ids[i]].push_back(i); + indices[i] = it->second.index; + found[i] = true; } else { - indices[i] = it->second.index; + indices[i].setNotExists(); } } @@ -560,22 +565,16 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst } } -template -void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, - std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const +void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); for (size_t i = 0; i < ids.size(); ++i) { auto it = key_to_index_and_metadata.find(ids[i]); - if (it == std::end(key_to_index_and_metadata)) - { - not_found[ids[i]].push_back(i); - } - else if (it->second.metadata.expiresAt() <= now) + if (it == std::end(key_to_index_and_metadata) || it->second.metadata.expiresAt() <= now) { - not_found[ids[i]].push_back(i); + out[i] = static_cast(-1); } else { diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index faea7f7a350f..c6f2a26f15ee 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -43,6 +43,26 @@ using AttributeValueVariant = std::variant< class CachePartition { public: + struct Index final + { + bool inMemory() const; + void setInMemory(const bool in_memory); + + bool exists() const; + void setNotExists(); + + size_t getAddressInBlock() const; + void setAddressInBlock(const size_t address_in_block); + + size_t getBlockId() const; + void setBlockId(const size_t block_id); + + bool operator< (const Index & rhs) const { return index < rhs.index; } + + /// Stores `is_in_memory` flag, block id, address in uncompressed block + size_t index = 0; + }; + struct Metadata final { using time_point_t = std::chrono::system_clock::time_point; @@ -61,6 +81,7 @@ class CachePartition using Offset = size_t; using Offsets = std::vector; + using Key = IDictionary::Key; CachePartition( const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, @@ -71,16 +92,14 @@ class CachePartition template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; - template + template void getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::unordered_map> & not_found, + ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const; // TODO:: getString - template - void has(const PaddedPODArray & ids, ResultArrayType & out, - std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; + void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; struct Attribute { @@ -107,31 +126,12 @@ class CachePartition }; using Attributes = std::vector; - // Key, (Metadata), attributes - void appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata); + /// Returns false if there are no allocated space to append block. + size_t appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata); void flush(); -private: - struct Index final - { - bool inMemory() const; - void setInMemory(const bool in_memory); - - bool exists() const; - void setNotExists(); - - size_t getAddressInBlock() const; - void setAddressInBlock(const size_t address_in_block); - - size_t getBlockId() const; - void setBlockId(const size_t block_id); - - bool operator< (const Index & rhs) const { return index < rhs.index; } - - /// Stores `is_in_memory` flag, block id, address in uncompressed block - size_t index = 0; - }; +private: template void getValueFromMemory( const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; @@ -178,7 +178,7 @@ class CacheStorage { public: using Attributes = std::vector; - using Key = IDictionary::Key; + using Key = CachePartition::Key; CacheStorage(const Attributes & attributes_structure_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_); @@ -191,7 +191,13 @@ class CacheStorage ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { - partitions[0]->getValue(attribute_index, ids, out, not_found, now); + std::vector found(ids.size(), false); + for (auto & partition : partitions) + partition->getValue(attribute_index, ids, out, found, now); + + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); } // getString(); @@ -200,8 +206,12 @@ class CacheStorage void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { - //for (auto & partition : partitions) - partitions[0]->has(ids, out, not_found, now); + for (auto & partition : partitions) + partition->has(ids, out, now); + + for (size_t i = 0; i < ids.size(); ++i) + if (out[i] == static_cast(-1)) + not_found[ids[i]].push_back(i); } template From 8489f2fef5dd4081c64136064ef5446f218f0853 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 19:38:43 +0300 Subject: [PATCH 0029/1102] many files in cache --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 48 ++++++++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 19 +++++--- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 8619051ee48a..a5df0c674989 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -64,7 +64,7 @@ namespace constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры - constexpr size_t FILE_SIZE_IN_BLOCKS = 16; + constexpr size_t FILE_SIZE_IN_BLOCKS = 2; constexpr size_t FILE_SIZE_TO_PREALLOCATE = FILE_SIZE_IN_BLOCKS * SSD_BLOCK_SIZE; constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры @@ -186,7 +186,8 @@ CachePartition::~CachePartition() ::close(fd); } -size_t CachePartition::appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata) +size_t CachePartition::appendBlock( + const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); if (current_file_block_id >= FILE_SIZE_IN_BLOCKS) @@ -201,9 +202,9 @@ size_t CachePartition::appendBlock(const Attribute & new_keys, const Attributes if (!write_buffer) write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); - for (size_t index = 0; index < ids.size();) + for (size_t index = begin; index < ids.size();) { - auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; + IndexAndMetadata index_and_metadata; index_and_metadata.index.setInMemory(true); index_and_metadata.index.setBlockId(current_memory_block_id); index_and_metadata.index.setAddressInBlock(write_buffer->offset()); @@ -257,15 +258,16 @@ size_t CachePartition::appendBlock(const Attribute & new_keys, const Attributes if (!flushed) { + key_to_index_and_metadata[ids[index]] = index_and_metadata; ids_buffer.push_back(ids[index]); ++index; } else if (current_file_block_id < FILE_SIZE_IN_BLOCKS) write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); else - return index; // End of current file. + return index - begin; // End of current file. } - return ids.size(); + return ids.size() - begin; } void CachePartition::flush() @@ -471,7 +473,6 @@ void CachePartition::getValueFromStorage( const auto & request = requests[request_id]; if (events[i].res != static_cast(request.aio_nbytes)) throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(events[i].res), ErrorCodes::AIO_WRITE_ERROR); - for (const size_t idx : blocks_to_indices[request_id]) { const auto & [file_index, out_index] = index_to_out[idx]; @@ -583,17 +584,19 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType(AttributeUnderlyingType::utUInt64, - attributes_structure, path_, partition_id, partition_max_size)); } template @@ -601,6 +604,23 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & null_values) { + auto append_block = [this](const CachePartition::Attribute & new_keys, + const CachePartition::Attributes & new_attributes, const PaddedPODArray & metadata) + { + size_t inserted = 0; + while (inserted < metadata.size()) + { + if (!partitions.empty()) + inserted += partitions.front()->appendBlock(new_keys, new_attributes, metadata, inserted); + if (inserted < metadata.size()) + { + partitions.emplace_front(std::make_unique( + AttributeUnderlyingType::utUInt64, attributes_structure, path, + (partitions.empty() ? 0 : partitions.back()->getId() + 1), partition_max_size)); + } + } + }; + CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; ProfileEvents::increment(ProfileEvents::DictCacheKeysRequested, requested_ids.size()); @@ -645,7 +665,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes, metadata); + append_block(new_keys, new_attributes, metadata); } stream->readSuffix(); @@ -782,7 +802,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes, metadata); + append_block(new_keys, new_attributes, metadata); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index c6f2a26f15ee..9aa9b7f91aa2 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -126,11 +126,13 @@ class CachePartition }; using Attributes = std::vector; - /// Returns false if there are no allocated space to append block. - size_t appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata); + size_t appendBlock(const Attribute & new_keys, const Attributes & new_attributes, + const PaddedPODArray & metadata, const size_t begin); void flush(); + size_t getId() const; + private: template void getValueFromMemory( @@ -177,10 +179,10 @@ using CachePartitionPtr = std::shared_ptr; class CacheStorage { public: - using Attributes = std::vector; + using AttributeTypes = std::vector; using Key = CachePartition::Key; - CacheStorage(const Attributes & attributes_structure_, const std::string & path_, + CacheStorage(const AttributeTypes & attributes_structure_, const std::string & path_, const size_t partitions_count_, const size_t partition_max_size_); template @@ -192,6 +194,8 @@ class CacheStorage std::chrono::system_clock::time_point now) const { std::vector found(ids.size(), false); + + std::shared_lock lock(rw_lock); for (auto & partition : partitions) partition->getValue(attribute_index, ids, out, found, now); @@ -206,6 +210,7 @@ class CacheStorage void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { + std::shared_lock lock(rw_lock); for (auto & partition : partitions) partition->has(ids, out, now); @@ -229,13 +234,15 @@ class CacheStorage CachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); - const Attributes attributes_structure; + void collectGarbage() {} + + const AttributeTypes attributes_structure; const std::string path; const size_t partition_max_size; mutable std::shared_mutex rw_lock; - std::vector partitions; + std::list partitions; Logger * const log; From a77ef1ed074929335cdac5af6eb26c5a239be65e Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 23:23:51 +0300 Subject: [PATCH 0030/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 40 ++++++++++++++++++-- dbms/src/Dictionaries/SSDCacheDictionary.h | 26 ++----------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index a5df0c674989..198687f5b0e9 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -82,6 +82,8 @@ namespace constexpr size_t NOT_EXISTS = -1; + constexpr UInt8 HAS_NOT_FOUND = 2; + const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; @@ -277,7 +279,7 @@ void CachePartition::flush() if (ids.empty()) return; - Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!!"); + Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); AIOContext aio_context{1}; @@ -358,16 +360,19 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray { if (found[i]) { + Poco::Logger::get("kek").information("FOUND BEFORE:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); indices[i].setNotExists(); } else if (auto it = key_to_index_and_metadata.find(ids[i]); it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) { + Poco::Logger::get("kek").information(std::to_string(file_id) + " FOUND BEFORE: inmemory: " + std::to_string(it->second.index.inMemory()) + " " + std::to_string(it->second.index.getBlockId()) + " " + std::to_string(it->second.index.getAddressInBlock())); indices[i] = it->second.index; found[i] = true; } else { + Poco::Logger::get("kek").information("NF:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); indices[i].setNotExists(); } } @@ -575,7 +580,8 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayTypesecond.metadata.expiresAt() <= now) { - out[i] = static_cast(-1); + Poco::Logger::get("kek").information("NF:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); + out[i] = HAS_NOT_FOUND; } else { @@ -599,6 +605,34 @@ CacheStorage::CacheStorage( { } +template +void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, + ResultArrayType & out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const +{ + std::vector found(ids.size(), false); + + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->getValue(attribute_index, ids, out, found, now); + + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); +} + +void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, + std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const +{ + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->has(ids, out, now); + + for (size_t i = 0; i < ids.size(); ++i) + if (out[i] == HAS_NOT_FOUND) + not_found[ids[i]].push_back(i); +} + template void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, @@ -616,7 +650,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector( AttributeUnderlyingType::utUInt64, attributes_structure, path, - (partitions.empty() ? 0 : partitions.back()->getId() + 1), partition_max_size)); + (partitions.empty() ? 0 : partitions.front()->getId() + 1), partition_max_size)); } } }; diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 9aa9b7f91aa2..847aa4d4c45f 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -191,33 +191,13 @@ class CacheStorage template void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const - { - std::vector found(ids.size(), false); - - std::shared_lock lock(rw_lock); - for (auto & partition : partitions) - partition->getValue(attribute_index, ids, out, found, now); - - for (size_t i = 0; i < ids.size(); ++i) - if (!found[i]) - not_found[ids[i]].push_back(i); - } + std::chrono::system_clock::time_point now) const; // getString(); - template - void has(const PaddedPODArray & ids, ResultArrayType & out, - std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const - { - std::shared_lock lock(rw_lock); - for (auto & partition : partitions) - partition->has(ids, out, now); - for (size_t i = 0; i < ids.size(); ++i) - if (out[i] == static_cast(-1)) - not_found[ids[i]].push_back(i); - } + void has(const PaddedPODArray & ids, ResultArrayType & out, + std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; template void update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, From 590ed9759f04d1ea122a4fe9a2abcaf99865b47d Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 23:27:50 +0300 Subject: [PATCH 0031/1102] test --- .../queries/0_stateless/01053_ssd_dictionary.reference | 5 +++++ dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index cda8dc267c5f..dda210b162b3 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -6,6 +6,11 @@ VALUE FROM RAM BUFFER 8 VALUES FROM DISK AND RAM BUFFER 118 +HAS +1 +2 +5 +10 VALUES NOT FROM TABLE 0 -1 DUPLICATE KEYS diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 045b55b73a37..64e95c868da2 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -62,13 +62,17 @@ SELECT 'VALUES FROM DISK AND RAM BUFFER'; -- 118 SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; +SELECT 'HAS'; +-- 1 2 5 10 +SELECT id FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', toUInt64(id)) ORDER BY id; + SELECT 'VALUES NOT FROM TABLE'; -- 0 -1 SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)); SELECT 'DUPLICATE KEYS'; SELECT arrayJoin([1, 2, 3, 3, 2, 1]) AS id, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(id)); - +--SELECT DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; DROP TABLE IF EXISTS database_for_dict.keys_table; @@ -99,7 +103,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/1')); +LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/2')); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 From 52a101516d7736d56deee483f2c4add296cf56aa Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 Jan 2020 23:56:27 +0300 Subject: [PATCH 0032/1102] improve locks --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 91 +++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 198687f5b0e9..7c6ae384233b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -664,60 +664,62 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector backoff_end_time) { - try + const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; + + if (now > backoff_end_time) { - if (update_error_count) + try { - /// Recover after error: we have to clone the source here because - /// it could keep connections which should be reset after error. - source_ptr = source_ptr->clone(); - } + if (update_error_count) + { + /// Recover after error: we have to clone the source here because + /// it could keep connections which should be reset after error. + source_ptr = source_ptr->clone(); + } - Stopwatch watch; - auto stream = source_ptr->loadIds(requested_ids); - stream->readPrefix(); + Stopwatch watch; + auto stream = source_ptr->loadIds(requested_ids); + stream->readPrefix(); - while (const auto block = stream->read()) - { - const auto new_keys = std::move(createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front()); - const auto new_attributes = createAttributesFromBlock(block, 1, attributes_structure); + while (const auto block = stream->read()) + { + const auto new_keys = std::move(createAttributesFromBlock(block, 0, { AttributeUnderlyingType::utUInt64 }).front()); + const auto new_attributes = createAttributesFromBlock(block, 1, attributes_structure); - const auto & ids = std::get>(new_keys.values); + const auto & ids = std::get>(new_keys.values); - PaddedPODArray metadata(ids.size()); + PaddedPODArray metadata(ids.size()); - for (const auto i : ext::range(0, ids.size())) - { - std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; - metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); - /// mark corresponding id as found - on_updated(ids[i], i, new_attributes); - remaining_ids[ids[i]] = 1; - } + for (const auto i : ext::range(0, ids.size())) + { + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; + metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); + /// mark corresponding id as found + on_updated(ids[i], i, new_attributes); + remaining_ids[ids[i]] = 1; + } - append_block(new_keys, new_attributes, metadata); - } + append_block(new_keys, new_attributes, metadata); + } - stream->readSuffix(); + stream->readSuffix(); - update_error_count = 0; - last_update_exception = std::exception_ptr{}; - backoff_end_time = std::chrono::system_clock::time_point{}; + update_error_count = 0; + last_update_exception = std::exception_ptr{}; + backoff_end_time = std::chrono::system_clock::time_point{}; - ProfileEvents::increment(ProfileEvents::DictCacheRequestTimeNs, watch.elapsed()); - } - catch (...) - { - ++update_error_count; - last_update_exception = std::current_exception(); - backoff_end_time = now + std::chrono::seconds(calculateDurationWithBackoff(rnd_engine, update_error_count)); + ProfileEvents::increment(ProfileEvents::DictCacheRequestTimeNs, watch.elapsed()); + } + catch (...) + { + ++update_error_count; + last_update_exception = std::current_exception(); + backoff_end_time = now + std::chrono::seconds(calculateDurationWithBackoff(rnd_engine, update_error_count)); - tryLogException(last_update_exception, log, - "Could not update ssd cache dictionary, next update is scheduled at " + ext::to_string(backoff_end_time)); + tryLogException(last_update_exception, log, + "Could not update ssd cache dictionary, next update is scheduled at " + ext::to_string(backoff_end_time)); + } } } @@ -835,8 +837,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector Date: Sun, 12 Jan 2020 14:32:43 +0300 Subject: [PATCH 0033/1102] fifo compaction --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 50 ++++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 10 ++-- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 7c6ae384233b..2b3fe9d8751b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace ProfileEvents { @@ -159,7 +160,7 @@ CachePartition::CachePartition( const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure_, const std::string & dir_path, const size_t file_id_, const size_t max_size_) : file_id(file_id_), max_size(max_size_), path(dir_path + "/" + std::to_string(file_id)) - , attributes_structure(attributes_structure_), memory(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT) + , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; keys_buffer.values = PaddedPODArray(); @@ -201,8 +202,10 @@ size_t CachePartition::appendBlock( const auto & ids = std::get>(new_keys.values); auto & ids_buffer = std::get>(keys_buffer.values); + if (!memory) + memory.emplace(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT); if (!write_buffer) - write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); + write_buffer.emplace(memory->data(), SSD_BLOCK_SIZE); for (size_t index = begin; index < ids.size();) { @@ -265,9 +268,14 @@ size_t CachePartition::appendBlock( ++index; } else if (current_file_block_id < FILE_SIZE_IN_BLOCKS) - write_buffer.emplace(memory.data(), SSD_BLOCK_SIZE); + { + write_buffer.emplace(memory->data(), SSD_BLOCK_SIZE); + } else + { + memory.reset(); return index - begin; // End of current file. + } } return ids.size() - begin; } @@ -289,13 +297,13 @@ void CachePartition::flush() #if defined(__FreeBSD__) write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; - write_request.aio.aio_buf = reinterpret_cast(memory.data()); + write_request.aio.aio_buf = reinterpret_cast(memory->data()); write_request.aio.aio_nbytes = SSD_BLOCK_SIZE; write_request.aio.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; - write_request.aio_buf = reinterpret_cast(memory.data()); + write_request.aio_buf = reinterpret_cast(memory->data()); write_request.aio_nbytes = SSD_BLOCK_SIZE; write_request.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; #endif @@ -392,7 +400,7 @@ void CachePartition::getValueFromMemory( { const size_t offset = index.getAddressInBlock(); - ReadBufferFromMemory read_buffer(memory.data() + offset, SSD_BLOCK_SIZE - offset); + ReadBufferFromMemory read_buffer(memory->data() + offset, SSD_BLOCK_SIZE - offset); readValueFromBuffer(attribute_index, out[i], read_buffer); } } @@ -595,12 +603,19 @@ size_t CachePartition::getId() const return file_id; } +void CachePartition::remove() +{ + std::unique_lock lock(rw_lock); + std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); +} + CacheStorage::CacheStorage( const AttributeTypes & attributes_structure_, const std::string & path_, - const size_t /* partitions_count_ */, const size_t partition_max_size_) + const size_t max_partitions_count_, const size_t partition_max_size_) : attributes_structure(attributes_structure_) , path(path_) , partition_max_size(partition_max_size_) + , max_partitions_count(max_partitions_count_) , log(&Poco::Logger::get("CacheStorage")) { } @@ -653,6 +668,8 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorgetId() + 1), partition_max_size)); } } + + collectGarbage(); }; CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; @@ -848,6 +865,23 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector max_partitions_count) + { + partition_delete_queue.push_back(partitions.back()); + partitions.pop_back(); + } + + // drop unused partitions + while (!partition_delete_queue.empty() && partition_delete_queue.front().use_count() == 1) + { + partition_delete_queue.front()->remove(); + partition_delete_queue.pop_front(); + } +} + CachePartition::Attributes CacheStorage::createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure) { @@ -910,7 +944,7 @@ SSDCacheDictionary::SSDCacheDictionary( , path(path_) , partition_max_size(partition_max_size_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, 1, partition_max_size) + path, 2, partition_max_size) , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 847aa4d4c45f..7a876a3ed953 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -131,6 +131,8 @@ class CachePartition void flush(); + void remove(); + size_t getId() const; private: @@ -164,7 +166,7 @@ class CachePartition Attribute keys_buffer; const std::vector attributes_structure; - DB::Memory<> memory; + std::optional> memory; std::optional write_buffer; size_t current_memory_block_id = 0; @@ -183,7 +185,7 @@ class CacheStorage using Key = CachePartition::Key; CacheStorage(const AttributeTypes & attributes_structure_, const std::string & path_, - const size_t partitions_count_, const size_t partition_max_size_); + const size_t max_partitions_count_, const size_t partition_max_size_); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -214,15 +216,17 @@ class CacheStorage CachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); - void collectGarbage() {} + void collectGarbage(); const AttributeTypes attributes_structure; const std::string path; const size_t partition_max_size; + const size_t max_partitions_count; mutable std::shared_mutex rw_lock; std::list partitions; + std::list partition_delete_queue; Logger * const log; From e18fa890a12febead234f16631ed35225012be7a Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 12 Jan 2020 15:29:42 +0300 Subject: [PATCH 0034/1102] remove and create files --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 20 ++++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 2b3fe9d8751b..9e9d166dcae0 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "DictionaryFactory.h" #include #include @@ -165,6 +166,10 @@ CachePartition::CachePartition( keys_buffer.type = AttributeUnderlyingType::utUInt64; keys_buffer.values = PaddedPODArray(); + Poco::File directory(dir_path); + if (!directory.exists()) + directory.createDirectory(); + { ProfileEvents::increment(ProfileEvents::FileOpen); @@ -606,7 +611,8 @@ size_t CachePartition::getId() const void CachePartition::remove() { std::unique_lock lock(rw_lock); - std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); + Poco::File(path + BIN_FILE_EXT).remove(); + //std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); } CacheStorage::CacheStorage( @@ -620,6 +626,13 @@ CacheStorage::CacheStorage( { } +CacheStorage::~CacheStorage() +{ + std::unique_lock lock(rw_lock); + partition_delete_queue.splice(std::end(partition_delete_queue), partitions); + collectGarbage(); +} + template void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, @@ -868,10 +881,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector max_partitions_count) + while (partitions.size() > max_partitions_count) { - partition_delete_queue.push_back(partitions.back()); - partitions.pop_back(); + partition_delete_queue.splice(std::end(partition_delete_queue), partitions, std::prev(std::end(partitions))); } // drop unused partitions diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 7a876a3ed953..31732f25c1af 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -187,6 +187,8 @@ class CacheStorage CacheStorage(const AttributeTypes & attributes_structure_, const std::string & path_, const size_t max_partitions_count_, const size_t partition_max_size_); + ~CacheStorage(); + template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; From 3ccf10f7b23e6720b5a8bffccb1fde25918fe040 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 12 Jan 2020 17:23:32 +0300 Subject: [PATCH 0035/1102] params --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 119 +++++++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 45 +++++-- 2 files changed, 113 insertions(+), 51 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 9e9d166dcae0..6a10dc471aa5 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -63,14 +63,14 @@ namespace ErrorCodes namespace { - constexpr size_t SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры - constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; // TODO: в параметры + constexpr size_t DEFAULT_SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t DEFAULT_FILE_SIZE = 4 * 1024 * 1024 * 1024ULL; + constexpr size_t DEFAULT_PARTITIONS_COUNT = 16; + constexpr size_t DEFAULT_READ_BUFFER_SIZE = 16 * DEFAULT_SSD_BLOCK_SIZE; - constexpr size_t FILE_SIZE_IN_BLOCKS = 2; - constexpr size_t FILE_SIZE_TO_PREALLOCATE = FILE_SIZE_IN_BLOCKS * SSD_BLOCK_SIZE; + constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры - constexpr size_t READ_BUFFER_SIZE_BLOCKS = 16; // TODO: в параметры static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -158,9 +158,18 @@ void CachePartition::Index::setBlockId(const size_t block_id) } CachePartition::CachePartition( - const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure_, - const std::string & dir_path, const size_t file_id_, const size_t max_size_) - : file_id(file_id_), max_size(max_size_), path(dir_path + "/" + std::to_string(file_id)) + const AttributeUnderlyingType & /* key_structure */, + const std::vector & attributes_structure_, + const std::string & dir_path, + const size_t file_id_, + const size_t max_size_, + const size_t block_size_, + const size_t read_buffer_size_) + : file_id(file_id_) + , max_size(max_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) + , path(dir_path + "/" + std::to_string(file_id)) , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; @@ -181,7 +190,7 @@ CachePartition::CachePartition( throwFromErrnoWithPath("Cannot open file " + filename, filename, error_code); } - if (preallocateDiskSpace(fd, FILE_SIZE_TO_PREALLOCATE) < 0) + if (preallocateDiskSpace(fd, max_size * block_size) < 0) { throwFromErrnoWithPath("Cannot preallocate space for the file " + filename, filename, ErrorCodes::CANNOT_ALLOCATE_MEMORY); } @@ -198,7 +207,7 @@ size_t CachePartition::appendBlock( const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); - if (current_file_block_id >= FILE_SIZE_IN_BLOCKS) + if (current_file_block_id >= max_size) return 0; if (new_attributes.size() != attributes_structure.size()) @@ -208,9 +217,9 @@ size_t CachePartition::appendBlock( auto & ids_buffer = std::get>(keys_buffer.values); if (!memory) - memory.emplace(SSD_BLOCK_SIZE, BUFFER_ALIGNMENT); + memory.emplace(block_size, BUFFER_ALIGNMENT); if (!write_buffer) - write_buffer.emplace(memory->data(), SSD_BLOCK_SIZE); + write_buffer.emplace(memory->data(), block_size); for (size_t index = begin; index < ids.size();) { @@ -272,9 +281,9 @@ size_t CachePartition::appendBlock( ids_buffer.push_back(ids[index]); ++index; } - else if (current_file_block_id < FILE_SIZE_IN_BLOCKS) + else if (current_file_block_id < max_size) { - write_buffer.emplace(memory->data(), SSD_BLOCK_SIZE); + write_buffer.emplace(memory->data(), block_size); } else { @@ -303,14 +312,14 @@ void CachePartition::flush() write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; write_request.aio.aio_buf = reinterpret_cast(memory->data()); - write_request.aio.aio_nbytes = SSD_BLOCK_SIZE; - write_request.aio.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; + write_request.aio.aio_nbytes = block_size; + write_request.aio.aio_offset = block_size * current_file_block_id; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory->data()); - write_request.aio_nbytes = SSD_BLOCK_SIZE; - write_request.aio_offset = SSD_BLOCK_SIZE * current_file_block_id; + write_request.aio_nbytes = block_size; + write_request.aio_offset = block_size * current_file_block_id; #endif Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); @@ -405,7 +414,7 @@ void CachePartition::getValueFromMemory( { const size_t offset = index.getAddressInBlock(); - ReadBufferFromMemory read_buffer(memory->data() + offset, SSD_BLOCK_SIZE - offset); + ReadBufferFromMemory read_buffer(memory->data() + offset, block_size - offset); readValueFromBuffer(attribute_index, out[i], read_buffer); } } @@ -428,7 +437,7 @@ void CachePartition::getValueFromStorage( /// sort by (block_id, offset_in_block) std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(SSD_BLOCK_SIZE * READ_BUFFER_SIZE_BLOCKS, BUFFER_ALIGNMENT); + DB::Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); std::vector requests; std::vector pointers; @@ -439,7 +448,7 @@ void CachePartition::getValueFromStorage( for (size_t i = 0; i < index_to_out.size(); ++i) { if (!requests.empty() && - static_cast(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE) + static_cast(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * block_size) { blocks_to_indices.back().push_back(i); continue; @@ -457,9 +466,9 @@ void CachePartition::getValueFromStorage( #else request.aio_lio_opcode = IOCB_CMD_PREAD; request.aio_fildes = fd; - request.aio_buf = reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS); - request.aio_nbytes = SSD_BLOCK_SIZE; - request.aio_offset = index_to_out[i].first.getBlockId() * SSD_BLOCK_SIZE; + request.aio_buf = reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size); + request.aio_nbytes = block_size; + request.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #endif requests.push_back(request); @@ -468,7 +477,7 @@ void CachePartition::getValueFromStorage( blocks_to_indices.back().push_back(i); } - AIOContext aio_context(READ_BUFFER_SIZE_BLOCKS); + AIOContext aio_context(read_buffer_size); std::vector processed(requests.size(), false); std::vector events(requests.size()); @@ -496,7 +505,7 @@ void CachePartition::getValueFromStorage( const auto & [file_index, out_index] = index_to_out[idx]; DB::ReadBufferFromMemory buf( reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), - SSD_BLOCK_SIZE - file_index.getAddressInBlock()); + block_size - file_index.getAddressInBlock()); readValueFromBuffer(attribute_index, out[out_index], buf); } @@ -507,7 +516,7 @@ void CachePartition::getValueFromStorage( ++to_pop; /// add new io tasks - const size_t new_tasks_count = std::min(READ_BUFFER_SIZE_BLOCKS - (to_push - to_pop), requests.size() - to_push); + const size_t new_tasks_count = std::min(read_buffer_size - (to_push - to_pop), requests.size() - to_push); size_t pushed = 0; while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) @@ -616,12 +625,18 @@ void CachePartition::remove() } CacheStorage::CacheStorage( - const AttributeTypes & attributes_structure_, const std::string & path_, - const size_t max_partitions_count_, const size_t partition_max_size_) + const AttributeTypes & attributes_structure_, + const std::string & path_, + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_) : attributes_structure(attributes_structure_) , path(path_) - , partition_max_size(partition_max_size_) , max_partitions_count(max_partitions_count_) + , partition_size(partition_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) , log(&Poco::Logger::get("CacheStorage")) { } @@ -678,7 +693,8 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector( AttributeUnderlyingType::utUInt64, attributes_structure, path, - (partitions.empty() ? 0 : partitions.front()->getId() + 1), partition_max_size)); + (partitions.empty() ? 0 : partitions.front()->getId() + 1), + partition_size, block_size, read_buffer_size)); } } @@ -948,15 +964,21 @@ SSDCacheDictionary::SSDCacheDictionary( DictionarySourcePtr source_ptr_, const DictionaryLifetime dict_lifetime_, const std::string & path_, - const size_t partition_max_size_) + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_) : name(name_) , dict_struct(dict_struct_) , source_ptr(std::move(source_ptr_)) , dict_lifetime(dict_lifetime_) , path(path_) - , partition_max_size(partition_max_size_) + , max_partitions_count(max_partitions_count_) + , partition_size(partition_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, 2, partition_max_size) + path, max_partitions_count, partition_size, block_size, read_buffer_size) , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) @@ -1240,17 +1262,36 @@ void registerDictionarySSDCache(DictionaryFactory & factory) "for a dictionary of layout 'range_hashed'", ErrorCodes::BAD_ARGUMENTS}; const auto & layout_prefix = config_prefix + ".layout"; - const auto max_partition_size = config.getInt(layout_prefix + ".ssd.max_partition_size"); - if (max_partition_size == 0) - throw Exception{name + ": dictionary of layout 'cache' cannot have 0 cells", ErrorCodes::TOO_SMALL_BUFFER_SIZE}; + + const auto max_partitions_count = config.getInt(layout_prefix + ".ssd.max_partitions_count", DEFAULT_PARTITIONS_COUNT); + if (max_partitions_count <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + + const auto block_size = config.getInt(layout_prefix + ".ssd.block_size", DEFAULT_SSD_BLOCK_SIZE); + if (block_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; + + const auto partition_size = config.getInt64(layout_prefix + ".ssd.partition_size", DEFAULT_FILE_SIZE); + if (partition_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; + if (partition_size % block_size != 0) + throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + + const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); + if (read_buffer_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + if (read_buffer_size % block_size != 0) + throw Exception{name + ": read_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; const auto path = config.getString(layout_prefix + ".ssd.path"); if (path.empty()) - throw Exception{name + ": dictionary of layout 'cache' cannot have empty path", + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", ErrorCodes::BAD_ARGUMENTS}; const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; - return std::make_unique(name, dict_struct, std::move(source_ptr), dict_lifetime, path, max_partition_size); + return std::make_unique( + name, dict_struct, std::move(source_ptr), dict_lifetime, path, + max_partitions_count, partition_size / block_size, block_size, read_buffer_size / block_size); }; factory.registerLayout("ssd", create_layout, false); } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 31732f25c1af..44b11421da4a 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -84,8 +84,13 @@ class CachePartition using Key = IDictionary::Key; CachePartition( - const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, - const std::string & dir_path, const size_t file_id, const size_t max_size); + const AttributeUnderlyingType & key_structure, + const std::vector & attributes_structure, + const std::string & dir_path, + const size_t file_id, + const size_t max_size, + const size_t block_size, + const size_t read_buffer_size); ~CachePartition(); @@ -147,9 +152,11 @@ class CachePartition template void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; - size_t file_id; - size_t max_size; - std::string path; + const size_t file_id; + const size_t max_size; + const size_t block_size; + const size_t read_buffer_size; + const std::string path; mutable std::shared_mutex rw_lock; @@ -184,8 +191,13 @@ class CacheStorage using AttributeTypes = std::vector; using Key = CachePartition::Key; - CacheStorage(const AttributeTypes & attributes_structure_, const std::string & path_, - const size_t max_partitions_count_, const size_t partition_max_size_); + CacheStorage( + const AttributeTypes & attributes_structure_, + const std::string & path_, + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_); ~CacheStorage(); @@ -223,8 +235,10 @@ class CacheStorage const AttributeTypes attributes_structure; const std::string path; - const size_t partition_max_size; const size_t max_partitions_count; + const size_t partition_size; + const size_t block_size; + const size_t read_buffer_size; mutable std::shared_mutex rw_lock; std::list partitions; @@ -256,7 +270,10 @@ class SSDCacheDictionary final : public IDictionary DictionarySourcePtr source_ptr_, const DictionaryLifetime dict_lifetime_, const std::string & path, - const size_t partition_max_size_); + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_); std::string getName() const override { return name; } @@ -273,13 +290,14 @@ class SSDCacheDictionary final : public IDictionary size_t getElementCount() const override { return element_count.load(std::memory_order_relaxed); } - double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / partition_max_size; } // TODO: fix + double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / partition_size; } // TODO: fix bool supportUpdates() const override { return false; } std::shared_ptr clone() const override { - return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, partition_max_size); + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, + max_partitions_count, partition_size, block_size, read_buffer_size); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -398,7 +416,10 @@ class SSDCacheDictionary final : public IDictionary const DictionaryLifetime dict_lifetime; const std::string path; - const size_t partition_max_size; + const size_t max_partitions_count; + const size_t partition_size; + const size_t block_size; + const size_t read_buffer_size; std::map attribute_index_by_name; std::vector null_values; From 1ee46c0690ade29b13ac1b4f83d83ac67c523cf2 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 18 Jan 2020 14:47:58 +0300 Subject: [PATCH 0036/1102] select * from ssd_dictionary --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 42 ++++++++++++++++--- dbms/src/Dictionaries/SSDCacheDictionary.h | 11 +++-- .../01053_ssd_dictionary.reference | 7 ++++ .../0_stateless/01053_ssd_dictionary.sql | 26 +++++++++++- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 6a10dc471aa5..38d0d22be75e 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "DictionaryBlockInputStream.h" #include "DictionaryFactory.h" #include #include @@ -382,19 +383,16 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray { if (found[i]) { - Poco::Logger::get("kek").information("FOUND BEFORE:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); indices[i].setNotExists(); } else if (auto it = key_to_index_and_metadata.find(ids[i]); it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) { - Poco::Logger::get("kek").information(std::to_string(file_id) + " FOUND BEFORE: inmemory: " + std::to_string(it->second.index.inMemory()) + " " + std::to_string(it->second.index.getBlockId()) + " " + std::to_string(it->second.index.getAddressInBlock())); indices[i] = it->second.index; found[i] = true; } else { - Poco::Logger::get("kek").information("NF:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); indices[i].setNotExists(); } } @@ -499,7 +497,9 @@ void CachePartition::getValueFromStorage( const auto request_id = events[i].data; const auto & request = requests[request_id]; if (events[i].res != static_cast(request.aio_nbytes)) - throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(events[i].res), ErrorCodes::AIO_WRITE_ERROR); + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + + "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_WRITE_ERROR); for (const size_t idx : blocks_to_indices[request_id]) { const auto & [file_index, out_index] = index_to_out[idx]; @@ -602,7 +602,6 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayTypesecond.metadata.expiresAt() <= now) { - Poco::Logger::get("kek").information("NF:: Key :" + std::to_string(ids[i]) + " i: " + std::to_string(i)); out[i] = HAS_NOT_FOUND; } else @@ -617,6 +616,17 @@ size_t CachePartition::getId() const return file_id; } +PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point now) const +{ + const ProfilingScopedReadRWLock read_lock{rw_lock, ProfileEvents::DictCacheLockReadNs}; + + PaddedPODArray array; + for (const auto & [key, index_and_metadata] : key_to_index_and_metadata) + if (!index_and_metadata.metadata.isDefault() && index_and_metadata.metadata.expiresAt() > now) + array.push_back(key); + return array; +} + void CachePartition::remove() { std::unique_lock lock(rw_lock); @@ -894,6 +904,22 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector CacheStorage::getCachedIds() const +{ + PaddedPODArray array; + + const auto now = std::chrono::system_clock::now(); + + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + { + const auto cached_in_partition = partition->getCachedIds(now); + array.insert(std::begin(cached_in_partition), std::end(cached_in_partition)); + } + + return array; +} + void CacheStorage::collectGarbage() { // add partitions to queue @@ -1175,6 +1201,12 @@ void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray; + return std::make_shared(shared_from_this(), max_block_size, storage.getCachedIds(), column_names); +} + size_t SSDCacheDictionary::getAttributeIndex(const std::string & attr_name) const { auto it = attribute_index_by_name.find(attr_name); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 44b11421da4a..789cf4efd5d0 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -140,6 +140,8 @@ class CachePartition size_t getId() const; + PaddedPODArray getCachedIds(const std::chrono::system_clock::time_point now) const; + private: template void getValueFromMemory( @@ -220,6 +222,8 @@ class CacheStorage PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, const DictionaryLifetime lifetime, const std::vector & null_values); + PaddedPODArray getCachedIds() const; + //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; std::exception_ptr getLastException() const { return last_update_exception; } @@ -388,12 +392,7 @@ class SSDCacheDictionary final : public IDictionary void has(const PaddedPODArray & ids, PaddedPODArray & out) const override; - BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override // TODO - { - UNUSED(column_names); - UNUSED(max_block_size); - return nullptr; - } + BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override; private: size_t getAttributeIndex(const std::string & attr_name) const; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index dda210b162b3..88b6db86f94c 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -1,3 +1,10 @@ +TEST_SMALL +-100 +-1 +6 +0 +5 6 7 +1 100 -100 UPDATE DICTIONARY 118 VALUE FROM DISK diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 64e95c868da2..da32cee0009f 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -17,6 +17,28 @@ ORDER BY id; INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100), (2, 3, 4), (5, 6, 7), (10, 9, 8); +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +CREATE DICTIONARY database_for_dict.ssd_dict +( + id UInt64, + a UInt64 DEFAULT 0, + b Int32 DEFAULT -1 +) +PRIMARY KEY id +SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) +LIFETIME(MIN 1000 MAX 2000) +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d')); + +SELECT 'TEST_SMALL'; +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(4)); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(5)); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(6)); + +SELECT * FROM database_for_dict.ssd_dict; +DROP DICTIONARY database_for_dict.ssd_dict; + DROP TABLE IF EXISTS database_for_dict.keys_table; CREATE TABLE database_for_dict.keys_table @@ -44,7 +66,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/1')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d')); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -103,7 +125,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(MAX_PARTITION_SIZE 1000 PATH '/mnt/disk4/clickhouse_dicts/2')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/2d')); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 From cac20e3c9f0e73355ae1fd2217674a39857ad27b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 18 Jan 2020 16:21:07 +0300 Subject: [PATCH 0037/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 4 ++-- dbms/src/Dictionaries/SSDCacheDictionary.h | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 38d0d22be75e..bdb8c430a742 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -435,7 +435,7 @@ void CachePartition::getValueFromStorage( /// sort by (block_id, offset_in_block) std::sort(std::begin(index_to_out), std::end(index_to_out)); - DB::Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); + Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); std::vector requests; std::vector pointers; @@ -503,7 +503,7 @@ void CachePartition::getValueFromStorage( for (const size_t idx : blocks_to_indices[request_id]) { const auto & [file_index, out_index] = index_to_out[idx]; - DB::ReadBufferFromMemory buf( + ReadBufferFromMemory buf( reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), block_size - file_index.getAddressInBlock()); readValueFromBuffer(attribute_index, out[out_index], buf); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 789cf4efd5d0..8f6a6c363b99 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -175,8 +175,8 @@ class CachePartition Attribute keys_buffer; const std::vector attributes_structure; - std::optional> memory; - std::optional write_buffer; + std::optional> memory; + std::optional write_buffer; size_t current_memory_block_id = 0; size_t current_file_block_id = 0; @@ -213,7 +213,6 @@ class CacheStorage // getString(); - void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; From af1161a8e17256e0f36aaf5e0f7c1192f612b04c Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 18 Jan 2020 20:46:00 +0300 Subject: [PATCH 0038/1102] counters --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 67 ++++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 31 ++++++--- 2 files changed, 76 insertions(+), 22 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index bdb8c430a742..982af2283fc2 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -220,7 +220,12 @@ size_t CachePartition::appendBlock( if (!memory) memory.emplace(block_size, BUFFER_ALIGNMENT); if (!write_buffer) + { write_buffer.emplace(memory->data(), block_size); + // codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); + // compressed_buffer.emplace(*write_buffer, codec); + // hashing_buffer.emplace(*compressed_buffer); + } for (size_t index = begin; index < ids.size();) { @@ -616,6 +621,18 @@ size_t CachePartition::getId() const return file_id; } +double CachePartition::getLoadFactor() const +{ + std::shared_lock lock(rw_lock); + return static_cast(current_file_block_id) / max_size; +} + +size_t CachePartition::getElementCount() const +{ + std::shared_lock lock(rw_lock); + return key_to_index_and_metadata.size(); +} + PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point now) const { const ProfilingScopedReadRWLock read_lock{rw_lock, ProfileEvents::DictCacheLockReadNs}; @@ -665,25 +682,33 @@ void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray found(ids.size(), false); - std::shared_lock lock(rw_lock); - for (auto & partition : partitions) - partition->getValue(attribute_index, ids, out, found, now); + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->getValue(attribute_index, ids, out, found, now); - for (size_t i = 0; i < ids.size(); ++i) - if (!found[i]) - not_found[ids[i]].push_back(i); + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); + } + query_count.fetch_add(ids.size(), std::memory_order_relaxed); + hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { - std::shared_lock lock(rw_lock); - for (auto & partition : partitions) - partition->has(ids, out, now); + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->has(ids, out, now); - for (size_t i = 0; i < ids.size(); ++i) - if (out[i] == HAS_NOT_FOUND) - not_found[ids[i]].push_back(i); + for (size_t i = 0; i < ids.size(); ++i) + if (out[i] == HAS_NOT_FOUND) + not_found[ids[i]].push_back(i); + } + query_count.fetch_add(ids.size(), std::memory_order_relaxed); + hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } template @@ -920,6 +945,24 @@ PaddedPODArray CacheStorage::getCachedIds() const return array; } +double CacheStorage::getLoadFactor() const +{ + double result = 0; + std::shared_lock lock(rw_lock); + for (const auto & partition : partitions) + result += partition->getLoadFactor(); + return result / partitions.size(); +} + +size_t CacheStorage::getElementCount() const +{ + size_t result = 0; + std::shared_lock lock(rw_lock); + for (const auto & partition : partitions) + result += partition->getElementCount(); + return result; +} + void CacheStorage::collectGarbage() { // add partitions to queue diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 8f6a6c363b99..667c2528265a 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -16,6 +16,8 @@ #include "IDictionary.h" #include "IDictionarySource.h" #include +#include +#include namespace DB { @@ -142,6 +144,10 @@ class CachePartition PaddedPODArray getCachedIds(const std::chrono::system_clock::time_point now) const; + double getLoadFactor() const; + + size_t getElementCount() const; + private: template void getValueFromMemory( @@ -177,11 +183,12 @@ class CachePartition std::optional> memory; std::optional write_buffer; + // std::optional compressed_buffer; + // std::optional hashing_buffer; + // CompressionCodecPtr codec; size_t current_memory_block_id = 0; size_t current_file_block_id = 0; - - // mutable std::atomic element_count{0}; }; using CachePartitionPtr = std::shared_ptr; @@ -229,6 +236,14 @@ class CacheStorage const std::string & getPath() const { return path; } + size_t getQueryCount() const { return query_count.load(std::memory_order_relaxed); } + + size_t getHitCount() const { return hit_count.load(std::memory_order_acquire); } + + size_t getElementCount() const; + + double getLoadFactor() const; + private: CachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); @@ -258,7 +273,6 @@ class CacheStorage // stats mutable size_t bytes_allocated = 0; - mutable std::atomic element_count{0}; mutable std::atomic hit_count{0}; mutable std::atomic query_count{0}; }; @@ -284,16 +298,16 @@ class SSDCacheDictionary final : public IDictionary size_t getBytesAllocated() const override { return 0; } // TODO: ? - size_t getQueryCount() const override { return query_count.load(std::memory_order_relaxed); } + size_t getQueryCount() const override { return storage.getQueryCount(); } double getHitRate() const override { - return static_cast(hit_count.load(std::memory_order_acquire)) / query_count.load(std::memory_order_relaxed); + return static_cast(storage.getHitCount()) / storage.getQueryCount(); } - size_t getElementCount() const override { return element_count.load(std::memory_order_relaxed); } + size_t getElementCount() const override { return storage.getElementCount(); } - double getLoadFactor() const override { return static_cast(element_count.load(std::memory_order_relaxed)) / partition_size; } // TODO: fix + double getLoadFactor() const override { return storage.getLoadFactor(); } bool supportUpdates() const override { return false; } @@ -425,9 +439,6 @@ class SSDCacheDictionary final : public IDictionary Logger * const log; mutable size_t bytes_allocated = 0; - mutable std::atomic element_count{0}; - mutable std::atomic hit_count{0}; - mutable std::atomic query_count{0}; }; } From ac7b8400f37314c05e08f5d1b0b7d7d686c0f1ae Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 19 Jan 2020 11:49:40 +0300 Subject: [PATCH 0039/1102] write buffer config --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 62 ++++++++++++------- dbms/src/Dictionaries/SSDCacheDictionary.h | 24 ++++--- .../0_stateless/01053_ssd_dictionary.sql | 4 +- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 982af2283fc2..8088025bd35c 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -68,11 +68,10 @@ namespace constexpr size_t DEFAULT_FILE_SIZE = 4 * 1024 * 1024 * 1024ULL; constexpr size_t DEFAULT_PARTITIONS_COUNT = 16; constexpr size_t DEFAULT_READ_BUFFER_SIZE = 16 * DEFAULT_SSD_BLOCK_SIZE; + constexpr size_t DEFAULT_WRITE_BUFFER_SIZE = DEFAULT_SSD_BLOCK_SIZE; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; - constexpr size_t WRITE_BUFFER_SIZE_BLOCKS = 1; // TODO: в параметры - static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -165,11 +164,13 @@ CachePartition::CachePartition( const size_t file_id_, const size_t max_size_, const size_t block_size_, - const size_t read_buffer_size_) + const size_t read_buffer_size_, + const size_t write_buffer_size_) : file_id(file_id_) , max_size(max_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) , path(dir_path + "/" + std::to_string(file_id)) , attributes_structure(attributes_structure_) { @@ -221,7 +222,7 @@ size_t CachePartition::appendBlock( memory.emplace(block_size, BUFFER_ALIGNMENT); if (!write_buffer) { - write_buffer.emplace(memory->data(), block_size); + write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); // codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); // compressed_buffer.emplace(*write_buffer, codec); // hashing_buffer.emplace(*compressed_buffer); @@ -247,7 +248,9 @@ size_t CachePartition::appendBlock( { \ if (sizeof(TYPE) > write_buffer->available()) \ { \ - flush(); \ + write_buffer.reset(); \ + if (++current_memory_block_id == write_buffer_size) \ + flush(); \ flushed = true; \ continue; \ } \ @@ -287,14 +290,14 @@ size_t CachePartition::appendBlock( ids_buffer.push_back(ids[index]); ++index; } - else if (current_file_block_id < max_size) + else if (current_file_block_id < max_size) // next block in write buffer or flushed to ssd { - write_buffer.emplace(memory->data(), block_size); + write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); } - else + else // flushed to ssd, end of current file { memory.reset(); - return index - begin; // End of current file. + return index - begin; } } return ids.size() - begin; @@ -302,7 +305,6 @@ size_t CachePartition::appendBlock( void CachePartition::flush() { - write_buffer.reset(); const auto & ids = std::get>(keys_buffer.values); if (ids.empty()) return; @@ -324,7 +326,7 @@ void CachePartition::flush() write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory->data()); - write_request.aio_nbytes = block_size; + write_request.aio_nbytes = block_size * write_buffer_size; write_request.aio_offset = block_size * current_file_block_id; #endif @@ -367,11 +369,16 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) { - key_to_index_and_metadata[ids[row]].index.setInMemory(false); - key_to_index_and_metadata[ids[row]].index.setBlockId(current_file_block_id); + auto & index = key_to_index_and_metadata[ids[row]].index; + if (index.getInMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. + { + index.setInMemory(false); + index.setBlockId(current_file_block_id + index.getBlockId()); + } } - ++current_file_block_id; + current_file_block_id += write_buffer_size; + current_memory_block_id = 0; /// clear buffer std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); @@ -415,9 +422,9 @@ void CachePartition::getValueFromMemory( const auto & index = indices[i]; if (index.exists() && index.inMemory()) { - const size_t offset = index.getAddressInBlock(); + const size_t offset = index.getBlockId() * block_size + index.getAddressInBlock(); - ReadBufferFromMemory read_buffer(memory->data() + offset, block_size - offset); + ReadBufferFromMemory read_buffer(memory->data() + offset, block_size * write_buffer_size - offset); readValueFromBuffer(attribute_index, out[i], read_buffer); } } @@ -647,7 +654,7 @@ PaddedPODArray CachePartition::getCachedIds(const std::chro void CachePartition::remove() { std::unique_lock lock(rw_lock); - Poco::File(path + BIN_FILE_EXT).remove(); + //Poco::File(path + BIN_FILE_EXT).remove(); //std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); } @@ -657,13 +664,15 @@ CacheStorage::CacheStorage( const size_t max_partitions_count_, const size_t partition_size_, const size_t block_size_, - const size_t read_buffer_size_) + const size_t read_buffer_size_, + const size_t write_buffer_size_) : attributes_structure(attributes_structure_) , path(path_) , max_partitions_count(max_partitions_count_) , partition_size(partition_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) , log(&Poco::Logger::get("CacheStorage")) { } @@ -729,7 +738,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size)); + partition_size, block_size, read_buffer_size, write_buffer_size)); } } @@ -1036,7 +1045,8 @@ SSDCacheDictionary::SSDCacheDictionary( const size_t max_partitions_count_, const size_t partition_size_, const size_t block_size_, - const size_t read_buffer_size_) + const size_t read_buffer_size_, + const size_t write_buffer_size_) : name(name_) , dict_struct(dict_struct_) , source_ptr(std::move(source_ptr_)) @@ -1046,8 +1056,9 @@ SSDCacheDictionary::SSDCacheDictionary( , partition_size(partition_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, max_partitions_count, partition_size, block_size, read_buffer_size) + path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size) , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) @@ -1358,6 +1369,12 @@ void registerDictionarySSDCache(DictionaryFactory & factory) if (read_buffer_size % block_size != 0) throw Exception{name + ": read_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + const auto write_buffer_size = config.getInt64(layout_prefix + ".ssd.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); + if (write_buffer_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + if (write_buffer_size % block_size != 0) + throw Exception{name + ": write_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + const auto path = config.getString(layout_prefix + ".ssd.path"); if (path.empty()) throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", @@ -1366,7 +1383,8 @@ void registerDictionarySSDCache(DictionaryFactory & factory) const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( name, dict_struct, std::move(source_ptr), dict_lifetime, path, - max_partitions_count, partition_size / block_size, block_size, read_buffer_size / block_size); + max_partitions_count, partition_size / block_size, block_size, + read_buffer_size / block_size, write_buffer_size / block_size); }; factory.registerLayout("ssd", create_layout, false); } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 667c2528265a..a0ebf818090c 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -92,7 +92,8 @@ class CachePartition const size_t file_id, const size_t max_size, const size_t block_size, - const size_t read_buffer_size); + const size_t read_buffer_size, + const size_t write_buffer_size); ~CachePartition(); @@ -164,6 +165,7 @@ class CachePartition const size_t max_size; const size_t block_size; const size_t read_buffer_size; + const size_t write_buffer_size; const std::string path; mutable std::shared_mutex rw_lock; @@ -201,12 +203,13 @@ class CacheStorage using Key = CachePartition::Key; CacheStorage( - const AttributeTypes & attributes_structure_, - const std::string & path_, - const size_t max_partitions_count_, - const size_t partition_size_, - const size_t block_size_, - const size_t read_buffer_size_); + const AttributeTypes & attributes_structure, + const std::string & path, + const size_t max_partitions_count, + const size_t partition_size, + const size_t block_size, + const size_t read_buffer_size, + const size_t write_buffer_size); ~CacheStorage(); @@ -257,6 +260,7 @@ class CacheStorage const size_t partition_size; const size_t block_size; const size_t read_buffer_size; + const size_t write_buffer_size; mutable std::shared_mutex rw_lock; std::list partitions; @@ -290,7 +294,8 @@ class SSDCacheDictionary final : public IDictionary const size_t max_partitions_count_, const size_t partition_size_, const size_t block_size_, - const size_t read_buffer_size_); + const size_t read_buffer_size_, + const size_t write_buffer_size_); std::string getName() const override { return name; } @@ -314,7 +319,7 @@ class SSDCacheDictionary final : public IDictionary std::shared_ptr clone() const override { return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, - max_partitions_count, partition_size, block_size, read_buffer_size); + max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -432,6 +437,7 @@ class SSDCacheDictionary final : public IDictionary const size_t partition_size; const size_t block_size; const size_t read_buffer_size; + const size_t write_buffer_size; std::map attribute_index_by_name; std::vector null_values; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index da32cee0009f..27036c246300 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -66,7 +66,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096)); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -125,7 +125,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/2d')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024)); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 From 9bf80448735455aeb978d4d41d7e58648681bb23 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 19 Jan 2020 12:51:19 +0300 Subject: [PATCH 0040/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index a0ebf818090c..f25fa0f99a3e 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -22,9 +22,6 @@ namespace DB { -class SSDCacheDictionary; -class CacheStorage; - using AttributeValueVariant = std::variant< UInt8, UInt16, From de2b39b5875db2b18a8265a2146f9139f2a9d7a1 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 19 Jan 2020 13:54:36 +0300 Subject: [PATCH 0041/1102] debug --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 8088025bd35c..793a6db169c3 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -370,7 +370,7 @@ void CachePartition::flush() for (size_t row = 0; row < ids.size(); ++row) { auto & index = key_to_index_and_metadata[ids[row]].index; - if (index.getInMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. + if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); index.setBlockId(current_file_block_id + index.getBlockId()); @@ -491,6 +491,8 @@ void CachePartition::getValueFromStorage( std::vector processed(requests.size(), false); std::vector events(requests.size()); + for (auto & event : events) + event.res = -1; // TODO: remove size_t to_push = 0; size_t to_pop = 0; @@ -501,7 +503,7 @@ void CachePartition::getValueFromStorage( while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) < 0) { if (errno != EINTR) - throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); } for (size_t i = to_pop; i < to_pop + popped; ++i) From c5b1e3c56f9333e36173a9c57552876f41fe00fe Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 20 Jan 2020 20:29:06 +0300 Subject: [PATCH 0042/1102] rocksdb --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index 6075b7e92432..ac7d79446162 100644 --- a/.gitmodules +++ b/.gitmodules @@ -134,3 +134,6 @@ [submodule "contrib/libc-headers"] path = contrib/libc-headers url = https://github.com/ClickHouse-Extras/libc-headers.git +[submodule "contrib/rocksdb"] + path = contrib/rocksdb + url = https://github.com/facebook/rocksdb.git From 94b41450e59f135539ebc98b7ca2ae2012dd1552 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 25 Jan 2020 22:23:27 +0300 Subject: [PATCH 0043/1102] fix --- dbms/src/Dictionaries/SSDCacheDictionary.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index f25fa0f99a3e..9a69c3ff2f79 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -294,7 +294,9 @@ class SSDCacheDictionary final : public IDictionary const size_t read_buffer_size_, const size_t write_buffer_size_); - std::string getName() const override { return name; } + const std::string & getDatabase() const override { return name; } + const std::string & getName() const override { return name; } + const std::string & getFullName() const override { return getName(); } std::string getTypeName() const override { return "SSDCache"; } From bbcba746b2e67c1553f488d5a0eb82db5ce58694 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 26 Jan 2020 20:35:39 +0300 Subject: [PATCH 0044/1102] strings basic --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 215 ++++++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 28 +-- .../Functions/FunctionsExternalDictionaries.h | 2 + .../01053_ssd_dictionary.reference | 9 +- .../0_stateless/01053_ssd_dictionary.sql | 16 +- 5 files changed, 183 insertions(+), 87 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 793a6db169c3..88fdbd95fffb 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -175,7 +175,7 @@ CachePartition::CachePartition( , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; - keys_buffer.values = PaddedPODArray(); + keys_buffer.values = CachePartition::Attribute::Container(); Poco::File directory(dir_path); if (!directory.exists()) @@ -278,9 +278,24 @@ size_t CachePartition::appendBlock( DISPATCH(Float64) #undef DISPATCH - case AttributeUnderlyingType::utString: - // TODO: string support - break; + case AttributeUnderlyingType::utString: + /*{ + LOG_DEBUG(&Poco::Logger::get("kek"), "string write"); + const auto & value = std::get>(attribute.values)[index]; + if (sizeof(UInt64) + value.size() > write_buffer->available()) + { + write_buffer.reset(); + if (++current_memory_block_id == write_buffer_size) + flush(); + flushed = true; + continue; + } + else + { + writeStringBinary(value, *write_buffer); + } + }*/ + break; } } @@ -386,8 +401,8 @@ void CachePartition::flush() template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::vector & found, - std::chrono::system_clock::time_point now) const + ResultArrayType & out, std::vector & found, + std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); @@ -409,13 +424,17 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } } - getValueFromMemory(attribute_index, indices, out); - getValueFromStorage(attribute_index, indices, out); + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + readValueFromBuffer(attribute_index, out, index, buf); + }; + + getValueFromMemory(indices, set_value); + getValueFromStorage(indices, set_value); } -template -void CachePartition::getValueFromMemory( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const +template +void CachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc set) const { for (size_t i = 0; i < indices.size(); ++i) { @@ -425,14 +444,13 @@ void CachePartition::getValueFromMemory( const size_t offset = index.getBlockId() * block_size + index.getAddressInBlock(); ReadBufferFromMemory read_buffer(memory->data() + offset, block_size * write_buffer_size - offset); - readValueFromBuffer(attribute_index, out[i], read_buffer); + set(i, read_buffer); } } } -template -void CachePartition::getValueFromStorage( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const +template +void CachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc set) const { std::vector> index_to_out; for (size_t i = 0; i < indices.size(); ++i) @@ -520,7 +538,7 @@ void CachePartition::getValueFromStorage( ReadBufferFromMemory buf( reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), block_size - file_index.getAddressInBlock()); - readValueFromBuffer(attribute_index, out[out_index], buf); + set(i, buf); } processed[request_id] = true; @@ -543,18 +561,18 @@ void CachePartition::getValueFromStorage( } template -void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const +void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const { for (size_t i = 0; i < attribute_index; ++i) { switch (attributes_structure[i]) { #define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - { \ - buf.ignore(sizeof(TYPE)); \ - } \ - break; + case AttributeUnderlyingType::ut##TYPE: \ + { \ + buf.ignore(sizeof(TYPE)); \ + } \ + break; DISPATCH(UInt8) DISPATCH(UInt16) @@ -572,39 +590,26 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst DISPATCH(Float64) #undef DISPATCH - case AttributeUnderlyingType::utString: - // TODO: string support - break; + case AttributeUnderlyingType::utString: + /*{ + size_t size = 0; + readVarUInt(size, buf); + buf.ignore(size); + }*/ + break; } } - switch (attributes_structure[attribute_index]) + //if constexpr (!std::is_same_v) + readBinary(dst[index], buf); + /*else { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - readBinary(dst, buf); \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - // TODO: string support - break; - } + LOG_DEBUG(&Poco::Logger::get("kek"), "string READ"); + UNUSED(index); + size_t size = 0; + readVarUInt(size, buf); + dst.insertData(buf.position(), size); + }*/ } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const @@ -820,7 +825,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); + new_keys.values = CachePartition::Attribute::Container(); CachePartition::Attributes new_attributes; { /// TODO: create attributes from structure @@ -832,7 +837,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); \ + new_attributes.back().values = CachePartition::Attribute::Container(); \ break; DISPATCH(UInt8) @@ -852,7 +857,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); + }*/ break; } } @@ -880,7 +889,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); + std::get>(new_keys.values).push_back(id); std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata.emplace_back(); @@ -897,7 +906,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_attributes[i].values); \ + auto & to_values = std::get>(new_attributes[i].values); \ auto & null_value = std::get(null_values[i]); \ to_values.push_back(null_value); \ } \ @@ -920,7 +929,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_attributes[i].values); + auto & null_value = std::get(null_values[i]); + to_values.push_back(null_value); + }*/ break; } } @@ -1004,7 +1017,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ - PaddedPODArray values(column->size()); \ + CachePartition::Attribute::Container values(column->size()); \ const auto raw_data = column->getRawData(); \ memcpy(&values[0], raw_data.data, raw_data.size * sizeof(TYPE)); \ attributes.emplace_back(); \ @@ -1030,7 +1043,18 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( #undef DISPATCH case AttributeUnderlyingType::utString: - // TODO: string support + /*{ + attributes.emplace_back(); + CachePartition::Attribute::Container values(column->size()); + for (size_t j = 0; j < column->size(); ++j) + { + const auto ref = column->getDataAt(j); + values[j].resize(ref.size); + memcpy(values[j].data(), ref.data, ref.size); + } + attributes.back().type = structure[i]; + attributes.back().values = std::move(values); + }*/ break; } } @@ -1180,7 +1204,7 @@ void SSDCacheDictionary::getItemsNumberImpl( required_ids, [&](const auto id, const auto row, const auto & new_attributes) { for (const size_t out_row : not_found_ids[id]) - out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; + out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const size_t id) { @@ -1198,7 +1222,7 @@ void SSDCacheDictionary::getString(const std::string & attribute_name, const Pad const auto null_value = StringRef{std::get(null_values[index])}; - getItemsString(index, ids, out, [&](const size_t) { return null_value; }); + getItemsStringImpl(index, ids, out, [&](const size_t) { return null_value; }); } void SSDCacheDictionary::getString( @@ -1207,7 +1231,7 @@ void SSDCacheDictionary::getString( const auto index = getAttributeIndex(attribute_name); checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); - getItemsString(index, ids, out, [&](const size_t row) { return def->getDataAt(row); }); + getItemsStringImpl(index, ids, out, [&](const size_t row) { return def->getDataAt(row); }); } void SSDCacheDictionary::getString( @@ -1216,17 +1240,78 @@ void SSDCacheDictionary::getString( const auto index = getAttributeIndex(attribute_name); checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); - getItemsString(index, ids, out, [&](const size_t) { return StringRef{def}; }); + getItemsStringImpl(index, ids, out, [&](const size_t) { return StringRef{def}; }); } template -void SSDCacheDictionary::getItemsString(const size_t attribute_index, const PaddedPODArray & ids, +void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const { UNUSED(attribute_index); UNUSED(ids); UNUSED(out); + const auto now = std::chrono::system_clock::now(); + UNUSED(now); UNUSED(get_default); + + return; +/* + std::unordered_map> not_found_ids; + + auto from_cache = ColumnString::create(); + //storage.template getValue(attribute_index, ids, *from_cache, not_found_ids, now); + if (not_found_ids.empty()) + { + out->getChars().resize(from_cache->getChars().size()); + memcpy(out->getChars().data(), from_cache->getChars().data(), from_cache->getChars().size() * sizeof(from_cache->getChars()[0])); + out->getOffsets().resize(from_cache->getOffsets().size()); + memcpy(out->getOffsets().data(), from_cache->getOffsets().data(), from_cache->getOffsets().size() * sizeof(from_cache->getOffsets()[0])); + return; + } + + std::vector required_ids(not_found_ids.size()); + std::transform(std::begin(not_found_ids), std::end(not_found_ids), std::begin(required_ids), [](const auto & pair) { return pair.first; }); + + std::unordered_map update_result; + + storage.update( + source_ptr, + required_ids, + [&](const auto id, const auto row, const auto & new_attributes) + { + update_result[id] = std::get>(new_attributes[attribute_index].values)[row]; + }, + [&](const size_t) {}, + getLifetime(), + null_values); + + LOG_DEBUG(&Poco::Logger::get("log"), "fill data"); + size_t from_cache_counter = 0; + for (size_t row = 0; row < ids.size(); ++row) + { + const auto & id = ids[row]; + auto it = not_found_ids.find(id); + if (it == std::end(not_found_ids)) + { + LOG_DEBUG(&Poco::Logger::get("log"), "fill found " << row << " " << id); + out->insertFrom(*from_cache, from_cache_counter++); + } + else + { + auto it_update = update_result.find(id); + if (it_update != std::end(update_result)) + { + LOG_DEBUG(&Poco::Logger::get("log"), "fill update " << row << " " << id); + out->insertData(it_update->second.data(), it_update->second.size()); + } + else + { + LOG_DEBUG(&Poco::Logger::get("log"), "fill default " << row << " " << id); + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + } + } + }*/ } void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray & out) const @@ -1284,8 +1369,6 @@ AttributeValueVariant SSDCacheDictionary::createAttributeNullValueWithTypeImpl(); bytes_allocated += sizeof(StringRef); - //if (!string_arena) - // string_arena = std::make_unique(); return var_null_value; } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 9a69c3ff2f79..4c8e48ca7b59 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -102,14 +102,12 @@ class CachePartition ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const; - // TODO:: getString - void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; struct Attribute { template - using Container = PaddedPODArray; + using Container = std::vector; AttributeUnderlyingType type; std::variant< @@ -134,6 +132,8 @@ class CachePartition size_t appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin); + //size_t appendDefaults(); + void flush(); void remove(); @@ -147,16 +147,14 @@ class CachePartition size_t getElementCount() const; private: - template - void getValueFromMemory( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + template + void getValueFromMemory(const PaddedPODArray & indices, SetFunc set) const; - template - void getValueFromStorage( - const size_t attribute_index, const PaddedPODArray & indices, ResultArrayType & out) const; + template + void getValueFromStorage(const PaddedPODArray & indices, SetFunc set) const; template - void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; + void readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const; const size_t file_id; const size_t max_size; @@ -211,15 +209,13 @@ class CacheStorage ~CacheStorage(); template - using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + using ResultArrayType = CachePartition::ResultArrayType; template void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; - // getString(); - void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; @@ -230,8 +226,6 @@ class CacheStorage PaddedPODArray getCachedIds() const; - //BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const; - std::exception_ptr getLastException() const { return last_update_exception; } const std::string & getPath() const { return path; } @@ -339,7 +333,7 @@ class SSDCacheDictionary final : public IDictionary std::exception_ptr getLastException() const override { return storage.getLastException(); } template - using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + using ResultArrayType = CacheStorage::ResultArrayType; #define DECLARE(TYPE) \ void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const; @@ -423,7 +417,7 @@ class SSDCacheDictionary final : public IDictionary void getItemsNumberImpl( const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; template - void getItemsString(const size_t attribute_index, const PaddedPODArray & ids, + void getItemsStringImpl(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; const std::string name; diff --git a/dbms/src/Functions/FunctionsExternalDictionaries.h b/dbms/src/Functions/FunctionsExternalDictionaries.h index 080247170fa9..56e2f65c5da4 100644 --- a/dbms/src/Functions/FunctionsExternalDictionaries.h +++ b/dbms/src/Functions/FunctionsExternalDictionaries.h @@ -313,6 +313,7 @@ class FunctionDictGetString final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && + //!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && @@ -499,6 +500,7 @@ class FunctionDictGetStringOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && + //!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr)) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index 88b6db86f94c..2e0e18bd97ce 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -3,14 +3,19 @@ TEST_SMALL -1 6 0 -5 6 7 -1 100 -100 +database +none +1 100 -100 clickhouse +2 3 4 database +5 6 7 columns UPDATE DICTIONARY 118 VALUE FROM DISK -100 +clickhouse VALUE FROM RAM BUFFER 8 + VALUES FROM DISK AND RAM BUFFER 118 HAS diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 27036c246300..b684a7acc8e1 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -11,10 +11,12 @@ CREATE TABLE database_for_dict.table_for_dict id UInt64, a UInt64, b Int32 + --c String ) ENGINE = MergeTree() ORDER BY id; +--INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100, 'clickhouse'), (2, 3, 4, 'database'), (5, 6, 7, 'columns'), (10, 9, 8, ''); INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100), (2, 3, 4), (5, 6, 7), (10, 9, 8); DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; @@ -24,19 +26,22 @@ CREATE DICTIONARY database_for_dict.ssd_dict id UInt64, a UInt64 DEFAULT 0, b Int32 DEFAULT -1 + --c String DEFAULT 'none' ) PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(4)); SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(5)); SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(6)); +--SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(2)); +--SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(3)); -SELECT * FROM database_for_dict.ssd_dict; +SELECT * FROM database_for_dict.ssd_dict ORDER BY id; DROP DICTIONARY database_for_dict.ssd_dict; DROP TABLE IF EXISTS database_for_dict.keys_table; @@ -62,6 +67,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict id UInt64, a UInt64 DEFAULT 0, b Int32 DEFAULT -1 + --c String DEFAULT 'none' ) PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) @@ -76,10 +82,16 @@ SELECT 'VALUE FROM DISK'; -- -100 SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); +-- 'clickhouse' +--SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(1)); + SELECT 'VALUE FROM RAM BUFFER'; -- 8 SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(10)); +-- '' +--SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(10)); + SELECT 'VALUES FROM DISK AND RAM BUFFER'; -- 118 SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; From 6803196d9347ff050d75e8fcc55dec0a61d42745 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 27 Jan 2020 22:29:23 +0300 Subject: [PATCH 0045/1102] impr --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 24 +++++++++----------- dbms/src/Dictionaries/SSDCacheDictionary.h | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 88fdbd95fffb..a3114c265954 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -424,13 +424,14 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } } - auto set_value = [&](const size_t index, ReadBuffer & buf) + getValueFromMemory(indices, [&](const size_t index, ReadBuffer & buf) { - readValueFromBuffer(attribute_index, out, index, buf); - }; - - getValueFromMemory(indices, set_value); - getValueFromStorage(indices, set_value); + readValueFromBuffer(attribute_index, out[index], buf); + }); + getValueFromStorage(indices, [&](const size_t index, ReadBuffer & buf) + { + readValueFromBuffer(attribute_index, out[index], buf); + }); } template @@ -561,7 +562,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } template -void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const +void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const { for (size_t i = 0; i < attribute_index; ++i) { @@ -569,9 +570,7 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - { \ - buf.ignore(sizeof(TYPE)); \ - } \ + buf.ignore(sizeof(TYPE)); \ break; DISPATCH(UInt8) @@ -601,7 +600,7 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst } //if constexpr (!std::is_same_v) - readBinary(dst[index], buf); + readBinary(dst, buf); /*else { LOG_DEBUG(&Poco::Logger::get("kek"), "string READ"); @@ -1018,8 +1017,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( case AttributeUnderlyingType::ut##TYPE: \ { \ CachePartition::Attribute::Container values(column->size()); \ - const auto raw_data = column->getRawData(); \ - memcpy(&values[0], raw_data.data, raw_data.size * sizeof(TYPE)); \ + memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ attributes.emplace_back(); \ attributes.back().type = structure[i]; \ attributes.back().values = std::move(values); \ diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 4c8e48ca7b59..08d0b69c3e3a 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -154,7 +154,7 @@ class CachePartition void getValueFromStorage(const PaddedPODArray & indices, SetFunc set) const; template - void readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const; + void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; const size_t file_id; const size_t max_size; From 468b7237b242b9b709a7680897c9a39bbb86ef98 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 28 Jan 2020 23:32:41 +0300 Subject: [PATCH 0046/1102] strings support --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 61 ++++++++----------- dbms/src/Dictionaries/SSDCacheDictionary.h | 9 +-- .../Functions/FunctionsExternalDictionaries.h | 4 +- .../0_stateless/01053_ssd_dictionary.sql | 23 ++++--- 4 files changed, 44 insertions(+), 53 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index a3114c265954..ca3ae43bd070 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -279,7 +279,7 @@ size_t CachePartition::appendBlock( #undef DISPATCH case AttributeUnderlyingType::utString: - /*{ + { LOG_DEBUG(&Poco::Logger::get("kek"), "string write"); const auto & value = std::get>(attribute.values)[index]; if (sizeof(UInt64) + value.size() > write_buffer->available()) @@ -294,7 +294,7 @@ size_t CachePartition::appendBlock( { writeStringBinary(value, *write_buffer); } - }*/ + } break; } } @@ -424,18 +424,17 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } } - getValueFromMemory(indices, [&](const size_t index, ReadBuffer & buf) + auto set_value = [&](const size_t index, ReadBuffer & buf) { - readValueFromBuffer(attribute_index, out[index], buf); - }); - getValueFromStorage(indices, [&](const size_t index, ReadBuffer & buf) - { - readValueFromBuffer(attribute_index, out[index], buf); - }); + readValueFromBuffer(attribute_index, out, index, buf); + }; + + getValueFromMemory(indices, set_value); + getValueFromStorage(indices, set_value); } template -void CachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc set) const +void CachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const { for (size_t i = 0; i < indices.size(); ++i) { @@ -451,7 +450,7 @@ void CachePartition::getValueFromMemory(const PaddedPODArray & indices, S } template -void CachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc set) const +void CachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const { std::vector> index_to_out; for (size_t i = 0; i < indices.size(); ++i) @@ -539,7 +538,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, ReadBufferFromMemory buf( reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), block_size - file_index.getAddressInBlock()); - set(i, buf); + set(out_index, buf); } processed[request_id] = true; @@ -562,7 +561,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } template -void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const +void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const { for (size_t i = 0; i < attribute_index; ++i) { @@ -590,25 +589,24 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst #undef DISPATCH case AttributeUnderlyingType::utString: - /*{ + { size_t size = 0; readVarUInt(size, buf); buf.ignore(size); - }*/ + } break; } } - //if constexpr (!std::is_same_v) - readBinary(dst, buf); - /*else + if constexpr (!std::is_same_v) + readBinary(dst[index], buf); + else { - LOG_DEBUG(&Poco::Logger::get("kek"), "string READ"); UNUSED(index); size_t size = 0; readVarUInt(size, buf); dst.insertData(buf.position(), size); - }*/ + } } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const @@ -856,11 +854,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); - }*/ + } break; } } @@ -928,11 +926,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_attributes[i].values); auto & null_value = std::get(null_values[i]); to_values.push_back(null_value); - }*/ + } break; } } @@ -1041,7 +1039,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( #undef DISPATCH case AttributeUnderlyingType::utString: - /*{ + { attributes.emplace_back(); CachePartition::Attribute::Container values(column->size()); for (size_t j = 0; j < column->size(); ++j) @@ -1052,7 +1050,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( } attributes.back().type = structure[i]; attributes.back().values = std::move(values); - }*/ + } break; } } @@ -1245,19 +1243,12 @@ template void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const { - UNUSED(attribute_index); - UNUSED(ids); - UNUSED(out); const auto now = std::chrono::system_clock::now(); - UNUSED(now); - UNUSED(get_default); - return; -/* std::unordered_map> not_found_ids; auto from_cache = ColumnString::create(); - //storage.template getValue(attribute_index, ids, *from_cache, not_found_ids, now); + storage.getValue(attribute_index, ids, *from_cache, not_found_ids, now); if (not_found_ids.empty()) { out->getChars().resize(from_cache->getChars().size()); @@ -1309,7 +1300,7 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const out->insertData(to_insert.data, to_insert.size); } } - }*/ + } } void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray & out) const diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 08d0b69c3e3a..bd120d65c7ae 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -95,7 +95,8 @@ class CachePartition ~CachePartition(); template - using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, + std::conditional_t, ColumnString, PaddedPODArray>>; template void getValue(const size_t attribute_index, const PaddedPODArray & ids, @@ -148,13 +149,13 @@ class CachePartition private: template - void getValueFromMemory(const PaddedPODArray & indices, SetFunc set) const; + void getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const; template - void getValueFromStorage(const PaddedPODArray & indices, SetFunc set) const; + void getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const; template - void readValueFromBuffer(const size_t attribute_index, Out & dst, ReadBuffer & buf) const; + void readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const; const size_t file_id; const size_t max_size; diff --git a/dbms/src/Functions/FunctionsExternalDictionaries.h b/dbms/src/Functions/FunctionsExternalDictionaries.h index 56e2f65c5da4..33e9859bf332 100644 --- a/dbms/src/Functions/FunctionsExternalDictionaries.h +++ b/dbms/src/Functions/FunctionsExternalDictionaries.h @@ -313,7 +313,7 @@ class FunctionDictGetString final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && - //!executeDispatch(block, arguments, result, dict_ptr) && + !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && @@ -500,7 +500,7 @@ class FunctionDictGetStringOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatch(block, arguments, result, dict_ptr) && - //!executeDispatch(block, arguments, result, dict_ptr) && + !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr)) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index b684a7acc8e1..273501b280bc 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -10,14 +10,13 @@ CREATE TABLE database_for_dict.table_for_dict ( id UInt64, a UInt64, - b Int32 - --c String + b Int32, + c String ) ENGINE = MergeTree() ORDER BY id; ---INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100, 'clickhouse'), (2, 3, 4, 'database'), (5, 6, 7, 'columns'), (10, 9, 8, ''); -INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100), (2, 3, 4), (5, 6, 7), (10, 9, 8); +INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100, 'clickhouse'), (2, 3, 4, 'database'), (5, 6, 7, 'columns'), (10, 9, 8, ''); DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; @@ -25,8 +24,8 @@ CREATE DICTIONARY database_for_dict.ssd_dict ( id UInt64, a UInt64 DEFAULT 0, - b Int32 DEFAULT -1 - --c String DEFAULT 'none' + b Int32 DEFAULT -1, + c String DEFAULT 'none' ) PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) @@ -38,8 +37,8 @@ SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(4)); SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(5)); SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(6)); ---SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(2)); ---SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(3)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(2)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(3)); SELECT * FROM database_for_dict.ssd_dict ORDER BY id; DROP DICTIONARY database_for_dict.ssd_dict; @@ -66,8 +65,8 @@ CREATE DICTIONARY database_for_dict.ssd_dict ( id UInt64, a UInt64 DEFAULT 0, - b Int32 DEFAULT -1 - --c String DEFAULT 'none' + b Int32 DEFAULT -1, + c String DEFAULT 'none' ) PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) @@ -83,14 +82,14 @@ SELECT 'VALUE FROM DISK'; SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); -- 'clickhouse' ---SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(1)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(1)); SELECT 'VALUE FROM RAM BUFFER'; -- 8 SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(10)); -- '' ---SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(10)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(10)); SELECT 'VALUES FROM DISK AND RAM BUFFER'; -- 118 From 80acedb8be98d279c57620b64976dacb0441d3c8 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 29 Jan 2020 21:51:09 +0300 Subject: [PATCH 0047/1102] getString --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 70 ++++++++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 18 +++-- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index ca3ae43bd070..52f72ee8d3a6 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -403,6 +403,33 @@ template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + ignoreFromBufferToIndex(attribute_index, buf); + readBinary(out[index], buf); + }; + + getImpl(ids, set_value, found, now); +} + +void CachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, + ColumnString * out, std::vector & found, std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t, ReadBuffer & buf) + { + ignoreFromBufferToIndex(attribute_index, buf); + size_t size = 0; + readVarUInt(size, buf); + out->insertData(buf.position(), size); + }; + + getImpl(ids, set_value, found, now); +} + +template +void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found, + std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); @@ -424,13 +451,8 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } } - auto set_value = [&](const size_t index, ReadBuffer & buf) - { - readValueFromBuffer(attribute_index, out, index, buf); - }; - - getValueFromMemory(indices, set_value); - getValueFromStorage(indices, set_value); + getValueFromMemory(indices, set); + getValueFromStorage(indices, set); } template @@ -560,8 +582,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } } -template -void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const +void CachePartition::ignoreFromBufferToIndex(const size_t attribute_index, ReadBuffer & buf) const { for (size_t i = 0; i < attribute_index; ++i) { @@ -597,16 +618,6 @@ void CachePartition::readValueFromBuffer(const size_t attribute_index, Out & dst break; } } - - if constexpr (!std::is_same_v) - readBinary(dst[index], buf); - else - { - UNUSED(index); - size_t size = 0; - readVarUInt(size, buf); - dst.insertData(buf.position(), size); - } } void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const @@ -708,6 +719,25 @@ void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, + ColumnString * out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const +{ + std::vector found(ids.size(), false); + + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->getString(attribute_index, ids, out, found, now); + + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); + } + query_count.fetch_add(ids.size(), std::memory_order_relaxed); + hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); +} + void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { @@ -1248,7 +1278,7 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const std::unordered_map> not_found_ids; auto from_cache = ColumnString::create(); - storage.getValue(attribute_index, ids, *from_cache, not_found_ids, now); + storage.getString(attribute_index, ids, from_cache.get(), not_found_ids, now); if (not_found_ids.empty()) { out->getChars().resize(from_cache->getChars().size()); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index bd120d65c7ae..f2bcd73aeeab 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -95,14 +95,17 @@ class CachePartition ~CachePartition(); template - using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, - std::conditional_t, ColumnString, PaddedPODArray>>; + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; template void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const; + void getString(const size_t attribute_index, const PaddedPODArray & ids, + ColumnString * out, std::vector & found, + std::chrono::system_clock::time_point now) const; + void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; struct Attribute @@ -148,14 +151,17 @@ class CachePartition size_t getElementCount() const; private: + template + void getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found, + std::chrono::system_clock::time_point now) const; + template void getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const; template void getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const; - template - void readValueFromBuffer(const size_t attribute_index, Out & dst, const size_t index, ReadBuffer & buf) const; + void ignoreFromBufferToIndex(const size_t attribute_index, ReadBuffer & buf) const; const size_t file_id; const size_t max_size; @@ -217,6 +223,10 @@ class CacheStorage ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; + void getString(const size_t attribute_index, const PaddedPODArray & ids, + ColumnString * out, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const; + void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; From 98e54aaae0b20c51665873650cbc0b6b9a798745 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 1 Feb 2020 13:12:35 +0300 Subject: [PATCH 0048/1102] string support fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 55 +++++++++++++------- dbms/src/Dictionaries/SSDCacheDictionary.h | 6 +-- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 52f72ee8d3a6..fbede3784132 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -205,8 +205,24 @@ CachePartition::~CachePartition() ::close(fd); } +size_t CachePartition::appendDefaults( + const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin) +{ + std::unique_lock lock(rw_lock); + + const auto & ids = std::get>(new_keys.values); + for (size_t index = begin; index < ids.size(); ++index) + { + auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; + index_and_metadata.metadata = metadata[index]; + index_and_metadata.metadata.setDefault(); + } + + return ids.size() - begin; +} + size_t CachePartition::appendBlock( - const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) + const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); if (current_file_block_id >= max_size) @@ -401,8 +417,8 @@ void CachePartition::flush() template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::vector & found, - std::chrono::system_clock::time_point now) const + ResultArrayType & out, std::vector & found, + std::chrono::system_clock::time_point now) const { auto set_value = [&](const size_t index, ReadBuffer & buf) { @@ -414,14 +430,17 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } void CachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, - ColumnString * out, std::vector & found, std::chrono::system_clock::time_point now) const + StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::chrono::system_clock::time_point now) const { - auto set_value = [&](const size_t, ReadBuffer & buf) + auto set_value = [&](const size_t index, ReadBuffer & buf) { ignoreFromBufferToIndex(attribute_index, buf); size_t size = 0; readVarUInt(size, buf); - out->insertData(buf.position(), size); + char * string_ptr = arena.alloc(size); + memcpy(string_ptr, buf.position(), size); + refs[index].data = string_ptr; + refs[index].size = size; }; getImpl(ids, set_value, found, now); @@ -429,7 +448,7 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra template void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found, - std::chrono::system_clock::time_point now) const + std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); @@ -440,7 +459,7 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, indices[i].setNotExists(); } else if (auto it = key_to_index_and_metadata.find(ids[i]); - it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) + it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) { indices[i] = it->second.index; found[i] = true; @@ -720,15 +739,15 @@ void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ColumnString * out, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const + StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, + std::chrono::system_clock::time_point now) const { std::vector found(ids.size(), false); { std::shared_lock lock(rw_lock); for (auto & partition : partitions) - partition->getString(attribute_index, ids, out, found, now); + partition->getString(attribute_index, ids, refs, arena, found, now); for (size_t i = 0; i < ids.size(); ++i) if (!found[i]) @@ -1277,14 +1296,13 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const std::unordered_map> not_found_ids; - auto from_cache = ColumnString::create(); - storage.getString(attribute_index, ids, from_cache.get(), not_found_ids, now); + StringRefs refs(ids.size()); + ArenaWithFreeLists string_arena; + storage.getString(attribute_index, ids, refs, string_arena, not_found_ids, now); if (not_found_ids.empty()) { - out->getChars().resize(from_cache->getChars().size()); - memcpy(out->getChars().data(), from_cache->getChars().data(), from_cache->getChars().size() * sizeof(from_cache->getChars()[0])); - out->getOffsets().resize(from_cache->getOffsets().size()); - memcpy(out->getOffsets().data(), from_cache->getOffsets().data(), from_cache->getOffsets().size() * sizeof(from_cache->getOffsets()[0])); + for (size_t row = 0; row < ids.size(); ++row) + out->insertData(refs[row].data, refs[row].size); return; } @@ -1305,7 +1323,6 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const null_values); LOG_DEBUG(&Poco::Logger::get("log"), "fill data"); - size_t from_cache_counter = 0; for (size_t row = 0; row < ids.size(); ++row) { const auto & id = ids[row]; @@ -1313,7 +1330,7 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const if (it == std::end(not_found_ids)) { LOG_DEBUG(&Poco::Logger::get("log"), "fill found " << row << " " << id); - out->insertFrom(*from_cache, from_cache_counter++); + out->insertData(refs[row].data, refs[row].size); } else { diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index f2bcd73aeeab..6a04d574bea2 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -103,7 +103,7 @@ class CachePartition std::chrono::system_clock::time_point now) const; void getString(const size_t attribute_index, const PaddedPODArray & ids, - ColumnString * out, std::vector & found, + StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::chrono::system_clock::time_point now) const; void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; @@ -136,7 +136,7 @@ class CachePartition size_t appendBlock(const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin); - //size_t appendDefaults(); + size_t appendDefaults(const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin); void flush(); @@ -224,7 +224,7 @@ class CacheStorage std::chrono::system_clock::time_point now) const; void getString(const size_t attribute_index, const PaddedPODArray & ids, - ColumnString * out, std::unordered_map> & not_found, + StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; void has(const PaddedPODArray & ids, ResultArrayType & out, From 21577c3395ed4238adcf676497cc52eb1d8be121 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 1 Feb 2020 21:13:43 +0300 Subject: [PATCH 0049/1102] default --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 227 ++++++++---------- dbms/src/Dictionaries/SSDCacheDictionary.h | 20 +- .../01053_ssd_dictionary.reference | 3 +- .../0_stateless/01053_ssd_dictionary.sql | 5 +- 4 files changed, 110 insertions(+), 145 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index fbede3784132..468f9860be4b 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -415,9 +415,9 @@ void CachePartition::flush() std::visit([](auto & attr) { attr.clear(); }, keys_buffer.values); } -template +template void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::vector & found, + ResultArrayType & out, std::vector & found, GetDefault & get_default, std::chrono::system_clock::time_point now) const { auto set_value = [&](const size_t index, ReadBuffer & buf) @@ -426,11 +426,17 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray readBinary(out[index], buf); }; - getImpl(ids, set_value, found, now); + auto set_default = [&](const size_t index) + { + out[index] = get_default(index); + }; + + getImpl(ids, set_value, set_default, found, now); } void CachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, - StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::chrono::system_clock::time_point now) const + StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::vector & default_ids, + std::chrono::system_clock::time_point now) const { auto set_value = [&](const size_t index, ReadBuffer & buf) { @@ -443,12 +449,17 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra refs[index].size = size; }; - getImpl(ids, set_value, found, now); + auto set_default = [&](const size_t index) + { + default_ids.push_back(index); + }; + + getImpl(ids, set_value, set_default, found, now); } -template -void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found, - std::chrono::system_clock::time_point now) const +template +void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, SetDefault & set_default, + std::vector & found, std::chrono::system_clock::time_point now) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); @@ -461,7 +472,13 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, else if (auto it = key_to_index_and_metadata.find(ids[i]); it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) { - indices[i] = it->second.index; + if (unlikely(it->second.metadata.isDefault())) + { + indices[i].setNotExists(); + set_default(i); + } + else + indices[i] = it->second.index; found[i] = true; } else @@ -718,41 +735,43 @@ CacheStorage::~CacheStorage() collectGarbage(); } -template +template void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const + GetDefault & get_default, std::chrono::system_clock::time_point now) const { std::vector found(ids.size(), false); { std::shared_lock lock(rw_lock); for (auto & partition : partitions) - partition->getValue(attribute_index, ids, out, found, now); - - for (size_t i = 0; i < ids.size(); ++i) - if (!found[i]) - not_found[ids[i]].push_back(i); + partition->getValue(attribute_index, ids, out, found, get_default, now); } + + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); + query_count.fetch_add(ids.size(), std::memory_order_relaxed); hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } void CacheStorage::getString(const size_t attribute_index, const PaddedPODArray & ids, StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const + std::vector & default_ids, std::chrono::system_clock::time_point now) const { std::vector found(ids.size(), false); { std::shared_lock lock(rw_lock); for (auto & partition : partitions) - partition->getString(attribute_index, ids, refs, arena, found, now); - - for (size_t i = 0; i < ids.size(); ++i) - if (!found[i]) - not_found[ids[i]].push_back(i); + partition->getString(attribute_index, ids, refs, arena, found, default_ids, now); } + + for (size_t i = 0; i < ids.size(); ++i) + if (!found[i]) + not_found[ids[i]].push_back(i); + query_count.fetch_add(ids.size(), std::memory_order_relaxed); hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } @@ -776,7 +795,7 @@ void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, - const DictionaryLifetime lifetime, const std::vector & null_values) + const DictionaryLifetime lifetime) { auto append_block = [this](const CachePartition::Attribute & new_keys, const CachePartition::Attributes & new_attributes, const PaddedPODArray & metadata) @@ -866,52 +885,30 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); - CachePartition::Attributes new_attributes; + auto append_defaults = [this](const CachePartition::Attribute & new_keys, const PaddedPODArray & metadata) { - /// TODO: create attributes from structure - for (const auto & attribute_type : attributes_structure) + size_t inserted = 0; + while (inserted < metadata.size()) { - switch (attribute_type) + if (!partitions.empty()) + inserted += partitions.front()->appendDefaults(new_keys, metadata, inserted); + if (inserted < metadata.size()) { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - new_attributes.emplace_back(); \ - new_attributes.back().type = attribute_type; \ - new_attributes.back().values = CachePartition::Attribute::Container(); \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - { - new_attributes.emplace_back(); - new_attributes.back().type = attribute_type; - new_attributes.back().values = CachePartition::Attribute::Container(); - } - break; + partitions.emplace_front(std::make_unique( + AttributeUnderlyingType::utUInt64, attributes_structure, path, + (partitions.empty() ? 0 : partitions.front()->getId() + 1), + partition_size, block_size, read_buffer_size, write_buffer_size)); } } - } + + collectGarbage(); + }; + + size_t not_found_num = 0, found_num = 0; + /// Check which ids have not been found and require setting null_value + CachePartition::Attribute new_keys; + new_keys.type = AttributeUnderlyingType::utUInt64; + new_keys.values = CachePartition::Attribute::Container(); PaddedPODArray metadata; @@ -942,48 +939,6 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_attributes[i].values); \ - auto & null_value = std::get(null_values[i]); \ - to_values.push_back(null_value); \ - } \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - { - auto & to_values = std::get>(new_attributes[i].values); - auto & null_value = std::get(null_values[i]); - to_values.push_back(null_value); - } - break; - } - } - /// inform caller that the cell has not been found on_id_not_found(id); } @@ -991,7 +946,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector> not_found_ids; - storage.getValue(attribute_index, ids, out, not_found_ids, now); + storage.getValue(attribute_index, ids, out, not_found_ids, get_default, now); if (not_found_ids.empty()) return; @@ -1256,8 +1211,7 @@ void SSDCacheDictionary::getItemsNumberImpl( for (const size_t row : not_found_ids[id]) out[row] = get_default(row); }, - getLifetime(), - null_values); + getLifetime()); } void SSDCacheDictionary::getString(const std::string & attribute_name, const PaddedPODArray & ids, ColumnString * out) const @@ -1298,11 +1252,24 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const StringRefs refs(ids.size()); ArenaWithFreeLists string_arena; - storage.getString(attribute_index, ids, refs, string_arena, not_found_ids, now); + std::vector default_rows; + storage.getString(attribute_index, ids, refs, string_arena, not_found_ids, default_rows, now); + std::sort(std::begin(default_rows), std::end(default_rows)); + if (not_found_ids.empty()) { + size_t default_index = 0; for (size_t row = 0; row < ids.size(); ++row) - out->insertData(refs[row].data, refs[row].size); + { + if (unlikely(default_index != default_rows.size() && default_rows[default_index] == row)) + { + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + ++default_index; + } + else + out->insertData(refs[row].data, refs[row].size); + } return; } @@ -1319,33 +1286,30 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const update_result[id] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const size_t) {}, - getLifetime(), - null_values); + getLifetime()); - LOG_DEBUG(&Poco::Logger::get("log"), "fill data"); + size_t default_index = 0; for (size_t row = 0; row < ids.size(); ++row) { const auto & id = ids[row]; - auto it = not_found_ids.find(id); - if (it == std::end(not_found_ids)) + if (unlikely(default_index != default_rows.size() && default_rows[default_index] == row)) + { + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + ++default_index; + } + else if (auto it = not_found_ids.find(id); it == std::end(not_found_ids)) { - LOG_DEBUG(&Poco::Logger::get("log"), "fill found " << row << " " << id); out->insertData(refs[row].data, refs[row].size); } + else if (auto it_update = update_result.find(id); it_update != std::end(update_result)) + { + out->insertData(it_update->second.data(), it_update->second.size()); + } else { - auto it_update = update_result.find(id); - if (it_update != std::end(update_result)) - { - LOG_DEBUG(&Poco::Logger::get("log"), "fill update " << row << " " << id); - out->insertData(it_update->second.data(), it_update->second.size()); - } - else - { - LOG_DEBUG(&Poco::Logger::get("log"), "fill default " << row << " " << id); - auto to_insert = get_default(row); - out->insertData(to_insert.data, to_insert.size); - } + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); } } } @@ -1374,8 +1338,7 @@ void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; - template + template void getValue(const size_t attribute_index, const PaddedPODArray & ids, - ResultArrayType & out, std::vector & found, + ResultArrayType & out, std::vector & found, GetDefault & get_default, std::chrono::system_clock::time_point now) const; void getString(const size_t attribute_index, const PaddedPODArray & ids, StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, - std::chrono::system_clock::time_point now) const; + std::vector & default_ids, std::chrono::system_clock::time_point now) const; void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; @@ -151,9 +151,9 @@ class CachePartition size_t getElementCount() const; private: - template - void getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found, - std::chrono::system_clock::time_point now) const; + template + void getImpl(const PaddedPODArray & ids, SetFunc & set, SetDefault & set_default, + std::vector & found, std::chrono::system_clock::time_point now) const; template void getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const; @@ -218,14 +218,14 @@ class CacheStorage template using ResultArrayType = CachePartition::ResultArrayType; - template + template void getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const; + GetDefault & get_default, std::chrono::system_clock::time_point now) const; void getString(const size_t attribute_index, const PaddedPODArray & ids, StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, - std::chrono::system_clock::time_point now) const; + std::vector & default_ids, std::chrono::system_clock::time_point now) const; void has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const; @@ -233,7 +233,7 @@ class CacheStorage template void update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, - const DictionaryLifetime lifetime, const std::vector & null_values); + const DictionaryLifetime lifetime); PaddedPODArray getCachedIds() const; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index 2e0e18bd97ce..8035fc015f12 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -24,7 +24,8 @@ HAS 5 10 VALUES NOT FROM TABLE -0 -1 +0 -1 none +0 -1 none DUPLICATE KEYS 1 -100 2 4 diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 273501b280bc..7311d3894708 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -100,8 +100,9 @@ SELECT 'HAS'; SELECT id FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', toUInt64(id)) ORDER BY id; SELECT 'VALUES NOT FROM TABLE'; --- 0 -1 -SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)); +-- 0 -1 none +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)), dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(1000000)); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(1000000)), dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1000000)), dictGetString('database_for_dict.ssd_dict', 'c', toUInt64(1000000)); SELECT 'DUPLICATE KEYS'; SELECT arrayJoin([1, 2, 3, 3, 2, 1]) AS id, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(id)); From d0cc94f0156776a73cc9ce990da84b986260664a Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 1 Feb 2020 21:19:39 +0300 Subject: [PATCH 0050/1102] upd test --- .../tests/queries/0_stateless/01053_ssd_dictionary.reference | 5 ++++- dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index 8035fc015f12..3fd425ceebbd 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -4,10 +4,13 @@ TEST_SMALL 6 0 database -none +a 1 100 -100 clickhouse 2 3 4 database +3 0 -1 a +4 0 -1 a 5 6 7 columns +6 0 -1 a UPDATE DICTIONARY 118 VALUE FROM DISK diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 7311d3894708..9f027cd20392 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -17,6 +17,9 @@ ENGINE = MergeTree() ORDER BY id; INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100, 'clickhouse'), (2, 3, 4, 'database'), (5, 6, 7, 'columns'), (10, 9, 8, ''); +INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'a' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 370; +INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'b' FROM system.numbers LIMIT 370, 370; +INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'c' FROM system.numbers LIMIT 700, 370; DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; From 0fb0239383945049ebe332c5ee96ab241d982099 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 22 Mar 2020 16:23:13 +0300 Subject: [PATCH 0051/1102] store keys --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 21 ++++++++++++++++--- dbms/src/Dictionaries/SSDCacheDictionary.h | 2 +- dbms/src/Parsers/ASTDictionary.cpp | 2 +- .../0_stateless/01053_ssd_dictionary.sql | 6 +++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 468f9860be4b..7dfad530f6cd 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -254,8 +254,22 @@ size_t CachePartition::appendBlock( bool flushed = false; + if (sizeof(UInt64) > write_buffer->available()) + { + write_buffer.reset(); + if (++current_memory_block_id == write_buffer_size) + flush(); + flushed = true; + } + else + { + writeBinary(ids[index], *write_buffer); + } + for (const auto & attribute : new_attributes) { + if (flushed) + break; // TODO:: переделать через столбцы + getDataAt switch (attribute.type) { @@ -422,7 +436,7 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray { auto set_value = [&](const size_t index, ReadBuffer & buf) { - ignoreFromBufferToIndex(attribute_index, buf); + ignoreFromBufferToAttributeIndex(attribute_index, buf); readBinary(out[index], buf); }; @@ -440,7 +454,7 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra { auto set_value = [&](const size_t index, ReadBuffer & buf) { - ignoreFromBufferToIndex(attribute_index, buf); + ignoreFromBufferToAttributeIndex(attribute_index, buf); size_t size = 0; readVarUInt(size, buf); char * string_ptr = arena.alloc(size); @@ -618,8 +632,9 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } } -void CachePartition::ignoreFromBufferToIndex(const size_t attribute_index, ReadBuffer & buf) const +void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const { + buf.ignore(sizeof(UInt64)); for (size_t i = 0; i < attribute_index; ++i) { switch (attributes_structure[i]) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 0c37114cdfdb..7082339a4008 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -161,7 +161,7 @@ class CachePartition template void getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const; - void ignoreFromBufferToIndex(const size_t attribute_index, ReadBuffer & buf) const; + void ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const; const size_t file_id; const size_t max_size; diff --git a/dbms/src/Parsers/ASTDictionary.cpp b/dbms/src/Parsers/ASTDictionary.cpp index 2ac4cb84aca0..a6b22e21640a 100644 --- a/dbms/src/Parsers/ASTDictionary.cpp +++ b/dbms/src/Parsers/ASTDictionary.cpp @@ -96,7 +96,7 @@ void ASTDictionaryLayout::formatImpl(const FormatSettings & settings, << (settings.hilite ? hilite_none : "") << " "; - parameter->second->formatImpl(settings, state, frame); + parameter.second->formatImpl(settings, state, frame); first = false; } settings.ostr << ")"; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 9f027cd20392..13033eb21891 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -33,7 +33,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/0d')); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); @@ -74,7 +74,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096)); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096)); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -140,7 +140,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/mnt/disk4/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024)); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024)); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 From bcea5b26d7947803d3930c9ae6fa2d9dec2aaf0d Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 22 Mar 2020 17:17:42 +0300 Subject: [PATCH 0052/1102] small fix --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 7dfad530f6cd..e8b24de72ef8 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -235,7 +235,7 @@ size_t CachePartition::appendBlock( auto & ids_buffer = std::get>(keys_buffer.values); if (!memory) - memory.emplace(block_size, BUFFER_ALIGNMENT); + memory.emplace(block_size * write_buffer_size, BUFFER_ALIGNMENT); if (!write_buffer) { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); From 9e61702b950e9f922b9c33f770dde22065445f17 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 30 Mar 2020 10:12:12 +0300 Subject: [PATCH 0053/1102] one file --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 167 ++++++++++++++++-- dbms/src/Dictionaries/SSDCacheDictionary.h | 3 + .../01053_ssd_dictionary.reference | 5 +- .../0_stateless/01053_ssd_dictionary.sql | 14 +- 4 files changed, 163 insertions(+), 26 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index e8b24de72ef8..87431b3d728a 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -49,17 +49,18 @@ namespace DB namespace ErrorCodes { - extern const int TYPE_MISMATCH; + extern const int AIO_READ_ERROR; + extern const int AIO_WRITE_ERROR; extern const int BAD_ARGUMENTS; - extern const int UNSUPPORTED_METHOD; + extern const int CANNOT_FSYNC; + extern const int CANNOT_IO_GETEVENTS; + extern const int CANNOT_IO_SUBMIT; + extern const int CANNOT_OPEN_FILE; + extern const int FILE_DOESNT_EXIST; extern const int LOGICAL_ERROR; extern const int TOO_SMALL_BUFFER_SIZE; - extern const int FILE_DOESNT_EXIST; - extern const int CANNOT_OPEN_FILE; - extern const int CANNOT_IO_SUBMIT; - extern const int CANNOT_IO_GETEVENTS; - extern const int AIO_WRITE_ERROR; - extern const int CANNOT_FSYNC; + extern const int TYPE_MISMATCH; + extern const int UNSUPPORTED_METHOD; } namespace @@ -71,6 +72,7 @@ namespace constexpr size_t DEFAULT_WRITE_BUFFER_SIZE = DEFAULT_SSD_BLOCK_SIZE; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; @@ -225,8 +227,8 @@ size_t CachePartition::appendBlock( const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); - if (current_file_block_id >= max_size) - return 0; + //if (current_file_block_id >= max_size) + // return 0; if (new_attributes.size() != attributes_structure.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; @@ -239,6 +241,9 @@ size_t CachePartition::appendBlock( if (!write_buffer) { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); + uint32_t tmp = 0; + write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); + keys_in_block = 0; // codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); // compressed_buffer.emplace(*write_buffer, codec); // hashing_buffer.emplace(*compressed_buffer); @@ -279,6 +284,7 @@ size_t CachePartition::appendBlock( if (sizeof(TYPE) > write_buffer->available()) \ { \ write_buffer.reset(); \ + std::memcpy(memory->data() + block_size * current_memory_block_id, &keys_in_block, sizeof(keys_in_block)); /* set count */ \ if (++current_memory_block_id == write_buffer_size) \ flush(); \ flushed = true; \ @@ -310,11 +316,12 @@ size_t CachePartition::appendBlock( case AttributeUnderlyingType::utString: { - LOG_DEBUG(&Poco::Logger::get("kek"), "string write"); + //LOG_DEBUG(&Poco::Logger::get("kek"), "string write"); const auto & value = std::get>(attribute.values)[index]; if (sizeof(UInt64) + value.size() > write_buffer->available()) { write_buffer.reset(); + std::memcpy(memory->data() + block_size * current_memory_block_id, &keys_in_block, sizeof(keys_in_block)); // set count if (++current_memory_block_id == write_buffer_size) flush(); flushed = true; @@ -334,22 +341,33 @@ size_t CachePartition::appendBlock( key_to_index_and_metadata[ids[index]] = index_and_metadata; ids_buffer.push_back(ids[index]); ++index; + ++keys_in_block; } - else if (current_file_block_id < max_size) // next block in write buffer or flushed to ssd + else //if (current_file_block_id < max_size) // next block in write buffer or flushed to ssd { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); + uint32_t tmp = 0; + write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); + keys_in_block = 0; } - else // flushed to ssd, end of current file + /*else // flushed to ssd, end of current file { + //write_buffer.emplace(memory->data() + current_memory_block_id * block_size + BLOCK_SPECIAL_FIELDS_SIZE, block_size - BLOCK_SPECIAL_FIELDS_SIZE); + keys_in_block = 0; + //clearOldestBlocks(); memory.reset(); return index - begin; - } + }*/ } return ids.size() - begin; } void CachePartition::flush() { + if (current_file_block_id >= max_size) { + clearOldestBlocks(); + } + const auto & ids = std::get>(keys_buffer.values); if (ids.empty()) return; @@ -539,6 +557,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); + // TODO: merge requests std::vector requests; std::vector pointers; std::vector> blocks_to_indices; @@ -603,7 +622,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, if (events[i].res != static_cast(request.aio_nbytes)) throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + - "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_WRITE_ERROR); + "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_READ_ERROR); for (const size_t idx : blocks_to_indices[request_id]) { const auto & [file_index, out_index] = index_to_out[idx]; @@ -632,6 +651,124 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } } +void CachePartition::clearOldestBlocks() +{ + Poco::Logger::get("GC").information("GC clear -----------------"); + // write_buffer_size, because we need to erase the whole buffer. + Memory read_buffer_memory(block_size * write_buffer_size, BUFFER_ALIGNMENT); + + iocb request{}; +#if defined(__FreeBSD__) + request.aio.aio_lio_opcode = LIO_READ; + request.aio.aio_fildes = fd; + request.aio.aio_buf = reinterpret_cast(reinterpret_cast(read_buffer_memory.data())); + request.aio.aio_nbytes = block_size * write_buffer_size; + request.aio.aio_offset = (current_file_block_id % max_size) * block_size; + request.aio_data = 0; +#else + request.aio_lio_opcode = IOCB_CMD_PREAD; + request.aio_fildes = fd; + request.aio_buf = reinterpret_cast(read_buffer_memory.data()); + request.aio_nbytes = block_size * write_buffer_size; + request.aio_offset = (current_file_block_id % max_size) * block_size; + request.aio_data = 0; +#endif + + { + iocb* request_ptr = &request; + io_event event{}; + AIOContext aio_context(1); + + if (io_submit(aio_context.ctx, 1, &request_ptr) != 1) + { + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + } + + if (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) != 1) + { + throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); + } + + if (event.res != static_cast(request.aio_nbytes)) + { + throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "aio_nbytes=" + std::to_string(request.aio_nbytes) + + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); + } + } + + std::vector keys; + keys.reserve(write_buffer_size); + + // TODO: писать кол-во значений + for (size_t i = 0; i < write_buffer_size; ++i) + { + ReadBufferFromMemory read_buffer(read_buffer_memory.data() + i * block_size, block_size); + uint32_t keys_in_current_block = 0; + readBinary(keys_in_current_block, read_buffer); + Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); + + for (uint32_t j = 0; j < keys_in_current_block; ++j) + { + //Poco::Logger::get("GC").information(std::to_string(j) + " " + std::to_string(read_buffer.offset())); + keys.emplace_back(); + readBinary(keys.back(), read_buffer); + + for (size_t attr = 0; attr < attributes_structure.size(); ++attr) + { + + switch (attributes_structure[attr]) + { + #define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + read_buffer.ignore(sizeof(TYPE)); \ + //Poco::Logger::get("GC").information("read TYPE");\ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + #undef DISPATCH + + case AttributeUnderlyingType::utString: + { + //Poco::Logger::get("GC").information("read string"); + size_t size = 0; + readVarUInt(size, read_buffer); + //Poco::Logger::get("GC").information("read string " + std::to_string(size)); + read_buffer.ignore(size); + } + break; + } + } + } + } + + const size_t start_block = current_file_block_id % max_size; + const size_t finish_block = start_block + block_size * write_buffer_size; + for (const auto& key : keys) + { + auto it = key_to_index_and_metadata.find(key); + if (it != std::end(key_to_index_and_metadata)) { + size_t block_id = it->second.index.getBlockId(); + if (start_block <= block_id && block_id < finish_block) { + key_to_index_and_metadata.erase(it); + } + } + } +} + void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const { buf.ignore(sizeof(UInt64)); diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 7082339a4008..849c64daf648 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -138,6 +138,8 @@ class CachePartition size_t appendDefaults(const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin); + void clearOldestBlocks(); + void flush(); void remove(); @@ -187,6 +189,7 @@ class CachePartition std::optional> memory; std::optional write_buffer; + uint32_t keys_in_block = 0; // std::optional compressed_buffer; // std::optional hashing_buffer; // CompressionCodecPtr codec; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference index 3fd425ceebbd..d78ab31f8d91 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.reference @@ -22,10 +22,7 @@ VALUE FROM RAM BUFFER VALUES FROM DISK AND RAM BUFFER 118 HAS -1 -2 -5 -10 +1006 VALUES NOT FROM TABLE 0 -1 none 0 -1 none diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index 13033eb21891..b8dd9158b503 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -18,8 +18,8 @@ ORDER BY id; INSERT INTO database_for_dict.table_for_dict VALUES (1, 100, -100, 'clickhouse'), (2, 3, 4, 'database'), (5, 6, 7, 'columns'), (10, 9, 8, ''); INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'a' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 370; -INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'b' FROM system.numbers LIMIT 370, 370; -INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'c' FROM system.numbers LIMIT 700, 370; +INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'b' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 370, 370; +INSERT INTO database_for_dict.table_for_dict SELECT number, 0, -1, 'c' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 700, 370; DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; @@ -55,11 +55,11 @@ CREATE TABLE database_for_dict.keys_table ENGINE = StripeLog(); INSERT INTO database_for_dict.keys_table VALUES (1); -INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370; +INSERT INTO database_for_dict.keys_table SELECT 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 370; INSERT INTO database_for_dict.keys_table VALUES (2); -INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 370, 370; +INSERT INTO database_for_dict.keys_table SELECT 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 370, 370; INSERT INTO database_for_dict.keys_table VALUES (5); -INSERT INTO database_for_dict.keys_table SELECT intHash64(number) FROM system.numbers LIMIT 700, 370; +INSERT INTO database_for_dict.keys_table SELECT 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 700, 370; INSERT INTO database_for_dict.keys_table VALUES (10); DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; @@ -99,8 +99,8 @@ SELECT 'VALUES FROM DISK AND RAM BUFFER'; SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', toUInt64(id))) FROM database_for_dict.keys_table; SELECT 'HAS'; --- 1 2 5 10 -SELECT id FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', toUInt64(id)) ORDER BY id; +-- 1006 +SELECT count() FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', toUInt64(id)); SELECT 'VALUES NOT FROM TABLE'; -- 0 -1 none From c7a8063e751568fc3b688c0bff1cb7826827da66 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 4 Apr 2020 15:44:16 +0300 Subject: [PATCH 0054/1102] lru --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 50 +++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 67 +++++++++++++++++++- 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 87431b3d728a..735f9a806f8d 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -174,6 +174,7 @@ CachePartition::CachePartition( , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) , path(dir_path + "/" + std::to_string(file_id)) + , key_to_index_and_metadata(100000) , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; @@ -215,9 +216,11 @@ size_t CachePartition::appendDefaults( const auto & ids = std::get>(new_keys.values); for (size_t index = begin; index < ids.size(); ++index) { - auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; + //auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; + IndexAndMetadata index_and_metadata; index_and_metadata.metadata = metadata[index]; index_and_metadata.metadata.setDefault(); + key_to_index_and_metadata.set(ids[index], index_and_metadata); } return ids.size() - begin; @@ -338,7 +341,8 @@ size_t CachePartition::appendBlock( if (!flushed) { - key_to_index_and_metadata[ids[index]] = index_and_metadata; + //key_to_index_and_metadata[ids[index]] = index_and_metadata; + key_to_index_and_metadata.set(ids[index], index_and_metadata); ids_buffer.push_back(ids[index]); ++index; ++keys_in_block; @@ -432,11 +436,17 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) { - auto & index = key_to_index_and_metadata[ids[row]].index; - if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. - { - index.setInMemory(false); - index.setBlockId(current_file_block_id + index.getBlockId()); + IndexAndMetadata index_and_metadata; + if (key_to_index_and_metadata.get(ids[row], index_and_metadata)) { + auto & index = index_and_metadata.index; + if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. + { + index.setInMemory(false); + index.setBlockId(current_file_block_id + index.getBlockId()); + } + key_to_index_and_metadata.set(ids[row], index_and_metadata); + } else { + // Key was evicted from cache. } } @@ -497,20 +507,20 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { + IndexAndMetadata index_and_metadata; if (found[i]) { indices[i].setNotExists(); } - else if (auto it = key_to_index_and_metadata.find(ids[i]); - it != std::end(key_to_index_and_metadata) && it->second.metadata.expiresAt() > now) + else if (key_to_index_and_metadata.get(ids[i], index_and_metadata) && index_and_metadata.metadata.expiresAt() > now) { - if (unlikely(it->second.metadata.isDefault())) + if (unlikely(index_and_metadata.metadata.isDefault())) { indices[i].setNotExists(); set_default(i); } else - indices[i] = it->second.index; + indices[i] = index_and_metadata.index; found[i] = true; } else @@ -722,7 +732,6 @@ void CachePartition::clearOldestBlocks() #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ read_buffer.ignore(sizeof(TYPE)); \ - //Poco::Logger::get("GC").information("read TYPE");\ break; DISPATCH(UInt8) @@ -759,11 +768,11 @@ void CachePartition::clearOldestBlocks() const size_t finish_block = start_block + block_size * write_buffer_size; for (const auto& key : keys) { - auto it = key_to_index_and_metadata.find(key); - if (it != std::end(key_to_index_and_metadata)) { - size_t block_id = it->second.index.getBlockId(); + IndexAndMetadata index_and_metadata; + if (key_to_index_and_metadata.get(key, index_and_metadata)) { + size_t block_id = index_and_metadata.index.getBlockId(); if (start_block <= block_id && block_id < finish_block) { - key_to_index_and_metadata.erase(it); + key_to_index_and_metadata.erase(key); } } } @@ -813,15 +822,14 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayTypesecond.metadata.expiresAt() <= now) + IndexAndMetadata index_and_metadata; + if (!key_to_index_and_metadata.get(ids[i], index_and_metadata) || index_and_metadata.metadata.expiresAt() <= now) { out[i] = HAS_NOT_FOUND; } else { - out[i] = !it->second.metadata.isDefault(); + out[i] = !index_and_metadata.metadata.isDefault(); } } } @@ -849,7 +857,7 @@ PaddedPODArray CachePartition::getCachedIds(const std::chro PaddedPODArray array; for (const auto & [key, index_and_metadata] : key_to_index_and_metadata) - if (!index_and_metadata.metadata.isDefault() && index_and_metadata.metadata.expiresAt() > now) + if (!index_and_metadata.second.metadata.isDefault() && index_and_metadata.second.metadata.expiresAt() > now) array.push_back(key); return array; } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 849c64daf648..03553e6b50d3 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,67 @@ namespace DB { +template +class CLRUCache +{ + using Iter = std::list::iterator; +public: + CLRUCache(size_t max_size_) : max_size(max_size_) { + } + + void set(K key, V val) { + auto it = cache.find(key); + if (it == std::end(cache)) { + auto & item = cache[key]; + item.first = queue.insert(std::end(queue), key); + item.second = val; + if (queue.size() > max_size) { + //Poco::Logger::get("Evict").fatal("eviction"); + cache.erase(queue.front()); + queue.pop_front(); + } + } else { + queue.erase(it->second.first); + it->second.first = queue.insert(std::end(queue), key); + it->second.second = val; + } + } + + bool get(K key, V & val) { + auto it = cache.find(key); + if (it == std::end(cache)) { + return false; + } + val = it->second.second; + queue.erase(it->second.first); + it->second.first = queue.insert(std::end(queue), key); + return true; + } + + void erase(K key) { + auto it = cache.find(key); + queue.erase(it->second.first); + cache.erase(it); + } + + size_t size() const { + return cache.size(); + } + + auto begin() { + return std::begin(cache); + } + + auto end() { + return std::end(cache); + } + +private: + std::unordered_map> cache; + std::list queue; + size_t max_size; +}; + using AttributeValueVariant = std::variant< UInt8, UInt16, @@ -59,7 +121,7 @@ class CachePartition bool operator< (const Index & rhs) const { return index < rhs.index; } /// Stores `is_in_memory` flag, block id, address in uncompressed block - size_t index = 0; + uint64_t index = 0; }; struct Metadata final @@ -182,7 +244,8 @@ class CachePartition Metadata metadata{}; }; - mutable std::unordered_map key_to_index_and_metadata; + //mutable std::unordered_map key_to_index_and_metadata; + mutable CLRUCache key_to_index_and_metadata; Attribute keys_buffer; const std::vector attributes_structure; From 0a52fc80f8cbc136844ca42c1dbbfa405bb798f2 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 4 Apr 2020 16:24:35 +0300 Subject: [PATCH 0055/1102] lru settings --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 31 +++++++++++++------ dbms/src/Dictionaries/SSDCacheDictionary.h | 15 ++++++--- .../0_stateless/01053_ssd_dictionary.sql | 4 +-- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 735f9a806f8d..3b6fea18567d 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -71,6 +71,8 @@ namespace constexpr size_t DEFAULT_READ_BUFFER_SIZE = 16 * DEFAULT_SSD_BLOCK_SIZE; constexpr size_t DEFAULT_WRITE_BUFFER_SIZE = DEFAULT_SSD_BLOCK_SIZE; + constexpr size_t DEFAULT_MAX_STORED_KEYS = 100000; + constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; @@ -167,14 +169,16 @@ CachePartition::CachePartition( const size_t max_size_, const size_t block_size_, const size_t read_buffer_size_, - const size_t write_buffer_size_) + const size_t write_buffer_size_, + const size_t max_stored_keys_) : file_id(file_id_) , max_size(max_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) , path(dir_path + "/" + std::to_string(file_id)) - , key_to_index_and_metadata(100000) + , key_to_index_and_metadata(max_stored_keys) , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; @@ -866,7 +870,7 @@ void CachePartition::remove() { std::unique_lock lock(rw_lock); //Poco::File(path + BIN_FILE_EXT).remove(); - //std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); + std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); } CacheStorage::CacheStorage( @@ -876,7 +880,8 @@ CacheStorage::CacheStorage( const size_t partition_size_, const size_t block_size_, const size_t read_buffer_size_, - const size_t write_buffer_size_) + const size_t write_buffer_size_, + const size_t max_stored_keys_) : attributes_structure(attributes_structure_) , path(path_) , max_partitions_count(max_partitions_count_) @@ -884,6 +889,7 @@ CacheStorage::CacheStorage( , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) , log(&Poco::Logger::get("CacheStorage")) { } @@ -970,7 +976,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size)); + partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1057,7 +1063,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size)); + partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1232,7 +1238,8 @@ SSDCacheDictionary::SSDCacheDictionary( const size_t partition_size_, const size_t block_size_, const size_t read_buffer_size_, - const size_t write_buffer_size_) + const size_t write_buffer_size_, + const size_t max_stored_keys_) : name(name_) , dict_struct(dict_struct_) , source_ptr(std::move(source_ptr_)) @@ -1243,8 +1250,9 @@ SSDCacheDictionary::SSDCacheDictionary( , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size) + path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) , log(&Poco::Logger::get("SSDCacheDictionary")) { if (!this->source_ptr->supportsSelectiveLoad()) @@ -1623,12 +1631,17 @@ void registerDictionarySSDCache(DictionaryFactory & factory) if (path.empty()) throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", ErrorCodes::BAD_ARGUMENTS}; + + const auto max_stored_keys = config.getInt64(layout_prefix + ".ssd.max_stored_keys", DEFAULT_MAX_STORED_KEYS); + if (max_stored_keys <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( name, dict_struct, std::move(source_ptr), dict_lifetime, path, max_partitions_count, partition_size / block_size, block_size, - read_buffer_size / block_size, write_buffer_size / block_size); + read_buffer_size / block_size, write_buffer_size / block_size, + max_stored_keys); }; factory.registerLayout("ssd", create_layout, false); } diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 03553e6b50d3..305418ed7b74 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -38,7 +38,6 @@ class CLRUCache item.first = queue.insert(std::end(queue), key); item.second = val; if (queue.size() > max_size) { - //Poco::Logger::get("Evict").fatal("eviction"); cache.erase(queue.front()); queue.pop_front(); } @@ -152,7 +151,8 @@ class CachePartition const size_t max_size, const size_t block_size, const size_t read_buffer_size, - const size_t write_buffer_size); + const size_t write_buffer_size, + const size_t max_stored_keys); ~CachePartition(); @@ -232,6 +232,7 @@ class CachePartition const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; + const size_t max_stored_keys; const std::string path; mutable std::shared_mutex rw_lock; @@ -277,7 +278,8 @@ class CacheStorage const size_t partition_size, const size_t block_size, const size_t read_buffer_size, - const size_t write_buffer_size); + const size_t write_buffer_size, + const size_t max_stored_keys); ~CacheStorage(); @@ -329,6 +331,7 @@ class CacheStorage const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; + const size_t max_stored_keys; mutable std::shared_mutex rw_lock; std::list partitions; @@ -363,7 +366,8 @@ class SSDCacheDictionary final : public IDictionary const size_t partition_size_, const size_t block_size_, const size_t read_buffer_size_, - const size_t write_buffer_size_); + const size_t write_buffer_size_, + const size_t max_stored_keys_); const std::string & getDatabase() const override { return name; } const std::string & getName() const override { return name; } @@ -389,7 +393,7 @@ class SSDCacheDictionary final : public IDictionary std::shared_ptr clone() const override { return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, - max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size); + max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -508,6 +512,7 @@ class SSDCacheDictionary final : public IDictionary const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; + const size_t max_stored_keys; std::map attribute_index_by_name; std::vector null_values; diff --git a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql index b8dd9158b503..18a79223f8c9 100644 --- a/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/dbms/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -74,7 +74,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096)); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -140,7 +140,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024)); +LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024 MAX_STORED_KEYS 10)); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 From 354325e0cf3cd05f6088ce017fbe904b5a56fff6 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 22 Apr 2020 09:23:25 +0300 Subject: [PATCH 0056/1102] checksums --- dbms/src/Dictionaries/SSDCacheDictionary.cpp | 47 ++++++++++++++++---- dbms/src/Dictionaries/SSDCacheDictionary.h | 6 +-- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.cpp b/dbms/src/Dictionaries/SSDCacheDictionary.cpp index 3b6fea18567d..32fb31717370 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.cpp +++ b/dbms/src/Dictionaries/SSDCacheDictionary.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace ProfileEvents { @@ -61,6 +62,7 @@ namespace ErrorCodes extern const int TOO_SMALL_BUFFER_SIZE; extern const int TYPE_MISMATCH; extern const int UNSUPPORTED_METHOD; + extern const int CORRUPTED_DATA; } namespace @@ -74,6 +76,7 @@ namespace constexpr size_t DEFAULT_MAX_STORED_KEYS = 100000; constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t BLOCK_CHECKSUM_SIZE = 8; constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); @@ -248,11 +251,12 @@ size_t CachePartition::appendBlock( if (!write_buffer) { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); - uint32_t tmp = 0; + uint64_t tmp = 0; + write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); keys_in_block = 0; - // codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); - // compressed_buffer.emplace(*write_buffer, codec); + //codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); + //compressed_buffer.emplace(*write_buffer, codec); // hashing_buffer.emplace(*compressed_buffer); } @@ -269,6 +273,9 @@ size_t CachePartition::appendBlock( if (sizeof(UInt64) > write_buffer->available()) { write_buffer.reset(); + std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count + uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum + std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); if (++current_memory_block_id == write_buffer_size) flush(); flushed = true; @@ -291,7 +298,9 @@ size_t CachePartition::appendBlock( if (sizeof(TYPE) > write_buffer->available()) \ { \ write_buffer.reset(); \ - std::memcpy(memory->data() + block_size * current_memory_block_id, &keys_in_block, sizeof(keys_in_block)); /* set count */ \ + std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); /* set count */ \ + uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); /* checksum */ \ + std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); \ if (++current_memory_block_id == write_buffer_size) \ flush(); \ flushed = true; \ @@ -328,7 +337,9 @@ size_t CachePartition::appendBlock( if (sizeof(UInt64) + value.size() > write_buffer->available()) { write_buffer.reset(); - std::memcpy(memory->data() + block_size * current_memory_block_id, &keys_in_block, sizeof(keys_in_block)); // set count + std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count + uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum + std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); if (++current_memory_block_id == write_buffer_size) flush(); flushed = true; @@ -354,7 +365,8 @@ size_t CachePartition::appendBlock( else //if (current_file_block_id < max_size) // next block in write buffer or flushed to ssd { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); - uint32_t tmp = 0; + uint64_t tmp = 0; + write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); keys_in_block = 0; } @@ -540,6 +552,7 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, template void CachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const { + // Do not check checksum while reading from memory. for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; @@ -637,6 +650,16 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_READ_ERROR); + + uint64_t checksum = 0; + ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); + readBinary(checksum, buf_special); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(reinterpret_cast(request.aio_buf) + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + if (checksum != calculated_checksum) + { + throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); + } + for (const size_t idx : blocks_to_indices[request_id]) { const auto & [file_index, out_index] = index_to_out[idx]; @@ -718,13 +741,21 @@ void CachePartition::clearOldestBlocks() for (size_t i = 0; i < write_buffer_size; ++i) { ReadBufferFromMemory read_buffer(read_buffer_memory.data() + i * block_size, block_size); + + uint64_t checksum = 0; + readBinary(checksum, read_buffer); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(read_buffer_memory.data() + i * block_size + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + if (checksum != calculated_checksum) + { + throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); + } + uint32_t keys_in_current_block = 0; readBinary(keys_in_current_block, read_buffer); Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); for (uint32_t j = 0; j < keys_in_current_block; ++j) { - //Poco::Logger::get("GC").information(std::to_string(j) + " " + std::to_string(read_buffer.offset())); keys.emplace_back(); readBinary(keys.back(), read_buffer); @@ -756,10 +787,8 @@ void CachePartition::clearOldestBlocks() case AttributeUnderlyingType::utString: { - //Poco::Logger::get("GC").information("read string"); size_t size = 0; readVarUInt(size, read_buffer); - //Poco::Logger::get("GC").information("read string " + std::to_string(size)); read_buffer.ignore(size); } break; diff --git a/dbms/src/Dictionaries/SSDCacheDictionary.h b/dbms/src/Dictionaries/SSDCacheDictionary.h index 305418ed7b74..93758e9182f1 100644 --- a/dbms/src/Dictionaries/SSDCacheDictionary.h +++ b/dbms/src/Dictionaries/SSDCacheDictionary.h @@ -254,9 +254,9 @@ class CachePartition std::optional> memory; std::optional write_buffer; uint32_t keys_in_block = 0; - // std::optional compressed_buffer; - // std::optional hashing_buffer; - // CompressionCodecPtr codec; + //std::optional compressed_buffer; + //std::optional hashing_buffer; + //CompressionCodecPtr codec; size_t current_memory_block_id = 0; size_t current_file_block_id = 0; From b01ea01e8727b8aeb6e1ca8f6ed6ff722a0c46e6 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 22 Apr 2020 20:27:35 +0300 Subject: [PATCH 0057/1102] metadata on ssd --- src/Dictionaries/SSDCacheDictionary.cpp | 260 +++++++++++++----------- src/Dictionaries/SSDCacheDictionary.h | 12 +- 2 files changed, 141 insertions(+), 131 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 32fb31717370..a46d18fbd0a9 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -218,29 +218,25 @@ CachePartition::~CachePartition() size_t CachePartition::appendDefaults( const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin) { - std::unique_lock lock(rw_lock); + /*std::unique_lock lock(rw_lock); const auto & ids = std::get>(new_keys.values); for (size_t index = begin; index < ids.size(); ++index) { - //auto & index_and_metadata = key_to_index_and_metadata[ids[index]]; IndexAndMetadata index_and_metadata; index_and_metadata.metadata = metadata[index]; index_and_metadata.metadata.setDefault(); key_to_index_and_metadata.set(ids[index], index_and_metadata); } - - return ids.size() - begin; + */ + return appendBlock(new_keys, Attributes{}, metadata, begin); } size_t CachePartition::appendBlock( const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); - //if (current_file_block_id >= max_size) - // return 0; - - if (new_attributes.size() != attributes_structure.size()) + if (!new_attributes.empty() && new_attributes.size() != attributes_structure.size()) throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; const auto & ids = std::get>(new_keys.values); @@ -248,18 +244,34 @@ size_t CachePartition::appendBlock( if (!memory) memory.emplace(block_size * write_buffer_size, BUFFER_ALIGNMENT); - if (!write_buffer) - { + + auto init_write_buffer = [&]() { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); uint64_t tmp = 0; write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); keys_in_block = 0; + }; + + if (!write_buffer) + { + init_write_buffer(); //codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); //compressed_buffer.emplace(*write_buffer, codec); // hashing_buffer.emplace(*compressed_buffer); } + bool flushed = false; + auto finish_block = [&]() { + write_buffer.reset(); + std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count + uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum + std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); + if (++current_memory_block_id == write_buffer_size) + flush(); + flushed = true; + }; + for (size_t index = begin; index < ids.size();) { IndexAndMetadata index_and_metadata; @@ -268,28 +280,21 @@ size_t CachePartition::appendBlock( index_and_metadata.index.setAddressInBlock(write_buffer->offset()); index_and_metadata.metadata = metadata[index]; - bool flushed = false; - - if (sizeof(UInt64) > write_buffer->available()) + flushed = false; + if (2 * sizeof(UInt64) > write_buffer->available()) // place for key and metadata { - write_buffer.reset(); - std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count - uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum - std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); - if (++current_memory_block_id == write_buffer_size) - flush(); - flushed = true; + finish_block(); } else { writeBinary(ids[index], *write_buffer); + writeBinary(metadata[index].data, *write_buffer); } for (const auto & attribute : new_attributes) { if (flushed) break; - // TODO:: переделать через столбцы + getDataAt switch (attribute.type) { #define DISPATCH(TYPE) \ @@ -297,13 +302,7 @@ size_t CachePartition::appendBlock( { \ if (sizeof(TYPE) > write_buffer->available()) \ { \ - write_buffer.reset(); \ - std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); /* set count */ \ - uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); /* checksum */ \ - std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); \ - if (++current_memory_block_id == write_buffer_size) \ - flush(); \ - flushed = true; \ + finish_block(); \ continue; \ } \ else \ @@ -336,13 +335,7 @@ size_t CachePartition::appendBlock( const auto & value = std::get>(attribute.values)[index]; if (sizeof(UInt64) + value.size() > write_buffer->available()) { - write_buffer.reset(); - std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count - uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum - std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); - if (++current_memory_block_id == write_buffer_size) - flush(); - flushed = true; + finish_block(); continue; } else @@ -356,28 +349,15 @@ size_t CachePartition::appendBlock( if (!flushed) { - //key_to_index_and_metadata[ids[index]] = index_and_metadata; key_to_index_and_metadata.set(ids[index], index_and_metadata); ids_buffer.push_back(ids[index]); ++index; ++keys_in_block; } - else //if (current_file_block_id < max_size) // next block in write buffer or flushed to ssd + else // next block in write buffer or flushed to ssd { - write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); - uint64_t tmp = 0; - write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); - write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); - keys_in_block = 0; + init_write_buffer(); } - /*else // flushed to ssd, end of current file - { - //write_buffer.emplace(memory->data() + current_memory_block_id * block_size + BLOCK_SPECIAL_FIELDS_SIZE, block_size - BLOCK_SPECIAL_FIELDS_SIZE); - keys_in_block = 0; - //clearOldestBlocks(); - memory.reset(); - return index - begin; - }*/ } return ids.size() - begin; } @@ -480,16 +460,27 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray { auto set_value = [&](const size_t index, ReadBuffer & buf) { - ignoreFromBufferToAttributeIndex(attribute_index, buf); - readBinary(out[index], buf); + buf.ignore(sizeof(Key)); // key + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) { + if (metadata.isDefault()) { + out[index] = get_default(index); + } else { + ignoreFromBufferToAttributeIndex(attribute_index, buf); + readBinary(out[index], buf); + } + found[index] = true; + } }; - auto set_default = [&](const size_t index) + /*auto set_default = [&](const size_t index) { out[index] = get_default(index); - }; + };*/ - getImpl(ids, set_value, set_default, found, now); + getImpl(ids, set_value, found); } void CachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, @@ -498,26 +489,55 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra { auto set_value = [&](const size_t index, ReadBuffer & buf) { - ignoreFromBufferToAttributeIndex(attribute_index, buf); - size_t size = 0; - readVarUInt(size, buf); - char * string_ptr = arena.alloc(size); - memcpy(string_ptr, buf.position(), size); - refs[index].data = string_ptr; - refs[index].size = size; + buf.ignore(sizeof(Key)); // key + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) { + if (metadata.isDefault()) { + default_ids.push_back(index); + } else { + ignoreFromBufferToAttributeIndex(attribute_index, buf); + size_t size = 0; + readVarUInt(size, buf); + char * string_ptr = arena.alloc(size); + memcpy(string_ptr, buf.position(), size); + refs[index].data = string_ptr; + refs[index].size = size; + } + found[index] = true; + } }; - auto set_default = [&](const size_t index) + /*auto set_default = [&](const size_t index) { + buf.ignore(sizeof(UInt64)); // key default_ids.push_back(index); - }; + };*/ - getImpl(ids, set_value, set_default, found, now); + getImpl(ids, set_value, found); } -template -void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, SetDefault & set_default, +void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + buf.ignore(sizeof(Key)); // key + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) { + out[index] = !metadata.isDefault(); + } + }; + + getImpl(ids, set_value, found); +} + +template +void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, + std::vector & found) const { std::shared_lock lock(rw_lock); PaddedPODArray indices(ids.size()); @@ -528,16 +548,15 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, { indices[i].setNotExists(); } - else if (key_to_index_and_metadata.get(ids[i], index_and_metadata) && index_and_metadata.metadata.expiresAt() > now) + else if (key_to_index_and_metadata.get(ids[i], index_and_metadata)/* && index_and_metadata.metadata.expiresAt() > now*/) { - if (unlikely(index_and_metadata.metadata.isDefault())) + /*if (unlikely(index_and_metadata.metadata.isDefault())) { indices[i].setNotExists(); - set_default(i); + //set_default(i); } - else - indices[i] = index_and_metadata.index; - found[i] = true; + else*/ + indices[i] = index_and_metadata.index; } else { @@ -758,40 +777,45 @@ void CachePartition::clearOldestBlocks() { keys.emplace_back(); readBinary(keys.back(), read_buffer); - - for (size_t attr = 0; attr < attributes_structure.size(); ++attr) + Metadata metadata; + readBinary(metadata.data, read_buffer); + + if (!metadata.isDefault()) { - - switch (attributes_structure[attr]) + for (size_t attr = 0; attr < attributes_structure.size(); ++attr) { - #define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - read_buffer.ignore(sizeof(TYPE)); \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) - #undef DISPATCH - - case AttributeUnderlyingType::utString: + + switch (attributes_structure[attr]) { - size_t size = 0; - readVarUInt(size, read_buffer); - read_buffer.ignore(size); + #define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + read_buffer.ignore(sizeof(TYPE)); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + #undef DISPATCH + + case AttributeUnderlyingType::utString: + { + size_t size = 0; + readVarUInt(size, read_buffer); + read_buffer.ignore(size); + } + break; } - break; } } } @@ -813,7 +837,7 @@ void CachePartition::clearOldestBlocks() void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const { - buf.ignore(sizeof(UInt64)); + //buf.ignore(2 * sizeof(UInt64)); // key and metadata for (size_t i = 0; i < attribute_index; ++i) { switch (attributes_structure[i]) @@ -850,23 +874,6 @@ void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_ind } } -void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const -{ - std::shared_lock lock(rw_lock); - for (size_t i = 0; i < ids.size(); ++i) - { - IndexAndMetadata index_and_metadata; - if (!key_to_index_and_metadata.get(ids[i], index_and_metadata) || index_and_metadata.metadata.expiresAt() <= now) - { - out[i] = HAS_NOT_FOUND; - } - else - { - out[i] = !index_and_metadata.metadata.isDefault(); - } - } -} - size_t CachePartition::getId() const { return file_id; @@ -884,13 +891,13 @@ size_t CachePartition::getElementCount() const return key_to_index_and_metadata.size(); } -PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point now) const +PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { const ProfilingScopedReadRWLock read_lock{rw_lock, ProfileEvents::DictCacheLockReadNs}; PaddedPODArray array; for (const auto & [key, index_and_metadata] : key_to_index_and_metadata) - if (!index_and_metadata.second.metadata.isDefault() && index_and_metadata.second.metadata.expiresAt() > now) + if (!index_and_metadata.second.metadata.isDefault() /* && index_and_metadata.second.metadata.expiresAt() > now */) array.push_back(key); return array; } @@ -974,15 +981,20 @@ void CacheStorage::getString(const size_t attribute_index, const PaddedPODArray< void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { + for (size_t i = 0; i < ids.size(); ++i) + out[i] = HAS_NOT_FOUND; + std::vector found(ids.size(), false); + { std::shared_lock lock(rw_lock); for (auto & partition : partitions) - partition->has(ids, out, now); + partition->has(ids, out, found, now); for (size_t i = 0; i < ids.size(); ++i) if (out[i] == HAS_NOT_FOUND) not_found[ids[i]].push_back(i); } + query_count.fetch_add(ids.size(), std::memory_order_relaxed); hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 93758e9182f1..650e6f3666e2 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -168,7 +168,8 @@ class CachePartition StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::vector & default_ids, std::chrono::system_clock::time_point now) const; - void has(const PaddedPODArray & ids, ResultArrayType & out, std::chrono::system_clock::time_point now) const; + void has(const PaddedPODArray & ids, ResultArrayType & out, + std::vector & found, std::chrono::system_clock::time_point now) const; struct Attribute { @@ -215,9 +216,8 @@ class CachePartition size_t getElementCount() const; private: - template - void getImpl(const PaddedPODArray & ids, SetFunc & set, SetDefault & set_default, - std::vector & found, std::chrono::system_clock::time_point now) const; + template + void getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found) const; template void getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const; @@ -245,17 +245,15 @@ class CachePartition Metadata metadata{}; }; - //mutable std::unordered_map key_to_index_and_metadata; mutable CLRUCache key_to_index_and_metadata; Attribute keys_buffer; + //std::vector metadata_buffer; const std::vector attributes_structure; std::optional> memory; std::optional write_buffer; uint32_t keys_in_block = 0; - //std::optional compressed_buffer; - //std::optional hashing_buffer; //CompressionCodecPtr codec; size_t current_memory_block_id = 0; From 04347a94d4c09ff76d605f3d59d373279c135818 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 22 Apr 2020 21:00:08 +0300 Subject: [PATCH 0058/1102] get rid of metadata in ram --- src/Dictionaries/SSDCacheDictionary.cpp | 81 ++++++------------------- src/Dictionaries/SSDCacheDictionary.h | 10 +-- 2 files changed, 21 insertions(+), 70 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index a46d18fbd0a9..4ec24b452bdc 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -181,7 +181,7 @@ CachePartition::CachePartition( , write_buffer_size(write_buffer_size_) , max_stored_keys(max_stored_keys_) , path(dir_path + "/" + std::to_string(file_id)) - , key_to_index_and_metadata(max_stored_keys) + , key_to_index(max_stored_keys) , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; @@ -218,17 +218,6 @@ CachePartition::~CachePartition() size_t CachePartition::appendDefaults( const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin) { - /*std::unique_lock lock(rw_lock); - - const auto & ids = std::get>(new_keys.values); - for (size_t index = begin; index < ids.size(); ++index) - { - IndexAndMetadata index_and_metadata; - index_and_metadata.metadata = metadata[index]; - index_and_metadata.metadata.setDefault(); - key_to_index_and_metadata.set(ids[index], index_and_metadata); - } - */ return appendBlock(new_keys, Attributes{}, metadata, begin); } @@ -257,8 +246,6 @@ size_t CachePartition::appendBlock( { init_write_buffer(); //codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); - //compressed_buffer.emplace(*write_buffer, codec); - // hashing_buffer.emplace(*compressed_buffer); } bool flushed = false; @@ -274,11 +261,10 @@ size_t CachePartition::appendBlock( for (size_t index = begin; index < ids.size();) { - IndexAndMetadata index_and_metadata; - index_and_metadata.index.setInMemory(true); - index_and_metadata.index.setBlockId(current_memory_block_id); - index_and_metadata.index.setAddressInBlock(write_buffer->offset()); - index_and_metadata.metadata = metadata[index]; + Index cache_index; + cache_index.setInMemory(true); + cache_index.setBlockId(current_memory_block_id); + cache_index.setAddressInBlock(write_buffer->offset()); flushed = false; if (2 * sizeof(UInt64) > write_buffer->available()) // place for key and metadata @@ -331,7 +317,6 @@ size_t CachePartition::appendBlock( case AttributeUnderlyingType::utString: { - //LOG_DEBUG(&Poco::Logger::get("kek"), "string write"); const auto & value = std::get>(attribute.values)[index]; if (sizeof(UInt64) + value.size() > write_buffer->available()) { @@ -349,7 +334,7 @@ size_t CachePartition::appendBlock( if (!flushed) { - key_to_index_and_metadata.set(ids[index], index_and_metadata); + key_to_index.set(ids[index], cache_index); ids_buffer.push_back(ids[index]); ++index; ++keys_in_block; @@ -432,17 +417,14 @@ void CachePartition::flush() /// commit changes in index for (size_t row = 0; row < ids.size(); ++row) { - IndexAndMetadata index_and_metadata; - if (key_to_index_and_metadata.get(ids[row], index_and_metadata)) { - auto & index = index_and_metadata.index; + Index index; + if (key_to_index.get(ids[row], index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); index.setBlockId(current_file_block_id + index.getBlockId()); } - key_to_index_and_metadata.set(ids[row], index_and_metadata); - } else { - // Key was evicted from cache. + key_to_index.set(ids[row], index); } } @@ -475,11 +457,6 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray } }; - /*auto set_default = [&](const size_t index) - { - out[index] = get_default(index); - };*/ - getImpl(ids, set_value, found); } @@ -509,12 +486,6 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra } }; - /*auto set_default = [&](const size_t index) - { - buf.ignore(sizeof(UInt64)); // key - default_ids.push_back(index); - };*/ - getImpl(ids, set_value, found); } @@ -543,25 +514,13 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, PaddedPODArray indices(ids.size()); for (size_t i = 0; i < ids.size(); ++i) { - IndexAndMetadata index_and_metadata; + Index index; if (found[i]) - { indices[i].setNotExists(); - } - else if (key_to_index_and_metadata.get(ids[i], index_and_metadata)/* && index_and_metadata.metadata.expiresAt() > now*/) - { - /*if (unlikely(index_and_metadata.metadata.isDefault())) - { - indices[i].setNotExists(); - //set_default(i); - } - else*/ - indices[i] = index_and_metadata.index; - } + else if (key_to_index.get(ids[i], index)) + indices[i] = index; else - { indices[i].setNotExists(); - } } getValueFromMemory(indices, set); @@ -825,11 +784,11 @@ void CachePartition::clearOldestBlocks() const size_t finish_block = start_block + block_size * write_buffer_size; for (const auto& key : keys) { - IndexAndMetadata index_and_metadata; - if (key_to_index_and_metadata.get(key, index_and_metadata)) { - size_t block_id = index_and_metadata.index.getBlockId(); + Index index; + if (key_to_index.get(key, index)) { + size_t block_id = index.getBlockId(); if (start_block <= block_id && block_id < finish_block) { - key_to_index_and_metadata.erase(key); + key_to_index.erase(key); } } } @@ -888,7 +847,7 @@ double CachePartition::getLoadFactor() const size_t CachePartition::getElementCount() const { std::shared_lock lock(rw_lock); - return key_to_index_and_metadata.size(); + return key_to_index.size(); } PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const @@ -896,16 +855,14 @@ PaddedPODArray CachePartition::getCachedIds(const std::chro const ProfilingScopedReadRWLock read_lock{rw_lock, ProfileEvents::DictCacheLockReadNs}; PaddedPODArray array; - for (const auto & [key, index_and_metadata] : key_to_index_and_metadata) - if (!index_and_metadata.second.metadata.isDefault() /* && index_and_metadata.second.metadata.expiresAt() > now */) - array.push_back(key); + for (const auto & [key, index] : key_to_index) + array.push_back(key); // TODO: exclude default return array; } void CachePartition::remove() { std::unique_lock lock(rw_lock); - //Poco::File(path + BIN_FILE_EXT).remove(); std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); } diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 650e6f3666e2..547e9d1e5383 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -100,6 +100,7 @@ using AttributeValueVariant = std::variant< Float64, String>; + class CachePartition { public: @@ -239,16 +240,9 @@ class CachePartition int fd = -1; - struct IndexAndMetadata final - { - Index index{}; - Metadata metadata{}; - }; - - mutable CLRUCache key_to_index_and_metadata; + mutable CLRUCache key_to_index; Attribute keys_buffer; - //std::vector metadata_buffer; const std::vector attributes_structure; std::optional> memory; From cebe0d5850066c7f93305a058c2f80d51d388f59 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 23 Apr 2020 14:44:12 +0300 Subject: [PATCH 0059/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 60 ++++++++++++++----------- src/Dictionaries/SSDCacheDictionary.h | 41 ++++++++++------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 4ec24b452bdc..a38d21cad9c4 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "DictionaryBlockInputStream.h" #include "DictionaryFactory.h" #include @@ -187,9 +186,7 @@ CachePartition::CachePartition( keys_buffer.type = AttributeUnderlyingType::utUInt64; keys_buffer.values = CachePartition::Attribute::Container(); - Poco::File directory(dir_path); - if (!directory.exists()) - directory.createDirectory(); + std::filesystem::create_directories(std::filesystem::path{dir_path}); { ProfileEvents::increment(ProfileEvents::FileOpen); @@ -234,7 +231,8 @@ size_t CachePartition::appendBlock( if (!memory) memory.emplace(block_size * write_buffer_size, BUFFER_ALIGNMENT); - auto init_write_buffer = [&]() { + auto init_write_buffer = [&]() + { write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); uint64_t tmp = 0; write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); @@ -249,7 +247,8 @@ size_t CachePartition::appendBlock( } bool flushed = false; - auto finish_block = [&]() { + auto finish_block = [&]() + { write_buffer.reset(); std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum @@ -349,9 +348,8 @@ size_t CachePartition::appendBlock( void CachePartition::flush() { - if (current_file_block_id >= max_size) { + if (current_file_block_id >= max_size) clearOldestBlocks(); - } const auto & ids = std::get>(keys_buffer.values); if (ids.empty()) @@ -418,7 +416,8 @@ void CachePartition::flush() for (size_t row = 0; row < ids.size(); ++row) { Index index; - if (key_to_index.get(ids[row], index)) { + if (key_to_index.get(ids[row], index)) + { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); @@ -446,10 +445,12 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray Metadata metadata; readVarUInt(metadata.data, buf); - if (metadata.expiresAt() > now) { - if (metadata.isDefault()) { + if (metadata.expiresAt() > now) + { + if (metadata.isDefault()) out[index] = get_default(index); - } else { + else + { ignoreFromBufferToAttributeIndex(attribute_index, buf); readBinary(out[index], buf); } @@ -471,9 +472,10 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra readVarUInt(metadata.data, buf); if (metadata.expiresAt() > now) { - if (metadata.isDefault()) { + if (metadata.isDefault()) default_ids.push_back(index); - } else { + else + { ignoreFromBufferToAttributeIndex(attribute_index, buf); size_t size = 0; readVarUInt(size, buf); @@ -498,9 +500,8 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType now) { + if (metadata.expiresAt() > now) out[index] = !metadata.isDefault(); - } }; getImpl(ids, set_value, found); @@ -654,9 +655,9 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, ++to_pop; /// add new io tasks - const size_t new_tasks_count = std::min(read_buffer_size - (to_push - to_pop), requests.size() - to_push); + const int new_tasks_count = std::min(read_buffer_size - (to_push - to_pop), requests.size() - to_push); - size_t pushed = 0; + int pushed = 0; while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) { if (errno != EINTR) @@ -731,14 +732,14 @@ void CachePartition::clearOldestBlocks() uint32_t keys_in_current_block = 0; readBinary(keys_in_current_block, read_buffer); Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); - + for (uint32_t j = 0; j < keys_in_current_block; ++j) { keys.emplace_back(); readBinary(keys.back(), read_buffer); Metadata metadata; readBinary(metadata.data, read_buffer); - + if (!metadata.isDefault()) { for (size_t attr = 0; attr < attributes_structure.size(); ++attr) @@ -785,11 +786,11 @@ void CachePartition::clearOldestBlocks() for (const auto& key : keys) { Index index; - if (key_to_index.get(key, index)) { + if (key_to_index.get(key, index)) + { size_t block_id = index.getBlockId(); - if (start_block <= block_id && block_id < finish_block) { + if (start_block <= block_id && block_id < finish_block) key_to_index.erase(key); - } } } } @@ -1253,6 +1254,7 @@ SSDCacheDictionary::SSDCacheDictionary( path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) , log(&Poco::Logger::get("SSDCacheDictionary")) { + LOG_INFO(log, "Using storage path '" << path << "'."); if (!this->source_ptr->supportsSelectiveLoad()) throw Exception{name + ": source cannot be used with CacheDictionary", ErrorCodes::UNSUPPORTED_METHOD}; @@ -1368,7 +1370,8 @@ void SSDCacheDictionary::getItemsNumberImpl( storage.update( source_ptr, required_ids, - [&](const auto id, const auto row, const auto & new_attributes) { + [&](const auto id, const auto row, const auto & new_attributes) + { for (const size_t out_row : not_found_ids[id]) out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, @@ -1495,7 +1498,8 @@ void SSDCacheDictionary::has(const PaddedPODArray & ids, PaddedPODArray class CLRUCache { - using Iter = std::list::iterator; + using Iter = typename std::list::iterator; public: - CLRUCache(size_t max_size_) : max_size(max_size_) { + CLRUCache(size_t max_size_) : max_size(max_size_) + { } - void set(K key, V val) { + void set(K key, V val) + { auto it = cache.find(key); - if (it == std::end(cache)) { + if (it == std::end(cache)) + { auto & item = cache[key]; item.first = queue.insert(std::end(queue), key); item.second = val; - if (queue.size() > max_size) { + if (queue.size() > max_size) + { cache.erase(queue.front()); queue.pop_front(); } - } else { + } + else + { queue.erase(it->second.first); it->second.first = queue.insert(std::end(queue), key); it->second.second = val; } } - bool get(K key, V & val) { + bool get(K key, V & val) + { auto it = cache.find(key); - if (it == std::end(cache)) { + if (it == std::end(cache)) return false; - } val = it->second.second; queue.erase(it->second.first); it->second.first = queue.insert(std::end(queue), key); return true; } - void erase(K key) { + void erase(K key) + { auto it = cache.find(key); queue.erase(it->second.first); cache.erase(it); } - size_t size() const { + size_t size() const + { return cache.size(); } - auto begin() { + auto begin() + { return std::begin(cache); } - auto end() { + auto end() + { return std::end(cache); } @@ -401,7 +411,7 @@ class SSDCacheDictionary final : public IDictionary bool hasHierarchy() const override { return false; } - void toParent(const PaddedPODArray & /* ids */, PaddedPODArray & /* out */ ) const override {} + void toParent(const PaddedPODArray &, PaddedPODArray &) const override { } std::exception_ptr getLastException() const override { return storage.getLastException(); } @@ -489,10 +499,11 @@ class SSDCacheDictionary final : public IDictionary template void getItemsNumberImpl( const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, DefaultGetter && get_default) const; + template void getItemsStringImpl(const size_t attribute_index, const PaddedPODArray & ids, ColumnString * out, DefaultGetter && get_default) const; - + const std::string name; const DictionaryStructure dict_struct; mutable DictionarySourcePtr source_ptr; From 60648b3d49f06fa56eae0a96b4e6358460a3c748 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 23 Apr 2020 21:03:38 +0300 Subject: [PATCH 0060/1102] fix eintr --- src/Dictionaries/SSDCacheDictionary.cpp | 15 +++++++++------ src/Dictionaries/SSDCacheDictionary.h | 25 ++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index a38d21cad9c4..ac3eb017db07 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -448,7 +448,7 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray if (metadata.expiresAt() > now) { if (metadata.isDefault()) - out[index] = get_default(index); + out[index] = get_default(index); else { ignoreFromBufferToAttributeIndex(attribute_index, buf); @@ -471,7 +471,8 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra Metadata metadata; readVarUInt(metadata.data, buf); - if (metadata.expiresAt() > now) { + if (metadata.expiresAt() > now) + { if (metadata.isDefault()) default_ids.push_back(index); else @@ -695,14 +696,16 @@ void CachePartition::clearOldestBlocks() io_event event{}; AIOContext aio_context(1); - if (io_submit(aio_context.ctx, 1, &request_ptr) != 1) + while (io_submit(aio_context.ctx, 1, &request_ptr) != 1) { - throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + if (errno != EINTR) + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); } - if (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) != 1) + while (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) != 1) { - throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); + if (errno != EINTR) + throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); } if (event.res != static_cast(request.aio_nbytes)) diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index c4b09dbf74b6..2899dbe319de 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -1,24 +1,24 @@ #pragma once +#include "DictionaryStructure.h" +#include "IDictionary.h" +#include "IDictionarySource.h" #include #include -#include -#include -#include -#include -#include -#include #include #include -#include #include #include -#include "DictionaryStructure.h" -#include "IDictionary.h" -#include "IDictionarySource.h" -#include +#include #include +#include #include +#include +#include +#include +#include +#include +#include namespace DB { @@ -43,7 +43,7 @@ class CLRUCache if (queue.size() > max_size) { cache.erase(queue.front()); - queue.pop_front(); + queue.pop_front(); } } else @@ -110,7 +110,6 @@ using AttributeValueVariant = std::variant< Float64, String>; - class CachePartition { public: From a9085760330c9b2c5bbf2e706dae6e5639e6bcc9 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 23 Apr 2020 22:07:03 +0300 Subject: [PATCH 0061/1102] fix double params --- src/Parsers/ASTDictionary.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Parsers/ASTDictionary.cpp b/src/Parsers/ASTDictionary.cpp index 0aa311b5be27..ff5e06e47447 100644 --- a/src/Parsers/ASTDictionary.cpp +++ b/src/Parsers/ASTDictionary.cpp @@ -67,6 +67,8 @@ ASTPtr ASTDictionaryLayout::clone() const auto res = std::make_shared(*this); res->children.clear(); res->layout_type = layout_type; + res->parameters.clear(); + res->has_brackets = has_brackets; for (const auto & parameter : parameters) { res->parameters.emplace_back(parameter.first, nullptr); From 06945d83ecaed4092208392e017ca8111d225844 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 25 Apr 2020 11:12:13 +0300 Subject: [PATCH 0062/1102] impr --- src/Dictionaries/SSDCacheDictionary.cpp | 1 + src/Dictionaries/SSDCacheDictionary.h | 32 ++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index ac3eb017db07..55655bea839a 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -786,6 +786,7 @@ void CachePartition::clearOldestBlocks() const size_t start_block = current_file_block_id % max_size; const size_t finish_block = start_block + block_size * write_buffer_size; + Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); for (const auto& key : keys) { Index index; diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 2899dbe319de..685efba26000 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -27,6 +27,13 @@ template class CLRUCache { using Iter = typename std::list::iterator; + + struct Cell + { + Iter iter; + V val; + }; + public: CLRUCache(size_t max_size_) : max_size(max_size_) { @@ -38,8 +45,8 @@ class CLRUCache if (it == std::end(cache)) { auto & item = cache[key]; - item.first = queue.insert(std::end(queue), key); - item.second = val; + item.iter = queue.insert(std::end(queue), key); + item.val = val; if (queue.size() > max_size) { cache.erase(queue.front()); @@ -48,9 +55,9 @@ class CLRUCache } else { - queue.erase(it->second.first); - it->second.first = queue.insert(std::end(queue), key); - it->second.second = val; + queue.erase(it->second.iter); + it->second.iter = queue.insert(std::end(queue), key); + it->second.val = val; } } @@ -59,17 +66,20 @@ class CLRUCache auto it = cache.find(key); if (it == std::end(cache)) return false; - val = it->second.second; - queue.erase(it->second.first); - it->second.first = queue.insert(std::end(queue), key); + val = it->second.val; + queue.erase(it->second.iter); + it->second.iter = queue.insert(std::end(queue), key); return true; } - void erase(K key) + bool erase(K key) { auto it = cache.find(key); - queue.erase(it->second.first); + if (it == std::end(cache)) + return false; + queue.erase(it->second.iter); cache.erase(it); + return true; } size_t size() const @@ -88,7 +98,7 @@ class CLRUCache } private: - std::unordered_map> cache; + std::unordered_map cache; std::list queue; size_t max_size; }; From 4f968fb11d58e80014cf779e8e1d91e481dfc935 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 25 Apr 2020 15:05:01 +0300 Subject: [PATCH 0063/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 5 ++--- src/Dictionaries/SSDCacheDictionary.h | 11 +++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 55655bea839a..2f9e7a800f7a 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -615,7 +615,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, while (to_pop < requests.size()) { /// get io tasks from previous iteration - size_t popped = 0; + int popped = 0; while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) < 0) { if (errno != EINTR) @@ -857,8 +857,7 @@ size_t CachePartition::getElementCount() const PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { - const ProfilingScopedReadRWLock read_lock{rw_lock, ProfileEvents::DictCacheLockReadNs}; - + std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. PaddedPODArray array; for (const auto & [key, index] : key_to_index) array.push_back(key); // TODO: exclude default diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 685efba26000..4d207777c76d 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -41,6 +41,7 @@ class CLRUCache void set(K key, V val) { + std::lock_guard lock(mutex); auto it = cache.find(key); if (it == std::end(cache)) { @@ -63,6 +64,7 @@ class CLRUCache bool get(K key, V & val) { + std::lock_guard lock(mutex); auto it = cache.find(key); if (it == std::end(cache)) return false; @@ -74,6 +76,7 @@ class CLRUCache bool erase(K key) { + std::lock_guard lock(mutex); auto it = cache.find(key); if (it == std::end(cache)) return false; @@ -82,18 +85,21 @@ class CLRUCache return true; } - size_t size() const + size_t size() { + std::lock_guard lock(mutex); return cache.size(); } auto begin() { + std::lock_guard lock(mutex); return std::begin(cache); } auto end() { + std::lock_guard lock(mutex); return std::end(cache); } @@ -101,6 +107,7 @@ class CLRUCache std::unordered_map cache; std::list queue; size_t max_size; + std::mutex mutex; }; using AttributeValueVariant = std::variant< @@ -357,7 +364,7 @@ class CacheStorage mutable std::chrono::system_clock::time_point backoff_end_time; // stats - mutable size_t bytes_allocated = 0; + //mutable size_t bytes_allocated = 0; mutable std::atomic hit_count{0}; mutable std::atomic query_count{0}; From c5f8ebd98ca681dbc92e0be796b84bc7e5cc1108 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 26 Apr 2020 11:41:07 +0300 Subject: [PATCH 0064/1102] fix deadlock --- src/Dictionaries/SSDCacheDictionary.cpp | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 2f9e7a800f7a..d8edf543df73 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1079,40 +1079,40 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); PaddedPODArray metadata; - - for (const auto & id_found_pair : remaining_ids) { - if (id_found_pair.second) + const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; + + for (const auto & id_found_pair : remaining_ids) { - ++found_num; - continue; - } - ++not_found_num; + if (id_found_pair.second) + { + ++found_num; + continue; + } + ++not_found_num; - const auto id = id_found_pair.first; + const auto id = id_found_pair.first; - if (update_error_count) - { - /// TODO: юзать старые значения. + if (update_error_count) + { + /// TODO: юзать старые значения. - /// We don't have expired data for that `id` so all we can do is to rethrow `last_exception`. - std::rethrow_exception(last_update_exception); - } + /// We don't have expired data for that `id` so all we can do is to rethrow `last_exception`. + std::rethrow_exception(last_update_exception); + } - // Set key - std::get>(new_keys.values).push_back(id); + // Set key + std::get>(new_keys.values).push_back(id); - std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; - metadata.emplace_back(); - metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); - metadata.back().setDefault(); + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; + metadata.emplace_back(); + metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); + metadata.back().setDefault(); - /// inform caller that the cell has not been found - on_id_not_found(id); - } + /// inform caller that the cell has not been found + on_id_not_found(id); + } - { - const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; if (not_found_num) append_defaults(new_keys, metadata); } From 84d73c88fdd426be71acf11f68f5805c36ae0018 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:20:27 +0300 Subject: [PATCH 0065/1102] Added Ruby integrations activecube --- docs/en/interfaces/third-party/integrations.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/en/interfaces/third-party/integrations.md b/docs/en/interfaces/third-party/integrations.md index 9f4b0f2fa656..6b1c170252c5 100644 --- a/docs/en/interfaces/third-party/integrations.md +++ b/docs/en/interfaces/third-party/integrations.md @@ -96,5 +96,11 @@ toc_title: Integrations - Elixir - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) + [Original article](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From d212bc2ebc42d72839606b33c84d32bbbf9b2d48 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:39:37 +0300 Subject: [PATCH 0066/1102] Added Ruby integrations --- docs/ru/interfaces/third-party/integrations.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/ru/interfaces/third-party/integrations.md b/docs/ru/interfaces/third-party/integrations.md index d16404e2f27d..508c67340457 100644 --- a/docs/ru/interfaces/third-party/integrations.md +++ b/docs/ru/interfaces/third-party/integrations.md @@ -91,5 +91,10 @@ - Elixir - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) + [Оригинальная статья](https://clickhouse.tech/docs/ru/interfaces/third-party/integrations/) From fd53a8c15502f88f910ff60b3e8bec2d7bd28954 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:40:39 +0300 Subject: [PATCH 0067/1102] Added Ruby integration --- docs/es/interfaces/third-party/integrations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/es/interfaces/third-party/integrations.md b/docs/es/interfaces/third-party/integrations.md index d706278a280a..50fd9f1ce3bb 100644 --- a/docs/es/interfaces/third-party/integrations.md +++ b/docs/es/interfaces/third-party/integrations.md @@ -98,5 +98,10 @@ toc_title: "Integraci\xF3n" - Elixir - [Ecto](https://github.com/elixir-ecto/ecto) - [Método de codificación de datos:](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) [Artículo Original](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From d9648bc8348a133f946db76591edcb6da9cdf9f2 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:41:45 +0300 Subject: [PATCH 0068/1102] Added Ruby integration --- docs/fa/interfaces/third-party/integrations.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/fa/interfaces/third-party/integrations.md b/docs/fa/interfaces/third-party/integrations.md index 0ad13d1fd9e6..f9e0411201a8 100644 --- a/docs/fa/interfaces/third-party/integrations.md +++ b/docs/fa/interfaces/third-party/integrations.md @@ -95,5 +95,10 @@ toc_title: "\u06CC\u06A9\u067E\u0627\u0631\u0686\u06AF\u06CC" - اکسیر - [Ecto](https://github.com/elixir-ecto/ecto) - [حذف جستجو](https://github.com/appodeal/clickhouse_ecto) - +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) + [مقاله اصلی](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From 1279c4a0f3bfd7c3a1d11cc2d31969c9a1109cf1 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:46:08 +0300 Subject: [PATCH 0069/1102] Added ruby integrations --- docs/fr/interfaces/third-party/integrations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/fr/interfaces/third-party/integrations.md b/docs/fr/interfaces/third-party/integrations.md index 565be2b9604a..100e4e34f54d 100644 --- a/docs/fr/interfaces/third-party/integrations.md +++ b/docs/fr/interfaces/third-party/integrations.md @@ -95,5 +95,10 @@ toc_title: "Int\xE9gration" - Elixir - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) [Article Original](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From 7e25a352f386445690500e6ceea125265f9ba9ab Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:47:15 +0300 Subject: [PATCH 0070/1102] Added ruby integrations --- docs/ja/interfaces/third-party/integrations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/ja/interfaces/third-party/integrations.md b/docs/ja/interfaces/third-party/integrations.md index 74d0a51e7de4..f45a6734b2c9 100644 --- a/docs/ja/interfaces/third-party/integrations.md +++ b/docs/ja/interfaces/third-party/integrations.md @@ -95,5 +95,10 @@ toc_title: "\u7D71\u5408" - エリクサー - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) [元の記事](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From 06c7b8ba4c951c4c84d08c9516e3220edd2c3a3a Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:48:03 +0300 Subject: [PATCH 0071/1102] Added ruby integrations --- docs/tr/interfaces/third-party/integrations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tr/interfaces/third-party/integrations.md b/docs/tr/interfaces/third-party/integrations.md index 2216e68a4c45..fdfbbc18b65d 100644 --- a/docs/tr/interfaces/third-party/integrations.md +++ b/docs/tr/interfaces/third-party/integrations.md @@ -95,5 +95,10 @@ toc_title: Entegrasyonlar - İksir - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) [Orijinal makale](https://clickhouse.tech/docs/en/interfaces/third-party/integrations/) From 8cb574fd907339c97038c7e1dfb10862d31fd496 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Mon, 27 Apr 2020 11:48:39 +0300 Subject: [PATCH 0072/1102] Added ruby integrations --- docs/zh/interfaces/third-party/integrations.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/zh/interfaces/third-party/integrations.md b/docs/zh/interfaces/third-party/integrations.md index 128a4060c2d7..ecd33e93a4d9 100644 --- a/docs/zh/interfaces/third-party/integrations.md +++ b/docs/zh/interfaces/third-party/integrations.md @@ -89,5 +89,10 @@ - 仙丹 - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) +- Ruby + - [Ruby on rails](https://rubyonrails.org/) + - [activecube](https://github.com/bitquery/activecube) + - [GraphQL](https://github.com/graphql) + - [activecube-graphql](https://github.com/bitquery/activecube-graphql) [来源文章](https://clickhouse.tech/docs/zh/interfaces/third-party/integrations/) From fac0439efb0b52e435d166636101503cb1bdffb2 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 30 Apr 2020 23:50:31 +0300 Subject: [PATCH 0073/1102] complex_key --- src/Dictionaries/SSDCacheDictionary.cpp | 119 +- src/Dictionaries/SSDCacheDictionary.h | 28 +- .../SSDComplexKeyCacheDictionary.cpp | 1805 +++++++++++++++++ .../SSDComplexKeyCacheDictionary.h | 790 ++++++++ src/Dictionaries/registerDictionaries.cpp | 1 + src/Dictionaries/registerDictionaries.h | 1 + src/Functions/FunctionsExternalDictionaries.h | 7 +- 7 files changed, 2676 insertions(+), 75 deletions(-) create mode 100644 src/Dictionaries/SSDComplexKeyCacheDictionary.cpp create mode 100644 src/Dictionaries/SSDComplexKeyCacheDictionary.h diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index d8edf543df73..108ed19c8627 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -105,65 +105,65 @@ namespace } } -CachePartition::Metadata::time_point_t CachePartition::Metadata::expiresAt() const +SSDCachePartition::Metadata::time_point_t SSDCachePartition::Metadata::expiresAt() const { return ext::safe_bit_cast(data & KEY_METADATA_EXPIRES_AT_MASK); } -void CachePartition::Metadata::setExpiresAt(const time_point_t & t) +void SSDCachePartition::Metadata::setExpiresAt(const time_point_t & t) { data = ext::safe_bit_cast(t); } -bool CachePartition::Metadata::isDefault() const +bool SSDCachePartition::Metadata::isDefault() const { return (data & KEY_METADATA_IS_DEFAULT_MASK) == KEY_METADATA_IS_DEFAULT_MASK; } -void CachePartition::Metadata::setDefault() +void SSDCachePartition::Metadata::setDefault() { data |= KEY_METADATA_IS_DEFAULT_MASK; } -bool CachePartition::Index::inMemory() const +bool SSDCachePartition::Index::inMemory() const { return (index & KEY_IN_MEMORY) == KEY_IN_MEMORY; } -bool CachePartition::Index::exists() const +bool SSDCachePartition::Index::exists() const { return index != NOT_EXISTS; } -void CachePartition::Index::setNotExists() +void SSDCachePartition::Index::setNotExists() { index = NOT_EXISTS; } -void CachePartition::Index::setInMemory(const bool in_memory) +void SSDCachePartition::Index::setInMemory(const bool in_memory) { index = (index & ~KEY_IN_MEMORY) | (static_cast(in_memory) << KEY_IN_MEMORY_BIT); } -size_t CachePartition::Index::getAddressInBlock() const +size_t SSDCachePartition::Index::getAddressInBlock() const { return index & INDEX_IN_BLOCK_MASK; } -void CachePartition::Index::setAddressInBlock(const size_t address_in_block) +void SSDCachePartition::Index::setAddressInBlock(const size_t address_in_block) { index = (index & ~INDEX_IN_BLOCK_MASK) | address_in_block; } -size_t CachePartition::Index::getBlockId() const +size_t SSDCachePartition::Index::getBlockId() const { return (index & BLOCK_INDEX_MASK) >> INDEX_IN_BLOCK_BITS; } -void CachePartition::Index::setBlockId(const size_t block_id) +void SSDCachePartition::Index::setBlockId(const size_t block_id) { index = (index & ~BLOCK_INDEX_MASK) | (block_id << INDEX_IN_BLOCK_BITS); } -CachePartition::CachePartition( +SSDCachePartition::SSDCachePartition( const AttributeUnderlyingType & /* key_structure */, const std::vector & attributes_structure_, const std::string & dir_path, @@ -184,7 +184,7 @@ CachePartition::CachePartition( , attributes_structure(attributes_structure_) { keys_buffer.type = AttributeUnderlyingType::utUInt64; - keys_buffer.values = CachePartition::Attribute::Container(); + keys_buffer.values = SSDCachePartition::Attribute::Container(); std::filesystem::create_directories(std::filesystem::path{dir_path}); @@ -206,19 +206,19 @@ CachePartition::CachePartition( } } -CachePartition::~CachePartition() +SSDCachePartition::~SSDCachePartition() { std::unique_lock lock(rw_lock); ::close(fd); } -size_t CachePartition::appendDefaults( +size_t SSDCachePartition::appendDefaults( const Attribute & new_keys, const PaddedPODArray & metadata, const size_t begin) { return appendBlock(new_keys, Attributes{}, metadata, begin); } -size_t CachePartition::appendBlock( +size_t SSDCachePartition::appendBlock( const Attribute & new_keys, const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) { std::unique_lock lock(rw_lock); @@ -346,7 +346,7 @@ size_t CachePartition::appendBlock( return ids.size() - begin; } -void CachePartition::flush() +void SSDCachePartition::flush() { if (current_file_block_id >= max_size) clearOldestBlocks(); @@ -435,7 +435,7 @@ void CachePartition::flush() } template -void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, +void SSDCachePartition::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::vector & found, GetDefault & get_default, std::chrono::system_clock::time_point now) const { @@ -461,7 +461,7 @@ void CachePartition::getValue(const size_t attribute_index, const PaddedPODArray getImpl(ids, set_value, found); } -void CachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, +void SSDCachePartition::getString(const size_t attribute_index, const PaddedPODArray & ids, StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, std::vector & default_ids, std::chrono::system_clock::time_point now) const { @@ -492,7 +492,7 @@ void CachePartition::getString(const size_t attribute_index, const PaddedPODArra getImpl(ids, set_value, found); } -void CachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, +void SSDCachePartition::has(const PaddedPODArray & ids, ResultArrayType & out, std::vector & found, std::chrono::system_clock::time_point now) const { auto set_value = [&](const size_t index, ReadBuffer & buf) @@ -509,7 +509,7 @@ void CachePartition::has(const PaddedPODArray & ids, ResultArrayType -void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, +void SSDCachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found) const { std::shared_lock lock(rw_lock); @@ -530,7 +530,7 @@ void CachePartition::getImpl(const PaddedPODArray & ids, SetFunc & set, } template -void CachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const +void SSDCachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const { // Do not check checksum while reading from memory. for (size_t i = 0; i < indices.size(); ++i) @@ -547,7 +547,7 @@ void CachePartition::getValueFromMemory(const PaddedPODArray & indices, S } template -void CachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const +void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const { std::vector> index_to_out; for (size_t i = 0; i < indices.size(); ++i) @@ -668,7 +668,7 @@ void CachePartition::getValueFromStorage(const PaddedPODArray & indices, } } -void CachePartition::clearOldestBlocks() +void SSDCachePartition::clearOldestBlocks() { Poco::Logger::get("GC").information("GC clear -----------------"); // write_buffer_size, because we need to erase the whole buffer. @@ -799,9 +799,8 @@ void CachePartition::clearOldestBlocks() } } -void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const +void SSDCachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const { - //buf.ignore(2 * sizeof(UInt64)); // key and metadata for (size_t i = 0; i < attribute_index; ++i) { switch (attributes_structure[i]) @@ -838,24 +837,24 @@ void CachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_ind } } -size_t CachePartition::getId() const +size_t SSDCachePartition::getId() const { return file_id; } -double CachePartition::getLoadFactor() const +double SSDCachePartition::getLoadFactor() const { std::shared_lock lock(rw_lock); return static_cast(current_file_block_id) / max_size; } -size_t CachePartition::getElementCount() const +size_t SSDCachePartition::getElementCount() const { std::shared_lock lock(rw_lock); return key_to_index.size(); } -PaddedPODArray CachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const +PaddedPODArray SSDCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. PaddedPODArray array; @@ -864,13 +863,13 @@ PaddedPODArray CachePartition::getCachedIds(const std::chro return array; } -void CachePartition::remove() +void SSDCachePartition::remove() { std::unique_lock lock(rw_lock); std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); } -CacheStorage::CacheStorage( +SSDCacheStorage::SSDCacheStorage( const AttributeTypes & attributes_structure_, const std::string & path_, const size_t max_partitions_count_, @@ -887,11 +886,11 @@ CacheStorage::CacheStorage( , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) , max_stored_keys(max_stored_keys_) - , log(&Poco::Logger::get("CacheStorage")) + , log(&Poco::Logger::get("SSDCacheStorage")) { } -CacheStorage::~CacheStorage() +SSDCacheStorage::~SSDCacheStorage() { std::unique_lock lock(rw_lock); partition_delete_queue.splice(std::end(partition_delete_queue), partitions); @@ -899,7 +898,7 @@ CacheStorage::~CacheStorage() } template -void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, +void SSDCacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, GetDefault & get_default, std::chrono::system_clock::time_point now) const { @@ -919,7 +918,7 @@ void CacheStorage::getValue(const size_t attribute_index, const PaddedPODArray & ids, +void SSDCacheStorage::getString(const size_t attribute_index, const PaddedPODArray & ids, StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, std::vector & default_ids, std::chrono::system_clock::time_point now) const { @@ -939,7 +938,7 @@ void CacheStorage::getString(const size_t attribute_index, const PaddedPODArray< hit_count.fetch_add(ids.size() - not_found.size(), std::memory_order_release); } -void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, +void SSDCacheStorage::has(const PaddedPODArray & ids, ResultArrayType & out, std::unordered_map> & not_found, std::chrono::system_clock::time_point now) const { for (size_t i = 0; i < ids.size(); ++i) @@ -961,12 +960,12 @@ void CacheStorage::has(const PaddedPODArray & ids, ResultArrayType -void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, +void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, const DictionaryLifetime lifetime) { - auto append_block = [this](const CachePartition::Attribute & new_keys, - const CachePartition::Attributes & new_attributes, const PaddedPODArray & metadata) + auto append_block = [this](const SSDCachePartition::Attribute & new_keys, + const SSDCachePartition::Attributes & new_attributes, const PaddedPODArray & metadata) { size_t inserted = 0; while (inserted < metadata.size()) @@ -975,7 +974,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendBlock(new_keys, new_attributes, metadata, inserted); if (inserted < metadata.size()) { - partitions.emplace_front(std::make_unique( + partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); @@ -1017,9 +1016,9 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values); + const auto & ids = std::get>(new_keys.values); - PaddedPODArray metadata(ids.size()); + PaddedPODArray metadata(ids.size()); for (const auto i : ext::range(0, ids.size())) { @@ -1053,7 +1052,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & metadata) + auto append_defaults = [this](const SSDCachePartition::Attribute & new_keys, const PaddedPODArray & metadata) { size_t inserted = 0; while (inserted < metadata.size()) @@ -1062,7 +1061,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vectorappendDefaults(new_keys, metadata, inserted); if (inserted < metadata.size()) { - partitions.emplace_front(std::make_unique( + partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); @@ -1074,11 +1073,11 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector(); + new_keys.values = SSDCachePartition::Attribute::Container(); - PaddedPODArray metadata; + PaddedPODArray metadata; { const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; @@ -1102,7 +1101,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector>(new_keys.values).push_back(id); + std::get>(new_keys.values).push_back(id); std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata.emplace_back(); @@ -1122,7 +1121,7 @@ void CacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector CacheStorage::getCachedIds() const +PaddedPODArray SSDCacheStorage::getCachedIds() const { PaddedPODArray array; @@ -1138,7 +1137,7 @@ PaddedPODArray CacheStorage::getCachedIds() const return array; } -double CacheStorage::getLoadFactor() const +double SSDCacheStorage::getLoadFactor() const { double result = 0; std::shared_lock lock(rw_lock); @@ -1147,7 +1146,7 @@ double CacheStorage::getLoadFactor() const return result / partitions.size(); } -size_t CacheStorage::getElementCount() const +size_t SSDCacheStorage::getElementCount() const { size_t result = 0; std::shared_lock lock(rw_lock); @@ -1156,7 +1155,7 @@ size_t CacheStorage::getElementCount() const return result; } -void CacheStorage::collectGarbage() +void SSDCacheStorage::collectGarbage() { // add partitions to queue while (partitions.size() > max_partitions_count) @@ -1172,10 +1171,10 @@ void CacheStorage::collectGarbage() } } -CachePartition::Attributes CacheStorage::createAttributesFromBlock( +SSDCachePartition::Attributes SSDCacheStorage::createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure) { - CachePartition::Attributes attributes; + SSDCachePartition::Attributes attributes; const auto columns = block.getColumns(); for (size_t i = 0; i < structure.size(); ++i) @@ -1186,7 +1185,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ { \ - CachePartition::Attribute::Container values(column->size()); \ + SSDCachePartition::Attribute::Container values(column->size()); \ memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ attributes.emplace_back(); \ attributes.back().type = structure[i]; \ @@ -1213,7 +1212,7 @@ CachePartition::Attributes CacheStorage::createAttributesFromBlock( case AttributeUnderlyingType::utString: { attributes.emplace_back(); - CachePartition::Attribute::Container values(column->size()); + SSDCachePartition::Attribute::Container values(column->size()); for (size_t j = 0; j < column->size(); ++j) { const auto ref = column->getDataAt(j); @@ -1376,7 +1375,7 @@ void SSDCacheDictionary::getItemsNumberImpl( [&](const auto id, const auto row, const auto & new_attributes) { for (const size_t out_row : not_found_ids[id]) - out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; + out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const size_t id) { @@ -1455,7 +1454,7 @@ void SSDCacheDictionary::getItemsStringImpl(const size_t attribute_index, const required_ids, [&](const auto id, const auto row, const auto & new_attributes) { - update_result[id] = std::get>(new_attributes[attribute_index].values)[row]; + update_result[id] = std::get>(new_attributes[attribute_index].values)[row]; }, [&](const size_t) {}, getLifetime()); diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 4d207777c76d..0409a100aa60 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -127,7 +127,7 @@ using AttributeValueVariant = std::variant< Float64, String>; -class CachePartition +class SSDCachePartition { public: struct Index final @@ -170,7 +170,7 @@ class CachePartition using Offsets = std::vector; using Key = IDictionary::Key; - CachePartition( + SSDCachePartition( const AttributeUnderlyingType & key_structure, const std::vector & attributes_structure, const std::string & dir_path, @@ -181,7 +181,7 @@ class CachePartition const size_t write_buffer_size, const size_t max_stored_keys); - ~CachePartition(); + ~SSDCachePartition(); template using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; @@ -280,16 +280,16 @@ class CachePartition size_t current_file_block_id = 0; }; -using CachePartitionPtr = std::shared_ptr; +using SSDCachePartitionPtr = std::shared_ptr; -class CacheStorage +class SSDCacheStorage { public: using AttributeTypes = std::vector; - using Key = CachePartition::Key; + using Key = SSDCachePartition::Key; - CacheStorage( + SSDCacheStorage( const AttributeTypes & attributes_structure, const std::string & path, const size_t max_partitions_count, @@ -299,10 +299,10 @@ class CacheStorage const size_t write_buffer_size, const size_t max_stored_keys); - ~CacheStorage(); + ~SSDCacheStorage(); template - using ResultArrayType = CachePartition::ResultArrayType; + using ResultArrayType = SSDCachePartition::ResultArrayType; template void getValue(const size_t attribute_index, const PaddedPODArray & ids, @@ -336,7 +336,7 @@ class CacheStorage double getLoadFactor() const; private: - CachePartition::Attributes createAttributesFromBlock( + SSDCachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); void collectGarbage(); @@ -352,8 +352,8 @@ class CacheStorage const size_t max_stored_keys; mutable std::shared_mutex rw_lock; - std::list partitions; - std::list partition_delete_queue; + std::list partitions; + std::list partition_delete_queue; Logger * const log; @@ -432,7 +432,7 @@ class SSDCacheDictionary final : public IDictionary std::exception_ptr getLastException() const override { return storage.getLastException(); } template - using ResultArrayType = CacheStorage::ResultArrayType; + using ResultArrayType = SSDCacheStorage::ResultArrayType; #define DECLARE(TYPE) \ void get##TYPE(const std::string & attribute_name, const PaddedPODArray & ids, ResultArrayType & out) const; @@ -535,7 +535,7 @@ class SSDCacheDictionary final : public IDictionary std::map attribute_index_by_name; std::vector null_values; - mutable CacheStorage storage; + mutable SSDCacheStorage storage; Logger * const log; mutable size_t bytes_allocated = 0; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp new file mode 100644 index 000000000000..83b9f0f25a74 --- /dev/null +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -0,0 +1,1805 @@ +#include "SSDComplexKeyCacheDictionary.h" + +#include +#include +#include +#include +#include +#include +#include +#include "DictionaryBlockInputStream.h" +#include "DictionaryFactory.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ProfileEvents +{ + extern const Event DictCacheKeysRequested; + extern const Event DictCacheKeysRequestedMiss; + extern const Event DictCacheKeysRequestedFound; + extern const Event DictCacheKeysExpired; + extern const Event DictCacheKeysNotFound; + extern const Event DictCacheKeysHit; + extern const Event DictCacheRequestTimeNs; + extern const Event DictCacheRequests; + extern const Event DictCacheLockWriteNs; + extern const Event DictCacheLockReadNs; + extern const Event FileOpen; + extern const Event WriteBufferAIOWrite; + extern const Event WriteBufferAIOWriteBytes; +} + +namespace CurrentMetrics +{ + extern const Metric DictCacheRequests; + extern const Metric Write; +} + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int AIO_READ_ERROR; + extern const int AIO_WRITE_ERROR; + extern const int BAD_ARGUMENTS; + extern const int CANNOT_FSYNC; + extern const int CANNOT_IO_GETEVENTS; + extern const int CANNOT_IO_SUBMIT; + extern const int CANNOT_OPEN_FILE; + extern const int FILE_DOESNT_EXIST; + extern const int LOGICAL_ERROR; + extern const int TOO_SMALL_BUFFER_SIZE; + extern const int TYPE_MISMATCH; + extern const int UNSUPPORTED_METHOD; + extern const int CORRUPTED_DATA; +} + +namespace +{ + constexpr size_t DEFAULT_SSD_BLOCK_SIZE = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t DEFAULT_FILE_SIZE = 4 * 1024 * 1024 * 1024ULL; + constexpr size_t DEFAULT_PARTITIONS_COUNT = 16; + constexpr size_t DEFAULT_READ_BUFFER_SIZE = 16 * DEFAULT_SSD_BLOCK_SIZE; + constexpr size_t DEFAULT_WRITE_BUFFER_SIZE = DEFAULT_SSD_BLOCK_SIZE; + + constexpr size_t DEFAULT_MAX_STORED_KEYS = 100000; + + constexpr size_t BUFFER_ALIGNMENT = DEFAULT_AIO_FILE_BLOCK_SIZE; + constexpr size_t BLOCK_CHECKSUM_SIZE = 8; + constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; + + static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); + static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; + + constexpr size_t KEY_IN_MEMORY_BIT = 63; + constexpr size_t KEY_IN_MEMORY = (1ULL << KEY_IN_MEMORY_BIT); + constexpr size_t BLOCK_INDEX_BITS = 32; + constexpr size_t INDEX_IN_BLOCK_BITS = 16; + constexpr size_t INDEX_IN_BLOCK_MASK = (1ULL << INDEX_IN_BLOCK_BITS) - 1; + constexpr size_t BLOCK_INDEX_MASK = ((1ULL << (BLOCK_INDEX_BITS + INDEX_IN_BLOCK_BITS)) - 1) ^ INDEX_IN_BLOCK_MASK; + + constexpr size_t NOT_EXISTS = -1; + + constexpr UInt8 HAS_NOT_FOUND = 2; + + //constexpr UInt16 MAX_KEY_SIZE = std::numeric_limits::max(); + + const std::string BIN_FILE_EXT = ".bin"; + const std::string IND_FILE_EXT = ".idx"; + + int preallocateDiskSpace(int fd, size_t len) + { + #if defined(__FreeBSD__) + return posix_fallocate(fd, 0, len); + #else + return fallocate(fd, 0, 0, len); + #endif + } +} + +SSDComplexKeyCachePartition::Metadata::time_point_t SSDComplexKeyCachePartition::Metadata::expiresAt() const +{ + return ext::safe_bit_cast(data & KEY_METADATA_EXPIRES_AT_MASK); +} +void SSDComplexKeyCachePartition::Metadata::setExpiresAt(const time_point_t & t) +{ + data = ext::safe_bit_cast(t); +} + +bool SSDComplexKeyCachePartition::Metadata::isDefault() const +{ + return (data & KEY_METADATA_IS_DEFAULT_MASK) == KEY_METADATA_IS_DEFAULT_MASK; +} +void SSDComplexKeyCachePartition::Metadata::setDefault() +{ + data |= KEY_METADATA_IS_DEFAULT_MASK; +} + +bool SSDComplexKeyCachePartition::Index::inMemory() const +{ + return (index & KEY_IN_MEMORY) == KEY_IN_MEMORY; +} + +bool SSDComplexKeyCachePartition::Index::exists() const +{ + return index != NOT_EXISTS; +} + +void SSDComplexKeyCachePartition::Index::setNotExists() +{ + index = NOT_EXISTS; +} + +void SSDComplexKeyCachePartition::Index::setInMemory(const bool in_memory) +{ + index = (index & ~KEY_IN_MEMORY) | (static_cast(in_memory) << KEY_IN_MEMORY_BIT); +} + +size_t SSDComplexKeyCachePartition::Index::getAddressInBlock() const +{ + return index & INDEX_IN_BLOCK_MASK; +} + +void SSDComplexKeyCachePartition::Index::setAddressInBlock(const size_t address_in_block) +{ + index = (index & ~INDEX_IN_BLOCK_MASK) | address_in_block; +} + +size_t SSDComplexKeyCachePartition::Index::getBlockId() const +{ + return (index & BLOCK_INDEX_MASK) >> INDEX_IN_BLOCK_BITS; +} + +void SSDComplexKeyCachePartition::Index::setBlockId(const size_t block_id) +{ + index = (index & ~BLOCK_INDEX_MASK) | (block_id << INDEX_IN_BLOCK_BITS); +} + +SSDComplexKeyCachePartition::SSDComplexKeyCachePartition( + const AttributeUnderlyingType & /* key_structure */, + const std::vector & attributes_structure_, + const std::string & dir_path, + const size_t file_id_, + const size_t max_size_, + const size_t block_size_, + const size_t read_buffer_size_, + const size_t write_buffer_size_, + const size_t max_stored_keys_) + : file_id(file_id_) + , max_size(max_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) + , path(dir_path + "/" + std::to_string(file_id)) + , key_to_index(max_stored_keys, keys_pool) + , attributes_structure(attributes_structure_) +{ + std::filesystem::create_directories(std::filesystem::path{dir_path}); + + { + ProfileEvents::increment(ProfileEvents::FileOpen); + + const std::string filename = path + BIN_FILE_EXT; + fd = ::open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, 0666); + if (fd == -1) + { + auto error_code = (errno == ENOENT) ? ErrorCodes::FILE_DOESNT_EXIST : ErrorCodes::CANNOT_OPEN_FILE; + throwFromErrnoWithPath("Cannot open file " + filename, filename, error_code); + } + + if (preallocateDiskSpace(fd, max_size * block_size) < 0) + { + throwFromErrnoWithPath("Cannot preallocate space for the file " + filename, filename, ErrorCodes::CANNOT_ALLOCATE_MEMORY); + } + } +} + +SSDComplexKeyCachePartition::~SSDComplexKeyCachePartition() +{ + std::unique_lock lock(rw_lock); + ::close(fd); +} + +size_t SSDComplexKeyCachePartition::appendDefaults( + const KeyRefs & keys_in, + const PaddedPODArray & metadata, + const size_t begin) +{ + std::unique_lock lock(rw_lock); + KeyRefs keys(keys_in.size()); + for (size_t i = 0; i < keys_in.size(); ++i) + { + keys[i] = keys_pool.copyKeyFrom(keys_in[i]); + } + return append(keys, Attributes{}, metadata, begin); +} + +size_t SSDComplexKeyCachePartition::appendBlock( + const Columns & key_columns, const DataTypes & /* key_types */, + const Attributes & new_attributes, const PaddedPODArray & metadata, const size_t begin) +{ + std::unique_lock lock(rw_lock); + if (!new_attributes.empty() && new_attributes.size() != attributes_structure.size()) + throw Exception{"Wrong columns number in block.", ErrorCodes::BAD_ARGUMENTS}; + + const auto keys_size = key_columns.size(); + KeyRefs keys(key_columns.front()->size()); + { + StringRefs tmp_keys_refs(keys_size); + for (size_t i = 0; i < key_columns.front()->size(); ++i) + { + keys[i] = keys_pool.allocKey(i, key_columns, tmp_keys_refs); + } + } + + return append(keys, new_attributes, metadata, begin); +} + +size_t SSDComplexKeyCachePartition::append( + const KeyRefs & keys, + const Attributes & new_attributes, + const PaddedPODArray & metadata, + const size_t begin) +{ + if (!memory) + memory.emplace(block_size * write_buffer_size, BUFFER_ALIGNMENT); + + auto init_write_buffer = [&]() + { + write_buffer.emplace(memory->data() + current_memory_block_id * block_size, block_size); + uint64_t tmp = 0; + write_buffer->write(reinterpret_cast(&tmp), BLOCK_CHECKSUM_SIZE); + write_buffer->write(reinterpret_cast(&tmp), BLOCK_SPECIAL_FIELDS_SIZE); + keys_in_block = 0; + }; + + if (!write_buffer) + { + init_write_buffer(); + } + + bool flushed = false; + auto finish_block = [&]() + { + write_buffer.reset(); + std::memcpy(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, &keys_in_block, sizeof(keys_in_block)); // set count + uint64_t checksum = CityHash_v1_0_2::CityHash64(memory->data() + block_size * current_memory_block_id + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); // checksum + std::memcpy(memory->data() + block_size * current_memory_block_id, &checksum, sizeof(checksum)); + if (++current_memory_block_id == write_buffer_size) + flush(); + flushed = true; + }; + + for (size_t index = begin; index < keys.size();) + { + Poco::Logger::get("test").information("wb off: " + std::to_string(write_buffer->offset())); + Index cache_index; + cache_index.setInMemory(true); + cache_index.setBlockId(current_memory_block_id); + cache_index.setAddressInBlock(write_buffer->offset()); + + flushed = false; + if (keys[index].fullSize() + sizeof(UInt64) > write_buffer->available()) // place for key and metadata + { + finish_block(); + } + else + { + keys_pool.writeKey(keys[index], *write_buffer); + writeBinary(metadata[index].data, *write_buffer); + } + + Poco::Logger::get("test key").information("wb off: " + std::to_string(write_buffer->offset())); + + for (const auto & attribute : new_attributes) + { + if (flushed) + break; + switch (attribute.type) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + if (sizeof(TYPE) > write_buffer->available()) \ + { \ + finish_block(); \ + continue; \ + } \ + else \ + { \ + const auto & values = std::get>(attribute.values); \ + writeBinary(values[index], *write_buffer); \ + } \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + { + const auto & value = std::get>(attribute.values)[index]; + if (sizeof(UInt64) + value.size() > write_buffer->available()) + { + finish_block(); + continue; + } + else + { + writeStringBinary(value, *write_buffer); + } + } + break; + } + } + + if (!flushed) + { + key_to_index.set(keys[index], cache_index); + keys_buffer.push_back(keys[index]); + ++index; + ++keys_in_block; + } + else // next block in write buffer or flushed to ssd + { + init_write_buffer(); + } + Poco::Logger::get("test final").information("wb off: " + std::to_string(write_buffer->offset())); + } + return keys.size() - begin; +} + +void SSDComplexKeyCachePartition::flush() +{ + if (current_file_block_id >= max_size) + clearOldestBlocks(); + + if (keys_buffer.empty()) + return; + + Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); + + AIOContext aio_context{1}; + + iocb write_request{}; + iocb * write_request_ptr{&write_request}; + +#if defined(__FreeBSD__) + write_request.aio.aio_lio_opcode = LIO_WRITE; + write_request.aio.aio_fildes = fd; + write_request.aio.aio_buf = reinterpret_cast(memory->data()); + write_request.aio.aio_nbytes = block_size; + write_request.aio.aio_offset = block_size * current_file_block_id; +#else + write_request.aio_lio_opcode = IOCB_CMD_PWRITE; + write_request.aio_fildes = fd; + write_request.aio_buf = reinterpret_cast(memory->data()); + write_request.aio_nbytes = block_size * write_buffer_size; + write_request.aio_offset = block_size * current_file_block_id; +#endif + + Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); + + while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) + { + if (errno != EINTR) + throw Exception("Cannot submit request for asynchronous IO on file " + path + BIN_FILE_EXT, ErrorCodes::CANNOT_IO_SUBMIT); + } + + CurrentMetrics::Increment metric_increment_write{CurrentMetrics::Write}; + + io_event event; + while (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) < 0) + { + if (errno != EINTR) + throw Exception("Failed to wait for asynchronous IO completion on file " + path + BIN_FILE_EXT, ErrorCodes::CANNOT_IO_GETEVENTS); + } + + // Unpoison the memory returned from an uninstrumented system function. + __msan_unpoison(&event, sizeof(event)); + + ssize_t bytes_written; +#if defined(__FreeBSD__) + bytes_written = aio_return(reinterpret_cast(event.udata)); +#else + bytes_written = event.res; +#endif + + ProfileEvents::increment(ProfileEvents::WriteBufferAIOWrite); + ProfileEvents::increment(ProfileEvents::WriteBufferAIOWriteBytes, bytes_written); + + if (bytes_written != static_cast(write_request.aio_nbytes)) + throw Exception("Not all data was written for asynchronous IO on file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(bytes_written), ErrorCodes::AIO_WRITE_ERROR); + + if (::fsync(fd) < 0) + throwFromErrnoWithPath("Cannot fsync " + path + BIN_FILE_EXT, path + BIN_FILE_EXT, ErrorCodes::CANNOT_FSYNC); + + /// commit changes in index + for (size_t row = 0; row < keys_buffer.size(); ++row) + { + Index index; + if (key_to_index.get(keys_buffer[row], index)) + { + if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. + { + index.setInMemory(false); + index.setBlockId(current_file_block_id + index.getBlockId()); + } + key_to_index.set(keys_buffer[row], index); + } + } + + current_file_block_id += write_buffer_size; + current_memory_block_id = 0; + + /// clear buffer + keys_buffer.clear(); +} + +template +void SSDComplexKeyCachePartition::getValue( + const size_t attribute_index, const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, std::vector & found, GetDefault & get_default, + std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + keys_pool.ignoreKey(buf); + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) + { + if (metadata.isDefault()) + out[index] = get_default(index); + else + { + ignoreFromBufferToAttributeIndex(attribute_index, buf); + readBinary(out[index], buf); + } + found[index] = true; + } + }; + + getImpl(key_columns, key_types, set_value, found); +} + +void SSDComplexKeyCachePartition::getString(const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, + std::vector & default_ids, + std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + keys_pool.ignoreKey(buf); + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) + { + if (metadata.isDefault()) + default_ids.push_back(index); + else + { + ignoreFromBufferToAttributeIndex(attribute_index, buf); + size_t size = 0; + readVarUInt(size, buf); + char * string_ptr = arena.alloc(size); + memcpy(string_ptr, buf.position(), size); + refs[index].data = string_ptr; + refs[index].size = size; + } + found[index] = true; + } + }; + + getImpl(key_columns, key_types, set_value, found); +} + +void SSDComplexKeyCachePartition::has( + const Columns & key_columns, const DataTypes & key_types, ResultArrayType & out, + std::vector & found, std::chrono::system_clock::time_point now) const +{ + auto set_value = [&](const size_t index, ReadBuffer & buf) + { + keys_pool.ignoreKey(buf); + Metadata metadata; + readVarUInt(metadata.data, buf); + + if (metadata.expiresAt() > now) + out[index] = !metadata.isDefault(); + }; + + getImpl(key_columns, key_types, set_value, found); +} + +template +void SSDComplexKeyCachePartition::getImpl( + const Columns & key_columns, const DataTypes & /* key_types */, + SetFunc & set, std::vector & found) const +{ + TemporalComplexKeysPool tmp_keys_pool; + StringRefs tmp_refs(key_columns.size()); + + std::shared_lock lock(rw_lock); + PaddedPODArray indices(key_columns.front()->size()); + for (size_t i = 0; i < key_columns.front()->size(); ++i) + { + auto key = tmp_keys_pool.allocKey(i, key_columns, tmp_refs); + SCOPE_EXIT(tmp_keys_pool.rollback(key)); + Index index; + if (found[i]) + indices[i].setNotExists(); + else if (key_to_index.get(key, index)) + indices[i] = index; + else + indices[i].setNotExists(); + } + + getValueFromMemory(indices, set); + getValueFromStorage(indices, set); +} + +template +void SSDComplexKeyCachePartition::getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const +{ + // Do not check checksum while reading from memory. + for (size_t i = 0; i < indices.size(); ++i) + { + const auto & index = indices[i]; + if (index.exists() && index.inMemory()) + { + const size_t offset = index.getBlockId() * block_size + index.getAddressInBlock(); + + ReadBufferFromMemory read_buffer(memory->data() + offset, block_size * write_buffer_size - offset); + set(i, read_buffer); + } + } +} + +template +void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const +{ + std::vector> index_to_out; + for (size_t i = 0; i < indices.size(); ++i) + { + const auto & index = indices[i]; + if (index.exists() && !index.inMemory()) + index_to_out.emplace_back(index, i); + } + if (index_to_out.empty()) + return; + + /// sort by (block_id, offset_in_block) + std::sort(std::begin(index_to_out), std::end(index_to_out)); + + Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); + + // TODO: merge requests + std::vector requests; + std::vector pointers; + std::vector> blocks_to_indices; + requests.reserve(index_to_out.size()); + pointers.reserve(index_to_out.size()); + blocks_to_indices.reserve(index_to_out.size()); + for (size_t i = 0; i < index_to_out.size(); ++i) + { + if (!requests.empty() && + static_cast(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * block_size) + { + blocks_to_indices.back().push_back(i); + continue; + } + + iocb request{}; +#if defined(__FreeBSD__) + request.aio.aio_lio_opcode = LIO_READ; + request.aio.aio_fildes = fd; + request.aio.aio_buf = reinterpret_cast( + reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS)); + request.aio.aio_nbytes = SSD_BLOCK_SIZE; + request.aio.aio_offset = index_to_out[i].first; + request.aio_data = requests.size(); +#else + request.aio_lio_opcode = IOCB_CMD_PREAD; + request.aio_fildes = fd; + request.aio_buf = reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size); + request.aio_nbytes = block_size; + request.aio_offset = index_to_out[i].first.getBlockId() * block_size; + request.aio_data = requests.size(); +#endif + requests.push_back(request); + pointers.push_back(&requests.back()); + blocks_to_indices.emplace_back(); + blocks_to_indices.back().push_back(i); + } + + AIOContext aio_context(read_buffer_size); + + std::vector processed(requests.size(), false); + std::vector events(requests.size()); + for (auto & event : events) + event.res = -1; // TODO: remove + + size_t to_push = 0; + size_t to_pop = 0; + while (to_pop < requests.size()) + { + /// get io tasks from previous iteration + int popped = 0; + while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) < 0) + { + if (errno != EINTR) + throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); + } + + for (size_t i = to_pop; i < to_pop + popped; ++i) + { + const auto request_id = events[i].data; + const auto & request = requests[request_id]; + if (events[i].res != static_cast(request.aio_nbytes)) + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + + "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_READ_ERROR); + + uint64_t checksum = 0; + ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); + readBinary(checksum, buf_special); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(reinterpret_cast(request.aio_buf) + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + if (checksum != calculated_checksum) + { + throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); + } + + for (const size_t idx : blocks_to_indices[request_id]) + { + const auto & [file_index, out_index] = index_to_out[idx]; + ReadBufferFromMemory buf( + reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), + block_size - file_index.getAddressInBlock()); + set(out_index, buf); + } + + processed[request_id] = true; + } + + while (to_pop < requests.size() && processed[to_pop]) + ++to_pop; + + /// add new io tasks + const int new_tasks_count = std::min(read_buffer_size - (to_push - to_pop), requests.size() - to_push); + + int pushed = 0; + while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) + { + if (errno != EINTR) + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + } + to_push += pushed; + } +} + +void SSDComplexKeyCachePartition::clearOldestBlocks() +{ + Poco::Logger::get("GC").information("GC clear -----------------"); + // write_buffer_size, because we need to erase the whole buffer. + Memory read_buffer_memory(block_size * write_buffer_size, BUFFER_ALIGNMENT); + + iocb request{}; +#if defined(__FreeBSD__) + request.aio.aio_lio_opcode = LIO_READ; + request.aio.aio_fildes = fd; + request.aio.aio_buf = reinterpret_cast(reinterpret_cast(read_buffer_memory.data())); + request.aio.aio_nbytes = block_size * write_buffer_size; + request.aio.aio_offset = (current_file_block_id % max_size) * block_size; + request.aio_data = 0; +#else + request.aio_lio_opcode = IOCB_CMD_PREAD; + request.aio_fildes = fd; + request.aio_buf = reinterpret_cast(read_buffer_memory.data()); + request.aio_nbytes = block_size * write_buffer_size; + request.aio_offset = (current_file_block_id % max_size) * block_size; + request.aio_data = 0; +#endif + + { + iocb* request_ptr = &request; + io_event event{}; + AIOContext aio_context(1); + + while (io_submit(aio_context.ctx, 1, &request_ptr) != 1) + { + if (errno != EINTR) + throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); + } + + while (io_getevents(aio_context.ctx, 1, 1, &event, nullptr) != 1) + { + if (errno != EINTR) + throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); + } + + if (event.res != static_cast(request.aio_nbytes)) + { + throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "aio_nbytes=" + std::to_string(request.aio_nbytes) + + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); + } + } + + TemporalComplexKeysPool tmp_keys_pool; + KeyRefs keys; + keys.reserve(write_buffer_size); + + // TODO: писать кол-во значений + for (size_t i = 0; i < write_buffer_size; ++i) + { + ReadBufferFromMemory read_buffer(read_buffer_memory.data() + i * block_size, block_size); + + uint64_t checksum = 0; + readBinary(checksum, read_buffer); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(read_buffer_memory.data() + i * block_size + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + if (checksum != calculated_checksum) + { + throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); + } + + uint32_t keys_in_current_block = 0; + readBinary(keys_in_current_block, read_buffer); + Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); + + for (uint32_t j = 0; j < keys_in_current_block; ++j) + { + keys.emplace_back(); + tmp_keys_pool.readKey(keys.back(), read_buffer); + + Metadata metadata; + readBinary(metadata.data, read_buffer); + + if (!metadata.isDefault()) + { + for (size_t attr = 0; attr < attributes_structure.size(); ++attr) + { + + switch (attributes_structure[attr]) + { + #define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + read_buffer.ignore(sizeof(TYPE)); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + #undef DISPATCH + + case AttributeUnderlyingType::utString: + { + size_t size = 0; + readVarUInt(size, read_buffer); + read_buffer.ignore(size); + } + break; + } + } + } + } + } + + const size_t start_block = current_file_block_id % max_size; + const size_t finish_block = start_block + block_size * write_buffer_size; + Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); + for (const auto& key : keys) + { + Index index; + if (key_to_index.get(key, index)) + { + size_t block_id = index.getBlockId(); + if (start_block <= block_id && block_id < finish_block) + key_to_index.erase(key); + } + } +} + +void SSDComplexKeyCachePartition::ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const +{ + for (size_t i = 0; i < attribute_index; ++i) + { + switch (attributes_structure[i]) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + buf.ignore(sizeof(TYPE)); \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + { + size_t size = 0; + readVarUInt(size, buf); + buf.ignore(size); + } + break; + } + } +} + +size_t SSDComplexKeyCachePartition::getId() const +{ + return file_id; +} + +double SSDComplexKeyCachePartition::getLoadFactor() const +{ + std::shared_lock lock(rw_lock); + return static_cast(current_file_block_id) / max_size; +} + +size_t SSDComplexKeyCachePartition::getElementCount() const +{ + std::shared_lock lock(rw_lock); + return key_to_index.size(); +} + +PaddedPODArray SSDComplexKeyCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const +{ + std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. + PaddedPODArray array; + for (const auto & [key, index] : key_to_index) + array.push_back(key); // TODO: exclude default + return array; +} + +void SSDComplexKeyCachePartition::remove() +{ + std::unique_lock lock(rw_lock); + std::filesystem::remove(std::filesystem::path(path + BIN_FILE_EXT)); +} + +SSDComplexKeyCacheStorage::SSDComplexKeyCacheStorage( + const AttributeTypes & attributes_structure_, + const std::string & path_, + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_, + const size_t write_buffer_size_, + const size_t max_stored_keys_) + : attributes_structure(attributes_structure_) + , path(path_) + , max_partitions_count(max_partitions_count_) + , partition_size(partition_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) + , log(&Poco::Logger::get("SSDComplexKeyCacheStorage")) +{ +} + +SSDComplexKeyCacheStorage::~SSDComplexKeyCacheStorage() +{ + std::unique_lock lock(rw_lock); + partition_delete_queue.splice(std::end(partition_delete_queue), partitions); + collectGarbage(); +} + +template +void SSDComplexKeyCacheStorage::getValue( + const size_t attribute_index, const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, + GetDefault & get_default, std::chrono::system_clock::time_point now) const +{ + size_t n = key_columns.front()->size(); + std::vector found(n, false); + + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->getValue(attribute_index, key_columns, key_types, out, found, get_default, now); + } + + size_t count_not_found = 0; + StringRefs tmp_refs(key_columns.size()); + for (size_t i = 0; i < n; ++i) + { + if (!found[i]) + { + auto key = not_found_pool.allocKey(i, key_columns, tmp_refs); + not_found[key].push_back(i); + ++count_not_found; + } + } + + query_count.fetch_add(n, std::memory_order_relaxed); + hit_count.fetch_add(n - count_not_found, std::memory_order_release); +} + +void SSDComplexKeyCacheStorage::getString( + const size_t attribute_index, const Columns & key_columns, const DataTypes & key_types, + StringRefs & refs, ArenaWithFreeLists & arena, + std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, + std::vector & default_ids, std::chrono::system_clock::time_point now) const +{ + size_t n = key_columns.front()->size(); + std::vector found(n, false); + + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->getString(attribute_index, key_columns, key_types, refs, arena, found, default_ids, now); + } + + size_t count_not_found = 0; + StringRefs tmp_refs(key_columns.size()); + for (size_t i = 0; i < n; ++i) + { + if (!found[i]) + { + auto key = not_found_pool.allocKey(i, key_columns, tmp_refs); + not_found[key].push_back(i); + ++count_not_found; + } + } + + query_count.fetch_add(n, std::memory_order_relaxed); + hit_count.fetch_add(n - count_not_found, std::memory_order_release); +} + +void SSDComplexKeyCacheStorage::has( + const Columns & key_columns, const DataTypes & key_types, ResultArrayType & out, + std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, std::chrono::system_clock::time_point now) const +{ + size_t n = key_columns.front()->size(); + for (size_t i = 0; i < n; ++i) + out[i] = HAS_NOT_FOUND; + std::vector found(n, false); + + { + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + partition->has(key_columns, key_types, out, found, now); + } + + size_t count_not_found = 0; + StringRefs tmp_refs(key_columns.size()); + for (size_t i = 0; i < n; ++i) + { + if (out[i] == HAS_NOT_FOUND) + { + auto key = not_found_pool.allocKey(i, key_columns, tmp_refs); + not_found[key].push_back(i); + ++count_not_found; + } + } + + query_count.fetch_add(n, std::memory_order_relaxed); + hit_count.fetch_add(n - count_not_found, std::memory_order_release); +} + +template +void SSDComplexKeyCacheStorage::update( + DictionarySourcePtr & source_ptr, + const Columns & key_columns, + const DataTypes & key_types, + const KeyRefs & required_keys, + const std::vector & required_rows, + PresentIdHandler && on_updated, + AbsentIdHandler && on_key_not_found, + const DictionaryLifetime lifetime) +{ + auto append_block = [&key_types, this]( + const Columns & new_keys, + const SSDComplexKeyCachePartition::Attributes & new_attributes, + const PaddedPODArray & metadata) + { + size_t inserted = 0; + while (inserted < metadata.size()) + { + if (!partitions.empty()) + inserted += partitions.front()->appendBlock( + new_keys, key_types, new_attributes, metadata, inserted); + if (inserted < metadata.size()) + { + partitions.emplace_front(std::make_unique( + AttributeUnderlyingType::utUInt64, attributes_structure, path, + (partitions.empty() ? 0 : partitions.front()->getId() + 1), + partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + } + } + + collectGarbage(); + }; + + CurrentMetrics::Increment metric_increment{CurrentMetrics::DictCacheRequests}; + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequested, required_keys.size()); + + std::unordered_map remaining_keys{required_keys.size()}; + for (const auto & key : required_keys) + remaining_keys.insert({key, 0}); + + const auto now = std::chrono::system_clock::now(); + + { + const auto keys_size = key_columns.size(); + StringRefs keys(keys_size); + TemporalComplexKeysPool tmp_keys_pool; + + const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; + + if (now > backoff_end_time) + { + try + { + if (update_error_count) + { + /// Recover after error: we have to clone the source here because + /// it could keep connections which should be reset after error. + source_ptr = source_ptr->clone(); + } + + Stopwatch watch; + auto stream = source_ptr->loadKeys(key_columns, required_rows); + stream->readPrefix(); + + while (const auto block = stream->read()) + { + const auto new_key_columns = ext::map( + ext::range(0, keys_size), + [&](const size_t attribute_idx) { return block.safeGetByPosition(attribute_idx).column; }); + + const auto new_attributes = createAttributesFromBlock(block, keys_size, attributes_structure); + + const auto rows_num = block.rows(); + + PaddedPODArray metadata(rows_num); + + for (const auto i : ext::range(0, rows_num)) + { + auto key = tmp_keys_pool.allocKey(i, new_key_columns, keys); + SCOPE_EXIT(tmp_keys_pool.rollback(key)); + + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; + metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); + /// mark corresponding id as found + on_updated(key, i, new_attributes); + remaining_keys[key] = 1; + } + + append_block(new_key_columns, new_attributes, metadata); + } + + stream->readSuffix(); + + update_error_count = 0; + last_update_exception = std::exception_ptr{}; + backoff_end_time = std::chrono::system_clock::time_point{}; + + ProfileEvents::increment(ProfileEvents::DictCacheRequestTimeNs, watch.elapsed()); + } + catch (...) + { + ++update_error_count; + last_update_exception = std::current_exception(); + backoff_end_time = now + std::chrono::seconds(calculateDurationWithBackoff(rnd_engine, update_error_count)); + + tryLogException(last_update_exception, log, + "Could not update ssd cache dictionary, next update is scheduled at " + ext::to_string(backoff_end_time)); + } + } + } + + auto append_defaults = [this]( + const KeyRefs & new_keys, + const PaddedPODArray & metadata) + { + size_t inserted = 0; + while (inserted < metadata.size()) + { + if (!partitions.empty()) + inserted += partitions.front()->appendDefaults( + new_keys, metadata, inserted); + if (inserted < metadata.size()) + { + partitions.emplace_front(std::make_unique( + AttributeUnderlyingType::utUInt64, attributes_structure, path, + (partitions.empty() ? 0 : partitions.front()->getId() + 1), + partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + } + } + + collectGarbage(); + }; + + size_t not_found_num = 0, found_num = 0; + /// Check which ids have not been found and require setting null_value + KeyRefs default_keys; + + PaddedPODArray metadata; + { + const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; + + for (const auto & key_found_pair : remaining_keys) + { + if (key_found_pair.second) + { + ++found_num; + continue; + } + ++not_found_num; + + const auto key = key_found_pair.first; + + if (update_error_count) + { + /// TODO: юзать старые значения. + + /// We don't have expired data for that `id` so all we can do is to rethrow `last_exception`. + std::rethrow_exception(last_update_exception); + } + + std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; + metadata.emplace_back(); + metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); + metadata.back().setDefault(); + + default_keys.push_back(key); + + /// inform caller that the cell has not been found + on_key_not_found(key); + } + + if (not_found_num) + append_defaults(default_keys, metadata); + } + + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedMiss, not_found_num); + ProfileEvents::increment(ProfileEvents::DictCacheKeysRequestedFound, found_num); + ProfileEvents::increment(ProfileEvents::DictCacheRequests); +} + +PaddedPODArray SSDComplexKeyCacheStorage::getCachedIds() const +{ + /*PaddedPODArray array; + + const auto now = std::chrono::system_clock::now(); + + std::shared_lock lock(rw_lock); + for (auto & partition : partitions) + { + const auto cached_in_partition = partition->getCachedIds(now); + array.insert(std::begin(cached_in_partition), std::end(cached_in_partition)); + }*/ + + return {}; +} + +double SSDComplexKeyCacheStorage::getLoadFactor() const +{ + double result = 0; + std::shared_lock lock(rw_lock); + for (const auto & partition : partitions) + result += partition->getLoadFactor(); + return result / partitions.size(); +} + +size_t SSDComplexKeyCacheStorage::getElementCount() const +{ + size_t result = 0; + std::shared_lock lock(rw_lock); + for (const auto & partition : partitions) + result += partition->getElementCount(); + return result; +} + +void SSDComplexKeyCacheStorage::collectGarbage() +{ + // add partitions to queue + while (partitions.size() > max_partitions_count) + { + partition_delete_queue.splice(std::end(partition_delete_queue), partitions, std::prev(std::end(partitions))); + } + + // drop unused partitions + while (!partition_delete_queue.empty() && partition_delete_queue.front().use_count() == 1) + { + partition_delete_queue.front()->remove(); + partition_delete_queue.pop_front(); + } +} + +SSDComplexKeyCachePartition::Attributes SSDComplexKeyCacheStorage::createAttributesFromBlock( + const Block & block, const size_t begin_column, const std::vector & structure) +{ + SSDComplexKeyCachePartition::Attributes attributes; + + const auto columns = block.getColumns(); + for (size_t i = 0; i < structure.size(); ++i) + { + const auto & column = columns[i + begin_column]; + switch (structure[i]) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + SSDComplexKeyCachePartition::Attribute::Container values(column->size()); \ + memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ + attributes.emplace_back(); \ + attributes.back().type = structure[i]; \ + attributes.back().values = std::move(values); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + { + attributes.emplace_back(); + SSDComplexKeyCachePartition::Attribute::Container values(column->size()); + for (size_t j = 0; j < column->size(); ++j) + { + const auto ref = column->getDataAt(j); + values[j].resize(ref.size); + memcpy(values[j].data(), ref.data, ref.size); + } + attributes.back().type = structure[i]; + attributes.back().values = std::move(values); + } + break; + } + } + + return attributes; +} + +SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( + const std::string & name_, + const DictionaryStructure & dict_struct_, + DictionarySourcePtr source_ptr_, + const DictionaryLifetime dict_lifetime_, + const std::string & path_, + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_, + const size_t write_buffer_size_, + const size_t max_stored_keys_) + : name(name_) + , dict_struct(dict_struct_) + , source_ptr(std::move(source_ptr_)) + , dict_lifetime(dict_lifetime_) + , path(path_) + , max_partitions_count(max_partitions_count_) + , partition_size(partition_size_) + , block_size(block_size_) + , read_buffer_size(read_buffer_size_) + , write_buffer_size(write_buffer_size_) + , max_stored_keys(max_stored_keys_) + , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), + path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) + , log(&Poco::Logger::get("SSDComplexKeyCacheDictionary")) +{ + LOG_INFO(log, "Using storage path '" << path << "'."); + if (!this->source_ptr->supportsSelectiveLoad()) + throw Exception{name + ": source cannot be used with CacheDictionary", ErrorCodes::UNSUPPORTED_METHOD}; + + createAttributes(); +} + +#define DECLARE(TYPE) \ + void SSDComplexKeyCacheDictionary::get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ + const auto null_value = std::get(null_values[index]); \ + getItemsNumberImpl( \ + index, \ + key_columns, \ + key_types, \ + out, \ + [&](const size_t) { return null_value; }); \ + } + + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +#define DECLARE(TYPE) \ + void SSDComplexKeyCacheDictionary::get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + const PaddedPODArray & def, \ + ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ + getItemsNumberImpl( \ + index, \ + key_columns, \ + key_types, \ + out, \ + [&](const size_t row) { return def[row]; }); \ + } + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +#define DECLARE(TYPE) \ + void SSDComplexKeyCacheDictionary::get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + const TYPE def, \ + ResultArrayType & out) const \ + { \ + const auto index = getAttributeIndex(attribute_name); \ + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ + getItemsNumberImpl( \ + index, \ + key_columns, \ + key_types, \ + out, \ + [&](const size_t) { return def; }); \ + } + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + +template +void SSDComplexKeyCacheDictionary::getItemsNumberImpl( + const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, DefaultGetter && get_default) const +{ + const auto now = std::chrono::system_clock::now(); + + TemporalComplexKeysPool not_found_pool; + std::unordered_map> not_found_keys; + storage.getValue(attribute_index, key_columns, key_types, out, not_found_keys, not_found_pool, get_default, now); + if (not_found_keys.empty()) + return; + + std::vector required_keys(not_found_keys.size()); + std::transform(std::begin(not_found_keys), std::end(not_found_keys), std::begin(required_keys), [](const auto & pair) { return pair.first; }); + std::vector required_rows; + required_rows.reserve(required_keys.size()); + for (const auto & key_ref : required_keys) + required_rows.push_back(not_found_keys[key_ref].front()); + + storage.update( + source_ptr, + key_columns, + key_types, + required_keys, + required_rows, + [&](const auto key, const auto row, const auto & new_attributes) + { + for (const size_t out_row : not_found_keys[key]) + out[out_row] = std::get>(new_attributes[attribute_index].values)[row]; + }, + [&](const auto key) + { + for (const size_t row : not_found_keys[key]) + out[row] = get_default(row); + }, + getLifetime()); +} + +void SSDComplexKeyCacheDictionary::getString( + const std::string & attribute_name, + const Columns & key_columns, const DataTypes & key_types, ColumnString * out) const +{ + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); + + const auto null_value = StringRef{std::get(null_values[index])}; + + getItemsStringImpl(index, key_columns, key_types, out, [&](const size_t) { return null_value; }); +} + +void SSDComplexKeyCacheDictionary::getString( + const std::string & attribute_name, + const Columns & key_columns, const DataTypes & key_types, + const ColumnString * const def, ColumnString * const out) const +{ + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); + + getItemsStringImpl(index, key_columns, key_types, out, [&](const size_t row) { return def->getDataAt(row); }); +} + +void SSDComplexKeyCacheDictionary::getString( + const std::string & attribute_name, + const Columns & key_columns, + const DataTypes & key_types, + const String & def, + ColumnString * const out) const +{ + const auto index = getAttributeIndex(attribute_name); + checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::utString); + + getItemsStringImpl(index, key_columns, key_types, out, [&](const size_t) { return StringRef{def}; }); +} + +template +void SSDComplexKeyCacheDictionary::getItemsStringImpl( + const size_t attribute_index, + const Columns & key_columns, + const DataTypes & key_types, + ColumnString * out, + DefaultGetter && get_default) const +{ + const auto now = std::chrono::system_clock::now(); + + TemporalComplexKeysPool not_found_pool; + std::unordered_map> not_found_keys; + + const size_t n = key_columns.front()->size(); + + StringRefs refs(n); + ArenaWithFreeLists string_arena; + std::vector default_rows; + storage.getString( + attribute_index, key_columns, key_types, + refs, string_arena, not_found_keys, not_found_pool, default_rows, now); + std::sort(std::begin(default_rows), std::end(default_rows)); + + if (not_found_keys.empty()) + { + size_t default_index = 0; + for (size_t row = 0; row < n; ++row) + { + if (unlikely(default_index != default_rows.size() && default_rows[default_index] == row)) + { + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + ++default_index; + } + else + out->insertData(refs[row].data, refs[row].size); + } + return; + } + + std::vector required_keys(not_found_keys.size()); + std::transform(std::begin(not_found_keys), std::end(not_found_keys), std::begin(required_keys), [](const auto & pair) { return pair.first; }); + + std::unordered_map update_result; + + std::vector required_rows; + required_rows.reserve(required_keys.size()); + for (const auto & key_ref : required_keys) + required_rows.push_back(not_found_keys[key_ref].front()); + + storage.update( + source_ptr, + key_columns, + key_types, + required_keys, + required_rows, + [&](const auto key, const auto row, const auto & new_attributes) + { + update_result[key] = std::get>(new_attributes[attribute_index].values)[row]; + }, + [&](const auto) {}, + getLifetime()); + + TemporalComplexKeysPool tmp_keys_pool; + StringRefs tmp_refs(key_columns.size()); + size_t default_index = 0; + for (size_t row = 0; row < n; ++row) + { + const auto key = tmp_keys_pool.allocKey(row, key_columns, tmp_refs); + SCOPE_EXIT(tmp_keys_pool.rollback(key)); + if (unlikely(default_index != default_rows.size() && default_rows[default_index] == row)) + { + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + ++default_index; + } + else if (auto it = not_found_keys.find(key); it == std::end(not_found_keys)) + { + out->insertData(refs[row].data, refs[row].size); + } + else if (auto it_update = update_result.find(key); it_update != std::end(update_result)) + { + out->insertData(it_update->second.data(), it_update->second.size()); + } + else + { + auto to_insert = get_default(row); + out->insertData(to_insert.data, to_insert.size); + } + } +} + +void SSDComplexKeyCacheDictionary::has( + const Columns & key_columns, + const DataTypes & key_types, + PaddedPODArray & out) const +{ + const auto now = std::chrono::system_clock::now(); + + std::unordered_map> not_found_keys; + TemporalComplexKeysPool not_found_pool; + storage.has(key_columns, key_types, out, not_found_keys, not_found_pool, now); + if (not_found_keys.empty()) + return; + + std::vector required_keys(not_found_keys.size()); + std::transform(std::begin(not_found_keys), std::end(not_found_keys), std::begin(required_keys), [](const auto & pair) { return pair.first; }); + + std::vector required_rows; + required_rows.reserve(required_keys.size()); + for (const auto & key_ref : required_keys) + required_rows.push_back(not_found_keys[key_ref].front()); + + storage.update( + source_ptr, + key_columns, + key_types, + required_keys, + required_rows, + [&](const auto key, const auto, const auto &) + { + for (const size_t out_row : not_found_keys[key]) + out[out_row] = true; + }, + [&](const auto key) + { + for (const size_t row : not_found_keys[key]) + out[row] = false; + }, + getLifetime()); +} + +BlockInputStreamPtr SSDComplexKeyCacheDictionary::getBlockInputStream( + const Names & /* column_names */, size_t /* max_block_size*/) const +{ + //using BlockInputStreamType = DictionaryBlockInputStream; + return nullptr; //std::make_shared(shared_from_this(), max_block_size, storage.getCachedIds(), column_names); +} + +size_t SSDComplexKeyCacheDictionary::getAttributeIndex(const std::string & attr_name) const +{ + auto it = attribute_index_by_name.find(attr_name); + if (it == std::end(attribute_index_by_name)) + throw Exception{"Attribute `" + name + "` does not exist.", ErrorCodes::BAD_ARGUMENTS}; + return it->second; +} + +template +AttributeValueVariant SSDComplexKeyCacheDictionary::createAttributeNullValueWithTypeImpl(const Field & null_value) +{ + AttributeValueVariant var_null_value = static_cast(null_value.get>()); + bytes_allocated += sizeof(T); + return var_null_value; +} + +template <> +AttributeValueVariant SSDComplexKeyCacheDictionary::createAttributeNullValueWithTypeImpl(const Field & null_value) +{ + AttributeValueVariant var_null_value = null_value.get(); + bytes_allocated += sizeof(StringRef); + return var_null_value; +} + +AttributeValueVariant SSDComplexKeyCacheDictionary::createAttributeNullValueWithType(const AttributeUnderlyingType type, const Field & null_value) +{ + switch (type) + { +#define DISPATCH(TYPE) \ +case AttributeUnderlyingType::ut##TYPE: \ + return createAttributeNullValueWithTypeImpl(null_value); + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) + DISPATCH(String) +#undef DISPATCH + } + throw Exception{"Unknown attribute type: " + std::to_string(static_cast(type)), ErrorCodes::TYPE_MISMATCH}; +} + +void SSDComplexKeyCacheDictionary::createAttributes() +{ + null_values.reserve(dict_struct.attributes.size()); + for (size_t i = 0; i < dict_struct.attributes.size(); ++i) + { + const auto & attribute = dict_struct.attributes[i]; + + attribute_index_by_name.emplace(attribute.name, i); + null_values.push_back(createAttributeNullValueWithType(attribute.underlying_type, attribute.null_value)); + + if (attribute.hierarchical) + throw Exception{name + ": hierarchical attributes not supported for dictionary of type " + getTypeName(), + ErrorCodes::TYPE_MISMATCH}; + } +} + +void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) +{ + auto create_layout = [=](const std::string & name, + const DictionaryStructure & dict_struct, + const Poco::Util::AbstractConfiguration & config, + const std::string & config_prefix, + DictionarySourcePtr source_ptr) -> DictionaryPtr + { + if (dict_struct.id) + throw Exception{"'id' is not supported for dictionary of layout 'complex_key_cache'", ErrorCodes::UNSUPPORTED_METHOD}; + + if (dict_struct.range_min || dict_struct.range_max) + throw Exception{name + + ": elements .structure.range_min and .structure.range_max should be defined only " + "for a dictionary of layout 'range_hashed'", + ErrorCodes::BAD_ARGUMENTS}; + const auto & layout_prefix = config_prefix + ".layout"; + + const auto max_partitions_count = config.getInt(layout_prefix + ".ssd_complex_key.max_partitions_count", DEFAULT_PARTITIONS_COUNT); + if (max_partitions_count <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + + const auto block_size = config.getInt(layout_prefix + ".ssd_complex_key.block_size", DEFAULT_SSD_BLOCK_SIZE); + if (block_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; + + const auto partition_size = config.getInt64(layout_prefix + ".ssd_complex_key.partition_size", DEFAULT_FILE_SIZE); + if (partition_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; + if (partition_size % block_size != 0) + throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + + const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd_complex_key.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); + if (read_buffer_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + if (read_buffer_size % block_size != 0) + throw Exception{name + ": read_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + + const auto write_buffer_size = config.getInt64(layout_prefix + ".ssd_complex_key.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); + if (write_buffer_size <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + if (write_buffer_size % block_size != 0) + throw Exception{name + ": write_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + + auto path = config.getString(layout_prefix + ".ssd_complex_key.path"); + if (path.empty()) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", + ErrorCodes::BAD_ARGUMENTS}; + if (path.at(0) != '/') + path = std::filesystem::path{config.getString("path")}.concat(path).string(); + + const auto max_stored_keys = config.getInt64(layout_prefix + ".ssd_complex_key.max_stored_keys", DEFAULT_MAX_STORED_KEYS); + if (max_stored_keys <= 0) + throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; + + const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; + return std::make_unique( + name, dict_struct, std::move(source_ptr), dict_lifetime, path, + max_partitions_count, partition_size / block_size, block_size, + read_buffer_size / block_size, write_buffer_size / block_size, + max_stored_keys); + }; + factory.registerLayout("ssd_complex_key", create_layout, true); +} + +} diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h new file mode 100644 index 000000000000..6e64078cffdf --- /dev/null +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -0,0 +1,790 @@ +#pragma once + +#include "DictionaryStructure.h" +#include "IDictionary.h" +#include "IDictionarySource.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +class KeyRef +{ +public: + explicit KeyRef(char * data) : ptr(data) {} + + KeyRef() : ptr(nullptr) {} + + inline UInt16 size() const { + return *reinterpret_cast(ptr); + } + + inline size_t fullSize() const { + return static_cast(size()) + sizeof(UInt16); + } + + inline char * data() const { + return ptr + sizeof(UInt16); + } + + inline char * fullData() const { + return ptr; + } + + inline char * fullData() { + return ptr; + } + + inline const StringRef getRef() const { + return StringRef(data(), size()); + } + + inline bool operator==(const KeyRef & other) const { + return getRef() == other.getRef(); + } + + inline bool operator<(const KeyRef & other) const { + return getRef() < other.getRef(); + } + +private: + char * ptr; +}; + +using KeyRefs = std::vector; +} + +namespace std +{ + template <> + struct hash + { + size_t operator() (DB::KeyRef key_ref) const + { + return hasher(key_ref.getRef()); + } + + std::hash hasher; + }; +} + +namespace DB +{ + +using AttributeValueVariant = std::variant< + UInt8, + UInt16, + UInt32, + UInt64, + UInt128, + Int8, + Int16, + Int32, + Int64, + Decimal32, + Decimal64, + Decimal128, + Float32, + Float64, + String>; + +template +class ComplexKeysPoolImpl +{ +public: + KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) + { + if constexpr (std::is_same_v) + { + // not working now + const auto res = arena->alloc(); + auto place = res; + + for (const auto & key_column : key_columns) + { + const StringRef key = key_column->getDataAt(row); + memcpy(place, key.data, key.size); + place += key.size; + } + + return KeyRef(res); + } + else + { + const auto keys_size = key_columns.size(); + UInt16 sum_keys_size{}; + + for (size_t j = 0; j < keys_size; ++j) + { + keys[j] = key_columns[j]->getDataAt(row); + sum_keys_size += keys[j].size; + if (!key_columns[j]->valuesHaveFixedSize()) // String + sum_keys_size += sizeof(size_t) + 1; + } + + auto place = arena.alloc(sum_keys_size + sizeof(sum_keys_size)); + + auto key_start = place; + memcpy(key_start, &sum_keys_size, sizeof(sum_keys_size)); + key_start += sizeof(sum_keys_size); + for (size_t j = 0; j < keys_size; ++j) + { + if (!key_columns[j]->valuesHaveFixedSize()) // String + { + auto start = key_start; + auto key_size = keys[j].size + 1; + memcpy(key_start, &key_size, sizeof(size_t)); + key_start += sizeof(size_t); + memcpy(key_start, keys[j].data, keys[j].size); + key_start += keys[j].size; + *key_start = '\0'; + ++key_start; + keys[j].data = start; + keys[j].size += sizeof(size_t) + 1; + } + else + { + memcpy(key_start, keys[j].data, keys[j].size); + keys[j].data = key_start; + key_start += keys[j].size; + } + } + + return KeyRef(place); + } + } + + KeyRef copyKeyFrom(const KeyRef & key) + { + char * data = arena.alloc(key.fullSize()); + memcpy(data, key.fullData(), key.fullSize()); + return KeyRef(data); + } + + void freeKey(const KeyRef & key) + { + if constexpr (std::is_same_v) + arena.free(key.fullData(), key.fullSize()); + else if constexpr (std::is_same_v) + arena.free(key.fullData()); + else + throw Exception("Free not supported.", ErrorCodes::LOGICAL_ERROR); + } + + void rollback(const KeyRef & key) + { + if constexpr (std::is_same_v) + arena.rollback(key.fullSize()); + else + throw Exception("Rollback not supported.", ErrorCodes::LOGICAL_ERROR); + } + + void writeKey(const KeyRef & key, WriteBuffer & buf) + { + buf.write(key.fullData(), key.fullSize()); + } + + void readKey(KeyRef & key, ReadBuffer & buf) + { + UInt16 sz; + readBinary(sz, buf); + char * data = nullptr; + if constexpr (std::is_same_v) + data = arena.alloc(); + else + data = arena.alloc(sz + sizeof(sz)); + memcpy(data, &sz, sizeof(sz)); + buf.read(data + sizeof(sz), sz); + key = KeyRef(data); + } + + void ignoreKey(ReadBuffer & buf) const + { + UInt16 sz; + readBinary(sz, buf); + buf.ignore(sz); + } + +private: + A arena; +}; + +using TemporalComplexKeysPool = ComplexKeysPoolImpl; +using ComplexKeysPool = ComplexKeysPoolImpl; +//using FixedComplexKeysPool = ComplexKeysPoolImpl; + +template +class ComplexKeyLRUCache +{ + using Iter = typename std::list::iterator; + + struct Cell + { + Iter iter; + V val; + }; + +public: + ComplexKeyLRUCache(size_t max_size_, Pool & keys_pool_) + : max_size(max_size_) + , keys_pool(keys_pool_) + { + } + + void set(K key, V val) + { + std::lock_guard lock(mutex); + auto it = cache.find(key); + if (it == std::end(cache)) + { + auto & item = cache[key]; + item.iter = queue.insert(std::end(queue), key); + item.val = val; + if (queue.size() > max_size) + { + keys_pool.freeKey(queue.front()); + cache.erase(queue.front()); + queue.pop_front(); + } + } + else + { + queue.erase(it->second.iter); + it->second.iter = queue.insert(std::end(queue), key); + it->second.val = val; + } + } + + bool get(K key, V & val) + { + std::lock_guard lock(mutex); + auto it = cache.find(key); + if (it == std::end(cache)) + return false; + val = it->second.val; + queue.erase(it->second.iter); + it->second.iter = queue.insert(std::end(queue), key); + return true; + } + + bool erase(K key) + { + std::lock_guard lock(mutex); + auto it = cache.find(key); + if (it == std::end(cache)) + return false; + + keys_pool.freeKey(key); + queue.erase(it->second.iter); + cache.erase(it); + return true; + } + + size_t size() + { + std::lock_guard lock(mutex); + return cache.size(); + } + + auto begin() + { + std::lock_guard lock(mutex); + return std::begin(cache); + } + + auto end() + { + std::lock_guard lock(mutex); + return std::end(cache); + } + +private: + std::unordered_map cache; + std::list queue; + size_t max_size; + Pool & keys_pool; + std::mutex mutex; +}; + +class SSDComplexKeyCachePartition +{ +public: + struct Index final + { + bool inMemory() const; + void setInMemory(const bool in_memory); + + bool exists() const; + void setNotExists(); + + size_t getAddressInBlock() const; + void setAddressInBlock(const size_t address_in_block); + + size_t getBlockId() const; + void setBlockId(const size_t block_id); + + bool operator< (const Index & rhs) const { return index < rhs.index; } + + /// Stores `is_in_memory` flag, block id, address in uncompressed block + uint64_t index = 0; + }; + + struct Metadata final + { + using time_point_t = std::chrono::system_clock::time_point; + using time_point_rep_t = time_point_t::rep; + using time_point_urep_t = std::make_unsigned_t; + + time_point_t expiresAt() const; + void setExpiresAt(const time_point_t & t); + + bool isDefault() const; + void setDefault(); + + /// Stores both expiration time and `is_default` flag in the most significant bit + time_point_urep_t data = 0; + }; + + using Offset = size_t; + using Offsets = std::vector; + + + SSDComplexKeyCachePartition( + const AttributeUnderlyingType & key_structure, + const std::vector & attributes_structure, + const std::string & dir_path, + const size_t file_id, + const size_t max_size, + const size_t block_size, + const size_t read_buffer_size, + const size_t write_buffer_size, + const size_t max_stored_keys); + + ~SSDComplexKeyCachePartition(); + + template + using ResultArrayType = std::conditional_t, DecimalPaddedPODArray, PaddedPODArray>; + + template + void getValue(const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, std::vector & found, GetDefault & get_default, + std::chrono::system_clock::time_point now) const; + + void getString(const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + StringRefs & refs, ArenaWithFreeLists & arena, std::vector & found, + std::vector & default_ids, std::chrono::system_clock::time_point now) const; + + void has(const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, std::vector & found, + std::chrono::system_clock::time_point now) const; + + struct Attribute + { + template + using Container = std::vector; + + AttributeUnderlyingType type; + std::variant< + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container, + Container> values; + }; + using Attributes = std::vector; + + size_t appendBlock( + const Columns & key_columns, + const DataTypes & key_types, + const Attributes & new_attributes, + const PaddedPODArray & metadata, + const size_t begin); + + size_t appendDefaults( + const KeyRefs & keys, + const PaddedPODArray & metadata, + const size_t begin); + + void clearOldestBlocks(); + + void flush(); + + void remove(); + + size_t getId() const; + + PaddedPODArray getCachedIds(const std::chrono::system_clock::time_point now) const; + + double getLoadFactor() const; + + size_t getElementCount() const; + +private: + size_t append( + const KeyRefs & keys, + const Attributes & new_attributes, + const PaddedPODArray & metadata, + const size_t begin); + + template + void getImpl(const Columns & key_columns, const DataTypes & key_types, + SetFunc & set, std::vector & found) const; + + template + void getValueFromMemory(const PaddedPODArray & indices, SetFunc & set) const; + + template + void getValueFromStorage(const PaddedPODArray & indices, SetFunc & set) const; + + void ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const; + + /*KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) const; + void freeKey(const KeyRef key) const; + + void writeKey(KeyRef key, WriteBuffer & buf); + template + void readKey(KeyRef & key, ArenaForKey & arena, ReadBuffer & buf); + void ignoreKey(ReadBuffer & buf);*/ + + const size_t file_id; + const size_t max_size; + const size_t block_size; + const size_t read_buffer_size; + const size_t write_buffer_size; + const size_t max_stored_keys; + const std::string path; + + mutable std::shared_mutex rw_lock; + + int fd = -1; + + ComplexKeysPool keys_pool; + mutable ComplexKeyLRUCache key_to_index; + KeyRefs keys_buffer; + + const std::vector attributes_structure; + + std::optional> memory; + std::optional write_buffer; + uint32_t keys_in_block = 0; + //CompressionCodecPtr codec; + + size_t current_memory_block_id = 0; + size_t current_file_block_id = 0; +}; + +using SSDComplexKeyCachePartitionPtr = std::shared_ptr; + + +class SSDComplexKeyCacheStorage +{ +public: + using AttributeTypes = std::vector; + + SSDComplexKeyCacheStorage( + const AttributeTypes & attributes_structure, + const std::string & path, + const size_t max_partitions_count, + const size_t partition_size, + const size_t block_size, + const size_t read_buffer_size, + const size_t write_buffer_size, + const size_t max_stored_keys); + + ~SSDComplexKeyCacheStorage(); + + template + using ResultArrayType = SSDComplexKeyCachePartition::ResultArrayType; + + template + void getValue(const size_t attribute_index, const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, + GetDefault & get_default, std::chrono::system_clock::time_point now) const; + + void getString(const size_t attribute_index, const Columns & key_columns, const DataTypes & key_types, + StringRefs & refs, ArenaWithFreeLists & arena, std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, + std::vector & default_ids, std::chrono::system_clock::time_point now) const; + + void has(const Columns & key_columns, const DataTypes & key_types, ResultArrayType & out, + std::unordered_map> & not_found, + TemporalComplexKeysPool & not_found_pool, std::chrono::system_clock::time_point now) const; + + template + void update(DictionarySourcePtr & source_ptr, + const Columns & key_columns, const DataTypes & key_types, + const KeyRefs & required_keys, const std::vector & required_rows, + PresentIdHandler && on_updated, AbsentIdHandler && on_key_not_found, + const DictionaryLifetime lifetime); + + PaddedPODArray getCachedIds() const; + + std::exception_ptr getLastException() const { return last_update_exception; } + + const std::string & getPath() const { return path; } + + size_t getQueryCount() const { return query_count.load(std::memory_order_relaxed); } + + size_t getHitCount() const { return hit_count.load(std::memory_order_acquire); } + + size_t getElementCount() const; + + double getLoadFactor() const; + +private: + SSDComplexKeyCachePartition::Attributes createAttributesFromBlock( + const Block & block, const size_t begin_column, const std::vector & structure); + + void collectGarbage(); + + const AttributeTypes attributes_structure; + + const std::string path; + const size_t max_partitions_count; + const size_t partition_size; + const size_t block_size; + const size_t read_buffer_size; + const size_t write_buffer_size; + const size_t max_stored_keys; + + mutable std::shared_mutex rw_lock; + std::list partitions; + std::list partition_delete_queue; + + Logger * const log; + + mutable pcg64 rnd_engine; + + mutable std::exception_ptr last_update_exception; + mutable size_t update_error_count = 0; + mutable std::chrono::system_clock::time_point backoff_end_time; + + // stats + //mutable size_t bytes_allocated = 0; + + mutable std::atomic hit_count{0}; + mutable std::atomic query_count{0}; +}; + + +class SSDComplexKeyCacheDictionary final : public IDictionaryBase +{ +public: + SSDComplexKeyCacheDictionary( + const std::string & name_, + const DictionaryStructure & dict_struct_, + DictionarySourcePtr source_ptr_, + const DictionaryLifetime dict_lifetime_, + const std::string & path, + const size_t max_partitions_count_, + const size_t partition_size_, + const size_t block_size_, + const size_t read_buffer_size_, + const size_t write_buffer_size_, + const size_t max_stored_keys_); + + const std::string & getDatabase() const override { return name; } + const std::string & getName() const override { return name; } + const std::string & getFullName() const override { return getName(); } + + std::string getKeyDescription() const { return dict_struct.getKeyDescription(); } + + std::string getTypeName() const override { return "SSDComplexKeyCache"; } + + size_t getBytesAllocated() const override { return 0; } // TODO: ? + + size_t getQueryCount() const override { return storage.getQueryCount(); } + + double getHitRate() const override + { + return static_cast(storage.getHitCount()) / storage.getQueryCount(); + } + + size_t getElementCount() const override { return storage.getElementCount(); } + + double getLoadFactor() const override { return storage.getLoadFactor(); } + + bool supportUpdates() const override { return false; } + + std::shared_ptr clone() const override + { + return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, + max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); + } + + const IDictionarySource * getSource() const override { return source_ptr.get(); } + + const DictionaryLifetime & getLifetime() const override { return dict_lifetime; } + + const DictionaryStructure & getStructure() const override { return dict_struct; } + + bool isInjective(const std::string & attribute_name) const override + { + return dict_struct.attributes[getAttributeIndex(attribute_name)].injective; + } + + /*bool hasHierarchy() const { return false; } + + void toParent(const PaddedPODArray &, PaddedPODArray &) const { }*/ + + std::exception_ptr getLastException() const override { return storage.getLastException(); } + + template + using ResultArrayType = SSDComplexKeyCacheStorage::ResultArrayType; + +#define DECLARE(TYPE) \ + void get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void getString(const std::string & attribute_name, const Columns & key_columns, + const DataTypes & key_types, ColumnString * out) const; + +#define DECLARE(TYPE) \ + void get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + const PaddedPODArray & def, \ + ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void getString(const std::string & attribute_name, const Columns & key_columns, + const DataTypes & key_types, const ColumnString * const def, ColumnString * const out) const; + +#define DECLARE(TYPE) \ + void get##TYPE( \ + const std::string & attribute_name, \ + const Columns & key_columns, \ + const DataTypes & key_types, \ + const TYPE def, \ + ResultArrayType & out) const; + DECLARE(UInt8) + DECLARE(UInt16) + DECLARE(UInt32) + DECLARE(UInt64) + DECLARE(UInt128) + DECLARE(Int8) + DECLARE(Int16) + DECLARE(Int32) + DECLARE(Int64) + DECLARE(Float32) + DECLARE(Float64) + DECLARE(Decimal32) + DECLARE(Decimal64) + DECLARE(Decimal128) +#undef DECLARE + + void getString(const std::string & attribute_name, const Columns & key_columns, + const DataTypes & key_types, const String & def, ColumnString * const out) const; + + void has(const Columns & key_columns, const DataTypes & key_types, PaddedPODArray & out) const; + + BlockInputStreamPtr getBlockInputStream(const Names & column_names, size_t max_block_size) const override; + +private: + size_t getAttributeIndex(const std::string & attr_name) const; + + template + AttributeValueVariant createAttributeNullValueWithTypeImpl(const Field & null_value); + AttributeValueVariant createAttributeNullValueWithType(const AttributeUnderlyingType type, const Field & null_value); + void createAttributes(); + + template + void getItemsNumberImpl( + const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + ResultArrayType & out, DefaultGetter && get_default) const; + + template + void getItemsStringImpl( + const size_t attribute_index, + const Columns & key_columns, const DataTypes & key_types, + ColumnString * out, DefaultGetter && get_default) const; + + const std::string name; + const DictionaryStructure dict_struct; + mutable DictionarySourcePtr source_ptr; + const DictionaryLifetime dict_lifetime; + + const std::string path; + const size_t max_partitions_count; + const size_t partition_size; + const size_t block_size; + const size_t read_buffer_size; + const size_t write_buffer_size; + const size_t max_stored_keys; + + std::map attribute_index_by_name; + std::vector null_values; + mutable SSDComplexKeyCacheStorage storage; + Logger * const log; + + mutable size_t bytes_allocated = 0; +}; + +} diff --git a/src/Dictionaries/registerDictionaries.cpp b/src/Dictionaries/registerDictionaries.cpp index d2f37ad650b4..f3df13374aa4 100644 --- a/src/Dictionaries/registerDictionaries.cpp +++ b/src/Dictionaries/registerDictionaries.cpp @@ -32,6 +32,7 @@ void registerDictionaries() registerDictionaryHashed(factory); registerDictionaryCache(factory); registerDictionarySSDCache(factory); + registerDictionarySSDComplexKeyCache(factory); registerDictionaryPolygon(factory); } } diff --git a/src/Dictionaries/registerDictionaries.h b/src/Dictionaries/registerDictionaries.h index 1a4127b2d4c4..131c0dd0af45 100644 --- a/src/Dictionaries/registerDictionaries.h +++ b/src/Dictionaries/registerDictionaries.h @@ -25,6 +25,7 @@ void registerDictionaryFlat(DictionaryFactory & factory); void registerDictionaryHashed(DictionaryFactory & factory); void registerDictionaryCache(DictionaryFactory & factory); void registerDictionarySSDCache(DictionaryFactory & factory); +void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory); void registerDictionaryPolygon(DictionaryFactory & factory); void registerDictionaries(); diff --git a/src/Functions/FunctionsExternalDictionaries.h b/src/Functions/FunctionsExternalDictionaries.h index ec6f2d878c91..b359c7403f80 100644 --- a/src/Functions/FunctionsExternalDictionaries.h +++ b/src/Functions/FunctionsExternalDictionaries.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,7 @@ class FunctionDictHas final : public IFunction !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && - !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && #endif @@ -311,6 +312,7 @@ class FunctionDictGetString final : public IFunction !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && #endif @@ -496,6 +498,7 @@ class FunctionDictGetStringOrDefault final : public IFunction !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && #endif @@ -837,6 +840,7 @@ class FunctionDictGet final : public IFunction !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && #endif @@ -1100,6 +1104,7 @@ class FunctionDictGetOrDefault final : public IFunction !executeDispatch(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && #endif From 8e3442bae494f412a22bb1aa094035506d93cf29 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 30 Apr 2020 23:51:06 +0300 Subject: [PATCH 0074/1102] complex_key_test --- ...01280_ssd_complex_key_dictionary.reference | 0 .../01280_ssd_complex_key_dictionary.sql | 133 ++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference create mode 100644 tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql new file mode 100644 index 000000000000..2b9288d9257c --- /dev/null +++ b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql @@ -0,0 +1,133 @@ +SET send_logs_level = 'none'; + +DROP DATABASE IF EXISTS database_for_dict; + +CREATE DATABASE database_for_dict Engine = Ordinary; + +DROP TABLE IF EXISTS database_for_dict.table_for_dict; + +CREATE TABLE database_for_dict.table_for_dict +( + k1 String, + k2 Int32, + a UInt64, + b Int32, + c String +) +ENGINE = MergeTree() +ORDER BY (k1, k2); + +INSERT INTO database_for_dict.table_for_dict VALUES (toString(1), 3, 100, -100, 'clickhouse'), (toString(2), -1, 3, 4, 'database'), (toString(5), -3, 6, 7, 'columns'), (toString(10), -20, 9, 8, ''); +INSERT INTO database_for_dict.table_for_dict SELECT toString(number), number + 1, 0, -1, 'a' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 370; +INSERT INTO database_for_dict.table_for_dict SELECT toString(number), number + 10, 0, -1, 'b' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 370, 370; +INSERT INTO database_for_dict.table_for_dict SELECT toString(number), number + 100, 0, -1, 'c' FROM system.numbers WHERE number NOT IN (1, 2, 5, 10) LIMIT 700, 370; + +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +CREATE DICTIONARY database_for_dict.ssd_dict +( + k1 String, + k2 Int32, + a UInt64 DEFAULT 0, + b Int32 DEFAULT -1, + c String DEFAULT 'none' +) +PRIMARY KEY k1, k2 +SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) +LIFETIME(MIN 1000 MAX 2000) +LAYOUT(SSD_COMPLEX_KEY(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); + +SELECT 'TEST_SMALL'; +SELECT 'VALUE FROM RAM BUFFER'; + +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', tuple('1', toInt32(3))); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', tuple('1', toInt32(3))); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', tuple('1', toInt32(3))); + +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', tuple('1', toInt32(3))); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', tuple('1', toInt32(3))); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', tuple('1', toInt32(3))); + +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', tuple('2', toInt32(-1))); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', tuple('2', toInt32(-1))); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', tuple('2', toInt32(-1))); + +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', tuple('5', toInt32(-3))); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', tuple('5', toInt32(-3))); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', tuple('5', toInt32(-3))); + +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', tuple('10', toInt32(-20))); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', tuple('10', toInt32(-20))); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', tuple('10', toInt32(-20))); + +DROP DICTIONARY database_for_dict.ssd_dict; + +DROP TABLE IF EXISTS database_for_dict.keys_table; + +CREATE TABLE database_for_dict.keys_table +( + k1 String, + k2 Int32 +) +ENGINE = StripeLog(); + +INSERT INTO database_for_dict.keys_table VALUES ('1', 3); +INSERT INTO database_for_dict.keys_table SELECT toString(intHash64(number + 1) % 1200), 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 370; +INSERT INTO database_for_dict.keys_table VALUES ('2', -1); +INSERT INTO database_for_dict.keys_table SELECT toString(intHash64(number + 1) % 1200), 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 370, 370; +INSERT INTO database_for_dict.keys_table VALUES ('5', -3); +INSERT INTO database_for_dict.keys_table SELECT toString(intHash64(number + 1) % 1200), 11 + intHash64(number) % 1200 FROM system.numbers LIMIT 700, 370; +INSERT INTO database_for_dict.keys_table VALUES ('10', -20); + +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +CREATE DICTIONARY database_for_dict.ssd_dict +( + k1 String, + k2 Int32, + a UInt64 DEFAULT 0, + b Int32 DEFAULT -1, + c String DEFAULT 'none' +) +PRIMARY KEY k1, k2 +SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) +LIFETIME(MIN 1000 MAX 2000) +LAYOUT(SSD_COMPLEX_KEY(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); + +SELECT 'UPDATE DICTIONARY'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', (k1, k2))) FROM database_for_dict.keys_table; + +SELECT 'VALUE FROM DISK'; +-- -100 +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('1', 3)); + +-- 'clickhouse' +SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('1', 3)); + +SELECT 'VALUE FROM RAM BUFFER'; +-- 8 +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('10', -20)); + +-- '' +SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('10', -20)); + +SELECT 'VALUES FROM DISK AND RAM BUFFER'; +-- 118 +SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', (k1, k2))) FROM database_for_dict.keys_table; + +SELECT 'HAS'; +-- 1006 +SELECT count() FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', (k1, k2)); + +SELECT 'VALUES NOT FROM TABLE'; +-- 0 -1 none +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', 0)), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', 0)), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', 0)); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', 0)), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', 0)), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', 0)); + +SELECT 'DUPLICATE KEYS'; +SELECT arrayJoin([('1', 3), ('2', -1), ('', 0), ('', 0), ('2', -1), ('1', 3)]) AS keys, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(keys)); +--SELECT +DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; + +DROP TABLE IF EXISTS database_for_dict.keys_table; From ca621483a5ddb30b8e306e5d44179e7c94ddfd95 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:04 +0300 Subject: [PATCH 0075/1102] Update docs/en/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/en/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/en/interfaces/third-party/integrations.md b/docs/en/interfaces/third-party/integrations.md index 6b1c170252c5..993c4e8dffdb 100644 --- a/docs/en/interfaces/third-party/integrations.md +++ b/docs/en/interfaces/third-party/integrations.md @@ -99,6 +99,7 @@ toc_title: Integrations - Ruby - [Ruby on rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From 845dab0245a321f5c6bab7cb431b2830e15f1d19 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:20 +0300 Subject: [PATCH 0076/1102] Update docs/en/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/en/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/interfaces/third-party/integrations.md b/docs/en/interfaces/third-party/integrations.md index 993c4e8dffdb..a7538112c225 100644 --- a/docs/en/interfaces/third-party/integrations.md +++ b/docs/en/interfaces/third-party/integrations.md @@ -97,7 +97,7 @@ toc_title: Integrations - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) From b218198ab84040d639a34008eaf7ffa86d00eb23 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:28 +0300 Subject: [PATCH 0077/1102] Update docs/es/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/es/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/es/interfaces/third-party/integrations.md b/docs/es/interfaces/third-party/integrations.md index 50fd9f1ce3bb..416cab32a1dd 100644 --- a/docs/es/interfaces/third-party/integrations.md +++ b/docs/es/interfaces/third-party/integrations.md @@ -99,7 +99,7 @@ toc_title: "Integraci\xF3n" - [Ecto](https://github.com/elixir-ecto/ecto) - [Método de codificación de datos:](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From 6d9e48a7756b53cd63e7dfbd7d161b89f2d9443f Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:37 +0300 Subject: [PATCH 0078/1102] Update docs/fa/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/fa/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fa/interfaces/third-party/integrations.md b/docs/fa/interfaces/third-party/integrations.md index f9e0411201a8..cda8fa250d14 100644 --- a/docs/fa/interfaces/third-party/integrations.md +++ b/docs/fa/interfaces/third-party/integrations.md @@ -96,7 +96,7 @@ toc_title: "\u06CC\u06A9\u067E\u0627\u0631\u0686\u06AF\u06CC" - [Ecto](https://github.com/elixir-ecto/ecto) - [حذف جستجو](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From f0c6aa32dede17815c354594ab70411cf42fed9f Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:44 +0300 Subject: [PATCH 0079/1102] Update docs/fr/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/fr/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fr/interfaces/third-party/integrations.md b/docs/fr/interfaces/third-party/integrations.md index 100e4e34f54d..42341b97beaa 100644 --- a/docs/fr/interfaces/third-party/integrations.md +++ b/docs/fr/interfaces/third-party/integrations.md @@ -96,7 +96,7 @@ toc_title: "Int\xE9gration" - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From a352ed42291d6d6afee2b36246866c6f70384852 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:10:58 +0300 Subject: [PATCH 0080/1102] Update docs/ru/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/ru/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/interfaces/third-party/integrations.md b/docs/ru/interfaces/third-party/integrations.md index 508c67340457..fe26c85e62c2 100644 --- a/docs/ru/interfaces/third-party/integrations.md +++ b/docs/ru/interfaces/third-party/integrations.md @@ -92,7 +92,7 @@ - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From 0347fe936411abe24847f450b20bdfcfb528bbbb Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:12 +0300 Subject: [PATCH 0081/1102] Update docs/tr/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/tr/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tr/interfaces/third-party/integrations.md b/docs/tr/interfaces/third-party/integrations.md index fdfbbc18b65d..19eca91b65e7 100644 --- a/docs/tr/interfaces/third-party/integrations.md +++ b/docs/tr/interfaces/third-party/integrations.md @@ -96,7 +96,7 @@ toc_title: Entegrasyonlar - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From e576cfe52f75f3d02603c80d88d1d1627880215a Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:20 +0300 Subject: [PATCH 0082/1102] Update docs/zh/interfaces/third-party/integrations.md Co-authored-by: Ivan Blinkov --- docs/zh/interfaces/third-party/integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/interfaces/third-party/integrations.md b/docs/zh/interfaces/third-party/integrations.md index ecd33e93a4d9..a874b92b0cf7 100644 --- a/docs/zh/interfaces/third-party/integrations.md +++ b/docs/zh/interfaces/third-party/integrations.md @@ -90,7 +90,7 @@ - [Ecto](https://github.com/elixir-ecto/ecto) - [clickhouse\_ecto](https://github.com/appodeal/clickhouse_ecto) - Ruby - - [Ruby on rails](https://rubyonrails.org/) + - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From c39b85ea6952a31896fbe35260f38b7313852294 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:31 +0300 Subject: [PATCH 0083/1102] Update docs/es/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/es/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/es/interfaces/third-party/integrations.md b/docs/es/interfaces/third-party/integrations.md index 416cab32a1dd..dc28db3dff26 100644 --- a/docs/es/interfaces/third-party/integrations.md +++ b/docs/es/interfaces/third-party/integrations.md @@ -101,6 +101,7 @@ toc_title: "Integraci\xF3n" - Ruby - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From 4ac9dca348f906a491a33a06ea81cd0c9ace9016 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:41 +0300 Subject: [PATCH 0084/1102] Update docs/fa/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/fa/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fa/interfaces/third-party/integrations.md b/docs/fa/interfaces/third-party/integrations.md index cda8fa250d14..e7f174f5f2d0 100644 --- a/docs/fa/interfaces/third-party/integrations.md +++ b/docs/fa/interfaces/third-party/integrations.md @@ -98,6 +98,7 @@ toc_title: "\u06CC\u06A9\u067E\u0627\u0631\u0686\u06AF\u06CC" - Ruby - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From c928547f82b78308f15be47fa8e76964f3debe89 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:48 +0300 Subject: [PATCH 0085/1102] Update docs/fr/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/fr/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fr/interfaces/third-party/integrations.md b/docs/fr/interfaces/third-party/integrations.md index 42341b97beaa..544c0e8c3b16 100644 --- a/docs/fr/interfaces/third-party/integrations.md +++ b/docs/fr/interfaces/third-party/integrations.md @@ -98,6 +98,7 @@ toc_title: "Int\xE9gration" - Ruby - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From e9f7319d59130916a5dd2c484dd3eda2bb91eac7 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:11:54 +0300 Subject: [PATCH 0086/1102] Update docs/ja/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/ja/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ja/interfaces/third-party/integrations.md b/docs/ja/interfaces/third-party/integrations.md index f45a6734b2c9..09e68239b4b3 100644 --- a/docs/ja/interfaces/third-party/integrations.md +++ b/docs/ja/interfaces/third-party/integrations.md @@ -98,6 +98,7 @@ toc_title: "\u7D71\u5408" - Ruby - [Ruby on rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From dd8d091c19028fb169bd00f443671f03d4018ab6 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:12:02 +0300 Subject: [PATCH 0087/1102] Update docs/ru/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/ru/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ru/interfaces/third-party/integrations.md b/docs/ru/interfaces/third-party/integrations.md index fe26c85e62c2..47e33a88a206 100644 --- a/docs/ru/interfaces/third-party/integrations.md +++ b/docs/ru/interfaces/third-party/integrations.md @@ -94,6 +94,7 @@ - Ruby - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From c43e01e712f14d6bf4b881be0cd984361e6ad9e7 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Wed, 6 May 2020 19:12:09 +0300 Subject: [PATCH 0088/1102] Update docs/tr/interfaces/third-party/integrations.md Co-authored-by: Ilya Yatsishin <2159081+qoega@users.noreply.github.com> --- docs/tr/interfaces/third-party/integrations.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tr/interfaces/third-party/integrations.md b/docs/tr/interfaces/third-party/integrations.md index 19eca91b65e7..ac42757980c0 100644 --- a/docs/tr/interfaces/third-party/integrations.md +++ b/docs/tr/interfaces/third-party/integrations.md @@ -98,6 +98,7 @@ toc_title: Entegrasyonlar - Ruby - [Ruby on Rails](https://rubyonrails.org/) - [activecube](https://github.com/bitquery/activecube) + - [ActiveRecord](https://github.com/PNixx/clickhouse-activerecord) - [GraphQL](https://github.com/graphql) - [activecube-graphql](https://github.com/bitquery/activecube-graphql) From 270268f4a28ddd3dca4a2dea0f2962d471e93ec0 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 14:36:23 +0300 Subject: [PATCH 0089/1102] fix addr --- .../SSDComplexKeyCacheDictionary.cpp | 35 ++++++++++++++----- .../SSDComplexKeyCacheDictionary.h | 35 ++++++++++++++----- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 83b9f0f25a74..172cb91f0122 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -268,6 +268,10 @@ size_t SSDComplexKeyCachePartition::append( { init_write_buffer(); } + if (!keys_buffer_pool) + { + keys_buffer_pool.emplace(); + } bool flushed = false; auto finish_block = [&]() @@ -360,7 +364,7 @@ size_t SSDComplexKeyCachePartition::append( if (!flushed) { key_to_index.set(keys[index], cache_index); - keys_buffer.push_back(keys[index]); + keys_buffer.push_back(keys_buffer_pool->copyKeyFrom(keys[index])); ++index; ++keys_in_block; } @@ -442,6 +446,7 @@ void SSDComplexKeyCachePartition::flush() for (size_t row = 0; row < keys_buffer.size(); ++row) { Index index; + Poco::Logger::get("get:").information("sz = " + std::to_string(keys_buffer[row].size())); if (key_to_index.get(keys_buffer[row], index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. @@ -451,6 +456,7 @@ void SSDComplexKeyCachePartition::flush() } key_to_index.set(keys_buffer[row], index); } + Poco::Logger::get("get:").information("finish"); } current_file_block_id += write_buffer_size; @@ -458,6 +464,8 @@ void SSDComplexKeyCachePartition::flush() /// clear buffer keys_buffer.clear(); + keys_buffer_pool.reset(); + keys_buffer_pool.emplace(); } template @@ -754,7 +762,6 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() TemporalComplexKeysPool tmp_keys_pool; KeyRefs keys; - keys.reserve(write_buffer_size); // TODO: писать кол-во значений for (size_t i = 0; i < write_buffer_size; ++i) @@ -777,6 +784,8 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() { keys.emplace_back(); tmp_keys_pool.readKey(keys.back(), read_buffer); + Poco::Logger::get("ClearOldestBlocks").information("ktest: sz=" + std::to_string(keys.back().size()) + + " data=" + std::to_string(reinterpret_cast(keys.back().fullData()))); Metadata metadata; readBinary(metadata.data, read_buffer); @@ -827,13 +836,18 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); for (const auto& key : keys) { + Poco::Logger::get("ClearOldestBlocks").information("ktest: null=" + std::to_string(key.isNull())); + Poco::Logger::get("ClearOldestBlocks").information("ktest: data=" + std::to_string(reinterpret_cast(key.fullData()))); + Poco::Logger::get("ClearOldestBlocks").information("ktest: sz=" + std::to_string(key.size()) + " fz=" + std::to_string(key.fullSize())); Index index; if (key_to_index.get(key, index)) { + Poco::Logger::get("ClearOldestBlocks").information("erase"); size_t block_id = index.getBlockId(); if (start_block <= block_id && block_id < finish_block) key_to_index.erase(key); } + Poco::Logger::get("ClearOldestBlocks").information("finish"); } } @@ -1038,6 +1052,7 @@ void SSDComplexKeyCacheStorage::update( const DataTypes & key_types, const KeyRefs & required_keys, const std::vector & required_rows, + TemporalComplexKeysPool & tmp_keys_pool, PresentIdHandler && on_updated, AbsentIdHandler && on_key_not_found, const DictionaryLifetime lifetime) @@ -1056,9 +1071,9 @@ void SSDComplexKeyCacheStorage::update( if (inserted < metadata.size()) { partitions.emplace_front(std::make_unique( - AttributeUnderlyingType::utUInt64, attributes_structure, path, - (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + AttributeUnderlyingType::utUInt64, attributes_structure, path, + (partitions.empty() ? 0 : partitions.front()->getId() + 1), + partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1077,7 +1092,6 @@ void SSDComplexKeyCacheStorage::update( { const auto keys_size = key_columns.size(); StringRefs keys(keys_size); - TemporalComplexKeysPool tmp_keys_pool; const ProfilingScopedWriteRWLock write_lock{rw_lock, ProfileEvents::DictCacheLockWriteNs}; @@ -1111,7 +1125,7 @@ void SSDComplexKeyCacheStorage::update( for (const auto i : ext::range(0, rows_num)) { auto key = tmp_keys_pool.allocKey(i, new_key_columns, keys); - SCOPE_EXIT(tmp_keys_pool.rollback(key)); + //SCOPE_EXIT(tmp_keys_pool.rollback(key)); std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; metadata[i].setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); @@ -1475,12 +1489,14 @@ void SSDComplexKeyCacheDictionary::getItemsNumberImpl( for (const auto & key_ref : required_keys) required_rows.push_back(not_found_keys[key_ref].front()); + TemporalComplexKeysPool tmp_keys_pool; storage.update( source_ptr, key_columns, key_types, required_keys, required_rows, + tmp_keys_pool, [&](const auto key, const auto row, const auto & new_attributes) { for (const size_t out_row : not_found_keys[key]) @@ -1580,12 +1596,14 @@ void SSDComplexKeyCacheDictionary::getItemsStringImpl( for (const auto & key_ref : required_keys) required_rows.push_back(not_found_keys[key_ref].front()); + TemporalComplexKeysPool tmp_keys_pool; storage.update( source_ptr, key_columns, key_types, required_keys, required_rows, + tmp_keys_pool, [&](const auto key, const auto row, const auto & new_attributes) { update_result[key] = std::get>(new_attributes[attribute_index].values)[row]; @@ -1593,7 +1611,6 @@ void SSDComplexKeyCacheDictionary::getItemsStringImpl( [&](const auto) {}, getLifetime()); - TemporalComplexKeysPool tmp_keys_pool; StringRefs tmp_refs(key_columns.size()); size_t default_index = 0; for (size_t row = 0; row < n; ++row) @@ -1643,12 +1660,14 @@ void SSDComplexKeyCacheDictionary::has( for (const auto & key_ref : required_keys) required_rows.push_back(not_found_keys[key_ref].front()); + TemporalComplexKeysPool tmp_keys_pool; storage.update( source_ptr, key_columns, key_types, required_keys, required_rows, + tmp_keys_pool, [&](const auto key, const auto, const auto &) { for (const size_t out_row : not_found_keys[key]) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 6e64078cffdf..2bb71ed52b13 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -36,13 +35,20 @@ class KeyRef KeyRef() : ptr(nullptr) {} inline UInt16 size() const { - return *reinterpret_cast(ptr); + UInt16 sz; + memcpy(&sz, ptr, sizeof(sz)); + return sz; + //return *reinterpret_cast(ptr); } inline size_t fullSize() const { return static_cast(size()) + sizeof(UInt16); } + inline bool isNull() const { + return ptr == nullptr; + } + inline char * data() const { return ptr + sizeof(UInt16); } @@ -114,6 +120,7 @@ class ComplexKeysPoolImpl public: KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) { + std::lock_guard lock(m); if constexpr (std::is_same_v) { // not working now @@ -151,7 +158,7 @@ class ComplexKeysPoolImpl { if (!key_columns[j]->valuesHaveFixedSize()) // String { - auto start = key_start; + //auto start = key_start; auto key_size = keys[j].size + 1; memcpy(key_start, &key_size, sizeof(size_t)); key_start += sizeof(size_t); @@ -159,13 +166,13 @@ class ComplexKeysPoolImpl key_start += keys[j].size; *key_start = '\0'; ++key_start; - keys[j].data = start; - keys[j].size += sizeof(size_t) + 1; + //keys[j].data = start; + //keys[j].size += sizeof(size_t) + 1; } else { memcpy(key_start, keys[j].data, keys[j].size); - keys[j].data = key_start; + //keys[j].data = key_start; key_start += keys[j].size; } } @@ -176,13 +183,17 @@ class ComplexKeysPoolImpl KeyRef copyKeyFrom(const KeyRef & key) { + std::lock_guard lock(m); + //Poco::Logger::get("test cpy").information("--- --- --- "); char * data = arena.alloc(key.fullSize()); + //Poco::Logger::get("test cpy").information("--- --- --- finish"); memcpy(data, key.fullData(), key.fullSize()); return KeyRef(data); } void freeKey(const KeyRef & key) { + std::lock_guard lock(m); if constexpr (std::is_same_v) arena.free(key.fullData(), key.fullSize()); else if constexpr (std::is_same_v) @@ -193,6 +204,7 @@ class ComplexKeysPoolImpl void rollback(const KeyRef & key) { + std::lock_guard lock(m); if constexpr (std::is_same_v) arena.rollback(key.fullSize()); else @@ -206,8 +218,10 @@ class ComplexKeysPoolImpl void readKey(KeyRef & key, ReadBuffer & buf) { + std::lock_guard lock(m); UInt16 sz; readBinary(sz, buf); + Poco::Logger::get("test read key").information("sz " + std::to_string(sz)); char * data = nullptr; if constexpr (std::is_same_v) data = arena.alloc(); @@ -216,6 +230,7 @@ class ComplexKeysPoolImpl memcpy(data, &sz, sizeof(sz)); buf.read(data + sizeof(sz), sz); key = KeyRef(data); + Poco::Logger::get("test read key").information("ksz = " + std::to_string(key.size())); } void ignoreKey(ReadBuffer & buf) const @@ -226,6 +241,7 @@ class ComplexKeysPoolImpl } private: + std::mutex m; A arena; }; @@ -270,7 +286,7 @@ class ComplexKeyLRUCache else { queue.erase(it->second.iter); - it->second.iter = queue.insert(std::end(queue), key); + it->second.iter = queue.insert(std::end(queue), it->first); it->second.val = val; } } @@ -294,7 +310,7 @@ class ComplexKeyLRUCache if (it == std::end(cache)) return false; - keys_pool.freeKey(key); + keys_pool.freeKey(it->first); queue.erase(it->second.iter); cache.erase(it); return true; @@ -492,6 +508,8 @@ class SSDComplexKeyCachePartition ComplexKeysPool keys_pool; mutable ComplexKeyLRUCache key_to_index; + + std::optional keys_buffer_pool; KeyRefs keys_buffer; const std::vector attributes_structure; @@ -547,6 +565,7 @@ class SSDComplexKeyCacheStorage void update(DictionarySourcePtr & source_ptr, const Columns & key_columns, const DataTypes & key_types, const KeyRefs & required_keys, const std::vector & required_rows, + TemporalComplexKeysPool & tmp_keys_pool, PresentIdHandler && on_updated, AbsentIdHandler && on_key_not_found, const DictionaryLifetime lifetime); From 74d5ed65c16963c1f5d17758b57b951971d2b3c4 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 14:48:33 +0300 Subject: [PATCH 0090/1102] fix tests --- ...01280_ssd_complex_key_dictionary.reference | 39 +++++++++++++++++++ .../01280_ssd_complex_key_dictionary.sql | 18 ++++----- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference index e69de29bb2d1..fa42fa7239ea 100644 --- a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference +++ b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.reference @@ -0,0 +1,39 @@ +TEST_SMALL +VALUE FROM RAM BUFFER +100 +-100 +clickhouse +100 +-100 +clickhouse +3 +4 +database +6 +7 +columns +9 +8 + +UPDATE DICTIONARY +118 +VALUE FROM DISK +-100 +clickhouse +VALUE FROM RAM BUFFER +8 + +VALUES FROM DISK AND RAM BUFFER +118 +HAS +6 +VALUES NOT FROM TABLE +0 -1 none +0 -1 none +DUPLICATE KEYS +('1',3) -100 +('2',-1) 4 +('',0) -1 +('',0) -1 +('2',-1) 4 +('1',3) -100 diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql index 2b9288d9257c..0b7d73684aad 100644 --- a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql +++ b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql @@ -100,34 +100,34 @@ SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', (k1, k2))) FROM data SELECT 'VALUE FROM DISK'; -- -100 -SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('1', 3)); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('1', toInt32(3))); -- 'clickhouse' -SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('1', 3)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('1', toInt32(3))); SELECT 'VALUE FROM RAM BUFFER'; -- 8 -SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('10', -20)); +SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', ('10', toInt32(-20))); -- '' -SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('10', -20)); +SELECT dictGetString('database_for_dict.ssd_dict', 'c', ('10', toInt32(-20))); SELECT 'VALUES FROM DISK AND RAM BUFFER'; -- 118 SELECT sum(dictGetUInt64('database_for_dict.ssd_dict', 'a', (k1, k2))) FROM database_for_dict.keys_table; SELECT 'HAS'; --- 1006 +-- 6 SELECT count() FROM database_for_dict.keys_table WHERE dictHas('database_for_dict.ssd_dict', (k1, k2)); SELECT 'VALUES NOT FROM TABLE'; -- 0 -1 none -SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', 0)), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', 0)), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', 0)); -SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', 0)), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', 0)), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', 0)); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', toInt32(0))), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', toInt32(0))), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', toInt32(0))); +SELECT dictGetUInt64('database_for_dict.ssd_dict', 'a', ('unknown', toInt32(0))), dictGetInt32('database_for_dict.ssd_dict', 'b', ('unknown', toInt32(0))), dictGetString('database_for_dict.ssd_dict', 'c', ('unknown', toInt32(0))); SELECT 'DUPLICATE KEYS'; -SELECT arrayJoin([('1', 3), ('2', -1), ('', 0), ('', 0), ('2', -1), ('1', 3)]) AS keys, dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(keys)); ---SELECT +SELECT arrayJoin([('1', toInt32(3)), ('2', toInt32(-1)), ('', toInt32(0)), ('', toInt32(0)), ('2', toInt32(-1)), ('1', toInt32(3))]) AS keys, dictGetInt32('database_for_dict.ssd_dict', 'b', keys); + DROP DICTIONARY IF EXISTS database_for_dict.ssd_dict; DROP TABLE IF EXISTS database_for_dict.keys_table; From 774b107b6ccf360ba7501e58bc8a5c8dda15ac67 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 14:53:07 +0300 Subject: [PATCH 0091/1102] rm lock --- src/Dictionaries/SSDComplexKeyCacheDictionary.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 2bb71ed52b13..63c6a39361f4 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -35,10 +35,7 @@ class KeyRef KeyRef() : ptr(nullptr) {} inline UInt16 size() const { - UInt16 sz; - memcpy(&sz, ptr, sizeof(sz)); - return sz; - //return *reinterpret_cast(ptr); + return *reinterpret_cast(ptr); } inline size_t fullSize() const { @@ -120,7 +117,6 @@ class ComplexKeysPoolImpl public: KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) { - std::lock_guard lock(m); if constexpr (std::is_same_v) { // not working now @@ -183,7 +179,6 @@ class ComplexKeysPoolImpl KeyRef copyKeyFrom(const KeyRef & key) { - std::lock_guard lock(m); //Poco::Logger::get("test cpy").information("--- --- --- "); char * data = arena.alloc(key.fullSize()); //Poco::Logger::get("test cpy").information("--- --- --- finish"); @@ -193,7 +188,6 @@ class ComplexKeysPoolImpl void freeKey(const KeyRef & key) { - std::lock_guard lock(m); if constexpr (std::is_same_v) arena.free(key.fullData(), key.fullSize()); else if constexpr (std::is_same_v) @@ -204,7 +198,6 @@ class ComplexKeysPoolImpl void rollback(const KeyRef & key) { - std::lock_guard lock(m); if constexpr (std::is_same_v) arena.rollback(key.fullSize()); else @@ -218,7 +211,6 @@ class ComplexKeysPoolImpl void readKey(KeyRef & key, ReadBuffer & buf) { - std::lock_guard lock(m); UInt16 sz; readBinary(sz, buf); Poco::Logger::get("test read key").information("sz " + std::to_string(sz)); @@ -241,7 +233,6 @@ class ComplexKeysPoolImpl } private: - std::mutex m; A arena; }; From 0896c2a8b464c32ff68dab6df5cefb6872e5221a Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 16:40:41 +0300 Subject: [PATCH 0092/1102] ret found --- src/Dictionaries/SSDCacheDictionary.cpp | 8 +++++--- .../SSDComplexKeyCacheDictionary.cpp | 16 ++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 108ed19c8627..0eee3ca702f4 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -81,6 +81,8 @@ namespace static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; + //constexpr size_t KEY_RECENTLY_USED_BIT = 63; + //constexpr size_t KEY_RECENTLY_USED = (1ULL << KEY_RECENTLY_USED_BIT); constexpr size_t KEY_IN_MEMORY_BIT = 63; constexpr size_t KEY_IN_MEMORY = (1ULL << KEY_IN_MEMORY_BIT); constexpr size_t BLOCK_INDEX_BITS = 32; @@ -243,7 +245,6 @@ size_t SSDCachePartition::appendBlock( if (!write_buffer) { init_write_buffer(); - //codec = CompressionCodecFactory::instance().get("NONE", std::nullopt); } bool flushed = false; @@ -376,8 +377,6 @@ void SSDCachePartition::flush() write_request.aio_offset = block_size * current_file_block_id; #endif - Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); - while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) { if (errno != EINTR) @@ -520,7 +519,10 @@ void SSDCachePartition::getImpl(const PaddedPODArray & ids, SetFunc & se if (found[i]) indices[i].setNotExists(); else if (key_to_index.get(ids[i], index)) + { indices[i] = index; + found[i] = true; + } else indices[i].setNotExists(); } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 172cb91f0122..51b43ee3bd98 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1228,18 +1228,7 @@ void SSDComplexKeyCacheStorage::update( PaddedPODArray SSDComplexKeyCacheStorage::getCachedIds() const { - /*PaddedPODArray array; - - const auto now = std::chrono::system_clock::now(); - - std::shared_lock lock(rw_lock); - for (auto & partition : partitions) - { - const auto cached_in_partition = partition->getCachedIds(now); - array.insert(std::begin(cached_in_partition), std::end(cached_in_partition)); - }*/ - - return {}; + throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); } double SSDComplexKeyCacheStorage::getLoadFactor() const @@ -1684,8 +1673,7 @@ void SSDComplexKeyCacheDictionary::has( BlockInputStreamPtr SSDComplexKeyCacheDictionary::getBlockInputStream( const Names & /* column_names */, size_t /* max_block_size*/) const { - //using BlockInputStreamType = DictionaryBlockInputStream; - return nullptr; //std::make_shared(shared_from_this(), max_block_size, storage.getCachedIds(), column_names); + throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); } size_t SSDComplexKeyCacheDictionary::getAttributeIndex(const std::string & attr_name) const From c894976ff2f4c1c658c61e480ca1c30a6b931284 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 21:17:32 +0300 Subject: [PATCH 0093/1102] fix:w --- src/Dictionaries/SSDCacheDictionary.cpp | 53 +++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 0eee3ca702f4..19b6b98ddfc8 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -264,6 +264,11 @@ size_t SSDCachePartition::appendBlock( Index cache_index; cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); + Poco::Logger::get("wr").information(" block mem: " + std::to_string(current_memory_block_id) + " wb: " + std::to_string(write_buffer_size)); + if (current_memory_block_id >= write_buffer_size) { + throw DB::Exception("lel " + std::to_string(current_memory_block_id) + " " + + std::to_string(write_buffer_size) + " " + std::to_string(index), ErrorCodes::LOGICAL_ERROR); + } cache_index.setAddressInBlock(write_buffer->offset()); flushed = false; @@ -334,6 +339,7 @@ size_t SSDCachePartition::appendBlock( if (!flushed) { + Poco::Logger::get("wr").information(" set: " + std::to_string(cache_index.getBlockId()) + " " + std::to_string(cache_index.getAddressInBlock())); key_to_index.set(ids[index], cache_index); ids_buffer.push_back(ids[index]); ++index; @@ -344,6 +350,7 @@ size_t SSDCachePartition::appendBlock( init_write_buffer(); } } + Poco::Logger::get("wr").information("exit"); return ids.size() - begin; } @@ -367,14 +374,14 @@ void SSDCachePartition::flush() write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; write_request.aio.aio_buf = reinterpret_cast(memory->data()); - write_request.aio.aio_nbytes = block_size; - write_request.aio.aio_offset = block_size * current_file_block_id; + write_request.aio.aio_nbytes = block_size * write_buffer_size; + write_request.aio.aio_offset = (current_file_block_id % max_size) * block_size; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory->data()); write_request.aio_nbytes = block_size * write_buffer_size; - write_request.aio_offset = block_size * current_file_block_id; + write_request.aio_offset = (current_file_block_id % max_size) * block_size; #endif while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) @@ -420,7 +427,11 @@ void SSDCachePartition::flush() if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); - index.setBlockId(current_file_block_id + index.getBlockId()); + Poco::Logger::get("pt").information("block: " + std::to_string(index.getBlockId()) + " " + std::to_string(current_file_block_id) + " "); + index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); + if (index.getBlockId() >= max_size) { + throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); + } } key_to_index.set(ids[row], index); } @@ -442,8 +453,7 @@ void SSDCachePartition::getValue(const size_t attribute_index, const PaddedPODAr { buf.ignore(sizeof(Key)); // key Metadata metadata; - readVarUInt(metadata.data, buf); - + readBinary(metadata.data, buf); if (metadata.expiresAt() > now) { if (metadata.isDefault()) @@ -468,7 +478,7 @@ void SSDCachePartition::getString(const size_t attribute_index, const PaddedPODA { buf.ignore(sizeof(Key)); // key Metadata metadata; - readVarUInt(metadata.data, buf); + readBinary(metadata.data, buf); if (metadata.expiresAt() > now) { @@ -498,7 +508,7 @@ void SSDCachePartition::has(const PaddedPODArray & ids, ResultArrayType< { buf.ignore(sizeof(Key)); // key Metadata metadata; - readVarUInt(metadata.data, buf); + readBinary(metadata.data, buf); if (metadata.expiresAt() > now) out[index] = !metadata.isDefault(); @@ -521,7 +531,6 @@ void SSDCachePartition::getImpl(const PaddedPODArray & ids, SetFunc & se else if (key_to_index.get(ids[i], index)) { indices[i] = index; - found[i] = true; } else indices[i].setNotExists(); @@ -596,6 +605,10 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice request.aio_fildes = fd; request.aio_buf = reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size); request.aio_nbytes = block_size; + Poco::Logger::get("RR").information("block found" + std::to_string(index_to_out[i].first.getBlockId()) + " max_size" + std::to_string(max_size)); + if (index_to_out[i].first.getBlockId() > max_size) { + throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); + } request.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #endif @@ -616,9 +629,12 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice size_t to_pop = 0; while (to_pop < requests.size()) { + Poco::Logger::get("RR").information( + "push = " + std::to_string(to_push) + " pop=" + std::to_string(to_pop) + + "bi = " + std::to_string(blocks_to_indices.size()) + " req = " + std::to_string(requests.size())); /// get io tasks from previous iteration int popped = 0; - while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) < 0) + while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) <= 0) { if (errno != EINTR) throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); @@ -629,10 +645,12 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice const auto request_id = events[i].data; const auto & request = requests[request_id]; if (events[i].res != static_cast(request.aio_nbytes)) + { throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + - "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + - "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_READ_ERROR); - + "request_id= " + std::to_string(request.aio_data) + "/ " + std::to_string(requests.size()) + + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + + ", returned=" + std::to_string(events[i].res) + ", errno=" + std::to_string(errno), ErrorCodes::AIO_READ_ERROR); + } uint64_t checksum = 0; ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); readBinary(checksum, buf_special); @@ -661,12 +679,13 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice const int new_tasks_count = std::min(read_buffer_size - (to_push - to_pop), requests.size() - to_push); int pushed = 0; - while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) + while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) <= 0) { if (errno != EINTR) throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); } to_push += pushed; + Poco::Logger::get("RR").information("fin iter"); } } @@ -693,6 +712,8 @@ void SSDCachePartition::clearOldestBlocks() request.aio_data = 0; #endif + Poco::Logger::get("GC").information("GC offset=" + std::to_string(request.aio_offset)); + { iocb* request_ptr = &request; io_event event{}; @@ -787,8 +808,8 @@ void SSDCachePartition::clearOldestBlocks() } const size_t start_block = current_file_block_id % max_size; - const size_t finish_block = start_block + block_size * write_buffer_size; - Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); + const size_t finish_block = start_block + write_buffer_size; + Poco::Logger::get("ClearOldestBlocks").information("> erasing keys < start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); for (const auto& key : keys) { Index index; From 873dd7319ce8d8f788026851ef51bfaad82d1588 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 9 May 2020 23:33:58 +0300 Subject: [PATCH 0094/1102] fix complex key --- src/Dictionaries/SSDCacheDictionary.cpp | 38 +++++++++---------- .../SSDComplexKeyCacheDictionary.cpp | 14 +++---- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 19b6b98ddfc8..f09cd4b76976 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -111,6 +111,7 @@ SSDCachePartition::Metadata::time_point_t SSDCachePartition::Metadata::expiresAt { return ext::safe_bit_cast(data & KEY_METADATA_EXPIRES_AT_MASK); } + void SSDCachePartition::Metadata::setExpiresAt(const time_point_t & t) { data = ext::safe_bit_cast(t); @@ -264,7 +265,7 @@ size_t SSDCachePartition::appendBlock( Index cache_index; cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); - Poco::Logger::get("wr").information(" block mem: " + std::to_string(current_memory_block_id) + " wb: " + std::to_string(write_buffer_size)); + // Poco::Logger::get("wr").information(" block mem: " + std::to_string(current_memory_block_id) + " wb: " + std::to_string(write_buffer_size)); if (current_memory_block_id >= write_buffer_size) { throw DB::Exception("lel " + std::to_string(current_memory_block_id) + " " + std::to_string(write_buffer_size) + " " + std::to_string(index), ErrorCodes::LOGICAL_ERROR); @@ -339,7 +340,7 @@ size_t SSDCachePartition::appendBlock( if (!flushed) { - Poco::Logger::get("wr").information(" set: " + std::to_string(cache_index.getBlockId()) + " " + std::to_string(cache_index.getAddressInBlock())); + // Poco::Logger::get("wr").information(" set: " + std::to_string(cache_index.getBlockId()) + " " + std::to_string(cache_index.getAddressInBlock())); key_to_index.set(ids[index], cache_index); ids_buffer.push_back(ids[index]); ++index; @@ -350,7 +351,7 @@ size_t SSDCachePartition::appendBlock( init_write_buffer(); } } - Poco::Logger::get("wr").information("exit"); + // Poco::Logger::get("wr").information("exit"); return ids.size() - begin; } @@ -363,7 +364,7 @@ void SSDCachePartition::flush() if (ids.empty()) return; - Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); + // Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); AIOContext aio_context{1}; @@ -427,11 +428,8 @@ void SSDCachePartition::flush() if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); - Poco::Logger::get("pt").information("block: " + std::to_string(index.getBlockId()) + " " + std::to_string(current_file_block_id) + " "); + // Poco::Logger::get("pt").information("block: " + std::to_string(index.getBlockId()) + " " + std::to_string(current_file_block_id) + " "); index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); - if (index.getBlockId() >= max_size) { - throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); - } } key_to_index.set(ids[row], index); } @@ -605,10 +603,10 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice request.aio_fildes = fd; request.aio_buf = reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size); request.aio_nbytes = block_size; - Poco::Logger::get("RR").information("block found" + std::to_string(index_to_out[i].first.getBlockId()) + " max_size" + std::to_string(max_size)); - if (index_to_out[i].first.getBlockId() > max_size) { - throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); - } + // Poco::Logger::get("RR").information("block found" + std::to_string(index_to_out[i].first.getBlockId()) + " max_size" + std::to_string(max_size)); + // if (index_to_out[i].first.getBlockId() > max_size) { + // throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); + // } request.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #endif @@ -629,9 +627,9 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice size_t to_pop = 0; while (to_pop < requests.size()) { - Poco::Logger::get("RR").information( - "push = " + std::to_string(to_push) + " pop=" + std::to_string(to_pop) + - "bi = " + std::to_string(blocks_to_indices.size()) + " req = " + std::to_string(requests.size())); + // Poco::Logger::get("RR").information( + // "push = " + std::to_string(to_push) + " pop=" + std::to_string(to_pop) + + // "bi = " + std::to_string(blocks_to_indices.size()) + " req = " + std::to_string(requests.size())); /// get io tasks from previous iteration int popped = 0; while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) <= 0) @@ -685,13 +683,13 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); } to_push += pushed; - Poco::Logger::get("RR").information("fin iter"); + // Poco::Logger::get("RR").information("fin iter"); } } void SSDCachePartition::clearOldestBlocks() { - Poco::Logger::get("GC").information("GC clear -----------------"); + // Poco::Logger::get("GC").information("GC clear -----------------"); // write_buffer_size, because we need to erase the whole buffer. Memory read_buffer_memory(block_size * write_buffer_size, BUFFER_ALIGNMENT); @@ -712,7 +710,7 @@ void SSDCachePartition::clearOldestBlocks() request.aio_data = 0; #endif - Poco::Logger::get("GC").information("GC offset=" + std::to_string(request.aio_offset)); + // Poco::Logger::get("GC").information("GC offset=" + std::to_string(request.aio_offset)); { iocb* request_ptr = &request; @@ -757,7 +755,7 @@ void SSDCachePartition::clearOldestBlocks() uint32_t keys_in_current_block = 0; readBinary(keys_in_current_block, read_buffer); - Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); + // Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); for (uint32_t j = 0; j < keys_in_current_block; ++j) { @@ -809,7 +807,7 @@ void SSDCachePartition::clearOldestBlocks() const size_t start_block = current_file_block_id % max_size; const size_t finish_block = start_block + write_buffer_size; - Poco::Logger::get("ClearOldestBlocks").information("> erasing keys < start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); + // Poco::Logger::get("ClearOldestBlocks").information("> erasing keys < start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); for (const auto& key : keys) { Index index; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 51b43ee3bd98..f75261b4cb50 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -403,7 +403,7 @@ void SSDComplexKeyCachePartition::flush() write_request.aio_fildes = fd; write_request.aio_buf = reinterpret_cast(memory->data()); write_request.aio_nbytes = block_size * write_buffer_size; - write_request.aio_offset = block_size * current_file_block_id; + write_request.aio_offset = (current_file_block_id % max_size) * block_size; #endif Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); @@ -452,7 +452,7 @@ void SSDComplexKeyCachePartition::flush() if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); - index.setBlockId(current_file_block_id + index.getBlockId()); + index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); } key_to_index.set(keys_buffer[row], index); } @@ -506,7 +506,7 @@ void SSDComplexKeyCachePartition::getString(const size_t attribute_index, { keys_pool.ignoreKey(buf); Metadata metadata; - readVarUInt(metadata.data, buf); + readBinary(metadata.data, buf); if (metadata.expiresAt() > now) { @@ -537,7 +537,7 @@ void SSDComplexKeyCachePartition::has( { keys_pool.ignoreKey(buf); Metadata metadata; - readVarUInt(metadata.data, buf); + readBinary(metadata.data, buf); if (metadata.expiresAt() > now) out[index] = !metadata.isDefault(); @@ -660,7 +660,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) < 0) + while (new_tasks_count > 0 && (pushed = io_submit(aio_context.ctx, new_tasks_count, &pointers[to_push])) <= 0) { if (errno != EINTR) throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); @@ -832,7 +832,7 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() } const size_t start_block = current_file_block_id % max_size; - const size_t finish_block = start_block + block_size * write_buffer_size; + const size_t finish_block = start_block + write_buffer_size; Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); for (const auto& key : keys) { From 99b0abcb928cefd9eb66023c373386c7a4ff6326 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 10:00:57 +0300 Subject: [PATCH 0095/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 2 +- src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 2 +- src/Dictionaries/SSDComplexKeyCacheDictionary.h | 4 +++- src/Functions/FunctionsExternalDictionaries.h | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index f09cd4b76976..de90c0de77d0 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1670,7 +1670,7 @@ void registerDictionarySSDCache(DictionaryFactory & factory) read_buffer_size / block_size, write_buffer_size / block_size, max_stored_keys); }; - factory.registerLayout("ssd", create_layout, false); + factory.registerLayout("ssd_cache", create_layout, false); } } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index f75261b4cb50..eb645fca222f 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1806,7 +1806,7 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) read_buffer_size / block_size, write_buffer_size / block_size, max_stored_keys); }; - factory.registerLayout("ssd_complex_key", create_layout, true); + factory.registerLayout("complex_key_ssd_cache", create_layout, true); } } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 63c6a39361f4..dbb15896318b 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -35,7 +35,9 @@ class KeyRef KeyRef() : ptr(nullptr) {} inline UInt16 size() const { - return *reinterpret_cast(ptr); + UInt16 res; + memcpy(&res, ptr, sizeof(res)); + return res; } inline size_t fullSize() const { diff --git a/src/Functions/FunctionsExternalDictionaries.h b/src/Functions/FunctionsExternalDictionaries.h index b359c7403f80..ee8453e296b4 100644 --- a/src/Functions/FunctionsExternalDictionaries.h +++ b/src/Functions/FunctionsExternalDictionaries.h @@ -137,6 +137,7 @@ class FunctionDictHas final : public IFunction !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchSimple(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && + !executeDispatchComplex(block, arguments, result, dict_ptr) && !executeDispatchComplex(block, arguments, result, dict_ptr) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict_ptr) && From 3d41582310b0697071d7956d2f0e2046e21db2cd Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 10:01:10 +0300 Subject: [PATCH 0096/1102] fix --- tests/queries/0_stateless/01053_ssd_dictionary.sql | 6 +++--- .../0_stateless/01280_ssd_complex_key_dictionary.sql | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/queries/0_stateless/01053_ssd_dictionary.sql b/tests/queries/0_stateless/01053_ssd_dictionary.sql index 18a79223f8c9..97773528dcb7 100644 --- a/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -33,7 +33,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); +LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); @@ -74,7 +74,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); +LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -140,7 +140,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024 MAX_STORED_KEYS 10)); +LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024 MAX_STORED_KEYS 10)); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql index 0b7d73684aad..411a0a21ea34 100644 --- a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql +++ b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql @@ -35,7 +35,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY k1, k2 SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD_COMPLEX_KEY(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); +LAYOUT(COMPLEX_KEY_SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT 'VALUE FROM RAM BUFFER'; @@ -92,7 +92,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY k1, k2 SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD_COMPLEX_KEY(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); +LAYOUT(COMPLEX_KEY_SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); SELECT 'UPDATE DICTIONARY'; -- 118 From 5056328a92e75c9b233b31ca3ce32c96cfd2ef68 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 10:35:33 +0300 Subject: [PATCH 0097/1102] fix names --- src/Dictionaries/SSDCacheDictionary.cpp | 28 +++++++++---------- .../SSDComplexKeyCacheDictionary.cpp | 28 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index de90c0de77d0..b0431fd8b577 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1626,42 +1626,42 @@ void registerDictionarySSDCache(DictionaryFactory & factory) ErrorCodes::BAD_ARGUMENTS}; const auto & layout_prefix = config_prefix + ".layout"; - const auto max_partitions_count = config.getInt(layout_prefix + ".ssd.max_partitions_count", DEFAULT_PARTITIONS_COUNT); + const auto max_partitions_count = config.getInt(layout_prefix + ".ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; - const auto block_size = config.getInt(layout_prefix + ".ssd.block_size", DEFAULT_SSD_BLOCK_SIZE); + const auto block_size = config.getInt(layout_prefix + ".ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto partition_size = config.getInt64(layout_prefix + ".ssd.partition_size", DEFAULT_FILE_SIZE); + const auto partition_size = config.getInt64(layout_prefix + ".ssd_cache.partition_size", DEFAULT_FILE_SIZE); if (partition_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; if (partition_size % block_size != 0) throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); + const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd_cache.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); if (read_buffer_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; if (read_buffer_size % block_size != 0) throw Exception{name + ": read_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto write_buffer_size = config.getInt64(layout_prefix + ".ssd.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); + const auto write_buffer_size = config.getInt64(layout_prefix + ".ssd_cache.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); if (write_buffer_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; if (write_buffer_size % block_size != 0) throw Exception{name + ": write_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - auto path = config.getString(layout_prefix + ".ssd.path"); + auto path = config.getString(layout_prefix + ".ssd_cache.path"); if (path.empty()) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have empty path", ErrorCodes::BAD_ARGUMENTS}; if (path.at(0) != '/') path = std::filesystem::path{config.getString("path")}.concat(path).string(); - const auto max_stored_keys = config.getInt64(layout_prefix + ".ssd.max_stored_keys", DEFAULT_MAX_STORED_KEYS); + const auto max_stored_keys = config.getInt64(layout_prefix + ".ssd_cache.max_stored_keys", DEFAULT_MAX_STORED_KEYS); if (max_stored_keys <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index eb645fca222f..20c81aaf0651 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1762,42 +1762,42 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) ErrorCodes::BAD_ARGUMENTS}; const auto & layout_prefix = config_prefix + ".layout"; - const auto max_partitions_count = config.getInt(layout_prefix + ".ssd_complex_key.max_partitions_count", DEFAULT_PARTITIONS_COUNT); + const auto max_partitions_count = config.getInt(layout_prefix + ".complex_key_ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; - const auto block_size = config.getInt(layout_prefix + ".ssd_complex_key.block_size", DEFAULT_SSD_BLOCK_SIZE); + const auto block_size = config.getInt(layout_prefix + ".complex_key_ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto partition_size = config.getInt64(layout_prefix + ".ssd_complex_key.partition_size", DEFAULT_FILE_SIZE); + const auto partition_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.partition_size", DEFAULT_FILE_SIZE); if (partition_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; if (partition_size % block_size != 0) throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd_complex_key.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); + const auto read_buffer_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); if (read_buffer_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) read_buffer_size", ErrorCodes::BAD_ARGUMENTS}; if (read_buffer_size % block_size != 0) throw Exception{name + ": read_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto write_buffer_size = config.getInt64(layout_prefix + ".ssd_complex_key.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); + const auto write_buffer_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.write_buffer_size", DEFAULT_WRITE_BUFFER_SIZE); if (write_buffer_size <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) write_buffer_size", ErrorCodes::BAD_ARGUMENTS}; if (write_buffer_size % block_size != 0) throw Exception{name + ": write_buffer_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; - auto path = config.getString(layout_prefix + ".ssd_complex_key.path"); + auto path = config.getString(layout_prefix + ".complex_key_ssd_cache.path"); if (path.empty()) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have empty path", + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have empty path", ErrorCodes::BAD_ARGUMENTS}; if (path.at(0) != '/') path = std::filesystem::path{config.getString("path")}.concat(path).string(); - const auto max_stored_keys = config.getInt64(layout_prefix + ".ssd_complex_key.max_stored_keys", DEFAULT_MAX_STORED_KEYS); + const auto max_stored_keys = config.getInt64(layout_prefix + ".complex_key_ssd_cache.max_stored_keys", DEFAULT_MAX_STORED_KEYS); if (max_stored_keys <= 0) - throw Exception{name + ": dictionary of layout 'ssdcache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) max_stored_keys", ErrorCodes::BAD_ARGUMENTS}; const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( From bdeea1ac516d7cd0aba9e3c975d6e715f3af549d Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 11:22:14 +0300 Subject: [PATCH 0098/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 21 +++++------ .../SSDComplexKeyCacheDictionary.cpp | 16 ++++----- .../SSDComplexKeyCacheDictionary.h | 36 +++++++++++-------- .../getDictionaryConfigurationFromAST.cpp | 1 + 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index b0431fd8b577..307d2f3196b8 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -52,16 +52,17 @@ namespace ErrorCodes extern const int AIO_READ_ERROR; extern const int AIO_WRITE_ERROR; extern const int BAD_ARGUMENTS; + extern const int CANNOT_ALLOCATE_MEMORY; extern const int CANNOT_FSYNC; extern const int CANNOT_IO_GETEVENTS; extern const int CANNOT_IO_SUBMIT; extern const int CANNOT_OPEN_FILE; + extern const int CORRUPTED_DATA; extern const int FILE_DOESNT_EXIST; extern const int LOGICAL_ERROR; - extern const int TOO_SMALL_BUFFER_SIZE; + extern const int NOT_IMPLEMENTED; extern const int TYPE_MISMATCH; extern const int UNSUPPORTED_METHOD; - extern const int CORRUPTED_DATA; } namespace @@ -78,8 +79,8 @@ namespace constexpr size_t BLOCK_CHECKSUM_SIZE = 8; constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; - static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); - static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; + constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); + constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; //constexpr size_t KEY_RECENTLY_USED_BIT = 63; //constexpr size_t KEY_RECENTLY_USED = (1ULL << KEY_RECENTLY_USED_BIT); @@ -266,10 +267,10 @@ size_t SSDCachePartition::appendBlock( cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); // Poco::Logger::get("wr").information(" block mem: " + std::to_string(current_memory_block_id) + " wb: " + std::to_string(write_buffer_size)); - if (current_memory_block_id >= write_buffer_size) { + if (current_memory_block_id >= write_buffer_size) throw DB::Exception("lel " + std::to_string(current_memory_block_id) + " " + std::to_string(write_buffer_size) + " " + std::to_string(index), ErrorCodes::LOGICAL_ERROR); - } + cache_index.setAddressInBlock(write_buffer->offset()); flushed = false; @@ -927,7 +928,7 @@ void SSDCacheStorage::getValue(const size_t attribute_index, const PaddedPODArra { std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) partition->getValue(attribute_index, ids, out, found, get_default, now); } @@ -947,7 +948,7 @@ void SSDCacheStorage::getString(const size_t attribute_index, const PaddedPODArr { std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) partition->getString(attribute_index, ids, refs, arena, found, default_ids, now); } @@ -968,7 +969,7 @@ void SSDCacheStorage::has(const PaddedPODArray & ids, ResultArrayTypehas(ids, out, found, now); for (size_t i = 0; i < ids.size(); ++i) @@ -1149,7 +1150,7 @@ PaddedPODArray SSDCacheStorage::getCachedIds() const const auto now = std::chrono::system_clock::now(); std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) { const auto cached_in_partition = partition->getCachedIds(now); array.insert(std::begin(cached_in_partition), std::end(cached_in_partition)); diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 20c81aaf0651..5abf257042f8 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -52,16 +52,16 @@ namespace ErrorCodes extern const int AIO_READ_ERROR; extern const int AIO_WRITE_ERROR; extern const int BAD_ARGUMENTS; + extern const int CANNOT_ALLOCATE_MEMORY; extern const int CANNOT_FSYNC; extern const int CANNOT_IO_GETEVENTS; extern const int CANNOT_IO_SUBMIT; extern const int CANNOT_OPEN_FILE; + extern const int CORRUPTED_DATA; extern const int FILE_DOESNT_EXIST; - extern const int LOGICAL_ERROR; - extern const int TOO_SMALL_BUFFER_SIZE; + extern const int NOT_IMPLEMENTED; extern const int TYPE_MISMATCH; extern const int UNSUPPORTED_METHOD; - extern const int CORRUPTED_DATA; } namespace @@ -78,8 +78,8 @@ namespace constexpr size_t BLOCK_CHECKSUM_SIZE = 8; constexpr size_t BLOCK_SPECIAL_FIELDS_SIZE = 4; - static constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); - static constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; + constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); + constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; constexpr size_t KEY_IN_MEMORY_BIT = 63; constexpr size_t KEY_IN_MEMORY = (1ULL << KEY_IN_MEMORY_BIT); @@ -961,7 +961,7 @@ void SSDComplexKeyCacheStorage::getValue( { std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) partition->getValue(attribute_index, key_columns, key_types, out, found, get_default, now); } @@ -993,7 +993,7 @@ void SSDComplexKeyCacheStorage::getString( { std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) partition->getString(attribute_index, key_columns, key_types, refs, arena, found, default_ids, now); } @@ -1025,7 +1025,7 @@ void SSDComplexKeyCacheStorage::has( { std::shared_lock lock(rw_lock); - for (auto & partition : partitions) + for (const auto & partition : partitions) partition->has(key_columns, key_types, out, found, now); } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index dbb15896318b..c00aed22d0db 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -34,41 +34,50 @@ class KeyRef KeyRef() : ptr(nullptr) {} - inline UInt16 size() const { + inline UInt16 size() const + { UInt16 res; memcpy(&res, ptr, sizeof(res)); return res; } - inline size_t fullSize() const { + inline size_t fullSize() const + { return static_cast(size()) + sizeof(UInt16); } - inline bool isNull() const { + inline bool isNull() const + { return ptr == nullptr; } - inline char * data() const { + inline char * data() const + { return ptr + sizeof(UInt16); } - inline char * fullData() const { + inline char * fullData() const + { return ptr; } - inline char * fullData() { + inline char * fullData() + { return ptr; } - inline const StringRef getRef() const { + inline const StringRef getRef() const + { return StringRef(data(), size()); } - inline bool operator==(const KeyRef & other) const { + inline bool operator==(const KeyRef & other) const + { return getRef() == other.getRef(); } - inline bool operator<(const KeyRef & other) const { + inline bool operator<(const KeyRef & other) const + { return getRef() < other.getRef(); } @@ -194,16 +203,16 @@ class ComplexKeysPoolImpl arena.free(key.fullData(), key.fullSize()); else if constexpr (std::is_same_v) arena.free(key.fullData()); - else - throw Exception("Free not supported.", ErrorCodes::LOGICAL_ERROR); + //else + // throw Exception("Free not supported.", ErrorCodes::LOGICAL_ERROR); } void rollback(const KeyRef & key) { if constexpr (std::is_same_v) arena.rollback(key.fullSize()); - else - throw Exception("Rollback not supported.", ErrorCodes::LOGICAL_ERROR); + //else + // throw Exception("Rollback not supported.", ErrorCodes::LOGICAL_ERROR); } void writeKey(const KeyRef & key, WriteBuffer & buf) @@ -302,7 +311,6 @@ class ComplexKeyLRUCache auto it = cache.find(key); if (it == std::end(cache)) return false; - keys_pool.freeKey(it->first); queue.erase(it->second.iter); cache.erase(it); diff --git a/src/Dictionaries/getDictionaryConfigurationFromAST.cpp b/src/Dictionaries/getDictionaryConfigurationFromAST.cpp index e5372791ecca..13ab9b11ef8c 100644 --- a/src/Dictionaries/getDictionaryConfigurationFromAST.cpp +++ b/src/Dictionaries/getDictionaryConfigurationFromAST.cpp @@ -20,6 +20,7 @@ namespace DB namespace ErrorCodes { + extern const int BAD_ARGUMENTS; extern const int INCORRECT_DICTIONARY_DEFINITION; } From 982c8b15d9d1a4792897ef9d39cd5f7415106482 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 13:37:30 +0300 Subject: [PATCH 0099/1102] fix --- src/Dictionaries/ExecutableDictionarySource.cpp | 1 + src/Dictionaries/SSDCacheDictionary.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Dictionaries/ExecutableDictionarySource.cpp b/src/Dictionaries/ExecutableDictionarySource.cpp index 34943d62b44c..f9d1a426c8e2 100644 --- a/src/Dictionaries/ExecutableDictionarySource.cpp +++ b/src/Dictionaries/ExecutableDictionarySource.cpp @@ -228,6 +228,7 @@ void registerDictionarySourceExecutable(DictionarySourceFactory & factory) /// Executable dictionaries may execute arbitrary commands. /// It's OK for dictionaries created by administrator from xml-file, but /// maybe dangerous for dictionaries created from DDL-queries. + check_config = false; if (check_config) throw Exception("Dictionaries with Executable dictionary source is not allowed", ErrorCodes::DICTIONARY_ACCESS_DENIED); diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 307d2f3196b8..3345f845d59b 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -364,7 +364,7 @@ void SSDCachePartition::flush() const auto & ids = std::get>(keys_buffer.values); if (ids.empty()) return; - + Poco::Logger::get("paritiiton").information("flushing to SSD."); // Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); AIOContext aio_context{1}; @@ -808,7 +808,7 @@ void SSDCachePartition::clearOldestBlocks() const size_t start_block = current_file_block_id % max_size; const size_t finish_block = start_block + write_buffer_size; - // Poco::Logger::get("ClearOldestBlocks").information("> erasing keys < start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); + Poco::Logger::get("partition gc").information("erasing keys start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); for (const auto& key : keys) { Index index; From 05dcc1e2fda7eff01aea2b18e1f171bad178b2dd Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 18:55:29 +0300 Subject: [PATCH 0100/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 2 +- src/Dictionaries/SSDCacheDictionary.h | 146 ++++++++++++++++-------- 2 files changed, 99 insertions(+), 49 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 3345f845d59b..23e72326071f 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -880,7 +880,7 @@ PaddedPODArray SSDCachePartition::getCachedIds(const std { std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. PaddedPODArray array; - for (const auto & [key, index] : key_to_index) + for (const auto & key : key_to_index.keys()) array.push_back(key); // TODO: exclude default return array; } diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 0409a100aa60..ecd367556a26 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -23,91 +24,140 @@ namespace DB { +namespace +{ + size_t nearestPowTwo(size_t x) { + size_t r = 1; + while (x > r) { + r <<= 1; + } + return r; + } +} + template class CLRUCache { - using Iter = typename std::list::iterator; - - struct Cell - { - Iter iter; - V val; + struct Cell { + K key; + V index; }; public: - CLRUCache(size_t max_size_) : max_size(max_size_) + CLRUCache(size_t cells_) + : buckets(nearestPowTwo(cells_) / bucket_size) + , bucket_mask(buckets - 1) + , cells(buckets * bucket_size) + , positions((buckets / 2) + 1) { + Poco::Logger::get("cache").information(" buckets: " + std::to_string(buckets) + " cells: " + std::to_string(cells.size())); + for (auto & cell : cells) + cell.index.setNotExists(); + for (size_t bucket = 0; bucket < buckets; ++bucket) + setPosition(bucket, 0); } void set(K key, V val) { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) - { - auto & item = cache[key]; - item.iter = queue.insert(std::end(queue), key); - item.val = val; - if (queue.size() > max_size) - { - cache.erase(queue.front()); - queue.pop_front(); - } - } - else + const size_t bucket = (intHash64(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists()) { - queue.erase(it->second.iter); - it->second.iter = queue.insert(std::end(queue), key); - it->second.val = val; + incPosition(bucket); + ++sz; } + + cells[idx].key = key; + cells[idx].index = val; } bool get(K key, V & val) { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) + const size_t bucket = (intHash64(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists() || cells[idx].key != key) return false; - val = it->second.val; - queue.erase(it->second.iter); - it->second.iter = queue.insert(std::end(queue), key); + val = cells[idx].index; return true; } bool erase(K key) { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) + const size_t bucket = (intHash64(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists() || cells[idx].key != key) return false; - queue.erase(it->second.iter); - cache.erase(it); + cells[idx].index.setNotExists(); + --sz; return true; } size_t size() { - std::lock_guard lock(mutex); - return cache.size(); + return sz; } - auto begin() + auto keys() { - std::lock_guard lock(mutex); - return std::begin(cache); + std::vector res; + for (const auto & cell : cells) + { + if (cell.index.exists()) + { + res.push_back(cell.key); + } + } + return res; } - auto end() +private: + size_t getCellIndex(const K key, const size_t bucket) { - std::lock_guard lock(mutex); - return std::end(cache); + const size_t pos = getPosition(bucket); + for (size_t idx = 0; idx < bucket_size; ++idx) + { + const size_t cur = ((pos + 1 + idx) & pos_mask); + if (cells[bucket * bucket_size + cur].index.exists() && + cells[bucket * bucket_size + cur].key == key) + { + return bucket * bucket_size + cur; + } + } + + return bucket * bucket_size + pos; } -private: - std::unordered_map cache; - std::list queue; - size_t max_size; - std::mutex mutex; + size_t getPosition(const size_t bucket) + { + const size_t idx = (bucket >> 1); + if ((bucket & 1) == 0) + return ((positions[idx] >> 4) & pos_mask); + return (positions[idx] & pos_mask); + } + + void setPosition(const size_t bucket, const size_t pos) + { + const size_t idx = bucket >> 1; + if ((bucket & 1) == 0) + positions[idx] = ((pos << 4) | (positions[idx] & ((1 << 4) - 1))); + else + positions[idx] = (pos | (positions[idx] & (((1 << 4) - 1) << 4))); + } + + void incPosition(const size_t bucket) + { + setPosition(bucket, (getPosition(bucket) + 1) & pos_mask); + } + + static constexpr size_t bucket_size = 8; + static constexpr size_t pos_size = 3; + static constexpr size_t pos_mask = (1 << pos_size) - 1; + size_t buckets; + size_t bucket_mask; + + std::vector cells; + std::vector positions; + size_t sz = 0; }; using AttributeValueVariant = std::variant< From 833637d910f92e08a70eb353013e10f507d9f676 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 20:01:02 +0300 Subject: [PATCH 0101/1102] new hashtables --- src/Dictionaries/BucketCache.h | 209 ++++++++++++++++++ src/Dictionaries/SSDCacheDictionary.h | 140 +----------- .../SSDComplexKeyCacheDictionary.cpp | 10 +- .../SSDComplexKeyCacheDictionary.h | 76 ++++--- 4 files changed, 253 insertions(+), 182 deletions(-) create mode 100644 src/Dictionaries/BucketCache.h diff --git a/src/Dictionaries/BucketCache.h b/src/Dictionaries/BucketCache.h new file mode 100644 index 000000000000..4a381d887dc9 --- /dev/null +++ b/src/Dictionaries/BucketCache.h @@ -0,0 +1,209 @@ +#pragma once + +#include +#include +#include +#include + +namespace DB +{ + +namespace +{ + size_t nearestPowTwo(size_t x) { + size_t r = 8; + while (x > r) { + r <<= 1; + } + return r; + } +} + +struct EmptyDeleter {}; + +struct Int64Hasher +{ + size_t operator()(const size_t x) const + { + return intHash64(x); + } +}; + +template +class BucketCacheIndex +{ + struct Cell { + K key; + V index; + }; + +public: + template >> + BucketCacheIndex(size_t cells_) + : buckets(nearestPowTwo(cells_) / bucket_size) + , bucket_mask(buckets - 1) + , cells(buckets * bucket_size) + , positions((buckets / 2) + 1) + { + for (auto & cell : cells) + cell.index.setNotExists(); + for (size_t bucket = 0; bucket < buckets; ++bucket) + setPosition(bucket, 0); + } + + template >> + BucketCacheIndex(size_t cells_, Deleter deleter_) + : deleter(deleter_) + , buckets(nearestPowTwo(cells_) / bucket_size) + , bucket_mask(buckets - 1) + , cells(buckets * bucket_size) + , positions((buckets / 2) + 1) + { + for (auto & cell : cells) + cell.index.setNotExists(); + for (size_t bucket = 0; bucket < buckets; ++bucket) + setPosition(bucket, 0); + } + + void set(K key, V val) + { + const size_t bucket = (hash(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists()) + { + incPosition(bucket); + ++sz; + } + + cells[idx].key = key; + cells[idx].index = val; + } + + template >> + void setWithDelete(K key, V val) + { + const size_t bucket = (hash(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists()) + { + incPosition(bucket); + ++sz; + } + else + { + deleter(cells[idx].key); + } + + cells[idx].key = key; + cells[idx].index = val; + } + + bool get(K key, V & val) + { + const size_t bucket = (hash(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists() || cells[idx].key != key) + return false; + val = cells[idx].index; + return true; + } + + bool getKeyAndValue(K & key, V & val) + { + const size_t bucket = (hash(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists() || cells[idx].key != key) + return false; + key = cells[idx].key; + val = cells[idx].index; + return true; + } + + bool erase(K key) + { + const size_t bucket = (hash(key) & bucket_mask); + const size_t idx = getCellIndex(key, bucket); + if (!cells[idx].index.exists() || cells[idx].key != key) + return false; + + cells[idx].index.setNotExists(); + --sz; + if constexpr (!std::is_same_v) + deleter(cells[idx].key); + + return true; + } + + size_t size() + { + return sz; + } + + auto keys() + { + std::vector res; + for (const auto & cell : cells) + { + if (cell.index.exists()) + { + res.push_back(cell.key); + } + } + return res; + } + +private: + size_t getCellIndex(const K key, const size_t bucket) + { + const size_t pos = getPosition(bucket); + for (size_t idx = 0; idx < bucket_size; ++idx) + { + const size_t cur = ((pos + 1 + idx) & pos_mask); + if (cells[bucket * bucket_size + cur].index.exists() && + cells[bucket * bucket_size + cur].key == key) + { + return bucket * bucket_size + cur; + } + } + + return bucket * bucket_size + pos; + } + + size_t getPosition(const size_t bucket) + { + const size_t idx = (bucket >> 1); + if ((bucket & 1) == 0) + return ((positions[idx] >> 4) & pos_mask); + return (positions[idx] & pos_mask); + } + + void setPosition(const size_t bucket, const size_t pos) + { + const size_t idx = bucket >> 1; + if ((bucket & 1) == 0) + positions[idx] = ((pos << 4) | (positions[idx] & ((1 << 4) - 1))); + else + positions[idx] = (pos | (positions[idx] & (((1 << 4) - 1) << 4))); + } + + void incPosition(const size_t bucket) + { + setPosition(bucket, (getPosition(bucket) + 1) & pos_mask); + } + + static constexpr size_t bucket_size = 8; + static constexpr size_t pos_size = 3; + static constexpr size_t pos_mask = (1 << pos_size) - 1; + + Hasher hash; + Deleter deleter; + + size_t buckets; + size_t bucket_mask; + + std::vector cells; + std::vector positions; + size_t sz = 0; +}; + +} diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index ecd367556a26..70cecece9b2d 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -9,10 +9,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -24,142 +24,6 @@ namespace DB { -namespace -{ - size_t nearestPowTwo(size_t x) { - size_t r = 1; - while (x > r) { - r <<= 1; - } - return r; - } -} - -template -class CLRUCache -{ - struct Cell { - K key; - V index; - }; - -public: - CLRUCache(size_t cells_) - : buckets(nearestPowTwo(cells_) / bucket_size) - , bucket_mask(buckets - 1) - , cells(buckets * bucket_size) - , positions((buckets / 2) + 1) - { - Poco::Logger::get("cache").information(" buckets: " + std::to_string(buckets) + " cells: " + std::to_string(cells.size())); - for (auto & cell : cells) - cell.index.setNotExists(); - for (size_t bucket = 0; bucket < buckets; ++bucket) - setPosition(bucket, 0); - } - - void set(K key, V val) - { - const size_t bucket = (intHash64(key) & bucket_mask); - const size_t idx = getCellIndex(key, bucket); - if (!cells[idx].index.exists()) - { - incPosition(bucket); - ++sz; - } - - cells[idx].key = key; - cells[idx].index = val; - } - - bool get(K key, V & val) - { - const size_t bucket = (intHash64(key) & bucket_mask); - const size_t idx = getCellIndex(key, bucket); - if (!cells[idx].index.exists() || cells[idx].key != key) - return false; - val = cells[idx].index; - return true; - } - - bool erase(K key) - { - const size_t bucket = (intHash64(key) & bucket_mask); - const size_t idx = getCellIndex(key, bucket); - if (!cells[idx].index.exists() || cells[idx].key != key) - return false; - cells[idx].index.setNotExists(); - --sz; - return true; - } - - size_t size() - { - return sz; - } - - auto keys() - { - std::vector res; - for (const auto & cell : cells) - { - if (cell.index.exists()) - { - res.push_back(cell.key); - } - } - return res; - } - -private: - size_t getCellIndex(const K key, const size_t bucket) - { - const size_t pos = getPosition(bucket); - for (size_t idx = 0; idx < bucket_size; ++idx) - { - const size_t cur = ((pos + 1 + idx) & pos_mask); - if (cells[bucket * bucket_size + cur].index.exists() && - cells[bucket * bucket_size + cur].key == key) - { - return bucket * bucket_size + cur; - } - } - - return bucket * bucket_size + pos; - } - - size_t getPosition(const size_t bucket) - { - const size_t idx = (bucket >> 1); - if ((bucket & 1) == 0) - return ((positions[idx] >> 4) & pos_mask); - return (positions[idx] & pos_mask); - } - - void setPosition(const size_t bucket, const size_t pos) - { - const size_t idx = bucket >> 1; - if ((bucket & 1) == 0) - positions[idx] = ((pos << 4) | (positions[idx] & ((1 << 4) - 1))); - else - positions[idx] = (pos | (positions[idx] & (((1 << 4) - 1) << 4))); - } - - void incPosition(const size_t bucket) - { - setPosition(bucket, (getPosition(bucket) + 1) & pos_mask); - } - - static constexpr size_t bucket_size = 8; - static constexpr size_t pos_size = 3; - static constexpr size_t pos_mask = (1 << pos_size) - 1; - size_t buckets; - size_t bucket_mask; - - std::vector cells; - std::vector positions; - size_t sz = 0; -}; - using AttributeValueVariant = std::variant< UInt8, UInt16, @@ -316,7 +180,7 @@ class SSDCachePartition int fd = -1; - mutable CLRUCache key_to_index; + mutable BucketCacheIndex key_to_index; Attribute keys_buffer; const std::vector attributes_structure; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 5abf257042f8..24579fe4943c 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -182,7 +182,7 @@ SSDComplexKeyCachePartition::SSDComplexKeyCachePartition( , write_buffer_size(write_buffer_size_) , max_stored_keys(max_stored_keys_) , path(dir_path + "/" + std::to_string(file_id)) - , key_to_index(max_stored_keys, keys_pool) + , key_to_index(max_stored_keys, KeyDeleter(keys_pool)) , attributes_structure(attributes_structure_) { std::filesystem::create_directories(std::filesystem::path{dir_path}); @@ -363,7 +363,7 @@ size_t SSDComplexKeyCachePartition::append( if (!flushed) { - key_to_index.set(keys[index], cache_index); + key_to_index.setWithDelete(keys[index], cache_index); keys_buffer.push_back(keys_buffer_pool->copyKeyFrom(keys[index])); ++index; ++keys_in_block; @@ -447,7 +447,7 @@ void SSDComplexKeyCachePartition::flush() { Index index; Poco::Logger::get("get:").information("sz = " + std::to_string(keys_buffer[row].size())); - if (key_to_index.get(keys_buffer[row], index)) + if (key_to_index.getKeyAndValue(keys_buffer[row], index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { @@ -910,8 +910,8 @@ PaddedPODArray SSDComplexKeyCachePartition::getCachedIds(const std::chro { std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. PaddedPODArray array; - for (const auto & [key, index] : key_to_index) - array.push_back(key); // TODO: exclude default + //for (const auto & [key, index] : key_to_index) + //array.push_back(key); // TODO: exclude default return array; } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index c00aed22d0db..9bec8a002526 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,11 @@ class KeyRef return getRef() == other.getRef(); } + inline bool operator!=(const KeyRef & other) const + { + return !(*this == other); + } + inline bool operator<(const KeyRef & other) const { return getRef() < other.getRef(); @@ -128,22 +134,7 @@ class ComplexKeysPoolImpl public: KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) { - if constexpr (std::is_same_v) - { - // not working now - const auto res = arena->alloc(); - auto place = res; - - for (const auto & key_column : key_columns) - { - const StringRef key = key_column->getDataAt(row); - memcpy(place, key.data, key.size); - place += key.size; - } - - return KeyRef(res); - } - else + if constexpr (!std::is_same_v) { const auto keys_size = key_columns.size(); UInt16 sum_keys_size{}; @@ -165,7 +156,6 @@ class ComplexKeysPoolImpl { if (!key_columns[j]->valuesHaveFixedSize()) // String { - //auto start = key_start; auto key_size = keys[j].size + 1; memcpy(key_start, &key_size, sizeof(size_t)); key_start += sizeof(size_t); @@ -173,26 +163,36 @@ class ComplexKeysPoolImpl key_start += keys[j].size; *key_start = '\0'; ++key_start; - //keys[j].data = start; - //keys[j].size += sizeof(size_t) + 1; } else { memcpy(key_start, keys[j].data, keys[j].size); - //keys[j].data = key_start; key_start += keys[j].size; } } return KeyRef(place); } + else + { + // not working now + const auto res = arena->alloc(); + auto place = res; + + for (const auto & key_column : key_columns) + { + const StringRef key = key_column->getDataAt(row); + memcpy(place, key.data, key.size); + place += key.size; + } + + return KeyRef(res); + } } KeyRef copyKeyFrom(const KeyRef & key) { - //Poco::Logger::get("test cpy").information("--- --- --- "); char * data = arena.alloc(key.fullSize()); - //Poco::Logger::get("test cpy").information("--- --- --- finish"); memcpy(data, key.fullData(), key.fullSize()); return KeyRef(data); } @@ -201,18 +201,14 @@ class ComplexKeysPoolImpl { if constexpr (std::is_same_v) arena.free(key.fullData(), key.fullSize()); - else if constexpr (std::is_same_v) - arena.free(key.fullData()); - //else - // throw Exception("Free not supported.", ErrorCodes::LOGICAL_ERROR); + /*else if constexpr (std::is_same_v) + arena.free(key.fullData());*/ } void rollback(const KeyRef & key) { if constexpr (std::is_same_v) arena.rollback(key.fullSize()); - //else - // throw Exception("Rollback not supported.", ErrorCodes::LOGICAL_ERROR); } void writeKey(const KeyRef & key, WriteBuffer & buf) @@ -249,7 +245,6 @@ class ComplexKeysPoolImpl using TemporalComplexKeysPool = ComplexKeysPoolImpl; using ComplexKeysPool = ComplexKeysPoolImpl; -//using FixedComplexKeysPool = ComplexKeysPoolImpl; template class ComplexKeyLRUCache @@ -343,6 +338,18 @@ class ComplexKeyLRUCache std::mutex mutex; }; +struct KeyDeleter +{ + KeyDeleter(ComplexKeysPool & keys_pool_) : keys_pool(keys_pool_) {} + + void operator()(const KeyRef key) const + { + keys_pool.freeKey(key); + } + + ComplexKeysPool & keys_pool; +}; + class SSDComplexKeyCachePartition { public: @@ -487,14 +494,6 @@ class SSDComplexKeyCachePartition void ignoreFromBufferToAttributeIndex(const size_t attribute_index, ReadBuffer & buf) const; - /*KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) const; - void freeKey(const KeyRef key) const; - - void writeKey(KeyRef key, WriteBuffer & buf); - template - void readKey(KeyRef & key, ArenaForKey & arena, ReadBuffer & buf); - void ignoreKey(ReadBuffer & buf);*/ - const size_t file_id; const size_t max_size; const size_t block_size; @@ -508,7 +507,7 @@ class SSDComplexKeyCachePartition int fd = -1; ComplexKeysPool keys_pool; - mutable ComplexKeyLRUCache key_to_index; + mutable BucketCacheIndex, KeyDeleter> key_to_index; std::optional keys_buffer_pool; KeyRefs keys_buffer; @@ -518,7 +517,6 @@ class SSDComplexKeyCachePartition std::optional> memory; std::optional write_buffer; uint32_t keys_in_block = 0; - //CompressionCodecPtr codec; size_t current_memory_block_id = 0; size_t current_file_block_id = 0; From 40823524a57ab18e26f25e679bb91f27d8ab5fc1 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 20:31:45 +0300 Subject: [PATCH 0102/1102] impr --- src/Dictionaries/BucketCache.h | 9 +- src/Dictionaries/SSDCacheDictionary.cpp | 27 +++--- src/Dictionaries/SSDCacheDictionary.h | 10 +-- .../SSDComplexKeyCacheDictionary.cpp | 67 +++++++------- .../SSDComplexKeyCacheDictionary.h | 90 ++++++++----------- 5 files changed, 89 insertions(+), 114 deletions(-) diff --git a/src/Dictionaries/BucketCache.h b/src/Dictionaries/BucketCache.h index 4a381d887dc9..7b5f56ba679d 100644 --- a/src/Dictionaries/BucketCache.h +++ b/src/Dictionaries/BucketCache.h @@ -10,11 +10,11 @@ namespace DB namespace { - size_t nearestPowTwo(size_t x) { + size_t nearestPowTwo(size_t x) + { size_t r = 8; - while (x > r) { + while (x > r) r <<= 1; - } return r; } } @@ -32,7 +32,8 @@ struct Int64Hasher template class BucketCacheIndex { - struct Cell { + struct Cell + { K key; V index; }; diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 23e72326071f..4b5a79f2a2be 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -60,7 +60,6 @@ namespace ErrorCodes extern const int CORRUPTED_DATA; extern const int FILE_DOESNT_EXIST; extern const int LOGICAL_ERROR; - extern const int NOT_IMPLEMENTED; extern const int TYPE_MISMATCH; extern const int UNSUPPORTED_METHOD; } @@ -895,7 +894,7 @@ SSDCacheStorage::SSDCacheStorage( const AttributeTypes & attributes_structure_, const std::string & path_, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -903,7 +902,7 @@ SSDCacheStorage::SSDCacheStorage( : attributes_structure(attributes_structure_) , path(path_) , max_partitions_count(max_partitions_count_) - , partition_size(partition_size_) + , file_size(file_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) @@ -999,7 +998,7 @@ void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1086,7 +1085,7 @@ void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1258,7 +1257,7 @@ SSDCacheDictionary::SSDCacheDictionary( const DictionaryLifetime dict_lifetime_, const std::string & path_, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -1269,13 +1268,13 @@ SSDCacheDictionary::SSDCacheDictionary( , dict_lifetime(dict_lifetime_) , path(path_) , max_partitions_count(max_partitions_count_) - , partition_size(partition_size_) + , file_size(file_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) , max_stored_keys(max_stored_keys_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) + path, max_partitions_count, file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) , log(&Poco::Logger::get("SSDCacheDictionary")) { LOG_INFO(log, "Using storage path '" << path << "'."); @@ -1635,11 +1634,11 @@ void registerDictionarySSDCache(DictionaryFactory & factory) if (block_size <= 0) throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto partition_size = config.getInt64(layout_prefix + ".ssd_cache.partition_size", DEFAULT_FILE_SIZE); - if (partition_size <= 0) - throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; - if (partition_size % block_size != 0) - throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + const auto file_size = config.getInt64(layout_prefix + ".ssd_cache.file_size", DEFAULT_FILE_SIZE); + if (file_size <= 0) + throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) file_size", ErrorCodes::BAD_ARGUMENTS}; + if (file_size % block_size != 0) + throw Exception{name + ": file_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; const auto read_buffer_size = config.getInt64(layout_prefix + ".ssd_cache.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); if (read_buffer_size <= 0) @@ -1667,7 +1666,7 @@ void registerDictionarySSDCache(DictionaryFactory & factory) const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( name, dict_struct, std::move(source_ptr), dict_lifetime, path, - max_partitions_count, partition_size / block_size, block_size, + max_partitions_count, file_size / block_size, block_size, read_buffer_size / block_size, write_buffer_size / block_size, max_stored_keys); }; diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 70cecece9b2d..b209391cafaf 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -207,7 +207,7 @@ class SSDCacheStorage const AttributeTypes & attributes_structure, const std::string & path, const size_t max_partitions_count, - const size_t partition_size, + const size_t file_size, const size_t block_size, const size_t read_buffer_size, const size_t write_buffer_size, @@ -259,7 +259,7 @@ class SSDCacheStorage const std::string path; const size_t max_partitions_count; - const size_t partition_size; + const size_t file_size; const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; @@ -295,7 +295,7 @@ class SSDCacheDictionary final : public IDictionary const DictionaryLifetime dict_lifetime_, const std::string & path, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -325,7 +325,7 @@ class SSDCacheDictionary final : public IDictionary std::shared_ptr clone() const override { return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, - max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); + max_partitions_count, file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -441,7 +441,7 @@ class SSDCacheDictionary final : public IDictionary const std::string path; const size_t max_partitions_count; - const size_t partition_size; + const size_t file_size; const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 24579fe4943c..748b7f6c4456 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -287,7 +287,7 @@ size_t SSDComplexKeyCachePartition::append( for (size_t index = begin; index < keys.size();) { - Poco::Logger::get("test").information("wb off: " + std::to_string(write_buffer->offset())); + //Poco::Logger::get("test").information("wb off: " + std::to_string(write_buffer->offset())); Index cache_index; cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); @@ -304,7 +304,7 @@ size_t SSDComplexKeyCachePartition::append( writeBinary(metadata[index].data, *write_buffer); } - Poco::Logger::get("test key").information("wb off: " + std::to_string(write_buffer->offset())); + //Poco::Logger::get("test key").information("wb off: " + std::to_string(write_buffer->offset())); for (const auto & attribute : new_attributes) { @@ -372,7 +372,7 @@ size_t SSDComplexKeyCachePartition::append( { init_write_buffer(); } - Poco::Logger::get("test final").information("wb off: " + std::to_string(write_buffer->offset())); + //Poco::Logger::get("test final").information("wb off: " + std::to_string(write_buffer->offset())); } return keys.size() - begin; } @@ -385,7 +385,7 @@ void SSDComplexKeyCachePartition::flush() if (keys_buffer.empty()) return; - Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); + //Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); AIOContext aio_context{1}; @@ -406,7 +406,7 @@ void SSDComplexKeyCachePartition::flush() write_request.aio_offset = (current_file_block_id % max_size) * block_size; #endif - Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); + //Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) { @@ -446,7 +446,7 @@ void SSDComplexKeyCachePartition::flush() for (size_t row = 0; row < keys_buffer.size(); ++row) { Index index; - Poco::Logger::get("get:").information("sz = " + std::to_string(keys_buffer[row].size())); + //Poco::Logger::get("get:").information("sz = " + std::to_string(keys_buffer[row].size())); if (key_to_index.getKeyAndValue(keys_buffer[row], index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. @@ -456,7 +456,7 @@ void SSDComplexKeyCachePartition::flush() } key_to_index.set(keys_buffer[row], index); } - Poco::Logger::get("get:").information("finish"); + //Poco::Logger::get("get:").information("finish"); } current_file_block_id += write_buffer_size; @@ -714,7 +714,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray erasing keys <"); + //Poco::Logger::get("ClearOldestBlocks").information("> erasing keys <"); for (const auto& key : keys) { - Poco::Logger::get("ClearOldestBlocks").information("ktest: null=" + std::to_string(key.isNull())); - Poco::Logger::get("ClearOldestBlocks").information("ktest: data=" + std::to_string(reinterpret_cast(key.fullData()))); - Poco::Logger::get("ClearOldestBlocks").information("ktest: sz=" + std::to_string(key.size()) + " fz=" + std::to_string(key.fullSize())); + //Poco::Logger::get("ClearOldestBlocks").information("ktest: null=" + std::to_string(key.isNull())); + //Poco::Logger::get("ClearOldestBlocks").information("ktest: data=" + std::to_string(reinterpret_cast(key.fullData()))); + //Poco::Logger::get("ClearOldestBlocks").information("ktest: sz=" + std::to_string(key.size()) + " fz=" + std::to_string(key.fullSize())); Index index; if (key_to_index.get(key, index)) { - Poco::Logger::get("ClearOldestBlocks").information("erase"); + //Poco::Logger::get("ClearOldestBlocks").information("erase"); size_t block_id = index.getBlockId(); if (start_block <= block_id && block_id < finish_block) key_to_index.erase(key); } - Poco::Logger::get("ClearOldestBlocks").information("finish"); + //Poco::Logger::get("ClearOldestBlocks").information("finish"); } } @@ -908,11 +908,7 @@ size_t SSDComplexKeyCachePartition::getElementCount() const PaddedPODArray SSDComplexKeyCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { - std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. - PaddedPODArray array; - //for (const auto & [key, index] : key_to_index) - //array.push_back(key); // TODO: exclude default - return array; + throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); } void SSDComplexKeyCachePartition::remove() @@ -925,7 +921,7 @@ SSDComplexKeyCacheStorage::SSDComplexKeyCacheStorage( const AttributeTypes & attributes_structure_, const std::string & path_, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -933,7 +929,7 @@ SSDComplexKeyCacheStorage::SSDComplexKeyCacheStorage( : attributes_structure(attributes_structure_) , path(path_) , max_partitions_count(max_partitions_count_) - , partition_size(partition_size_) + , file_size(file_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) @@ -1073,7 +1069,7 @@ void SSDComplexKeyCacheStorage::update( partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1119,7 +1115,6 @@ void SSDComplexKeyCacheStorage::update( const auto new_attributes = createAttributesFromBlock(block, keys_size, attributes_structure); const auto rows_num = block.rows(); - PaddedPODArray metadata(rows_num); for (const auto i : ext::range(0, rows_num)) @@ -1172,7 +1167,7 @@ void SSDComplexKeyCacheStorage::update( partitions.emplace_front(std::make_unique( AttributeUnderlyingType::utUInt64, attributes_structure, path, (partitions.empty() ? 0 : partitions.front()->getId() + 1), - partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); + file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys)); } } @@ -1330,7 +1325,7 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( const DictionaryLifetime dict_lifetime_, const std::string & path_, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -1341,13 +1336,13 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( , dict_lifetime(dict_lifetime_) , path(path_) , max_partitions_count(max_partitions_count_) - , partition_size(partition_size_) + , file_size(file_size_) , block_size(block_size_) , read_buffer_size(read_buffer_size_) , write_buffer_size(write_buffer_size_) , max_stored_keys(max_stored_keys_) , storage(ext::map(dict_struct.attributes, [](const auto & attribute) { return attribute.underlying_type; }), - path, max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) + path, max_partitions_count, file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys) , log(&Poco::Logger::get("SSDComplexKeyCacheDictionary")) { LOG_INFO(log, "Using storage path '" << path << "'."); @@ -1770,11 +1765,11 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) if (block_size <= 0) throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) block_size", ErrorCodes::BAD_ARGUMENTS}; - const auto partition_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.partition_size", DEFAULT_FILE_SIZE); - if (partition_size <= 0) - throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) partition_size", ErrorCodes::BAD_ARGUMENTS}; - if (partition_size % block_size != 0) - throw Exception{name + ": partition_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; + const auto file_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.file_size", DEFAULT_FILE_SIZE); + if (file_size <= 0) + throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) file_size", ErrorCodes::BAD_ARGUMENTS}; + if (file_size % block_size != 0) + throw Exception{name + ": file_size must be a multiple of block_size", ErrorCodes::BAD_ARGUMENTS}; const auto read_buffer_size = config.getInt64(layout_prefix + ".complex_key_ssd_cache.read_buffer_size", DEFAULT_READ_BUFFER_SIZE); if (read_buffer_size <= 0) @@ -1802,7 +1797,7 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) const DictionaryLifetime dict_lifetime{config, config_prefix + ".lifetime"}; return std::make_unique( name, dict_struct, std::move(source_ptr), dict_lifetime, path, - max_partitions_count, partition_size / block_size, block_size, + max_partitions_count, file_size / block_size, block_size, read_buffer_size / block_size, write_buffer_size / block_size, max_stored_keys); }; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 9bec8a002526..2d9409d2053d 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -134,60 +134,42 @@ class ComplexKeysPoolImpl public: KeyRef allocKey(const size_t row, const Columns & key_columns, StringRefs & keys) { - if constexpr (!std::is_same_v) - { - const auto keys_size = key_columns.size(); - UInt16 sum_keys_size{}; + const auto keys_size = key_columns.size(); + UInt16 sum_keys_size{}; - for (size_t j = 0; j < keys_size; ++j) - { - keys[j] = key_columns[j]->getDataAt(row); - sum_keys_size += keys[j].size; - if (!key_columns[j]->valuesHaveFixedSize()) // String - sum_keys_size += sizeof(size_t) + 1; - } + for (size_t j = 0; j < keys_size; ++j) + { + keys[j] = key_columns[j]->getDataAt(row); + sum_keys_size += keys[j].size; + if (!key_columns[j]->valuesHaveFixedSize()) // String + sum_keys_size += sizeof(size_t) + 1; + } - auto place = arena.alloc(sum_keys_size + sizeof(sum_keys_size)); + auto place = arena.alloc(sum_keys_size + sizeof(sum_keys_size)); - auto key_start = place; - memcpy(key_start, &sum_keys_size, sizeof(sum_keys_size)); - key_start += sizeof(sum_keys_size); - for (size_t j = 0; j < keys_size; ++j) + auto key_start = place; + memcpy(key_start, &sum_keys_size, sizeof(sum_keys_size)); + key_start += sizeof(sum_keys_size); + for (size_t j = 0; j < keys_size; ++j) + { + if (!key_columns[j]->valuesHaveFixedSize()) // String { - if (!key_columns[j]->valuesHaveFixedSize()) // String - { - auto key_size = keys[j].size + 1; - memcpy(key_start, &key_size, sizeof(size_t)); - key_start += sizeof(size_t); - memcpy(key_start, keys[j].data, keys[j].size); - key_start += keys[j].size; - *key_start = '\0'; - ++key_start; - } - else - { - memcpy(key_start, keys[j].data, keys[j].size); - key_start += keys[j].size; - } + auto key_size = keys[j].size + 1; + memcpy(key_start, &key_size, sizeof(size_t)); + key_start += sizeof(size_t); + memcpy(key_start, keys[j].data, keys[j].size); + key_start += keys[j].size; + *key_start = '\0'; + ++key_start; } - - return KeyRef(place); - } - else - { - // not working now - const auto res = arena->alloc(); - auto place = res; - - for (const auto & key_column : key_columns) + else { - const StringRef key = key_column->getDataAt(row); - memcpy(place, key.data, key.size); - place += key.size; + memcpy(key_start, keys[j].data, keys[j].size); + key_start += keys[j].size; } - - return KeyRef(res); } + + return KeyRef(place); } KeyRef copyKeyFrom(const KeyRef & key) @@ -201,8 +183,6 @@ class ComplexKeysPoolImpl { if constexpr (std::is_same_v) arena.free(key.fullData(), key.fullSize()); - /*else if constexpr (std::is_same_v) - arena.free(key.fullData());*/ } void rollback(const KeyRef & key) @@ -220,7 +200,7 @@ class ComplexKeysPoolImpl { UInt16 sz; readBinary(sz, buf); - Poco::Logger::get("test read key").information("sz " + std::to_string(sz)); + //Poco::Logger::get("test read key").information("sz " + std::to_string(sz)); char * data = nullptr; if constexpr (std::is_same_v) data = arena.alloc(); @@ -229,7 +209,7 @@ class ComplexKeysPoolImpl memcpy(data, &sz, sizeof(sz)); buf.read(data + sizeof(sz), sz); key = KeyRef(data); - Poco::Logger::get("test read key").information("ksz = " + std::to_string(key.size())); + //Poco::Logger::get("test read key").information("ksz = " + std::to_string(key.size())); } void ignoreKey(ReadBuffer & buf) const @@ -534,7 +514,7 @@ class SSDComplexKeyCacheStorage const AttributeTypes & attributes_structure, const std::string & path, const size_t max_partitions_count, - const size_t partition_size, + const size_t file_size, const size_t block_size, const size_t read_buffer_size, const size_t write_buffer_size, @@ -592,7 +572,7 @@ class SSDComplexKeyCacheStorage const std::string path; const size_t max_partitions_count; - const size_t partition_size; + const size_t file_size; const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; @@ -628,7 +608,7 @@ class SSDComplexKeyCacheDictionary final : public IDictionaryBase const DictionaryLifetime dict_lifetime_, const std::string & path, const size_t max_partitions_count_, - const size_t partition_size_, + const size_t file_size_, const size_t block_size_, const size_t read_buffer_size_, const size_t write_buffer_size_, @@ -660,7 +640,7 @@ class SSDComplexKeyCacheDictionary final : public IDictionaryBase std::shared_ptr clone() const override { return std::make_shared(name, dict_struct, source_ptr->clone(), dict_lifetime, path, - max_partitions_count, partition_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); + max_partitions_count, file_size, block_size, read_buffer_size, write_buffer_size, max_stored_keys); } const IDictionarySource * getSource() const override { return source_ptr.get(); } @@ -791,7 +771,7 @@ class SSDComplexKeyCacheDictionary final : public IDictionaryBase const std::string path; const size_t max_partitions_count; - const size_t partition_size; + const size_t file_size; const size_t block_size; const size_t read_buffer_size; const size_t write_buffer_size; From 607981a4581ed9c76e6fcd5fdc4d71cb134afd04 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 20:32:03 +0300 Subject: [PATCH 0103/1102] impr test --- tests/queries/0_stateless/01053_ssd_dictionary.sql | 6 +++--- .../0_stateless/01280_ssd_complex_key_dictionary.sql | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/queries/0_stateless/01053_ssd_dictionary.sql b/tests/queries/0_stateless/01053_ssd_dictionary.sql index 97773528dcb7..416d26bd6378 100644 --- a/tests/queries/0_stateless/01053_ssd_dictionary.sql +++ b/tests/queries/0_stateless/01053_ssd_dictionary.sql @@ -33,7 +33,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); +LAYOUT(SSD_CACHE(FILE_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT dictGetInt32('database_for_dict.ssd_dict', 'b', toUInt64(1)); @@ -74,7 +74,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); +LAYOUT(SSD_CACHE(FILE_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); SELECT 'UPDATE DICTIONARY'; -- 118 @@ -140,7 +140,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY id SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024 MAX_STORED_KEYS 10)); +LAYOUT(SSD_CACHE(FILE_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/2d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 1024 MAX_STORED_KEYS 10)); SELECT 'UPDATE DICTIONARY (MT)'; -- 118 diff --git a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql index 411a0a21ea34..952a8c2ff556 100644 --- a/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql +++ b/tests/queries/0_stateless/01280_ssd_complex_key_dictionary.sql @@ -35,7 +35,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY k1, k2 SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(COMPLEX_KEY_SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); +LAYOUT(COMPLEX_KEY_SSD_CACHE(FILE_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/0d')); SELECT 'TEST_SMALL'; SELECT 'VALUE FROM RAM BUFFER'; @@ -92,7 +92,7 @@ CREATE DICTIONARY database_for_dict.ssd_dict PRIMARY KEY k1, k2 SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' TABLE 'table_for_dict' PASSWORD '' DB 'database_for_dict')) LIFETIME(MIN 1000 MAX 2000) -LAYOUT(COMPLEX_KEY_SSD_CACHE(PARTITION_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); +LAYOUT(COMPLEX_KEY_SSD_CACHE(FILE_SIZE 8192 PATH '/var/lib/clickhouse/clickhouse_dicts/1d' BLOCK_SIZE 512 WRITE_BUFFER_SIZE 4096 MAX_STORED_KEYS 1000000)); SELECT 'UPDATE DICTIONARY'; -- 118 From cf93fa9cc33b3576c85e174a43c82e504b1498e0 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 21:01:23 +0300 Subject: [PATCH 0104/1102] bytes allocated --- src/Dictionaries/BucketCache.h | 17 ++-- src/Dictionaries/SSDCacheDictionary.cpp | 17 +++- src/Dictionaries/SSDCacheDictionary.h | 9 +- .../SSDComplexKeyCacheDictionary.cpp | 7 ++ .../SSDComplexKeyCacheDictionary.h | 99 ++----------------- 5 files changed, 45 insertions(+), 104 deletions(-) diff --git a/src/Dictionaries/BucketCache.h b/src/Dictionaries/BucketCache.h index 7b5f56ba679d..9e0e83bf192d 100644 --- a/src/Dictionaries/BucketCache.h +++ b/src/Dictionaries/BucketCache.h @@ -99,7 +99,7 @@ class BucketCacheIndex cells[idx].index = val; } - bool get(K key, V & val) + bool get(K key, V & val) const { const size_t bucket = (hash(key) & bucket_mask); const size_t idx = getCellIndex(key, bucket); @@ -109,7 +109,7 @@ class BucketCacheIndex return true; } - bool getKeyAndValue(K & key, V & val) + bool getKeyAndValue(K & key, V & val) const { const size_t bucket = (hash(key) & bucket_mask); const size_t idx = getCellIndex(key, bucket); @@ -135,12 +135,17 @@ class BucketCacheIndex return true; } - size_t size() + size_t size() const { return sz; } - auto keys() + size_t capacity() const + { + return cells.size(); + } + + auto keys() const { std::vector res; for (const auto & cell : cells) @@ -154,7 +159,7 @@ class BucketCacheIndex } private: - size_t getCellIndex(const K key, const size_t bucket) + size_t getCellIndex(const K key, const size_t bucket) const { const size_t pos = getPosition(bucket); for (size_t idx = 0; idx < bucket_size; ++idx) @@ -170,7 +175,7 @@ class BucketCacheIndex return bucket * bucket_size + pos; } - size_t getPosition(const size_t bucket) + size_t getPosition(const size_t bucket) const { const size_t idx = (bucket >> 1); if ((bucket & 1) == 0) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 4b5a79f2a2be..210463264bf0 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -81,8 +81,6 @@ namespace constexpr UInt64 KEY_METADATA_EXPIRES_AT_MASK = std::numeric_limits::max(); constexpr UInt64 KEY_METADATA_IS_DEFAULT_MASK = ~KEY_METADATA_EXPIRES_AT_MASK; - //constexpr size_t KEY_RECENTLY_USED_BIT = 63; - //constexpr size_t KEY_RECENTLY_USED = (1ULL << KEY_RECENTLY_USED_BIT); constexpr size_t KEY_IN_MEMORY_BIT = 63; constexpr size_t KEY_IN_MEMORY = (1ULL << KEY_IN_MEMORY_BIT); constexpr size_t BLOCK_INDEX_BITS = 32; @@ -875,6 +873,12 @@ size_t SSDCachePartition::getElementCount() const return key_to_index.size(); } +size_t SSDCachePartition::getBytesAllocated() const +{ + std::shared_lock lock(rw_lock); + return 16.5 * key_to_index.capacity() + (memory ? memory->size() : 0); +} + PaddedPODArray SSDCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. @@ -1176,6 +1180,15 @@ size_t SSDCacheStorage::getElementCount() const return result; } +size_t SSDCacheStorage::getBytesAllocated() const +{ + size_t result = 0; + std::shared_lock lock(rw_lock); + for (const auto & partition : partitions) + result += partition->getBytesAllocated(); + return result; +} + void SSDCacheStorage::collectGarbage() { // add partitions to queue diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index b209391cafaf..09c5f79da0dc 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -156,6 +156,8 @@ class SSDCachePartition size_t getElementCount() const; + size_t getBytesAllocated() const; + private: template void getImpl(const PaddedPODArray & ids, SetFunc & set, std::vector & found) const; @@ -249,6 +251,8 @@ class SSDCacheStorage double getLoadFactor() const; + size_t getBytesAllocated() const; + private: SSDCachePartition::Attributes createAttributesFromBlock( const Block & block, const size_t begin_column, const std::vector & structure); @@ -277,9 +281,6 @@ class SSDCacheStorage mutable size_t update_error_count = 0; mutable std::chrono::system_clock::time_point backoff_end_time; - // stats - //mutable size_t bytes_allocated = 0; - mutable std::atomic hit_count{0}; mutable std::atomic query_count{0}; }; @@ -307,7 +308,7 @@ class SSDCacheDictionary final : public IDictionary std::string getTypeName() const override { return "SSDCache"; } - size_t getBytesAllocated() const override { return 0; } // TODO: ? + size_t getBytesAllocated() const override { return storage.getBytesAllocated(); } size_t getQueryCount() const override { return storage.getQueryCount(); } diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 748b7f6c4456..0a97c59f5246 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -906,6 +906,13 @@ size_t SSDComplexKeyCachePartition::getElementCount() const return key_to_index.size(); } +size_t SSDComplexKeyCachePartition::getBytesAllocated() const +{ + std::shared_lock lock(rw_lock); + return 16.5 * key_to_index.capacity() + keys_pool.size() + + (keys_buffer_pool ? keys_buffer_pool->size() : 0) + (memory ? memory->size() : 0); +} + PaddedPODArray SSDComplexKeyCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const { throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 2d9409d2053d..b6717d16f656 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -219,6 +219,11 @@ class ComplexKeysPoolImpl buf.ignore(sz); } + size_t size() const + { + return arena.size(); + } + private: A arena; }; @@ -226,98 +231,6 @@ class ComplexKeysPoolImpl using TemporalComplexKeysPool = ComplexKeysPoolImpl; using ComplexKeysPool = ComplexKeysPoolImpl; -template -class ComplexKeyLRUCache -{ - using Iter = typename std::list::iterator; - - struct Cell - { - Iter iter; - V val; - }; - -public: - ComplexKeyLRUCache(size_t max_size_, Pool & keys_pool_) - : max_size(max_size_) - , keys_pool(keys_pool_) - { - } - - void set(K key, V val) - { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) - { - auto & item = cache[key]; - item.iter = queue.insert(std::end(queue), key); - item.val = val; - if (queue.size() > max_size) - { - keys_pool.freeKey(queue.front()); - cache.erase(queue.front()); - queue.pop_front(); - } - } - else - { - queue.erase(it->second.iter); - it->second.iter = queue.insert(std::end(queue), it->first); - it->second.val = val; - } - } - - bool get(K key, V & val) - { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) - return false; - val = it->second.val; - queue.erase(it->second.iter); - it->second.iter = queue.insert(std::end(queue), key); - return true; - } - - bool erase(K key) - { - std::lock_guard lock(mutex); - auto it = cache.find(key); - if (it == std::end(cache)) - return false; - keys_pool.freeKey(it->first); - queue.erase(it->second.iter); - cache.erase(it); - return true; - } - - size_t size() - { - std::lock_guard lock(mutex); - return cache.size(); - } - - auto begin() - { - std::lock_guard lock(mutex); - return std::begin(cache); - } - - auto end() - { - std::lock_guard lock(mutex); - return std::end(cache); - } - -private: - std::unordered_map cache; - std::list queue; - size_t max_size; - Pool & keys_pool; - std::mutex mutex; -}; - struct KeyDeleter { KeyDeleter(ComplexKeysPool & keys_pool_) : keys_pool(keys_pool_) {} @@ -455,6 +368,8 @@ class SSDComplexKeyCachePartition size_t getElementCount() const; + size_t getBytesAllocated() const; + private: size_t append( const KeyRefs & keys, From 5e9cb4060fc0e6f6c086a418457200ffe77a8e86 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 10 May 2020 22:58:11 +0300 Subject: [PATCH 0105/1102] fix --- src/Dictionaries/BucketCache.h | 2 +- .../ExecutableDictionarySource.cpp | 1 - src/Dictionaries/SSDCacheDictionary.cpp | 130 +++++++++--------- src/Dictionaries/SSDCacheDictionary.h | 3 - 4 files changed, 67 insertions(+), 69 deletions(-) diff --git a/src/Dictionaries/BucketCache.h b/src/Dictionaries/BucketCache.h index 9e0e83bf192d..9a231c7a1b58 100644 --- a/src/Dictionaries/BucketCache.h +++ b/src/Dictionaries/BucketCache.h @@ -10,7 +10,7 @@ namespace DB namespace { - size_t nearestPowTwo(size_t x) + inline size_t nearestPowTwo(size_t x) { size_t r = 8; while (x > r) diff --git a/src/Dictionaries/ExecutableDictionarySource.cpp b/src/Dictionaries/ExecutableDictionarySource.cpp index f9d1a426c8e2..34943d62b44c 100644 --- a/src/Dictionaries/ExecutableDictionarySource.cpp +++ b/src/Dictionaries/ExecutableDictionarySource.cpp @@ -228,7 +228,6 @@ void registerDictionarySourceExecutable(DictionarySourceFactory & factory) /// Executable dictionaries may execute arbitrary commands. /// It's OK for dictionaries created by administrator from xml-file, but /// maybe dangerous for dictionaries created from DDL-queries. - check_config = false; if (check_config) throw Exception("Dictionaries with Executable dictionary source is not allowed", ErrorCodes::DICTIONARY_ACCESS_DENIED); diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 210463264bf0..e3053e18e8e2 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -297,7 +297,7 @@ size_t SSDCachePartition::appendBlock( } \ else \ { \ - const auto & values = std::get>(attribute.values); \ + const auto & values = std::get>(attribute.values); /* NOLINT */ \ writeBinary(values[index], *write_buffer); \ } \ } \ @@ -764,10 +764,9 @@ void SSDCachePartition::clearOldestBlocks() if (!metadata.isDefault()) { - for (size_t attr = 0; attr < attributes_structure.size(); ++attr) + for (const auto & attribute : attributes_structure) { - - switch (attributes_structure[attr]) + switch (attribute) { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ @@ -984,6 +983,67 @@ void SSDCacheStorage::has(const PaddedPODArray & ids, ResultArrayType & structure) +{ + SSDCachePartition::Attributes attributes; + + const auto columns = block.getColumns(); + for (size_t i = 0; i < structure.size(); ++i) + { + const auto & column = columns[i + begin_column]; + switch (structure[i]) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + SSDCachePartition::Attribute::Container values(column->size()); \ + memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ + attributes.emplace_back(); \ + attributes.back().type = structure[i]; \ + attributes.back().values = std::move(values); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + { + attributes.emplace_back(); + SSDCachePartition::Attribute::Container values(column->size()); + for (size_t j = 0; j < column->size(); ++j) + { + const auto ref = column->getDataAt(j); + values[j].resize(ref.size); + memcpy(values[j].data(), ref.data, ref.size); + } + attributes.back().type = structure[i]; + attributes.back().values = std::move(values); + } + break; + } + } + + return attributes; +} +} + template void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector & requested_ids, PresentIdHandler && on_updated, AbsentIdHandler && on_id_not_found, @@ -1205,64 +1265,6 @@ void SSDCacheStorage::collectGarbage() } } -SSDCachePartition::Attributes SSDCacheStorage::createAttributesFromBlock( - const Block & block, const size_t begin_column, const std::vector & structure) -{ - SSDCachePartition::Attributes attributes; - - const auto columns = block.getColumns(); - for (size_t i = 0; i < structure.size(); ++i) - { - const auto & column = columns[i + begin_column]; - switch (structure[i]) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - { \ - SSDCachePartition::Attribute::Container values(column->size()); \ - memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ - attributes.emplace_back(); \ - attributes.back().type = structure[i]; \ - attributes.back().values = std::move(values); \ - } \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - { - attributes.emplace_back(); - SSDCachePartition::Attribute::Container values(column->size()); - for (size_t j = 0; j < column->size(); ++j) - { - const auto ref = column->getDataAt(j); - values[j].resize(ref.size); - memcpy(values[j].data(), ref.data, ref.size); - } - attributes.back().type = structure[i]; - attributes.back().values = std::move(values); - } - break; - } - } - - return attributes; -} - SSDCacheDictionary::SSDCacheDictionary( const std::string & name_, const DictionaryStructure & dict_struct_, @@ -1303,8 +1305,8 @@ SSDCacheDictionary::SSDCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ - const auto null_value = std::get(null_values[index]); \ - getItemsNumberImpl( \ + const auto null_value = std::get(null_values[index]); /* NOLINT */ \ + getItemsNumberImpl( /* NOLINT */ \ index, \ ids, \ out, \ diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 09c5f79da0dc..6352d3a25222 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -254,9 +254,6 @@ class SSDCacheStorage size_t getBytesAllocated() const; private: - SSDCachePartition::Attributes createAttributesFromBlock( - const Block & block, const size_t begin_column, const std::vector & structure); - void collectGarbage(); const AttributeTypes attributes_structure; From c26144968ad6202d6003e859bda32ed143dbeac9 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 11 May 2020 17:44:46 +0300 Subject: [PATCH 0106/1102] fix --- src/Dictionaries/BucketCache.h | 2 +- src/Dictionaries/SSDCacheDictionary.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Dictionaries/BucketCache.h b/src/Dictionaries/BucketCache.h index 9a231c7a1b58..262aef21019a 100644 --- a/src/Dictionaries/BucketCache.h +++ b/src/Dictionaries/BucketCache.h @@ -162,7 +162,7 @@ class BucketCacheIndex size_t getCellIndex(const K key, const size_t bucket) const { const size_t pos = getPosition(bucket); - for (size_t idx = 0; idx < bucket_size; ++idx) + for (int idx = 7; idx >= 0; --idx) { const size_t cur = ((pos + 1 + idx) & pos_mask); if (cells[bucket * bucket_size + cur].index.exists() && diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index e3053e18e8e2..a065b3671011 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -418,10 +418,10 @@ void SSDCachePartition::flush() throwFromErrnoWithPath("Cannot fsync " + path + BIN_FILE_EXT, path + BIN_FILE_EXT, ErrorCodes::CANNOT_FSYNC); /// commit changes in index - for (size_t row = 0; row < ids.size(); ++row) + for (const auto & id : ids) { Index index; - if (key_to_index.get(ids[row], index)) + if (key_to_index.get(id, index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { @@ -429,7 +429,7 @@ void SSDCachePartition::flush() // Poco::Logger::get("pt").information("block: " + std::to_string(index.getBlockId()) + " " + std::to_string(current_file_block_id) + " "); index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); } - key_to_index.set(ids[row], index); + key_to_index.set(id, index); } } @@ -1307,10 +1307,10 @@ SSDCacheDictionary::SSDCacheDictionary( checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ const auto null_value = std::get(null_values[index]); /* NOLINT */ \ getItemsNumberImpl( /* NOLINT */ \ - index, \ - ids, \ - out, \ - [&](const size_t) { return null_value; }); \ + index, /* NOLINT */ \ + ids, /* NOLINT */ \ + out, /* NOLINT */ \ + [&](const size_t) { return null_value; }); /* NOLINT */ \ } DECLARE(UInt8) From 4739b87732449af62b8b7dff26bc8103075d859d Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Thu, 14 May 2020 05:14:50 +0300 Subject: [PATCH 0107/1102] Add -Distinct combinator --- .../AggregateFunctionDistinct.cpp | 53 +++++++++ .../AggregateFunctionDistinct.h | 108 ++++++++++++++++++ .../registerAggregateFunctions.cpp | 1 + .../registerAggregateFunctions.h | 1 + 4 files changed, 163 insertions(+) create mode 100644 src/AggregateFunctions/AggregateFunctionDistinct.cpp create mode 100644 src/AggregateFunctions/AggregateFunctionDistinct.h diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp new file mode 100644 index 000000000000..d477a04568f2 --- /dev/null +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include "registerAggregateFunctions.h" + +namespace DB +{ + + namespace ErrorCodes + { + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + } + + class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombinator + { + public: + String getName() const override { return "Distinct"; } + + DataTypes transformArguments(const DataTypes & arguments) const override + { + if (arguments.empty()) + throw Exception("Incorrect number of arguments for aggregate function with " + getName() + " suffix", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + +// return DataTypes(arguments.begin(), std::prev(arguments.end())); + DataTypes nested_arguments; + for (const auto & type : arguments) + { + nested_arguments.push_back(type); +// if (const DataTypeArray * array = typeid_cast(type.get())) +// nested_arguments.push_back(array->getNestedType()); +// else +// throw Exception("Illegal type " + type->getName() + " of argument" +// " for aggregate function with " + getName() + " suffix. Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + + return nested_arguments; + } + + AggregateFunctionPtr transformAggregateFunction( + const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array &) const override + { + return std::make_shared(nested_function, arguments); + } + }; + + void registerAggregateFunctionCombinatorDistinct(AggregateFunctionCombinatorFactory & factory) + { + factory.registerCombinator(std::make_shared()); + } + +} diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h new file mode 100644 index 000000000000..160e113d23bf --- /dev/null +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} + +/** Adaptor for aggregate functions. + * Adding -Distinct suffix to aggregate function +**/ + +class AggregateFunctionDistinct final : public IAggregateFunctionHelper { +private: + mutable std::mutex mutex; + AggregateFunctionPtr nested_func; + mutable HashSet< + UInt128, + UInt128TrivialHash, + HashTableGrower<3>, + HashTableAllocatorWithStackMemory> storage; + +public: + AggregateFunctionDistinct(AggregateFunctionPtr nested, const DataTypes & arguments) + : IAggregateFunctionHelper(arguments, {}) + , nested_func(nested) + { + if (arguments.empty()) + throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + + String getName() const override { + return nested_func->getName() + "Distinct"; + } + + DataTypePtr getReturnType() const override { + return nested_func->getReturnType(); + } + + void create(AggregateDataPtr place) const override + { + nested_func->create(place); + } + + void destroy(AggregateDataPtr place) const noexcept override { + nested_func->destroy(place); + } + + size_t sizeOfData() const override + { + return nested_func->sizeOfData(); + } + + size_t alignOfData() const override + { + return nested_func->alignOfData(); + } + + bool hasTrivialDestructor() const override { + return nested_func->hasTrivialDestructor(); + } + + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override { + UInt128 key; + SipHash hash; + columns[0]->updateHashWithValue(row_num, hash); + hash.get128(key.low, key.high); + { + std::lock_guard lock(mutex); + if (!storage.insert(key).second) { + return; + } + } + nested_func->add(place, columns, row_num, arena); + } + + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { + nested_func->merge(place, rhs, arena); + } + + void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { + nested_func->serialize(place, buf); + } + + void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { + nested_func->deserialize(place, buf, arena); + } + + void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override { + nested_func->insertResultInto(place, to); + } + + bool allocatesMemoryInArena() const override { + return nested_func->allocatesMemoryInArena(); + } +}; + +} diff --git a/src/AggregateFunctions/registerAggregateFunctions.cpp b/src/AggregateFunctions/registerAggregateFunctions.cpp index a9ab1d4f8eac..a8d0cf6e37ca 100644 --- a/src/AggregateFunctions/registerAggregateFunctions.cpp +++ b/src/AggregateFunctions/registerAggregateFunctions.cpp @@ -58,6 +58,7 @@ void registerAggregateFunctions() registerAggregateFunctionCombinatorNull(factory); registerAggregateFunctionCombinatorOrFill(factory); registerAggregateFunctionCombinatorResample(factory); + registerAggregateFunctionCombinatorDistinct(factory); } } diff --git a/src/AggregateFunctions/registerAggregateFunctions.h b/src/AggregateFunctions/registerAggregateFunctions.h index 88cdf4a504d2..981273141f95 100644 --- a/src/AggregateFunctions/registerAggregateFunctions.h +++ b/src/AggregateFunctions/registerAggregateFunctions.h @@ -45,6 +45,7 @@ void registerAggregateFunctionCombinatorMerge(AggregateFunctionCombinatorFactory void registerAggregateFunctionCombinatorNull(AggregateFunctionCombinatorFactory &); void registerAggregateFunctionCombinatorOrFill(AggregateFunctionCombinatorFactory &); void registerAggregateFunctionCombinatorResample(AggregateFunctionCombinatorFactory &); +void registerAggregateFunctionCombinatorDistinct(AggregateFunctionCombinatorFactory &); void registerAggregateFunctions(); From 6e2b93e5af00317f9612fbc9535cd6c8e00a5406 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Thu, 14 May 2020 22:37:53 +0300 Subject: [PATCH 0108/1102] Stylefix --- .../AggregateFunctionDistinct.h | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 160e113d23bf..bab78aa88bfb 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -11,24 +11,35 @@ namespace DB { -namespace ErrorCodes -{ +namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } +struct AggregateFunctionDistinctData { + using Key = UInt128; + + HashSet< + Key, + UInt128TrivialHash, + HashTableGrower<3>, + HashTableAllocatorWithStackMemory + > data; + std::mutex mutex; + + bool ALWAYS_INLINE TryToInsert(const Key& key) { + std::lock_guard lock(mutex); + return data.insert(key).second; + } +}; + /** Adaptor for aggregate functions. * Adding -Distinct suffix to aggregate function **/ class AggregateFunctionDistinct final : public IAggregateFunctionHelper { private: - mutable std::mutex mutex; AggregateFunctionPtr nested_func; - mutable HashSet< - UInt128, - UInt128TrivialHash, - HashTableGrower<3>, - HashTableAllocatorWithStackMemory> storage; + mutable AggregateFunctionDistinctData storage; public: AggregateFunctionDistinct(AggregateFunctionPtr nested, const DataTypes & arguments) @@ -71,17 +82,14 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperupdateHashWithValue(row_num, hash); + + UInt128 key; hash.get128(key.low, key.high); - { - std::lock_guard lock(mutex); - if (!storage.insert(key).second) { - return; - } - } - nested_func->add(place, columns, row_num, arena); + + if (storage.TryToInsert(key)) + nested_func->add(place, columns, row_num, arena); } void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { From da81c56b5e5a33e1b36c5949775f22c5a78c350f Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Thu, 14 May 2020 22:46:01 +0300 Subject: [PATCH 0109/1102] Delete extra lines --- src/AggregateFunctions/AggregateFunctionDistinct.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index d477a04568f2..369b4a5f7dfc 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -23,16 +23,9 @@ namespace DB throw Exception("Incorrect number of arguments for aggregate function with " + getName() + " suffix", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); -// return DataTypes(arguments.begin(), std::prev(arguments.end())); DataTypes nested_arguments; - for (const auto & type : arguments) - { + for (const auto & type : arguments) { nested_arguments.push_back(type); -// if (const DataTypeArray * array = typeid_cast(type.get())) -// nested_arguments.push_back(array->getNestedType()); -// else -// throw Exception("Illegal type " + type->getName() + " of argument" -// " for aggregate function with " + getName() + " suffix. Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } return nested_arguments; From 95677432e5326267ca5639aa5d92d4a4caaab4ac Mon Sep 17 00:00:00 2001 From: bobrovskij artemij Date: Thu, 14 May 2020 02:20:45 +0300 Subject: [PATCH 0110/1102] MongoDB engine (read-only) --- src/Access/AccessType.h | 1 + src/Dictionaries/MongoDBDictionarySource.cpp | 44 ++---- src/Dictionaries/MongoDBDictionarySource.h | 7 + src/Storages/StorageMongoDB.cpp | 133 +++++++++++++++++++ src/Storages/StorageMongoDB.h | 60 +++++++++ src/Storages/registerStorages.cpp | 2 + src/Storages/registerStorages.h | 2 + 7 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 src/Storages/StorageMongoDB.cpp create mode 100644 src/Storages/StorageMongoDB.h diff --git a/src/Access/AccessType.h b/src/Access/AccessType.h index d0665a6e55f2..db31d87cfa64 100644 --- a/src/Access/AccessType.h +++ b/src/Access/AccessType.h @@ -150,6 +150,7 @@ enum class AccessType M(FILE, "", GLOBAL, SOURCES) \ M(URL, "", GLOBAL, SOURCES) \ M(REMOTE, "", GLOBAL, SOURCES) \ + M(MONGO, "", GLOBAL, SOURCES) \ M(MYSQL, "", GLOBAL, SOURCES) \ M(ODBC, "", GLOBAL, SOURCES) \ M(JDBC, "", GLOBAL, SOURCES) \ diff --git a/src/Dictionaries/MongoDBDictionarySource.cpp b/src/Dictionaries/MongoDBDictionarySource.cpp index 7247d8a4613a..da1db35437d7 100644 --- a/src/Dictionaries/MongoDBDictionarySource.cpp +++ b/src/Dictionaries/MongoDBDictionarySource.cpp @@ -5,32 +5,19 @@ namespace DB { -namespace ErrorCodes -{ - extern const int SUPPORT_IS_DISABLED; -} -void registerDictionarySourceMongoDB(DictionarySourceFactory & factory) -{ - auto create_table_source = [=](const DictionaryStructure & dict_struct, - const Poco::Util::AbstractConfiguration & config, - const std::string & config_prefix, - Block & sample_block, - const Context & /* context */, - bool /* check_config */) -> DictionarySourcePtr { -#if USE_POCO_MONGODB - return std::make_unique(dict_struct, config, config_prefix + ".mongodb", sample_block); -#else - (void)dict_struct; - (void)config; - (void)config_prefix; - (void)sample_block; - throw Exception{"Dictionary source of type `mongodb` is disabled because poco library was built without mongodb support.", - ErrorCodes::SUPPORT_IS_DISABLED}; -#endif - }; - factory.registerSource("mongodb", create_table_source); -} + void registerDictionarySourceMongoDB(DictionarySourceFactory & factory) + { + auto create_table_source = [=](const DictionaryStructure & dict_struct, + const Poco::Util::AbstractConfiguration & config, + const std::string & config_prefix, + Block & sample_block, + const Context & /* context */, + bool /* check_config */) -> DictionarySourcePtr { + return std::make_unique(dict_struct, config, config_prefix + ".mongodb", sample_block); + }; + factory.registerSource("mongodb", create_table_source); + } } @@ -69,8 +56,7 @@ static const UInt64 max_block_size = 8192; # if POCO_VERSION < 0x01070800 /// See https://pocoproject.org/forum/viewtopic.php?f=10&t=6326&p=11426&hilit=mongodb+auth#p11485 -static void -authenticate(Poco::MongoDB::Connection & connection, const std::string & database, const std::string & user, const std::string & password) +void authenticate(Poco::MongoDB::Connection & connection, const std::string & database, const std::string & user, const std::string & password) { Poco::MongoDB::Database db(database); @@ -238,8 +224,7 @@ MongoDBDictionarySource::MongoDBDictionarySource(const MongoDBDictionarySource & MongoDBDictionarySource::~MongoDBDictionarySource() = default; -static std::unique_ptr -createCursor(const std::string & database, const std::string & collection, const Block & sample_block_to_select) +std::unique_ptr createCursor(const std::string & database, const std::string & collection, const Block & sample_block_to_select) { auto cursor = std::make_unique(database, collection); @@ -249,7 +234,6 @@ createCursor(const std::string & database, const std::string & collection, const for (const auto & column : sample_block_to_select) cursor->query().returnFieldSelector().add(column.name, 1); - return cursor; } diff --git a/src/Dictionaries/MongoDBDictionarySource.h b/src/Dictionaries/MongoDBDictionarySource.h index bf4669248dcb..b4339d40a95f 100644 --- a/src/Dictionaries/MongoDBDictionarySource.h +++ b/src/Dictionaries/MongoDBDictionarySource.h @@ -17,6 +17,7 @@ namespace Util namespace MongoDB { class Connection; + class Cursor; } } @@ -28,6 +29,12 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; } +# if POCO_VERSION < 0x01070800 +void authenticate(Poco::MongoDB::Connection & connection, const std::string & database, const std::string & user, const std::string & password); +# endif + +std::unique_ptr createCursor(const std::string & database, const std::string & collection, const Block & sample_block_to_select); + /// Allows loading dictionaries from a MongoDB collection class MongoDBDictionarySource final : public IDictionarySource { diff --git a/src/Storages/StorageMongoDB.cpp b/src/Storages/StorageMongoDB.cpp new file mode 100644 index 000000000000..68a3c3eb1da9 --- /dev/null +++ b/src/Storages/StorageMongoDB.cpp @@ -0,0 +1,133 @@ +#include "StorageMongoDB.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int BAD_ARGUMENTS; + extern const int MONGODB_CANNOT_AUTHENTICATE; +} + +StorageMongoDB::StorageMongoDB( + const StorageID & table_id_, + const std::string & host_, + short unsigned int port_, + const std::string & database_name_, + const std::string & collection_name_, + const std::string & username_, + const std::string & password_, + const ColumnsDescription & columns_, + const ConstraintsDescription & constraints_, + const Context & context_) + : IStorage(table_id_) + , host(host_) + , port(port_) + , database_name(database_name_) + , collection_name(collection_name_) + , username(username_) + , password(password_) + , global_context(context_) + , connection{std::make_shared(host, port)} +{ + setColumns(columns_); + setConstraints(constraints_); +} + + +Pipes StorageMongoDB::read( + const Names & column_names, + const SelectQueryInfo & /*query_info*/, + const Context & /*context*/, + QueryProcessingStage::Enum /*processed_stage*/, + size_t max_block_size, + unsigned) +{ + check(column_names); + +#if POCO_VERSION >= 0x01070800 + Poco::MongoDB::Database poco_db(database_name); + if (!poco_db.authenticate(*connection, username, password, Poco::MongoDB::Database::AUTH_SCRAM_SHA1)) + throw Exception("Cannot authenticate in MongoDB, incorrect user or password", ErrorCodes::MONGODB_CANNOT_AUTHENTICATE); +#else + authenticate(*connection, database_name, username, password); +#endif + + Block sample_block; + for (const String & column_name : column_names) + { + auto column_data = getColumn(column_name); + sample_block.insert({ column_data.type, column_data.name }); + } + + Pipes pipes; + pipes.emplace_back(std::make_shared( + std::make_shared(connection, createCursor(database_name, collection_name, sample_block), sample_block, max_block_size))); + + return pipes; +} + +void registerStorageMongoDB(StorageFactory & factory) +{ + factory.registerStorage("MongoDB", [](const StorageFactory::Arguments & args) + { + ASTs & engine_args = args.engine_args; + + if (engine_args.size() != 5) + throw Exception( + "Storage MongoDB requires 5 parameters: MongoDB('host:port', database, collection, 'user', 'password').", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + for (auto & engine_arg : engine_args) + engine_arg = evaluateConstantExpressionOrIdentifierAsLiteral(engine_arg, args.local_context); + + /// 27017 is the default MongoDB port. + auto parsed_host_port = parseAddress(engine_args[0]->as().value.safeGet(), 27017); + + const String & remote_database = engine_args[1]->as().value.safeGet(); + const String & collection = engine_args[2]->as().value.safeGet(); + const String & username = engine_args[3]->as().value.safeGet(); + const String & password = engine_args[4]->as().value.safeGet(); + + + return StorageMongoDB::create( + args.table_id, + parsed_host_port.first, + parsed_host_port.second, + remote_database, + collection, + username, + password, + args.columns, + args.constraints, + args.context); + }, + { + .source_access_type = AccessType::MONGO, + }); +} + +} diff --git a/src/Storages/StorageMongoDB.h b/src/Storages/StorageMongoDB.h new file mode 100644 index 000000000000..c037972f36b5 --- /dev/null +++ b/src/Storages/StorageMongoDB.h @@ -0,0 +1,60 @@ + +#pragma once + +#include "config_core.h" + +#include + +#include +#include +#include +#include + + +namespace DB +{ +/* Implements storage in the MongoDB database. + * Use ENGINE = mysql(host_port, database_name, table_name, user_name, password) + * Read only. + */ + +class StorageMongoDB final : public ext::shared_ptr_helper, public IStorage +{ + friend struct ext::shared_ptr_helper; +public: + StorageMongoDB( + const StorageID & table_id_, + const std::string & host_, + short unsigned int port_, + const std::string & database_name_, + const std::string & collection_name_, + const std::string & username_, + const std::string & password_, + const ColumnsDescription & columns_, + const ConstraintsDescription & constraints_, + const Context & context_); + + std::string getName() const override { return "MongoDB"; } + + Pipes read( + const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) override; + + +private: + std::string host; + short unsigned int port; + std::string database_name; + std::string collection_name; + std::string username; + std::string password; + + Context global_context; + std::shared_ptr connection; +}; + +} diff --git a/src/Storages/registerStorages.cpp b/src/Storages/registerStorages.cpp index f5fab52285dd..8201bbb8ffa3 100644 --- a/src/Storages/registerStorages.cpp +++ b/src/Storages/registerStorages.cpp @@ -46,6 +46,8 @@ void registerStorages() registerStorageMySQL(factory); #endif + registerStorageMongoDB(factory); + #if USE_RDKAFKA registerStorageKafka(factory); #endif diff --git a/src/Storages/registerStorages.h b/src/Storages/registerStorages.h index 63a758f5b38b..aee2b3edd9e7 100644 --- a/src/Storages/registerStorages.h +++ b/src/Storages/registerStorages.h @@ -40,6 +40,8 @@ void registerStorageJDBC(StorageFactory & factory); void registerStorageMySQL(StorageFactory & factory); #endif +void registerStorageMongoDB(StorageFactory & factory); + #if USE_RDKAFKA void registerStorageKafka(StorageFactory & factory); #endif From 13224c22ab7c7078e4a41457a72bd971792d1dc9 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Fri, 15 May 2020 05:02:57 +0300 Subject: [PATCH 0111/1102] Stylecheck fix --- .../AggregateFunctionDistinct.cpp | 3 +- .../AggregateFunctionDistinct.h | 36 ++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index 369b4a5f7dfc..b01bd2226c79 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -24,7 +24,8 @@ namespace DB ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); DataTypes nested_arguments; - for (const auto & type : arguments) { + for (const auto & type : arguments) + { nested_arguments.push_back(type); } diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index bab78aa88bfb..5580cc3b4dfd 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -11,11 +11,13 @@ namespace DB { -namespace ErrorCodes { +namespace ErrorCodes +{ extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } -struct AggregateFunctionDistinctData { +struct AggregateFunctionDistinctData +{ using Key = UInt128; HashSet< @@ -36,7 +38,8 @@ struct AggregateFunctionDistinctData { * Adding -Distinct suffix to aggregate function **/ -class AggregateFunctionDistinct final : public IAggregateFunctionHelper { +class AggregateFunctionDistinct final : public IAggregateFunctionHelper +{ private: AggregateFunctionPtr nested_func; mutable AggregateFunctionDistinctData storage; @@ -50,11 +53,13 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelpergetName() + "Distinct"; } - DataTypePtr getReturnType() const override { + DataTypePtr getReturnType() const override + { return nested_func->getReturnType(); } @@ -77,11 +82,13 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperalignOfData(); } - bool hasTrivialDestructor() const override { + bool hasTrivialDestructor() const override + { return nested_func->hasTrivialDestructor(); } - void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override { + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + { SipHash hash; columns[0]->updateHashWithValue(row_num, hash); @@ -92,23 +99,28 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperadd(place, columns, row_num, arena); } - void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override + { nested_func->merge(place, rhs, arena); } - void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { + void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override + { nested_func->serialize(place, buf); } - void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { + void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override + { nested_func->deserialize(place, buf, arena); } - void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override { + void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override + { nested_func->insertResultInto(place, to); } - bool allocatesMemoryInArena() const override { + bool allocatesMemoryInArena() const override + { return nested_func->allocatesMemoryInArena(); } }; From 7c6322c5b03232a0dfd603a54b2e0037bf817122 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Sat, 16 May 2020 02:06:25 +0300 Subject: [PATCH 0112/1102] Add support for many columns --- src/AggregateFunctions/AggregateFunctionDistinct.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 5580cc3b4dfd..b87183f15d67 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -42,12 +42,13 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelper(arguments, {}) - , nested_func(nested) + , nested_func(nested), num_arguments(arguments.size()) { if (arguments.empty()) throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); @@ -90,7 +91,8 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperupdateHashWithValue(row_num, hash); + for (size_t i = 0; i < num_arguments; ++i) + columns[i]->updateHashWithValue(row_num, hash); UInt128 key; hash.get128(key.low, key.high); From 8c6f687010a925955c63f201bf52a15baa699d08 Mon Sep 17 00:00:00 2001 From: bobrovskij artemij Date: Sat, 16 May 2020 01:53:09 +0300 Subject: [PATCH 0113/1102] build/style fix --- src/Storages/StorageMongoDB.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Storages/StorageMongoDB.cpp b/src/Storages/StorageMongoDB.cpp index 68a3c3eb1da9..2f27042b162c 100644 --- a/src/Storages/StorageMongoDB.cpp +++ b/src/Storages/StorageMongoDB.cpp @@ -28,7 +28,6 @@ namespace DB namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int BAD_ARGUMENTS; extern const int MONGODB_CANNOT_AUTHENTICATE; } @@ -79,7 +78,7 @@ Pipes StorageMongoDB::read( Block sample_block; for (const String & column_name : column_names) { - auto column_data = getColumn(column_name); + auto column_data = getColumns().getPhysical(column_name); sample_block.insert({ column_data.type, column_data.name }); } From fa38cf780c0071e1d05e4e79cf0face02628516e Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Sat, 16 May 2020 03:02:55 +0300 Subject: [PATCH 0114/1102] Add tests for -Distinct combinator --- tests/queries/0_stateless/01259_combinator_distinct.reference | 4 ++++ tests/queries/0_stateless/01259_combinator_distinct.sql | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 tests/queries/0_stateless/01259_combinator_distinct.reference create mode 100644 tests/queries/0_stateless/01259_combinator_distinct.sql diff --git a/tests/queries/0_stateless/01259_combinator_distinct.reference b/tests/queries/0_stateless/01259_combinator_distinct.reference new file mode 100644 index 000000000000..34d13676466f --- /dev/null +++ b/tests/queries/0_stateless/01259_combinator_distinct.reference @@ -0,0 +1,4 @@ +499500 +78 +[0,1,2,3,4,5,6,7,8,9,10,11,12] +5.669227916063075e-17 diff --git a/tests/queries/0_stateless/01259_combinator_distinct.sql b/tests/queries/0_stateless/01259_combinator_distinct.sql new file mode 100644 index 000000000000..e3c4bb114a33 --- /dev/null +++ b/tests/queries/0_stateless/01259_combinator_distinct.sql @@ -0,0 +1,4 @@ +SELECT sum(DISTINCT x) FROM (SELECT number AS x FROM system.numbers LIMIT 1000); +SELECT sum(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); +SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); +SELECT corrStableDistinct(DISTINCT x, y) FROM (SELECT number % 11 AS x, number % 13 AS y FROM system.numbers LIMIT 1000); \ No newline at end of file From aeb195950c65f7e4d9ab8ec374c599e8e1466442 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Sat, 16 May 2020 03:15:44 +0300 Subject: [PATCH 0115/1102] Checkstyle fix --- .../AggregateFunctionDistinct.cpp | 55 +++++++++---------- .../AggregateFunctionDistinct.h | 3 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index b01bd2226c79..820c2f0f72c6 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -6,42 +6,41 @@ namespace DB { - namespace ErrorCodes - { - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int ILLEGAL_TYPE_OF_ARGUMENT; - } +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} - class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombinator +class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombinator +{ +public: + String getName() const override { return "Distinct"; } + + DataTypes transformArguments(const DataTypes & arguments) const override { - public: - String getName() const override { return "Distinct"; } + if (arguments.empty()) + throw Exception("Incorrect number of arguments for aggregate function with " + getName() + " suffix", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - DataTypes transformArguments(const DataTypes & arguments) const override + DataTypes nested_arguments; + for (const auto & type : arguments) { - if (arguments.empty()) - throw Exception("Incorrect number of arguments for aggregate function with " + getName() + " suffix", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - DataTypes nested_arguments; - for (const auto & type : arguments) - { - nested_arguments.push_back(type); - } - - return nested_arguments; + nested_arguments.push_back(type); } - AggregateFunctionPtr transformAggregateFunction( - const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array &) const override - { - return std::make_shared(nested_function, arguments); - } - }; + return nested_arguments; + } - void registerAggregateFunctionCombinatorDistinct(AggregateFunctionCombinatorFactory & factory) + AggregateFunctionPtr transformAggregateFunction( + const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array &) const override { - factory.registerCombinator(std::make_shared()); + return std::make_shared(nested_function, arguments); } +}; + +void registerAggregateFunctionCombinatorDistinct(AggregateFunctionCombinatorFactory & factory) +{ + factory.registerCombinator(std::make_shared()); +} } diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index b87183f15d67..cc4c52ea5ffc 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -28,7 +28,8 @@ struct AggregateFunctionDistinctData > data; std::mutex mutex; - bool ALWAYS_INLINE TryToInsert(const Key& key) { + bool ALWAYS_INLINE TryToInsert(const Key& key) + { std::lock_guard lock(mutex); return data.insert(key).second; } From f4369381c97fca9394bd9f0673a5bb91b0983d29 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Sat, 16 May 2020 18:00:33 +0300 Subject: [PATCH 0116/1102] Fix build --- src/AggregateFunctions/AggregateFunctionDistinct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index cc4c52ea5ffc..e7ccbc62c576 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -117,7 +117,7 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperdeserialize(place, buf, arena); } - void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to) const override { nested_func->insertResultInto(place, to); } From a055e3308713e4c13f997b4a771015021b5a5ab7 Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 07:28:55 +0300 Subject: [PATCH 0117/1102] Add libevent & AMQP-CPP libraries --- .gitmodules | 6 + CMakeLists.txt | 2 + cmake/find/amqpcpp.cmake | 20 + cmake/find/libevent.cmake | 22 + contrib/AMQP-CPP | 1 + contrib/CMakeLists.txt | 8 + contrib/amqpcpp-cmake/CMakeLists.txt | 44 ++ contrib/libevent | 1 + contrib/libevent-cmake/CMakeLists.txt | 42 ++ contrib/libevent-cmake/evconfig-private.h | 39 ++ contrib/libevent-cmake/event-config.h | 516 ++++++++++++++++++++++ src/CMakeLists.txt | 7 + 12 files changed, 708 insertions(+) create mode 100644 cmake/find/amqpcpp.cmake create mode 100644 cmake/find/libevent.cmake create mode 160000 contrib/AMQP-CPP create mode 100644 contrib/amqpcpp-cmake/CMakeLists.txt create mode 160000 contrib/libevent create mode 100644 contrib/libevent-cmake/CMakeLists.txt create mode 100644 contrib/libevent-cmake/evconfig-private.h create mode 100644 contrib/libevent-cmake/event-config.h diff --git a/.gitmodules b/.gitmodules index f7a16b84d379..bc4654e3b61a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -157,3 +157,9 @@ [submodule "contrib/openldap"] path = contrib/openldap url = https://github.com/openldap/openldap.git +[submodule "contrib/AMQP-CPP"] + path = contrib/AMQP-CPP + url = https://github.com/CopernicaMarketingSoftware/AMQP-CPP.git +[submodule "contrib/libevent"] + path = contrib/libevent + url = https://github.com/libevent/libevent.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 53dfd1df1cb9..5e9a642c9031 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -345,6 +345,8 @@ include (cmake/find/sparsehash.cmake) include (cmake/find/re2.cmake) include (cmake/find/libgsasl.cmake) include (cmake/find/rdkafka.cmake) +include (cmake/find/libevent.cmake) +include (cmake/find/amqpcpp.cmake) include (cmake/find/capnp.cmake) include (cmake/find/llvm.cmake) include (cmake/find/opencl.cmake) diff --git a/cmake/find/amqpcpp.cmake b/cmake/find/amqpcpp.cmake new file mode 100644 index 000000000000..147824ff3958 --- /dev/null +++ b/cmake/find/amqpcpp.cmake @@ -0,0 +1,20 @@ +SET(ENABLE_AMQPCPP 1) +if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/AMQP-CPP/CMakeLists.txt") + message (WARNING "submodule contrib/AMQP-CPP is missing. to fix try run: \n git submodule update --init --recursive") + set (ENABLE_AMQPCPP 0) +endif () + +if (ENABLE_AMQPCPP) + + set (USE_AMQPCPP 1) + set (AMQPCPP_LIBRARY AMQP-CPP) + + set (AMQPCPP_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/AMQP-CPP/include") + + list (APPEND AMQPCPP_INCLUDE_DIR + "${ClickHouse_SOURCE_DIR}/contrib/AMQP-CPP/include" + "${ClickHouse_SOURCE_DIR}/contrib/AMQP-CPP") + +endif() + +message (STATUS "Using AMQP-CPP=${USE_AMQPCPP}: ${AMQPCPP_INCLUDE_DIR} : ${AMQPCPP_LIBRARY}") diff --git a/cmake/find/libevent.cmake b/cmake/find/libevent.cmake new file mode 100644 index 000000000000..2f714b43475d --- /dev/null +++ b/cmake/find/libevent.cmake @@ -0,0 +1,22 @@ +SET(ENABLE_LIBEVENT 1) +if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/libevent/CMakeLists.txt") + message (WARNING "submodule contrib/libevent is missing. to fix try run: + \n git submodule update --init --recursive") + + set (ENABLE_LIBEVENT 0) +endif () + +if (ENABLE_LIBEVENT) + + set (USE_LIBEVENT 1) + set (LIBEVENT_LIBRARY LIBEVENT) + + set (LIBEVENT_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/libevent") + + list (APPEND LIBEVENT_INCLUDE_DIR + "${ClickHouse_SOURCE_DIR}/contrib/libevent/include/event2" + "${ClickHouse_SOURCE_DIR}/contrib/libevent/include") + +endif() + +message (STATUS "Using libevent=${USE_LIBEVENT}: ${LIBEVENT_INCLUDE_DIR} : ${LIBEVENT_LIBRARY}") diff --git a/contrib/AMQP-CPP b/contrib/AMQP-CPP new file mode 160000 index 000000000000..1c08399ab0ab --- /dev/null +++ b/contrib/AMQP-CPP @@ -0,0 +1 @@ +Subproject commit 1c08399ab0ab9e4042ef8e2bbe9e208e5dcbc13b diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 1031285eac7a..ea90f7129f25 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -317,3 +317,11 @@ endif() if (USE_FASTOPS) add_subdirectory (fastops-cmake) endif() + +if (USE_AMQPCPP) + add_subdirectory (amqpcpp-cmake) +endif() + +if (USE_LIBEVENT) + add_subdirectory(libevent-cmake) +endif() diff --git a/contrib/amqpcpp-cmake/CMakeLists.txt b/contrib/amqpcpp-cmake/CMakeLists.txt new file mode 100644 index 000000000000..eae3122e216f --- /dev/null +++ b/contrib/amqpcpp-cmake/CMakeLists.txt @@ -0,0 +1,44 @@ +set (LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/AMQP-CPP) + +set (SRCS + ${LIBRARY_DIR}/src/array.cpp + ${LIBRARY_DIR}/src/channel.cpp + ${LIBRARY_DIR}/src/channelimpl.cpp + ${LIBRARY_DIR}/src/connectionimpl.cpp + ${LIBRARY_DIR}/src/deferredcancel.cpp + ${LIBRARY_DIR}/src/deferredconfirm.cpp + ${LIBRARY_DIR}/src/deferredconsumer.cpp + ${LIBRARY_DIR}/src/deferredextreceiver.cpp + ${LIBRARY_DIR}/src/deferredget.cpp + ${LIBRARY_DIR}/src/deferredpublisher.cpp + ${LIBRARY_DIR}/src/deferredreceiver.cpp + ${LIBRARY_DIR}/src/field.cpp + ${LIBRARY_DIR}/src/flags.cpp + ${LIBRARY_DIR}/src/linux_tcp/openssl.cpp + ${LIBRARY_DIR}/src/linux_tcp/tcpconnection.cpp + ${LIBRARY_DIR}/src/receivedframe.cpp + ${LIBRARY_DIR}/src/table.cpp + ${LIBRARY_DIR}/src/watchable.cpp +) + +add_library(amqp-cpp ${SRCS}) + +target_compile_options (amqp-cpp + PUBLIC + -Wno-old-style-cast + -Wno-inconsistent-missing-destructor-override + -Wno-deprecated + -Wno-unused-parameter + -Wno-shadow + -Wno-tautological-type-limit-compare + -Wno-extra-semi +# NOTE: disable all warnings at last because the warning: + # "conversion function converting 'XXX' to itself will never be used" + # doesn't have it's own diagnostic flag yet. + -w +) + +target_include_directories (amqp-cpp PUBLIC ${LIBRARY_DIR}/include) + +target_link_libraries (amqp-cpp PUBLIC libevent ssl) + diff --git a/contrib/libevent b/contrib/libevent new file mode 160000 index 000000000000..eee26deed38f --- /dev/null +++ b/contrib/libevent @@ -0,0 +1 @@ +Subproject commit eee26deed38fc7a6b6780b54628b007a2810efcd diff --git a/contrib/libevent-cmake/CMakeLists.txt b/contrib/libevent-cmake/CMakeLists.txt new file mode 100644 index 000000000000..f99bc2214827 --- /dev/null +++ b/contrib/libevent-cmake/CMakeLists.txt @@ -0,0 +1,42 @@ +set(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/libevent) + +set(SRCS + ${LIBRARY_DIR}/buffer.c + ${LIBRARY_DIR}/bufferevent_filter.c + ${LIBRARY_DIR}/bufferevent_pair.c + ${LIBRARY_DIR}/bufferevent_ratelim.c + ${LIBRARY_DIR}/bufferevent_sock.c + ${LIBRARY_DIR}/bufferevent.c + ${LIBRARY_DIR}/event.c + ${LIBRARY_DIR}/evmap.c + ${LIBRARY_DIR}/evthread.c + ${LIBRARY_DIR}/evutil_rand.c + ${LIBRARY_DIR}/evutil_time.c + ${LIBRARY_DIR}/evutil.c + ${LIBRARY_DIR}/listener.c + ${LIBRARY_DIR}/log.c + ${LIBRARY_DIR}/signal.c + ${LIBRARY_DIR}/strlcpy.c + ${LIBRARY_DIR}/watch.c +) + +if (OS_LINUX) + list (APPEND SRCS + ${LIBRARY_DIR}/epoll.c + ${LIBRARY_DIR}/poll.c + ${LIBRARY_DIR}/select.c + ) + +elseif (OS_DARWIN) + list (APPEND SRCS ${LIBRARY_DIR}/kqueue.c) +endif () + +add_library(libevent ${SRCS}) + +target_compile_options (libevent PUBLIC -Wno-reserved-id-macro) + +if (OS_LINUX) + target_include_directories (libevent PUBLIC linux) +endif () + +target_include_directories (libevent PUBLIC ${LIBRARY_DIR}/include) diff --git a/contrib/libevent-cmake/evconfig-private.h b/contrib/libevent-cmake/evconfig-private.h new file mode 100644 index 000000000000..a39d2b71fbc3 --- /dev/null +++ b/contrib/libevent-cmake/evconfig-private.h @@ -0,0 +1,39 @@ +#ifndef EVCONFIG_PRIVATE_H_INCLUDED_ +#define EVCONFIG_PRIVATE_H_INCLUDED_ + +/* Enable extensions on AIX 3, Interix. */ +/* #undef _ALL_SOURCE */ + +/* Enable GNU extensions on systems that have them. */ +#define _GNU_SOURCE 1 + +/* Enable threading extensions on Solaris. */ +/* #undef _POSIX_PTHREAD_SEMANTICS */ + +/* Enable extensions on HP NonStop. */ +/* #undef _TANDEM_SOURCE */ + +/* Enable general extensions on Solaris. */ +/* #undef __EXTENSIONS__ */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Enable POSIX.2 extensions on QNX for getopt */ +#ifdef __QNX__ +/* #undef __EXT_POSIX2 */ +#endif + +#endif diff --git a/contrib/libevent-cmake/event-config.h b/contrib/libevent-cmake/event-config.h new file mode 100644 index 000000000000..090674124906 --- /dev/null +++ b/contrib/libevent-cmake/event-config.h @@ -0,0 +1,516 @@ +/* event-config.h + * + * This file was generated by cmake when the makefiles were generated. + * + * DO NOT EDIT THIS FILE. + * + * Do not rely on macros in this file existing in later versions. + */ +#ifndef EVENT2_EVENT_CONFIG_H_INCLUDED_ +#define EVENT2_EVENT_CONFIG_H_INCLUDED_ + +/* Numeric representation of the version */ +#define EVENT__NUMERIC_VERSION 0x02020001 +#define EVENT__PACKAGE_VERSION "2.2.0" + +#define EVENT__VERSION_MAJOR 2 +#define EVENT__VERSION_MINOR 2 +#define EVENT__VERSION_PATCH 0 + +/* Version number of package */ +#define EVENT__VERSION "2.2.0-alpha-dev" + +/* Name of package */ +#define EVENT__PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define EVENT__PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define EVENT__PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define EVENT__PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define EVENT__PACKAGE_TARNAME "" + +/* Define if libevent should build without support for a debug mode */ +/* #undef EVENT__DISABLE_DEBUG_MODE */ + +/* Define if libevent should not allow replacing the mm functions */ +/* #undef EVENT__DISABLE_MM_REPLACEMENT */ + +/* Define if libevent should not be compiled with thread support */ +/* #undef EVENT__DISABLE_THREAD_SUPPORT */ + +/* Define to 1 if you have the `accept4' function. */ +#define EVENT__HAVE_ACCEPT4 1 + +/* Define to 1 if you have the `arc4random' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM */ + +/* Define to 1 if you have the `arc4random_buf' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM_BUF */ + +/* Define to 1 if you have the `arc4random_addrandom' function. */ +/* #undef EVENT__HAVE_ARC4RANDOM_ADDRANDOM */ + +/* Define if clock_gettime is available in libc */ +#define EVENT__DNS_USE_CPU_CLOCK_FOR_ID 1 + +/* Define is no secure id variant is available */ +/* #undef EVENT__DNS_USE_GETTIMEOFDAY_FOR_ID */ +/* #undef EVENT__DNS_USE_FTIME_FOR_ID */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +#define EVENT__HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the declaration of `CTL_KERN'. */ +#define EVENT__HAVE_DECL_CTL_KERN 1 + +/* Define to 1 if you have the declaration of `KERN_ARND'. */ +#define EVENT__HAVE_DECL_KERN_ARND 0 + +/* Define to 1 if you have `getrandom' function. */ +#define EVENT__HAVE_GETRANDOM 1 + +/* Define if /dev/poll is available */ +/* #undef EVENT__HAVE_DEVPOLL */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETDB_H 1 + +/* Define to 1 if fd_mask type is defined */ +#define EVENT__HAVE_FD_MASK 1 + +/* Define to 1 if the header file defines TAILQ_FOREACH. */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_DLFCN_H 1 + +/* Define if your system supports the epoll system calls */ +#define EVENT__HAVE_EPOLL 1 + +/* Define to 1 if you have the `epoll_create1' function. */ +#define EVENT__HAVE_EPOLL_CREATE1 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +#define EVENT__HAVE_EPOLL_CTL 1 + +/* Define to 1 if you have the `eventfd' function. */ +#define EVENT__HAVE_EVENTFD 1 + +/* Define if your system supports event ports */ +/* #undef EVENT__HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +#define EVENT__HAVE_FCNTL 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define EVENT__HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `getegid' function. */ +#define EVENT__HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define EVENT__HAVE_GETEUID 1 + +/* TODO: Check for different gethostname argument counts. CheckPrototypeDefinition.cmake can be used. */ +/* Define this if you have any gethostbyname_r() */ +#define EVENT__HAVE_GETHOSTBYNAME_R 1 + +/* Define this if gethostbyname_r takes 3 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_3_ARG */ + +/* Define this if gethostbyname_r takes 5 arguments */ +/* #undef EVENT__HAVE_GETHOSTBYNAME_R_5_ARG */ + +/* Define this if gethostbyname_r takes 6 arguments */ +#define EVENT__HAVE_GETHOSTBYNAME_R_6_ARG 1 + +/* Define to 1 if you have the `getifaddrs' function. */ +#define EVENT__HAVE_GETIFADDRS 1 + +/* Define to 1 if you have the `getnameinfo' function. */ +#define EVENT__HAVE_GETNAMEINFO 1 + +/* Define to 1 if you have the `getprotobynumber' function. */ +#define EVENT__HAVE_GETPROTOBYNUMBER 1 + +/* Define to 1 if you have the `getservbyname' function. */ +#define EVENT__HAVE_GETSERVBYNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define EVENT__HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_IFADDRS_H 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define EVENT__HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define EVENT__HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `issetugid' function. */ +/* #undef EVENT__HAVE_ISSETUGID */ + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef EVENT__HAVE_KQUEUE */ + +/* Define if the system has zlib */ +#define EVENT__HAVE_LIBZ 1 + +/* Define to 1 if you have the `mach_absolute_time' function. */ +/* #undef EVENT__HAVE_MACH_ABSOLUTE_TIME */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_MACH_MACH_TIME_H */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_MACH_MACH_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mmap' function. */ +#define EVENT__HAVE_MMAP 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define EVENT__HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the `usleep' function. */ +#define EVENT__HAVE_USLEEP 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_AFUNIX_H */ + +/* Define if the system has openssl */ +#define EVENT__HAVE_OPENSSL 1 + +/* Define to 1 if you have the `pipe' function. */ +#define EVENT__HAVE_PIPE 1 + +/* Define to 1 if you have the `pipe2' function. */ +#define EVENT__HAVE_PIPE2 1 + +/* Define to 1 if you have the `poll' function. */ +#define EVENT__HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef EVENT__HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_PORT_H */ + +/* Define if we have pthreads on this system */ +#define EVENT__HAVE_PTHREADS 1 + +/* Define to 1 if you have the `putenv' function. */ +#define EVENT__HAVE_PUTENV 1 + +/* Define to 1 if the system has the type `sa_family_t'. */ +#define EVENT__HAVE_SA_FAMILY_T 1 + +/* Define to 1 if you have the `select' function. */ +#define EVENT__HAVE_SELECT 1 + +/* Define to 1 if you have the `setenv' function. */ +#define EVENT__HAVE_SETENV 1 + +/* Define if F_SETFD is defined in */ +#define EVENT__HAVE_SETFD 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define EVENT__HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `sendfile' function. */ +#define EVENT__HAVE_SENDFILE 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define EVENT__HAVE_SIGACTION 1 + +/* Define to 1 if you have the `signal' function. */ +#define EVENT__HAVE_SIGNAL 1 + +/* Define to 1 if you have the `strsignal' function. */ +#define EVENT__HAVE_STRSIGNAL 1 + +/* Define to 1 if you have the `splice' function. */ +#define EVENT__HAVE_SPLICE 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef EVENT__HAVE_STRLCPY */ + +/* Define to 1 if you have the `strsep' function. */ +#define EVENT__HAVE_STRSEP 1 + +/* Define to 1 if you have the `strtok_r' function. */ +#define EVENT__HAVE_STRTOK_R 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define EVENT__HAVE_STRTOLL 1 + +/* Define to 1 if you have the `_gmtime64_s' function. */ +/* #undef EVENT__HAVE__GMTIME64_S */ + +/* Define to 1 if you have the `_gmtime64' function. */ +/* #undef EVENT__HAVE__GMTIME64 */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define EVENT__HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if `s6_addr16' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR16 1 + +/* Define to 1 if `s6_addr32' is member of `struct in6_addr'. */ +#define EVENT__HAVE_STRUCT_IN6_ADDR_S6_ADDR32 1 + +/* Define to 1 if the system has the type `struct sockaddr_in6'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_IN6 1 + +/* Define to 1 if `sin6_len' is member of `struct sockaddr_in6'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN */ + +/* Define to 1 if `sin_len' is member of `struct sockaddr_in'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_UN 1 + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#define EVENT__HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +/* #undef EVENT__HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if the system has the type `struct linger'. */ +#define EVENT__HAVE_STRUCT_LINGER 1 + +/* Define to 1 if you have the `sysctl' function. */ +/* #undef EVENT__HAVE_SYSCTL */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EPOLL_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_EVENTFD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_MMAN_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_QUEUE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SENDFILE_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_RANDOM_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef EVENT__HAVE_SYS_SYSCTL_H */ + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIMERFD_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_ERRNO_H 1 + +/* Define if TAILQ_FOREACH is defined in */ +#define EVENT__HAVE_TAILQFOREACH 1 + +/* Define if timeradd is defined in */ +#define EVENT__HAVE_TIMERADD 1 + +/* Define if timerclear is defined in */ +#define EVENT__HAVE_TIMERCLEAR 1 + +/* Define if timercmp is defined in */ +#define EVENT__HAVE_TIMERCMP 1 + + +/* Define to 1 if you have the `timerfd_create' function. */ +#define EVENT__HAVE_TIMERFD_CREATE 1 + +/* Define if timerisset is defined in */ +#define EVENT__HAVE_TIMERISSET 1 + +/* Define to 1 if the system has the type `uint8_t'. */ +#define EVENT__HAVE_UINT8_T 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define EVENT__HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `uint32_t'. */ +#define EVENT__HAVE_UINT32_T 1 + +/* Define to 1 if the system has the type `uint64_t'. */ +#define EVENT__HAVE_UINT64_T 1 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define EVENT__HAVE_UINTPTR_T 1 + +/* Define to 1 if you have the `umask' function. */ +#define EVENT__HAVE_UMASK 1 + +/* Define to 1 if you have the header file. */ +#define EVENT__HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define EVENT__HAVE_UNSETENV 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define EVENT__HAVE_VASPRINTF 1 + +/* Define if kqueue works correctly with pipes */ +/* #undef EVENT__HAVE_WORKING_KQUEUE */ + +#ifdef __USE_UNUSED_DEFINITIONS__ +/* Define to necessary symbol if this constant uses a non-standard name on your system. */ +/* XXX: Hello, this isn't even used, nor is it defined anywhere... - Ellzey */ +#define EVENT__PTHREAD_CREATE_JOINABLE +#endif + +/* The size of `pthread_t', as computed by sizeof. */ +#define EVENT__SIZEOF_PTHREAD_T 8 + +/* The size of a `int', as computed by sizeof. */ +#define EVENT__SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG 8 + +/* The size of a `long long', as computed by sizeof. */ +#define EVENT__SIZEOF_LONG_LONG 8 + +/* The size of `off_t', as computed by sizeof. */ +#define EVENT__SIZEOF_OFF_T 8 + +#define EVENT__SIZEOF_SSIZE_T 8 + + +/* The size of a `short', as computed by sizeof. */ +#define EVENT__SIZEOF_SHORT 2 + +/* The size of `size_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SIZE_T 8 + +/* Define to 1 if you can safely include both and . */ +/* #undef EVENT__TIME_WITH_SYS_TIME */ + +/* The size of `socklen_t', as computed by sizeof. */ +#define EVENT__SIZEOF_SOCKLEN_T 4 + +/* The size of 'void *', as computer by sizeof */ +#define EVENT__SIZEOF_VOID_P 8 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* why not c++? + * + * and are we really expected to use EVENT__inline everywhere, + * shouldn't we just do: + * ifdef EVENT__inline + * define inline EVENT__inline + * + * - Ellzey + */ + +#define EVENT__inline inline +#endif + +#define EVENT__HAVE___func__ 1 +#define EVENT__HAVE___FUNCTION__ 1 + +/* Define to `unsigned' if does not define. */ +#define EVENT__size_t size_t + +/* Define to unsigned int if you dont have it */ +#define EVENT__socklen_t socklen_t + +/* Define to `int' if does not define. */ +#define EVENT__ssize_t ssize_t + +#endif /* \EVENT2_EVENT_CONFIG_H_INCLUDED_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 222a3e486f98..f720b5c1c852 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,10 @@ if(USE_RDKAFKA) add_headers_and_sources(dbms Storages/Kafka) endif() +if (USE_AMQPCPP) + add_headers_and_sources(dbms Storages/RabbitMQ) +endif() + if (USE_AWS_S3) add_headers_and_sources(dbms Disks/S3) endif() @@ -253,6 +257,9 @@ if (USE_RDKAFKA) endif() endif() +if (USE_AMQPCPP) + dbms_target_link_libraries(PUBLIC amqp-cpp) +endif() if(RE2_INCLUDE_DIR) target_include_directories(clickhouse_common_io SYSTEM BEFORE PUBLIC ${RE2_INCLUDE_DIR}) From 3b75f214c59d1a674f283a9f4675005ef5f04f61 Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 08:30:38 +0300 Subject: [PATCH 0118/1102] Register RabbitMQ storage --- src/Core/Settings.h | 1 + src/Core/config_core.h.in | 1 + src/Storages/RabbitMQ/RabbitMQSettings.cpp | 44 ++++ src/Storages/RabbitMQ/RabbitMQSettings.h | 26 +++ src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 249 +++++++++++++++++++++ src/Storages/RabbitMQ/StorageRabbitMQ.h | 62 +++++ src/Storages/registerStorages.cpp | 4 + src/Storages/registerStorages.h | 4 + 8 files changed, 391 insertions(+) create mode 100644 src/Storages/RabbitMQ/RabbitMQSettings.cpp create mode 100644 src/Storages/RabbitMQ/RabbitMQSettings.h create mode 100644 src/Storages/RabbitMQ/StorageRabbitMQ.cpp create mode 100644 src/Storages/RabbitMQ/StorageRabbitMQ.h diff --git a/src/Core/Settings.h b/src/Core/Settings.h index eda76584f0b2..9cd6287a75da 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -71,6 +71,7 @@ struct Settings : public SettingsCollection M(SettingMilliseconds, connection_pool_max_wait_ms, 0, "The wait time when the connection pool is full.", 0) \ M(SettingMilliseconds, replace_running_query_max_wait_ms, 5000, "The wait time for running query with the same query_id to finish when setting 'replace_running_query' is active.", 0) \ M(SettingMilliseconds, kafka_max_wait_ms, 5000, "The wait time for reading from Kafka before retry.", 0) \ + M(SettingMilliseconds, rabbitmq_max_wait_ms, 5000, "The wait time for reading from RabbitMQ before retry.", 0) \ M(SettingUInt64, poll_interval, DBMS_DEFAULT_POLL_INTERVAL, "Block at the query wait loop on the server for the specified number of seconds.", 0) \ M(SettingUInt64, idle_connection_timeout, 3600, "Close idle TCP connections after specified number of seconds.", 0) \ M(SettingUInt64, distributed_connections_pool_size, DBMS_DEFAULT_DISTRIBUTED_CONNECTIONS_POOL_SIZE, "Maximum number of connections with one remote server in the pool.", 0) \ diff --git a/src/Core/config_core.h.in b/src/Core/config_core.h.in index 620c23c21cca..5991c12a1f25 100644 --- a/src/Core/config_core.h.in +++ b/src/Core/config_core.h.in @@ -5,6 +5,7 @@ #cmakedefine01 USE_ICU #cmakedefine01 USE_MYSQL #cmakedefine01 USE_RDKAFKA +#cmakedefine01 USE_AMQPCPP #cmakedefine01 USE_EMBEDDED_COMPILER #cmakedefine01 USE_INTERNAL_LLVM_LIBRARY #cmakedefine01 USE_SSL diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.cpp b/src/Storages/RabbitMQ/RabbitMQSettings.cpp new file mode 100644 index 000000000000..ed8d4ad801aa --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQSettings.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int BAD_ARGUMENTS; + extern const int UNKNOWN_SETTING; +} + +IMPLEMENT_SETTINGS_COLLECTION(RabbitMQSettings, LIST_OF_RABBITMQ_SETTINGS) + +void RabbitMQSettings::loadFromQuery(ASTStorage & storage_def) +{ + if (storage_def.settings) + { + try + { + applyChanges(storage_def.settings->changes); + } + catch (Exception & e) + { + if (e.code() == ErrorCodes::UNKNOWN_SETTING) + throw Exception(e.message() + " for storage " + storage_def.engine->name, ErrorCodes::BAD_ARGUMENTS); + else + e.rethrow(); + } + } + else + { + auto settings_ast = std::make_shared(); + settings_ast->is_standalone = false; + storage_def.set(storage_def.settings, settings_ast); + } +} +} + diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h new file mode 100644 index 000000000000..0b0f58169fa3 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace DB +{ + class ASTStorage; + + struct RabbitMQSettings : public SettingsCollection + { + +#define LIST_OF_RABBITMQ_SETTINGS(M) \ + M(SettingString, rabbitmq_host_port, "", "A host-port to connect to RabbitMQ server.", 0) \ + M(SettingString, rabbitmq_routing_key, "5672", "A routing key to connect producer->exchange->queue<->consumer.", 0) \ + M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exhange name, to which messages are sent. Needed to bind queues to it.", 0) \ + M(SettingString, rabbitmq_format, "", "The message format.", 0) \ + M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ + M(SettingUInt64, rabbitmq_bind_by_id, 0, "A flag which indicates that binding should be done in range [0, num_consumers).", 0) \ + M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ + M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ + M(SettingUInt64, rabbitmq_hash_exchange, 0, "A flag which indicates whether consistent-hash-exchange should be used.", 0) \ + + DECLARE_SETTINGS_COLLECTION(LIST_OF_RABBITMQ_SETTINGS) + + void loadFromQuery(ASTStorage & storage_def); + }; +} diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp new file mode 100644 index 000000000000..98e7e97e2e1d --- /dev/null +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NOT_IMPLEMENTED; + extern const int LOGICAL_ERROR; + extern const int BAD_ARGUMENTS; + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} + +StorageRabbitMQ::StorageRabbitMQ( + const StorageID & table_id_, + Context & context_, + const ColumnsDescription & columns_, + const String & host_port_, + const String & routing_key_, + const String & exchange_name_, + const String & format_name_, + char row_delimiter_, + size_t num_consumers_, + bool bind_by_id_, + size_t num_queues_, + bool hash_exchange_) + : IStorage(table_id_) + , global_context(context_.getGlobalContext()) + , rabbitmq_context(Context(global_context)) + , routing_key(global_context.getMacros()->expand(routing_key_)) + , exchange_name(exchange_name_) + , format_name(global_context.getMacros()->expand(format_name_)) + , row_delimiter(row_delimiter_) + , num_consumers(num_consumers_) + , bind_by_id(bind_by_id_) + , num_queues(num_queues_) + , hash_exchange(hash_exchange_) + , log(&Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) + , semaphore(0, num_consumers_) + , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) +{ +} + + +void registerStorageRabbitMQ(StorageFactory & factory) +{ + auto creator_fn = [](const StorageFactory::Arguments & args) + { + ASTs & engine_args = args.engine_args; + size_t args_count = engine_args.size(); + bool has_settings = args.storage_def->settings; + + RabbitMQSettings rabbitmq_settings; + if (has_settings) + { + rabbitmq_settings.loadFromQuery(*args.storage_def); + } + + String host_port = rabbitmq_settings.rabbitmq_host_port; + if (args_count >= 1) + { + const auto * ast = engine_args[0]->as(); + if (ast && ast->value.getType() == Field::Types::String) + { + host_port = safeGet(ast->value); + } + else + { + throw Exception(String("RabbitMQ host:port must be a string"), ErrorCodes::BAD_ARGUMENTS); + } + } + + String routing_key = rabbitmq_settings.rabbitmq_routing_key.value; + if (args_count >= 2) + { + const auto * ast = engine_args[1]->as(); + if (ast && ast->value.getType() == Field::Types::String) + { + routing_key = safeGet(ast->value); + } + else + { + throw Exception(String("RabbitMQ routing key must be a string"), ErrorCodes::BAD_ARGUMENTS); + } + } + + String exchange = rabbitmq_settings.rabbitmq_exchange_name.value; + if (args_count >= 3) + { + engine_args[2] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[2], args.local_context); + + const auto * ast = engine_args[2]->as(); + if (ast && ast->value.getType() == Field::Types::String) + { + exchange = safeGet(ast->value); + } + } + + String format = rabbitmq_settings.rabbitmq_format.value; + if (args_count >= 4) + { + engine_args[3] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[3], args.local_context); + + const auto * ast = engine_args[3]->as(); + if (ast && ast->value.getType() == Field::Types::String) + { + format = safeGet(ast->value); + } + else + { + throw Exception("Format must be a string", ErrorCodes::BAD_ARGUMENTS); + } + } + + char row_delimiter = rabbitmq_settings.rabbitmq_row_delimiter; + if (args_count >= 5) + { + engine_args[4] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[4], args.local_context); + + const auto * ast = engine_args[4]->as(); + String arg; + if (ast && ast->value.getType() == Field::Types::String) + { + arg = safeGet(ast->value); + } + else + { + throw Exception("Row delimiter must be a char", ErrorCodes::BAD_ARGUMENTS); + } + if (arg.size() > 1) + { + throw Exception("Row delimiter must be a char", ErrorCodes::BAD_ARGUMENTS); + } + else if (arg.empty()) + { + row_delimiter = '\0'; + } + else + { + row_delimiter = arg[0]; + } + } + + size_t bind_by_id = static_cast(rabbitmq_settings.rabbitmq_bind_by_id); + if (args_count >= 6) + { + const auto * ast = engine_args[5]->as(); + if (ast && ast->value.getType() == Field::Types::UInt64) + { + bind_by_id = static_cast(safeGet(ast->value)); + } + else + { + throw Exception("Hash exchange flag must be a boolean", ErrorCodes::BAD_ARGUMENTS); + } + } + + UInt64 num_consumers = rabbitmq_settings.rabbitmq_num_consumers; + if (args_count >= 7) + { + const auto * ast = engine_args[6]->as(); + if (ast && ast->value.getType() == Field::Types::UInt64) + { + num_consumers = safeGet(ast->value); + } + else + { + throw Exception("Number of consumers must be a positive integer", ErrorCodes::BAD_ARGUMENTS); + } + } + + UInt64 num_queues = rabbitmq_settings.rabbitmq_num_queues; + if (args_count >= 8) + { + const auto * ast = engine_args[7]->as(); + if (ast && ast->value.getType() == Field::Types::UInt64) + { + num_consumers = safeGet(ast->value); + } + else + { + throw Exception("Number of queues must be a positive integer", ErrorCodes::BAD_ARGUMENTS); + } + } + + size_t hash_exchange = static_cast(rabbitmq_settings.rabbitmq_hash_exchange); + if (args_count >= 9) + { + const auto * ast = engine_args[8]->as(); + if (ast && ast->value.getType() == Field::Types::UInt64) + { + hash_exchange = static_cast(safeGet(ast->value)); + } + else + { + throw Exception("Hash exchange flag must be a boolean", ErrorCodes::BAD_ARGUMENTS); + } + } + + return StorageRabbitMQ::create(args.table_id, args.context, args.columns, host_port, routing_key, exchange, + format, row_delimiter, num_consumers, bind_by_id, num_queues, hash_exchange); + }; + + factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); + +} + + +NamesAndTypesList StorageRabbitMQ::getVirtuals() const +{ + return NamesAndTypesList{ + {"_exchange", std::make_shared()}, + {"_routingKey", std::make_shared()} + }; +} + +} + diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h new file mode 100644 index 000000000000..37b8c2b1078b --- /dev/null +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +using ChannelPtr = std::shared_ptr; + +class StorageRabbitMQ final: public ext::shared_ptr_helper, public IStorage +{ + friend struct ext::shared_ptr_helper; + +public: + std::string getName() const override { return "RabbitMQ"; } + bool supportsSettings() const override { return true; } + +protected: + StorageRabbitMQ( + const StorageID & table_id_, + Context & context_, + const ColumnsDescription & columns_, + const String & host_port_, + const String & routing_key_, const String & exchange_name_, + const String & format_name_, char row_delimiter_, + size_t num_consumers_, bool bind_by_id_, size_t num_queues_, bool hash_exchange); + +private: + Context global_context; + Context rabbitmq_context; + + String routing_key; + const String exchange_name; + + const String format_name; + char row_delimiter; + size_t num_consumers; + size_t num_created_consumers = 0; + + bool bind_by_id; + size_t num_queues; + const bool hash_exchange; + + Poco::Logger * log; + std::pair parsed_address; + + Poco::Semaphore semaphore; + std::mutex mutex; + + size_t consumer_id = 0; + + BackgroundSchedulePool::TaskHolder task; + std::atomic stream_cancelled{false}; +}; + +} diff --git a/src/Storages/registerStorages.cpp b/src/Storages/registerStorages.cpp index 5ad26b708033..c349a4e5c8fd 100644 --- a/src/Storages/registerStorages.cpp +++ b/src/Storages/registerStorages.cpp @@ -46,6 +46,10 @@ void registerStorages() #if USE_RDKAFKA registerStorageKafka(factory); #endif + + #if USE_AMQPCPP + registerStorageRabbitMQ(factory); + #endif } } diff --git a/src/Storages/registerStorages.h b/src/Storages/registerStorages.h index c98745510730..2823f5c2d2c5 100644 --- a/src/Storages/registerStorages.h +++ b/src/Storages/registerStorages.h @@ -47,6 +47,10 @@ void registerStorageMySQL(StorageFactory & factory); void registerStorageKafka(StorageFactory & factory); #endif +#if USE_AMQPCPP +void registerStorageRabbitMQ(StorageFactory & factory); +#endif + void registerStorages(); } From 41b99edc044ae0fc820d99a9e8e69a59a27ca76c Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 09:22:12 +0300 Subject: [PATCH 0119/1102] Add base for RabbitMQ integration tests --- .../compose/docker_compose_rabbitmq.yml | 12 ++ tests/integration/helpers/cluster.py | 24 +++- .../test_storage_rabbitmq/__init__.py | 0 .../configs/log_conf.xml | 11 ++ .../configs/rabbitmq.xml | 5 + .../test_storage_rabbitmq/configs/users.xml | 25 ++++ .../integration/test_storage_rabbitmq/test.py | 123 ++++++++++++++++++ .../test_rabbitmq_json.reference | 50 +++++++ 8 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 docker/test/integration/compose/docker_compose_rabbitmq.yml create mode 100644 tests/integration/test_storage_rabbitmq/__init__.py create mode 100644 tests/integration/test_storage_rabbitmq/configs/log_conf.xml create mode 100644 tests/integration/test_storage_rabbitmq/configs/rabbitmq.xml create mode 100644 tests/integration/test_storage_rabbitmq/configs/users.xml create mode 100644 tests/integration/test_storage_rabbitmq/test.py create mode 100644 tests/integration/test_storage_rabbitmq/test_rabbitmq_json.reference diff --git a/docker/test/integration/compose/docker_compose_rabbitmq.yml b/docker/test/integration/compose/docker_compose_rabbitmq.yml new file mode 100644 index 000000000000..7ebee3c0ea56 --- /dev/null +++ b/docker/test/integration/compose/docker_compose_rabbitmq.yml @@ -0,0 +1,12 @@ +version: '2.2' + +services: + rabbitmq1: + image: rabbitmq:3-management + hostname: rabbitmq1 + ports: + - "5672:5672" + - "15672:15672" + environment: + RABBITMQ_DEFAULT_USER: "root" + RABBITMQ_DEFAULT_PASS: "clickhouse" diff --git a/tests/integration/helpers/cluster.py b/tests/integration/helpers/cluster.py index 53c36ff89247..6d9ca1b7861d 100644 --- a/tests/integration/helpers/cluster.py +++ b/tests/integration/helpers/cluster.py @@ -108,12 +108,14 @@ def __init__(self, base_path, name=None, base_configs_dir=None, server_bin_path= self.base_zookeeper_cmd = None self.base_mysql_cmd = [] self.base_kafka_cmd = [] + self.base_rabbitmq_cmd = [] self.pre_zookeeper_commands = [] self.instances = {} self.with_zookeeper = False self.with_mysql = False self.with_postgres = False self.with_kafka = False + self.with_rabbitmq = False self.with_odbc_drivers = False self.with_hdfs = False self.with_mongo = False @@ -143,7 +145,7 @@ def get_client_cmd(self): return cmd def add_instance(self, name, config_dir=None, main_configs=None, user_configs=None, macros=None, - with_zookeeper=False, with_mysql=False, with_kafka=False, clickhouse_path_dir=None, + with_zookeeper=False, with_mysql=False, with_kafka=False, with_rabbitmq=False, clickhouse_path_dir=None, with_odbc_drivers=False, with_postgres=False, with_hdfs=False, with_mongo=False, with_redis=False, with_minio=False, hostname=None, env_variables=None, image="yandex/clickhouse-integration-test", @@ -167,7 +169,7 @@ def add_instance(self, name, config_dir=None, main_configs=None, user_configs=No instance = ClickHouseInstance( self, self.base_dir, name, config_dir, main_configs or [], user_configs or [], macros or {}, with_zookeeper, - self.zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, + self.zookeeper_config_path, with_mysql, with_kafka, with_rabbitmq, with_mongo, with_redis, with_minio, self.base_configs_dir, self.server_bin_path, self.odbc_bridge_bin_path, clickhouse_path_dir, with_odbc_drivers, hostname=hostname, env_variables=env_variables or {}, image=image, stay_alive=stay_alive, ipv4_address=ipv4_address, @@ -231,6 +233,13 @@ def add_instance(self, name, config_dir=None, main_configs=None, user_configs=No self.project_name, '--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_kafka.yml')] cmds.append(self.base_kafka_cmd) + if with_rabbitmq and not self.with_rabbitmq: + self.with_rabbitmq = True + self.base_cmd.extend(['--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_rabbitmq.yml')]) + self.base_rabbitmq_cmd = ['docker-compose', '--project-directory', self.base_dir, '--project-name', + self.project_name, '--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_rabbitmq.yml')] + cmds.append(self.base_rabbitmq_cmd) + if with_hdfs and not self.with_hdfs: self.with_hdfs = True self.base_cmd.extend(['--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_hdfs.yml')]) @@ -482,6 +491,10 @@ def start(self, destroy_dirs=True): self.kafka_docker_id = self.get_instance_docker_id('kafka1') self.wait_schema_registry_to_start(120) + if self.with_rabbitmq and self.base_rabbitmq_cmd: + subprocess_check_call(self.base_rabbitmq_cmd + common_opts + ['--renew-anon-volumes']) + self.rabbitmq_docker_id = self.get_instance_docker_id('rabbitmq1') + if self.with_hdfs and self.base_hdfs_cmd: subprocess_check_call(self.base_hdfs_cmd + common_opts) self.wait_hdfs_to_start(120) @@ -621,7 +634,7 @@ class ClickHouseInstance: def __init__( self, cluster, base_path, name, custom_config_dir, custom_main_configs, custom_user_configs, macros, - with_zookeeper, zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, + with_zookeeper, zookeeper_config_path, with_mysql, with_kafka, with_rabbitmq, with_mongo, with_redis, with_minio, base_configs_dir, server_bin_path, odbc_bridge_bin_path, clickhouse_path_dir, with_odbc_drivers, hostname=None, env_variables=None, image="yandex/clickhouse-integration-test", @@ -648,6 +661,7 @@ def __init__( self.with_mysql = with_mysql self.with_kafka = with_kafka + self.with_rabbitmq = with_rabbitmq self.with_mongo = with_mongo self.with_redis = with_redis self.with_minio = with_minio @@ -993,6 +1007,9 @@ def create_dir(self, destroy_dir=True): depends_on.append("kafka1") depends_on.append("schema-registry") + if self.with_rabbitmq: + depends_on.append("rabbitmq1") + if self.with_zookeeper: depends_on.append("zoo1") depends_on.append("zoo2") @@ -1072,3 +1089,4 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): self.clickhouse_node.restore_clickhouse() + diff --git a/tests/integration/test_storage_rabbitmq/__init__.py b/tests/integration/test_storage_rabbitmq/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/integration/test_storage_rabbitmq/configs/log_conf.xml b/tests/integration/test_storage_rabbitmq/configs/log_conf.xml new file mode 100644 index 000000000000..f9d15e572aa8 --- /dev/null +++ b/tests/integration/test_storage_rabbitmq/configs/log_conf.xml @@ -0,0 +1,11 @@ + + + trace + /var/log/clickhouse-server/log.log + /var/log/clickhouse-server/log.err.log + 1000M + 10 + /var/log/clickhouse-server/stderr.log + /var/log/clickhouse-server/stdout.log + + diff --git a/tests/integration/test_storage_rabbitmq/configs/rabbitmq.xml b/tests/integration/test_storage_rabbitmq/configs/rabbitmq.xml new file mode 100644 index 000000000000..33a8a43fb1a4 --- /dev/null +++ b/tests/integration/test_storage_rabbitmq/configs/rabbitmq.xml @@ -0,0 +1,5 @@ + + + earliest + + diff --git a/tests/integration/test_storage_rabbitmq/configs/users.xml b/tests/integration/test_storage_rabbitmq/configs/users.xml new file mode 100644 index 000000000000..246e6b069ef2 --- /dev/null +++ b/tests/integration/test_storage_rabbitmq/configs/users.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + ::/0 + + default + default + + + + + + + + diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py new file mode 100644 index 000000000000..475b89f6c607 --- /dev/null +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -0,0 +1,123 @@ +import os.path as p +import random +import threading +import time +import pytest + +from random import randrange +import pika +from sys import getdefaultencoding + +from helpers.cluster import ClickHouseCluster +from helpers.test_tools import TSV +from helpers.client import QueryRuntimeException +from helpers.network import PartitionManager + +import json +import subprocess + +from google.protobuf.internal.encoder import _VarintBytes + +cluster = ClickHouseCluster(__file__) +instance = cluster.add_instance('instance', + config_dir='configs', + main_configs=['configs/rabbitmq.xml','configs/log_conf.xml'], + with_rabbitmq=True) +rabbitmq_id = '' + + +# Helpers + +def check_rabbitmq_is_available(): + p = subprocess.Popen(('docker', + 'exec', + '-i', + rabbitmq_id, + 'rabbitmqctl', + 'await_startup'), + stdout=subprocess.PIPE) + p.communicate() + return p.returncode == 0 + + +def enable_consistent_hash_plugin(): + p = subprocess.Popen(('docker', + 'exec', + '-i', + rabbitmq_id, + "rabbitmq-plugins", "enable", "rabbitmq_consistent_hash_exchange"), + stdout=subprocess.PIPE) + p.communicate() + return p.returncode == 0 + + +def wait_rabbitmq_is_available(max_retries=50): + retries = 0 + while True: + if check_rabbitmq_is_available(): + break + else: + retries += 1 + if retries > max_retries: + raise "RabbitMQ is not available" + print("Waiting for RabbitMQ to start up") + time.sleep(1) + + +def wait_rabbitmq_plugin_enabled(max_retries=50): + retries = 0 + while True: + if enable_consistent_hash_plugin(): + break + else: + retries += 1 + if retries > max_retries: + raise "RabbitMQ plugin is not available" + print("Waiting for plugin") + time.sleep(1) + + +def rabbitmq_check_result(result, check=False, ref_file='test_rabbitmq_json.reference'): + fpath = p.join(p.dirname(__file__), ref_file) + with open(fpath) as reference: + if check: + assert TSV(result) == TSV(reference) + else: + return TSV(result) == TSV(reference) + + +# Fixtures + +@pytest.fixture(scope="module") +def rabbitmq_cluster(): + try: + global rabbitmq_id + cluster.start() + rabbitmq_id = instance.cluster.rabbitmq_docker_id + print("rabbitmq_id is {}".format(rabbitmq_id)) + instance.query('CREATE DATABASE test') + + yield cluster + + finally: + cluster.shutdown() + + +@pytest.fixture(autouse=True) +def rabbitmq_setup_teardown(): + wait_rabbitmq_is_available() + wait_rabbitmq_plugin_enabled() + print("RabbitMQ is available - running test") + yield # run test + instance.query('DROP TABLE IF EXISTS test.rabbitmq') + + +# Tests + + + +if __name__ == '__main__': + cluster.start() + raw_input("Cluster created, press any key to destroy...") + cluster.shutdown() + diff --git a/tests/integration/test_storage_rabbitmq/test_rabbitmq_json.reference b/tests/integration/test_storage_rabbitmq/test_rabbitmq_json.reference new file mode 100644 index 000000000000..959bb2aad74c --- /dev/null +++ b/tests/integration/test_storage_rabbitmq/test_rabbitmq_json.reference @@ -0,0 +1,50 @@ +0 0 +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +11 11 +12 12 +13 13 +14 14 +15 15 +16 16 +17 17 +18 18 +19 19 +20 20 +21 21 +22 22 +23 23 +24 24 +25 25 +26 26 +27 27 +28 28 +29 29 +30 30 +31 31 +32 32 +33 33 +34 34 +35 35 +36 36 +37 37 +38 38 +39 39 +40 40 +41 41 +42 42 +43 43 +44 44 +45 45 +46 46 +47 47 +48 48 +49 49 From aeffab3fdb07f7d7ca60f3e9791181a8657c15d2 Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 09:40:49 +0000 Subject: [PATCH 0120/1102] Enable SELECT and CREATE MV queries with engine RabbitMQ --- src/Storages/RabbitMQ/Buffer_fwd.h | 11 + .../RabbitMQ/RabbitMQBlockInputStream.cpp | 156 ++++++++++ .../RabbitMQ/RabbitMQBlockInputStream.h | 41 +++ src/Storages/RabbitMQ/RabbitMQHandler.cpp | 32 +++ src/Storages/RabbitMQ/RabbitMQHandler.h | 28 ++ src/Storages/RabbitMQ/RabbitMQSettings.cpp | 2 - src/Storages/RabbitMQ/RabbitMQSettings.h | 3 +- .../ReadBufferFromRabbitMQConsumer.cpp | 268 ++++++++++++++++++ .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 81 ++++++ src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 232 ++++++++++++++- src/Storages/RabbitMQ/StorageRabbitMQ.h | 38 ++- 11 files changed, 883 insertions(+), 9 deletions(-) create mode 100644 src/Storages/RabbitMQ/Buffer_fwd.h create mode 100644 src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp create mode 100644 src/Storages/RabbitMQ/RabbitMQBlockInputStream.h create mode 100644 src/Storages/RabbitMQ/RabbitMQHandler.cpp create mode 100644 src/Storages/RabbitMQ/RabbitMQHandler.h create mode 100644 src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp create mode 100644 src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h diff --git a/src/Storages/RabbitMQ/Buffer_fwd.h b/src/Storages/RabbitMQ/Buffer_fwd.h new file mode 100644 index 000000000000..f0ef010c5182 --- /dev/null +++ b/src/Storages/RabbitMQ/Buffer_fwd.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace DB +{ + +class ReadBufferFromRabbitMQConsumer; +using ConsumerBufferPtr = std::shared_ptr; + +} diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp new file mode 100644 index 000000000000..89ea490e8425 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +RabbitMQBlockInputStream::RabbitMQBlockInputStream( + StorageRabbitMQ & storage_, const Context & context_, const Names & columns, Poco::Logger * log_) + : storage(storage_) + , context(context_) + , column_names(columns) + , log(log_) + , non_virtual_header(storage.getSampleBlockNonMaterialized()) + , virtual_header(storage.getSampleBlockForColumns({"_exchange", "_routingKey"})) +{ +} + + +RabbitMQBlockInputStream::~RabbitMQBlockInputStream() +{ + if (!claimed) + return; + + storage.pushReadBuffer(buffer); +} + + +Block RabbitMQBlockInputStream::getHeader() const +{ + return storage.getSampleBlockForColumns(column_names); +} + + +void RabbitMQBlockInputStream::readPrefixImpl() +{ + auto timeout = std::chrono::milliseconds(context.getSettingsRef().rabbitmq_max_wait_ms.totalMilliseconds()); + + buffer = storage.popReadBuffer(timeout); + claimed = !!buffer; + + if (!buffer || finished) + return; + + buffer->subscribeConsumer(); +} + + +Block RabbitMQBlockInputStream::readImpl() +{ + if (!buffer || finished) + return Block(); + + finished = true; + + MutableColumns result_columns = non_virtual_header.cloneEmptyColumns(); + MutableColumns virtual_columns = virtual_header.cloneEmptyColumns(); + + auto input_format = FormatFactory::instance().getInputFormat( + storage.getFormatName(), *buffer, non_virtual_header, context, 1); + + InputPort port(input_format->getPort().getHeader(), input_format.get()); + connect(input_format->getPort(), port); + port.setNeeded(); + + auto read_rabbitmq_message = [&] + { + size_t new_rows = 0; + + while (true) + { + auto status = input_format->prepare(); + + switch (status) + { + case IProcessor::Status::Ready: + input_format->work(); + break; + + case IProcessor::Status::Finished: + input_format->resetParser(); + return new_rows; + + case IProcessor::Status::PortFull: + { + auto chunk = port.pull(); + + auto chunk_rows = chunk.getNumRows(); + new_rows += chunk_rows; + + auto columns = chunk.detachColumns(); + + for (size_t i = 0, s = columns.size(); i < s; ++i) + { + result_columns[i]->insertRangeFrom(*columns[i], 0, columns[i]->size()); + } + break; + } + case IProcessor::Status::NeedData: + case IProcessor::Status::Async: + case IProcessor::Status::Wait: + case IProcessor::Status::ExpandPipeline: + throw Exception("Source processor returned status " + IProcessor::statusToName(status), ErrorCodes::LOGICAL_ERROR); + } + } + }; + + size_t total_rows = 0; + + while (true) + { + if (buffer->eof()) + break; + + auto new_rows = read_rabbitmq_message(); + + auto _exchange = storage.getExchangeName(); + auto _routingKey = storage.getRoutingKey(); + + for (size_t i = 0; i < new_rows; ++i) + { + virtual_columns[0]->insert(_exchange); + virtual_columns[1]->insert(_routingKey); + } + + total_rows = total_rows + new_rows; + buffer->allowNext(); + + if (!new_rows || !checkTimeLimit()) + break; + } + + if (total_rows == 0) + return Block(); + + auto result_block = non_virtual_header.cloneWithColumns(std::move(result_columns)); + auto virtual_block = virtual_header.cloneWithColumns(std::move(virtual_columns)); + + LOG_DEBUG(log, "Total amount of rows is " + std::to_string(result_block.rows())); + + for (const auto & column : virtual_block.getColumnsWithTypeAndName()) + { + result_block.insert(column); + } + + return ConvertingBlockInputStream( + std::make_shared(result_block), + getHeader(), + ConvertingBlockInputStream::MatchColumnsMode::Name) + .read(); +} + +} diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h new file mode 100644 index 000000000000..c82fd68a680a --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + + +namespace DB +{ +class RabbitMQBlockInputStream : public IBlockInputStream +{ + +public: + RabbitMQBlockInputStream( + StorageRabbitMQ & storage_, + const Context & context_, + const Names & columns, + Poco::Logger * log_); + + ~RabbitMQBlockInputStream() override; + + String getName() const override { return storage.getName(); } + Block getHeader() const override; + + void readPrefixImpl() override; + Block readImpl() override; + //void readSuffixImpl() override; + +private: + StorageRabbitMQ & storage; + Context context; + Names column_names; + Poco::Logger * log; + bool finished = false, claimed = false; + const Block non_virtual_header, virtual_header; + + ConsumerBufferPtr buffer; +}; + +} diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp new file mode 100644 index 000000000000..b18d6bf2cfb2 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace DB +{ + +RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : + LibEventHandler(evbase_), + evbase(evbase_), + log(log_) +{ +} + + +void RabbitMQHandler::onError(AMQP::TcpConnection * /*connection*/, const char * message) +{ + LOG_ERROR(log, "Library error report: " << message); + stop(); +} + + +void RabbitMQHandler::startNonBlock() +{ + event_base_loop(evbase, EVLOOP_NONBLOCK); +} + +void RabbitMQHandler::stop() +{ + event_base_loopbreak(evbase); +} + +} diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h new file mode 100644 index 000000000000..94a559cad389 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +class RabbitMQHandler : public AMQP::LibEventHandler +{ + +public: + RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); + + void onError(AMQP::TcpConnection * connection, const char * message) override; + void startNonBlock(); + void stop(); + +private: + event_base * evbase; + Poco::Logger * log; +}; + +} diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.cpp b/src/Storages/RabbitMQ/RabbitMQSettings.cpp index ed8d4ad801aa..efb73396515f 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.cpp +++ b/src/Storages/RabbitMQ/RabbitMQSettings.cpp @@ -5,7 +5,6 @@ #include #include - namespace DB { @@ -41,4 +40,3 @@ void RabbitMQSettings::loadFromQuery(ASTStorage & storage_def) } } } - diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index 0b0f58169fa3..f4c62756703f 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -1,4 +1,5 @@ #pragma once + #include namespace DB @@ -14,7 +15,7 @@ namespace DB M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exhange name, to which messages are sent. Needed to bind queues to it.", 0) \ M(SettingString, rabbitmq_format, "", "The message format.", 0) \ M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ - M(SettingUInt64, rabbitmq_bind_by_id, 0, "A flag which indicates that binding should be done in range [0, num_consumers).", 0) \ + M(SettingUInt64, rabbitmq_bind_by_id, 0, "A flag which indicates that binding should be done in range [0, num_consumers * num_queues).", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ M(SettingUInt64, rabbitmq_hash_exchange, 0, "A flag which indicates whether consistent-hash-exchange should be used.", 0) \ diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp new file mode 100644 index 000000000000..a9f804aaa021 --- /dev/null +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( + std::pair & parsed_address, + const String & exchange_name_, + const String & routing_key_, + const size_t channel_id_, + Poco::Logger * log_, + char row_delimiter_, + const bool bind_by_id_, + const bool hash_exchange_, + const size_t num_queues_, + const std::atomic & stopped_) + : ReadBuffer(nullptr, 0) + , evbase(event_base_new()) + , eventHandler(evbase, log) + , connection(&eventHandler, + AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) + , exchange_name(exchange_name_) + , routing_key(routing_key_) + , channel_id(channel_id_) + , log(log_) + , row_delimiter(row_delimiter_) + , bind_by_id(bind_by_id_) + , hash_exchange(hash_exchange_) + , num_queues(num_queues_) + , stopped(stopped_) +{ + /* It turned out to be very important to make a different connection each time the object of this class is created, + * because in case when num_consumers > 1 - inputStreams run asynchronously and if they share the same connection, + * then they also will share the same event loop. But it will mean that if one stream's consumer starts event loop, + * then it will run all callbacks on the connection - including other stream's consumer's callbacks - + * it result in asynchronous run of the same code and lead to occasional seg faults. + */ + while (!connection.ready()) + { + event_base_loop(evbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); + } + + consumer_channel = std::make_shared(&connection); + + messages.clear(); + current = messages.begin(); + + /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. + * By default there is one queue per consumer. + */ + for (size_t queue_id = 0; queue_id < num_queues; ++queue_id) + { + initQueueBindings(queue_id); + } +} + + +ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() +{ + connection.close(); + + messages.clear(); + current = messages.begin(); + BufferBase::set(nullptr, 0, 0); +} + + +void ReadBufferFromRabbitMQConsumer::initExchange() +{ + /* As there are 5 different types of exchanges and the type should be set as a parameter while publishing the message, + * then for uniformity this parameter should always be set as fanout-exchange type. In current implementation, the exchange, + * to which messages a published, will be bound to the exchange of the needed type, which will distribute messages according to its type. + */ + consumer_channel->declareExchange(exchange_name, AMQP::fanout).onError([&](const char * message) + { + exchange_declared = false; + LOG_ERROR(log, "Failed to declare fanout exchange: " << message); + }); + + if (hash_exchange) + { + current_exchange_name = exchange_name + "_hash"; + consumer_channel->declareExchange(current_exchange_name, AMQP::consistent_hash).onError([&](const char * message) + { + exchange_declared = false; + }); + + consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * message) + { + exchange_declared = false; + }); + } + else + { + current_exchange_name = exchange_name + "_direct"; + consumer_channel->declareExchange(current_exchange_name, AMQP::direct).onError([&](const char * message) + { + exchange_declared = false; + }); + + consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * message) + { + exchange_declared = false; + }); + } +} + + +void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) +{ + if (!exchange_declared) + { + initExchange(); + exchange_declared = true; + } + + bool bindings_ok = false, bindings_error = false; + + consumer_channel->declareQueue(AMQP::exclusive) + .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) + { + queues.emplace_back(queue_name_); + + String binding_key = routing_key; + + if (bind_by_id && !hash_exchange) + { + if (queues.size() == 1) + { + binding_key = routing_key + "_" + std::to_string(channel_id); + } + else + { + binding_key = routing_key + "_" + std::to_string(channel_id + queue_id); + } + } + + LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); + + consumer_channel->bindQueue(current_exchange_name, queue_name_, binding_key) + .onSuccess([&] + { + bindings_ok = true; + }) + .onError([&](const char * message) + { + bindings_error = true; + LOG_ERROR(log, "Failed to create queue binding: " << message); + }); + }) + .onError([&](const char * message) + { + bindings_error = true; + LOG_ERROR(log, "Failed to declare queue on the channel: " << message); + }); + + while (!bindings_ok && !bindings_error) + { + startNonBlockEventLoop(); + } +} + + +void ReadBufferFromRabbitMQConsumer::subscribeConsumer() +{ + if (subscribed) + return; + + LOG_TRACE(log, "Subscribing to " + std::to_string(queues.size()) + " queues"); + + for (auto & queue : queues) + { + subscribe(queue); + } + + subscribed = true; +} + + +void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) +{ + bool consumer_ok = false, consumer_error = false; + + consumer_channel->consume(queue_name, AMQP::noack) + .onSuccess([&](const std::string & consumer) + { + if (consumerTag == "") + consumerTag = consumer; + + consumer_ok = true; + + LOG_TRACE(log, "Consumer " + consumerTag + " is subscribed to queue " + queue_name); + }) + .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) + { + size_t message_size = message.bodySize(); + + if (message_size && message.body() != nullptr) + { + String message_received = std::string(message.body(), message.body() + message_size); + + if (row_delimiter != '\0') + message_received += row_delimiter; + + //LOG_TRACE(log, "Consumer " + consumerTag + " received the message " + message_received); + + received.push_back(message_received); + } + }) + .onError([&](const char * message) + { + consumer_error = true; + LOG_ERROR(log, "Consumer failed: " << message); + }); + + while (!consumer_ok && !consumer_error) + { + startNonBlockEventLoop(); + } +} + + +void ReadBufferFromRabbitMQConsumer::startNonBlockEventLoop() +{ + eventHandler.startNonBlock(); +} + + +bool ReadBufferFromRabbitMQConsumer::nextImpl() +{ + if (stopped || !allowed) + return false; + + if (current == messages.end()) + { + if (received.empty()) + { + /* Run the onReceived callbacks to save the messages that have been received by now + */ + startNonBlockEventLoop(); + } + + if (received.empty()) + { + LOG_TRACE(log, "Stalled"); + return false; + } + + messages.clear(); + messages.swap(received); + current = messages.begin(); + } + + auto new_position = const_cast(current->data()); + BufferBase::set(new_position, current->size(), 0); + + ++current; + allowed = false; + + return true; +} + +} diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h new file mode 100644 index 000000000000..7592fb53bfcb --- /dev/null +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Poco +{ + class Logger; +} + +namespace DB +{ + +using ChannelPtr = std::shared_ptr; + +class ReadBufferFromRabbitMQConsumer : public ReadBuffer +{ + +public: + ReadBufferFromRabbitMQConsumer( + std::pair & parsed_address, + const String & exchange_name_, + const String & routing_key_, + const size_t channel_id_, + Poco::Logger * log_, + char row_delimiter_, + const bool bind_by_id_, + const bool hash_exchange_, + const size_t num_queues_, + const std::atomic & stopped_); + + ~ReadBufferFromRabbitMQConsumer() override; + + void allowNext() { allowed = true; } // Allow to read next message. + void subscribeConsumer(); + +private: + using Messages = std::vector; + using Queues = std::vector; + + event_base * evbase; + RabbitMQHandler eventHandler; + AMQP::TcpConnection connection; + ChannelPtr consumer_channel; + + const String & exchange_name; + const String & routing_key; + const size_t channel_id; + const bool bind_by_id; + const bool hash_exchange; + + Poco::Logger * log; + char row_delimiter; + bool stalled = false; + bool allowed = true; + const std::atomic & stopped; + + std::atomic exchange_declared = false; + const size_t num_queues; + String consumerTag; // ID for the consumer + Queues queues; + bool subscribed = false; + String current_exchange_name; + + Messages received; + Messages messages; + Messages::iterator current; + + bool nextImpl() override; + + void initExchange(); + void initQueueBindings(const size_t queue_id); + void subscribe(const String & queue_name); + void startNonBlockEventLoop(); + +}; +} diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 98e7e97e2e1d..7e7da953d80a 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -26,11 +30,9 @@ #include #include #include - -#include -#include #include + namespace DB { @@ -42,6 +44,7 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } + StorageRabbitMQ::StorageRabbitMQ( const StorageID & table_id_, Context & context_, @@ -70,6 +73,228 @@ StorageRabbitMQ::StorageRabbitMQ( , semaphore(0, num_consumers_) , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) { + rabbitmq_context.makeQueryContext(); + + setColumns(columns_); + task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); + task->deactivate(); + + /// Enable a different routing algorithm. + bind_by_id = num_consumers > 1 || num_queues > 1 || bind_by_id; +} + + +Pipes StorageRabbitMQ::read( + const Names & column_names, + const SelectQueryInfo & /* query_info */, + const Context & context, + QueryProcessingStage::Enum /* processed_stage */, + size_t /* max_block_size */, + unsigned /* num_streams */) +{ + if (num_created_consumers == 0) + return {}; + + Pipes pipes; + pipes.reserve(num_created_consumers); + + for (size_t i = 0; i < num_created_consumers; ++i) + { + pipes.emplace_back(std::make_shared(std::make_shared( + *this, context, column_names, log))); + } + + LOG_DEBUG(log, "Starting reading " << pipes.size() << " streams"); + return pipes; +} + + +void StorageRabbitMQ::startup() +{ + for (size_t i = 0; i < num_consumers; ++i) + { + try + { + pushReadBuffer(createReadBuffer()); + ++num_created_consumers; + } + catch (const AMQP::Exception &) + { + tryLogCurrentException(log); + } + } + + task->activateAndSchedule(); +} + + +void StorageRabbitMQ::shutdown() +{ + stream_cancelled = true; + + for (size_t i = 0; i < num_created_consumers; ++i) + { + auto buffer = popReadBuffer(); + } + + task->deactivate(); +} + + +void StorageRabbitMQ::pushReadBuffer(ConsumerBufferPtr buffer) +{ + std::lock_guard lock(mutex); + buffers.push_back(buffer); + semaphore.set(); +} + + +ConsumerBufferPtr StorageRabbitMQ::popReadBuffer() +{ + return popReadBuffer(std::chrono::milliseconds::zero()); +} + + +ConsumerBufferPtr StorageRabbitMQ::popReadBuffer(std::chrono::milliseconds timeout) +{ + // Wait for the first free buffer + if (timeout == std::chrono::milliseconds::zero()) + semaphore.wait(); + else + { + if (!semaphore.tryWait(timeout.count())) + return nullptr; + } + + // Take the first available buffer from the list + std::lock_guard lock(mutex); + auto buffer = buffers.back(); + buffers.pop_back(); + + return buffer; +} + + +ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() +{ + if (update_channel_id) + next_channel_id += num_queues; + update_channel_id = true; + + return std::make_shared(parsed_address, exchange_name, routing_key, next_channel_id, + log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); +} + + +bool StorageRabbitMQ::checkDependencies(const StorageID & table_id) +{ + // Check if all dependencies are attached + auto dependencies = DatabaseCatalog::instance().getDependencies(table_id); + if (dependencies.empty()) + return true; + + // Check the dependencies are ready? + for (const auto & db_tab : dependencies) + { + auto table = DatabaseCatalog::instance().tryGetTable(db_tab); + if (!table) + return false; + + // If it materialized view, check it's target table + auto * materialized_view = dynamic_cast(table.get()); + if (materialized_view && !materialized_view->tryGetTargetTable()) + return false; + + // Check all its dependencies + if (!checkDependencies(db_tab)) + return false; + } + + return true; +} + + +void StorageRabbitMQ::threadFunc() +{ + try + { + auto table_id = getStorageID(); + // Check if at least one direct dependency is attached + size_t dependencies_count = DatabaseCatalog::instance().getDependencies(table_id).size(); + + if (dependencies_count) + { + // Keep streaming as long as there are attached views and streaming is not cancelled + while (!stream_cancelled && num_created_consumers > 0) + { + if (!checkDependencies(table_id)) + break; + + LOG_DEBUG(log, "Started streaming to " << dependencies_count << " attached views"); + + if (!streamToViews()) + break; + } + } + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + + /// Wait for attached views + if (!stream_cancelled) + task->scheduleAfter(500); +} + + +bool StorageRabbitMQ::streamToViews() +{ + auto table_id = getStorageID(); + auto table = DatabaseCatalog::instance().getTable(table_id); + if (!table) + throw Exception("Engine table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::LOGICAL_ERROR); + + // Create an INSERT query for streaming data + auto insert = std::make_shared(); + insert->table_id = table_id; + + InterpreterInsertQuery interpreter(insert, rabbitmq_context, false, true, true); + auto block_io = interpreter.execute(); + + // Create a stream for each consumer and join them in a union stream + BlockInputStreams streams; + streams.reserve(num_created_consumers); + + for (size_t i = 0; i < num_created_consumers; ++i) + { + auto stream = std::make_shared(*this, rabbitmq_context, block_io.out->getHeader().getNames(), log); + streams.emplace_back(stream); + + // Limit read batch to maximum block size to allow DDL + IBlockInputStream::LocalLimits limits; + const Settings & settings = global_context.getSettingsRef(); + limits.speed_limits.max_execution_time = settings.stream_flush_interval_ms; + limits.timeout_overflow_mode = OverflowMode::BREAK; + stream->setLimits(limits); + } + + // Join multiple streams if necessary + BlockInputStreamPtr in; + if (streams.size() > 1) + in = std::make_shared(streams, nullptr, streams.size()); + else + in = streams[0]; + + std::atomic stub = {false}; + copyData(*in, *block_io.out, &stub); + + // Check whether the limits were applied during query execution + bool limits_applied = false; + const BlockStreamProfileInfo & info = in->getProfileInfo(); + limits_applied = info.hasAppliedLimit(); + + return limits_applied; } @@ -246,4 +471,3 @@ NamesAndTypesList StorageRabbitMQ::getVirtuals() const } } - diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 37b8c2b1078b..8a3a48135b8a 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -1,4 +1,5 @@ #pragma once + #include #include #include @@ -6,8 +7,11 @@ #include #include #include +#include +#include #include + namespace DB { @@ -19,8 +23,30 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub public: std::string getName() const override { return "RabbitMQ"; } + bool supportsSettings() const override { return true; } + void startup() override; + void shutdown() override; + + Pipes read( + const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) override; + + void pushReadBuffer(ConsumerBufferPtr buf); + ConsumerBufferPtr popReadBuffer(); + ConsumerBufferPtr popReadBuffer(std::chrono::milliseconds timeout); + + const String & getExchangeName() const { return exchange_name; } + const String & getRoutingKey() const { return routing_key; } + + const String & getFormatName() const { return format_name; } + NamesAndTypesList getVirtuals() const override; + protected: StorageRabbitMQ( const StorageID & table_id_, @@ -31,6 +57,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub const String & format_name_, char row_delimiter_, size_t num_consumers_, bool bind_by_id_, size_t num_queues_, bool hash_exchange); + private: Context global_context; Context rabbitmq_context; @@ -42,7 +69,6 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub char row_delimiter; size_t num_consumers; size_t num_created_consumers = 0; - bool bind_by_id; size_t num_queues; const bool hash_exchange; @@ -52,11 +78,19 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Poco::Semaphore semaphore; std::mutex mutex; + std::vector buffers; /// available buffers for RabbitMQ consumers - size_t consumer_id = 0; + size_t next_channel_id = 0; + bool update_channel_id = false; BackgroundSchedulePool::TaskHolder task; std::atomic stream_cancelled{false}; + + ConsumerBufferPtr createReadBuffer(); + + void threadFunc(); + bool streamToViews(); + bool checkDependencies(const StorageID & table_id); }; } From 1760f01f74ee8d7fdc7ac39526ce1d041ff2fa2c Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 09:42:56 +0000 Subject: [PATCH 0121/1102] Add tests for RabbitMQ read-only part --- .../integration/test_storage_rabbitmq/test.py | 726 ++++++++++++++++++ 1 file changed, 726 insertions(+) diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 475b89f6c607..815a84c1999a 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -114,6 +114,732 @@ def rabbitmq_setup_teardown(): # Tests +@pytest.mark.timeout(180) +def test_rabbitmq_select_from_new_syntax_table(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'new', + rabbitmq_exchange_name = 'clickhouse-exchange', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for i in range(25): + messages.append(json.dumps({'key': i, 'value': i})) + + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='new', body=message) + + messages = [] + for i in range(25, 50): + messages.append(json.dumps({'key': i, 'value': i})) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='new', body=message) + + result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=False) + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_select_from_old_syntax_table(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ('rabbitmq1:5672', 'old', 'clickhouse-exchange', 'JSONEachRow', '\\n'); + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for i in range(50): + messages.append(json.dumps({'key': i, 'value': i})) + + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='old', body=message) + + result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_select_empty(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'empty', + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + ''') + + assert int(instance.query('SELECT count() FROM test.rabbitmq')) == 0 + + +@pytest.mark.timeout(180) +def test_rabbitmq_json_without_delimiter(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'json', + rabbitmq_exchange_name = 'clickhouse-exchange', + rabbitmq_format = 'JSONEachRow' + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = '' + for i in range(25): + messages += json.dumps({'key': i, 'value': i}) + '\n' + + all_messages = [messages] + for message in all_messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='json', body=message) + + messages = '' + for i in range(25, 50): + messages += json.dumps({'key': i, 'value': i}) + '\n' + all_messages = [messages] + for message in all_messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='json', body=message) + + result = '' + while True: + result += instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + if rabbitmq_check_result(result): + break + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_csv_with_delimiter(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'csv', + rabbitmq_exchange_name = 'clickhouse-exchange', + rabbitmq_format = 'CSV', + rabbitmq_row_delimiter = '\\n'; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for i in range(50): + messages.append('{i}, {i}'.format(i=i)) + + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='csv', body=message) + + result = '' + while True: + result += instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + if rabbitmq_check_result(result): + break + + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_tsv_with_delimiter(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'tsv', + rabbitmq_exchange_name = 'clickhouse-exchange', + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for i in range(50): + messages.append('{i}\t{i}'.format(i=i)) + + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='tsv', body=message) + + result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_materialized_view(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'mv', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view (key UInt64, value UInt64) + ENGINE = MergeTree() + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.rabbitmq; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + + messages = [] + for i in range(50): + messages.append(json.dumps({'key': i, 'value': i})) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='mv', body=message) + + while True: + result = instance.query('SELECT * FROM test.view') + if (rabbitmq_check_result(result)): + break; + + instance.query(''' + DROP TABLE test.consumer; + DROP TABLE test.view; + ''') + + connection.close() + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_materialized_view_with_subquery(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'mvsq', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view (key UInt64, value UInt64) + ENGINE = MergeTree() + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM (SELECT * FROM test.rabbitmq); + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + + messages = [] + for i in range(50): + messages.append(json.dumps({'key': i, 'value': i})) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='mvsq', body=message) + + while True: + result = instance.query('SELECT * FROM test.view') + if rabbitmq_check_result(result): + break + + instance.query(''' + DROP TABLE test.consumer; + DROP TABLE test.view; + ''') + + connection.close(); + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(180) +def test_rabbitmq_many_materialized_views(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.view1; + DROP TABLE IF EXISTS test.view2; + DROP TABLE IF EXISTS test.consumer1; + DROP TABLE IF EXISTS test.consumer2; + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'mmv', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view1 (key UInt64, value UInt64) + ENGINE = MergeTree() + ORDER BY key; + CREATE TABLE test.view2 (key UInt64, value UInt64) + ENGINE = MergeTree() + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer1 TO test.view1 AS + SELECT * FROM test.rabbitmq; + CREATE MATERIALIZED VIEW test.consumer2 TO test.view2 AS + SELECT * FROM test.rabbitmq; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + + messages = [] + for i in range(50): + messages.append(json.dumps({'key': i, 'value': i})) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='mmv', body=message) + + while True: + result1 = instance.query('SELECT * FROM test.view1') + result2 = instance.query('SELECT * FROM test.view2') + if rabbitmq_check_result(result1) and rabbitmq_check_result(result2): + break + + instance.query(''' + DROP TABLE test.consumer1; + DROP TABLE test.consumer2; + DROP TABLE test.view1; + DROP TABLE test.view2; + ''') + + rabbitmq_check_result(result1, True) + rabbitmq_check_result(result2, True) + + +@pytest.mark.timeout(240) +def test_rabbitmq_big_message(rabbitmq_cluster): + # Create batchs of messages of size ~100Kb + rabbitmq_messages = 1000 + batch_messages = 1000 + messages = [json.dumps({'key': i, 'value': 'x' * 100}) * batch_messages for i in range(rabbitmq_messages)] + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + + instance.query(''' + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.rabbitmq (key UInt64, value String) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'big', + rabbitmq_format = 'JSONEachRow'; + CREATE TABLE test.view (key UInt64, value String) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.rabbitmq; + ''') + + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key='big', body=message) + + while True: + result = instance.query('SELECT count() FROM test.view') + if int(result) == batch_messages * rabbitmq_messages: + break + + connection.close() + instance.query(''' + DROP TABLE test.consumer; + DROP TABLE test.view; + ''') + + assert int(result) == rabbitmq_messages*batch_messages, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_sharding_between_tables(rabbitmq_cluster): + + NUMBER_OF_CONCURRENT_CONSUMERS = 10 + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + for consumer_id in range(NUMBER_OF_CONCURRENT_CONSUMERS): + table_name = 'rabbitmq_consumer{}'.format(consumer_id) + print("Setting up {}".format(table_name)) + + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + DROP TABLE IF EXISTS test.{0}_mv; + CREATE TABLE test.{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_hash_exchange = 1, + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.{0}; + '''.format(table_name)) + + i = [0] + messages_num = 1000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + + def produce(): + # init connection here because otherwise python rabbitmq client fails sometimes + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = 'topic_' + str(randrange(0, NUMBER_OF_CONCURRENT_CONSUMERS)) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) + connection.close() + time.sleep(1) + + threads = [] + threads_num = 20 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + for consumer_id in range(NUMBER_OF_CONCURRENT_CONSUMERS): + print("dropping rabbitmq_consumer{}".format(consumer_id)) + table_name = 'rabbitmq_consumer{}'.format(consumer_id) + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + DROP TABLE IF EXISTS test.{0}_mv; + '''.format(table_name)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_sharding_between_channels_publish(rabbitmq_cluster): + + NUM_CHANNELS = 5 + + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'clickhouse', + rabbitmq_num_consumers = 5, + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.view (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.rabbitmq; + ''') + + time.sleep(1) + + i = [0] + messages_num = 10000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + def produce(): + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = 'clickhouse_' + str(randrange(0, NUM_CHANNELS)) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) + connection.close() + + threads = [] + threads_num = 20 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_sharding_between_queues_publish(rabbitmq_cluster): + + NUM_QUEUES = 4 + + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_queues = 4, + rabbitmq_routing_key = 'clickhouse', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.view (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.rabbitmq; + ''') + + time.sleep(1) + + i = [0] + messages_num = 10000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + def produce(): + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = 'clickhouse_' + str(randrange(0, NUM_QUEUES)) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) + connection.close() + + threads = [] + threads_num = 20 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_sharding_between_channels_and_queues_publish(rabbitmq_cluster): + + NUM_CONSUMERS = 10 + NUM_QUEUES = 2 + + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_queues = 2, + rabbitmq_num_consumers = 10, + rabbitmq_routing_key = 'clickhouse', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + DROP TABLE IF EXISTS test.view; + DROP TABLE IF EXISTS test.consumer; + CREATE TABLE test.view (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.rabbitmq; + ''') + + time.sleep(1) + + i = [0] + messages_num = 10000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + def produce(): + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = 'clickhouse_' + str(randrange(0, NUM_QUEUES * NUM_CONSUMERS)) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) + connection.close() + + threads = [] + threads_num = 20 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_read_only_combo(rabbitmq_cluster): + + NUM_MV = 5; + NUM_CONSUMERS = 4 + + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 4, + rabbitmq_routing_key = 'clickhouse', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + ''') + + for mv_id in range(NUM_MV): + table_name = 'view{}'.format(mv_id) + print("Setting up {}".format(table_name)) + + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + DROP TABLE IF EXISTS test.{0}_mv; + CREATE TABLE test.{0} (key UInt64, value UInt64) + ENGINE = MergeTree() + ORDER BY key; + CREATE MATERIALIZED VIEW test.{0}_mv TO test.{0} AS + SELECT * FROM test.rabbitmq; + '''.format(table_name)) + + time.sleep(2) + + i = [0] + messages_num = 10000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + def produce(): + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = 'clickhouse_' + str(randrange(0, NUM_CONSUMERS)) + for message in messages: + channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) + connection.close() + + threads = [] + threads_num = 20 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = 0 + for view in range(NUM_MV): + result += int(instance.query('SELECT count() FROM test.view{0}'.format(view))) + if int(result) == messages_num * threads_num * NUM_MV: + break + time.sleep(1) + + for thread in threads: + thread.join() + + for mv_id in range(NUM_MV): + table_name = 'view{}'.format(mv_id) + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + '''.format(table_name)) + + + assert int(result) == messages_num * threads_num * NUM_MV, 'ClickHouse lost some messages: {}'.format(result) if __name__ == '__main__': From fe8d285e11061efa82e7f92aabd2b5d976fc36fe Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 12:52:16 +0000 Subject: [PATCH 0122/1102] Fix libevent build --- contrib/libevent-cmake/{ => linux}/evconfig-private.h | 0 contrib/libevent-cmake/{ => linux/event2}/event-config.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename contrib/libevent-cmake/{ => linux}/evconfig-private.h (100%) rename contrib/libevent-cmake/{ => linux/event2}/event-config.h (100%) diff --git a/contrib/libevent-cmake/evconfig-private.h b/contrib/libevent-cmake/linux/evconfig-private.h similarity index 100% rename from contrib/libevent-cmake/evconfig-private.h rename to contrib/libevent-cmake/linux/evconfig-private.h diff --git a/contrib/libevent-cmake/event-config.h b/contrib/libevent-cmake/linux/event2/event-config.h similarity index 100% rename from contrib/libevent-cmake/event-config.h rename to contrib/libevent-cmake/linux/event2/event-config.h From c3569882bbcc33c047d9d9b9424bf06b9a50a3bf Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 20 May 2020 18:24:40 +0000 Subject: [PATCH 0123/1102] Update version of docker_compose_rabbitmq.yml --- docker/test/integration/compose/docker_compose_rabbitmq.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/test/integration/compose/docker_compose_rabbitmq.yml b/docker/test/integration/compose/docker_compose_rabbitmq.yml index 7ebee3c0ea56..1e9c3777505e 100644 --- a/docker/test/integration/compose/docker_compose_rabbitmq.yml +++ b/docker/test/integration/compose/docker_compose_rabbitmq.yml @@ -1,4 +1,4 @@ -version: '2.2' +version: '2.3' services: rabbitmq1: From 05938e562cb9215c8e33b8a6687183449683808d Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 11:07:24 +0300 Subject: [PATCH 0124/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 35 +--- .../SSDComplexKeyCacheDictionary.cpp | 182 +++++++----------- .../SSDComplexKeyCacheDictionary.h | 12 -- 3 files changed, 79 insertions(+), 150 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index a065b3671011..ad6b5cb4ea8f 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -263,7 +263,6 @@ size_t SSDCachePartition::appendBlock( Index cache_index; cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); - // Poco::Logger::get("wr").information(" block mem: " + std::to_string(current_memory_block_id) + " wb: " + std::to_string(write_buffer_size)); if (current_memory_block_id >= write_buffer_size) throw DB::Exception("lel " + std::to_string(current_memory_block_id) + " " + std::to_string(write_buffer_size) + " " + std::to_string(index), ErrorCodes::LOGICAL_ERROR); @@ -338,7 +337,6 @@ size_t SSDCachePartition::appendBlock( if (!flushed) { - // Poco::Logger::get("wr").information(" set: " + std::to_string(cache_index.getBlockId()) + " " + std::to_string(cache_index.getAddressInBlock())); key_to_index.set(ids[index], cache_index); ids_buffer.push_back(ids[index]); ++index; @@ -349,7 +347,6 @@ size_t SSDCachePartition::appendBlock( init_write_buffer(); } } - // Poco::Logger::get("wr").information("exit"); return ids.size() - begin; } @@ -362,7 +359,6 @@ void SSDCachePartition::flush() if (ids.empty()) return; Poco::Logger::get("paritiiton").information("flushing to SSD."); - // Poco::Logger::get("paritiiton").information("@@@@@@@@@@@@@@@@@@@@ FLUSH!!! " + std::to_string(file_id) + " block: " + std::to_string(current_file_block_id)); AIOContext aio_context{1}; @@ -426,7 +422,6 @@ void SSDCachePartition::flush() if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); - // Poco::Logger::get("pt").information("block: " + std::to_string(index.getBlockId()) + " " + std::to_string(current_file_block_id) + " "); index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); } key_to_index.set(id, index); @@ -571,7 +566,6 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice Memory read_buffer(block_size * read_buffer_size, BUFFER_ALIGNMENT); - // TODO: merge requests std::vector requests; std::vector pointers; std::vector> blocks_to_indices; @@ -601,10 +595,6 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice request.aio_fildes = fd; request.aio_buf = reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size); request.aio_nbytes = block_size; - // Poco::Logger::get("RR").information("block found" + std::to_string(index_to_out[i].first.getBlockId()) + " max_size" + std::to_string(max_size)); - // if (index_to_out[i].first.getBlockId() > max_size) { - // throw DB::Exception("kek", ErrorCodes::LOGICAL_ERROR); - // } request.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #endif @@ -619,16 +609,12 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice std::vector processed(requests.size(), false); std::vector events(requests.size()); for (auto & event : events) - event.res = -1; // TODO: remove + event.res = -1; size_t to_push = 0; size_t to_pop = 0; while (to_pop < requests.size()) { - // Poco::Logger::get("RR").information( - // "push = " + std::to_string(to_push) + " pop=" + std::to_string(to_pop) + - // "bi = " + std::to_string(blocks_to_indices.size()) + " req = " + std::to_string(requests.size())); - /// get io tasks from previous iteration int popped = 0; while (to_pop < to_push && (popped = io_getevents(aio_context.ctx, to_push - to_pop, to_push - to_pop, &events[to_pop], nullptr)) <= 0) { @@ -681,13 +667,11 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice throwFromErrno("io_submit: Failed to submit a request for asynchronous IO", ErrorCodes::CANNOT_IO_SUBMIT); } to_push += pushed; - // Poco::Logger::get("RR").information("fin iter"); } } void SSDCachePartition::clearOldestBlocks() { - // Poco::Logger::get("GC").information("GC clear -----------------"); // write_buffer_size, because we need to erase the whole buffer. Memory read_buffer_memory(block_size * write_buffer_size, BUFFER_ALIGNMENT); @@ -708,8 +692,6 @@ void SSDCachePartition::clearOldestBlocks() request.aio_data = 0; #endif - // Poco::Logger::get("GC").information("GC offset=" + std::to_string(request.aio_offset)); - { iocb* request_ptr = &request; io_event event{}; @@ -738,7 +720,6 @@ void SSDCachePartition::clearOldestBlocks() std::vector keys; keys.reserve(write_buffer_size); - // TODO: писать кол-во значений for (size_t i = 0; i < write_buffer_size; ++i) { ReadBufferFromMemory read_buffer(read_buffer_memory.data() + i * block_size, block_size); @@ -753,7 +734,6 @@ void SSDCachePartition::clearOldestBlocks() uint32_t keys_in_current_block = 0; readBinary(keys_in_current_block, read_buffer); - // Poco::Logger::get("GC").information("keys in block: " + std::to_string(keys_in_current_block) + " offset=" + std::to_string(read_buffer.offset())); for (uint32_t j = 0; j < keys_in_current_block; ++j) { @@ -804,7 +784,6 @@ void SSDCachePartition::clearOldestBlocks() const size_t start_block = current_file_block_id % max_size; const size_t finish_block = start_block + write_buffer_size; - Poco::Logger::get("partition gc").information("erasing keys start = " + std::to_string(start_block) + " end = " + std::to_string(finish_block)); for (const auto& key : keys) { Index index; @@ -883,7 +862,7 @@ PaddedPODArray SSDCachePartition::getCachedIds(const std std::unique_lock lock(rw_lock); // Begin and end iterators can be changed. PaddedPODArray array; for (const auto & key : key_to_index.keys()) - array.push_back(key); // TODO: exclude default + array.push_back(key); return array; } @@ -1185,7 +1164,7 @@ void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector std::rethrow_exception(last_update_exception); } - // Set key + /// Set key std::get>(new_keys.values).push_back(id); std::uniform_int_distribution distribution{lifetime.min_sec, lifetime.max_sec}; @@ -1193,7 +1172,7 @@ void SSDCacheStorage::update(DictionarySourcePtr & source_ptr, const std::vector metadata.back().setExpiresAt(now + std::chrono::seconds(distribution(rnd_engine))); metadata.back().setDefault(); - /// inform caller that the cell has not been found + /// Inform caller that the cell has not been found on_id_not_found(id); } @@ -1306,11 +1285,7 @@ SSDCacheDictionary::SSDCacheDictionary( const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ const auto null_value = std::get(null_values[index]); /* NOLINT */ \ - getItemsNumberImpl( /* NOLINT */ \ - index, /* NOLINT */ \ - ids, /* NOLINT */ \ - out, /* NOLINT */ \ - [&](const size_t) { return null_value; }); /* NOLINT */ \ + getItemsNumberImpl(index, ids, out, [&](const size_t) { return null_value; }); /* NOLINT */ \ } DECLARE(UInt8) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 0a97c59f5246..df636baa19e3 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -92,8 +92,6 @@ namespace constexpr UInt8 HAS_NOT_FOUND = 2; - //constexpr UInt16 MAX_KEY_SIZE = std::numeric_limits::max(); - const std::string BIN_FILE_EXT = ".bin"; const std::string IND_FILE_EXT = ".idx"; @@ -287,7 +285,6 @@ size_t SSDComplexKeyCachePartition::append( for (size_t index = begin; index < keys.size();) { - //Poco::Logger::get("test").information("wb off: " + std::to_string(write_buffer->offset())); Index cache_index; cache_index.setInMemory(true); cache_index.setBlockId(current_memory_block_id); @@ -304,8 +301,6 @@ size_t SSDComplexKeyCachePartition::append( writeBinary(metadata[index].data, *write_buffer); } - //Poco::Logger::get("test key").information("wb off: " + std::to_string(write_buffer->offset())); - for (const auto & attribute : new_attributes) { if (flushed) @@ -322,7 +317,7 @@ size_t SSDComplexKeyCachePartition::append( } \ else \ { \ - const auto & values = std::get>(attribute.values); \ + const auto & values = std::get>(attribute.values); /* NOLINT */ \ writeBinary(values[index], *write_buffer); \ } \ } \ @@ -372,7 +367,6 @@ size_t SSDComplexKeyCachePartition::append( { init_write_buffer(); } - //Poco::Logger::get("test final").information("wb off: " + std::to_string(write_buffer->offset())); } return keys.size() - begin; } @@ -406,8 +400,6 @@ void SSDComplexKeyCachePartition::flush() write_request.aio_offset = (current_file_block_id % max_size) * block_size; #endif - //Poco::Logger::get("try:").information("offset: " + std::to_string(write_request.aio_offset) + " nbytes: " + std::to_string(write_request.aio_nbytes)); - while (io_submit(aio_context.ctx, 1, &write_request_ptr) < 0) { if (errno != EINTR) @@ -443,20 +435,18 @@ void SSDComplexKeyCachePartition::flush() throwFromErrnoWithPath("Cannot fsync " + path + BIN_FILE_EXT, path + BIN_FILE_EXT, ErrorCodes::CANNOT_FSYNC); /// commit changes in index - for (size_t row = 0; row < keys_buffer.size(); ++row) + for (auto & key : keys_buffer) { Index index; - //Poco::Logger::get("get:").information("sz = " + std::to_string(keys_buffer[row].size())); - if (key_to_index.getKeyAndValue(keys_buffer[row], index)) + if (key_to_index.getKeyAndValue(key, index)) { if (index.inMemory()) // Row can be inserted in the buffer twice, so we need to move to ssd only the last index. { index.setInMemory(false); index.setBlockId((current_file_block_id % max_size) + index.getBlockId()); } - key_to_index.set(keys_buffer[row], index); + key_to_index.set(key, index); } - //Poco::Logger::get("get:").information("finish"); } current_file_block_id += write_buffer_size; @@ -652,7 +642,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray processed(requests.size(), false); std::vector events(requests.size()); for (auto & event : events) - event.res = -1; // TODO: remove + event.res = -1; size_t to_push = 0; size_t to_pop = 0; @@ -714,7 +704,6 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray erasing keys <"); for (const auto& key : keys) { - //Poco::Logger::get("ClearOldestBlocks").information("ktest: null=" + std::to_string(key.isNull())); - //Poco::Logger::get("ClearOldestBlocks").information("ktest: data=" + std::to_string(reinterpret_cast(key.fullData()))); - //Poco::Logger::get("ClearOldestBlocks").information("ktest: sz=" + std::to_string(key.size()) + " fz=" + std::to_string(key.fullSize())); Index index; if (key_to_index.get(key, index)) { - //Poco::Logger::get("ClearOldestBlocks").information("erase"); size_t block_id = index.getBlockId(); if (start_block <= block_id && block_id < finish_block) key_to_index.erase(key); } - //Poco::Logger::get("ClearOldestBlocks").information("finish"); } } @@ -1048,6 +1026,67 @@ void SSDComplexKeyCacheStorage::has( hit_count.fetch_add(n - count_not_found, std::memory_order_release); } +namespace +{ +SSDComplexKeyCachePartition::Attributes createAttributesFromBlock( + const Block & block, const size_t begin_column, const std::vector & structure) +{ + SSDComplexKeyCachePartition::Attributes attributes; + + const auto columns = block.getColumns(); + for (size_t i = 0; i < structure.size(); ++i) + { + const auto & column = columns[i + begin_column]; + switch (structure[i]) + { +#define DISPATCH(TYPE) \ + case AttributeUnderlyingType::ut##TYPE: \ + { \ + SSDComplexKeyCachePartition::Attribute::Container values(column->size()); \ + memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ + attributes.emplace_back(); \ + attributes.back().type = structure[i]; \ + attributes.back().values = std::move(values); \ + } \ + break; + + DISPATCH(UInt8) + DISPATCH(UInt16) + DISPATCH(UInt32) + DISPATCH(UInt64) + DISPATCH(UInt128) + DISPATCH(Int8) + DISPATCH(Int16) + DISPATCH(Int32) + DISPATCH(Int64) + DISPATCH(Decimal32) + DISPATCH(Decimal64) + DISPATCH(Decimal128) + DISPATCH(Float32) + DISPATCH(Float64) +#undef DISPATCH + + case AttributeUnderlyingType::utString: + { + attributes.emplace_back(); + SSDComplexKeyCachePartition::Attribute::Container values(column->size()); + for (size_t j = 0; j < column->size(); ++j) + { + const auto ref = column->getDataAt(j); + values[j].resize(ref.size); + memcpy(values[j].data(), ref.data, ref.size); + } + attributes.back().type = structure[i]; + attributes.back().values = std::move(values); + } + break; + } + } + + return attributes; +} +} // namespace + template void SSDComplexKeyCacheStorage::update( DictionarySourcePtr & source_ptr, @@ -1202,7 +1241,7 @@ void SSDComplexKeyCacheStorage::update( if (update_error_count) { - /// TODO: юзать старые значения. + /// TODO: use old values. /// We don't have expired data for that `id` so all we can do is to rethrow `last_exception`. std::rethrow_exception(last_update_exception); @@ -1267,64 +1306,6 @@ void SSDComplexKeyCacheStorage::collectGarbage() } } -SSDComplexKeyCachePartition::Attributes SSDComplexKeyCacheStorage::createAttributesFromBlock( - const Block & block, const size_t begin_column, const std::vector & structure) -{ - SSDComplexKeyCachePartition::Attributes attributes; - - const auto columns = block.getColumns(); - for (size_t i = 0; i < structure.size(); ++i) - { - const auto & column = columns[i + begin_column]; - switch (structure[i]) - { -#define DISPATCH(TYPE) \ - case AttributeUnderlyingType::ut##TYPE: \ - { \ - SSDComplexKeyCachePartition::Attribute::Container values(column->size()); \ - memcpy(&values[0], column->getRawData().data, sizeof(TYPE) * values.size()); \ - attributes.emplace_back(); \ - attributes.back().type = structure[i]; \ - attributes.back().values = std::move(values); \ - } \ - break; - - DISPATCH(UInt8) - DISPATCH(UInt16) - DISPATCH(UInt32) - DISPATCH(UInt64) - DISPATCH(UInt128) - DISPATCH(Int8) - DISPATCH(Int16) - DISPATCH(Int32) - DISPATCH(Int64) - DISPATCH(Decimal32) - DISPATCH(Decimal64) - DISPATCH(Decimal128) - DISPATCH(Float32) - DISPATCH(Float64) -#undef DISPATCH - - case AttributeUnderlyingType::utString: - { - attributes.emplace_back(); - SSDComplexKeyCachePartition::Attribute::Container values(column->size()); - for (size_t j = 0; j < column->size(); ++j) - { - const auto ref = column->getDataAt(j); - values[j].resize(ref.size); - memcpy(values[j].data(), ref.data, ref.size); - } - attributes.back().type = structure[i]; - attributes.back().values = std::move(values); - } - break; - } - } - - return attributes; -} - SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( const std::string & name_, const DictionaryStructure & dict_struct_, @@ -1368,13 +1349,8 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ - const auto null_value = std::get(null_values[index]); \ - getItemsNumberImpl( \ - index, \ - key_columns, \ - key_types, \ - out, \ - [&](const size_t) { return null_value; }); \ + const auto null_value = std::get(null_values[index]); /* NOLINT */ \ + getItemsNumberImpl( index, key_columns, key_types, out, [&](const size_t) { return null_value; }); /* NOLINT */ \ } DECLARE(UInt8) @@ -1403,12 +1379,7 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ - getItemsNumberImpl( \ - index, \ - key_columns, \ - key_types, \ - out, \ - [&](const size_t row) { return def[row]; }); \ + getItemsNumberImpl(index, key_columns, key_types, out, [&](const size_t row) { return def[row]; }); /* NOLINT */ \ } DECLARE(UInt8) DECLARE(UInt16) @@ -1436,12 +1407,7 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( { \ const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ - getItemsNumberImpl( \ - index, \ - key_columns, \ - key_types, \ - out, \ - [&](const size_t) { return def; }); \ + getItemsNumberImpl(index, key_columns, key_types, out, [&](const size_t) { return def; }); /* NOLINT */ \ } DECLARE(UInt8) DECLARE(UInt16) @@ -1708,7 +1674,7 @@ AttributeValueVariant SSDComplexKeyCacheDictionary::createAttributeNullValueWith { #define DISPATCH(TYPE) \ case AttributeUnderlyingType::ut##TYPE: \ - return createAttributeNullValueWithTypeImpl(null_value); + return createAttributeNullValueWithTypeImpl(null_value); /* NOLINT */ DISPATCH(UInt8) DISPATCH(UInt16) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index b6717d16f656..7809bd1909d1 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -200,7 +200,6 @@ class ComplexKeysPoolImpl { UInt16 sz; readBinary(sz, buf); - //Poco::Logger::get("test read key").information("sz " + std::to_string(sz)); char * data = nullptr; if constexpr (std::is_same_v) data = arena.alloc(); @@ -209,7 +208,6 @@ class ComplexKeysPoolImpl memcpy(data, &sz, sizeof(sz)); buf.read(data + sizeof(sz), sz); key = KeyRef(data); - //Poco::Logger::get("test read key").information("ksz = " + std::to_string(key.size())); } void ignoreKey(ReadBuffer & buf) const @@ -478,9 +476,6 @@ class SSDComplexKeyCacheStorage double getLoadFactor() const; private: - SSDComplexKeyCachePartition::Attributes createAttributesFromBlock( - const Block & block, const size_t begin_column, const std::vector & structure); - void collectGarbage(); const AttributeTypes attributes_structure; @@ -505,9 +500,6 @@ class SSDComplexKeyCacheStorage mutable size_t update_error_count = 0; mutable std::chrono::system_clock::time_point backoff_end_time; - // stats - //mutable size_t bytes_allocated = 0; - mutable std::atomic hit_count{0}; mutable std::atomic query_count{0}; }; @@ -569,10 +561,6 @@ class SSDComplexKeyCacheDictionary final : public IDictionaryBase return dict_struct.attributes[getAttributeIndex(attribute_name)].injective; } - /*bool hasHierarchy() const { return false; } - - void toParent(const PaddedPODArray &, PaddedPODArray &) const { }*/ - std::exception_ptr getLastException() const override { return storage.getLastException(); } template From e7324ec4d740cc8f579b9ef756837d0e6c7fff35 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 13:41:27 +0300 Subject: [PATCH 0125/1102] docs --- .../external-dicts-dict-layout.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md b/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md index bdadf97cd11e..3bc49c25480f 100644 --- a/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md +++ b/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md @@ -54,10 +54,12 @@ LAYOUT(LAYOUT_TYPE(param value)) -- layout settings - [hashed](#dicts-external_dicts_dict_layout-hashed) - [sparse\_hashed](#dicts-external_dicts_dict_layout-sparse_hashed) - [cache](#cache) +- [ssd\_cache](#ssd-cache) - [direct](#direct) - [range\_hashed](#range-hashed) - [complex\_key\_hashed](#complex-key-hashed) - [complex\_key\_cache](#complex-key-cache) +- [ssd\_complex\_key\_cache](#ssd-cache) - [complex\_key\_direct](#complex-key-direct) - [ip\_trie](#ip-trie) @@ -296,6 +298,40 @@ Set a large enough cache size. You need to experiment to select the number of ce This type of storage is for use with composite [keys](external-dicts-dict-structure.md). Similar to `cache`. +### ssd\_cache {#ssd-cache} + +Similar to `cache`, but stores data on SSD and index in RAM. + +``` xml + + + + 4096 + + 16777216 + + 131072 + + 1048576 + + /var/lib/clickhouse/clickhouse_dictionaries/test_dict + + 1048576 + + +``` + +or + +``` sql +LAYOUT(CACHE(BLOCK_SIZE 4096 FILE_SIZE 16777216 READ_BUFFER_SIZE 1048576 + PATH /var/lib/clickhouse/clickhouse_dictionaries/test_dict MAX_STORED_KEYS 1048576)) +``` + +### complex\_key\_ssd\_cache {#complex-key-ssd-cache} + +This type of storage is for use with composite [keys](external-dicts-dict-structure.md). Similar to `ssd\_cache`. + ### direct {#direct} The dictionary is not stored in memory and directly goes to the source during the processing of a request. From deb0e2f199513689583ba97a5a407524fb8c8806 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 13:42:02 +0300 Subject: [PATCH 0126/1102] parts count --- src/Dictionaries/SSDCacheDictionary.cpp | 1 + src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index ad6b5cb4ea8f..8c9d61223dfd 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1619,6 +1619,7 @@ void registerDictionarySSDCache(DictionaryFactory & factory) const auto max_partitions_count = config.getInt(layout_prefix + ".ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + max_partitions_count = 1; const auto block_size = config.getInt(layout_prefix + ".ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index df636baa19e3..64b40833eb28 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1733,6 +1733,7 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) const auto max_partitions_count = config.getInt(layout_prefix + ".complex_key_ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; + max_partitions_count = 1; const auto block_size = config.getInt(layout_prefix + ".complex_key_ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) From c25850861086a9188c0c34e5357fa9ced1ac34b4 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 17:16:52 +0300 Subject: [PATCH 0127/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 1 - src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 8c9d61223dfd..ad6b5cb4ea8f 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1619,7 +1619,6 @@ void registerDictionarySSDCache(DictionaryFactory & factory) const auto max_partitions_count = config.getInt(layout_prefix + ".ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) throw Exception{name + ": dictionary of layout 'ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; - max_partitions_count = 1; const auto block_size = config.getInt(layout_prefix + ".ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 64b40833eb28..df636baa19e3 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1733,7 +1733,6 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) const auto max_partitions_count = config.getInt(layout_prefix + ".complex_key_ssd_cache.max_partitions_count", DEFAULT_PARTITIONS_COUNT); if (max_partitions_count <= 0) throw Exception{name + ": dictionary of layout 'complex_key_ssd_cache' cannot have 0 (or less) max_partitions_count", ErrorCodes::BAD_ARGUMENTS}; - max_partitions_count = 1; const auto block_size = config.getInt(layout_prefix + ".complex_key_ssd_cache.block_size", DEFAULT_SSD_BLOCK_SIZE); if (block_size <= 0) From 016964caedd534b7cc961a110c25198a6ccdd39c Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 17:19:14 +0300 Subject: [PATCH 0128/1102] style fix --- src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index df636baa19e3..0247a896f625 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1085,7 +1085,7 @@ SSDComplexKeyCachePartition::Attributes createAttributesFromBlock( return attributes; } -} // namespace +} template void SSDComplexKeyCacheStorage::update( @@ -1350,8 +1350,8 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ const auto null_value = std::get(null_values[index]); /* NOLINT */ \ - getItemsNumberImpl( index, key_columns, key_types, out, [&](const size_t) { return null_value; }); /* NOLINT */ \ - } + getItemsNumberImpl( index, key_columns, key_types, out, [&](const size_t) { return null_value; }); \ + } /* NOLINT */ DECLARE(UInt8) DECLARE(UInt16) From 1b9e2df78e022c1c7ce73b640c660d151fda9bb4 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 19:10:19 +0300 Subject: [PATCH 0129/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 1 + src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index ad6b5cb4ea8f..e5023fe32d6f 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -633,6 +633,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + ", returned=" + std::to_string(events[i].res) + ", errno=" + std::to_string(errno), ErrorCodes::AIO_READ_ERROR); } + __msan_unpoison(reinterpret_cast(request.aio_buf), request.aio_nbytes); uint64_t checksum = 0; ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); readBinary(checksum, buf_special); diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 0247a896f625..57c9b55143e1 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -664,7 +664,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray(request.aio_buf), request.aio_nbytes); uint64_t checksum = 0; ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); readBinary(checksum, buf_special); From 3fb0eab11664b8985aaeef69d738595a659ab13b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 19:11:20 +0300 Subject: [PATCH 0130/1102] fix --- src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 57c9b55143e1..73fe382c853b 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1350,8 +1350,8 @@ SSDComplexKeyCacheDictionary::SSDComplexKeyCacheDictionary( const auto index = getAttributeIndex(attribute_name); \ checkAttributeType(name, attribute_name, dict_struct.attributes[index].underlying_type, AttributeUnderlyingType::ut##TYPE); \ const auto null_value = std::get(null_values[index]); /* NOLINT */ \ - getItemsNumberImpl( index, key_columns, key_types, out, [&](const size_t) { return null_value; }); \ - } /* NOLINT */ + getItemsNumberImpl(index, key_columns, key_types, out, [&](const size_t) { return null_value; }); /* NOLINT */ \ + } DECLARE(UInt8) DECLARE(UInt16) From 7358410a847d879ec3ce6e0d8c15bf622089b2f2 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 23 May 2020 23:50:49 +0300 Subject: [PATCH 0131/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 2 ++ src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 12 ++---------- src/Dictionaries/SSDComplexKeyCacheDictionary.h | 4 ---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index e5023fe32d6f..d0830248b3df 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -716,6 +716,8 @@ void SSDCachePartition::clearOldestBlocks() "aio_nbytes=" + std::to_string(request.aio_nbytes) + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); } + + __msan_unpoison(read_buffer_memory.data(), read_buffer_memory.size()); } std::vector keys; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 73fe382c853b..ae3a3f4187f6 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -747,6 +747,8 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() "aio_nbytes=" + std::to_string(request.aio_nbytes) + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); } + + __msan_unpoison(read_buffer_memory.data(), read_buffer_memory.size()); } TemporalComplexKeysPool tmp_keys_pool; @@ -891,11 +893,6 @@ size_t SSDComplexKeyCachePartition::getBytesAllocated() const (keys_buffer_pool ? keys_buffer_pool->size() : 0) + (memory ? memory->size() : 0); } -PaddedPODArray SSDComplexKeyCachePartition::getCachedIds(const std::chrono::system_clock::time_point /* now */) const -{ - throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); -} - void SSDComplexKeyCachePartition::remove() { std::unique_lock lock(rw_lock); @@ -1267,11 +1264,6 @@ void SSDComplexKeyCacheStorage::update( ProfileEvents::increment(ProfileEvents::DictCacheRequests); } -PaddedPODArray SSDComplexKeyCacheStorage::getCachedIds() const -{ - throw DB::Exception("Method not supported.", ErrorCodes::NOT_IMPLEMENTED); -} - double SSDComplexKeyCacheStorage::getLoadFactor() const { double result = 0; diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 7809bd1909d1..79f5ddead2ff 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -360,8 +360,6 @@ class SSDComplexKeyCachePartition size_t getId() const; - PaddedPODArray getCachedIds(const std::chrono::system_clock::time_point now) const; - double getLoadFactor() const; size_t getElementCount() const; @@ -461,8 +459,6 @@ class SSDComplexKeyCacheStorage PresentIdHandler && on_updated, AbsentIdHandler && on_key_not_found, const DictionaryLifetime lifetime); - PaddedPODArray getCachedIds() const; - std::exception_ptr getLastException() const { return last_update_exception; } const std::string & getPath() const { return path; } From c70401b1e44ce48327f35aa6fcdf77de2861e4dc Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 24 May 2020 11:56:34 +0300 Subject: [PATCH 0132/1102] fix other os --- src/Dictionaries/SSDCacheDictionary.cpp | 59 ++++++++++++++----- src/Dictionaries/SSDCacheDictionary.h | 4 ++ .../SSDComplexKeyCacheDictionary.cpp | 4 ++ .../SSDComplexKeyCacheDictionary.h | 4 ++ src/Dictionaries/registerDictionaries.h | 2 + src/Functions/FunctionsExternalDictionaries.h | 22 +++++++ 6 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index d0830248b3df..b172f8bf9bf1 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -1,3 +1,5 @@ +#if defined(__linux__) || defined(__FreeBSD__) + #include "SSDCacheDictionary.h" #include @@ -407,7 +409,7 @@ void SSDCachePartition::flush() ProfileEvents::increment(ProfileEvents::WriteBufferAIOWrite); ProfileEvents::increment(ProfileEvents::WriteBufferAIOWriteBytes, bytes_written); - if (bytes_written != static_cast(write_request.aio_nbytes)) + if (bytes_written != static_cast(block_size * write_buffer_size)) throw Exception("Not all data was written for asynchronous IO on file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(bytes_written), ErrorCodes::AIO_WRITE_ERROR); if (::fsync(fd) < 0) @@ -574,8 +576,14 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice blocks_to_indices.reserve(index_to_out.size()); for (size_t i = 0; i < index_to_out.size(); ++i) { + #if defined(__FreeBSD__) + const auto back_offset = requests.back().aio.aio_offset; + #else + const auto back_offset = requests.back().aio_offset; + #endif + if (!requests.empty() && - static_cast(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * block_size) + static_cast(back_offset) == index_to_out[i].first.getBlockId() * block_size) { blocks_to_indices.back().push_back(i); continue; @@ -586,9 +594,9 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice request.aio.aio_lio_opcode = LIO_READ; request.aio.aio_fildes = fd; request.aio.aio_buf = reinterpret_cast( - reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS)); - request.aio.aio_nbytes = SSD_BLOCK_SIZE; - request.aio.aio_offset = index_to_out[i].first; + reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size)); + request.aio.aio_nbytes = block_size; + request.aio.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #else request.aio_lio_opcode = IOCB_CMD_PREAD; @@ -608,8 +616,13 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice std::vector processed(requests.size(), false); std::vector events(requests.size()); + #if defined(__FreeBSD__) + for (auto & event : events) + event.udata = -1; + #else for (auto & event : events) event.res = -1; + #endif size_t to_push = 0; size_t to_pop = 0; @@ -626,18 +639,34 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice { const auto request_id = events[i].data; const auto & request = requests[request_id]; - if (events[i].res != static_cast(request.aio_nbytes)) + + #if defined(__FreeBSD__) + const auto bytes_written = aio_return(reinterpret_cast(events[i].udata)); + #else + const auto bytes_written = events[i].res; + #endif + + if (bytes_written != static_cast(block_size)) { - throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + - "request_id= " + std::to_string(request.aio_data) + "/ " + std::to_string(requests.size()) + - ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + - ", returned=" + std::to_string(events[i].res) + ", errno=" + std::to_string(errno), ErrorCodes::AIO_READ_ERROR); + #if defined(__FreeBSD__) + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + "."); + #else + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "request_id= " + std::to_string(request.aio_data) + "/ " + std::to_string(requests.size()) + + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + + ", returned=" + std::to_string(events[i].res) + ", errno=" + std::to_string(errno), ErrorCodes::AIO_READ_ERROR); + #endif } - __msan_unpoison(reinterpret_cast(request.aio_buf), request.aio_nbytes); + #if defined(__FreeBSD__) + const auto* buf_ptr = reinterpret_cast(request.aio.aio_buf); + #else + const auto* buf_ptr = reinterpret_cast(request.aio_buf); + #endif + __msan_unpoison(buf_ptr, block_size); uint64_t checksum = 0; - ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); + ReadBufferFromMemory buf_special(buf_ptr, block_size); readBinary(checksum, buf_special); - uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(reinterpret_cast(request.aio_buf) + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(buf_ptr + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); if (checksum != calculated_checksum) { throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); @@ -647,7 +676,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice { const auto & [file_index, out_index] = index_to_out[idx]; ReadBufferFromMemory buf( - reinterpret_cast(request.aio_buf) + file_index.getAddressInBlock(), + buf_ptr + file_index.getAddressInBlock(), block_size - file_index.getAddressInBlock()); set(out_index, buf); } @@ -1667,3 +1696,5 @@ void registerDictionarySSDCache(DictionaryFactory & factory) } } + +#endif diff --git a/src/Dictionaries/SSDCacheDictionary.h b/src/Dictionaries/SSDCacheDictionary.h index 6352d3a25222..3525d5d44835 100644 --- a/src/Dictionaries/SSDCacheDictionary.h +++ b/src/Dictionaries/SSDCacheDictionary.h @@ -1,5 +1,7 @@ #pragma once +#if defined(__linux__) || defined(__FreeBSD__) + #include "DictionaryStructure.h" #include "IDictionary.h" #include "IDictionarySource.h" @@ -454,3 +456,5 @@ class SSDCacheDictionary final : public IDictionary }; } + +#endif diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index ae3a3f4187f6..d13b9469132b 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -1,3 +1,5 @@ +#if defined(__linux__) || defined(__FreeBSD__) + #include "SSDComplexKeyCacheDictionary.h" #include @@ -1770,3 +1772,5 @@ void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory) } } + +#endif diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.h b/src/Dictionaries/SSDComplexKeyCacheDictionary.h index 79f5ddead2ff..dcd9deb29ac3 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.h +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.h @@ -1,5 +1,7 @@ #pragma once +#if defined(__linux__) || defined(__FreeBSD__) + #include "DictionaryStructure.h" #include "IDictionary.h" #include "IDictionarySource.h" @@ -685,3 +687,5 @@ class SSDComplexKeyCacheDictionary final : public IDictionaryBase }; } + +#endif diff --git a/src/Dictionaries/registerDictionaries.h b/src/Dictionaries/registerDictionaries.h index 05eeccefb8bb..bca96159c9df 100644 --- a/src/Dictionaries/registerDictionaries.h +++ b/src/Dictionaries/registerDictionaries.h @@ -25,8 +25,10 @@ void registerDictionaryTrie(DictionaryFactory & factory); void registerDictionaryFlat(DictionaryFactory & factory); void registerDictionaryHashed(DictionaryFactory & factory); void registerDictionaryCache(DictionaryFactory & factory); +#if defined(__linux__) || defined(__FreeBSD__) void registerDictionarySSDCache(DictionaryFactory & factory); void registerDictionarySSDComplexKeyCache(DictionaryFactory & factory); +#endif void registerDictionaryPolygon(DictionaryFactory & factory); void registerDictionaryDirect(DictionaryFactory & factory); diff --git a/src/Functions/FunctionsExternalDictionaries.h b/src/Functions/FunctionsExternalDictionaries.h index cb5be77a332c..1e8c41cc724b 100644 --- a/src/Functions/FunctionsExternalDictionaries.h +++ b/src/Functions/FunctionsExternalDictionaries.h @@ -29,8 +29,10 @@ #include #include #include +#if defined(__linux__) || defined(__FreeBSD__) #include #include +#endif #include #include #include @@ -175,10 +177,14 @@ class FunctionDictHas final : public IFunction if (!executeDispatchSimple(block, arguments, result, dict) && !executeDispatchSimple(block, arguments, result, dict) && !executeDispatchSimple(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchSimple(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && @@ -327,10 +333,14 @@ class FunctionDictGetString final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && @@ -506,10 +516,14 @@ class FunctionDictGetStringOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && @@ -841,10 +855,14 @@ class FunctionDictGet final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && @@ -1097,10 +1115,14 @@ class FunctionDictGetOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && +#if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && +#endif !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && From e76cdbdc43b98be5e70f8c2d7c7f451d31da5637 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 24 May 2020 14:24:45 +0300 Subject: [PATCH 0133/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 24 +++++++++++------------ src/Dictionaries/registerDictionaries.cpp | 2 ++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index b172f8bf9bf1..022508b07b54 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -577,13 +577,12 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice for (size_t i = 0; i < index_to_out.size(); ++i) { #if defined(__FreeBSD__) - const auto back_offset = requests.back().aio.aio_offset; + const size_t back_offset = requests.empty() ? -1 : static_cast(requests.back().aio.aio_offset); #else - const auto back_offset = requests.back().aio_offset; + const size_t back_offset = requests.empty() ? -1 : static_cast(requests.back().aio_offset); #endif - if (!requests.empty() && - static_cast(back_offset) == index_to_out[i].first.getBlockId() * block_size) + if (!requests.empty() && back_offset == index_to_out[i].first.getBlockId() * block_size) { blocks_to_indices.back().push_back(i); continue; @@ -616,10 +615,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice std::vector processed(requests.size(), false); std::vector events(requests.size()); - #if defined(__FreeBSD__) - for (auto & event : events) - event.udata = -1; - #else + #if defined(__linux__) for (auto & event : events) event.res = -1; #endif @@ -649,7 +645,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice if (bytes_written != static_cast(block_size)) { #if defined(__FreeBSD__) - throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + "."); + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); #else throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + "request_id= " + std::to_string(request.aio_data) + "/ " + std::to_string(requests.size()) + @@ -658,7 +654,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice #endif } #if defined(__FreeBSD__) - const auto* buf_ptr = reinterpret_cast(request.aio.aio_buf); + const volatile auto* buf_ptr = reinterpret_cast(request.aio.aio_buf); #else const auto* buf_ptr = reinterpret_cast(request.aio_buf); #endif @@ -739,13 +735,15 @@ void SSDCachePartition::clearOldestBlocks() throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); } +#if defined(__FreeBSD__) + if (event.aio.res != static_cast(request.aio.aio_nbytes)) + throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); +#else if (event.res != static_cast(request.aio_nbytes)) - { throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ". " + "aio_nbytes=" + std::to_string(request.aio_nbytes) + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); - } - +#endif __msan_unpoison(read_buffer_memory.data(), read_buffer_memory.size()); } diff --git a/src/Dictionaries/registerDictionaries.cpp b/src/Dictionaries/registerDictionaries.cpp index e9c47ec034bf..58848dafdadb 100644 --- a/src/Dictionaries/registerDictionaries.cpp +++ b/src/Dictionaries/registerDictionaries.cpp @@ -32,8 +32,10 @@ void registerDictionaries() registerDictionaryFlat(factory); registerDictionaryHashed(factory); registerDictionaryCache(factory); +#if defined(__linux__) || defined(__FreeBSD__) registerDictionarySSDCache(factory); registerDictionarySSDComplexKeyCache(factory); +#endif registerDictionaryPolygon(factory); registerDictionaryDirect(factory); } From 63ef97309485039d3def06ed84629df0b87829f6 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 24 May 2020 19:06:59 +0300 Subject: [PATCH 0134/1102] fix other os --- src/Dictionaries/SSDCacheDictionary.cpp | 2 +- .../SSDComplexKeyCacheDictionary.cpp | 66 ++++++++++++++----- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 022508b07b54..9e246677f057 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -654,7 +654,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice #endif } #if defined(__FreeBSD__) - const volatile auto* buf_ptr = reinterpret_cast(request.aio.aio_buf); + const char* buf_ptr = reinterpret_cast(request.aio.aio_buf); #else const auto* buf_ptr = reinterpret_cast(request.aio_buf); #endif diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index d13b9469132b..6a9b9088cab7 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -392,8 +392,8 @@ void SSDComplexKeyCachePartition::flush() write_request.aio.aio_lio_opcode = LIO_WRITE; write_request.aio.aio_fildes = fd; write_request.aio.aio_buf = reinterpret_cast(memory->data()); - write_request.aio.aio_nbytes = block_size; - write_request.aio.aio_offset = block_size * current_file_block_id; + write_request.aio.aio_nbytes = block_size * write_buffer_size; + write_request.aio.aio_offset = (current_file_block_id % max_size) * block_size; #else write_request.aio_lio_opcode = IOCB_CMD_PWRITE; write_request.aio_fildes = fd; @@ -609,8 +609,13 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray(requests.back().aio_offset) == index_to_out[i].first.getBlockId() * block_size) + #if defined(__FreeBSD__) + const size_t back_offset = requests.empty() ? -1 : static_cast(requests.back().aio.aio_offset); + #else + const size_t back_offset = requests.empty() ? -1 : static_cast(requests.back().aio_offset); + #endif + + if (!requests.empty() && back_offset == index_to_out[i].first.getBlockId() * block_size) { blocks_to_indices.back().push_back(i); continue; @@ -621,9 +626,9 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray( - reinterpret_cast(read_buffer.data()) + SSD_BLOCK_SIZE * (requests.size() % READ_BUFFER_SIZE_BLOCKS)); - request.aio.aio_nbytes = SSD_BLOCK_SIZE; - request.aio.aio_offset = index_to_out[i].first; + reinterpret_cast(read_buffer.data()) + block_size * (requests.size() % read_buffer_size)); + request.aio.aio_nbytes = block_size; + request.aio.aio_offset = index_to_out[i].first.getBlockId() * block_size; request.aio_data = requests.size(); #else request.aio_lio_opcode = IOCB_CMD_PREAD; @@ -643,8 +648,11 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray processed(requests.size(), false); std::vector events(requests.size()); + #if defined(__linux__) for (auto & event : events) event.res = -1; + #endif + size_t to_push = 0; size_t to_pop = 0; @@ -662,15 +670,35 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray(request.aio_nbytes)) - throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + - "request_id= " + std::to_string(request.aio_data) + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + - "returned: " + std::to_string(events[i].res), ErrorCodes::AIO_READ_ERROR); - __msan_unpoison(reinterpret_cast(request.aio_buf), request.aio_nbytes); + + #if defined(__FreeBSD__) + const auto bytes_written = aio_return(reinterpret_cast(events[i].udata)); + #else + const auto bytes_written = events[i].res; + #endif + + if (bytes_written != static_cast(block_size)) + { + #if defined(__FreeBSD__) + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); + #else + throw Exception("AIO failed to read file " + path + BIN_FILE_EXT + ". " + + "request_id= " + std::to_string(request.aio_data) + "/ " + std::to_string(requests.size()) + + ", aio_nbytes=" + std::to_string(request.aio_nbytes) + ", aio_offset=" + std::to_string(request.aio_offset) + + ", returned=" + std::to_string(events[i].res) + ", errno=" + std::to_string(errno), ErrorCodes::AIO_READ_ERROR); + #endif + } + #if defined(__FreeBSD__) + const char* buf_ptr = reinterpret_cast(request.aio.aio_buf); + #else + const auto* buf_ptr = reinterpret_cast(request.aio_buf); + #endif + + __msan_unpoison(buf_ptr, block_size); uint64_t checksum = 0; - ReadBufferFromMemory buf_special(reinterpret_cast(request.aio_buf), block_size); + ReadBufferFromMemory buf_special(buf_ptr, block_size); readBinary(checksum, buf_special); - uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(reinterpret_cast(request.aio_buf) + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); + uint64_t calculated_checksum = CityHash_v1_0_2::CityHash64(buf_ptr + BLOCK_CHECKSUM_SIZE, block_size - BLOCK_CHECKSUM_SIZE); if (checksum != calculated_checksum) { throw Exception("Cache data corrupted. From block = " + std::to_string(checksum) + " calculated = " + std::to_string(calculated_checksum) + ".", ErrorCodes::CORRUPTED_DATA); @@ -680,7 +708,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray(request.aio_buf) + file_index.getAddressInBlock(), + buf_ptr + file_index.getAddressInBlock(), block_size - file_index.getAddressInBlock()); set(out_index, buf); } @@ -743,13 +771,15 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() throwFromErrno("io_getevents: Failed to get an event for asynchronous IO", ErrorCodes::CANNOT_IO_GETEVENTS); } +#if defined(__FreeBSD__) + if (event.aio.res != static_cast(request.aio.aio_nbytes)) + throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); +#else if (event.res != static_cast(request.aio_nbytes)) - { throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ". " + "aio_nbytes=" + std::to_string(request.aio_nbytes) + ", returned=" + std::to_string(event.res) + ".", ErrorCodes::AIO_READ_ERROR); - } - +#endif __msan_unpoison(read_buffer_memory.data(), read_buffer_memory.size()); } From 3150667aa34553e30ef077575006cd54bf1e33cc Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 24 May 2020 19:57:25 +0300 Subject: [PATCH 0135/1102] ya.make --- src/Dictionaries/ya.make | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Dictionaries/ya.make b/src/Dictionaries/ya.make index e47b55d52547..c462f3f665c6 100644 --- a/src/Dictionaries/ya.make +++ b/src/Dictionaries/ya.make @@ -58,6 +58,8 @@ SRCS( RedisBlockInputStream.cpp RedisDictionarySource.cpp registerDictionaries.cpp + SSDCacheDictionary.cpp + SSDComplexKeyCacheDictionary.cpp writeParenthesisedString.cpp XDBCDictionarySource.cpp From 797fa400b4a4f7957a690ec9bcf83e4e2a7ead13 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sun, 24 May 2020 21:59:06 +0300 Subject: [PATCH 0136/1102] fix --- src/Dictionaries/SSDCacheDictionary.cpp | 4 ++-- src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index 9e246677f057..f8e6ca4176fe 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -654,7 +654,7 @@ void SSDCachePartition::getValueFromStorage(const PaddedPODArray & indice #endif } #if defined(__FreeBSD__) - const char* buf_ptr = reinterpret_cast(request.aio.aio_buf); + const char* buf_ptr = reinterpret_cast(reinterpret_cast(request.aio.aio_buf)); #else const auto* buf_ptr = reinterpret_cast(request.aio_buf); #endif @@ -736,7 +736,7 @@ void SSDCachePartition::clearOldestBlocks() } #if defined(__FreeBSD__) - if (event.aio.res != static_cast(request.aio.aio_nbytes)) + if (event.aio.udata != static_cast(request.aio.aio_nbytes)) throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); #else if (event.res != static_cast(request.aio_nbytes)) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 6a9b9088cab7..69c248a5804c 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -689,7 +689,7 @@ void SSDComplexKeyCachePartition::getValueFromStorage(const PaddedPODArray(request.aio.aio_buf); + const char* buf_ptr = reinterpret_cast(reinterpret_cast(request.aio.aio_buf)); #else const auto* buf_ptr = reinterpret_cast(request.aio_buf); #endif @@ -772,7 +772,7 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() } #if defined(__FreeBSD__) - if (event.aio.res != static_cast(request.aio.aio_nbytes)) + if (event.aio.udata != static_cast(request.aio.aio_nbytes)) throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); #else if (event.res != static_cast(request.aio_nbytes)) From d6f4c66fbc492c1bbbf10b34519b444b2b4396ea Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 25 May 2020 00:00:44 +0300 Subject: [PATCH 0137/1102] fix aio for other os --- src/Dictionaries/SSDCacheDictionary.cpp | 2 +- src/Dictionaries/SSDComplexKeyCacheDictionary.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Dictionaries/SSDCacheDictionary.cpp b/src/Dictionaries/SSDCacheDictionary.cpp index f8e6ca4176fe..93ec2a1709a3 100644 --- a/src/Dictionaries/SSDCacheDictionary.cpp +++ b/src/Dictionaries/SSDCacheDictionary.cpp @@ -736,7 +736,7 @@ void SSDCachePartition::clearOldestBlocks() } #if defined(__FreeBSD__) - if (event.aio.udata != static_cast(request.aio.aio_nbytes)) + if (aio_return(reinterpret_cast(event.udata)) != static_cast(request.aio.aio_nbytes)) throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); #else if (event.res != static_cast(request.aio_nbytes)) diff --git a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp index 69c248a5804c..642882a1975e 100644 --- a/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp +++ b/src/Dictionaries/SSDComplexKeyCacheDictionary.cpp @@ -430,7 +430,7 @@ void SSDComplexKeyCachePartition::flush() ProfileEvents::increment(ProfileEvents::WriteBufferAIOWrite); ProfileEvents::increment(ProfileEvents::WriteBufferAIOWriteBytes, bytes_written); - if (bytes_written != static_cast(write_request.aio_nbytes)) + if (bytes_written != static_cast(block_size * write_buffer_size)) throw Exception("Not all data was written for asynchronous IO on file " + path + BIN_FILE_EXT + ". returned: " + std::to_string(bytes_written), ErrorCodes::AIO_WRITE_ERROR); if (::fsync(fd) < 0) @@ -772,7 +772,7 @@ void SSDComplexKeyCachePartition::clearOldestBlocks() } #if defined(__FreeBSD__) - if (event.aio.udata != static_cast(request.aio.aio_nbytes)) + if (aio_return(reinterpret_cast(event.udata)) != static_cast(request.aio.aio_nbytes)) throw Exception("GC: AIO failed to read file " + path + BIN_FILE_EXT + ".", ErrorCodes::AIO_READ_ERROR); #else if (event.res != static_cast(request.aio_nbytes)) From 207de9ca9c4ad923a86afc70b59ef096be9c6264 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 25 May 2020 00:21:08 +0300 Subject: [PATCH 0138/1102] fixed direct tests --- src/Functions/FunctionsExternalDictionaries.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Functions/FunctionsExternalDictionaries.h b/src/Functions/FunctionsExternalDictionaries.h index 1e8c41cc724b..425dcf8eec04 100644 --- a/src/Functions/FunctionsExternalDictionaries.h +++ b/src/Functions/FunctionsExternalDictionaries.h @@ -175,22 +175,22 @@ class FunctionDictHas final : public IFunction auto dict = helper.getDictionary(block.getByPosition(arguments[0])); if (!executeDispatchSimple(block, arguments, result, dict) && + !executeDispatchSimple(block, arguments, result, dict) && !executeDispatchSimple(block, arguments, result, dict) && !executeDispatchSimple(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchSimple(block, arguments, result, dict) && #endif !executeDispatchComplex(block, arguments, result, dict) && + !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && - !executeDispatchSimple(block, arguments, result, dict)) + !executeDispatchComplex(block, arguments, result, dict)) throw Exception{"Unsupported dictionary type " + dict->getTypeName(), ErrorCodes::UNKNOWN_TYPE}; } @@ -332,16 +332,17 @@ class FunctionDictGetString final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && + !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && #endif !executeDispatchComplex(block, arguments, result, dict) && + !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && #endif @@ -515,16 +516,17 @@ class FunctionDictGetStringOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && + !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && #endif !executeDispatchComplex(block, arguments, result, dict) && + !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && #endif @@ -854,16 +856,17 @@ class FunctionDictGet final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && + !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && #endif !executeDispatchComplex(block, arguments, result, dict) && + !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && #endif @@ -1114,16 +1117,17 @@ class FunctionDictGetOrDefault final : public IFunction if (!executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && + !executeDispatch(block, arguments, result, dict) && !executeDispatch(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatch(block, arguments, result, dict) && #endif !executeDispatchComplex(block, arguments, result, dict) && + !executeDispatchComplex(block, arguments, result, dict) && !executeDispatchComplex(block, arguments, result, dict) && #if defined(__linux__) || defined(__FreeBSD__) !executeDispatchComplex(block, arguments, result, dict) && #endif - !executeDispatchComplex(block, arguments, result, dict) && #if !defined(ARCADIA_BUILD) !executeDispatchComplex(block, arguments, result, dict) && #endif From a4d74601a69f589bdbcc5a70d5da5b0c63075d44 Mon Sep 17 00:00:00 2001 From: bobrovskij artemij Date: Mon, 25 May 2020 00:42:05 +0300 Subject: [PATCH 0139/1102] build warnings fix --- src/Dictionaries/MongoDBDictionarySource.h | 2 -- src/Storages/StorageMongoDB.cpp | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/Dictionaries/MongoDBDictionarySource.h b/src/Dictionaries/MongoDBDictionarySource.h index 190ffb1af91a..e73b40be8583 100644 --- a/src/Dictionaries/MongoDBDictionarySource.h +++ b/src/Dictionaries/MongoDBDictionarySource.h @@ -27,9 +27,7 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; } -# if POCO_VERSION < 0x01070800 void authenticate(Poco::MongoDB::Connection & connection, const std::string & database, const std::string & user, const std::string & password); -# endif std::unique_ptr createCursor(const std::string & database, const std::string & collection, const Block & sample_block_to_select); diff --git a/src/Storages/StorageMongoDB.cpp b/src/Storages/StorageMongoDB.cpp index 2f27042b162c..adfdd10db6f1 100644 --- a/src/Storages/StorageMongoDB.cpp +++ b/src/Storages/StorageMongoDB.cpp @@ -1,21 +1,16 @@ #include "StorageMongoDB.h" -#include #include #include #include -#include -#include #include #include #include #include #include #include -#include #include #include -#include #include #include #include From 7135b8491c5b66788a6dd8c3e0536e175a78a616 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Mon, 25 May 2020 15:12:50 +0300 Subject: [PATCH 0140/1102] Base memory data storage --- .../AggregateFunctionDistinct.h | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index e7ccbc62c576..7e86364ab0d3 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -25,13 +25,15 @@ struct AggregateFunctionDistinctData UInt128TrivialHash, HashTableGrower<3>, HashTableAllocatorWithStackMemory - > data; + > set; std::mutex mutex; - bool ALWAYS_INLINE TryToInsert(const Key& key) + bool ALWAYS_INLINE tryToInsert(const Key& key) { std::lock_guard lock(mutex); - return data.insert(key).second; + bool a = set.insert(key).second; + if (a) std::cerr << key.high << ' ' << key.low << ' ' << a << std::endl; + return a; } }; @@ -39,18 +41,30 @@ struct AggregateFunctionDistinctData * Adding -Distinct suffix to aggregate function **/ -class AggregateFunctionDistinct final : public IAggregateFunctionHelper +class AggregateFunctionDistinct final : public IAggregateFunctionDataHelper { private: AggregateFunctionPtr nested_func; size_t num_arguments; - mutable AggregateFunctionDistinctData storage; + size_t prefix_size; + + AggregateDataPtr getNestedPlace(AggregateDataPtr place) const noexcept + { + return place + prefix_size; + } + + ConstAggregateDataPtr getNestedPlace(ConstAggregateDataPtr place) const noexcept + { + return place + prefix_size; + } public: AggregateFunctionDistinct(AggregateFunctionPtr nested, const DataTypes & arguments) - : IAggregateFunctionHelper(arguments, {}) + : IAggregateFunctionDataHelper(arguments, {}) , nested_func(nested), num_arguments(arguments.size()) { + prefix_size = 640'000'000; + if (arguments.empty()) throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); } @@ -67,16 +81,19 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelpercreate(place); + new (place) AggregateFunctionDistinctData; + nested_func->create(getNestedPlace(place)); } - void destroy(AggregateDataPtr place) const noexcept override { - nested_func->destroy(place); + void destroy(AggregateDataPtr place) const noexcept override + { + data(place).~AggregateFunctionDistinctData(); + nested_func->destroy(getNestedPlace(place)); } size_t sizeOfData() const override { - return nested_func->sizeOfData(); + return prefix_size + nested_func->sizeOfData(); } size_t alignOfData() const override @@ -92,34 +109,35 @@ class AggregateFunctionDistinct final : public IAggregateFunctionHelperupdateHashWithValue(row_num, hash); + } UInt128 key; hash.get128(key.low, key.high); - if (storage.TryToInsert(key)) - nested_func->add(place, columns, row_num, arena); + if (this->data(place).tryToInsert(key)) + nested_func->add(getNestedPlace(place), columns, row_num, arena); } void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { - nested_func->merge(place, rhs, arena); + nested_func->merge(getNestedPlace(place), rhs, arena); } void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { - nested_func->serialize(place, buf); + nested_func->serialize(getNestedPlace(place), buf); } void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { - nested_func->deserialize(place, buf, arena); + nested_func->deserialize(getNestedPlace(place), buf, arena); } void insertResultInto(AggregateDataPtr place, IColumn & to) const override { - nested_func->insertResultInto(place, to); + nested_func->insertResultInto(getNestedPlace(place), to); } bool allocatesMemoryInArena() const override From f206d74b63f00b2037a82257291bd721decce8ff Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Mon, 25 May 2020 17:02:55 +0300 Subject: [PATCH 0141/1102] fix align of data && add test --- src/AggregateFunctions/AggregateFunctionDistinct.h | 7 ++----- .../0_stateless/01259_combinator_distinct.reference | 1 + tests/queries/0_stateless/01259_combinator_distinct.sql | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 7e86364ab0d3..57e17ffb13c9 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -30,10 +30,7 @@ struct AggregateFunctionDistinctData bool ALWAYS_INLINE tryToInsert(const Key& key) { - std::lock_guard lock(mutex); - bool a = set.insert(key).second; - if (a) std::cerr << key.high << ' ' << key.low << ' ' << a << std::endl; - return a; + return set.insert(key).second; } }; @@ -63,7 +60,7 @@ class AggregateFunctionDistinct final : public IAggregateFunctionDataHelper(arguments, {}) , nested_func(nested), num_arguments(arguments.size()) { - prefix_size = 640'000'000; + prefix_size = sizeof(AggregateFunctionDistinctData); if (arguments.empty()) throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); diff --git a/tests/queries/0_stateless/01259_combinator_distinct.reference b/tests/queries/0_stateless/01259_combinator_distinct.reference index 34d13676466f..739d225ad676 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.reference +++ b/tests/queries/0_stateless/01259_combinator_distinct.reference @@ -1,4 +1,5 @@ 499500 78 [0,1,2,3,4,5,6,7,8,9,10,11,12] +[0,1,2,3,4,5,6,7,8,9,10,11,12] 5.669227916063075e-17 diff --git a/tests/queries/0_stateless/01259_combinator_distinct.sql b/tests/queries/0_stateless/01259_combinator_distinct.sql index e3c4bb114a33..3f07dc443ddf 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.sql +++ b/tests/queries/0_stateless/01259_combinator_distinct.sql @@ -1,4 +1,5 @@ SELECT sum(DISTINCT x) FROM (SELECT number AS x FROM system.numbers LIMIT 1000); SELECT sum(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); +SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 1000); SELECT corrStableDistinct(DISTINCT x, y) FROM (SELECT number % 11 AS x, number % 13 AS y FROM system.numbers LIMIT 1000); \ No newline at end of file From 58711c924bfd462014b0297a29124051092acc59 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 26 May 2020 13:19:59 +0300 Subject: [PATCH 0142/1102] Fix cmake --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a0e74fdf254..de479fc669f1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -269,6 +269,7 @@ dbms_target_link_libraries ( clickhouse_common_zookeeper clickhouse_dictionaries_embedded Poco::JSON + Poco::MongoDB string_utils PUBLIC ${Boost_SYSTEM_LIBRARY} From 14c67c6ae63500eb54a9644844e9c42d087324bc Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 26 May 2020 17:34:57 +0000 Subject: [PATCH 0143/1102] Fixes --- .../RabbitMQ/RabbitMQBlockInputStream.cpp | 13 ++- .../RabbitMQ/RabbitMQBlockInputStream.h | 2 +- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 4 +- src/Storages/RabbitMQ/RabbitMQHandler.h | 2 +- src/Storages/RabbitMQ/RabbitMQSettings.h | 1 - .../ReadBufferFromRabbitMQConsumer.cpp | 82 ++++++++++++------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 5 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 35 +++----- src/Storages/RabbitMQ/StorageRabbitMQ.h | 13 +-- .../integration/test_storage_rabbitmq/test.py | 14 ++-- 10 files changed, 94 insertions(+), 77 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index 89ea490e8425..d498a36f95b9 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -5,6 +5,11 @@ #include #include +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + namespace DB { @@ -117,13 +122,13 @@ Block RabbitMQBlockInputStream::readImpl() auto new_rows = read_rabbitmq_message(); - auto _exchange = storage.getExchangeName(); - auto _routingKey = storage.getRoutingKey(); + auto exchange_name = storage.getExchangeName(); + auto routing_key = storage.getRoutingKey(); for (size_t i = 0; i < new_rows; ++i) { - virtual_columns[0]->insert(_exchange); - virtual_columns[1]->insert(_routingKey); + virtual_columns[0]->insert(exchange_name); + virtual_columns[1]->insert(routing_key); } total_rows = total_rows + new_rows; diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h index c82fd68a680a..fbdb40bded82 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.h @@ -25,7 +25,7 @@ class RabbitMQBlockInputStream : public IBlockInputStream void readPrefixImpl() override; Block readImpl() override; - //void readSuffixImpl() override; + ///void readSuffixImpl() override; private: StorageRabbitMQ & storage; diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index b18d6bf2cfb2..aa72ab51878a 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -12,14 +12,14 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : } -void RabbitMQHandler::onError(AMQP::TcpConnection * /*connection*/, const char * message) +void RabbitMQHandler::onError(AMQP::TcpConnection * , const char * message) { LOG_ERROR(log, "Library error report: " << message); stop(); } -void RabbitMQHandler::startNonBlock() +void RabbitMQHandler::start() { event_base_loop(evbase, EVLOOP_NONBLOCK); } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index 94a559cad389..5b8a08be548e 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -17,7 +17,7 @@ class RabbitMQHandler : public AMQP::LibEventHandler RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); void onError(AMQP::TcpConnection * connection, const char * message) override; - void startNonBlock(); + void start(); void stop(); private: diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index f4c62756703f..509ed68b8d36 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -15,7 +15,6 @@ namespace DB M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exhange name, to which messages are sent. Needed to bind queues to it.", 0) \ M(SettingString, rabbitmq_format, "", "The message format.", 0) \ M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ - M(SettingUInt64, rabbitmq_bind_by_id, 0, "A flag which indicates that binding should be done in range [0, num_consumers * num_queues).", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ M(SettingUInt64, rabbitmq_hash_exchange, 0, "A flag which indicates whether consistent-hash-exchange should be used.", 0) \ diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index a9f804aaa021..5cdcbccadce7 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -1,10 +1,18 @@ #include +#include +#include #include #include #include #include +enum +{ + Connection_setup_sleep = 200, + Connection_setup_retries_max = 1000 +}; + namespace DB { @@ -38,11 +46,21 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( * because in case when num_consumers > 1 - inputStreams run asynchronously and if they share the same connection, * then they also will share the same event loop. But it will mean that if one stream's consumer starts event loop, * then it will run all callbacks on the connection - including other stream's consumer's callbacks - - * it result in asynchronous run of the same code and lead to occasional seg faults. + * it result in asynchronous run of the same code (because local variables can be updated both by the current thread + * and in callbacks by another thread during event loop, which is blocking only to the thread that has started the loop). + * So sharing the connection (== sharing event loop) results in occasional seg faults in case of asynchronous run of objects that share the connection. */ - while (!connection.ready()) + + size_t cnt_retries = 0; + while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) { event_base_loop(evbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); + std::this_thread::sleep_for(std::chrono::milliseconds(Connection_setup_sleep)); + } + + if (!connection.ready()) + { + LOG_ERROR(log, "Cannot set up connection for consumer"); } consumer_channel = std::make_shared(&connection); @@ -85,12 +103,12 @@ void ReadBufferFromRabbitMQConsumer::initExchange() if (hash_exchange) { current_exchange_name = exchange_name + "_hash"; - consumer_channel->declareExchange(current_exchange_name, AMQP::consistent_hash).onError([&](const char * message) + consumer_channel->declareExchange(current_exchange_name, AMQP::consistent_hash).onError([&](const char * /* message */) { exchange_declared = false; }); - consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * message) + consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * /* message */) { exchange_declared = false; }); @@ -98,12 +116,12 @@ void ReadBufferFromRabbitMQConsumer::initExchange() else { current_exchange_name = exchange_name + "_direct"; - consumer_channel->declareExchange(current_exchange_name, AMQP::direct).onError([&](const char * message) + consumer_channel->declareExchange(current_exchange_name, AMQP::direct).onError([&](const char * /* message */) { exchange_declared = false; }); - consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * message) + consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * /* message */) { exchange_declared = false; }); @@ -113,30 +131,36 @@ void ReadBufferFromRabbitMQConsumer::initExchange() void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { + /* This varibale can be updated from a different thread in case of some error so its better to always check + * whether exchange is in a working state and if not - declare it once again. + */ if (!exchange_declared) { initExchange(); exchange_declared = true; } - bool bindings_ok = false, bindings_error = false; + bool bindings_created = false, bindings_error = false; consumer_channel->declareQueue(AMQP::exclusive) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) { queues.emplace_back(queue_name_); - String binding_key = routing_key; - if (bind_by_id && !hash_exchange) + /* Every consumer has at least one unique queue. Bind the queues to exchange based on the consumer_channel_id + * in case there is one queue per consumer and bind by queue_id in case there is more than 1 queue per consumer. + * (queue_id is based on channel_id) + */ + if (bind_by_id || hash_exchange) { if (queues.size() == 1) { - binding_key = routing_key + "_" + std::to_string(channel_id); + binding_key = std::to_string(channel_id); } else { - binding_key = routing_key + "_" + std::to_string(channel_id + queue_id); + binding_key = std::to_string(channel_id + queue_id); } } @@ -145,7 +169,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) consumer_channel->bindQueue(current_exchange_name, queue_name_, binding_key) .onSuccess([&] { - bindings_ok = true; + bindings_created = true; }) .onError([&](const char * message) { @@ -159,9 +183,14 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) LOG_ERROR(log, "Failed to declare queue on the channel: " << message); }); - while (!bindings_ok && !bindings_error) + /* Run event loop (which updates local variables in a separate thread) until bindings are created or failed to be created. + * It is important at this moment to make sure that queue bindings are created before any publishing can happen because + * otherwise messages will be routed nowhere. + */ + while (!bindings_created && !bindings_error) { - startNonBlockEventLoop(); + /// No need for timeouts as this event loop is blocking for the current thread and quits in case there are no active events + startEventLoop(); } } @@ -184,17 +213,14 @@ void ReadBufferFromRabbitMQConsumer::subscribeConsumer() void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) { - bool consumer_ok = false, consumer_error = false; + bool consumer_created = false, consumer_error = false; consumer_channel->consume(queue_name, AMQP::noack) - .onSuccess([&](const std::string & consumer) + .onSuccess([&](const std::string & /* consumer */) { - if (consumerTag == "") - consumerTag = consumer; - - consumer_ok = true; + consumer_created = true; - LOG_TRACE(log, "Consumer " + consumerTag + " is subscribed to queue " + queue_name); + LOG_TRACE(log, "Consumer " + std::to_string(channel_id) + " is subscribed to queue " + queue_name); }) .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) { @@ -218,16 +244,16 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) LOG_ERROR(log, "Consumer failed: " << message); }); - while (!consumer_ok && !consumer_error) + while (!consumer_created && !consumer_error) { - startNonBlockEventLoop(); + startEventLoop(); } } -void ReadBufferFromRabbitMQConsumer::startNonBlockEventLoop() +void ReadBufferFromRabbitMQConsumer::startEventLoop() { - eventHandler.startNonBlock(); + eventHandler.start(); } @@ -242,12 +268,12 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() { /* Run the onReceived callbacks to save the messages that have been received by now */ - startNonBlockEventLoop(); + startEventLoop(); } if (received.empty()) { - LOG_TRACE(log, "Stalled"); + LOG_TRACE(log, "No more messages to be fetched"); return false; } @@ -256,7 +282,7 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() current = messages.begin(); } - auto new_position = const_cast(current->data()); + auto * new_position = const_cast(current->data()); BufferBase::set(new_position, current->size(), 0); ++current; diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 7592fb53bfcb..5e4318246a66 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -59,9 +59,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer bool allowed = true; const std::atomic & stopped; - std::atomic exchange_declared = false; + bool exchange_declared = false; const size_t num_queues; - String consumerTag; // ID for the consumer Queues queues; bool subscribed = false; String current_exchange_name; @@ -75,7 +74,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer void initExchange(); void initQueueBindings(const size_t queue_id); void subscribe(const String & queue_name); - void startNonBlockEventLoop(); + void startEventLoop(); }; } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 7e7da953d80a..cfabb5412bad 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -33,6 +33,11 @@ #include +enum + { + RESCHEDULE_WAIT = 500 + }; + namespace DB { @@ -55,7 +60,6 @@ StorageRabbitMQ::StorageRabbitMQ( const String & format_name_, char row_delimiter_, size_t num_consumers_, - bool bind_by_id_, size_t num_queues_, bool hash_exchange_) : IStorage(table_id_) @@ -66,7 +70,6 @@ StorageRabbitMQ::StorageRabbitMQ( , format_name(global_context.getMacros()->expand(format_name_)) , row_delimiter(row_delimiter_) , num_consumers(num_consumers_) - , bind_by_id(bind_by_id_) , num_queues(num_queues_) , hash_exchange(hash_exchange_) , log(&Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) @@ -79,8 +82,7 @@ StorageRabbitMQ::StorageRabbitMQ( task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); task->deactivate(); - /// Enable a different routing algorithm. - bind_by_id = num_consumers > 1 || num_queues > 1 || bind_by_id; + bind_by_id = num_consumers > 1 || num_queues > 1; } @@ -181,7 +183,8 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() next_channel_id += num_queues; update_channel_id = true; - return std::make_shared(parsed_address, exchange_name, routing_key, next_channel_id, + return std::make_shared( + parsed_address, exchange_name, routing_key, next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); } @@ -244,7 +247,7 @@ void StorageRabbitMQ::threadFunc() /// Wait for attached views if (!stream_cancelled) - task->scheduleAfter(500); + task->scheduleAfter(RESCHEDULE_WAIT); } @@ -397,13 +400,13 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } - size_t bind_by_id = static_cast(rabbitmq_settings.rabbitmq_bind_by_id); + bool hash_exchange = static_cast(rabbitmq_settings.rabbitmq_hash_exchange); if (args_count >= 6) { const auto * ast = engine_args[5]->as(); if (ast && ast->value.getType() == Field::Types::UInt64) { - bind_by_id = static_cast(safeGet(ast->value)); + hash_exchange = static_cast(safeGet(ast->value)); } else { @@ -439,22 +442,8 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } - size_t hash_exchange = static_cast(rabbitmq_settings.rabbitmq_hash_exchange); - if (args_count >= 9) - { - const auto * ast = engine_args[8]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - hash_exchange = static_cast(safeGet(ast->value)); - } - else - { - throw Exception("Hash exchange flag must be a boolean", ErrorCodes::BAD_ARGUMENTS); - } - } - return StorageRabbitMQ::create(args.table_id, args.context, args.columns, host_port, routing_key, exchange, - format, row_delimiter, num_consumers, bind_by_id, num_queues, hash_exchange); + format, row_delimiter, num_consumers, num_queues, hash_exchange); }; factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 8a3a48135b8a..b334b48a301d 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -53,10 +53,13 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Context & context_, const ColumnsDescription & columns_, const String & host_port_, - const String & routing_key_, const String & exchange_name_, - const String & format_name_, char row_delimiter_, - size_t num_consumers_, bool bind_by_id_, size_t num_queues_, bool hash_exchange); - + const String & routing_key_, + const String & exchange_name_, + const String & format_name_, + char row_delimiter_, + size_t num_consumers_, + size_t num_queues_, + bool hash_exchange); private: Context global_context; @@ -80,7 +83,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub std::mutex mutex; std::vector buffers; /// available buffers for RabbitMQ consumers - size_t next_channel_id = 0; + size_t next_channel_id = 1; /// Must >= 1 because it is used as a binding key, which has to be > 0 bool update_channel_id = false; BackgroundSchedulePool::TaskHolder task; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 815a84c1999a..821c5a19e680 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -528,7 +528,7 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = 'topic_' + str(randrange(0, NUMBER_OF_CONCURRENT_CONSUMERS)) + key = str(randrange(1, NUMBER_OF_CONCURRENT_CONSUMERS)) for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) connection.close() @@ -576,7 +576,6 @@ def test_rabbitmq_sharding_between_channels_publish(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'clickhouse', rabbitmq_num_consumers = 5, rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; @@ -605,7 +604,7 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = 'clickhouse_' + str(randrange(0, NUM_CHANNELS)) + key = str(randrange(1, NUM_CHANNELS)) for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) connection.close() @@ -641,7 +640,6 @@ def test_rabbitmq_sharding_between_queues_publish(rabbitmq_cluster): ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_queues = 4, - rabbitmq_routing_key = 'clickhouse', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; DROP TABLE IF EXISTS test.view; @@ -669,7 +667,7 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = 'clickhouse_' + str(randrange(0, NUM_QUEUES)) + key = str(randrange(1, NUM_QUEUES)) for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) connection.close() @@ -707,7 +705,6 @@ def test_rabbitmq_sharding_between_channels_and_queues_publish(rabbitmq_cluster) SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_queues = 2, rabbitmq_num_consumers = 10, - rabbitmq_routing_key = 'clickhouse', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; DROP TABLE IF EXISTS test.view; @@ -735,7 +732,7 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = 'clickhouse_' + str(randrange(0, NUM_QUEUES * NUM_CONSUMERS)) + key = str(randrange(1, NUM_QUEUES * NUM_CONSUMERS)) for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) connection.close() @@ -772,7 +769,6 @@ def test_rabbitmq_read_only_combo(rabbitmq_cluster): ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_consumers = 4, - rabbitmq_routing_key = 'clickhouse', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; ''') @@ -807,7 +803,7 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = 'clickhouse_' + str(randrange(0, NUM_CONSUMERS)) + key = str(randrange(1, NUM_CONSUMERS)) for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) connection.close() From 5e472af425476c7e145d952d9d853b985d7e6e24 Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 26 May 2020 20:43:20 +0000 Subject: [PATCH 0144/1102] Fix merge & small fix --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 2 +- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp | 15 +++++++-------- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 6 +++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index aa72ab51878a..cde43862ede8 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -14,7 +14,7 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : void RabbitMQHandler::onError(AMQP::TcpConnection * , const char * message) { - LOG_ERROR(log, "Library error report: " << message); + LOG_ERROR(log, "Library error report: {}", message); stop(); } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 5cdcbccadce7..945de989b575 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -46,11 +46,10 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( * because in case when num_consumers > 1 - inputStreams run asynchronously and if they share the same connection, * then they also will share the same event loop. But it will mean that if one stream's consumer starts event loop, * then it will run all callbacks on the connection - including other stream's consumer's callbacks - - * it result in asynchronous run of the same code (because local variables can be updated both by the current thread - * and in callbacks by another thread during event loop, which is blocking only to the thread that has started the loop). - * So sharing the connection (== sharing event loop) results in occasional seg faults in case of asynchronous run of objects that share the connection. + * as a result local variables can be updated both by the current thread and in callbacks by another thread during + * event loop, which is blocking only to the thread that has started the loop. Therefore sharing the connection + * (== sharing event loop) results in occasional seg faults in case of asynchronous run of objects that share the connection. */ - size_t cnt_retries = 0; while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) { @@ -97,7 +96,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() consumer_channel->declareExchange(exchange_name, AMQP::fanout).onError([&](const char * message) { exchange_declared = false; - LOG_ERROR(log, "Failed to declare fanout exchange: " << message); + LOG_ERROR(log, "Failed to declare fanout exchange: {}", message); }); if (hash_exchange) @@ -174,13 +173,13 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding: " << message); + LOG_ERROR(log, "Failed to create queue binding: {}", message); }); }) .onError([&](const char * message) { bindings_error = true; - LOG_ERROR(log, "Failed to declare queue on the channel: " << message); + LOG_ERROR(log, "Failed to declare queue on the channel: {}", message); }); /* Run event loop (which updates local variables in a separate thread) until bindings are created or failed to be created. @@ -241,7 +240,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) .onError([&](const char * message) { consumer_error = true; - LOG_ERROR(log, "Consumer failed: " << message); + LOG_ERROR(log, "Consumer failed: {}", message); }); while (!consumer_created && !consumer_error) diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index cfabb5412bad..fb20569200d1 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -106,7 +106,7 @@ Pipes StorageRabbitMQ::read( *this, context, column_names, log))); } - LOG_DEBUG(log, "Starting reading " << pipes.size() << " streams"); + LOG_DEBUG(log, "Starting reading {} streams", pipes.size()); return pipes; } @@ -136,7 +136,7 @@ void StorageRabbitMQ::shutdown() for (size_t i = 0; i < num_created_consumers; ++i) { - auto buffer = popReadBuffer(); + popReadBuffer(); } task->deactivate(); @@ -233,7 +233,7 @@ void StorageRabbitMQ::threadFunc() if (!checkDependencies(table_id)) break; - LOG_DEBUG(log, "Started streaming to " << dependencies_count << " attached views"); + LOG_DEBUG(log, "Started streaming to {} attached views", dependencies_count); if (!streamToViews()) break; From 8d69223aafe29989c7166a1638f460ced450516d Mon Sep 17 00:00:00 2001 From: bobrovskij artemij Date: Wed, 27 May 2020 01:20:25 +0300 Subject: [PATCH 0145/1102] show_privileges test fix, one more build fix --- src/Storages/StorageMongoDB.cpp | 2 +- tests/queries/0_stateless/01271_show_privileges.reference | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Storages/StorageMongoDB.cpp b/src/Storages/StorageMongoDB.cpp index adfdd10db6f1..db19e08b9906 100644 --- a/src/Storages/StorageMongoDB.cpp +++ b/src/Storages/StorageMongoDB.cpp @@ -29,7 +29,7 @@ namespace ErrorCodes StorageMongoDB::StorageMongoDB( const StorageID & table_id_, const std::string & host_, - short unsigned int port_, + uint16_t port_, const std::string & database_name_, const std::string & collection_name_, const std::string & username_, diff --git a/tests/queries/0_stateless/01271_show_privileges.reference b/tests/queries/0_stateless/01271_show_privileges.reference index e85dbd898012..702f6d8cb585 100644 --- a/tests/queries/0_stateless/01271_show_privileges.reference +++ b/tests/queries/0_stateless/01271_show_privileges.reference @@ -103,6 +103,7 @@ INTROSPECTION ['INTROSPECTION FUNCTIONS'] \N ALL FILE [] GLOBAL SOURCES URL [] GLOBAL SOURCES REMOTE [] GLOBAL SOURCES +MONGO [] GLOBAL SOURCES MYSQL [] GLOBAL SOURCES ODBC [] GLOBAL SOURCES JDBC [] GLOBAL SOURCES From 709b4f42c82da439b0b3b2216fd6f56959411dd3 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 27 May 2020 22:11:04 +0300 Subject: [PATCH 0146/1102] Prototype sending crash reports on segfaults --- .gitmodules | 3 + CMakeLists.txt | 1 + base/daemon/BaseDaemon.cpp | 8 +- base/daemon/CMakeLists.txt | 8 +- base/daemon/SentryWriter.cpp | 107 +++++++++++++ base/daemon/SentryWriter.h | 21 +++ cmake/find/sentry.cmake | 19 +++ contrib/CMakeLists.txt | 14 +- contrib/curl-cmake/CMakeLists.txt | 2 + contrib/sentry-native | 1 + programs/server/Server.cpp | 2 + src/Common/StackTrace.cpp | 144 ++++++++++++------ src/Common/StackTrace.h | 22 ++- src/Common/TraceCollector.cpp | 2 +- .../System/StorageSystemStackTrace.cpp | 2 +- utils/check-style/check-include | 1 + 16 files changed, 297 insertions(+), 60 deletions(-) create mode 100644 base/daemon/SentryWriter.cpp create mode 100644 base/daemon/SentryWriter.h create mode 100644 cmake/find/sentry.cmake create mode 160000 contrib/sentry-native diff --git a/.gitmodules b/.gitmodules index 7f5d1307a6e1..daa5d12a62c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -160,3 +160,6 @@ [submodule "contrib/fmtlib"] path = contrib/fmtlib url = https://github.com/fmtlib/fmt.git +[submodule "contrib/sentry-native"] + path = contrib/sentry-native + url = git@github.com:getsentry/sentry-native.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 53dfd1df1cb9..79db4c624cab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,6 +365,7 @@ include (cmake/find/fastops.cmake) include (cmake/find/orc.cmake) include (cmake/find/avro.cmake) include (cmake/find/msgpack.cmake) +include (cmake/find/sentry.cmake) find_contrib_lib(cityhash) find_contrib_lib(farmhash) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index 10c7173d5b1b..f269c3923e0c 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -222,6 +223,7 @@ class SignalListener : public Poco::Runnable DB::readPODBinary(stack_trace, in); DB::readBinary(thread_num, in); DB::readBinary(query_id, in); + stack_trace.resetFrames(); /// This allows to receive more signals if failure happens inside onFault function. /// Example: segfault while symbolizing stack trace. @@ -247,6 +249,7 @@ class SignalListener : public Poco::Runnable UInt32 thread_num, const std::string & query_id) const { + SentryWriter::onFault(sig, info, context, stack_trace); LOG_FATAL(log, "########################################"); { @@ -272,7 +275,7 @@ class SignalListener : public Poco::Runnable std::stringstream bare_stacktrace; bare_stacktrace << "Stack trace:"; for (size_t i = stack_trace.getOffset(); i < stack_trace.getSize(); ++i) - bare_stacktrace << ' ' << stack_trace.getFrames()[i]; + bare_stacktrace << ' ' << stack_trace.getFramePointers()[i]; LOG_FATAL(log, bare_stacktrace.str()); } @@ -511,6 +514,8 @@ void debugIncreaseOOMScore() {} void BaseDaemon::initialize(Application & self) { closeFDs(); + SentryWriter::initialize(); + task_manager = std::make_unique(); ServerApplication::initialize(self); @@ -518,7 +523,6 @@ void BaseDaemon::initialize(Application & self) argsToConfig(argv(), config(), PRIO_APPLICATION - 100); bool is_daemon = config().getBool("application.runAsDaemon", false); - if (is_daemon) { /** When creating pid file and looking for config, will search for paths relative to the working path of the program when started. diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt index 5d9a37dc75e8..46fa4a0fe340 100644 --- a/base/daemon/CMakeLists.txt +++ b/base/daemon/CMakeLists.txt @@ -1,7 +1,13 @@ add_library (daemon BaseDaemon.cpp GraphiteWriter.cpp -) + SentryWriter.cpp) target_include_directories (daemon PUBLIC ..) target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES}) + +if (USE_SENTRY) + target_link_libraries (daemon PRIVATE curl) + target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) +# target_include_directories (daemon SYSTEM BEFORE PRIVATE ${SENTRY_INCLUDE_DIR}) +endif () \ No newline at end of file diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp new file mode 100644 index 000000000000..8859adc1c2e3 --- /dev/null +++ b/base/daemon/SentryWriter.cpp @@ -0,0 +1,107 @@ +#include + +#include +#if !defined(ARCADIA_BUILD) +# include "Common/config_version.h" +#endif + +#include + +namespace { + void setExtras() { + sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); + sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); + sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); + sentry_set_extra("version_revision", sentry_value_new_int32(VERSION_REVISION)); + sentry_set_extra("version_major", sentry_value_new_int32(VERSION_MAJOR)); + sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); + sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); + } +} + +void SentryWriter::initialize() { + sentry_options_t * options = sentry_options_new(); + sentry_options_set_release(options, VERSION_STRING); + sentry_options_set_debug(options, 1); + sentry_init(options); + sentry_options_set_dsn(options, "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277"); + if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { + sentry_options_set_environment(options, "prod"); + } else { + sentry_options_set_environment(options, "test"); + } +} + +void SentryWriter::shutdown() { + sentry_shutdown(); +} + +void SentryWriter::onFault( + int sig, + const siginfo_t & info, + const ucontext_t & context, + const StackTrace & stack_trace + ) +{ + const std::string & error_message = signalToErrorMessage(sig, info, context); + sentry_value_t event = sentry_value_new_message_event(SENTRY_LEVEL_FATAL, "fault", error_message.c_str()); + sentry_set_tag("signal", strsignal(sig)); + sentry_set_tag("server_name", getFQDNOrHostName().c_str()); + sentry_set_extra("signal_number", sentry_value_new_int32(sig)); + setExtras(); + + sentry_value_t frames = sentry_value_new_list(); + + size_t stack_size = stack_trace.getSize(); + if (stack_size > 0) + { + size_t offset = stack_trace.getOffset(); + if (stack_size == 1) + { + offset = 1; + } + char instruction_addr[100]; + for (size_t i = stack_size - 1; i >= offset; --i) + { + const StackTrace::Frame & current_frame = stack_trace.getFrames().value()[i]; + sentry_value_t frame = sentry_value_new_object(); + unsigned long long frame_ptr = reinterpret_cast(current_frame.virtual_addr); + snprintf(instruction_addr, sizeof(instruction_addr), "0x%llx", frame_ptr); + sentry_value_set_by_key(frame, "instruction_addr", sentry_value_new_string(instruction_addr)); + + if (current_frame.symbol.has_value()) + { + sentry_value_set_by_key(frame, "function", sentry_value_new_string(current_frame.symbol.value().c_str())); + } + + if (current_frame.file.has_value()) + { + sentry_value_set_by_key(frame, "filename", sentry_value_new_string(current_frame.file.value().c_str())); + } + + if (current_frame.line.has_value()) + { + sentry_value_set_by_key(frame, "lineno", sentry_value_new_int32(current_frame.line.value())); + } + + sentry_value_append(frames, frame); + } + } + + sentry_value_t stacktrace = sentry_value_new_object(); + sentry_value_set_by_key(stacktrace, "frames", frames); + + sentry_value_t thread = sentry_value_new_object(); + sentry_value_set_by_key(thread, "stacktrace", stacktrace); + + sentry_value_t values = sentry_value_new_list(); + sentry_value_append(values, thread); + + sentry_value_t threads = sentry_value_new_object(); + sentry_value_set_by_key(threads, "values", values); + + sentry_value_set_by_key(event, "threads", threads); + + sentry_capture_event(event); + shutdown(); +} diff --git a/base/daemon/SentryWriter.h b/base/daemon/SentryWriter.h new file mode 100644 index 000000000000..6c85ef04dd32 --- /dev/null +++ b/base/daemon/SentryWriter.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include + +class SentryWriter +{ +public: + SentryWriter() = delete; + + static void initialize(); + static void shutdown(); + static void onFault( + int sig, + const siginfo_t & info, + const ucontext_t & context, + const StackTrace & stack_trace + ); +}; diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake new file mode 100644 index 000000000000..f94b53ffb009 --- /dev/null +++ b/cmake/find/sentry.cmake @@ -0,0 +1,19 @@ +set (SENTRY_LIBRARY "sentry") +set (SENTRY_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/sentry-native/include") +if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") + message (WARNING "submodule contrib/sentry-native is missing. to fix try run: \n git submodule update --init --recursive") + return() +endif () + +option (USE_SENTRY "Use Sentry" ON) + +set (BUILD_SHARED_LIBS OFF) +set (SENTRY_PIC OFF) +set (SENTRY_BACKEND "none") +set (SENTRY_TRANSPORT "curl") +set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) +set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) + +message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") + +include_directories("${SENTRY_INCLUDE_DIR}") \ No newline at end of file diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 99f7be2cbb77..1d1d7756de24 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -274,7 +274,7 @@ if (USE_INTERNAL_HDFS3_LIBRARY) add_subdirectory(libhdfs3-cmake) endif () -if (USE_INTERNAL_AWS_S3_LIBRARY) +if (USE_INTERNAL_AWS_S3_LIBRARY OR USE_SENTRY) set (save_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) set (save_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set (save_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) @@ -286,12 +286,18 @@ if (USE_INTERNAL_AWS_S3_LIBRARY) set (CMAKE_CMAKE_REQUIRED_INCLUDES ${save_CMAKE_REQUIRED_INCLUDES}) set (CMAKE_REQUIRED_FLAGS ${save_CMAKE_REQUIRED_FLAGS}) set (CMAKE_CMAKE_MODULE_PATH ${save_CMAKE_MODULE_PATH}) + + # The library is large - avoid bloat. + target_compile_options (curl PRIVATE -g0) +endif () + +if (USE_INTERNAL_AWS_S3_LIBRARY) add_subdirectory(aws-s3-cmake) # The library is large - avoid bloat. target_compile_options (aws_s3 PRIVATE -g0) target_compile_options (aws_s3_checksums PRIVATE -g0) - target_compile_options (curl PRIVATE -g0) + endif () if (USE_BASE64) @@ -318,4 +324,8 @@ if (USE_FASTOPS) add_subdirectory (fastops-cmake) endif() +if (USE_SENTRY) + add_subdirectory (sentry-native) +endif() + add_subdirectory (fmtlib-cmake) diff --git a/contrib/curl-cmake/CMakeLists.txt b/contrib/curl-cmake/CMakeLists.txt index d9805612ffea..d0f6a7773b01 100644 --- a/contrib/curl-cmake/CMakeLists.txt +++ b/contrib/curl-cmake/CMakeLists.txt @@ -1,4 +1,6 @@ set (CURL_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl) +set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) +set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) set (SRCS ${CURL_DIR}/lib/file.c diff --git a/contrib/sentry-native b/contrib/sentry-native new file mode 160000 index 000000000000..3bfce2d17c1b --- /dev/null +++ b/contrib/sentry-native @@ -0,0 +1 @@ +Subproject commit 3bfce2d17c1b80fbbaae83bb5ef41c1b290d34fb diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index c1a520030f49..8383fa2d9bf5 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -60,6 +60,8 @@ #include #include #include "MySQLHandlerFactory.h" +#include + #if !defined(ARCADIA_BUILD) # include "config_core.h" diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 6d0b6a0f7d2e..5cc8c43a27a4 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -190,6 +190,63 @@ static void * getCallerAddress(const ucontext_t & context) #endif } +static void symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames) +{ +#if defined(__ELF__) && !defined(__FreeBSD__) + + const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); + std::unordered_map dwarfs; + + for (size_t i = 0; i < offset; ++i) { + frames.value()[i].virtual_addr = frame_pointers[i]; + } + + for (size_t i = offset; i < size; ++i) + { + StackTrace::Frame & current_frame = frames.value()[i]; + current_frame.virtual_addr = frame_pointers[i]; + const auto * object = symbol_index.findObject(current_frame.virtual_addr); + uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; + current_frame.physical_addr = reinterpret_cast(uintptr_t(current_frame.virtual_addr) - virtual_offset); + + if (object) + { + current_frame.object = object->name; + if (std::filesystem::exists(current_frame.object.value())) + { + auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; + + DB::Dwarf::LocationInfo location; + if (dwarf_it->second.findAddress(uintptr_t(current_frame.physical_addr), location, DB::Dwarf::LocationInfoMode::FAST)) { + current_frame.file = location.file.toString(); + current_frame.line = location.line; + } + } + } + else + { + current_frame.object = "?"; + } + + const auto * symbol = symbol_index.findSymbol(current_frame.virtual_addr); + if (symbol) + { + int status = 0; + current_frame.symbol = demangle(symbol->name, status); + } + else + { + current_frame.symbol = "?"; + } + } +# else + for (size_t i = 0; i < size; ++i) { + frames.value()[i].virtual_addr = frame_pointers[i]; + } + UNUSED(offset); +#endif +} + StackTrace::StackTrace() { tryCapture(); @@ -203,7 +260,7 @@ StackTrace::StackTrace(const ucontext_t & signal_context) if (size == 0 && caller_address) { - frames[0] = caller_address; + frame_pointers[0] = caller_address; size = 1; } else @@ -212,7 +269,7 @@ StackTrace::StackTrace(const ucontext_t & signal_context) for (size_t i = 0; i < size; ++i) { - if (frames[i] == caller_address) + if (frame_pointers[i] == caller_address) { offset = i; break; @@ -229,8 +286,8 @@ void StackTrace::tryCapture() { size = 0; #if USE_UNWIND - size = unw_backtrace(frames.data(), capacity); - __msan_unpoison(frames.data(), size * sizeof(frames[0])); + size = unw_backtrace(frame_pointers.data(), capacity); + __msan_unpoison(frame_pointers.data(), size * sizeof(frame_pointers[0])); #endif } @@ -244,102 +301,89 @@ size_t StackTrace::getOffset() const return offset; } +const StackTrace::FramePointers & StackTrace::getFramePointers() const +{ + return frame_pointers; +} + const StackTrace::Frames & StackTrace::getFrames() const { + if (!frames.has_value()) { + frames = {{}}; + symbolize(frame_pointers.data(), offset, size, frames); + } return frames; } - static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function callback) { if (size == 0) return callback(""); -#if defined(__ELF__) && !defined(__FreeBSD__) - const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); - std::unordered_map dwarfs; - std::stringstream out; for (size_t i = offset; i < size; ++i) { - const void * virtual_addr = frames[i]; - const auto * object = symbol_index.findObject(virtual_addr); - uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; - const void * physical_addr = reinterpret_cast(uintptr_t(virtual_addr) - virtual_offset); - + const StackTrace::Frame& current_frame = frames.value()[i]; out << i << ". "; - if (object) + if (current_frame.file.has_value() && current_frame.line.has_value()) { - if (std::filesystem::exists(object->name)) - { - auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; - - DB::Dwarf::LocationInfo location; - if (dwarf_it->second.findAddress(uintptr_t(physical_addr), location, DB::Dwarf::LocationInfoMode::FAST)) - out << location.file.toString() << ":" << location.line << ": "; - } + out << current_frame.file.value() << ":" << current_frame.line.value() << ": "; } - const auto * symbol = symbol_index.findSymbol(virtual_addr); - if (symbol) + if (current_frame.symbol.has_value()) { - int status = 0; - out << demangle(symbol->name, status); + out << current_frame.symbol.value(); } - else - out << "?"; - out << " @ " << physical_addr; - out << " in " << (object ? object->name : "?"); - - callback(out.str()); - out.str({}); - } -#else - std::stringstream out; - - for (size_t i = offset; i < size; ++i) - { - const void * addr = frames[i]; - out << i << ". " << addr; + out << " @ " << current_frame.physical_addr; + if (current_frame.object.has_value()) { + out << " in " << current_frame.object.value(); + } callback(out.str()); out.str({}); } -#endif } -static std::string toStringImpl(const StackTrace::Frames & frames, size_t offset, size_t size) +static std::string toStringImpl(const void * const * frame_pointers, size_t offset, size_t size) { std::stringstream out; + StackTrace::Frames frames{}; + frames = {{}}; + symbolize(frame_pointers, offset, size, frames); toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); return out.str(); } void StackTrace::toStringEveryLine(std::function callback) const { - toStringEveryLineImpl(frames, offset, size, std::move(callback)); + toStringEveryLineImpl(getFrames(), offset, size, std::move(callback)); +} + +void StackTrace::resetFrames() { + frames.reset(); } + std::string StackTrace::toString() const { /// Calculation of stack trace text is extremely slow. /// We use simple cache because otherwise the server could be overloaded by trash queries. static SimpleCache func_cached; - return func_cached(frames, offset, size); + return func_cached(frame_pointers.data(), offset, size); } -std::string StackTrace::toString(void ** frames_, size_t offset, size_t size) +std::string StackTrace::toString(void ** frame_pointers, size_t offset, size_t size) { __msan_unpoison(frames_, size * sizeof(*frames_)); - StackTrace::Frames frames_copy{}; + StackTrace::FramePointers frame_pointers_copy{}; for (size_t i = 0; i < size; ++i) - frames_copy[i] = frames_[i]; + frame_pointers_copy[i] = frame_pointers[i]; static SimpleCache func_cached; - return func_cached(frames_copy, offset, size); + return func_cached(frame_pointers_copy.data(), offset, size); } diff --git a/src/Common/StackTrace.h b/src/Common/StackTrace.h index 401c8344f2de..27b2c44dd949 100644 --- a/src/Common/StackTrace.h +++ b/src/Common/StackTrace.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -23,8 +25,18 @@ struct NoCapture class StackTrace { public: + struct Frame + { + const void * virtual_addr = nullptr; + void * physical_addr = nullptr; + std::optional symbol; + std::optional object; + std::optional file; + std::optional line; + }; static constexpr size_t capacity = 32; - using Frames = std::array; + using FramePointers = std::array; + using Frames = std::optional>; /// Tries to capture stack trace StackTrace(); @@ -38,19 +50,23 @@ class StackTrace size_t getSize() const; size_t getOffset() const; + const FramePointers & getFramePointers() const; const Frames & getFrames() const; std::string toString() const; - static std::string toString(void ** frames, size_t offset, size_t size); + static std::string toString(void ** frame_pointers, size_t offset, size_t size); void toStringEveryLine(std::function callback) const; + void resetFrames(); + protected: void tryCapture(); size_t size = 0; size_t offset = 0; /// How many frames to skip while displaying. - Frames frames{}; + FramePointers frame_pointers{}; + mutable Frames frames{}; }; std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context); diff --git a/src/Common/TraceCollector.cpp b/src/Common/TraceCollector.cpp index 7df06dc78928..f5bdfd2b8269 100644 --- a/src/Common/TraceCollector.cpp +++ b/src/Common/TraceCollector.cpp @@ -81,7 +81,7 @@ void TraceCollector::collect(TraceType trace_type, const StackTrace & stack_trac size_t stack_trace_offset = stack_trace.getOffset(); writeIntBinary(UInt8(stack_trace_size - stack_trace_offset), out); for (size_t i = stack_trace_offset; i < stack_trace_size; ++i) - writePODBinary(stack_trace.getFrames()[i], out); + writePODBinary(stack_trace.getFramePointers()[i], out); writePODBinary(trace_type, out); writePODBinary(thread_id, out); diff --git a/src/Storages/System/StorageSystemStackTrace.cpp b/src/Storages/System/StorageSystemStackTrace.cpp index a8966ad03079..bdce70894d58 100644 --- a/src/Storages/System/StorageSystemStackTrace.cpp +++ b/src/Storages/System/StorageSystemStackTrace.cpp @@ -198,7 +198,7 @@ void StorageSystemStackTrace::fillData(MutableColumns & res_columns, const Conte Array arr; arr.reserve(stack_trace_size - stack_trace_offset); for (size_t i = stack_trace_offset; i < stack_trace_size; ++i) - arr.emplace_back(reinterpret_cast(stack_trace->getFrames()[i])); + arr.emplace_back(reinterpret_cast(stack_trace->getFramePointers()[i])); res_columns[0]->insert(tid); res_columns[1]->insertData(query_id_data, query_id_size); diff --git a/utils/check-style/check-include b/utils/check-style/check-include index 211172979bd6..35f94d6e706b 100755 --- a/utils/check-style/check-include +++ b/utils/check-style/check-include @@ -59,6 +59,7 @@ inc="-I. \ -I./contrib/lz4/lib \ -I./contrib/hyperscan/src \ -I./contrib/simdjson/include \ +-I./contrib/sentry-native/include \ -I./src \ -I${BUILD_DIR}/src" From 31123236cb359f1783dcadf8c3062ddb1ca6b8cf Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 27 May 2020 23:15:33 +0300 Subject: [PATCH 0147/1102] Settings for crash report opt-in --- base/daemon/BaseDaemon.cpp | 2 +- base/daemon/SentryWriter.cpp | 154 +++++++++++++++++++++-------------- base/daemon/SentryWriter.h | 4 +- src/Common/config.h.in | 1 + 4 files changed, 100 insertions(+), 61 deletions(-) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index f269c3923e0c..a8a798275521 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -514,7 +514,6 @@ void debugIncreaseOOMScore() {} void BaseDaemon::initialize(Application & self) { closeFDs(); - SentryWriter::initialize(); task_manager = std::make_unique(); ServerApplication::initialize(self); @@ -533,6 +532,7 @@ void BaseDaemon::initialize(Application & self) } reloadConfiguration(); + SentryWriter::initialize(config()); /// This must be done before creation of any files (including logs). mode_t umask_num = 0027; diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 8859adc1c2e3..5c7d6eadd98b 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -1,14 +1,21 @@ #include +#include #include #if !defined(ARCADIA_BUILD) # include "Common/config_version.h" #endif +#if USE_SENTRY #include +#endif + namespace { + static bool initialized = false; + void setExtras() { +#if USE_SENTRY sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); @@ -16,24 +23,47 @@ namespace { sentry_set_extra("version_major", sentry_value_new_int32(VERSION_MAJOR)); sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); +#endif } } -void SentryWriter::initialize() { - sentry_options_t * options = sentry_options_new(); - sentry_options_set_release(options, VERSION_STRING); - sentry_options_set_debug(options, 1); - sentry_init(options); - sentry_options_set_dsn(options, "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277"); - if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { - sentry_options_set_environment(options, "prod"); - } else { - sentry_options_set_environment(options, "test"); +void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { +#if USE_SENTRY + bool enabled = false; + if (config.getBool("send_crash_reports.enabled", false)) + { + if ((strlen(VERSION_OFFICIAL) > 0) || config.getBool("send_crash_reports.debug", false)) + { + enabled = true; + } } + if (enabled) + { + const std::string & endpoint = config.getString( + "send_crash_reports.endpoint", + "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277" + ); + sentry_options_t * options = sentry_options_new(); + sentry_options_set_release(options, VERSION_STRING); + sentry_options_set_debug(options, 1); + sentry_init(options); + sentry_options_set_dsn(options, endpoint.c_str()); + if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { + sentry_options_set_environment(options, "prod"); + } else { + sentry_options_set_environment(options, "test"); + } + initialized = true; + } +#endif } void SentryWriter::shutdown() { - sentry_shutdown(); +#if USE_SENTRY + if (initialized) { + sentry_shutdown(); + } +#endif } void SentryWriter::onFault( @@ -43,65 +73,71 @@ void SentryWriter::onFault( const StackTrace & stack_trace ) { - const std::string & error_message = signalToErrorMessage(sig, info, context); - sentry_value_t event = sentry_value_new_message_event(SENTRY_LEVEL_FATAL, "fault", error_message.c_str()); - sentry_set_tag("signal", strsignal(sig)); - sentry_set_tag("server_name", getFQDNOrHostName().c_str()); - sentry_set_extra("signal_number", sentry_value_new_int32(sig)); - setExtras(); - - sentry_value_t frames = sentry_value_new_list(); - - size_t stack_size = stack_trace.getSize(); - if (stack_size > 0) +#if USE_SENTRY + if (initialized) { - size_t offset = stack_trace.getOffset(); - if (stack_size == 1) - { - offset = 1; - } - char instruction_addr[100]; - for (size_t i = stack_size - 1; i >= offset; --i) + const std::string & error_message = signalToErrorMessage(sig, info, context); + sentry_value_t event = sentry_value_new_message_event(SENTRY_LEVEL_FATAL, "fault", error_message.c_str()); + sentry_set_tag("signal", strsignal(sig)); + sentry_set_tag("server_name", getFQDNOrHostName().c_str()); + sentry_set_extra("signal_number", sentry_value_new_int32(sig)); + setExtras(); + + /// Prepare data for https://develop.sentry.dev/sdk/event-payloads/stacktrace/ + sentry_value_t frames = sentry_value_new_list(); + size_t stack_size = stack_trace.getSize(); + if (stack_size > 0) { - const StackTrace::Frame & current_frame = stack_trace.getFrames().value()[i]; - sentry_value_t frame = sentry_value_new_object(); - unsigned long long frame_ptr = reinterpret_cast(current_frame.virtual_addr); - snprintf(instruction_addr, sizeof(instruction_addr), "0x%llx", frame_ptr); - sentry_value_set_by_key(frame, "instruction_addr", sentry_value_new_string(instruction_addr)); - - if (current_frame.symbol.has_value()) + size_t offset = stack_trace.getOffset(); + if (stack_size == 1) { - sentry_value_set_by_key(frame, "function", sentry_value_new_string(current_frame.symbol.value().c_str())); + offset = 1; } - - if (current_frame.file.has_value()) + char instruction_addr[100]; + for (size_t i = stack_size - 1; i >= offset; --i) { - sentry_value_set_by_key(frame, "filename", sentry_value_new_string(current_frame.file.value().c_str())); + const StackTrace::Frame & current_frame = stack_trace.getFrames().value()[i]; + sentry_value_t frame = sentry_value_new_object(); + unsigned long long frame_ptr = reinterpret_cast(current_frame.virtual_addr); + snprintf(instruction_addr, sizeof(instruction_addr), "0x%llx", frame_ptr); + sentry_value_set_by_key(frame, "instruction_addr", sentry_value_new_string(instruction_addr)); + + if (current_frame.symbol.has_value()) + { + sentry_value_set_by_key(frame, "function", sentry_value_new_string(current_frame.symbol.value().c_str())); + } + + if (current_frame.file.has_value()) + { + sentry_value_set_by_key(frame, "filename", sentry_value_new_string(current_frame.file.value().c_str())); + } + + if (current_frame.line.has_value()) + { + sentry_value_set_by_key(frame, "lineno", sentry_value_new_int32(current_frame.line.value())); + } + + sentry_value_append(frames, frame); } - - if (current_frame.line.has_value()) - { - sentry_value_set_by_key(frame, "lineno", sentry_value_new_int32(current_frame.line.value())); - } - - sentry_value_append(frames, frame); } - } - sentry_value_t stacktrace = sentry_value_new_object(); - sentry_value_set_by_key(stacktrace, "frames", frames); + /// Prepare data for https://develop.sentry.dev/sdk/event-payloads/threads/ + sentry_value_t stacktrace = sentry_value_new_object(); + sentry_value_set_by_key(stacktrace, "frames", frames); - sentry_value_t thread = sentry_value_new_object(); - sentry_value_set_by_key(thread, "stacktrace", stacktrace); + sentry_value_t thread = sentry_value_new_object(); + sentry_value_set_by_key(thread, "stacktrace", stacktrace); - sentry_value_t values = sentry_value_new_list(); - sentry_value_append(values, thread); + sentry_value_t values = sentry_value_new_list(); + sentry_value_append(values, thread); - sentry_value_t threads = sentry_value_new_object(); - sentry_value_set_by_key(threads, "values", values); + sentry_value_t threads = sentry_value_new_object(); + sentry_value_set_by_key(threads, "values", values); - sentry_value_set_by_key(event, "threads", threads); + sentry_value_set_by_key(event, "threads", threads); - sentry_capture_event(event); - shutdown(); + sentry_capture_event(event); + shutdown(); + } +#endif } diff --git a/base/daemon/SentryWriter.h b/base/daemon/SentryWriter.h index 6c85ef04dd32..ee45ae4f203a 100644 --- a/base/daemon/SentryWriter.h +++ b/base/daemon/SentryWriter.h @@ -3,6 +3,8 @@ #include #include +#include + #include class SentryWriter @@ -10,7 +12,7 @@ class SentryWriter public: SentryWriter() = delete; - static void initialize(); + static void initialize(Poco::Util::LayeredConfiguration & config); static void shutdown(); static void onFault( int sig, diff --git a/src/Common/config.h.in b/src/Common/config.h.in index df2359c1c29a..dd6263c39481 100644 --- a/src/Common/config.h.in +++ b/src/Common/config.h.in @@ -9,4 +9,5 @@ #cmakedefine01 USE_BROTLI #cmakedefine01 USE_UNWIND #cmakedefine01 USE_OPENCL +#cmakedefine01 USE_SENTRY #cmakedefine01 CLICKHOUSE_SPLIT_BINARY From 52e4a0293d622072bbd8d9f09d37bc7257b83174 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 27 May 2020 23:21:53 +0300 Subject: [PATCH 0148/1102] Keep sentry-native in debug mode only under setting --- base/daemon/SentryWriter.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 5c7d6eadd98b..7e2a95c83698 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -30,9 +30,10 @@ namespace { void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { #if USE_SENTRY bool enabled = false; + bool debug = config.getBool("send_crash_reports.debug", false); if (config.getBool("send_crash_reports.enabled", false)) { - if ((strlen(VERSION_OFFICIAL) > 0) || config.getBool("send_crash_reports.debug", false)) + if (debug || (strlen(VERSION_OFFICIAL) > 0)) { enabled = true; } @@ -45,7 +46,10 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { ); sentry_options_t * options = sentry_options_new(); sentry_options_set_release(options, VERSION_STRING); - sentry_options_set_debug(options, 1); + if (debug) + { + sentry_options_set_debug(options, 1); + } sentry_init(options); sentry_options_set_dsn(options, endpoint.c_str()); if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { From f6a220916991d379d6bce7a320304e982184ded5 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 30 Mar 2020 18:36:02 +0200 Subject: [PATCH 0149/1102] Add target specific macros --- dbms/src/Functions/DynamicTarget/Target.h | 106 ++++++++++++++++++++++ src/Functions/IFunctionImpl.h | 2 +- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 dbms/src/Functions/DynamicTarget/Target.h diff --git a/dbms/src/Functions/DynamicTarget/Target.h b/dbms/src/Functions/DynamicTarget/Target.h new file mode 100644 index 000000000000..5e3032ded3e8 --- /dev/null +++ b/dbms/src/Functions/DynamicTarget/Target.h @@ -0,0 +1,106 @@ +#pragma once + +namespace DB::DynamicTarget +{ + +enum class TargetArch : int { + Scalar, + SSE4, + AVX, + AVX2, + AVX512, +}; + +#if defined(__GNUC__) +// TODO: There are lots of different AVX512 :( +# define BEGIN_AVX512_SPECIFIC_CODE \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native\")") +# define BEGIN_AVX2_SPECIFIC_CODE \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native\")") +# define BEGIN_AVX_SPECIFIC_CODE \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native\")") +# define BEGIN_SSE4_SPECIFIC_CODE \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") +# define END_TARGET_SPECIFIC_CODE \ + _Pragma("GCC pop_options") +#elif defined(__clang__) +// TODO: There are lots of different AVX512 :( +# define BEGIN_AVX512_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") +# define BEGIN_AVX2_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") +# define BEGIN_AVX_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx\"))))") +# define BEGIN_SSE4_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx\"))))") +# define END_TARGET_SPECIFIC_CODE \ + _Pragma("clang attribute pop") +#else +# error "Only CLANG and GCC compilers are supported" +#endif + +#define DECLARE_DEFAULT_CODE (...) \ +namespace TargetSpecific::Default { \ + __VA_ARGS__ \ +} + +#define DECLARE_SSE4_SPECIFIC_CODE (...) \ +BEGIN_SSE4_SPECIFIC_CODE \ +namespace TargetSpecific::SSE4 { \ + __VA_ARGS__ \ +} \ +END_TARGET_SPECIFIC_CODE + +#define DECLARE_AVX_SPECIFIC_CODE (...) \ +BEGIN_AVX_SPECIFIC_CODE \ +namespace TargetSpecific::AVX { \ + __VA_ARGS__ \ +} \ +END_TARGET_SPECIFIC_CODE + +#define DECLARE_AVX2_SPECIFIC_CODE (...) \ +BEGIN_AVX2_SPECIFIC_CODE \ +namespace TargetSpecific::AVX2 { \ + __VA_ARGS__ \ +} \ +END_TARGET_SPECIFIC_CODE + +#define DECLARE_AVX512_SPECIFIC_CODE (...) \ +BEGIN_AVX512_SPECIFIC_CODE \ +namespace TargetSpecific::AVX512 { \ + __VA_ARGS__ \ +} \ +END_TARGET_SPECIFIC_CODE + +#define DYNAMIC_CODE (...) \ +DECLARE_DEFAULT_CODE (__VA_ARGS__) \ +DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX512_SPECIFIC_CODE(__VA_ARGS__) + +DECLARE_DEFAULT_CODE( + constexpr auto BuildArch = TargetArch::Scalar; +) // DECLARE_DEFAULT_CODE + +DECLARE_SSE4_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::SSE4; +) // DECLARE_SSE4_SPECIFIC_CODE + +DECLARE_AVX_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::AVX; +) // DECLARE_AVX_SPECIFIC_CODE + +DECLARE_AVX2_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::AVX2; +) // DECLARE_AVX2_SPECIFIC_CODE + +DECLARE_AVX512_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::AVX512; +) // DECLARE_AVX512_SPECIFIC_CODE + +} // namespace DB::DynamicTarget \ No newline at end of file diff --git a/src/Functions/IFunctionImpl.h b/src/Functions/IFunctionImpl.h index 116363705de4..4da3e9ca0566 100644 --- a/src/Functions/IFunctionImpl.h +++ b/src/Functions/IFunctionImpl.h @@ -5,7 +5,7 @@ /// In order to implement a new function you can choose one of two options: /// * Implement interface for IFunction (old function interface, which is planned to be removed sometimes) /// * Implement three interfaces for IExecutableFunctionImpl, IFunctionBaseImpl and IFunctionOverloadResolverImpl -/// Generally saying, IFunction represents a union of tree new interfaces. However, it can't be used for all cases. +/// Generally saying, IFunction represents a union of three new interfaces. However, it can't be used for all cases. /// Examples: /// * Function properties may depend on arguments type (e.g. toUInt32(UInt8) is globally monotonic, toUInt32(UInt64) - only on intervals) /// * In implementation of lambda functions DataTypeFunction needs an functional object with known arguments and return type From 37d13d4bce5f4260979e38617457b9136ca1a15f Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 2 Apr 2020 15:48:14 +0200 Subject: [PATCH 0150/1102] Compilable --- .../Functions/DynamicTarget/CMakeLists.txt | 11 + .../DynamicTarget/DynamicFunctionAdaptors.h | 263 ++++++++++++++++++ dbms/src/Functions/DynamicTarget/Selector.h | 39 +++ dbms/src/Functions/DynamicTarget/Target.cpp | 12 + dbms/src/Functions/DynamicTarget/Target.h | 19 +- src/Compression/CompressionFactory.cpp | 2 +- src/Functions/CMakeLists.txt | 3 + src/Functions/FunctionStartsEndsWith.h | 103 ++++--- src/Functions/IFunctionImpl.h | 2 +- 9 files changed, 408 insertions(+), 46 deletions(-) create mode 100644 dbms/src/Functions/DynamicTarget/CMakeLists.txt create mode 100644 dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h create mode 100644 dbms/src/Functions/DynamicTarget/Selector.h create mode 100644 dbms/src/Functions/DynamicTarget/Target.cpp diff --git a/dbms/src/Functions/DynamicTarget/CMakeLists.txt b/dbms/src/Functions/DynamicTarget/CMakeLists.txt new file mode 100644 index 000000000000..154e47ab5f61 --- /dev/null +++ b/dbms/src/Functions/DynamicTarget/CMakeLists.txt @@ -0,0 +1,11 @@ +include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) +add_headers_and_sources(clickhouse_functions_dynamic_target .) +add_library(clickhouse_functions_dynamic_target + ${clickhouse_functions_dynamic_target_sources} + ${clickhouse_functions_dynamic_target_headers}) +target_link_libraries(clickhouse_functions_dynamic_target PRIVATE dbms) + +if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELWITHDEBINFO" OR CMAKE_BUILD_TYPE_UC STREQUAL "MINSIZEREL") + # Won't generate debug info for files with heavy template instantiation to achieve faster linking and lower size. + target_compile_options(clickhouse_functions_dynamic_target PRIVATE "-g0") +endif () diff --git a/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h b/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h new file mode 100644 index 000000000000..123faa859e9c --- /dev/null +++ b/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h @@ -0,0 +1,263 @@ +#pragma once +#include + +namespace DB +{ + +/// Adaptors are implement user interfaces from IFunction.h via developer interfaces from IFunctionImpl.h +/// Typically, you don't need to change this classes. + +class ExecutableFunctionAdaptor final : public IExecutableFunction +{ +public: + explicit ExecutableFunctionAdaptor(ExecutableFunctionImplPtr impl_) : impl(std::move(impl_)) {} + + String getName() const final { return impl->getName(); } + + void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count, bool dry_run) final; + + void createLowCardinalityResultCache(size_t cache_size) override; + +private: + ExecutableFunctionImplPtr impl; + + /// Cache is created by function createLowCardinalityResultCache() + ExecutableFunctionLowCardinalityResultCachePtr low_cardinality_result_cache; + + bool defaultImplementationForConstantArguments( + Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); + + bool defaultImplementationForNulls( + Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); + + void executeWithoutLowCardinalityColumns( + Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); +}; + +class FunctionBaseAdaptor final : public IFunctionBase +{ +public: + explicit FunctionBaseAdaptor(FunctionBaseImplPtr impl_) : impl(std::move(impl_)) {} + + String getName() const final { return impl->getName(); } + + const DataTypes & getArgumentTypes() const final { return impl->getArgumentTypes(); } + const DataTypePtr & getReturnType() const final { return impl->getReturnType(); } + + ExecutableFunctionPtr prepare(const Block & sample_block, const ColumnNumbers & arguments, size_t result) const final + { + return std::make_shared(impl->prepare(sample_block, arguments, result)); + } + +#if USE_EMBEDDED_COMPILER + + bool isCompilable() const final { return impl->isCompilable(); } + + llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override + { + return impl->compile(builder, std::move(values)); + } + +#endif + + bool isStateful() const final { return impl->isStateful(); } + bool isSuitableForConstantFolding() const final { return impl->isSuitableForConstantFolding(); } + + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const final + { + return impl->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments); + } + + bool isInjective(const Block & sample_block) final { return impl->isInjective(sample_block); } + bool isDeterministic() const final { return impl->isDeterministic(); } + bool isDeterministicInScopeOfQuery() const final { return impl->isDeterministicInScopeOfQuery(); } + bool hasInformationAboutMonotonicity() const final { return impl->hasInformationAboutMonotonicity(); } + + Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const final + { + return impl->getMonotonicityForRange(type, left, right); + } + + const IFunctionBaseImpl * getImpl() const { return impl.get(); } + +private: + FunctionBaseImplPtr impl; +}; + + +class FunctionOverloadResolverAdaptor final : public IFunctionOverloadResolver +{ +public: + explicit FunctionOverloadResolverAdaptor(FunctionOverloadResolverImplPtr impl_) : impl(std::move(impl_)) {} + + String getName() const final { return impl->getName(); } + + bool isDeterministic() const final { return impl->isDeterministic(); } + + bool isDeterministicInScopeOfQuery() const final { return impl->isDeterministicInScopeOfQuery(); } + + bool isStateful() const final { return impl->isStateful(); } + + bool isVariadic() const final { return impl->isVariadic(); } + + size_t getNumberOfArguments() const final { return impl->getNumberOfArguments(); } + + void checkNumberOfArguments(size_t number_of_arguments) const final; + + FunctionBaseImplPtr buildImpl(const ColumnsWithTypeAndName & arguments) const + { + return impl->build(arguments, getReturnType(arguments)); + } + + FunctionBasePtr build(const ColumnsWithTypeAndName & arguments) const final + { + return std::make_shared(buildImpl(arguments)); + } + + void getLambdaArgumentTypes(DataTypes & arguments) const final + { + checkNumberOfArguments(arguments.size()); + impl->getLambdaArgumentTypes(arguments); + } + + ColumnNumbers getArgumentsThatAreAlwaysConstant() const final { return impl->getArgumentsThatAreAlwaysConstant(); } + + ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const final + { + return impl->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); + } + +private: + FunctionOverloadResolverImplPtr impl; + + DataTypePtr getReturnTypeWithoutLowCardinality(const ColumnsWithTypeAndName & arguments) const; + DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const; +}; + + +/// Following classes are implement IExecutableFunctionImpl, IFunctionBaseImpl and IFunctionOverloadResolverImpl via IFunction. + +class DefaultExecutable final : public IExecutableFunctionImpl +{ +public: + explicit DefaultExecutable(std::shared_ptr function_) : function(std::move(function_)) {} + + String getName() const override { return function->getName(); } + +protected: + void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) final + { + return function->executeImpl(block, arguments, result, input_rows_count); + } + void executeDryRun(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) final + { + return function->executeImplDryRun(block, arguments, result, input_rows_count); + } + bool useDefaultImplementationForNulls() const final { return function->useDefaultImplementationForNulls(); } + bool useDefaultImplementationForConstants() const final { return function->useDefaultImplementationForConstants(); } + bool useDefaultImplementationForLowCardinalityColumns() const final { return function->useDefaultImplementationForLowCardinalityColumns(); } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const final { return function->getArgumentsThatAreAlwaysConstant(); } + bool canBeExecutedOnDefaultArguments() const override { return function->canBeExecutedOnDefaultArguments(); } + +private: + std::shared_ptr function; +}; + +class DefaultFunction final : public IFunctionBaseImpl +{ +public: + DefaultFunction(std::shared_ptr function_, DataTypes arguments_, DataTypePtr return_type_) + : function(std::move(function_)), arguments(std::move(arguments_)), return_type(std::move(return_type_)) {} + + String getName() const override { return function->getName(); } + + const DataTypes & getArgumentTypes() const override { return arguments; } + const DataTypePtr & getReturnType() const override { return return_type; } + +#if USE_EMBEDDED_COMPILER + + bool isCompilable() const override { return function->isCompilable(arguments); } + + llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override { return function->compile(builder, arguments, std::move(values)); } + +#endif + + ExecutableFunctionImplPtr prepare(const Block & /*sample_block*/, const ColumnNumbers & /*arguments*/, size_t /*result*/) const override + { + return std::make_unique(function); + } + + bool isSuitableForConstantFolding() const override { return function->isSuitableForConstantFolding(); } + ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments_) const override + { + return function->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments_); + } + + bool isStateful() const override { return function->isStateful(); } + + bool isInjective(const Block & sample_block) override { return function->isInjective(sample_block); } + + bool isDeterministic() const override { return function->isDeterministic(); } + + bool isDeterministicInScopeOfQuery() const override { return function->isDeterministicInScopeOfQuery(); } + + bool hasInformationAboutMonotonicity() const override { return function->hasInformationAboutMonotonicity(); } + + using Monotonicity = IFunctionBase::Monotonicity; + Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override + { + return function->getMonotonicityForRange(type, left, right); + } +private: + std::shared_ptr function; + DataTypes arguments; + DataTypePtr return_type; +}; + +class DefaultOverloadResolver : public IFunctionOverloadResolverImpl +{ +public: + explicit DefaultOverloadResolver(std::shared_ptr function_) : function(std::move(function_)) {} + + void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override + { + return function->checkNumberOfArgumentsIfVariadic(number_of_arguments); + } + + bool isDeterministic() const override { return function->isDeterministic(); } + bool isDeterministicInScopeOfQuery() const override { return function->isDeterministicInScopeOfQuery(); } + + String getName() const override { return function->getName(); } + bool isStateful() const override { return function->isStateful(); } + bool isVariadic() const override { return function->isVariadic(); } + size_t getNumberOfArguments() const override { return function->getNumberOfArguments(); } + + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return function->getArgumentsThatAreAlwaysConstant(); } + ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const override + { + return function->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); + } + + DataTypePtr getReturnType(const DataTypes & arguments) const override { return function->getReturnTypeImpl(arguments); } + DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const override { return function->getReturnTypeImpl(arguments); } + + bool useDefaultImplementationForNulls() const override { return function->useDefaultImplementationForNulls(); } + bool useDefaultImplementationForLowCardinalityColumns() const override { return function->useDefaultImplementationForLowCardinalityColumns(); } + bool canBeExecutedOnLowCardinalityDictionary() const override { return function->canBeExecutedOnLowCardinalityDictionary(); } + + FunctionBaseImplPtr build(const ColumnsWithTypeAndName & arguments, const DataTypePtr & return_type) const override + { + DataTypes data_types(arguments.size()); + for (size_t i = 0; i < arguments.size(); ++i) + data_types[i] = arguments[i].type; + return std::make_unique(function, data_types, return_type); + } + + void getLambdaArgumentTypes(DataTypes & arguments) const override { function->getLambdaArgumentTypes(arguments); } + +private: + std::shared_ptr function; +}; + + +} diff --git a/dbms/src/Functions/DynamicTarget/Selector.h b/dbms/src/Functions/DynamicTarget/Selector.h new file mode 100644 index 000000000000..257172a72230 --- /dev/null +++ b/dbms/src/Functions/DynamicTarget/Selector.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Target.h" + +namespace DB::DynamicTarget +{ + +class PerformanceStatistic +{}; + +template +class SelectorExecutor +{ +public: + using Executor = std::function; + // Should register all executors before + void registerExecutor(std::optional arch, Executor executor) + { + if (!arch || IsArchSupported(*arch)) { + executors_.emplace_back(std::move(executor)); + } + } + + void execute(Params... params) + { + if (executors_.empty()) { + throw "There are no realizations for this arch Arch"; + } + int impl = 0; + // TODO: choose implementation. + executors_[impl](params...); + } + +private: + std::vector executors_; + PerformanceStatistic statistic_; +}; + +} // namespace DB::DynamicTarget \ No newline at end of file diff --git a/dbms/src/Functions/DynamicTarget/Target.cpp b/dbms/src/Functions/DynamicTarget/Target.cpp new file mode 100644 index 000000000000..e0bdb5091640 --- /dev/null +++ b/dbms/src/Functions/DynamicTarget/Target.cpp @@ -0,0 +1,12 @@ +#include "Target.h" + +namespace DB::DynamicTarget +{ + +bool IsArchSupported(TargetArch arch) +{ + // TODO(dakovalkov): use cpuid + return arch != TargetArch::AVX512; +} + +} // namespace DB::DynamicTarget \ No newline at end of file diff --git a/dbms/src/Functions/DynamicTarget/Target.h b/dbms/src/Functions/DynamicTarget/Target.h index 5e3032ded3e8..2873871d16c7 100644 --- a/dbms/src/Functions/DynamicTarget/Target.h +++ b/dbms/src/Functions/DynamicTarget/Target.h @@ -43,40 +43,45 @@ enum class TargetArch : int { # error "Only CLANG and GCC compilers are supported" #endif -#define DECLARE_DEFAULT_CODE (...) \ +#define DECLARE_DEFAULT_CODE(...) \ namespace TargetSpecific::Default { \ + using namespace DB::DynamicTarget::TargetSpecific::Default; \ __VA_ARGS__ \ } -#define DECLARE_SSE4_SPECIFIC_CODE (...) \ +#define DECLARE_SSE4_SPECIFIC_CODE(...) \ BEGIN_SSE4_SPECIFIC_CODE \ namespace TargetSpecific::SSE4 { \ + using namespace DB::DynamicTarget::TargetSpecific::SSE4; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE -#define DECLARE_AVX_SPECIFIC_CODE (...) \ +#define DECLARE_AVX_SPECIFIC_CODE(...) \ BEGIN_AVX_SPECIFIC_CODE \ namespace TargetSpecific::AVX { \ + using namespace DB::DynamicTarget::TargetSpecific::AVX; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE -#define DECLARE_AVX2_SPECIFIC_CODE (...) \ +#define DECLARE_AVX2_SPECIFIC_CODE(...) \ BEGIN_AVX2_SPECIFIC_CODE \ namespace TargetSpecific::AVX2 { \ + using namespace DB::DynamicTarget::TargetSpecific::AVX2; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE -#define DECLARE_AVX512_SPECIFIC_CODE (...) \ +#define DECLARE_AVX512_SPECIFIC_CODE(...) \ BEGIN_AVX512_SPECIFIC_CODE \ namespace TargetSpecific::AVX512 { \ + using namespace DB::DynamicTarget::TargetSpecific::AVX512; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE -#define DYNAMIC_CODE (...) \ +#define DECLARE_MULTITARGET_CODE(...) \ DECLARE_DEFAULT_CODE (__VA_ARGS__) \ DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ @@ -103,4 +108,6 @@ DECLARE_AVX512_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX512; ) // DECLARE_AVX512_SPECIFIC_CODE +bool IsArchSupported(TargetArch arch); + } // namespace DB::DynamicTarget \ No newline at end of file diff --git a/src/Compression/CompressionFactory.cpp b/src/Compression/CompressionFactory.cpp index 5d5c5c14de69..2598fc07b08c 100644 --- a/src/Compression/CompressionFactory.cpp +++ b/src/Compression/CompressionFactory.cpp @@ -120,7 +120,7 @@ void CompressionCodecFactory::registerCompressionCodecWithType( if (byte_code) if (!family_code_with_codec.emplace(*byte_code, creator).second) - throw Exception("CompressionCodecFactory: the codec family name '" + family_name + "' is not unique", ErrorCodes::LOGICAL_ERROR); + throw Exception("CompressionCodecFactory: the codec family code '" + std::to_string(*byte_code) + "' is not unique", ErrorCodes::LOGICAL_ERROR); } void CompressionCodecFactory::registerCompressionCodec(const String & family_name, std::optional byte_code, Creator creator) diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 069a63aa9e1d..e9a33283d5bd 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -91,3 +91,6 @@ target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_url) add_subdirectory(array) target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_array) + +add_subdirectory(DynamicTarget) +target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_dynamic_target) \ No newline at end of file diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 4f56a827f4cd..0e63b616558c 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -6,6 +6,8 @@ #include #include +#include +#include namespace DB { @@ -27,43 +29,14 @@ struct NameEndsWith static constexpr auto name = "endsWith"; }; -template -class FunctionStartsEndsWith : public IFunction -{ -public: - static constexpr auto name = Name::name; - static FunctionPtr create(const Context &) - { - return std::make_shared(); - } +using DynamicTarget::TargetArch; - String getName() const override - { - return name; - } - - size_t getNumberOfArguments() const override - { - return 2; - } +DECLARE_MULTITARGET_CODE( - bool useDefaultImplementationForConstants() const override - { - return true; - } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (!isStringOrFixedString(arguments[0])) - throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!isStringOrFixedString(arguments[1])) - throw Exception("Illegal type " + arguments[1]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return std::make_shared(); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override +template +class FunctionStartsEndsWithImpl { +public: + static void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) { const IColumn * haystack_column = block.getByPosition(arguments[0]).column.get(); const IColumn * needle_column = block.getByPosition(arguments[1]).column.get(); @@ -82,14 +55,14 @@ class FunctionStartsEndsWith : public IFunction else if (const ColumnConst * haystack_const_fixed = checkAndGetColumnConst(haystack_column)) dispatch>(ConstSource(*haystack_const_fixed), needle_column, vec_res); else - throw Exception("Illegal combination of columns as arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); + throw Exception("Illegal combination of columns as arguments of function " "getName()", ErrorCodes::ILLEGAL_COLUMN); block.getByPosition(result).column = std::move(col_res); } private: template - void dispatch(HaystackSource haystack_source, const IColumn * needle_column, PaddedPODArray & res_data) const + static void dispatch(HaystackSource haystack_source, const IColumn * needle_column, PaddedPODArray & res_data) { if (const ColumnString * needle = checkAndGetColumn(needle_column)) execute(haystack_source, StringSource(*needle), res_data); @@ -100,7 +73,7 @@ class FunctionStartsEndsWith : public IFunction else if (const ColumnConst * needle_const_fixed = checkAndGetColumnConst(needle_column)) execute>(haystack_source, ConstSource(*needle_const_fixed), res_data); else - throw Exception("Illegal combination of columns as arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); + throw Exception("Illegal combination of columns as arguments of function " "getName()", ErrorCodes::ILLEGAL_COLUMN); } template @@ -136,4 +109,58 @@ class FunctionStartsEndsWith : public IFunction } }; +) // DECLARE_MULTITARGET_CODE + +template +class FunctionStartsEndsWith : public IFunction +{ +public: + static constexpr auto name = Name::name; + static FunctionPtr create(const Context &) + { + return std::make_shared(); + } + + FunctionStartsEndsWith() { + executor_.registerExecutor(std::nullopt, TargetSpecific::Default::FunctionStartsEndsWithImpl::executeImpl); + executor_.registerExecutor(TargetArch::SSE4, TargetSpecific::SSE4::FunctionStartsEndsWithImpl::executeImpl); + executor_.registerExecutor(TargetArch::AVX, TargetSpecific::AVX::FunctionStartsEndsWithImpl::executeImpl); + executor_.registerExecutor(TargetArch::AVX2, TargetSpecific::AVX2::FunctionStartsEndsWithImpl::executeImpl); + executor_.registerExecutor(TargetArch::AVX512, TargetSpecific::AVX512::FunctionStartsEndsWithImpl::executeImpl); + } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override + { + return 2; + } + + bool useDefaultImplementationForConstants() const override + { + return true; + } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isStringOrFixedString(arguments[0])) + throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!isStringOrFixedString(arguments[1])) + throw Exception("Illegal type " + arguments[1]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return std::make_shared(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + executor_.execute(block, arguments, result, input_rows_count); + } +private: + DynamicTarget::SelectorExecutor executor_; +}; + } diff --git a/src/Functions/IFunctionImpl.h b/src/Functions/IFunctionImpl.h index 4da3e9ca0566..27e7aec6bd4d 100644 --- a/src/Functions/IFunctionImpl.h +++ b/src/Functions/IFunctionImpl.h @@ -194,7 +194,7 @@ using FunctionOverloadResolverImplPtr = std::unique_ptr +class IFunction { public: virtual ~IFunction() = default; From 43657809d8f36c0e0595e0928d76b942381bad95 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sun, 5 Apr 2020 14:01:33 +0200 Subject: [PATCH 0151/1102] awful adaptor --- dbms/src/Functions/DynamicTarget/Selector.h | 324 ++++++++++++++++++-- dbms/src/Functions/DynamicTarget/Target.cpp | 2 +- dbms/src/Functions/DynamicTarget/Target.h | 6 +- src/Functions/CMakeLists.txt | 2 +- src/Functions/FunctionStartsEndsWith.h | 98 +++--- 5 files changed, 358 insertions(+), 74 deletions(-) diff --git a/dbms/src/Functions/DynamicTarget/Selector.h b/dbms/src/Functions/DynamicTarget/Selector.h index 257172a72230..70c14dd2d110 100644 --- a/dbms/src/Functions/DynamicTarget/Selector.h +++ b/dbms/src/Functions/DynamicTarget/Selector.h @@ -2,38 +2,324 @@ #include "Target.h" +#include + +#include + namespace DB::DynamicTarget { -class PerformanceStatistic -{}; +// TODO(dakovalkov): This is copied and pasted struct from LZ4_decompress_faster.h -template -class SelectorExecutor +/** When decompressing uniform sequence of blocks (for example, blocks from one file), + * you can pass single PerformanceStatistics object to subsequent invocations of 'decompress' method. + * It will accumulate statistics and use it as a feedback to choose best specialization of algorithm at runtime. + * One PerformanceStatistics object cannot be used concurrently from different threads. + */ +struct PerformanceStatistics { -public: - using Executor = std::function; - // Should register all executors before - void registerExecutor(std::optional arch, Executor executor) + struct Element { - if (!arch || IsArchSupported(*arch)) { - executors_.emplace_back(std::move(executor)); + double count = 0; + double sum = 0; + + double adjustedCount() const + { + return count - NUM_INVOCATIONS_TO_THROW_OFF; + } + + double mean() const + { + return sum / adjustedCount(); + } + + /// For better convergence, we don't use proper estimate of stddev. + /// We want to eventually separate between two algorithms even in case + /// when there is no statistical significant difference between them. + double sigma() const + { + return mean() / sqrt(adjustedCount()); } + + void update(double seconds, double bytes) + { + ++count; + + if (count > NUM_INVOCATIONS_TO_THROW_OFF) + sum += seconds / bytes; + } + + double sample(pcg64 & stat_rng) const + { + /// If there is a variant with not enough statistics, always choose it. + /// And in that case prefer variant with less number of invocations. + + if (adjustedCount() < 2) + return adjustedCount() - 1; + else + return std::normal_distribution<>(mean(), sigma())(stat_rng); + } + }; + + /// Cold invocations may be affected by additional memory latencies. Don't take first invocations into account. + static constexpr double NUM_INVOCATIONS_TO_THROW_OFF = 2; + + /// How to select method to run. + /// -1 - automatically, based on statistics (default); + /// -2 - choose methods in round robin fashion (for performance testing). + /// >= 0 - always choose specified method (for performance testing); + ssize_t choose_method = -1; + + std::vector data; + + /// It's Ok that generator is not seeded. + pcg64 rng; + + /// To select from different algorithms we use a kind of "bandits" algorithm. + /// Sample random values from estimated normal distributions and choose the minimal. + size_t select() + { + if (choose_method < 0) + { + std::vector samples(data.size()); + for (size_t i = 0; i < data.size(); ++i) + samples[i] = choose_method == -1 + ? data[i].sample(rng) + : data[i].adjustedCount(); + + return std::min_element(samples.begin(), samples.end()) - samples.begin(); + } + else + return choose_method; + } + + size_t size() { + return data.size(); + } + + void emplace_back() { + data.emplace_back(); + } + + PerformanceStatistics() {} + PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {} +}; + +// template +// class PerformanceExecutor +// { +// public: +// using Executor = std::function; +// // Should register all executors before execute +// void registerExecutor(Executor executor) +// { +// executors.emplace_back(std::move(executor)); +// } + +// // The performance of the execution is time / weight. +// // Weight is usualy the +// void execute(int weight, Params... params) +// { +// if (executors_.empty()) { +// throw "There are no realizations for current Arch"; +// } +// int impl = 0; +// // TODO: choose implementation. +// executors_[impl](params...); +// } + +// private: +// std::vector executors; +// PerformanceStatistics statistics; +// }; + +class FunctionDynamicAdaptor : public IFunction +{ +public: + template + FunctionDynamicAdaptor(const Context & context_) : context(context_) + { + registerImplementation(); + } + + virtual String getName() const override { + return impls.front()->getName(); + } + + virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + int id = statistics.select(); + // TODO(dakovalkov): measure time and change statistics. + impls[id]->executeImpl(block, arguments, result, input_rows_count); + } + virtual void executeImplDryRun(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + impls.front()->executeImplDryRun(block, arguments, result, input_rows_count); + } + + virtual bool useDefaultImplementationForNulls() const override + { + return impls.front()->useDefaultImplementationForNulls(); + } + + virtual bool useDefaultImplementationForConstants() const override + { + return impls.front()->useDefaultImplementationForConstants(); + } + + virtual bool useDefaultImplementationForLowCardinalityColumns() const override + { + return impls.front()->useDefaultImplementationForLowCardinalityColumns(); + } + + virtual bool canBeExecutedOnLowCardinalityDictionary() const override + { + return impls.front()->canBeExecutedOnLowCardinalityDictionary(); + } + + virtual ColumnNumbers getArgumentsThatAreAlwaysConstant() const override + { + return impls.front()->getArgumentsThatAreAlwaysConstant(); } - void execute(Params... params) + virtual bool canBeExecutedOnDefaultArguments() const override { - if (executors_.empty()) { - throw "There are no realizations for this arch Arch"; + return impls.front()->canBeExecutedOnDefaultArguments(); + } + +#if USE_EMBEDDED_COMPILER + + virtual bool isCompilable() const override + { + return impls.front()->isCompilable(); + } + + virtual llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override + { + return impls.front()->compile(builder, std::move(values)); + } + +#endif + + /// Properties from IFunctionBase (see IFunction.h) + virtual bool isSuitableForConstantFolding() const override + { + return impls.front()->isSuitableForConstantFolding(); + } + virtual ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const override + { + return impls.front()->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments); + } + virtual bool isInjective(const Block & sample_block) override + { + return impls.front()->isInjective(sample_block); + } + virtual bool isDeterministic() const override + { + return impls.front()->isDeterministic(); + } + virtual bool isDeterministicInScopeOfQuery() const override + { + return impls.front()->isDeterministicInScopeOfQuery(); + } + virtual bool isStateful() const override + { + return impls.front()->isStateful(); + } + virtual bool hasInformationAboutMonotonicity() const override + { + return impls.front()->hasInformationAboutMonotonicity(); + } + + using Monotonicity = IFunctionBase::Monotonicity; + virtual Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override + { + return impls.front()->getMonotonicityForRange(type, left, right); + } + + virtual size_t getNumberOfArguments() const override { + return impls.front()->getNumberOfArguments(); + } + + virtual DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + return impls.front()->getReturnTypeImpl(arguments); + } + + virtual DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + return impls.front()->getReturnTypeImpl(arguments); + } + + virtual bool isVariadic() const override + { + return impls.front()->isVariadic(); + } + + virtual void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override + { + impls.front()->checkNumberOfArgumentsIfVariadic(number_of_arguments); + } + + virtual void getLambdaArgumentTypes(DataTypes & arguments) const override + { + impls.front()->getLambdaArgumentTypes(arguments); + } + + virtual ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const override + { + return impls.front()->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); + } + +protected: + +#if USE_EMBEDDED_COMPILER + + virtual bool isCompilableImpl(const DataTypes & /* types */) const override + { + return false; + // return impls.front()->isCompilableImpl(types); + } + + virtual llvm::Value * compileImpl(llvm::IRBuilderBase & /* builder */, const DataTypes & /* types */, ValuePlaceholders /* ph */) const override + { + throw "safasf Error"; + // return impls.front()->compileImpl(builder, types, ph); + } + +#endif + /* + * Register implementation of the function. + */ + template + void registerImplementation(TargetArch arch = TargetArch::Default) { + if (arch == TargetArch::Default || IsArchSupported(arch)) { + impls.emplace_back(Function::create(context)); + statistics.emplace_back(); } - int impl = 0; - // TODO: choose implementation. - executors_[impl](params...); } private: - std::vector executors_; - PerformanceStatistic statistic_; + const Context & context; + std::vector impls; + PerformanceStatistics statistics; }; -} // namespace DB::DynamicTarget \ No newline at end of file +#define DECLARE_STANDART_TARGET_ADAPTOR(Function) \ +class Function : public FunctionDynamicAdaptor \ +{ \ +public: \ + Function(const Context & context) : FunctionDynamicAdaptor(context) \ + { \ + registerImplementation(TargetArch::SSE4); \ + registerImplementation(TargetArch::AVX); \ + registerImplementation(TargetArch::AVX2); \ + registerImplementation(TargetArch::AVX512); \ + } \ + static FunctionPtr create(const Context & context) \ + { \ + return std::make_shared(context); \ + } \ +} + +} // namespace DB::DynamicTarget diff --git a/dbms/src/Functions/DynamicTarget/Target.cpp b/dbms/src/Functions/DynamicTarget/Target.cpp index e0bdb5091640..54c41a1786cf 100644 --- a/dbms/src/Functions/DynamicTarget/Target.cpp +++ b/dbms/src/Functions/DynamicTarget/Target.cpp @@ -9,4 +9,4 @@ bool IsArchSupported(TargetArch arch) return arch != TargetArch::AVX512; } -} // namespace DB::DynamicTarget \ No newline at end of file +} // namespace DB::DynamicTarget diff --git a/dbms/src/Functions/DynamicTarget/Target.h b/dbms/src/Functions/DynamicTarget/Target.h index 2873871d16c7..e1772a11857b 100644 --- a/dbms/src/Functions/DynamicTarget/Target.h +++ b/dbms/src/Functions/DynamicTarget/Target.h @@ -4,7 +4,7 @@ namespace DB::DynamicTarget { enum class TargetArch : int { - Scalar, + Default, // Without any additional compiler options. SSE4, AVX, AVX2, @@ -89,7 +89,7 @@ DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX512_SPECIFIC_CODE(__VA_ARGS__) DECLARE_DEFAULT_CODE( - constexpr auto BuildArch = TargetArch::Scalar; + constexpr auto BuildArch = TargetArch::Default; ) // DECLARE_DEFAULT_CODE DECLARE_SSE4_SPECIFIC_CODE( @@ -110,4 +110,4 @@ DECLARE_AVX512_SPECIFIC_CODE( bool IsArchSupported(TargetArch arch); -} // namespace DB::DynamicTarget \ No newline at end of file +} // namespace DB::DynamicTarget diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index e9a33283d5bd..85b1b717d47c 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -93,4 +93,4 @@ add_subdirectory(array) target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_array) add_subdirectory(DynamicTarget) -target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_dynamic_target) \ No newline at end of file +target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_dynamic_target) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 0e63b616558c..497abc92508e 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -29,14 +29,45 @@ struct NameEndsWith static constexpr auto name = "endsWith"; }; -using DynamicTarget::TargetArch; - DECLARE_MULTITARGET_CODE( template -class FunctionStartsEndsWithImpl { +class FunctionStartsEndsWith : public IFunction +{ public: - static void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) + static constexpr auto name = Name::name; + static FunctionPtr create(const Context &) + { + return std::make_shared(); + } + + String getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override + { + return 2; + } + + bool useDefaultImplementationForConstants() const override + { + return true; + } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (!isStringOrFixedString(arguments[0])) + throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!isStringOrFixedString(arguments[1])) + throw Exception("Illegal type " + arguments[1]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return std::make_shared(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override { const IColumn * haystack_column = block.getByPosition(arguments[0]).column.get(); const IColumn * needle_column = block.getByPosition(arguments[1]).column.get(); @@ -55,14 +86,14 @@ class FunctionStartsEndsWithImpl { else if (const ColumnConst * haystack_const_fixed = checkAndGetColumnConst(haystack_column)) dispatch>(ConstSource(*haystack_const_fixed), needle_column, vec_res); else - throw Exception("Illegal combination of columns as arguments of function " "getName()", ErrorCodes::ILLEGAL_COLUMN); + throw Exception("Illegal combination of columns as arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); block.getByPosition(result).column = std::move(col_res); } private: template - static void dispatch(HaystackSource haystack_source, const IColumn * needle_column, PaddedPODArray & res_data) + void dispatch(HaystackSource haystack_source, const IColumn * needle_column, PaddedPODArray & res_data) const { if (const ColumnString * needle = checkAndGetColumn(needle_column)) execute(haystack_source, StringSource(*needle), res_data); @@ -73,7 +104,7 @@ class FunctionStartsEndsWithImpl { else if (const ColumnConst * needle_const_fixed = checkAndGetColumnConst(needle_column)) execute>(haystack_source, ConstSource(*needle_const_fixed), res_data); else - throw Exception("Illegal combination of columns as arguments of function " "getName()", ErrorCodes::ILLEGAL_COLUMN); + throw Exception("Illegal combination of columns as arguments of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } template @@ -112,55 +143,22 @@ class FunctionStartsEndsWithImpl { ) // DECLARE_MULTITARGET_CODE template -class FunctionStartsEndsWith : public IFunction +class FunctionStartsEndsWith : public DynamicTarget::FunctionDynamicAdaptor { public: static constexpr auto name = Name::name; - static FunctionPtr create(const Context &) - { - return std::make_shared(); - } - - FunctionStartsEndsWith() { - executor_.registerExecutor(std::nullopt, TargetSpecific::Default::FunctionStartsEndsWithImpl::executeImpl); - executor_.registerExecutor(TargetArch::SSE4, TargetSpecific::SSE4::FunctionStartsEndsWithImpl::executeImpl); - executor_.registerExecutor(TargetArch::AVX, TargetSpecific::AVX::FunctionStartsEndsWithImpl::executeImpl); - executor_.registerExecutor(TargetArch::AVX2, TargetSpecific::AVX2::FunctionStartsEndsWithImpl::executeImpl); - executor_.registerExecutor(TargetArch::AVX512, TargetSpecific::AVX512::FunctionStartsEndsWithImpl::executeImpl); - } - - String getName() const override + FunctionStartsEndsWith(const Context & context_) + : FunctionDynamicAdaptor>(context_) { - return name; + registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512); } - - size_t getNumberOfArguments() const override + static FunctionPtr create(const Context & context) { - return 2; - } - - bool useDefaultImplementationForConstants() const override - { - return true; + return std::make_shared>(context); \ } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (!isStringOrFixedString(arguments[0])) - throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (!isStringOrFixedString(arguments[1])) - throw Exception("Illegal type " + arguments[1]->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return std::make_shared(); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - executor_.execute(block, arguments, result, input_rows_count); - } -private: - DynamicTarget::SelectorExecutor executor_; }; -} +} \ No newline at end of file From e0a497d575c8c776510cce47e066156e848dac80 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sun, 5 Apr 2020 15:14:59 +0200 Subject: [PATCH 0152/1102] Better interface --- dbms/src/Functions/DynamicTarget/Selector.h | 162 ++------------------ src/Functions/FunctionStartsEndsWith.h | 19 ++- 2 files changed, 27 insertions(+), 154 deletions(-) diff --git a/dbms/src/Functions/DynamicTarget/Selector.h b/dbms/src/Functions/DynamicTarget/Selector.h index 70c14dd2d110..a59022a6c288 100644 --- a/dbms/src/Functions/DynamicTarget/Selector.h +++ b/dbms/src/Functions/DynamicTarget/Selector.h @@ -133,161 +133,30 @@ struct PerformanceStatistics // PerformanceStatistics statistics; // }; -class FunctionDynamicAdaptor : public IFunction +template +class FunctionDynamicAdaptor : public DefaultFunction { public: - template - FunctionDynamicAdaptor(const Context & context_) : context(context_) + template + FunctionDynamicAdaptor(const Context & context_, Params ...params) + : DefaultFunction(params...) + , context(context_) { - registerImplementation(); - } - - virtual String getName() const override { - return impls.front()->getName(); + statistics.emplace_back(); } virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override { int id = statistics.select(); // TODO(dakovalkov): measure time and change statistics. - impls[id]->executeImpl(block, arguments, result, input_rows_count); - } - virtual void executeImplDryRun(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - impls.front()->executeImplDryRun(block, arguments, result, input_rows_count); - } - - virtual bool useDefaultImplementationForNulls() const override - { - return impls.front()->useDefaultImplementationForNulls(); - } - - virtual bool useDefaultImplementationForConstants() const override - { - return impls.front()->useDefaultImplementationForConstants(); - } - - virtual bool useDefaultImplementationForLowCardinalityColumns() const override - { - return impls.front()->useDefaultImplementationForLowCardinalityColumns(); - } - - virtual bool canBeExecutedOnLowCardinalityDictionary() const override - { - return impls.front()->canBeExecutedOnLowCardinalityDictionary(); - } - - virtual ColumnNumbers getArgumentsThatAreAlwaysConstant() const override - { - return impls.front()->getArgumentsThatAreAlwaysConstant(); - } - - virtual bool canBeExecutedOnDefaultArguments() const override - { - return impls.front()->canBeExecutedOnDefaultArguments(); - } - -#if USE_EMBEDDED_COMPILER - - virtual bool isCompilable() const override - { - return impls.front()->isCompilable(); - } - - virtual llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override - { - return impls.front()->compile(builder, std::move(values)); - } - -#endif - - /// Properties from IFunctionBase (see IFunction.h) - virtual bool isSuitableForConstantFolding() const override - { - return impls.front()->isSuitableForConstantFolding(); - } - virtual ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const override - { - return impls.front()->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments); - } - virtual bool isInjective(const Block & sample_block) override - { - return impls.front()->isInjective(sample_block); - } - virtual bool isDeterministic() const override - { - return impls.front()->isDeterministic(); - } - virtual bool isDeterministicInScopeOfQuery() const override - { - return impls.front()->isDeterministicInScopeOfQuery(); - } - virtual bool isStateful() const override - { - return impls.front()->isStateful(); - } - virtual bool hasInformationAboutMonotonicity() const override - { - return impls.front()->hasInformationAboutMonotonicity(); - } - - using Monotonicity = IFunctionBase::Monotonicity; - virtual Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override - { - return impls.front()->getMonotonicityForRange(type, left, right); - } - - virtual size_t getNumberOfArguments() const override { - return impls.front()->getNumberOfArguments(); - } - - virtual DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - return impls.front()->getReturnTypeImpl(arguments); - } - - virtual DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override - { - return impls.front()->getReturnTypeImpl(arguments); - } - - virtual bool isVariadic() const override - { - return impls.front()->isVariadic(); - } - - virtual void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override - { - impls.front()->checkNumberOfArgumentsIfVariadic(number_of_arguments); - } - - virtual void getLambdaArgumentTypes(DataTypes & arguments) const override - { - impls.front()->getLambdaArgumentTypes(arguments); - } - - virtual ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const override - { - return impls.front()->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); + if (id == 0) { + DefaultFunction::executeImpl(block, arguments, result, input_rows_count); + } else { + impls[id - 1]->executeImpl(block, arguments, result, input_rows_count); + } } protected: - -#if USE_EMBEDDED_COMPILER - - virtual bool isCompilableImpl(const DataTypes & /* types */) const override - { - return false; - // return impls.front()->isCompilableImpl(types); - } - - virtual llvm::Value * compileImpl(llvm::IRBuilderBase & /* builder */, const DataTypes & /* types */, ValuePlaceholders /* ph */) const override - { - throw "safasf Error"; - // return impls.front()->compileImpl(builder, types, ph); - } - -#endif /* * Register implementation of the function. */ @@ -301,15 +170,16 @@ class FunctionDynamicAdaptor : public IFunction private: const Context & context; - std::vector impls; + std::vector impls; // Alternative implementations. PerformanceStatistics statistics; }; +// TODO(dakovalkov): May be it's better to delete this macros and write every function explicitly for better readability. #define DECLARE_STANDART_TARGET_ADAPTOR(Function) \ -class Function : public FunctionDynamicAdaptor \ +class Function : public FunctionDynamicAdaptor \ { \ public: \ - Function(const Context & context) : FunctionDynamicAdaptor(context) \ + Function(const Context & context) : FunctionDynamicAdaptor(context) \ { \ registerImplementation(TargetArch::SSE4); \ registerImplementation(TargetArch::AVX); \ diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 497abc92508e..e883dc3267aa 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -143,22 +143,25 @@ class FunctionStartsEndsWith : public IFunction ) // DECLARE_MULTITARGET_CODE template -class FunctionStartsEndsWith : public DynamicTarget::FunctionDynamicAdaptor +class FunctionStartsEndsWith + : public DynamicTarget::FunctionDynamicAdaptor> { public: - static constexpr auto name = Name::name; FunctionStartsEndsWith(const Context & context_) - : FunctionDynamicAdaptor>(context_) + : DynamicTarget::FunctionDynamicAdaptor>(context_) { - registerImplementation>(TargetArch::SSE4); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512); + registerImplementation>(DynamicTarget::TargetArch::SSE4); + registerImplementation>(DynamicTarget::TargetArch::AVX); + registerImplementation>(DynamicTarget::TargetArch::AVX2); + registerImplementation>(DynamicTarget::TargetArch::AVX512); } static FunctionPtr create(const Context & context) { - return std::make_shared>(context); \ + return std::make_shared>(context); } }; +// template +// using FunctionStartsEndsWith = TargetSpecific::Default::FunctionStartsEndsWith; + } \ No newline at end of file From 09bb9041ec09a4ac3d4ebd12820cbe06f9b3d64e Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sun, 5 Apr 2020 21:39:12 +0200 Subject: [PATCH 0153/1102] Add descriptions, move to Functions/, rename, measure time, and so on... --- .../Functions/DynamicTarget/CMakeLists.txt | 11 - .../DynamicTarget/DynamicFunctionAdaptors.h | 263 ------------------ .../Selector.h => PerformanceAdaptors.h} | 131 +++++---- .../Target.cpp => TargetSpecific.cpp} | 6 +- .../Target.h => TargetSpecific.h} | 77 ++++- src/Functions/CMakeLists.txt | 3 - src/Functions/FunctionStartsEndsWith.h | 25 +- 7 files changed, 156 insertions(+), 360 deletions(-) delete mode 100644 dbms/src/Functions/DynamicTarget/CMakeLists.txt delete mode 100644 dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h rename dbms/src/Functions/{DynamicTarget/Selector.h => PerformanceAdaptors.h} (56%) rename dbms/src/Functions/{DynamicTarget/Target.cpp => TargetSpecific.cpp} (59%) rename dbms/src/Functions/{DynamicTarget/Target.h => TargetSpecific.h} (56%) diff --git a/dbms/src/Functions/DynamicTarget/CMakeLists.txt b/dbms/src/Functions/DynamicTarget/CMakeLists.txt deleted file mode 100644 index 154e47ab5f61..000000000000 --- a/dbms/src/Functions/DynamicTarget/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) -add_headers_and_sources(clickhouse_functions_dynamic_target .) -add_library(clickhouse_functions_dynamic_target - ${clickhouse_functions_dynamic_target_sources} - ${clickhouse_functions_dynamic_target_headers}) -target_link_libraries(clickhouse_functions_dynamic_target PRIVATE dbms) - -if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELWITHDEBINFO" OR CMAKE_BUILD_TYPE_UC STREQUAL "MINSIZEREL") - # Won't generate debug info for files with heavy template instantiation to achieve faster linking and lower size. - target_compile_options(clickhouse_functions_dynamic_target PRIVATE "-g0") -endif () diff --git a/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h b/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h deleted file mode 100644 index 123faa859e9c..000000000000 --- a/dbms/src/Functions/DynamicTarget/DynamicFunctionAdaptors.h +++ /dev/null @@ -1,263 +0,0 @@ -#pragma once -#include - -namespace DB -{ - -/// Adaptors are implement user interfaces from IFunction.h via developer interfaces from IFunctionImpl.h -/// Typically, you don't need to change this classes. - -class ExecutableFunctionAdaptor final : public IExecutableFunction -{ -public: - explicit ExecutableFunctionAdaptor(ExecutableFunctionImplPtr impl_) : impl(std::move(impl_)) {} - - String getName() const final { return impl->getName(); } - - void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count, bool dry_run) final; - - void createLowCardinalityResultCache(size_t cache_size) override; - -private: - ExecutableFunctionImplPtr impl; - - /// Cache is created by function createLowCardinalityResultCache() - ExecutableFunctionLowCardinalityResultCachePtr low_cardinality_result_cache; - - bool defaultImplementationForConstantArguments( - Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); - - bool defaultImplementationForNulls( - Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); - - void executeWithoutLowCardinalityColumns( - Block & block, const ColumnNumbers & args, size_t result, size_t input_rows_count, bool dry_run); -}; - -class FunctionBaseAdaptor final : public IFunctionBase -{ -public: - explicit FunctionBaseAdaptor(FunctionBaseImplPtr impl_) : impl(std::move(impl_)) {} - - String getName() const final { return impl->getName(); } - - const DataTypes & getArgumentTypes() const final { return impl->getArgumentTypes(); } - const DataTypePtr & getReturnType() const final { return impl->getReturnType(); } - - ExecutableFunctionPtr prepare(const Block & sample_block, const ColumnNumbers & arguments, size_t result) const final - { - return std::make_shared(impl->prepare(sample_block, arguments, result)); - } - -#if USE_EMBEDDED_COMPILER - - bool isCompilable() const final { return impl->isCompilable(); } - - llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override - { - return impl->compile(builder, std::move(values)); - } - -#endif - - bool isStateful() const final { return impl->isStateful(); } - bool isSuitableForConstantFolding() const final { return impl->isSuitableForConstantFolding(); } - - ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments) const final - { - return impl->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments); - } - - bool isInjective(const Block & sample_block) final { return impl->isInjective(sample_block); } - bool isDeterministic() const final { return impl->isDeterministic(); } - bool isDeterministicInScopeOfQuery() const final { return impl->isDeterministicInScopeOfQuery(); } - bool hasInformationAboutMonotonicity() const final { return impl->hasInformationAboutMonotonicity(); } - - Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const final - { - return impl->getMonotonicityForRange(type, left, right); - } - - const IFunctionBaseImpl * getImpl() const { return impl.get(); } - -private: - FunctionBaseImplPtr impl; -}; - - -class FunctionOverloadResolverAdaptor final : public IFunctionOverloadResolver -{ -public: - explicit FunctionOverloadResolverAdaptor(FunctionOverloadResolverImplPtr impl_) : impl(std::move(impl_)) {} - - String getName() const final { return impl->getName(); } - - bool isDeterministic() const final { return impl->isDeterministic(); } - - bool isDeterministicInScopeOfQuery() const final { return impl->isDeterministicInScopeOfQuery(); } - - bool isStateful() const final { return impl->isStateful(); } - - bool isVariadic() const final { return impl->isVariadic(); } - - size_t getNumberOfArguments() const final { return impl->getNumberOfArguments(); } - - void checkNumberOfArguments(size_t number_of_arguments) const final; - - FunctionBaseImplPtr buildImpl(const ColumnsWithTypeAndName & arguments) const - { - return impl->build(arguments, getReturnType(arguments)); - } - - FunctionBasePtr build(const ColumnsWithTypeAndName & arguments) const final - { - return std::make_shared(buildImpl(arguments)); - } - - void getLambdaArgumentTypes(DataTypes & arguments) const final - { - checkNumberOfArguments(arguments.size()); - impl->getLambdaArgumentTypes(arguments); - } - - ColumnNumbers getArgumentsThatAreAlwaysConstant() const final { return impl->getArgumentsThatAreAlwaysConstant(); } - - ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const final - { - return impl->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); - } - -private: - FunctionOverloadResolverImplPtr impl; - - DataTypePtr getReturnTypeWithoutLowCardinality(const ColumnsWithTypeAndName & arguments) const; - DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const; -}; - - -/// Following classes are implement IExecutableFunctionImpl, IFunctionBaseImpl and IFunctionOverloadResolverImpl via IFunction. - -class DefaultExecutable final : public IExecutableFunctionImpl -{ -public: - explicit DefaultExecutable(std::shared_ptr function_) : function(std::move(function_)) {} - - String getName() const override { return function->getName(); } - -protected: - void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) final - { - return function->executeImpl(block, arguments, result, input_rows_count); - } - void executeDryRun(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) final - { - return function->executeImplDryRun(block, arguments, result, input_rows_count); - } - bool useDefaultImplementationForNulls() const final { return function->useDefaultImplementationForNulls(); } - bool useDefaultImplementationForConstants() const final { return function->useDefaultImplementationForConstants(); } - bool useDefaultImplementationForLowCardinalityColumns() const final { return function->useDefaultImplementationForLowCardinalityColumns(); } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const final { return function->getArgumentsThatAreAlwaysConstant(); } - bool canBeExecutedOnDefaultArguments() const override { return function->canBeExecutedOnDefaultArguments(); } - -private: - std::shared_ptr function; -}; - -class DefaultFunction final : public IFunctionBaseImpl -{ -public: - DefaultFunction(std::shared_ptr function_, DataTypes arguments_, DataTypePtr return_type_) - : function(std::move(function_)), arguments(std::move(arguments_)), return_type(std::move(return_type_)) {} - - String getName() const override { return function->getName(); } - - const DataTypes & getArgumentTypes() const override { return arguments; } - const DataTypePtr & getReturnType() const override { return return_type; } - -#if USE_EMBEDDED_COMPILER - - bool isCompilable() const override { return function->isCompilable(arguments); } - - llvm::Value * compile(llvm::IRBuilderBase & builder, ValuePlaceholders values) const override { return function->compile(builder, arguments, std::move(values)); } - -#endif - - ExecutableFunctionImplPtr prepare(const Block & /*sample_block*/, const ColumnNumbers & /*arguments*/, size_t /*result*/) const override - { - return std::make_unique(function); - } - - bool isSuitableForConstantFolding() const override { return function->isSuitableForConstantFolding(); } - ColumnPtr getResultIfAlwaysReturnsConstantAndHasArguments(const Block & block, const ColumnNumbers & arguments_) const override - { - return function->getResultIfAlwaysReturnsConstantAndHasArguments(block, arguments_); - } - - bool isStateful() const override { return function->isStateful(); } - - bool isInjective(const Block & sample_block) override { return function->isInjective(sample_block); } - - bool isDeterministic() const override { return function->isDeterministic(); } - - bool isDeterministicInScopeOfQuery() const override { return function->isDeterministicInScopeOfQuery(); } - - bool hasInformationAboutMonotonicity() const override { return function->hasInformationAboutMonotonicity(); } - - using Monotonicity = IFunctionBase::Monotonicity; - Monotonicity getMonotonicityForRange(const IDataType & type, const Field & left, const Field & right) const override - { - return function->getMonotonicityForRange(type, left, right); - } -private: - std::shared_ptr function; - DataTypes arguments; - DataTypePtr return_type; -}; - -class DefaultOverloadResolver : public IFunctionOverloadResolverImpl -{ -public: - explicit DefaultOverloadResolver(std::shared_ptr function_) : function(std::move(function_)) {} - - void checkNumberOfArgumentsIfVariadic(size_t number_of_arguments) const override - { - return function->checkNumberOfArgumentsIfVariadic(number_of_arguments); - } - - bool isDeterministic() const override { return function->isDeterministic(); } - bool isDeterministicInScopeOfQuery() const override { return function->isDeterministicInScopeOfQuery(); } - - String getName() const override { return function->getName(); } - bool isStateful() const override { return function->isStateful(); } - bool isVariadic() const override { return function->isVariadic(); } - size_t getNumberOfArguments() const override { return function->getNumberOfArguments(); } - - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return function->getArgumentsThatAreAlwaysConstant(); } - ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t number_of_arguments) const override - { - return function->getArgumentsThatDontImplyNullableReturnType(number_of_arguments); - } - - DataTypePtr getReturnType(const DataTypes & arguments) const override { return function->getReturnTypeImpl(arguments); } - DataTypePtr getReturnType(const ColumnsWithTypeAndName & arguments) const override { return function->getReturnTypeImpl(arguments); } - - bool useDefaultImplementationForNulls() const override { return function->useDefaultImplementationForNulls(); } - bool useDefaultImplementationForLowCardinalityColumns() const override { return function->useDefaultImplementationForLowCardinalityColumns(); } - bool canBeExecutedOnLowCardinalityDictionary() const override { return function->canBeExecutedOnLowCardinalityDictionary(); } - - FunctionBaseImplPtr build(const ColumnsWithTypeAndName & arguments, const DataTypePtr & return_type) const override - { - DataTypes data_types(arguments.size()); - for (size_t i = 0; i < arguments.size(); ++i) - data_types[i] = arguments[i].type; - return std::make_unique(function, data_types, return_type); - } - - void getLambdaArgumentTypes(DataTypes & arguments) const override { function->getLambdaArgumentTypes(arguments); } - -private: - std::shared_ptr function; -}; - - -} diff --git a/dbms/src/Functions/DynamicTarget/Selector.h b/dbms/src/Functions/PerformanceAdaptors.h similarity index 56% rename from dbms/src/Functions/DynamicTarget/Selector.h rename to dbms/src/Functions/PerformanceAdaptors.h index a59022a6c288..492a4791170f 100644 --- a/dbms/src/Functions/DynamicTarget/Selector.h +++ b/dbms/src/Functions/PerformanceAdaptors.h @@ -1,21 +1,20 @@ #pragma once -#include "Target.h" - +#include #include +#include + #include -namespace DB::DynamicTarget -{ +/// This file contains Adaptors which help to combine several implementations of the function. +/// Adaptors check that implementation can be executed on the current platform and choose +/// that one which works faster according to previous runs. -// TODO(dakovalkov): This is copied and pasted struct from LZ4_decompress_faster.h +namespace DB +{ -/** When decompressing uniform sequence of blocks (for example, blocks from one file), - * you can pass single PerformanceStatistics object to subsequent invocations of 'decompress' method. - * It will accumulate statistics and use it as a feedback to choose best specialization of algorithm at runtime. - * One PerformanceStatistics object cannot be used concurrently from different threads. - */ +// TODO(dakovalkov): This is copied and pasted struct from LZ4_decompress_faster.h with little changes. struct PerformanceStatistics { struct Element @@ -105,71 +104,97 @@ struct PerformanceStatistics PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {} }; -// template -// class PerformanceExecutor -// { -// public: -// using Executor = std::function; -// // Should register all executors before execute -// void registerExecutor(Executor executor) -// { -// executors.emplace_back(std::move(executor)); -// } - -// // The performance of the execution is time / weight. -// // Weight is usualy the -// void execute(int weight, Params... params) -// { -// if (executors_.empty()) { -// throw "There are no realizations for current Arch"; -// } -// int impl = 0; -// // TODO: choose implementation. -// executors_[impl](params...); -// } - -// private: -// std::vector executors; -// PerformanceStatistics statistics; -// }; +/// Combine several IExecutableFunctionImpl into one. +/// All the implementations should be equivalent. +/// Implementation to execute will be selected based on performance on previous runs. +/// DefaultFunction should be executable on every supported platform, while alternative implementations +/// could use extended set of instructions (AVX, NEON, etc). +/// It's convenient to inherit your func from this and register all alternative implementations in the constructor. +template +class ExecutableFunctionPerformanceAdaptor : public DefaultFunction +{ +public: + template + ExecutableFunctionPerformanceAdaptor(Params ...params) : DefaultFunction(params...) + { + statistics.emplace_back(); + } + + virtual void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + auto id = statistics.select(); + Stopwatch watch; + if (id == 0) { + DefaultFunction::execute(block, arguments, result, input_rows_count); + } else { + impls[id - 1]->execute(block, arguments, result, input_rows_count); + } + watch.stop(); + // TODO(dakovalkov): Calculate something more informative. + size_t rows_summary = 0; + for (auto i : arguments) { + rows_summary += block.getByPosition(i).column->size(); + } + if (rows_summary >= 1000) { + statistics.data[id].update(watch.elapsedSeconds(), rows_summary); + } + } + + // Register alternative implementation. + template + void registerImplementation(TargetArch arch, Params... params) { + if (arch == TargetArch::Default || IsArchSupported(arch)) { + impls.emplace_back(std::make_shared(params...)); + statistics.emplace_back(); + } + } +private: + std::vector impls; // Alternative implementations. + PerformanceStatistics statistics; +}; + +// The same as ExecutableFunctionPerformanceAdaptor, but combine via IFunction interface. template -class FunctionDynamicAdaptor : public DefaultFunction +class FunctionPerformanceAdaptor : public DefaultFunction { public: template - FunctionDynamicAdaptor(const Context & context_, Params ...params) - : DefaultFunction(params...) - , context(context_) + FunctionPerformanceAdaptor(Params ...params) : DefaultFunction(params...) { statistics.emplace_back(); } virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override { - int id = statistics.select(); - // TODO(dakovalkov): measure time and change statistics. + auto id = statistics.select(); + Stopwatch watch; if (id == 0) { DefaultFunction::executeImpl(block, arguments, result, input_rows_count); } else { impls[id - 1]->executeImpl(block, arguments, result, input_rows_count); } + watch.stop(); + // TODO(dakovalkov): Calculate something more informative. + size_t rows_summary = 0; + for (auto i : arguments) { + rows_summary += block.getByPosition(i).column->size(); + } + if (rows_summary >= 1000) { + statistics.data[id].update(watch.elapsedSeconds(), rows_summary); + } } -protected: - /* - * Register implementation of the function. - */ - template - void registerImplementation(TargetArch arch = TargetArch::Default) { + // Register alternative implementation. + template + void registerImplementation(TargetArch arch, Params... params) { if (arch == TargetArch::Default || IsArchSupported(arch)) { - impls.emplace_back(Function::create(context)); + impls.emplace_back(std::make_shared(params...)); statistics.emplace_back(); } } private: - const Context & context; std::vector impls; // Alternative implementations. PerformanceStatistics statistics; }; @@ -179,7 +204,7 @@ class FunctionDynamicAdaptor : public DefaultFunction class Function : public FunctionDynamicAdaptor \ { \ public: \ - Function(const Context & context) : FunctionDynamicAdaptor(context) \ + Function(const Context &) : FunctionDynamicAdaptor() \ { \ registerImplementation(TargetArch::SSE4); \ registerImplementation(TargetArch::AVX); \ @@ -192,4 +217,4 @@ public: \ } \ } -} // namespace DB::DynamicTarget +} // namespace DB diff --git a/dbms/src/Functions/DynamicTarget/Target.cpp b/dbms/src/Functions/TargetSpecific.cpp similarity index 59% rename from dbms/src/Functions/DynamicTarget/Target.cpp rename to dbms/src/Functions/TargetSpecific.cpp index 54c41a1786cf..837a6796bf27 100644 --- a/dbms/src/Functions/DynamicTarget/Target.cpp +++ b/dbms/src/Functions/TargetSpecific.cpp @@ -1,6 +1,6 @@ -#include "Target.h" +#include -namespace DB::DynamicTarget +namespace DB { bool IsArchSupported(TargetArch arch) @@ -9,4 +9,4 @@ bool IsArchSupported(TargetArch arch) return arch != TargetArch::AVX512; } -} // namespace DB::DynamicTarget +} // namespace DB diff --git a/dbms/src/Functions/DynamicTarget/Target.h b/dbms/src/Functions/TargetSpecific.h similarity index 56% rename from dbms/src/Functions/DynamicTarget/Target.h rename to dbms/src/Functions/TargetSpecific.h index e1772a11857b..ddb9fbb74f87 100644 --- a/dbms/src/Functions/DynamicTarget/Target.h +++ b/dbms/src/Functions/TargetSpecific.h @@ -1,6 +1,60 @@ #pragma once -namespace DB::DynamicTarget +/// This file contains macros and helpers for writing platform-dependent code. +/// +/// Macroses DECLARE__SPECIFIC_CODE will wrap code inside them into the namespace TargetSpecific:: and enable +/// Arch-specific compile options. +/// Thus, it's allowed to call functions inside only after checking platform in runtime (see IsArchSupported() below) +/// For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code into the namespace TargetSpecific::Default +/// but dosn't specify any additional copile options. +/// +/// Example: +/// +/// DECLARE_DEFAULT_CODE ( +/// int func() { +/// return 1; +/// } +/// ) // DECLARE_DEFAULT_CODE +/// +/// DECLARE_AVX2_SPECIFIC_CODE ( +/// int func() { +/// return 2; +/// } +/// ) // DECLARE_DEFAULT_CODE +/// +/// int func() { +/// if (IsArchSupported(TargetArch::AVX2)) +/// return TargetSpecifc::AVX2::func(); +/// return TargetSpecifc::Default::func(); +/// } +/// +/// Sometimes code may benefit from compiling with different options. +/// For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several copies of the code and +/// compile it with different options. These copies are available via TargetSpecifc namespaces described above. +/// +/// Inside every TargetSpecific namespace there is a constexpr variable BuildArch, which TODO +/// +/// Example: +/// +/// DECLARE_MULTITARGET_CODE( +/// int func(int size, ...) { +/// int iteration_size = 1; +/// if constexpr (BuildArch == TargetArch::SSE4) +/// iteration_size = 2 +/// else if constexpr (BuildArch == TargetArch::AVX || BuildArch == TargetArch::AVX2) +/// iteration_size = 4; +/// else if constexpr (BuildArch == TargetArch::AVX512) +/// iteration_size = 8; +/// for (int i = 0; i < size; i += iteration_size) +/// ... +/// } +/// ) // DECLARE_MULTITARGET_CODE +/// +/// // All 5 versions of func are available here. Use runtime detection to choose one. +/// +/// If you want to write IFunction or IExecutableFuncionImpl with runtime dispatching, see PerformanceAdaptors.h. + +namespace DB { enum class TargetArch : int { @@ -11,11 +65,14 @@ enum class TargetArch : int { AVX512, }; +// Runtime detection. +bool IsArchSupported(TargetArch arch); + #if defined(__GNUC__) // TODO: There are lots of different AVX512 :( # define BEGIN_AVX512_SPECIFIC_CODE \ _Pragma("GCC push_options") \ - _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native\")") + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f,tune=native\")") # define BEGIN_AVX2_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native\")") @@ -40,19 +97,19 @@ enum class TargetArch : int { # define END_TARGET_SPECIFIC_CODE \ _Pragma("clang attribute pop") #else -# error "Only CLANG and GCC compilers are supported" +# error "Only CLANG and GCC compilers are supported for vectorized code generation" #endif #define DECLARE_DEFAULT_CODE(...) \ namespace TargetSpecific::Default { \ - using namespace DB::DynamicTarget::TargetSpecific::Default; \ + using namespace DB::TargetSpecific::Default; \ __VA_ARGS__ \ } #define DECLARE_SSE4_SPECIFIC_CODE(...) \ BEGIN_SSE4_SPECIFIC_CODE \ namespace TargetSpecific::SSE4 { \ - using namespace DB::DynamicTarget::TargetSpecific::SSE4; \ + using namespace DB::TargetSpecific::SSE4; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -60,7 +117,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX_SPECIFIC_CODE(...) \ BEGIN_AVX_SPECIFIC_CODE \ namespace TargetSpecific::AVX { \ - using namespace DB::DynamicTarget::TargetSpecific::AVX; \ + using namespace DB::TargetSpecific::AVX; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -68,7 +125,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX2_SPECIFIC_CODE(...) \ BEGIN_AVX2_SPECIFIC_CODE \ namespace TargetSpecific::AVX2 { \ - using namespace DB::DynamicTarget::TargetSpecific::AVX2; \ + using namespace DB::TargetSpecific::AVX2; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -76,7 +133,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX512_SPECIFIC_CODE(...) \ BEGIN_AVX512_SPECIFIC_CODE \ namespace TargetSpecific::AVX512 { \ - using namespace DB::DynamicTarget::TargetSpecific::AVX512; \ + using namespace DB::TargetSpecific::AVX512; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -108,6 +165,4 @@ DECLARE_AVX512_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX512; ) // DECLARE_AVX512_SPECIFIC_CODE -bool IsArchSupported(TargetArch arch); - -} // namespace DB::DynamicTarget +} // namespace DB diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 85b1b717d47c..069a63aa9e1d 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -91,6 +91,3 @@ target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_url) add_subdirectory(array) target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_array) - -add_subdirectory(DynamicTarget) -target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_dynamic_target) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index e883dc3267aa..1306f3572f7a 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -6,8 +6,8 @@ #include #include -#include -#include +#include +#include namespace DB { @@ -36,10 +36,6 @@ class FunctionStartsEndsWith : public IFunction { public: static constexpr auto name = Name::name; - static FunctionPtr create(const Context &) - { - return std::make_shared(); - } String getName() const override { @@ -144,16 +140,16 @@ class FunctionStartsEndsWith : public IFunction template class FunctionStartsEndsWith - : public DynamicTarget::FunctionDynamicAdaptor> + : public FunctionPerformanceAdaptor> { public: - FunctionStartsEndsWith(const Context & context_) - : DynamicTarget::FunctionDynamicAdaptor>(context_) + FunctionStartsEndsWith(const Context &) + : FunctionPerformanceAdaptor>() { - registerImplementation>(DynamicTarget::TargetArch::SSE4); - registerImplementation>(DynamicTarget::TargetArch::AVX); - registerImplementation>(DynamicTarget::TargetArch::AVX2); - registerImplementation>(DynamicTarget::TargetArch::AVX512); + registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512); } static FunctionPtr create(const Context & context) { @@ -161,7 +157,4 @@ class FunctionStartsEndsWith } }; -// template -// using FunctionStartsEndsWith = TargetSpecific::Default::FunctionStartsEndsWith; - } \ No newline at end of file From 9d875d8adb796fc6b42ae69ea05541dce7360d75 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 6 Apr 2020 09:31:26 +0200 Subject: [PATCH 0154/1102] Arch detection --- dbms/src/Functions/TargetSpecific.cpp | 29 +++++++++++++++++++++++++-- dbms/src/Functions/TargetSpecific.h | 10 ++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/dbms/src/Functions/TargetSpecific.cpp b/dbms/src/Functions/TargetSpecific.cpp index 837a6796bf27..18040111ccac 100644 --- a/dbms/src/Functions/TargetSpecific.cpp +++ b/dbms/src/Functions/TargetSpecific.cpp @@ -1,12 +1,37 @@ #include +#if defined(__GNUC__) || defined(__clang__) +# include +#else +# error "Only CLANG and GCC compilers are supported for dynamic dispatch" +#endif + namespace DB { +int GetSupportedArches() { + unsigned int eax, ebx, ecx, edx; + if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + return 0; + } + int res = 0; + if (ecx & bit_SSE4_2) + res |= static_cast(TargetArch::SSE4); + if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX)) { + // TODO(dakovalkov): check XGETBV. + res |= static_cast(TargetArch::AVX); + if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) { + res |= static_cast(TargetArch::AVX2); + } + // TODO(dakovalkov): check AVX512 support. + } + return res; +} + bool IsArchSupported(TargetArch arch) { - // TODO(dakovalkov): use cpuid - return arch != TargetArch::AVX512; + static int arches = GetSupportedArches(); + return arch == TargetArch::Default || (arches & static_cast(arch)); } } // namespace DB diff --git a/dbms/src/Functions/TargetSpecific.h b/dbms/src/Functions/TargetSpecific.h index ddb9fbb74f87..c5cd78fe03cf 100644 --- a/dbms/src/Functions/TargetSpecific.h +++ b/dbms/src/Functions/TargetSpecific.h @@ -58,11 +58,11 @@ namespace DB { enum class TargetArch : int { - Default, // Without any additional compiler options. - SSE4, - AVX, - AVX2, - AVX512, + Default = 0, // Without any additional compiler options. + SSE4 = (1 << 0), + AVX = (1 << 1), + AVX2 = (1 << 2), + AVX512 = (1 << 3), }; // Runtime detection. From 9026187c2c17668c3a932271cbcd782e07769d83 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 6 Apr 2020 09:44:54 +0200 Subject: [PATCH 0155/1102] Cosmetics --- src/Functions/FunctionStartsEndsWith.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 1306f3572f7a..730f0b9efbbd 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -1,14 +1,13 @@ -#include #include #include #include +#include +#include +#include #include #include #include -#include -#include - namespace DB { @@ -157,4 +156,4 @@ class FunctionStartsEndsWith } }; -} \ No newline at end of file +} From f07f9188ddd9406aa5a1d95596f95c3bb8318fe8 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 13 Apr 2020 11:25:53 +0200 Subject: [PATCH 0156/1102] Fix unsuccessful rebase --- {dbms/src => src}/Functions/PerformanceAdaptors.h | 2 +- {dbms/src => src}/Functions/TargetSpecific.cpp | 0 {dbms/src => src}/Functions/TargetSpecific.h | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {dbms/src => src}/Functions/PerformanceAdaptors.h (99%) rename {dbms/src => src}/Functions/TargetSpecific.cpp (100%) rename {dbms/src => src}/Functions/TargetSpecific.h (100%) diff --git a/dbms/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h similarity index 99% rename from dbms/src/Functions/PerformanceAdaptors.h rename to src/Functions/PerformanceAdaptors.h index 492a4791170f..12f4b84dab95 100644 --- a/dbms/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -215,6 +215,6 @@ public: \ { \ return std::make_shared(context); \ } \ -} +}; } // namespace DB diff --git a/dbms/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp similarity index 100% rename from dbms/src/Functions/TargetSpecific.cpp rename to src/Functions/TargetSpecific.cpp diff --git a/dbms/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h similarity index 100% rename from dbms/src/Functions/TargetSpecific.h rename to src/Functions/TargetSpecific.h From acbd3b3a7088d1665063024f16ab73f5c44ffa9b Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 13 Apr 2020 11:26:56 +0200 Subject: [PATCH 0157/1102] Compile rand function with AVX --- src/Functions/FunctionsRandom.cpp | 13 ++++--------- src/Functions/FunctionsRandom.h | 26 +++++++++++++++++++++++--- src/Functions/generateUUIDv4.cpp | 3 ++- src/Functions/randConstant.cpp | 3 ++- tests/performance/rand.xml | 23 +++++++++++++++++++++++ 5 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 tests/performance/rand.xml diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 19b2f08cdba1..7506b118d5f6 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -8,21 +8,15 @@ namespace DB { +DECLARE_MULTITARGET_CODE( + namespace { - /// NOTE Probably - /// http://www.pcg-random.org/ - /// or http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ - /// or http://docs.yeppp.info/c/group__yep_random___w_e_l_l1024a.html - /// could go better. - struct LinearCongruentialGenerator { - /// Constants from `man lrand48_r`. static constexpr UInt64 a = 0x5DEECE66D; static constexpr UInt64 c = 0xB; - /// And this is from `head -c8 /dev/urandom | xxd -p` UInt64 current = 0x09826f4a081cee35ULL; void seed(UInt64 value) @@ -63,7 +57,8 @@ void RandImpl::execute(char * output, size_t size) unalignedStore(output + 12, generator3.next()); } - /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } +) //DECLARE_MULTITARGET_CODE + } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 1ac6d24a3561..f2fab585a47e 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -5,7 +5,10 @@ #include #include - +#include +#include +// #include "TargetSpecific.h" +// #include "PerformanceAdaptors.h" namespace DB { @@ -34,9 +37,10 @@ namespace ErrorCodes * This means that the timer must be of sufficient resolution to give different values to each block. */ +DECLARE_MULTITARGET_CODE( + struct RandImpl { - /// Fill memory with random data. The memory region must be 15-bytes padded. static void execute(char * output, size_t size); }; @@ -46,7 +50,6 @@ class FunctionRandom : public IFunction { public: static constexpr auto name = Name::name; - static FunctionPtr create(const Context &) { return std::make_shared(); } String getName() const override { @@ -83,4 +86,21 @@ class FunctionRandom : public IFunction } }; +) // DECLARE_MULTITARGET_CODE + +template +class FunctionRandom : public FunctionPerformanceAdaptor> +{ +public: + FunctionRandom() { + registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512); + } + static FunctionPtr create(const Context &) { + return std::make_shared>(); + } +}; + } diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index 39013519d2fb..d543226ba5cc 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -32,7 +32,8 @@ class FunctionGenerateUUIDv4 : public IFunction size_t size = input_rows_count; vec_to.resize(size); - RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); + // TODO(dakovalkov): rewrite this workaround + TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); for (UInt128 & uuid: vec_to) { diff --git a/src/Functions/randConstant.cpp b/src/Functions/randConstant.cpp index bad4b199ee2a..3eba5abf10d6 100644 --- a/src/Functions/randConstant.cpp +++ b/src/Functions/randConstant.cpp @@ -99,7 +99,8 @@ class RandomConstantOverloadResolver : public IFunctionOverloadResolverImpl argument_types.emplace_back(arguments.back().type); typename ColumnVector::Container vec_to(1); - RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); + // TODO(dakovalkov): Rewrite this workaround + TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); ToType value = vec_to[0]; return std::make_unique>(value, argument_types, return_type); diff --git a/tests/performance/rand.xml b/tests/performance/rand.xml new file mode 100644 index 000000000000..6f73c2b1f731 --- /dev/null +++ b/tests/performance/rand.xml @@ -0,0 +1,23 @@ + + + + 20000 + + + 40000 + + + + + + + table + + numbers(100000000) + + + + + SELECT rand() FROM {table} + SELECT rand64() FROM {table} + From 9674482a6c8f433b120a43c0ce52c5f49dfd8768 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 14 Apr 2020 17:46:53 +0200 Subject: [PATCH 0158/1102] clang fixes --- .gitmodules | 3 +++ contrib/SIMDxorshift | 1 + src/Functions/TargetSpecific.cpp | 2 +- src/Functions/TargetSpecific.h | 26 +++++++++++++------------- 4 files changed, 18 insertions(+), 14 deletions(-) create mode 160000 contrib/SIMDxorshift diff --git a/.gitmodules b/.gitmodules index 7f5d1307a6e1..c14fef404579 100644 --- a/.gitmodules +++ b/.gitmodules @@ -160,3 +160,6 @@ [submodule "contrib/fmtlib"] path = contrib/fmtlib url = https://github.com/fmtlib/fmt.git +[submodule "contrib/SIMDxorshift"] + path = contrib/SIMDxorshift + url = https://github.com/lemire/SIMDxorshift diff --git a/contrib/SIMDxorshift b/contrib/SIMDxorshift new file mode 160000 index 000000000000..270eb8936c9b --- /dev/null +++ b/contrib/SIMDxorshift @@ -0,0 +1 @@ +Subproject commit 270eb8936c9b4bd038c39f1783a8eba6b8f15b09 diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index 18040111ccac..f22a586c3333 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -1,6 +1,6 @@ #include -#if defined(__GNUC__) || defined(__clang__) +#if defined(__GNUC__) # include #else # error "Only CLANG and GCC compilers are supported for dynamic dispatch" diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index c5cd78fe03cf..e58186328431 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -68,7 +68,19 @@ enum class TargetArch : int { // Runtime detection. bool IsArchSupported(TargetArch arch); -#if defined(__GNUC__) +#if defined(__clang__) +// TODO: There are lots of different AVX512 :( +# define BEGIN_AVX512_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") +# define BEGIN_AVX2_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") +# define BEGIN_AVX_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx\"))))") +# define BEGIN_SSE4_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx\"))))") +# define END_TARGET_SPECIFIC_CODE \ + _Pragma("clang attribute pop") +#elif defined(__GNUC__) // TODO: There are lots of different AVX512 :( # define BEGIN_AVX512_SPECIFIC_CODE \ _Pragma("GCC push_options") \ @@ -84,18 +96,6 @@ bool IsArchSupported(TargetArch arch); _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") # define END_TARGET_SPECIFIC_CODE \ _Pragma("GCC pop_options") -#elif defined(__clang__) -// TODO: There are lots of different AVX512 :( -# define BEGIN_AVX512_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") -# define BEGIN_AVX2_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") -# define BEGIN_AVX_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx\"))))") -# define BEGIN_SSE4_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx\"))))") -# define END_TARGET_SPECIFIC_CODE \ - _Pragma("clang attribute pop") #else # error "Only CLANG and GCC compilers are supported for vectorized code generation" #endif From 808bb14c5cf1b22234352d5dc9992df16c49d30c Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 14 Apr 2020 17:48:33 +0200 Subject: [PATCH 0159/1102] Add xorshift-rand from lemire for comparing performance --- contrib/CMakeLists.txt | 1 + contrib/SIMDxorshift-cmake/CMakeLists.txt | 12 ++++ src/Functions/CMakeLists.txt | 4 ++ src/Functions/FunctionsRandom.cpp | 1 - src/Functions/FunctionsRandom.h | 1 - src/Functions/SIMDxorshift.cpp | 40 +++++++++++ src/Functions/SIMDxorshift.h | 84 +++++++++++++++++++++++ src/Functions/registerFunctionsRandom.cpp | 2 + tests/performance/rand.xml | 1 + 9 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 contrib/SIMDxorshift-cmake/CMakeLists.txt create mode 100644 src/Functions/SIMDxorshift.cpp create mode 100644 src/Functions/SIMDxorshift.h diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index d122188ad0b6..344a06f29b76 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory (murmurhash) add_subdirectory (replxx-cmake) add_subdirectory (ryu-cmake) add_subdirectory (unixodbc-cmake) +add_subdirectory (SIMDxorshift-cmake) add_subdirectory (poco-cmake) diff --git a/contrib/SIMDxorshift-cmake/CMakeLists.txt b/contrib/SIMDxorshift-cmake/CMakeLists.txt new file mode 100644 index 000000000000..573173ff1b4a --- /dev/null +++ b/contrib/SIMDxorshift-cmake/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SIMDXORSHIFT_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/SIMDxorshift/include") +set(SIMDXORSHIFT_SRC_DIR "${SIMDXORSHIFT_INCLUDE_DIR}/../src") +set(SIMDXORSHIFT_SRC + ${SIMDXORSHIFT_SRC_DIR}/xorshift128plus.c + ${SIMDXORSHIFT_SRC_DIR}/simdxorshift128plus.c +) + +set(SIMDXORSHIFT_LIBRARY "simdxorshift") + +add_library(${SIMDXORSHIFT_LIBRARY} ${SIMDXORSHIFT_SRC}) +target_include_directories(${SIMDXORSHIFT_LIBRARY} PUBLIC "${SIMDXORSHIFT_INCLUDE_DIR}") +target_compile_options(${SIMDXORSHIFT_LIBRARY} PRIVATE -mavx2) diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 069a63aa9e1d..451dfe97a03d 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -83,6 +83,10 @@ if(USE_RAPIDJSON) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIR}) endif() + +target_link_libraries(clickhouse_functions PUBLIC "simdxorshift") +message(STATUS "Using SIMDXORSHIFT ${SIMDXORSHIFT_LIBRARY}") + add_subdirectory(GatherUtils) target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_gatherutils) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 7506b118d5f6..1f91c54c598a 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -4,7 +4,6 @@ #include #include - namespace DB { diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index f2fab585a47e..c6bcd9cb1ae5 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -44,7 +44,6 @@ struct RandImpl static void execute(char * output, size_t size); }; - template class FunctionRandom : public IFunction { diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp new file mode 100644 index 000000000000..6cad047f6dae --- /dev/null +++ b/src/Functions/SIMDxorshift.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include + +#include + +extern "C" { +#include +} + +namespace DB +{ + +BEGIN_AVX_SPECIFIC_CODE + +void RandImplXorshift::execute(char * output, size_t size) +{ + avx_xorshift128plus_key_t mykey; + avx_xorshift128plus_init(324, 4444, &mykey); + // TODO(set last 16 bytes) + for (auto * end = output + size - 16; output < end; output += 32) { + unalignedStore<__m256i>(output, avx_xorshift128plus(&mykey)); + } +} + +struct NameRandXorshift { static constexpr auto name = "randxorshift"; }; +using FunctionRandXorshift = FunctionRandomXorshift; + +void registerFunctionRandXorshift(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +END_TARGET_SPECIFIC_CODE + +} diff --git a/src/Functions/SIMDxorshift.h b/src/Functions/SIMDxorshift.h new file mode 100644 index 000000000000..e46943f695a3 --- /dev/null +++ b/src/Functions/SIMDxorshift.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} + +struct RandImplXorshift +{ + static void execute(char * output, size_t size); +}; + +template +class FunctionRandomXorshift : public IFunction +{ +public: + static constexpr auto name = Name::name; + + static FunctionPtr create(const Context &) { + return std::make_shared>(); + } + + String getName() const override + { + return name; + } + + bool isDeterministic() const override { return false; } + bool isDeterministicInScopeOfQuery() const override { return false; } + bool useDefaultImplementationForNulls() const override { return false; } + + bool isVariadic() const override { return true; } + size_t getNumberOfArguments() const override { return 0; } + + DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override + { + if (arguments.size() > 1) + throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " + + toString(arguments.size()) + ", should be 0 or 1.", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + return std::make_shared>(); + } + + void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override + { + auto col_to = ColumnVector::create(); + typename ColumnVector::Container & vec_to = col_to->getData(); + + size_t size = input_rows_count; + vec_to.resize(size); + RandImplXorshift::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(ToType)); + + block.getByPosition(result).column = std::move(col_to); + } +}; + +// template +// class FunctionRandom : public FunctionPerformanceAdaptor> +// { +// public: +// FunctionRandom() { +// registerImplementation>(TargetArch::SSE4); +// registerImplementation>(TargetArch::AVX); +// registerImplementation>(TargetArch::AVX2); +// registerImplementation>(TargetArch::AVX512); +// } +// static FunctionPtr create(const Context &) { +// return std::make_shared>(); +// } +// }; + +} diff --git a/src/Functions/registerFunctionsRandom.cpp b/src/Functions/registerFunctionsRandom.cpp index 3638474c4fef..422ec91f025c 100644 --- a/src/Functions/registerFunctionsRandom.cpp +++ b/src/Functions/registerFunctionsRandom.cpp @@ -10,6 +10,7 @@ void registerFunctionRandomPrintableASCII(FunctionFactory & factory); void registerFunctionRandomString(FunctionFactory & factory); void registerFunctionRandomFixedString(FunctionFactory & factory); void registerFunctionRandomStringUTF8(FunctionFactory & factory); +void registerFunctionRandXorshift(FunctionFactory & factory); void registerFunctionsRandom(FunctionFactory & factory) { @@ -21,6 +22,7 @@ void registerFunctionsRandom(FunctionFactory & factory) registerFunctionRandomString(factory); registerFunctionRandomFixedString(factory); registerFunctionRandomStringUTF8(factory); + registerFunctionRandXorshift(factory); } } diff --git a/tests/performance/rand.xml b/tests/performance/rand.xml index 6f73c2b1f731..32ec38ddb4ef 100644 --- a/tests/performance/rand.xml +++ b/tests/performance/rand.xml @@ -20,4 +20,5 @@ SELECT rand() FROM {table} SELECT rand64() FROM {table} + SELECT randxorshift() FROM {table} From 0afa67208218eff5c4004b2d9084eb3b4266bcca Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 15 May 2020 10:40:27 +0200 Subject: [PATCH 0160/1102] make randxorshift great again --- src/Functions/FunctionsRandom.h | 21 +++++----- src/Functions/SIMDxorshift.cpp | 74 +++++++++++++++++++++++++++++---- src/Functions/SIMDxorshift.h | 64 +++++----------------------- 3 files changed, 87 insertions(+), 72 deletions(-) diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index c6bcd9cb1ae5..990c3a5f466d 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -44,8 +44,10 @@ struct RandImpl static void execute(char * output, size_t size); }; -template -class FunctionRandom : public IFunction +) // DECLARE_MULTITARGET_CODE + +template +class FunctionRandomImpl : public IFunction { public: static constexpr auto name = Name::name; @@ -85,21 +87,20 @@ class FunctionRandom : public IFunction } }; -) // DECLARE_MULTITARGET_CODE - template -class FunctionRandom : public FunctionPerformanceAdaptor> +class FunctionRandom : public FunctionPerformanceAdaptor> { public: FunctionRandom() { - registerImplementation>(TargetArch::SSE4); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512); + registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512); } + static FunctionPtr create(const Context &) { return std::make_shared>(); } }; -} +} // namespace DB diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp index 6cad047f6dae..739077b5480c 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/SIMDxorshift.cpp @@ -8,33 +8,89 @@ #include -extern "C" { +extern "C" +{ +#include #include } namespace DB { -BEGIN_AVX_SPECIFIC_CODE +DECLARE_DEFAULT_CODE( + +void RandXorshiftImpl::execute(char * output, size_t size) +{ + char * end = output + size; + + xorshift128plus_key_s mykey; + + xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), + 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), + &mykey); + + const int bytes_per_write = 8; + const intptr_t mask = bytes_per_write - 1; + + // Process head to make output aligned. + unalignedStore(output, xorshift128plus(&mykey)); + output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); + + while (end - output > 0) { + *reinterpret_cast(output) = xorshift128plus(&mykey); + output += bytes_per_write; + } +} + +) // DECLARE_DEFAULT_CODE -void RandImplXorshift::execute(char * output, size_t size) +DECLARE_AVX2_SPECIFIC_CODE( + +void RandXorshiftImpl::execute(char * output, size_t size) { + char * end = output + size; + avx_xorshift128plus_key_t mykey; - avx_xorshift128plus_init(324, 4444, &mykey); - // TODO(set last 16 bytes) - for (auto * end = output + size - 16; output < end; output += 32) { - unalignedStore<__m256i>(output, avx_xorshift128plus(&mykey)); + avx_xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), + 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), + &mykey); + + const int safe_overwrite = 16; // How many bytes we can write behind the end. + const int bytes_per_write = 32; + const intptr_t mask = bytes_per_write - 1; + + if (size + safe_overwrite <= bytes_per_write) { + _mm_storeu_si128(reinterpret_cast<__m128i*>(output), + _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); + return; + } + + // Process head to make output aligned. + _mm256_storeu_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); + output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); + + while ((end - output) + safe_overwrite >= bytes_per_write) { + _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); + output += bytes_per_write; } + + // Process tail. + if ((end - output) > 0) { + _mm_store_si128(reinterpret_cast<__m128i*>(output), + _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); + } } +) // DECLARE_AVX2_SPECIFIC_CODE + struct NameRandXorshift { static constexpr auto name = "randxorshift"; }; using FunctionRandXorshift = FunctionRandomXorshift; +struct NameRandXorshift64 { static constexpr auto name = "randxorshift64"; }; +using FunctionRandXorshift64 = FunctionRandomXorshift; void registerFunctionRandXorshift(FunctionFactory & factory) { factory.registerFunction(); } -END_TARGET_SPECIFIC_CODE - } diff --git a/src/Functions/SIMDxorshift.h b/src/Functions/SIMDxorshift.h index e46943f695a3..46732c4d876e 100644 --- a/src/Functions/SIMDxorshift.h +++ b/src/Functions/SIMDxorshift.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -16,69 +17,26 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } -struct RandImplXorshift +DECLARE_MULTITARGET_CODE( + +struct RandXorshiftImpl { static void execute(char * output, size_t size); }; +) // DECLARE_MULTITARGET_CODE + template -class FunctionRandomXorshift : public IFunction +class FunctionRandomXorshift : public FunctionPerformanceAdaptor> { public: - static constexpr auto name = Name::name; + FunctionRandomXorshift() { + registerImplementation>(TargetArch::AVX2); + } static FunctionPtr create(const Context &) { return std::make_shared>(); } - - String getName() const override - { - return name; - } - - bool isDeterministic() const override { return false; } - bool isDeterministicInScopeOfQuery() const override { return false; } - bool useDefaultImplementationForNulls() const override { return false; } - - bool isVariadic() const override { return true; } - size_t getNumberOfArguments() const override { return 0; } - - DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override - { - if (arguments.size() > 1) - throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 0 or 1.", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - return std::make_shared>(); - } - - void executeImpl(Block & block, const ColumnNumbers &, size_t result, size_t input_rows_count) override - { - auto col_to = ColumnVector::create(); - typename ColumnVector::Container & vec_to = col_to->getData(); - - size_t size = input_rows_count; - vec_to.resize(size); - RandImplXorshift::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(ToType)); - - block.getByPosition(result).column = std::move(col_to); - } }; -// template -// class FunctionRandom : public FunctionPerformanceAdaptor> -// { -// public: -// FunctionRandom() { -// registerImplementation>(TargetArch::SSE4); -// registerImplementation>(TargetArch::AVX); -// registerImplementation>(TargetArch::AVX2); -// registerImplementation>(TargetArch::AVX512); -// } -// static FunctionPtr create(const Context &) { -// return std::make_shared>(); -// } -// }; - -} +} // namespace DB From 089b3ca0085b24716c422ff16ed8342ae340d935 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 15 May 2020 12:10:34 +0200 Subject: [PATCH 0161/1102] Check target properly --- src/Functions/FunctionStartsEndsWith.h | 9 +++++---- src/Functions/FunctionsRandom.h | 2 +- src/Functions/PerformanceAdaptors.h | 25 +++++++---------------- src/Functions/TargetSpecific.cpp | 27 ++++++++++++++++++++++--- src/Functions/TargetSpecific.h | 28 ++++++++++++++------------ 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 730f0b9efbbd..29c81796d0ed 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -145,11 +145,12 @@ class FunctionStartsEndsWith FunctionStartsEndsWith(const Context &) : FunctionPerformanceAdaptor>() { - registerImplementation>(TargetArch::SSE4); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512); + registerImplementation> (TargetArch::SSE4); + registerImplementation> (TargetArch::AVX); + registerImplementation> (TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512f); } + static FunctionPtr create(const Context & context) { return std::make_shared>(context); diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 990c3a5f466d..98d04d61ad10 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -95,7 +95,7 @@ class FunctionRandom : public FunctionPerformanceAdaptor>(TargetArch::SSE4); registerImplementation>(TargetArch::AVX); registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512); + registerImplementation>(TargetArch::AVX512f); } static FunctionPtr create(const Context &) { diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 12f4b84dab95..a97fdbce0b00 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -104,6 +104,11 @@ struct PerformanceStatistics PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {} }; +struct PerformanceAdaptorOptions +{ + +}; + /// Combine several IExecutableFunctionImpl into one. /// All the implementations should be equivalent. /// Implementation to execute will be selected based on performance on previous runs. @@ -152,6 +157,7 @@ class ExecutableFunctionPerformanceAdaptor : public DefaultFunction private: std::vector impls; // Alternative implementations. PerformanceStatistics statistics; + PerformanceAdaptorOptions options; }; // The same as ExecutableFunctionPerformanceAdaptor, but combine via IFunction interface. @@ -197,24 +203,7 @@ class FunctionPerformanceAdaptor : public DefaultFunction private: std::vector impls; // Alternative implementations. PerformanceStatistics statistics; -}; - -// TODO(dakovalkov): May be it's better to delete this macros and write every function explicitly for better readability. -#define DECLARE_STANDART_TARGET_ADAPTOR(Function) \ -class Function : public FunctionDynamicAdaptor \ -{ \ -public: \ - Function(const Context &) : FunctionDynamicAdaptor() \ - { \ - registerImplementation(TargetArch::SSE4); \ - registerImplementation(TargetArch::AVX); \ - registerImplementation(TargetArch::AVX2); \ - registerImplementation(TargetArch::AVX512); \ - } \ - static FunctionPtr create(const Context & context) \ - { \ - return std::make_shared(context); \ - } \ + PerformanceAdaptorOptions options; }; } // namespace DB diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index f22a586c3333..aa017823e540 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -2,6 +2,7 @@ #if defined(__GNUC__) # include +# include #else # error "Only CLANG and GCC compilers are supported for dynamic dispatch" #endif @@ -9,6 +10,11 @@ namespace DB { +__attribute__ ((target("xsave"))) +uint64_t xgetbv(uint32_t ecx) { + return _xgetbv(ecx); +} + int GetSupportedArches() { unsigned int eax, ebx, ecx, edx; if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { @@ -17,13 +23,15 @@ int GetSupportedArches() { int res = 0; if (ecx & bit_SSE4_2) res |= static_cast(TargetArch::SSE4); - if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX)) { - // TODO(dakovalkov): check XGETBV. + // (xgetbv(0) & 0x6) == 0x6 checks that XMM state and YMM state are enabled. + if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX) && (xgetbv(0) & 0x6) == 0x6) { res |= static_cast(TargetArch::AVX); if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) { res |= static_cast(TargetArch::AVX2); + if (ebx & bit_AVX512F) { + res |= static_cast(TargetArch::AVX512f); + } } - // TODO(dakovalkov): check AVX512 support. } return res; } @@ -34,4 +42,17 @@ bool IsArchSupported(TargetArch arch) return arch == TargetArch::Default || (arches & static_cast(arch)); } +String ToString(TargetArch arch) +{ + switch (arch) { + case TargetArch::Default: return "default"; + case TargetArch::SSE4: return "sse4"; + case TargetArch::AVX: return "avx"; + case TargetArch::AVX2: return "avx2"; + case TargetArch::AVX512f: return "avx512f"; + } + + __builtin_unreachable(); +} + } // namespace DB diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index e58186328431..accb1dd7fab7 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -1,5 +1,7 @@ #pragma once +#include + /// This file contains macros and helpers for writing platform-dependent code. /// /// Macroses DECLARE__SPECIFIC_CODE will wrap code inside them into the namespace TargetSpecific:: and enable @@ -62,16 +64,17 @@ enum class TargetArch : int { SSE4 = (1 << 0), AVX = (1 << 1), AVX2 = (1 << 2), - AVX512 = (1 << 3), + AVX512f = (1 << 3), }; // Runtime detection. bool IsArchSupported(TargetArch arch); +String ToString(TargetArch arch); + #if defined(__clang__) -// TODO: There are lots of different AVX512 :( -# define BEGIN_AVX512_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") +# define BEGIN_AVX512f_SPECIFIC_CODE \ + _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f\"))))") # define BEGIN_AVX2_SPECIFIC_CODE \ _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") # define BEGIN_AVX_SPECIFIC_CODE \ @@ -81,8 +84,7 @@ bool IsArchSupported(TargetArch arch); # define END_TARGET_SPECIFIC_CODE \ _Pragma("clang attribute pop") #elif defined(__GNUC__) -// TODO: There are lots of different AVX512 :( -# define BEGIN_AVX512_SPECIFIC_CODE \ +# define BEGIN_AVX512f_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f,tune=native\")") # define BEGIN_AVX2_SPECIFIC_CODE \ @@ -130,10 +132,10 @@ namespace TargetSpecific::AVX2 { \ } \ END_TARGET_SPECIFIC_CODE -#define DECLARE_AVX512_SPECIFIC_CODE(...) \ -BEGIN_AVX512_SPECIFIC_CODE \ -namespace TargetSpecific::AVX512 { \ - using namespace DB::TargetSpecific::AVX512; \ +#define DECLARE_AVX512f_SPECIFIC_CODE(...) \ +BEGIN_AVX512f_SPECIFIC_CODE \ +namespace TargetSpecific::AVX512f { \ + using namespace DB::TargetSpecific::AVX512f; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -143,7 +145,7 @@ DECLARE_DEFAULT_CODE (__VA_ARGS__) \ DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ -DECLARE_AVX512_SPECIFIC_CODE(__VA_ARGS__) +DECLARE_AVX512f_SPECIFIC_CODE(__VA_ARGS__) DECLARE_DEFAULT_CODE( constexpr auto BuildArch = TargetArch::Default; @@ -161,8 +163,8 @@ DECLARE_AVX2_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX2; ) // DECLARE_AVX2_SPECIFIC_CODE -DECLARE_AVX512_SPECIFIC_CODE( - constexpr auto BuildArch = TargetArch::AVX512; +DECLARE_AVX512f_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::AVX512f; ) // DECLARE_AVX512_SPECIFIC_CODE } // namespace DB From 43f5ca868a14f49d0382d4954a247f47c0f919e8 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 15 May 2020 14:00:20 +0200 Subject: [PATCH 0162/1102] Cosmetics --- src/Functions/FunctionStartsEndsWith.h | 2 +- src/Functions/FunctionsRandom.cpp | 10 +++- src/Functions/FunctionsRandom.h | 8 +-- src/Functions/PerformanceAdaptors.h | 2 +- src/Functions/SIMDxorshift.cpp | 13 ++--- src/Functions/TargetSpecific.cpp | 4 +- src/Functions/TargetSpecific.h | 69 ++++++++++++++------------ 7 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 29c81796d0ed..77692b5c4141 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -148,7 +148,7 @@ class FunctionStartsEndsWith registerImplementation> (TargetArch::SSE4); registerImplementation> (TargetArch::AVX); registerImplementation> (TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512f); + registerImplementation>(TargetArch::AVX512F); } static FunctionPtr create(const Context & context) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 1f91c54c598a..11861d2d12cc 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -11,11 +11,19 @@ DECLARE_MULTITARGET_CODE( namespace { + /// NOTE Probably + /// http://www.pcg-random.org/ + /// or http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/ + /// or http://docs.yeppp.info/c/group__yep_random___w_e_l_l1024a.html + /// could go better. + struct LinearCongruentialGenerator { + /// Constants from `man lrand48_r`. static constexpr UInt64 a = 0x5DEECE66D; static constexpr UInt64 c = 0xB; + /// And this is from `head -c8 /dev/urandom | xxd -p` UInt64 current = 0x09826f4a081cee35ULL; void seed(UInt64 value) @@ -55,7 +63,7 @@ void RandImpl::execute(char * output, size_t size) unalignedStore(output + 8, generator2.next()); unalignedStore(output + 12, generator3.next()); } - + /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } ) //DECLARE_MULTITARGET_CODE diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 98d04d61ad10..ccc218574b01 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -92,10 +92,10 @@ class FunctionRandom : public FunctionPerformanceAdaptor>(TargetArch::SSE4); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512f); + registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512F); } static FunctionPtr create(const Context &) { diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index a97fdbce0b00..2ecd60e67fef 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -160,7 +160,7 @@ class ExecutableFunctionPerformanceAdaptor : public DefaultFunction PerformanceAdaptorOptions options; }; -// The same as ExecutableFunctionPerformanceAdaptor, but combine via IFunction interface. +/// The same as ExecutableFunctionPerformanceAdaptor, but combine via IFunction interface. template class FunctionPerformanceAdaptor : public DefaultFunction { diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp index 739077b5480c..9a45257d28d5 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/SIMDxorshift.cpp @@ -29,8 +29,8 @@ void RandXorshiftImpl::execute(char * output, size_t size) 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), &mykey); - const int bytes_per_write = 8; - const intptr_t mask = bytes_per_write - 1; + constexpr int bytes_per_write = 8; + constexpr intptr_t mask = bytes_per_write - 1; // Process head to make output aligned. unalignedStore(output, xorshift128plus(&mykey)); @@ -55,9 +55,9 @@ void RandXorshiftImpl::execute(char * output, size_t size) 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), &mykey); - const int safe_overwrite = 16; // How many bytes we can write behind the end. - const int bytes_per_write = 32; - const intptr_t mask = bytes_per_write - 1; + constexpr int safe_overwrite = 16; // How many bytes we can write behind the end. + constexpr int bytes_per_write = 32; + constexpr intptr_t mask = bytes_per_write - 1; if (size + safe_overwrite <= bytes_per_write) { _mm_storeu_si128(reinterpret_cast<__m128i*>(output), @@ -91,6 +91,7 @@ using FunctionRandXorshift64 = FunctionRandomXorshift(); + factory.registerFunction(); } -} +} // namespace DB diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index aa017823e540..19604a83ab7e 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -29,7 +29,7 @@ int GetSupportedArches() { if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) { res |= static_cast(TargetArch::AVX2); if (ebx & bit_AVX512F) { - res |= static_cast(TargetArch::AVX512f); + res |= static_cast(TargetArch::AVX512F); } } } @@ -49,7 +49,7 @@ String ToString(TargetArch arch) case TargetArch::SSE4: return "sse4"; case TargetArch::AVX: return "avx"; case TargetArch::AVX2: return "avx2"; - case TargetArch::AVX512f: return "avx512f"; + case TargetArch::AVX512F: return "avx512f"; } __builtin_unreachable(); diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index accb1dd7fab7..888d88d1d772 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -4,42 +4,47 @@ /// This file contains macros and helpers for writing platform-dependent code. /// -/// Macroses DECLARE__SPECIFIC_CODE will wrap code inside them into the namespace TargetSpecific:: and enable -/// Arch-specific compile options. -/// Thus, it's allowed to call functions inside only after checking platform in runtime (see IsArchSupported() below) -/// For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code into the namespace TargetSpecific::Default -/// but dosn't specify any additional copile options. +/// Macroses DECLARE__SPECIFIC_CODE will wrap code inside them into the +/// namespace TargetSpecific:: and enable Arch-specific compile options. +/// Thus, it's allowed to call functions inside these namespaces only after +/// checking platform in runtime (see IsArchSupported() below). +/// +/// For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code +/// into the namespace TargetSpecific::Default but dosn't specify any additional +/// copile options. /// /// Example: /// /// DECLARE_DEFAULT_CODE ( -/// int func() { +/// int funcImpl() { /// return 1; /// } /// ) // DECLARE_DEFAULT_CODE /// /// DECLARE_AVX2_SPECIFIC_CODE ( -/// int func() { +/// int funcImpl() { /// return 2; /// } /// ) // DECLARE_DEFAULT_CODE /// /// int func() { /// if (IsArchSupported(TargetArch::AVX2)) -/// return TargetSpecifc::AVX2::func(); -/// return TargetSpecifc::Default::func(); +/// return TargetSpecifc::AVX2::funcImpl(); +/// return TargetSpecifc::Default::funcImpl(); /// } /// /// Sometimes code may benefit from compiling with different options. -/// For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several copies of the code and -/// compile it with different options. These copies are available via TargetSpecifc namespaces described above. +/// For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several +/// copies of the code and compile it with different options. These copies are +/// available via TargetSpecifc namespaces described above. /// -/// Inside every TargetSpecific namespace there is a constexpr variable BuildArch, which TODO +/// Inside every TargetSpecific namespace there is a constexpr variable BuildArch, +/// which indicates the target platform for current code. /// /// Example: /// /// DECLARE_MULTITARGET_CODE( -/// int func(int size, ...) { +/// int funcImpl(int size, ...) { /// int iteration_size = 1; /// if constexpr (BuildArch == TargetArch::SSE4) /// iteration_size = 2 @@ -60,11 +65,11 @@ namespace DB { enum class TargetArch : int { - Default = 0, // Without any additional compiler options. - SSE4 = (1 << 0), - AVX = (1 << 1), - AVX2 = (1 << 2), - AVX512f = (1 << 3), + Default = 0, // Without any additional compiler options. + SSE4 = (1 << 0), + AVX = (1 << 1), + AVX2 = (1 << 2), + AVX512F = (1 << 3), }; // Runtime detection. @@ -73,7 +78,7 @@ bool IsArchSupported(TargetArch arch); String ToString(TargetArch arch); #if defined(__clang__) -# define BEGIN_AVX512f_SPECIFIC_CODE \ +# define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f\"))))") # define BEGIN_AVX2_SPECIFIC_CODE \ _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") @@ -84,7 +89,7 @@ String ToString(TargetArch arch); # define END_TARGET_SPECIFIC_CODE \ _Pragma("clang attribute pop") #elif defined(__GNUC__) -# define BEGIN_AVX512f_SPECIFIC_CODE \ +# define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f,tune=native\")") # define BEGIN_AVX2_SPECIFIC_CODE \ @@ -132,20 +137,20 @@ namespace TargetSpecific::AVX2 { \ } \ END_TARGET_SPECIFIC_CODE -#define DECLARE_AVX512f_SPECIFIC_CODE(...) \ -BEGIN_AVX512f_SPECIFIC_CODE \ -namespace TargetSpecific::AVX512f { \ - using namespace DB::TargetSpecific::AVX512f; \ +#define DECLARE_AVX512F_SPECIFIC_CODE(...) \ +BEGIN_AVX512F_SPECIFIC_CODE \ +namespace TargetSpecific::AVX512F { \ + using namespace DB::TargetSpecific::AVX512F; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE #define DECLARE_MULTITARGET_CODE(...) \ -DECLARE_DEFAULT_CODE (__VA_ARGS__) \ -DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ -DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ -DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ -DECLARE_AVX512f_SPECIFIC_CODE(__VA_ARGS__) +DECLARE_DEFAULT_CODE (__VA_ARGS__) \ +DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_AVX512F_SPECIFIC_CODE(__VA_ARGS__) DECLARE_DEFAULT_CODE( constexpr auto BuildArch = TargetArch::Default; @@ -163,8 +168,8 @@ DECLARE_AVX2_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX2; ) // DECLARE_AVX2_SPECIFIC_CODE -DECLARE_AVX512f_SPECIFIC_CODE( - constexpr auto BuildArch = TargetArch::AVX512f; -) // DECLARE_AVX512_SPECIFIC_CODE +DECLARE_AVX512F_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::AVX512F; +) // DECLARE_AVX512F_SPECIFIC_CODE } // namespace DB From d6d67b0da42b84302bfd13b624b3dad422941cd7 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 15 May 2020 14:06:12 +0200 Subject: [PATCH 0163/1102] Fix bug --- src/Functions/SIMDxorshift.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp index 9a45257d28d5..f7c5f953e092 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/SIMDxorshift.cpp @@ -55,11 +55,12 @@ void RandXorshiftImpl::execute(char * output, size_t size) 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), &mykey); - constexpr int safe_overwrite = 16; // How many bytes we can write behind the end. + constexpr int safe_overwrite = 15; // How many bytes we can write behind the end. constexpr int bytes_per_write = 32; constexpr intptr_t mask = bytes_per_write - 1; - if (size + safe_overwrite <= bytes_per_write) { + if (size + safe_overwrite < bytes_per_write) { + // size <= 16. _mm_storeu_si128(reinterpret_cast<__m128i*>(output), _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); return; @@ -74,7 +75,7 @@ void RandXorshiftImpl::execute(char * output, size_t size) output += bytes_per_write; } - // Process tail. + // Process tail. (end - output) <= 16. if ((end - output) > 0) { _mm_store_si128(reinterpret_cast<__m128i*>(output), _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); From 80ab14e3f96a4013a687d352bc2457c31ed1d099 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 08:15:39 +0200 Subject: [PATCH 0164/1102] Get rid of copy-paste in PerformanceAdaptor --- src/Functions/FunctionStartsEndsWith.h | 9 +- src/Functions/FunctionsRandom.h | 14 ++- src/Functions/PerformanceAdaptors.h | 132 +++++++++++++++---------- src/Functions/SIMDxorshift.cpp | 2 - src/Functions/SIMDxorshift.h | 12 ++- 5 files changed, 108 insertions(+), 61 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 77692b5c4141..44850257f990 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -41,6 +41,11 @@ class FunctionStartsEndsWith : public IFunction return name; } + static String getImplementationTag() + { + return ToString(BuildArch); + } + size_t getNumberOfArguments() const override { return 2; @@ -143,7 +148,9 @@ class FunctionStartsEndsWith { public: FunctionStartsEndsWith(const Context &) - : FunctionPerformanceAdaptor>() + : FunctionPerformanceAdaptor>( + PerformanceAdaptorOptions() + ) { registerImplementation> (TargetArch::SSE4); registerImplementation> (TargetArch::AVX); diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index ccc218574b01..ae54243164f9 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -42,6 +42,7 @@ DECLARE_MULTITARGET_CODE( struct RandImpl { static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch); } }; ) // DECLARE_MULTITARGET_CODE @@ -57,6 +58,11 @@ class FunctionRandomImpl : public IFunction return name; } + static String getImplementationTag() + { + return RandImpl::getImplementationTag(); + } + bool isDeterministic() const override { return false; } bool isDeterministicInScopeOfQuery() const override { return false; } bool useDefaultImplementationForNulls() const override { return false; } @@ -91,14 +97,18 @@ template class FunctionRandom : public FunctionPerformanceAdaptor> { public: - FunctionRandom() { + FunctionRandom() + : FunctionPerformanceAdaptor>( + PerformanceAdaptorOptions()) + { registerImplementation>(TargetArch::SSE4); registerImplementation>(TargetArch::AVX); registerImplementation>(TargetArch::AVX2); registerImplementation>(TargetArch::AVX512F); } - static FunctionPtr create(const Context &) { + static FunctionPtr create(const Context &) + { return std::make_shared>(); } }; diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 2ecd60e67fef..f7bb8cfd6eea 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -92,10 +92,14 @@ struct PerformanceStatistics return choose_method; } - size_t size() { + size_t size() const { return data.size(); } + bool empty() const { + return size() == 0; + } + void emplace_back() { data.emplace_back(); } @@ -106,7 +110,47 @@ struct PerformanceStatistics struct PerformanceAdaptorOptions { + std::optional> implementations; +}; + +// Redirects IExecutableFunctionImpl::execute() and IFunction:executeImpl() to executeFunctionImpl(); +template +class FunctionExecutor; + +template +class FunctionExecutor>> + : public DefaultFunction +{ +public: + using BaseFunctionPtr = ExecutableFunctionImplPtr; + + template + FunctionExecutor(Args ...args) : DefaultFunction(args...) {} + virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; + + virtual void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + executeFunctionImpl(block, arguments, result, input_rows_count); + } +}; + +template +class FunctionExecutor>> + : public DefaultFunction +{ +public: + using BaseFunctionPtr = FunctionPtr; + + template + FunctionExecutor(Args ...args) : DefaultFunction(args...) {} + + virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; + + virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + executeFunctionImpl(block, arguments, result, input_rows_count); + } }; /// Combine several IExecutableFunctionImpl into one. @@ -116,69 +160,60 @@ struct PerformanceAdaptorOptions /// could use extended set of instructions (AVX, NEON, etc). /// It's convenient to inherit your func from this and register all alternative implementations in the constructor. template -class ExecutableFunctionPerformanceAdaptor : public DefaultFunction +class FunctionPerformanceAdaptor : public FunctionExecutor { public: - template - ExecutableFunctionPerformanceAdaptor(Params ...params) : DefaultFunction(params...) - { - statistics.emplace_back(); - } + using BaseFunctionPtr = FunctionExecutor::BaseFunctionPtr; - virtual void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + template + FunctionPerformanceAdaptor(PerformanceAdaptorOptions options_, Params ...params) + : FunctionExecutor(params...) + , options(std::move(options_)) { - auto id = statistics.select(); - Stopwatch watch; - if (id == 0) { - DefaultFunction::execute(block, arguments, result, input_rows_count); - } else { - impls[id - 1]->execute(block, arguments, result, input_rows_count); - } - watch.stop(); - // TODO(dakovalkov): Calculate something more informative. - size_t rows_summary = 0; - for (auto i : arguments) { - rows_summary += block.getByPosition(i).column->size(); - } - if (rows_summary >= 1000) { - statistics.data[id].update(watch.elapsedSeconds(), rows_summary); + if (isImplementationEnabled(DefaultFunction::getImplementationTag())) { + statistics.emplace_back(); } } // Register alternative implementation. template void registerImplementation(TargetArch arch, Params... params) { - if (arch == TargetArch::Default || IsArchSupported(arch)) { + if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag())) { impls.emplace_back(std::make_shared(params...)); statistics.emplace_back(); } } -private: - std::vector impls; // Alternative implementations. - PerformanceStatistics statistics; - PerformanceAdaptorOptions options; -}; - -/// The same as ExecutableFunctionPerformanceAdaptor, but combine via IFunction interface. -template -class FunctionPerformanceAdaptor : public DefaultFunction -{ -public: - template - FunctionPerformanceAdaptor(Params ...params) : DefaultFunction(params...) - { - statistics.emplace_back(); + bool isImplementationEnabled(const String & impl_tag) { + if (!options.implementations) { + return true; + } + for (const auto & tag : *options.implementations) { + if (tag == impl_tag) { + return true; + } + } + return false; } - virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override +protected: + virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, + size_t result, size_t input_rows_count) override { + if (statistics.empty()) + throw "No implementations"; auto id = statistics.select(); Stopwatch watch; - if (id == 0) { - DefaultFunction::executeImpl(block, arguments, result, input_rows_count); + if (id == impls.size()) { + if constexpr (std::is_base_of_v) + DefaultFunction::executeImpl(block, arguments, result, input_rows_count); + else + DefaultFunction::execute(block, arguments, result, input_rows_count); } else { - impls[id - 1]->executeImpl(block, arguments, result, input_rows_count); + if constexpr (std::is_base_of_v) + impls[id]->executeImpl(block, arguments, result, input_rows_count); + else + impls[id]->execute(block, arguments, result, input_rows_count); } watch.stop(); // TODO(dakovalkov): Calculate something more informative. @@ -191,17 +226,8 @@ class FunctionPerformanceAdaptor : public DefaultFunction } } - // Register alternative implementation. - template - void registerImplementation(TargetArch arch, Params... params) { - if (arch == TargetArch::Default || IsArchSupported(arch)) { - impls.emplace_back(std::make_shared(params...)); - statistics.emplace_back(); - } - } - private: - std::vector impls; // Alternative implementations. + std::vector impls; // Alternative implementations. PerformanceStatistics statistics; PerformanceAdaptorOptions options; }; diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp index f7c5f953e092..b5c8b0995ac3 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/SIMDxorshift.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include extern "C" diff --git a/src/Functions/SIMDxorshift.h b/src/Functions/SIMDxorshift.h index 46732c4d876e..c8b741c06b1f 100644 --- a/src/Functions/SIMDxorshift.h +++ b/src/Functions/SIMDxorshift.h @@ -22,19 +22,25 @@ DECLARE_MULTITARGET_CODE( struct RandXorshiftImpl { static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch); } }; ) // DECLARE_MULTITARGET_CODE template -class FunctionRandomXorshift : public FunctionPerformanceAdaptor> +class FunctionRandomXorshift + : public FunctionPerformanceAdaptor> { public: - FunctionRandomXorshift() { + FunctionRandomXorshift() + : FunctionPerformanceAdaptor>( + PerformanceAdaptorOptions()) + { registerImplementation>(TargetArch::AVX2); } - static FunctionPtr create(const Context &) { + static FunctionPtr create(const Context &) + { return std::make_shared>(); } }; From f59b13a58d54ed5c447028dc4275cfed6ac38b88 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 08:59:08 +0200 Subject: [PATCH 0165/1102] Fix style issues --- src/Common/ErrorCodes.cpp | 1 + src/Functions/FunctionsRandom.h | 2 +- src/Functions/PerformanceAdaptors.h | 56 +++++++++++++++++++---------- src/Functions/SIMDxorshift.cpp | 35 ++++++++++-------- src/Functions/SIMDxorshift.h | 2 +- src/Functions/TargetSpecific.cpp | 42 ++++++++++++---------- src/Functions/TargetSpecific.h | 5 +-- 7 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/Common/ErrorCodes.cpp b/src/Common/ErrorCodes.cpp index cb4c591041c4..2681bd0773c8 100644 --- a/src/Common/ErrorCodes.cpp +++ b/src/Common/ErrorCodes.cpp @@ -498,6 +498,7 @@ namespace ErrorCodes extern const int ALTER_OF_COLUMN_IS_FORBIDDEN = 524; extern const int INCORRECT_DISK_INDEX = 525; extern const int UNKNOWN_VOLUME_TYPE = 526; + extern const int NO_SUITABLE_FUNCTION_IMPLEMENTATION = 527; extern const int KEEPER_EXCEPTION = 999; extern const int POCO_EXCEPTION = 1000; diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index ae54243164f9..6130ee1c2a56 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -113,4 +113,4 @@ class FunctionRandom : public FunctionPerformanceAdaptor> implementations; }; -// Redirects IExecutableFunctionImpl::execute() and IFunction:executeImpl() to executeFunctionImpl(); +/// Redirects IExecutableFunctionImpl::execute() and IFunction:executeImpl() to executeFunctionImpl(); template class FunctionExecutor; @@ -170,28 +178,28 @@ class FunctionPerformanceAdaptor : public FunctionExecutor : FunctionExecutor(params...) , options(std::move(options_)) { - if (isImplementationEnabled(DefaultFunction::getImplementationTag())) { + if (isImplementationEnabled(DefaultFunction::getImplementationTag())) statistics.emplace_back(); - } } - // Register alternative implementation. + /// Register alternative implementation. template void registerImplementation(TargetArch arch, Params... params) { - if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag())) { + if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag())) + { impls.emplace_back(std::make_shared(params...)); statistics.emplace_back(); } } bool isImplementationEnabled(const String & impl_tag) { - if (!options.implementations) { + if (!options.implementations) return true; - } - for (const auto & tag : *options.implementations) { - if (tag == impl_tag) { + + for (const auto & tag : *options.implementations) + { + if (tag == impl_tag) return true; - } } return false; } @@ -201,27 +209,37 @@ class FunctionPerformanceAdaptor : public FunctionExecutor size_t result, size_t input_rows_count) override { if (statistics.empty()) - throw "No implementations"; + throw Exception("All available implementations are disabled by user config", + ErrorCodes::NO_SUITABLE_FUNCTION_IMPLEMENTATION); + auto id = statistics.select(); Stopwatch watch; - if (id == impls.size()) { + + if (id == impls.size()) + { if constexpr (std::is_base_of_v) DefaultFunction::executeImpl(block, arguments, result, input_rows_count); else DefaultFunction::execute(block, arguments, result, input_rows_count); - } else { + } + else + { if constexpr (std::is_base_of_v) impls[id]->executeImpl(block, arguments, result, input_rows_count); else impls[id]->execute(block, arguments, result, input_rows_count); } watch.stop(); + // TODO(dakovalkov): Calculate something more informative. size_t rows_summary = 0; - for (auto i : arguments) { + for (auto i : arguments) + { rows_summary += block.getByPosition(i).column->size(); } - if (rows_summary >= 1000) { + + if (rows_summary >= 1000) + { statistics.data[id].update(watch.elapsedSeconds(), rows_summary); } } @@ -232,4 +250,4 @@ class FunctionPerformanceAdaptor : public FunctionExecutor PerformanceAdaptorOptions options; }; -} // namespace DB +} diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/SIMDxorshift.cpp index b5c8b0995ac3..a8410ed957a1 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/SIMDxorshift.cpp @@ -28,15 +28,16 @@ void RandXorshiftImpl::execute(char * output, size_t size) &mykey); constexpr int bytes_per_write = 8; - constexpr intptr_t mask = bytes_per_write - 1; - + constexpr intptr_t mask = bytes_per_write - 1; + // Process head to make output aligned. unalignedStore(output, xorshift128plus(&mykey)); output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); - while (end - output > 0) { + while (end - output > 0) + { *reinterpret_cast(output) = xorshift128plus(&mykey); - output += bytes_per_write; + output += bytes_per_write; } } @@ -46,6 +47,9 @@ DECLARE_AVX2_SPECIFIC_CODE( void RandXorshiftImpl::execute(char * output, size_t size) { + if (size == 0) + return; + char * end = output + size; avx_xorshift128plus_key_t mykey; @@ -53,31 +57,34 @@ void RandXorshiftImpl::execute(char * output, size_t size) 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), &mykey); - constexpr int safe_overwrite = 15; // How many bytes we can write behind the end. + constexpr int safe_overwrite = 15; /// How many bytes we can write behind the end. constexpr int bytes_per_write = 32; - constexpr intptr_t mask = bytes_per_write - 1; + constexpr intptr_t mask = bytes_per_write - 1; - if (size + safe_overwrite < bytes_per_write) { - // size <= 16. + if (size + safe_overwrite < bytes_per_write) + { + /// size <= 16. _mm_storeu_si128(reinterpret_cast<__m128i*>(output), _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); return; } - // Process head to make output aligned. + /// Process head to make output aligned. _mm256_storeu_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); - while ((end - output) + safe_overwrite >= bytes_per_write) { + while ((end - output) + safe_overwrite >= bytes_per_write) + { _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); output += bytes_per_write; } - // Process tail. (end - output) <= 16. - if ((end - output) > 0) { + /// Process tail. (end - output) <= 16. + if ((end - output) > 0) + { _mm_store_si128(reinterpret_cast<__m128i*>(output), _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); - } + } } ) // DECLARE_AVX2_SPECIFIC_CODE @@ -93,4 +100,4 @@ void registerFunctionRandXorshift(FunctionFactory & factory) factory.registerFunction(); } -} // namespace DB +} diff --git a/src/Functions/SIMDxorshift.h b/src/Functions/SIMDxorshift.h index c8b741c06b1f..c9e46cf71922 100644 --- a/src/Functions/SIMDxorshift.h +++ b/src/Functions/SIMDxorshift.h @@ -45,4 +45,4 @@ class FunctionRandomXorshift } }; -} // namespace DB +} diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index 19604a83ab7e..891d63d82582 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -11,25 +11,30 @@ namespace DB { __attribute__ ((target("xsave"))) -uint64_t xgetbv(uint32_t ecx) { +UInt64 xgetbv(UInt32 ecx) +{ return _xgetbv(ecx); } -int GetSupportedArches() { - unsigned int eax, ebx, ecx, edx; - if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { +UInt32 GetSupportedArches() +{ + UInt32 eax, ebx, ecx, edx; + if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) return 0; - } - int res = 0; + + UInt32 res = 0; if (ecx & bit_SSE4_2) - res |= static_cast(TargetArch::SSE4); - // (xgetbv(0) & 0x6) == 0x6 checks that XMM state and YMM state are enabled. - if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX) && (xgetbv(0) & 0x6) == 0x6) { - res |= static_cast(TargetArch::AVX); - if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) { - res |= static_cast(TargetArch::AVX2); - if (ebx & bit_AVX512F) { - res |= static_cast(TargetArch::AVX512F); + res |= static_cast(TargetArch::SSE4); + /// (xgetbv(0) & 0x6) == 0x6 checks that XMM state and YMM state are enabled. + if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX) && (xgetbv(0) & 0x6) == 0x6) + { + res |= static_cast(TargetArch::AVX); + if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) + { + res |= static_cast(TargetArch::AVX2); + if (ebx & bit_AVX512F) + { + res |= static_cast(TargetArch::AVX512F); } } } @@ -38,13 +43,14 @@ int GetSupportedArches() { bool IsArchSupported(TargetArch arch) { - static int arches = GetSupportedArches(); - return arch == TargetArch::Default || (arches & static_cast(arch)); + static UInt32 arches = GetSupportedArches(); + return arch == TargetArch::Default || (arches & static_cast(arch)); } String ToString(TargetArch arch) { - switch (arch) { + switch (arch) + { case TargetArch::Default: return "default"; case TargetArch::SSE4: return "sse4"; case TargetArch::AVX: return "avx"; @@ -55,4 +61,4 @@ String ToString(TargetArch arch) __builtin_unreachable(); } -} // namespace DB +} diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index 888d88d1d772..7af792ae3c7d 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -64,7 +64,8 @@ namespace DB { -enum class TargetArch : int { +enum class TargetArch : UInt32 +{ Default = 0, // Without any additional compiler options. SSE4 = (1 << 0), AVX = (1 << 1), @@ -172,4 +173,4 @@ DECLARE_AVX512F_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX512F; ) // DECLARE_AVX512F_SPECIFIC_CODE -} // namespace DB +} From 2609b1c370989238a0e619d7db246474e19d266f Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 09:01:46 +0200 Subject: [PATCH 0166/1102] Save test --- tests/performance/rand.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/performance/rand.xml b/tests/performance/rand.xml index 32ec38ddb4ef..a007eb501791 100644 --- a/tests/performance/rand.xml +++ b/tests/performance/rand.xml @@ -1,10 +1,10 @@ - 20000 + 10000 - 40000 + 20000 @@ -13,12 +13,13 @@ table - numbers(100000000) + numbers(10000000) - SELECT rand() FROM {table} - SELECT rand64() FROM {table} - SELECT randxorshift() FROM {table} + SELECT count() FROM (SELECT rand() FROM {table}) + SELECT count() FROM (SELECT randxorshift() FROM {table}) + SELECT count() FROM (SELECT rand64() FROM {table}) + SELECT count() FROM (SELECT randxorshift64() FROM {table}) From e1dc2330891a5fb1fa5946dae3841c7ad8714ec1 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 17:20:07 +0200 Subject: [PATCH 0167/1102] Fix clang build (probably) --- src/Functions/TargetSpecific.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index 7af792ae3c7d..a9ed8bfe71e4 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -79,16 +79,15 @@ bool IsArchSupported(TargetArch arch); String ToString(TargetArch arch); #if defined(__clang__) -# define BEGIN_AVX512F_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f\"))))") -# define BEGIN_AVX2_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2\"))))") -# define BEGIN_AVX_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx\"))))") -# define BEGIN_SSE4_SPECIFIC_CODE \ - _Pragma("clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx\"))))") -# define END_TARGET_SPECIFIC_CODE \ - _Pragma("clang attribute pop") +# define BEGIN_AVX512F_SPECIFIC_CODE _Pragma(\ + "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2,avx512f\"))),apply_to=function)") +# define BEGIN_AVX2_SPECIFIC_CODE _Pragma(\ + "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2\"))),apply_to=function)") +# define BEGIN_AVX_SPECIFIC_CODE _Pragma(\ + "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx\"))),apply_to=function)") +# define BEGIN_SSE4_SPECIFIC_CODE _Pragma(\ + "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") +# define END_TARGET_SPECIFIC_CODE _Pragma("clang attribute pop") #elif defined(__GNUC__) # define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("GCC push_options") \ From a4ff8bb9331d289a98dac69a92f5f3f0fec73217 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 17:43:05 +0200 Subject: [PATCH 0168/1102] Get rid of handwritten cpu feature detection --- src/Functions/FunctionStartsEndsWith.h | 2 +- src/Functions/FunctionsRandom.h | 2 +- src/Functions/TargetSpecific.cpp | 46 +++++++------------------- src/Functions/TargetSpecific.h | 26 +++++++-------- 4 files changed, 27 insertions(+), 49 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 44850257f990..9435adbe858b 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -152,7 +152,7 @@ class FunctionStartsEndsWith PerformanceAdaptorOptions() ) { - registerImplementation> (TargetArch::SSE4); + registerImplementation> (TargetArch::SSE42); registerImplementation> (TargetArch::AVX); registerImplementation> (TargetArch::AVX2); registerImplementation>(TargetArch::AVX512F); diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 6130ee1c2a56..8fb1758a60cd 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -101,7 +101,7 @@ class FunctionRandom : public FunctionPerformanceAdaptor>( PerformanceAdaptorOptions()) { - registerImplementation>(TargetArch::SSE4); + registerImplementation>(TargetArch::SSE42); registerImplementation>(TargetArch::AVX); registerImplementation>(TargetArch::AVX2); registerImplementation>(TargetArch::AVX512F); diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index 891d63d82582..4168fb60a594 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -1,44 +1,22 @@ #include -#if defined(__GNUC__) -# include -# include -#else -# error "Only CLANG and GCC compilers are supported for dynamic dispatch" -#endif +#include namespace DB { -__attribute__ ((target("xsave"))) -UInt64 xgetbv(UInt32 ecx) -{ - return _xgetbv(ecx); -} - UInt32 GetSupportedArches() { - UInt32 eax, ebx, ecx, edx; - if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx)) - return 0; - - UInt32 res = 0; - if (ecx & bit_SSE4_2) - res |= static_cast(TargetArch::SSE4); - /// (xgetbv(0) & 0x6) == 0x6 checks that XMM state and YMM state are enabled. - if ((ecx & bit_OSXSAVE) && (ecx & bit_AVX) && (xgetbv(0) & 0x6) == 0x6) - { - res |= static_cast(TargetArch::AVX); - if (__get_cpuid(7, &eax, &ebx, &ecx, &edx) && (ebx & bit_AVX2)) - { - res |= static_cast(TargetArch::AVX2); - if (ebx & bit_AVX512F) - { - res |= static_cast(TargetArch::AVX512F); - } - } - } - return res; + UInt32 result = 0; + if (Cpu::haveSSE42()) + result |= static_cast(TargetArch::SSE42); + if (Cpu::haveAVX()) + result |= static_cast(TargetArch::AVX); + if (Cpu::haveAVX2()) + result |= static_cast(TargetArch::AVX2); + if (Cpu::haveAVX512F()) + result |= static_cast(TargetArch::AVX512F); + return result; } bool IsArchSupported(TargetArch arch) @@ -52,7 +30,7 @@ String ToString(TargetArch arch) switch (arch) { case TargetArch::Default: return "default"; - case TargetArch::SSE4: return "sse4"; + case TargetArch::SSE42: return "sse42"; case TargetArch::AVX: return "avx"; case TargetArch::AVX2: return "avx2"; case TargetArch::AVX512F: return "avx512f"; diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index a9ed8bfe71e4..7a946effb538 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -46,7 +46,7 @@ /// DECLARE_MULTITARGET_CODE( /// int funcImpl(int size, ...) { /// int iteration_size = 1; -/// if constexpr (BuildArch == TargetArch::SSE4) +/// if constexpr (BuildArch == TargetArch::SSE42) /// iteration_size = 2 /// else if constexpr (BuildArch == TargetArch::AVX || BuildArch == TargetArch::AVX2) /// iteration_size = 4; @@ -66,8 +66,8 @@ namespace DB enum class TargetArch : UInt32 { - Default = 0, // Without any additional compiler options. - SSE4 = (1 << 0), + Default = 0, /// Without any additional compiler options. + SSE42 = (1 << 0), /// SSE4.2 AVX = (1 << 1), AVX2 = (1 << 2), AVX512F = (1 << 3), @@ -85,7 +85,7 @@ String ToString(TargetArch arch); "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2\"))),apply_to=function)") # define BEGIN_AVX_SPECIFIC_CODE _Pragma(\ "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx\"))),apply_to=function)") -# define BEGIN_SSE4_SPECIFIC_CODE _Pragma(\ +# define BEGIN_SSE42_SPECIFIC_CODE _Pragma(\ "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") # define END_TARGET_SPECIFIC_CODE _Pragma("clang attribute pop") #elif defined(__GNUC__) @@ -98,7 +98,7 @@ String ToString(TargetArch arch); # define BEGIN_AVX_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native\")") -# define BEGIN_SSE4_SPECIFIC_CODE \ +# define BEGIN_SSE42_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") # define END_TARGET_SPECIFIC_CODE \ @@ -113,10 +113,10 @@ namespace TargetSpecific::Default { \ __VA_ARGS__ \ } -#define DECLARE_SSE4_SPECIFIC_CODE(...) \ -BEGIN_SSE4_SPECIFIC_CODE \ -namespace TargetSpecific::SSE4 { \ - using namespace DB::TargetSpecific::SSE4; \ +#define DECLARE_SSE42_SPECIFIC_CODE(...) \ +BEGIN_SSE42_SPECIFIC_CODE \ +namespace TargetSpecific::SSE42 { \ + using namespace DB::TargetSpecific::SSE42; \ __VA_ARGS__ \ } \ END_TARGET_SPECIFIC_CODE @@ -147,7 +147,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_MULTITARGET_CODE(...) \ DECLARE_DEFAULT_CODE (__VA_ARGS__) \ -DECLARE_SSE4_SPECIFIC_CODE (__VA_ARGS__) \ +DECLARE_SSE42_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX2_SPECIFIC_CODE (__VA_ARGS__) \ DECLARE_AVX512F_SPECIFIC_CODE(__VA_ARGS__) @@ -156,9 +156,9 @@ DECLARE_DEFAULT_CODE( constexpr auto BuildArch = TargetArch::Default; ) // DECLARE_DEFAULT_CODE -DECLARE_SSE4_SPECIFIC_CODE( - constexpr auto BuildArch = TargetArch::SSE4; -) // DECLARE_SSE4_SPECIFIC_CODE +DECLARE_SSE42_SPECIFIC_CODE( + constexpr auto BuildArch = TargetArch::SSE42; +) // DECLARE_SSE42_SPECIFIC_CODE DECLARE_AVX_SPECIFIC_CODE( constexpr auto BuildArch = TargetArch::AVX; From ad0ddc936a75ec1e9bd26f98fff0258827d2bedf Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 19:21:23 +0200 Subject: [PATCH 0169/1102] Cosmetics, fix style issues --- src/Functions/PerformanceAdaptors.h | 16 +++++++++------- .../{SIMDxorshift.cpp => RandXorshift.cpp} | 2 +- src/Functions/{SIMDxorshift.h => RandXorshift.h} | 0 src/Functions/TargetSpecific.h | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) rename src/Functions/{SIMDxorshift.cpp => RandXorshift.cpp} (98%) rename src/Functions/{SIMDxorshift.h => RandXorshift.h} (100%) diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index f7b9c12c7cbc..ea3f2ae0b477 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -133,7 +133,7 @@ class FunctionExecutor - FunctionExecutor(Args ...args) : DefaultFunction(args...) {} + FunctionExecutor(Args&&... args) : DefaultFunction(std::forward(args)...) {} virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; @@ -151,7 +151,7 @@ class FunctionExecutor - FunctionExecutor(Args ...args) : DefaultFunction(args...) {} + FunctionExecutor(Args&&... args) : DefaultFunction(std::forward(args)...) {} virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; @@ -174,8 +174,8 @@ class FunctionPerformanceAdaptor : public FunctionExecutor using BaseFunctionPtr = FunctionExecutor::BaseFunctionPtr; template - FunctionPerformanceAdaptor(PerformanceAdaptorOptions options_, Params ...params) - : FunctionExecutor(params...) + FunctionPerformanceAdaptor(PerformanceAdaptorOptions options_, Params&&... params) + : FunctionExecutor(std::forward(params)...) , options(std::move(options_)) { if (isImplementationEnabled(DefaultFunction::getImplementationTag())) @@ -184,15 +184,17 @@ class FunctionPerformanceAdaptor : public FunctionExecutor /// Register alternative implementation. template - void registerImplementation(TargetArch arch, Params... params) { + void registerImplementation(TargetArch arch, Params&&... params) + { if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag())) { - impls.emplace_back(std::make_shared(params...)); + impls.emplace_back(std::make_shared(std::forward(params)...)); statistics.emplace_back(); } } - bool isImplementationEnabled(const String & impl_tag) { + bool isImplementationEnabled(const String & impl_tag) + { if (!options.implementations) return true; diff --git a/src/Functions/SIMDxorshift.cpp b/src/Functions/RandXorshift.cpp similarity index 98% rename from src/Functions/SIMDxorshift.cpp rename to src/Functions/RandXorshift.cpp index a8410ed957a1..652bb90f5595 100644 --- a/src/Functions/SIMDxorshift.cpp +++ b/src/Functions/RandXorshift.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include extern "C" { diff --git a/src/Functions/SIMDxorshift.h b/src/Functions/RandXorshift.h similarity index 100% rename from src/Functions/SIMDxorshift.h rename to src/Functions/RandXorshift.h diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index 7a946effb538..f5bd0267c52a 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -4,7 +4,7 @@ /// This file contains macros and helpers for writing platform-dependent code. /// -/// Macroses DECLARE__SPECIFIC_CODE will wrap code inside them into the +/// Macros DECLARE__SPECIFIC_CODE will wrap code inside them into the /// namespace TargetSpecific:: and enable Arch-specific compile options. /// Thus, it's allowed to call functions inside these namespaces only after /// checking platform in runtime (see IsArchSupported() below). From 234a828dd0315800d96ac87568dea7353464c9ba Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sat, 16 May 2020 21:30:00 +0200 Subject: [PATCH 0170/1102] Add option to disable multitarget build --- src/Functions/CMakeLists.txt | 6 +++++ src/Functions/FunctionStartsEndsWith.h | 11 +++++---- src/Functions/FunctionsRandom.h | 11 +++++---- src/Functions/RandXorshift.h | 5 ++++- src/Functions/TargetSpecific.h | 31 ++++++++++++++++++-------- 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 451dfe97a03d..8c9cf159e304 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -83,6 +83,12 @@ if(USE_RAPIDJSON) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${RAPIDJSON_INCLUDE_DIR}) endif() +option(ENABLE_MULTITARGET_CODE "" ON) +if (ENABLE_MULTITARGET_CODE) + add_definitions(-DUSE_MULTITARGET_CODE=1) +else() + add_definitions(-DUSE_MULTITARGET_CODE=0) +endif() target_link_libraries(clickhouse_functions PUBLIC "simdxorshift") message(STATUS "Using SIMDXORSHIFT ${SIMDXORSHIFT_LIBRARY}") diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 9435adbe858b..f433f9c46c23 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -152,10 +152,13 @@ class FunctionStartsEndsWith PerformanceAdaptorOptions() ) { - registerImplementation> (TargetArch::SSE42); - registerImplementation> (TargetArch::AVX); - registerImplementation> (TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512F); + if constexpr (UseMultitargetCode) + { + registerImplementation> (TargetArch::SSE42); + registerImplementation> (TargetArch::AVX); + registerImplementation> (TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512F); + } } static FunctionPtr create(const Context & context) diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 8fb1758a60cd..a957c0b5e97b 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -101,10 +101,13 @@ class FunctionRandom : public FunctionPerformanceAdaptor>( PerformanceAdaptorOptions()) { - registerImplementation>(TargetArch::SSE42); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512F); + if constexpr (UseMultitargetCode) + { + registerImplementation>(TargetArch::SSE42); + registerImplementation>(TargetArch::AVX); + registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX512F); + } } static FunctionPtr create(const Context &) diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index c9e46cf71922..5f3f3c9c04f7 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -36,7 +36,10 @@ class FunctionRandomXorshift : FunctionPerformanceAdaptor>( PerformanceAdaptorOptions()) { - registerImplementation>(TargetArch::AVX2); + if constexpr (UseMultitargetCode) + { + registerImplementation>(TargetArch::AVX2); + } } static FunctionPtr create(const Context &) diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index f5bd0267c52a..0c9eb7357d16 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -78,6 +78,10 @@ bool IsArchSupported(TargetArch arch); String ToString(TargetArch arch); +#if USE_MULTITARGET_CODE && defined(__GNUC__) && defined(__x86_64__) + +constexpr bool UseMultitargetCode = true; + #if defined(__clang__) # define BEGIN_AVX512F_SPECIFIC_CODE _Pragma(\ "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2,avx512f\"))),apply_to=function)") @@ -88,7 +92,7 @@ String ToString(TargetArch arch); # define BEGIN_SSE42_SPECIFIC_CODE _Pragma(\ "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") # define END_TARGET_SPECIFIC_CODE _Pragma("clang attribute pop") -#elif defined(__GNUC__) +#else # define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("GCC push_options") \ _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f,tune=native\")") @@ -103,16 +107,8 @@ String ToString(TargetArch arch); _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") # define END_TARGET_SPECIFIC_CODE \ _Pragma("GCC pop_options") -#else -# error "Only CLANG and GCC compilers are supported for vectorized code generation" #endif -#define DECLARE_DEFAULT_CODE(...) \ -namespace TargetSpecific::Default { \ - using namespace DB::TargetSpecific::Default; \ - __VA_ARGS__ \ -} - #define DECLARE_SSE42_SPECIFIC_CODE(...) \ BEGIN_SSE42_SPECIFIC_CODE \ namespace TargetSpecific::SSE42 { \ @@ -145,6 +141,23 @@ namespace TargetSpecific::AVX512F { \ } \ END_TARGET_SPECIFIC_CODE +#else + +constexpr bool UseMultitargetCode = false; + +#define DECLARE_SSE42_SPECIFIC_CODE(...) +#define DECLARE_AVX_SPECIFIC_CODE(...) +#define DECLARE_AVX2_SPECIFIC_CODE(...) +#define DECLARE_AVX512F_SPECIFIC_CODE(...) + +#endif + +#define DECLARE_DEFAULT_CODE(...) \ +namespace TargetSpecific::Default { \ + using namespace DB::TargetSpecific::Default; \ + __VA_ARGS__ \ +} + #define DECLARE_MULTITARGET_CODE(...) \ DECLARE_DEFAULT_CODE (__VA_ARGS__) \ DECLARE_SSE42_SPECIFIC_CODE (__VA_ARGS__) \ From 9387981abeb28cdb89faeb55d9a8b1007e637810 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sun, 17 May 2020 17:13:01 +0200 Subject: [PATCH 0171/1102] Add afwul option for choosing implementation --- src/Core/Settings.h | 1 + src/Core/SettingsCollection.h | 2 +- src/Functions/FunctionStartsEndsWith.h | 6 +-- src/Functions/FunctionsRandom.h | 9 ++-- src/Functions/PerformanceAdaptors.h | 27 +++++++----- src/Functions/RandXorshift.cpp | 61 ++++++++++++++++++++++++++ src/Functions/RandXorshift.h | 16 ++++--- src/Functions/TargetSpecific.cpp | 8 ++-- 8 files changed, 99 insertions(+), 31 deletions(-) diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 142e0872d721..68bebd0b6b01 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -424,6 +424,7 @@ struct Settings : public SettingsCollection M(SettingBool, allow_nondeterministic_mutations, false, "Allow non-deterministic functions in ALTER UPDATE/ALTER DELETE statements", 0) \ M(SettingSeconds, lock_acquire_timeout, DBMS_DEFAULT_LOCK_ACQUIRE_TIMEOUT_SEC, "How long locking request should wait before failing", 0) \ M(SettingBool, materialize_ttl_after_modify, true, "Apply TTL for old data, after ALTER MODIFY TTL query", 0) \ + M(SettingString, function_implementation, "", "Choose implementation. If empty enable all of them.", 0) \ \ /** Obsolete settings that do nothing but left for compatibility reasons. Remove each one after half a year of obsolescence. */ \ \ diff --git a/src/Core/SettingsCollection.h b/src/Core/SettingsCollection.h index 1fe5762de4c9..c34bd1a2990e 100644 --- a/src/Core/SettingsCollection.h +++ b/src/Core/SettingsCollection.h @@ -327,7 +327,7 @@ using SettingLogQueriesType = SettingEnum; enum class SettingsBinaryFormat { - OLD, /// Part of the settings are serialized as strings, and other part as varints. This is the old behaviour. + OLD, /// Part of the settings are serialized as strings, and other part as variants. This is the old behaviour. STRINGS, /// All settings are serialized as strings. Before each value the flag `is_ignorable` is serialized. DEFAULT = STRINGS, }; diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index f433f9c46c23..71b02e3b2644 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -147,10 +147,8 @@ class FunctionStartsEndsWith : public FunctionPerformanceAdaptor> { public: - FunctionStartsEndsWith(const Context &) - : FunctionPerformanceAdaptor>( - PerformanceAdaptorOptions() - ) + FunctionStartsEndsWith(const Context & context_) + : FunctionPerformanceAdaptor>(context_) { if constexpr (UseMultitargetCode) { diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index a957c0b5e97b..a716826d4e1b 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -97,9 +97,8 @@ template class FunctionRandom : public FunctionPerformanceAdaptor> { public: - FunctionRandom() - : FunctionPerformanceAdaptor>( - PerformanceAdaptorOptions()) + FunctionRandom(const Context & context_) + : FunctionPerformanceAdaptor>(context_) { if constexpr (UseMultitargetCode) { @@ -110,9 +109,9 @@ class FunctionRandom : public FunctionPerformanceAdaptor>(); + return std::make_shared>(context); } }; diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index ea3f2ae0b477..eaaa594a4bf1 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -4,6 +4,7 @@ #include #include +#include #include @@ -174,9 +175,9 @@ class FunctionPerformanceAdaptor : public FunctionExecutor using BaseFunctionPtr = FunctionExecutor::BaseFunctionPtr; template - FunctionPerformanceAdaptor(PerformanceAdaptorOptions options_, Params&&... params) + FunctionPerformanceAdaptor(const Context & context_, Params&&... params) : FunctionExecutor(std::forward(params)...) - , options(std::move(options_)) + , context(context_) { if (isImplementationEnabled(DefaultFunction::getImplementationTag())) statistics.emplace_back(); @@ -195,15 +196,17 @@ class FunctionPerformanceAdaptor : public FunctionExecutor bool isImplementationEnabled(const String & impl_tag) { - if (!options.implementations) - return true; - - for (const auto & tag : *options.implementations) - { - if (tag == impl_tag) - return true; - } - return false; + const String & tag = context.getSettingsRef().function_implementation.value; + return tag.empty() || tag == impl_tag; + // if (!options.implementations) + // return true; + + // for (const auto & tag : *options.implementations) + // { + // if (tag == impl_tag) + // return true; + // } + // return false; } protected: @@ -249,7 +252,7 @@ class FunctionPerformanceAdaptor : public FunctionExecutor private: std::vector impls; // Alternative implementations. PerformanceStatistics statistics; - PerformanceAdaptorOptions options; + const Context & context; }; } diff --git a/src/Functions/RandXorshift.cpp b/src/Functions/RandXorshift.cpp index 652bb90f5595..9f1dded700ca 100644 --- a/src/Functions/RandXorshift.cpp +++ b/src/Functions/RandXorshift.cpp @@ -19,6 +19,9 @@ DECLARE_DEFAULT_CODE( void RandXorshiftImpl::execute(char * output, size_t size) { + if (size == 0) + return; + char * end = output + size; xorshift128plus_key_s mykey; @@ -89,6 +92,64 @@ void RandXorshiftImpl::execute(char * output, size_t size) ) // DECLARE_AVX2_SPECIFIC_CODE +DECLARE_AVX2_SPECIFIC_CODE( + +void RandXorshiftImpl2::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + avx_xorshift128plus_key_t mykey; + avx_xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), + 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), + &mykey); + + avx_xorshift128plus_key_t mykey2; + avx_xorshift128plus_init(0xdfe532a6b5a5eb2cULL ^ reinterpret_cast(output), + 0x21cdf6cd1e22bf9cULL ^ reinterpret_cast(output), + &mykey2); + + constexpr int safe_overwrite = 15; /// How many bytes we can write behind the end. + constexpr int bytes_per_write = 32; + constexpr intptr_t mask = bytes_per_write - 1; + + if (size + safe_overwrite < bytes_per_write) + { + /// size <= 16. + _mm_storeu_si128(reinterpret_cast<__m128i*>(output), + _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); + return; + } + + /// Process head to make output aligned. + _mm256_storeu_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); + output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); + + while ((end - output) + safe_overwrite >= bytes_per_write * 2) + { + _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); + _mm256_store_si256(reinterpret_cast<__m256i*>(output + bytes_per_write), avx_xorshift128plus(&mykey2)); + output += bytes_per_write * 2; + } + + if ((end - output) + safe_overwrite >= bytes_per_write) + { + _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); + output += bytes_per_write; + } + + /// Process tail. (end - output) <= 16. + if ((end - output) > 0) + { + _mm_store_si128(reinterpret_cast<__m128i*>(output), + _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); + } +} + +) // DECLARE_AVX2_SPECIFIC_CODE + struct NameRandXorshift { static constexpr auto name = "randxorshift"; }; using FunctionRandXorshift = FunctionRandomXorshift; struct NameRandXorshift64 { static constexpr auto name = "randxorshift64"; }; diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 5f3f3c9c04f7..2dd7723ff0a6 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -25,6 +25,12 @@ struct RandXorshiftImpl static String getImplementationTag() { return ToString(BuildArch); } }; +struct RandXorshiftImpl2 +{ + static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } +}; + ) // DECLARE_MULTITARGET_CODE template @@ -32,19 +38,19 @@ class FunctionRandomXorshift : public FunctionPerformanceAdaptor> { public: - FunctionRandomXorshift() - : FunctionPerformanceAdaptor>( - PerformanceAdaptorOptions()) + FunctionRandomXorshift(const Context & context_) + : FunctionPerformanceAdaptor>(context_) { if constexpr (UseMultitargetCode) { registerImplementation>(TargetArch::AVX2); + registerImplementation>(TargetArch::AVX2); } } - static FunctionPtr create(const Context &) + static FunctionPtr create(const Context & context) { - return std::make_shared>(); + return std::make_shared>(context); } }; diff --git a/src/Functions/TargetSpecific.cpp b/src/Functions/TargetSpecific.cpp index 4168fb60a594..65f8641ee8eb 100644 --- a/src/Functions/TargetSpecific.cpp +++ b/src/Functions/TargetSpecific.cpp @@ -8,13 +8,13 @@ namespace DB UInt32 GetSupportedArches() { UInt32 result = 0; - if (Cpu::haveSSE42()) + if (Cpu::CpuFlagsCache::have_SSE42) result |= static_cast(TargetArch::SSE42); - if (Cpu::haveAVX()) + if (Cpu::CpuFlagsCache::have_AVX) result |= static_cast(TargetArch::AVX); - if (Cpu::haveAVX2()) + if (Cpu::CpuFlagsCache::have_AVX2) result |= static_cast(TargetArch::AVX2); - if (Cpu::haveAVX512F()) + if (Cpu::CpuFlagsCache::have_AVX512F) result |= static_cast(TargetArch::AVX512F); return result; } From 35e4f43ac749a479dee960452e866cb575b588a0 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 18 May 2020 09:24:22 +0200 Subject: [PATCH 0172/1102] Add alternative implenetation for rand --- src/Functions/FunctionsRandom.cpp | 46 +++++++++++++++++++++++++++++++ src/Functions/FunctionsRandom.h | 12 ++++++-- src/Functions/RandXorshift.h | 5 ---- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 11861d2d12cc..9c6d90e9e73d 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -66,6 +66,52 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } +void RandImpl2::execute(char * output, size_t size) +{ + if (size == 0) + return; + + LinearCongruentialGenerator generator0; + LinearCongruentialGenerator generator1; + LinearCongruentialGenerator generator2; + LinearCongruentialGenerator generator3; + LinearCongruentialGenerator generator4; + LinearCongruentialGenerator generator5; + LinearCongruentialGenerator generator6; + LinearCongruentialGenerator generator7; + + seed(generator0, 0xfb4121280b2ab902ULL + reinterpret_cast(output)); + seed(generator1, 0x0121cf76df39c673ULL + reinterpret_cast(output)); + seed(generator2, 0x17ae86e3a19a602fULL + reinterpret_cast(output)); + seed(generator3, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)); + seed(generator4, 0xfb4122280b2ab102ULL + reinterpret_cast(output)); + seed(generator5, 0x0121c276df39c173ULL + reinterpret_cast(output)); + seed(generator6, 0x17ae82e3a19a612fULL + reinterpret_cast(output)); + seed(generator7, 0x8b6e12da7e06d122ULL + reinterpret_cast(output)); + + const char * end = output + size; + + for (; (end - output + 15) <= 32; output += 32) + { + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); + unalignedStore(output + 12, generator3.next()); + unalignedStore(output + 16, generator4.next()); + unalignedStore(output + 20, generator5.next()); + unalignedStore(output + 24, generator6.next()); + unalignedStore(output + 28, generator7.next()); + } + + while (end - output > 0) { + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); + unalignedStore(output + 12, generator3.next()); + output += 16; + } +} + ) //DECLARE_MULTITARGET_CODE } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index a716826d4e1b..443f44a4e44a 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -7,8 +7,7 @@ #include #include -// #include "TargetSpecific.h" -// #include "PerformanceAdaptors.h" + namespace DB { @@ -45,6 +44,12 @@ struct RandImpl static String getImplementationTag() { return ToString(BuildArch); } }; +struct RandImpl2 +{ + static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } +}; + ) // DECLARE_MULTITARGET_CODE template @@ -106,6 +111,9 @@ class FunctionRandom : public FunctionPerformanceAdaptor>(TargetArch::AVX); registerImplementation>(TargetArch::AVX2); registerImplementation>(TargetArch::AVX512F); + + registerImplementation>(TargetArch::Default); + registerImplementation>(TargetArch::AVX2); } } diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 2dd7723ff0a6..b74fdeecbef3 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -12,11 +12,6 @@ namespace DB { -namespace ErrorCodes -{ - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; -} - DECLARE_MULTITARGET_CODE( struct RandXorshiftImpl From b2b3ba59420dce136943e4f0275c6f125540676d Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 18 May 2020 10:48:35 +0200 Subject: [PATCH 0173/1102] Finaly fix clang build --- src/Functions/FunctionStartsEndsWith.h | 8 ++++---- src/Functions/FunctionsRandom.h | 12 ++++++------ src/Functions/PerformanceAdaptors.h | 2 +- src/Functions/RandXorshift.h | 4 ++-- src/Functions/TargetSpecific.h | 4 ++++ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 71b02e3b2644..b0465ecefa6a 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -152,10 +152,10 @@ class FunctionStartsEndsWith { if constexpr (UseMultitargetCode) { - registerImplementation> (TargetArch::SSE42); - registerImplementation> (TargetArch::AVX); - registerImplementation> (TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512F); + this->template registerImplementation> (TargetArch::SSE42); + this->template registerImplementation> (TargetArch::AVX); + this->template registerImplementation> (TargetArch::AVX2); + this->template registerImplementation>(TargetArch::AVX512F); } } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 443f44a4e44a..eeba5546fc97 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -107,13 +107,13 @@ class FunctionRandom : public FunctionPerformanceAdaptor>(TargetArch::SSE42); - registerImplementation>(TargetArch::AVX); - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX512F); + this->template registerImplementation>(TargetArch::SSE42); + this->template registerImplementation>(TargetArch::AVX); + this->template registerImplementation>(TargetArch::AVX2); + this->template registerImplementation>(TargetArch::AVX512F); - registerImplementation>(TargetArch::Default); - registerImplementation>(TargetArch::AVX2); + this->template registerImplementation>(TargetArch::Default); + this->template registerImplementation>(TargetArch::AVX2); } } diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index eaaa594a4bf1..0b5e3e101045 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -172,7 +172,7 @@ template class FunctionPerformanceAdaptor : public FunctionExecutor { public: - using BaseFunctionPtr = FunctionExecutor::BaseFunctionPtr; + using BaseFunctionPtr = typename FunctionExecutor::BaseFunctionPtr; template FunctionPerformanceAdaptor(const Context & context_, Params&&... params) diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index b74fdeecbef3..8713d85fdbd5 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -38,8 +38,8 @@ class FunctionRandomXorshift { if constexpr (UseMultitargetCode) { - registerImplementation>(TargetArch::AVX2); - registerImplementation>(TargetArch::AVX2); + this->template registerImplementation>(TargetArch::AVX2); + this->template registerImplementation>(TargetArch::AVX2); } } diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index 0c9eb7357d16..e69bd22f2711 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -112,6 +112,7 @@ constexpr bool UseMultitargetCode = true; #define DECLARE_SSE42_SPECIFIC_CODE(...) \ BEGIN_SSE42_SPECIFIC_CODE \ namespace TargetSpecific::SSE42 { \ + void __dummy_function_clang(); \ using namespace DB::TargetSpecific::SSE42; \ __VA_ARGS__ \ } \ @@ -120,6 +121,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX_SPECIFIC_CODE(...) \ BEGIN_AVX_SPECIFIC_CODE \ namespace TargetSpecific::AVX { \ + void __dummy_function_clang(); \ using namespace DB::TargetSpecific::AVX; \ __VA_ARGS__ \ } \ @@ -128,6 +130,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX2_SPECIFIC_CODE(...) \ BEGIN_AVX2_SPECIFIC_CODE \ namespace TargetSpecific::AVX2 { \ + void __dummy_function_clang(); \ using namespace DB::TargetSpecific::AVX2; \ __VA_ARGS__ \ } \ @@ -136,6 +139,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX512F_SPECIFIC_CODE(...) \ BEGIN_AVX512F_SPECIFIC_CODE \ namespace TargetSpecific::AVX512F { \ + void __dummy_function_clang(); \ using namespace DB::TargetSpecific::AVX512F; \ __VA_ARGS__ \ } \ From bd847514baa5194e65c406a93179c2d6d05eae84 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 18 May 2020 13:44:56 +0200 Subject: [PATCH 0174/1102] better random --- src/Functions/FunctionsRandom.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 9c6d90e9e73d..1de5fb50b8a6 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -103,7 +103,7 @@ void RandImpl2::execute(char * output, size_t size) unalignedStore(output + 28, generator7.next()); } - while (end - output > 0) { + if (end - output > 0) { unalignedStore(output, generator0.next()); unalignedStore(output + 4, generator1.next()); unalignedStore(output + 8, generator2.next()); From ea1285328be4579b738c53d97ad8e34b6cf5f3e6 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 18 May 2020 19:07:36 +0200 Subject: [PATCH 0175/1102] Fix gcc build, fix PVS error --- src/Functions/FunctionsRandom.cpp | 3 +- src/Functions/TargetSpecific.h | 157 ++++++++++++++++-------------- 2 files changed, 86 insertions(+), 74 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 1de5fb50b8a6..fd493d5605bb 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -103,7 +103,8 @@ void RandImpl2::execute(char * output, size_t size) unalignedStore(output + 28, generator7.next()); } - if (end - output > 0) { + if (end - output > 0) + { unalignedStore(output, generator0.next()); unalignedStore(output + 4, generator1.next()); unalignedStore(output + 8, generator2.next()); diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index e69bd22f2711..11dae939bbd9 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -2,72 +2,73 @@ #include -/// This file contains macros and helpers for writing platform-dependent code. -/// -/// Macros DECLARE__SPECIFIC_CODE will wrap code inside them into the -/// namespace TargetSpecific:: and enable Arch-specific compile options. -/// Thus, it's allowed to call functions inside these namespaces only after -/// checking platform in runtime (see IsArchSupported() below). -/// -/// For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code -/// into the namespace TargetSpecific::Default but dosn't specify any additional -/// copile options. -/// -/// Example: -/// -/// DECLARE_DEFAULT_CODE ( -/// int funcImpl() { -/// return 1; -/// } -/// ) // DECLARE_DEFAULT_CODE -/// -/// DECLARE_AVX2_SPECIFIC_CODE ( -/// int funcImpl() { -/// return 2; -/// } -/// ) // DECLARE_DEFAULT_CODE -/// -/// int func() { -/// if (IsArchSupported(TargetArch::AVX2)) -/// return TargetSpecifc::AVX2::funcImpl(); -/// return TargetSpecifc::Default::funcImpl(); -/// } -/// -/// Sometimes code may benefit from compiling with different options. -/// For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several -/// copies of the code and compile it with different options. These copies are -/// available via TargetSpecifc namespaces described above. -/// -/// Inside every TargetSpecific namespace there is a constexpr variable BuildArch, -/// which indicates the target platform for current code. -/// -/// Example: -/// -/// DECLARE_MULTITARGET_CODE( -/// int funcImpl(int size, ...) { -/// int iteration_size = 1; -/// if constexpr (BuildArch == TargetArch::SSE42) -/// iteration_size = 2 -/// else if constexpr (BuildArch == TargetArch::AVX || BuildArch == TargetArch::AVX2) -/// iteration_size = 4; -/// else if constexpr (BuildArch == TargetArch::AVX512) -/// iteration_size = 8; -/// for (int i = 0; i < size; i += iteration_size) -/// ... -/// } -/// ) // DECLARE_MULTITARGET_CODE -/// -/// // All 5 versions of func are available here. Use runtime detection to choose one. -/// -/// If you want to write IFunction or IExecutableFuncionImpl with runtime dispatching, see PerformanceAdaptors.h. +/* This file contains macros and helpers for writing platform-dependent code. + * + * Macros DECLARE__SPECIFIC_CODE will wrap code inside them into the + * namespace TargetSpecific:: and enable Arch-specific compile options. + * Thus, it's allowed to call functions inside these namespaces only after + * checking platform in runtime (see IsArchSupported() below). + * + * For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code + * into the namespace TargetSpecific::Default but dosn't specify any additional + * copile options. + * + * Example: + * + * DECLARE_DEFAULT_CODE ( + * int funcImpl() { + * return 1; + * } + * ) // DECLARE_DEFAULT_CODE + * + * DECLARE_AVX2_SPECIFIC_CODE ( + * int funcImpl() { + * return 2; + * } + * ) // DECLARE_DEFAULT_CODE + * + * int func() { + * if (IsArchSupported(TargetArch::AVX2)) + * return TargetSpecifc::AVX2::funcImpl(); + * return TargetSpecifc::Default::funcImpl(); + * } + * + * Sometimes code may benefit from compiling with different options. + * For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several + * copies of the code and compile it with different options. These copies are + * available via TargetSpecifc namespaces described above. + * + * Inside every TargetSpecific namespace there is a constexpr variable BuildArch, + * which indicates the target platform for current code. + * + * Example: + * + * DECLARE_MULTITARGET_CODE( + * int funcImpl(int size, ...) { + * int iteration_size = 1; + * if constexpr (BuildArch == TargetArch::SSE42) + * iteration_size = 2 + * else if constexpr (BuildArch == TargetArch::AVX || BuildArch == TargetArch::AVX2) + * iteration_size = 4; + * else if constexpr (BuildArch == TargetArch::AVX512) + * iteration_size = 8; + * for (int i = 0; i < size; i += iteration_size) + * ... + * } + * ) // DECLARE_MULTITARGET_CODE + * + * // All 5 versions of func are available here. Use runtime detection to choose one. + * + * If you want to write IFunction or IExecutableFuncionImpl with runtime dispatching, see PerformanceAdaptors.h. + */ namespace DB { enum class TargetArch : UInt32 { - Default = 0, /// Without any additional compiler options. - SSE42 = (1 << 0), /// SSE4.2 + Default = 0, /// Without any additional compiler options. + SSE42 = (1 << 0), /// SSE4.2 AVX = (1 << 1), AVX2 = (1 << 2), AVX512F = (1 << 3), @@ -83,15 +84,21 @@ String ToString(TargetArch arch); constexpr bool UseMultitargetCode = true; #if defined(__clang__) -# define BEGIN_AVX512F_SPECIFIC_CODE _Pragma(\ - "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2,avx512f\"))),apply_to=function)") -# define BEGIN_AVX2_SPECIFIC_CODE _Pragma(\ - "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2\"))),apply_to=function)") -# define BEGIN_AVX_SPECIFIC_CODE _Pragma(\ - "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx\"))),apply_to=function)") -# define BEGIN_SSE42_SPECIFIC_CODE _Pragma(\ - "clang attribute push (__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") -# define END_TARGET_SPECIFIC_CODE _Pragma("clang attribute pop") +# define BEGIN_AVX512F_SPECIFIC_CODE \ + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2,avx512f\"))),apply_to=function)") +# define BEGIN_AVX2_SPECIFIC_CODE \ + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2\"))),apply_to=function)") +# define BEGIN_AVX_SPECIFIC_CODE \ + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx\"))),apply_to=function)") +# define BEGIN_SSE42_SPECIFIC_CODE \ + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") +# define END_TARGET_SPECIFIC_CODE \ + _Pragma("clang attribute pop") + +/* Clang shows warning when there aren't any objects to apply pragma. + * To prevent this warning we define this function inside every macros with pragmas. + */ +# define DUMMY_FUNCTION_DEFINITION void __dummy_function_definition(); #else # define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("GCC push_options") \ @@ -107,12 +114,16 @@ constexpr bool UseMultitargetCode = true; _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") # define END_TARGET_SPECIFIC_CODE \ _Pragma("GCC pop_options") + +/* GCC doesn't show such warning, we don't need to define anything. + */ +# define DUMMY_FUNCTION_DEFINITION #endif #define DECLARE_SSE42_SPECIFIC_CODE(...) \ BEGIN_SSE42_SPECIFIC_CODE \ namespace TargetSpecific::SSE42 { \ - void __dummy_function_clang(); \ + DUMMY_FUNCTION_DEFINITION \ using namespace DB::TargetSpecific::SSE42; \ __VA_ARGS__ \ } \ @@ -121,7 +132,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX_SPECIFIC_CODE(...) \ BEGIN_AVX_SPECIFIC_CODE \ namespace TargetSpecific::AVX { \ - void __dummy_function_clang(); \ + DUMMY_FUNCTION_DEFINITION \ using namespace DB::TargetSpecific::AVX; \ __VA_ARGS__ \ } \ @@ -130,7 +141,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX2_SPECIFIC_CODE(...) \ BEGIN_AVX2_SPECIFIC_CODE \ namespace TargetSpecific::AVX2 { \ - void __dummy_function_clang(); \ + DUMMY_FUNCTION_DEFINITION \ using namespace DB::TargetSpecific::AVX2; \ __VA_ARGS__ \ } \ @@ -139,7 +150,7 @@ END_TARGET_SPECIFIC_CODE #define DECLARE_AVX512F_SPECIFIC_CODE(...) \ BEGIN_AVX512F_SPECIFIC_CODE \ namespace TargetSpecific::AVX512F { \ - void __dummy_function_clang(); \ + DUMMY_FUNCTION_DEFINITION \ using namespace DB::TargetSpecific::AVX512F; \ __VA_ARGS__ \ } \ From c524642d245487315d8a9b37d7d3783c8b66139f Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 18 May 2020 22:07:24 +0200 Subject: [PATCH 0176/1102] Delete awful template PerformanceAdaptro and add simple ImplementationSelector instead --- src/Functions/FunctionStartsEndsWith.h | 29 ++-- src/Functions/FunctionsRandom.h | 36 +++-- src/Functions/PerformanceAdaptors.h | 179 ++++++++++--------------- src/Functions/RandXorshift.h | 23 +++- 4 files changed, 135 insertions(+), 132 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index b0465ecefa6a..76aa4530c990 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -143,26 +143,39 @@ class FunctionStartsEndsWith : public IFunction ) // DECLARE_MULTITARGET_CODE template -class FunctionStartsEndsWith - : public FunctionPerformanceAdaptor> +class FunctionStartsEndsWith : public TargetSpecific::Default::FunctionStartsEndsWith { public: - FunctionStartsEndsWith(const Context & context_) - : FunctionPerformanceAdaptor>(context_) + FunctionStartsEndsWith(const Context & context) : selector(context) { + selector.registerImplementation>(); + if constexpr (UseMultitargetCode) { - this->template registerImplementation> (TargetArch::SSE42); - this->template registerImplementation> (TargetArch::AVX); - this->template registerImplementation> (TargetArch::AVX2); - this->template registerImplementation>(TargetArch::AVX512F); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); } } + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + static FunctionPtr create(const Context & context) { return std::make_shared>(context); } + +private: + ImplementationSelector selector; }; } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index eeba5546fc97..995f8ffeb9cc 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -99,28 +99,44 @@ class FunctionRandomImpl : public IFunction }; template -class FunctionRandom : public FunctionPerformanceAdaptor> +class FunctionRandom : public FunctionRandomImpl { public: - FunctionRandom(const Context & context_) - : FunctionPerformanceAdaptor>(context_) + FunctionRandom(const Context & context) : selector(context) { + selector.registerImplementation>(); + selector.registerImplementation>(); + if constexpr (UseMultitargetCode) { - this->template registerImplementation>(TargetArch::SSE42); - this->template registerImplementation>(TargetArch::AVX); - this->template registerImplementation>(TargetArch::AVX2); - this->template registerImplementation>(TargetArch::AVX512F); - - this->template registerImplementation>(TargetArch::Default); - this->template registerImplementation>(TargetArch::AVX2); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + + selector.registerImplementation>(); } } + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + static FunctionPtr create(const Context & context) { return std::make_shared>(context); } + +private: + ImplementationSelector selector; }; } diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 0b5e3e101045..b86730952fb0 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -117,123 +117,67 @@ struct PerformanceStatistics PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {} }; -struct PerformanceAdaptorOptions -{ - std::optional> implementations; -}; - -/// Redirects IExecutableFunctionImpl::execute() and IFunction:executeImpl() to executeFunctionImpl(); -template -class FunctionExecutor; - -template -class FunctionExecutor>> - : public DefaultFunction -{ -public: - using BaseFunctionPtr = ExecutableFunctionImplPtr; - - template - FunctionExecutor(Args&&... args) : DefaultFunction(std::forward(args)...) {} - - virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; - - virtual void execute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - executeFunctionImpl(block, arguments, result, input_rows_count); - } -}; - -template -class FunctionExecutor>> - : public DefaultFunction -{ -public: - using BaseFunctionPtr = FunctionPtr; - - template - FunctionExecutor(Args&&... args) : DefaultFunction(std::forward(args)...) {} - - virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) = 0; - - virtual void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - executeFunctionImpl(block, arguments, result, input_rows_count); - } -}; - -/// Combine several IExecutableFunctionImpl into one. -/// All the implementations should be equivalent. -/// Implementation to execute will be selected based on performance on previous runs. -/// DefaultFunction should be executable on every supported platform, while alternative implementations -/// could use extended set of instructions (AVX, NEON, etc). -/// It's convenient to inherit your func from this and register all alternative implementations in the constructor. -template -class FunctionPerformanceAdaptor : public FunctionExecutor +/* Class which is used to store implementations for the function and selecting the best one to run + * based on processor architecture and statistics from previous runs. + * + * FunctionInterface is typically IFunction or IExecutableFunctionImpl, but practically it can be + * any interface that contains "execute" method (IFunction is an exception and is supported as well). + * + * Example of usage: + * + * class MyDefaulImpl : public IFunction {...}; + * class MySecondImpl : public IFunction {...}; + * class MyAVX2Impl : public IFunction {...}; + * + * /// All methods but execute/executeImpl are usually not bottleneck, so just use them from + * /// default implementation. + * class MyFunction : public MyDefaultImpl + * { + * MyFunction(const Context & context) : selector(context) { + * /// Register all implementations in constructor. + * /// There could be as many implementation for every target as you want. + * selector.registerImplementation(); + * selector.registerImplementation(); + * selector.registreImplementation(); + * } + * + * void executeImpl(...) override { + * selector.selectAndExecute(...); + * } + * + * static FunctionPtr create(const Context & context) { + * return std::make_shared(context); + * } + * private: + * ImplementationSelector selector; + * }; + */ +template +class ImplementationSelector { public: - using BaseFunctionPtr = typename FunctionExecutor::BaseFunctionPtr; - - template - FunctionPerformanceAdaptor(const Context & context_, Params&&... params) - : FunctionExecutor(std::forward(params)...) - , context(context_) - { - if (isImplementationEnabled(DefaultFunction::getImplementationTag())) - statistics.emplace_back(); - } - - /// Register alternative implementation. - template - void registerImplementation(TargetArch arch, Params&&... params) - { - if (IsArchSupported(arch) && isImplementationEnabled(Function::getImplementationTag())) - { - impls.emplace_back(std::make_shared(std::forward(params)...)); - statistics.emplace_back(); - } - } + using ImplementationPtr = std::shared_ptr; - bool isImplementationEnabled(const String & impl_tag) - { - const String & tag = context.getSettingsRef().function_implementation.value; - return tag.empty() || tag == impl_tag; - // if (!options.implementations) - // return true; - - // for (const auto & tag : *options.implementations) - // { - // if (tag == impl_tag) - // return true; - // } - // return false; - } + ImplementationSelector(const Context & context_) : context(context_) {} -protected: - virtual void executeFunctionImpl(Block & block, const ColumnNumbers & arguments, - size_t result, size_t input_rows_count) override + /* Select the best implementation based on previous runs. + * If FunctionInterface is IFunction, then "executeImpl" method of the implementation will be called + * and "execute" otherwise. + */ + void selectAndExecute(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) { - if (statistics.empty()) - throw Exception("All available implementations are disabled by user config", + if (implementations.empty()) + throw Exception("There are no available implementations for function " "TODO(dakovalkov): add name", ErrorCodes::NO_SUITABLE_FUNCTION_IMPLEMENTATION); auto id = statistics.select(); Stopwatch watch; - if (id == impls.size()) - { - if constexpr (std::is_base_of_v) - DefaultFunction::executeImpl(block, arguments, result, input_rows_count); - else - DefaultFunction::execute(block, arguments, result, input_rows_count); - } + if constexpr (std::is_same_v) + implementations[id]->executeImpl(block, arguments, result, input_rows_count); else - { - if constexpr (std::is_base_of_v) - impls[id]->executeImpl(block, arguments, result, input_rows_count); - else - impls[id]->execute(block, arguments, result, input_rows_count); - } + implementations[id]->execute(block, arguments, result, input_rows_count); + watch.stop(); // TODO(dakovalkov): Calculate something more informative. @@ -249,10 +193,29 @@ class FunctionPerformanceAdaptor : public FunctionExecutor } } + /* Register new implementation for function. + * + * Arch - required instruction set for running the implementation. It's guarantied that no one method would + * be called (even the constructor and static methods) if the processor doesn't support this instruction set. + * + * FunctionImpl - implementation, should be inherited from template argument FunctionInterface. + * + * All function arguments will be forwarded to the implementation constructor. + */ + template + void registerImplementation(Args&&... args) + { + if (IsArchSupported(Arch)) + { + implementations.emplace_back(std::make_shared(std::forward(args)...)); + statistics.emplace_back(); + } + } + private: - std::vector impls; // Alternative implementations. - PerformanceStatistics statistics; const Context & context; + std::vector implementations; + PerformanceStatistics statistics; }; } diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 8713d85fdbd5..49655d637f2d 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -29,24 +29,35 @@ struct RandXorshiftImpl2 ) // DECLARE_MULTITARGET_CODE template -class FunctionRandomXorshift - : public FunctionPerformanceAdaptor> +class FunctionRandomXorshift : public FunctionRandomImpl { public: - FunctionRandomXorshift(const Context & context_) - : FunctionPerformanceAdaptor>(context_) + FunctionRandomXorshift(const Context & context) : selector(context) { + selector.registerImplementation>(); + if constexpr (UseMultitargetCode) { - this->template registerImplementation>(TargetArch::AVX2); - this->template registerImplementation>(TargetArch::AVX2); + selector.registerImplementation>(); + selector.registerImplementation>(); } } + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + static FunctionPtr create(const Context & context) { return std::make_shared>(context); } + +private: + ImplementationSelector selector; }; } From 66d530e90190b38cf2ce766403eb811342fb3f3a Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 19 May 2020 12:54:41 +0200 Subject: [PATCH 0177/1102] fast rand for performance tests --- src/Functions/FunctionsRandom.cpp | 165 +++++++++++++++++++++++++--- src/Functions/FunctionsRandom.h | 83 ++++++++++---- src/Functions/PerformanceAdaptors.h | 9 +- src/Functions/generateUUIDv4.cpp | 2 +- src/Functions/randConstant.cpp | 2 +- 5 files changed, 222 insertions(+), 39 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index fd493d5605bb..d0d25e56c60f 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -7,8 +7,15 @@ namespace DB { +/* + +// TODO(dakovalkov): remove this workaround. +#pragma GCC diagnostic ignored "-Wvector-operation-performance" + DECLARE_MULTITARGET_CODE( +*/ + namespace { /// NOTE Probably @@ -80,22 +87,22 @@ void RandImpl2::execute(char * output, size_t size) LinearCongruentialGenerator generator6; LinearCongruentialGenerator generator7; - seed(generator0, 0xfb4121280b2ab902ULL + reinterpret_cast(output)); - seed(generator1, 0x0121cf76df39c673ULL + reinterpret_cast(output)); - seed(generator2, 0x17ae86e3a19a602fULL + reinterpret_cast(output)); - seed(generator3, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)); - seed(generator4, 0xfb4122280b2ab102ULL + reinterpret_cast(output)); - seed(generator5, 0x0121c276df39c173ULL + reinterpret_cast(output)); - seed(generator6, 0x17ae82e3a19a612fULL + reinterpret_cast(output)); - seed(generator7, 0x8b6e12da7e06d122ULL + reinterpret_cast(output)); + seed(generator0, 0xfaaae481acb5874aULL + reinterpret_cast(output)); + seed(generator1, 0x3181a34f32887db6ULL + reinterpret_cast(output)); + seed(generator2, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); + seed(generator3, 0xc16062649e83dc13ULL + reinterpret_cast(output)); + seed(generator4, 0xbb093972da5c8d92ULL + reinterpret_cast(output)); + seed(generator5, 0xc37dcc410dcfed31ULL + reinterpret_cast(output)); + seed(generator6, 0x45e1526b7a4367d5ULL + reinterpret_cast(output)); + seed(generator7, 0x99c2759203868a7fULL + reinterpret_cast(output)); const char * end = output + size; for (; (end - output + 15) <= 32; output += 32) { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); unalignedStore(output + 12, generator3.next()); unalignedStore(output + 16, generator4.next()); unalignedStore(output + 20, generator5.next()); @@ -105,14 +112,144 @@ void RandImpl2::execute(char * output, size_t size) if (end - output > 0) { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); unalignedStore(output + 12, generator3.next()); output += 16; } } +/* + +typedef UInt64 UInt64x16 __attribute__ ((vector_size (128))); +typedef UInt64 UInt64x8 __attribute__ ((vector_size (64))); +typedef UInt64 UInt64x4 __attribute__ ((vector_size (32))); + +typedef UInt32 UInt32x16 __attribute__ ((vector_size (64))); +typedef UInt32 UInt32x8 __attribute__ ((vector_size (32))); +typedef UInt32 UInt32x4 __attribute__ ((vector_size (16))); + +void RandImpl3::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64x4 generators = { + 0xfb4121280b2ab902ULL + reinterpret_cast(output), + 0x0121cf76df39c673ULL + reinterpret_cast(output), + 0x17ae86e3a19a602fULL + reinterpret_cast(output), + 0x8b6e16da7e06d622ULL + reinterpret_cast(output), + }; + + constexpr int bytes_per_write = sizeof(UInt32x4); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators, UInt32x4)); + output += bytes_per_write; + } +} + +void RandImpl4::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64x8 generators = { + 0x5f186ce5faee450bULL + reinterpret_cast(output), + 0x9adb2ca3c72ac2eeULL + reinterpret_cast(output), + 0x07acf8bfa2537705ULL + reinterpret_cast(output), + 0x692b1b533834db92ULL + reinterpret_cast(output), + 0x5148b84cdda30081ULL + reinterpret_cast(output), + 0xe17b8a75a301ad47ULL + reinterpret_cast(output), + 0x6d4a5d69ed2a5f56ULL + reinterpret_cast(output), + 0x114e23266201b333ULL + reinterpret_cast(output), + }; + + constexpr int bytes_per_write = sizeof(UInt32x8); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators, UInt32x8)); + output += bytes_per_write; + } + + if ((end - output) > 0) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + UInt32x8 values = __builtin_convertvector(generators, UInt32x8); + for (int i = 0; (end - output) > 0; ++i) + { + unalignedStore(output, values[i]); + output += sizeof(UInt32); + } + } +} + +void RandImpl5::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64x16 generators = { + 0xfb4121280b2ab902ULL + reinterpret_cast(output), + 0x0121cf76df39c673ULL + reinterpret_cast(output), + 0x17ae86e3a19a602fULL + reinterpret_cast(output), + 0x8b6e16da7e06d622ULL + reinterpret_cast(output), + 0xfb4121f80b2ab902ULL + reinterpret_cast(output), + 0x0122cf767f39c633ULL + reinterpret_cast(output), + 0x14ae86e3a79a502fULL + reinterpret_cast(output), + 0x876316da7e06d622ULL + reinterpret_cast(output), + 0xfb4821280b2ab912ULL + reinterpret_cast(output), + 0x0126cf76df39c633ULL + reinterpret_cast(output), + 0x17a486e3a19a602fULL + reinterpret_cast(output), + 0x8b6216da7e08d622ULL + reinterpret_cast(output), + 0xfb4101f80b5ab902ULL + reinterpret_cast(output), + 0x01226f767f34c633ULL + reinterpret_cast(output), + 0x14ae86e3a75a502fULL + reinterpret_cast(output), + 0x876e36da7e36d622ULL + reinterpret_cast(output), + }; + + constexpr int bytes_per_write = sizeof(UInt32x16); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators, UInt32x16)); + output += bytes_per_write; + } + + if ((end - output) > 0) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + UInt32x16 values = __builtin_convertvector(generators, UInt32x16); + for (int i = 0; (end - output) > 0; ++i) + { + unalignedStore(output, values[i]); + output += sizeof(UInt32); + } + } +} + ) //DECLARE_MULTITARGET_CODE +*/ + } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 995f8ffeb9cc..9a06d8df7a3d 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -36,22 +36,48 @@ namespace ErrorCodes * This means that the timer must be of sufficient resolution to give different values to each block. */ +/* + DECLARE_MULTITARGET_CODE( +*/ + struct RandImpl { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch); } + static String getImplementationTag() { return ToString(TargetArch::Default); } }; struct RandImpl2 { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } + static String getImplementationTag() { return ToString(TargetArch::Default) + "_v2"; } +}; + +/* + +struct RandImpl3 +{ + static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v3"; } +}; + +struct RandImpl4 +{ + static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v4"; } +}; + +struct RandImpl5 +{ + static void execute(char * output, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v5"; } }; ) // DECLARE_MULTITARGET_CODE +*/ + template class FunctionRandomImpl : public IFunction { @@ -99,30 +125,45 @@ class FunctionRandomImpl : public IFunction }; template -class FunctionRandom : public FunctionRandomImpl +class FunctionRandom : public FunctionRandomImpl { public: FunctionRandom(const Context & context) : selector(context) { + // selector.registerImplementation>(); selector.registerImplementation>(); - selector.registerImplementation>(); - - if constexpr (UseMultitargetCode) - { - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - - selector.registerImplementation>(); - } + FunctionRandomImpl>(); + + // if constexpr (UseMultitargetCode) + // { + // selector.registerImplementation>(); + // selector.registerImplementation>(); + // selector.registerImplementation>(); + // selector.registerImplementation>(); + + // selector.registerImplementation>(); + + // selector.registerImplementation>(); + // selector.registerImplementation>(); + + // selector.registerImplementation>(); + // selector.registerImplementation>(); + + // selector.registerImplementation>(); + // selector.registerImplementation>(); + // } } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index b86730952fb0..717ad196e614 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -207,8 +207,13 @@ class ImplementationSelector { if (IsArchSupported(Arch)) { - implementations.emplace_back(std::make_shared(std::forward(args)...)); - statistics.emplace_back(); + // TODO(dakovalkov): make this option better. + const auto & choose_impl = context.getSettingsRef().function_implementation.value; + if (choose_impl.empty() || choose_impl == FunctionImpl::getImplementationTag()) + { + implementations.emplace_back(std::make_shared(std::forward(args)...)); + statistics.emplace_back(); + } } } diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index d543226ba5cc..4db3bd4c73d2 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -33,7 +33,7 @@ class FunctionGenerateUUIDv4 : public IFunction size_t size = input_rows_count; vec_to.resize(size); // TODO(dakovalkov): rewrite this workaround - TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); + RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); for (UInt128 & uuid: vec_to) { diff --git a/src/Functions/randConstant.cpp b/src/Functions/randConstant.cpp index 3eba5abf10d6..163f943d2065 100644 --- a/src/Functions/randConstant.cpp +++ b/src/Functions/randConstant.cpp @@ -100,7 +100,7 @@ class RandomConstantOverloadResolver : public IFunctionOverloadResolverImpl typename ColumnVector::Container vec_to(1); // TODO(dakovalkov): Rewrite this workaround - TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); + RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); ToType value = vec_to[0]; return std::make_unique>(value, argument_types, return_type); From 90bc3e6136a751aed685d0fd2b7a6187652e6853 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 20 May 2020 14:42:21 +0200 Subject: [PATCH 0178/1102] More rand implementations --- src/Functions/FunctionsRandom.cpp | 313 +++++++++++++++++------------- src/Functions/FunctionsRandom.h | 135 ++++++++----- src/Functions/generateUUIDv4.cpp | 2 +- src/Functions/randConstant.cpp | 2 +- 4 files changed, 275 insertions(+), 177 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index d0d25e56c60f..496e0edcc5ad 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -3,19 +3,18 @@ #include #include #include +#include namespace DB { -/* - // TODO(dakovalkov): remove this workaround. -#pragma GCC diagnostic ignored "-Wvector-operation-performance" +#if !defined(__clang__) +# pragma GCC diagnostic ignored "-Wvector-operation-performance" +#endif DECLARE_MULTITARGET_CODE( -*/ - namespace { /// NOTE Probably @@ -45,10 +44,16 @@ namespace } }; - void seed(LinearCongruentialGenerator & generator, intptr_t additional_seed) + UInt64 calcSeed(UInt64 rand_seed, UInt64 additional_seed) + { + return intHash64(rand_seed ^ intHash64(additional_seed)); + } + + void seed(LinearCongruentialGenerator & generator, UInt64 rand_seed, intptr_t additional_seed) { - generator.seed(intHash64(randomSeed() ^ intHash64(additional_seed))); + generator.seed(calcSeed(rand_seed, additional_seed)); } + } void RandImpl::execute(char * output, size_t size) @@ -58,10 +63,12 @@ void RandImpl::execute(char * output, size_t size) LinearCongruentialGenerator generator2; LinearCongruentialGenerator generator3; - seed(generator0, 0xfb4121280b2ab902ULL + reinterpret_cast(output)); - seed(generator1, 0x0121cf76df39c673ULL + reinterpret_cast(output)); - seed(generator2, 0x17ae86e3a19a602fULL + reinterpret_cast(output)); - seed(generator3, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)); + UInt64 rand_seed = randomSeed(); + + seed(generator0, rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)); + seed(generator1, rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)); + seed(generator2, rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)); + seed(generator3, rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)); for (const char * end = output + size; output < end; output += 16) { @@ -73,55 +80,6 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } -void RandImpl2::execute(char * output, size_t size) -{ - if (size == 0) - return; - - LinearCongruentialGenerator generator0; - LinearCongruentialGenerator generator1; - LinearCongruentialGenerator generator2; - LinearCongruentialGenerator generator3; - LinearCongruentialGenerator generator4; - LinearCongruentialGenerator generator5; - LinearCongruentialGenerator generator6; - LinearCongruentialGenerator generator7; - - seed(generator0, 0xfaaae481acb5874aULL + reinterpret_cast(output)); - seed(generator1, 0x3181a34f32887db6ULL + reinterpret_cast(output)); - seed(generator2, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); - seed(generator3, 0xc16062649e83dc13ULL + reinterpret_cast(output)); - seed(generator4, 0xbb093972da5c8d92ULL + reinterpret_cast(output)); - seed(generator5, 0xc37dcc410dcfed31ULL + reinterpret_cast(output)); - seed(generator6, 0x45e1526b7a4367d5ULL + reinterpret_cast(output)); - seed(generator7, 0x99c2759203868a7fULL + reinterpret_cast(output)); - - const char * end = output + size; - - for (; (end - output + 15) <= 32; output += 32) - { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); - unalignedStore(output + 12, generator3.next()); - unalignedStore(output + 16, generator4.next()); - unalignedStore(output + 20, generator5.next()); - unalignedStore(output + 24, generator6.next()); - unalignedStore(output + 28, generator7.next()); - } - - if (end - output > 0) - { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); - unalignedStore(output + 12, generator3.next()); - output += 16; - } -} - -/* - typedef UInt64 UInt64x16 __attribute__ ((vector_size (128))); typedef UInt64 UInt64x8 __attribute__ ((vector_size (64))); typedef UInt64 UInt64x4 __attribute__ ((vector_size (32))); @@ -130,58 +88,85 @@ typedef UInt32 UInt32x16 __attribute__ ((vector_size (64))); typedef UInt32 UInt32x8 __attribute__ ((vector_size (32))); typedef UInt32 UInt32x4 __attribute__ ((vector_size (16))); -void RandImpl3::execute(char * output, size_t size) +template +struct DummyStruct; + +template <> +struct DummyStruct<4> { + using UInt64Type = UInt64x4; + using UInt32Type = UInt32x4; +}; +template <> +struct DummyStruct<8> +{ + using UInt64Type = UInt64x8; + using UInt32Type = UInt32x8; +}; +template <> +struct DummyStruct<16> +{ + using UInt64Type = UInt64x16; + using UInt32Type = UInt32x16; +}; + +template +using VecUInt64 = typename DummyStruct::UInt64Type; +template +using VecUInt32 = typename DummyStruct::UInt32Type; + +namespace { + +constexpr std::array random_numbers = { + 0x0c8ff307dabc0c4cULL, + 0xf4bce78bf3821c1bULL, + 0x4eb628a1e189c21aULL, + 0x85ae000d253e0dbcULL, + + 0xc98073e6480f8a10ULL, + 0xb17e9b70a084d570ULL, + 0x1361c752b768da8cULL, + 0x3d915f60c06d144dULL, + + 0xd5bc9b7aced79587ULL, + 0x66c28000ba8a66cfULL, + 0x0fb58da7a48820f5ULL, + 0x540ee1b57aa861a1ULL, + + 0x212f11936ef2db04ULL, + 0xa3939cd900edcc58ULL, + 0xc676c84420170102ULL, + 0xcbdc824e8b4bf3edULL, +}; + +}; + +template +void RandVecImpl::execute(char * output, size_t size) +{ + static_assert(VectorSize >= 4); + static_assert(VectorSize <= random_numbers.size()); + if (size == 0) return; char * end = output + size; - UInt64x4 generators = { - 0xfb4121280b2ab902ULL + reinterpret_cast(output), - 0x0121cf76df39c673ULL + reinterpret_cast(output), - 0x17ae86e3a19a602fULL + reinterpret_cast(output), - 0x8b6e16da7e06d622ULL + reinterpret_cast(output), - }; - - constexpr int bytes_per_write = sizeof(UInt32x4); constexpr int safe_overwrite = 15; + constexpr int bytes_per_write = sizeof(VecUInt32); - while ((end - output) + safe_overwrite >= bytes_per_write) - { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators, UInt32x4)); - output += bytes_per_write; - } -} + UInt64 rand_seed = randomSeed(); -void RandImpl4::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; - - UInt64x8 generators = { - 0x5f186ce5faee450bULL + reinterpret_cast(output), - 0x9adb2ca3c72ac2eeULL + reinterpret_cast(output), - 0x07acf8bfa2537705ULL + reinterpret_cast(output), - 0x692b1b533834db92ULL + reinterpret_cast(output), - 0x5148b84cdda30081ULL + reinterpret_cast(output), - 0xe17b8a75a301ad47ULL + reinterpret_cast(output), - 0x6d4a5d69ed2a5f56ULL + reinterpret_cast(output), - 0x114e23266201b333ULL + reinterpret_cast(output), - }; - - constexpr int bytes_per_write = sizeof(UInt32x8); - constexpr int safe_overwrite = 15; + VecUInt64 generators{}; + for (int i = 0; i < VectorSize; ++i) + generators[i] = calcSeed(rand_seed, random_numbers[VectorSize] + reinterpret_cast(output)); while ((end - output) + safe_overwrite >= bytes_per_write) { generators *= LinearCongruentialGenerator::a; generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators, UInt32x8)); + VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); + unalignedStore>(output, values); output += bytes_per_write; } @@ -189,7 +174,7 @@ void RandImpl4::execute(char * output, size_t size) { generators *= LinearCongruentialGenerator::a; generators += LinearCongruentialGenerator::c; - UInt32x8 values = __builtin_convertvector(generators, UInt32x8); + VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); for (int i = 0; (end - output) > 0; ++i) { unalignedStore(output, values[i]); @@ -198,49 +183,50 @@ void RandImpl4::execute(char * output, size_t size) } } -void RandImpl5::execute(char * output, size_t size) +template struct RandVecImpl<4>; +template struct RandVecImpl<8>; +template struct RandVecImpl<16>; + +template +void RandVecImpl2::execute(char * output, size_t size) { + static_assert(VectorSize >= 4); + if (size == 0) return; char * end = output + size; - UInt64x16 generators = { - 0xfb4121280b2ab902ULL + reinterpret_cast(output), - 0x0121cf76df39c673ULL + reinterpret_cast(output), - 0x17ae86e3a19a602fULL + reinterpret_cast(output), - 0x8b6e16da7e06d622ULL + reinterpret_cast(output), - 0xfb4121f80b2ab902ULL + reinterpret_cast(output), - 0x0122cf767f39c633ULL + reinterpret_cast(output), - 0x14ae86e3a79a502fULL + reinterpret_cast(output), - 0x876316da7e06d622ULL + reinterpret_cast(output), - 0xfb4821280b2ab912ULL + reinterpret_cast(output), - 0x0126cf76df39c633ULL + reinterpret_cast(output), - 0x17a486e3a19a602fULL + reinterpret_cast(output), - 0x8b6216da7e08d622ULL + reinterpret_cast(output), - 0xfb4101f80b5ab902ULL + reinterpret_cast(output), - 0x01226f767f34c633ULL + reinterpret_cast(output), - 0x14ae86e3a75a502fULL + reinterpret_cast(output), - 0x876e36da7e36d622ULL + reinterpret_cast(output), - }; - - constexpr int bytes_per_write = sizeof(UInt32x16); constexpr int safe_overwrite = 15; + constexpr int bytes_per_write = 2 * sizeof(VecUInt32); + + UInt64 rand_seed = randomSeed(); + VecUInt64 gens1{}, gens2{}; + for (int i = 0; i < VectorSize; ++i) + { + gens1[i] = calcSeed(rand_seed, i * 1123465ull * reinterpret_cast(output)); + gens2[i] = calcSeed(rand_seed, i * 6432453ull * reinterpret_cast(output)); + } while ((end - output) + safe_overwrite >= bytes_per_write) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators, UInt32x16)); + gens1 *= LinearCongruentialGenerator::a; + gens1 += LinearCongruentialGenerator::c; + VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); + unalignedStore>(output, values1); + gens2 *= LinearCongruentialGenerator::a; + gens2 += LinearCongruentialGenerator::c; + VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); + unalignedStore>(output, values2); output += bytes_per_write; } - - if ((end - output) > 0) + + while ((end - output) > 0) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - UInt32x16 values = __builtin_convertvector(generators, UInt32x16); - for (int i = 0; (end - output) > 0; ++i) + gens1 *= LinearCongruentialGenerator::a; + gens1 += LinearCongruentialGenerator::c; + VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); + for (int i = 0; (end - output) > 0 && i < VectorSize; ++i) { unalignedStore(output, values[i]); output += sizeof(UInt32); @@ -248,8 +234,73 @@ void RandImpl5::execute(char * output, size_t size) } } -) //DECLARE_MULTITARGET_CODE +template struct RandVecImpl2<4>; +template struct RandVecImpl2<8>; +template struct RandVecImpl2<16>; -*/ +// template +// void RandVecImpl4::execute(char * output, size_t size) +// { +// static_assert(VectorSize >= 4); + +// if (size == 0) +// return; + +// char * end = output + size; + +// constexpr int safe_overwrite = 15; +// constexpr int bytes_per_write = 4 * sizeof(VecUInt32); + +// VecUInt64 gens1{}, gens2{}, gens3{}, gens4{}; +// for (int i = 0; i < VectorSize; ++i) +// { +// gens1[i] = calcSeed(i * 1123465ull * reinterpret_cast(output)); +// gens2[i] = calcSeed(i * 6432453ull * reinterpret_cast(output)); +// gens3[i] = calcSeed(i * 1346434ull * reinterpret_cast(output)); +// gens4[i] = calcSeed(i * 5344753ull * reinterpret_cast(output)); +// } + +// while ((end - output) + safe_overwrite >= bytes_per_write) +// { +// gens1 *= LinearCongruentialGenerator::a; +// gens1 += LinearCongruentialGenerator::c; +// VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); +// unalignedStore>(output, values1); +// gens2 *= LinearCongruentialGenerator::a; +// gens2 += LinearCongruentialGenerator::c; +// VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); +// unalignedStore>(output, values2); +// gens3 *= LinearCongruentialGenerator::a; +// gens3 += LinearCongruentialGenerator::c; +// VecUInt32 values3 = __builtin_convertvector(gens3 >> 16, VecUInt32); +// unalignedStore>(output, values3); +// gens4 *= LinearCongruentialGenerator::a; +// gens4 += LinearCongruentialGenerator::c; +// VecUInt32 values4 = __builtin_convertvector(gens4 >> 16, VecUInt32); +// unalignedStore>(output, values4); +// output += bytes_per_write; +// } + +// while ((end - output) > 0) +// { +// gens1 *= LinearCongruentialGenerator::a; +// gens1 += LinearCongruentialGenerator::c; +// VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); +// for (int i = 0; (end - output) > 0 && i < VectorSize; i += 4) +// { +// unalignedStore(output, values[i]); +// unalignedStore(output + 4, values[i + 1]); +// unalignedStore(output + 8, values[i + 2]); +// unalignedStore(output + 12, values[i + 3]); +// output += 16; +// } +// } +// } + +// template struct RandVecImpl2<4>; +// template struct RandVecImpl2<8>; +// template struct RandVecImpl2<16>; + +) //DECLARE_MULTITARGET_CODE } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 9a06d8df7a3d..557e1fbe8688 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -36,26 +36,20 @@ namespace ErrorCodes * This means that the timer must be of sufficient resolution to give different values to each block. */ -/* - DECLARE_MULTITARGET_CODE( -*/ - struct RandImpl { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(TargetArch::Default); } + static String getImplementationTag() { return ToString(BuildArch); } }; struct RandImpl2 { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(TargetArch::Default) + "_v2"; } + static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } }; -/* - struct RandImpl3 { static void execute(char * output, size_t size); @@ -74,9 +68,27 @@ struct RandImpl5 static String getImplementationTag() { return ToString(BuildArch) + "_v5"; } }; -) // DECLARE_MULTITARGET_CODE +template +struct RandVecImpl +{ + static void execute(char * outpu, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_vec_" + toString(VectorSize); } +}; + +template +struct RandVecImpl2 +{ + static void execute(char * outpu, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_vec2_" + toString(VectorSize); } +}; -*/ +struct RandImpl6 +{ + static void execute(char * outpu, size_t size); + static String getImplementationTag() { return ToString(BuildArch) + "_v6"; } +}; + +) // DECLARE_MULTITARGET_CODE template class FunctionRandomImpl : public IFunction @@ -125,45 +137,80 @@ class FunctionRandomImpl : public IFunction }; template -class FunctionRandom : public FunctionRandomImpl +class FunctionRandom : public FunctionRandomImpl { public: FunctionRandom(const Context & context) : selector(context) { - // selector.registerImplementation>(); selector.registerImplementation>(); - - // if constexpr (UseMultitargetCode) - // { - // selector.registerImplementation>(); - // selector.registerImplementation>(); - // selector.registerImplementation>(); - // selector.registerImplementation>(); - - // selector.registerImplementation>(); - - // selector.registerImplementation>(); - // selector.registerImplementation>(); - - // selector.registerImplementation>(); - // selector.registerImplementation>(); - - // selector.registerImplementation>(); - // selector.registerImplementation>(); - // } + FunctionRandomImpl>(); + selector.registerImplementation>(); + + if constexpr (UseMultitargetCode) + { + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + + selector.registerImplementation>(); + + selector.registerImplementation>(); + selector.registerImplementation>(); + + selector.registerImplementation>(); + selector.registerImplementation>(); + + selector.registerImplementation>(); + selector.registerImplementation>(); + + // vec impl + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + // vec impl 2 + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); + + selector.registerImplementation>(); + } } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index 4db3bd4c73d2..d543226ba5cc 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -33,7 +33,7 @@ class FunctionGenerateUUIDv4 : public IFunction size_t size = input_rows_count; vec_to.resize(size); // TODO(dakovalkov): rewrite this workaround - RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); + TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); for (UInt128 & uuid: vec_to) { diff --git a/src/Functions/randConstant.cpp b/src/Functions/randConstant.cpp index 163f943d2065..3eba5abf10d6 100644 --- a/src/Functions/randConstant.cpp +++ b/src/Functions/randConstant.cpp @@ -100,7 +100,7 @@ class RandomConstantOverloadResolver : public IFunctionOverloadResolverImpl typename ColumnVector::Container vec_to(1); // TODO(dakovalkov): Rewrite this workaround - RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); + TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); ToType value = vec_to[0]; return std::make_unique>(value, argument_types, return_type); From e6031585710b3b3d3a1921b88e8d50b086b65dc9 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 20 May 2020 14:43:33 +0200 Subject: [PATCH 0179/1102] Rand implementations --- src/Functions/FunctionsRandom.cpp | 249 ++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 496e0edcc5ad..283013bdb9bf 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -80,6 +80,62 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } +void RandImpl2::execute(char * output, size_t size) +{ + if (size == 0) + return; + + LinearCongruentialGenerator generator0; + LinearCongruentialGenerator generator1; + LinearCongruentialGenerator generator2; + LinearCongruentialGenerator generator3; + LinearCongruentialGenerator generator4; + LinearCongruentialGenerator generator5; + LinearCongruentialGenerator generator6; + LinearCongruentialGenerator generator7; + + UInt64 rand_seed = randomSeed(); + + seed(generator0, rand_seed, 0xfaaae481acb5874aULL + reinterpret_cast(output)); + seed(generator1, rand_seed, 0x3181a34f32887db6ULL + reinterpret_cast(output)); + seed(generator2, rand_seed, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); + seed(generator3, rand_seed, 0xc16062649e83dc13ULL + reinterpret_cast(output)); + seed(generator4, rand_seed, 0xbb093972da5c8d92ULL + reinterpret_cast(output)); + seed(generator5, rand_seed, 0xc37dcc410dcfed31ULL + reinterpret_cast(output)); + seed(generator6, rand_seed, 0x45e1526b7a4367d5ULL + reinterpret_cast(output)); + seed(generator7, rand_seed, 0x99c2759203868a7fULL + reinterpret_cast(output)); + + const char * end = output + size; + + constexpr int bytes_per_write = 32; + constexpr int safe_overwrite = 15; + + for (; (end - output) + safe_overwrite >= bytes_per_write; output += safe_overwrite) + { + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); + unalignedStore(output + 12, generator3.next()); + unalignedStore(output + 16, generator4.next()); + unalignedStore(output + 20, generator5.next()); + unalignedStore(output + 24, generator6.next()); + unalignedStore(output + 28, generator7.next()); + } + + seed(generator0, rand_seed, 0xfaaae481acb5874aULL + reinterpret_cast(output)); + seed(generator1, rand_seed, 0x3181a34f32887db6ULL + reinterpret_cast(output)); + seed(generator2, rand_seed, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); + seed(generator3, rand_seed, 0xc16062649e83dc13ULL + reinterpret_cast(output)); + + if (end - output > 0) + { + unalignedStore(output, generator0.next()); + unalignedStore(output + 4, generator1.next()); + unalignedStore(output + 8, generator2.next()); + unalignedStore(output + 12, generator3.next()); + } +} + typedef UInt64 UInt64x16 __attribute__ ((vector_size (128))); typedef UInt64 UInt64x8 __attribute__ ((vector_size (64))); typedef UInt64 UInt64x4 __attribute__ ((vector_size (32))); @@ -115,6 +171,128 @@ using VecUInt64 = typename DummyStruct::UInt64Type; template using VecUInt32 = typename DummyStruct::UInt32Type; +void RandImpl3::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64x4 generators = { + 0xfb4121280b2ab902ULL + reinterpret_cast(output), + 0x0121cf76df39c673ULL + reinterpret_cast(output), + 0x17ae86e3a19a602fULL + reinterpret_cast(output), + 0x8b6e16da7e06d622ULL + reinterpret_cast(output), + }; + + constexpr int bytes_per_write = sizeof(UInt32x4); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x4)); + output += bytes_per_write; + } +} + +void RandImpl4::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64 rand_seed = randomSeed(); + + UInt64x8 generators = { + calcSeed(rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0xfb4121f80b2ab902ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x0122cf767f39c633ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x14ae86e3a79a502fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x876316da7e06d622ULL + reinterpret_cast(output)), + }; + + constexpr int bytes_per_write = sizeof(UInt32x8); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x8)); + output += bytes_per_write; + } + + if ((end - output) > 0) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + UInt32x8 values = __builtin_convertvector(generators >> 16, UInt32x8); + for (int i = 0; (end - output) > 0; ++i) + { + unalignedStore(output, values[i]); + output += sizeof(UInt32); + } + } +} + +void RandImpl5::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64 rand_seed = randomSeed(); + + UInt64x16 generators = { + calcSeed(rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0xfb4121f80b2ab902ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x0122cf767f39c633ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x14ae86e3a79a502fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x876316da7e06d622ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0xfb4821280b2ab912ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x0126cf76df39c633ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x17a486e3a19a602fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x8b6216da7e08d622ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0xfb4101f80b5ab902ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x01226f767f34c633ULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x14ae86e3a75a502fULL + reinterpret_cast(output)), + calcSeed(rand_seed, 0x876e36da7e36d622ULL + reinterpret_cast(output)), + }; + + constexpr int bytes_per_write = sizeof(UInt32x16); + constexpr int safe_overwrite = 15; + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x16)); + output += bytes_per_write; + } + + if ((end - output) > 0) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + UInt32x16 values = __builtin_convertvector(generators >> 16, UInt32x16); + for (int i = 0; (end - output) > 0; ++i) + { + unalignedStore(output, values[i]); + output += sizeof(UInt32); + } + } +} + namespace { constexpr std::array random_numbers = { @@ -303,4 +481,75 @@ template struct RandVecImpl2<16>; ) //DECLARE_MULTITARGET_CODE +DECLARE_AVX2_SPECIFIC_CODE( + +void RandImpl6::execute(char * output, size_t size) +{ + if (size == 0) + return; + + char * end = output + size; + + UInt64x8 generators = { + 0x5f186ce5faee450bULL + reinterpret_cast(output), + 0x9adb2ca3c72ac2eeULL + reinterpret_cast(output), + 0x07acf8bfa2537705ULL + reinterpret_cast(output), + 0x692b1b533834db92ULL + reinterpret_cast(output), + 0x5148b84cdda30081ULL + reinterpret_cast(output), + 0xe17b8a75a301ad47ULL + reinterpret_cast(output), + 0x6d4a5d69ed2a5f56ULL + reinterpret_cast(output), + 0x114e23266201b333ULL + reinterpret_cast(output), + }; + + union { + UInt64x8 vec; + __m256i mm[2]; + } gens {generators}; + + constexpr int bytes_per_write = sizeof(UInt32x8); + constexpr int safe_overwrite = 15; + + const auto low_a = _mm256_set1_epi64x(0xDEECE66D); + // const auto high_a = _mm256_set1_epi64x(5); + const auto c = _mm256_set1_epi64x(11); + + while ((end - output) + safe_overwrite >= bytes_per_write) + { + { + auto gens_high = _mm256_srli_epi64(gens.mm[0], 32); + auto low_low_res = _mm256_mul_epu32(gens.mm[0], low_a); + auto high_low_res = _mm256_slli_epi64(_mm256_mul_epu32(gens_high, low_a), 32); + auto low_high_res = _mm256_slli_epi64(gens.mm[0], 32) + _mm256_slli_epi64(gens.mm[0], 34); + gens.mm[0] = _mm256_add_epi64(_mm256_add_epi64(low_low_res, high_low_res), + _mm256_add_epi64(low_high_res, c)); + } + { + auto gens_high = _mm256_srli_epi64(gens.mm[1], 32); + auto low_low_res = _mm256_mul_epu32(gens.mm[1], low_a); + auto high_low_res = _mm256_slli_epi64(_mm256_mul_epu32(gens_high, low_a), 32); + auto low_high_res = _mm256_slli_epi64(gens.mm[1], 32) + _mm256_slli_epi64(gens.mm[1], 34); + gens.mm[1] = _mm256_add_epi64(_mm256_add_epi64(low_low_res, high_low_res), + _mm256_add_epi64(low_high_res, c)); + } + // generators *= LinearCongruentialGenerator::a; + // generators += LinearCongruentialGenerator::c; + unalignedStore(output, __builtin_convertvector(gens.vec >> 16, UInt32x8)); + output += bytes_per_write; + } + + if ((end - output) > 0) + { + generators *= LinearCongruentialGenerator::a; + generators += LinearCongruentialGenerator::c; + UInt32x8 values = __builtin_convertvector(generators >> 16, UInt32x8); + for (int i = 0; (end - output) > 0; ++i) + { + unalignedStore(output, values[i]); + output += sizeof(UInt32); + } + } +} + +) // DECLARE_AVX2_SPECIFIC_CODE + } From feaed1e020934ddac683fab616fd2927e6d256a8 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 20 May 2020 17:43:01 +0200 Subject: [PATCH 0180/1102] rand isn't avx2-vectorizable, I give it up --- src/Functions/FunctionsRandom.cpp | 519 ++++++++---------------------- src/Functions/FunctionsRandom.h | 109 +------ src/Functions/VectorExtension.h | 101 ++++++ 3 files changed, 246 insertions(+), 483 deletions(-) create mode 100644 src/Functions/VectorExtension.h diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 283013bdb9bf..5ab51e9e3b85 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,11 +9,6 @@ namespace DB { -// TODO(dakovalkov): remove this workaround. -#if !defined(__clang__) -# pragma GCC diagnostic ignored "-Wvector-operation-performance" -#endif - DECLARE_MULTITARGET_CODE( namespace @@ -80,212 +76,73 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } -void RandImpl2::execute(char * output, size_t size) -{ - if (size == 0) - return; - - LinearCongruentialGenerator generator0; - LinearCongruentialGenerator generator1; - LinearCongruentialGenerator generator2; - LinearCongruentialGenerator generator3; - LinearCongruentialGenerator generator4; - LinearCongruentialGenerator generator5; - LinearCongruentialGenerator generator6; - LinearCongruentialGenerator generator7; - - UInt64 rand_seed = randomSeed(); - - seed(generator0, rand_seed, 0xfaaae481acb5874aULL + reinterpret_cast(output)); - seed(generator1, rand_seed, 0x3181a34f32887db6ULL + reinterpret_cast(output)); - seed(generator2, rand_seed, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); - seed(generator3, rand_seed, 0xc16062649e83dc13ULL + reinterpret_cast(output)); - seed(generator4, rand_seed, 0xbb093972da5c8d92ULL + reinterpret_cast(output)); - seed(generator5, rand_seed, 0xc37dcc410dcfed31ULL + reinterpret_cast(output)); - seed(generator6, rand_seed, 0x45e1526b7a4367d5ULL + reinterpret_cast(output)); - seed(generator7, rand_seed, 0x99c2759203868a7fULL + reinterpret_cast(output)); - - const char * end = output + size; - - constexpr int bytes_per_write = 32; - constexpr int safe_overwrite = 15; - - for (; (end - output) + safe_overwrite >= bytes_per_write; output += safe_overwrite) - { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); - unalignedStore(output + 12, generator3.next()); - unalignedStore(output + 16, generator4.next()); - unalignedStore(output + 20, generator5.next()); - unalignedStore(output + 24, generator6.next()); - unalignedStore(output + 28, generator7.next()); - } - - seed(generator0, rand_seed, 0xfaaae481acb5874aULL + reinterpret_cast(output)); - seed(generator1, rand_seed, 0x3181a34f32887db6ULL + reinterpret_cast(output)); - seed(generator2, rand_seed, 0xb6970e4a91b66afdULL + reinterpret_cast(output)); - seed(generator3, rand_seed, 0xc16062649e83dc13ULL + reinterpret_cast(output)); - - if (end - output > 0) - { - unalignedStore(output, generator0.next()); - unalignedStore(output + 4, generator1.next()); - unalignedStore(output + 8, generator2.next()); - unalignedStore(output + 12, generator3.next()); - } -} - -typedef UInt64 UInt64x16 __attribute__ ((vector_size (128))); -typedef UInt64 UInt64x8 __attribute__ ((vector_size (64))); -typedef UInt64 UInt64x4 __attribute__ ((vector_size (32))); - -typedef UInt32 UInt32x16 __attribute__ ((vector_size (64))); -typedef UInt32 UInt32x8 __attribute__ ((vector_size (32))); -typedef UInt32 UInt32x4 __attribute__ ((vector_size (16))); - -template -struct DummyStruct; +namespace { -template <> -struct DummyStruct<4> -{ - using UInt64Type = UInt64x4; - using UInt32Type = UInt32x4; -}; -template <> -struct DummyStruct<8> -{ - using UInt64Type = UInt64x8; - using UInt32Type = UInt32x8; +// The array of random numbers from 'head -c8 /dev/urandom | xxd -p'. +// Can be used for creating seeds for random generators. +constexpr std::array random_numbers = { + 0x0c8ff307dabc0c4cULL, 0xf4bce78bf3821c1bULL, 0x4eb628a1e189c21aULL, 0x85ae000d253e0dbcULL, + 0xc98073e6480f8a10ULL, 0xb17e9b70a084d570ULL, 0x1361c752b768da8cULL, 0x3d915f60c06d144dULL, + 0xd5bc9b7aced79587ULL, 0x66c28000ba8a66cfULL, 0x0fb58da7a48820f5ULL, 0x540ee1b57aa861a1ULL, + 0x212f11936ef2db04ULL, 0xa3939cd900edcc58ULL, 0xc676c84420170102ULL, 0xcbdc824e8b4bf3edULL, + + 0x8296f9d93cc94e3bULL, 0x78a7e826d62085b2ULL, 0xaa30620211fc6c69ULL, 0xbd38de52f0a93677ULL, + 0x19983de8d79dcc4eULL, 0x8afe883ef2199e6fULL, 0xb7160f7ed022b60aULL, 0x2ce173d373ddafd4ULL, + 0x15762761bb55b9acULL, 0x3e448fc94fdd28e7ULL, 0xa5121232adfbe70aULL, 0xb1e0f6d286112804ULL, + 0x6062e96de9554806ULL, 0xcc679b329c28882aULL, 0x5c6d29f45cbc060eULL, 0x1af1325a86ffb162ULL, }; -template <> -struct DummyStruct<16> -{ - using UInt64Type = UInt64x16; - using UInt32Type = UInt32x16; + }; -template -using VecUInt64 = typename DummyStruct::UInt64Type; -template -using VecUInt32 = typename DummyStruct::UInt32Type; +using namespace VectorExtension; -void RandImpl3::execute(char * output, size_t size) +template +void RandVecImpl::execute(char * output, size_t size) { - if (size == 0) - return; - - char * end = output + size; - - UInt64x4 generators = { - 0xfb4121280b2ab902ULL + reinterpret_cast(output), - 0x0121cf76df39c673ULL + reinterpret_cast(output), - 0x17ae86e3a19a602fULL + reinterpret_cast(output), - 0x8b6e16da7e06d622ULL + reinterpret_cast(output), - }; - - constexpr int bytes_per_write = sizeof(UInt32x4); - constexpr int safe_overwrite = 15; + static_assert(VecSize >= 4); + static_assert(VecSize <= random_numbers.size()); - while ((end - output) + safe_overwrite >= bytes_per_write) - { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x4)); - output += bytes_per_write; - } -} + using VecUInt64 = UInt64x; + using VecUInt32 = UInt32x; -void RandImpl4::execute(char * output, size_t size) -{ if (size == 0) return; char * end = output + size; - UInt64 rand_seed = randomSeed(); - - UInt64x8 generators = { - calcSeed(rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0xfb4121f80b2ab902ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x0122cf767f39c633ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x14ae86e3a79a502fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x876316da7e06d622ULL + reinterpret_cast(output)), - }; - - constexpr int bytes_per_write = sizeof(UInt32x8); constexpr int safe_overwrite = 15; - - while ((end - output) + safe_overwrite >= bytes_per_write) - { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x8)); - output += bytes_per_write; - } - - if ((end - output) > 0) - { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - UInt32x8 values = __builtin_convertvector(generators >> 16, UInt32x8); - for (int i = 0; (end - output) > 0; ++i) - { - unalignedStore(output, values[i]); - output += sizeof(UInt32); - } - } -} - -void RandImpl5::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; + constexpr int bytes_per_write = sizeof(VecUInt32); UInt64 rand_seed = randomSeed(); - UInt64x16 generators = { - calcSeed(rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0xfb4121f80b2ab902ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x0122cf767f39c633ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x14ae86e3a79a502fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x876316da7e06d622ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0xfb4821280b2ab912ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x0126cf76df39c633ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x17a486e3a19a602fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x8b6216da7e08d622ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0xfb4101f80b5ab902ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x01226f767f34c633ULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x14ae86e3a75a502fULL + reinterpret_cast(output)), - calcSeed(rand_seed, 0x876e36da7e36d622ULL + reinterpret_cast(output)), - }; + UInt64 a = LinearCongruentialGenerator::a; + // TODO(dakovalkov): try to remove this. + /// Note: GCC likes to expand multiplication by a constant into shifts + additions. + /// In this case a few multiplications become tens of shifts and additions. That leads to a huge slow down. + /// To avoid it we pretend that 'a' is not a constant. Actually we hope that rand_seed is never 0. + if (rand_seed == 0) + a = LinearCongruentialGenerator::a + 2; - constexpr int bytes_per_write = sizeof(UInt32x16); - constexpr int safe_overwrite = 15; + constexpr UInt64 c = LinearCongruentialGenerator::c; + + VecUInt64 generators{}; + for (int i = 0; i < VecSize; ++i) + generators[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); while ((end - output) + safe_overwrite >= bytes_per_write) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(generators >> 16, UInt32x16)); + generators = generators * a + c;; + VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); + unalignedStore(output, values); output += bytes_per_write; } - if ((end - output) > 0) + // Process tail + while ((end - output) > 0) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - UInt32x16 values = __builtin_convertvector(generators >> 16, UInt32x16); - for (int i = 0; (end - output) > 0; ++i) + generators = generators * a + c;; + VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); + for (int i = 0; i < VecSize && (end - output) > 0; ++i) { unalignedStore(output, values[i]); output += sizeof(UInt32); @@ -293,37 +150,14 @@ void RandImpl5::execute(char * output, size_t size) } } -namespace { - -constexpr std::array random_numbers = { - 0x0c8ff307dabc0c4cULL, - 0xf4bce78bf3821c1bULL, - 0x4eb628a1e189c21aULL, - 0x85ae000d253e0dbcULL, - - 0xc98073e6480f8a10ULL, - 0xb17e9b70a084d570ULL, - 0x1361c752b768da8cULL, - 0x3d915f60c06d144dULL, - - 0xd5bc9b7aced79587ULL, - 0x66c28000ba8a66cfULL, - 0x0fb58da7a48820f5ULL, - 0x540ee1b57aa861a1ULL, - - 0x212f11936ef2db04ULL, - 0xa3939cd900edcc58ULL, - 0xc676c84420170102ULL, - 0xcbdc824e8b4bf3edULL, -}; - -}; - -template -void RandVecImpl::execute(char * output, size_t size) +template +void RandVecImpl2::execute(char * output, size_t size) { - static_assert(VectorSize >= 4); - static_assert(VectorSize <= random_numbers.size()); + static_assert(VecSize >= 4); + static_assert(2 * VecSize <= random_numbers.size()); + + using VecUInt64 = UInt64x; + using VecUInt32 = UInt32x; if (size == 0) return; @@ -331,29 +165,45 @@ void RandVecImpl::execute(char * output, size_t size) char * end = output + size; constexpr int safe_overwrite = 15; - constexpr int bytes_per_write = sizeof(VecUInt32); + constexpr int bytes_per_write = 2 * sizeof(VecUInt32); UInt64 rand_seed = randomSeed(); - VecUInt64 generators{}; - for (int i = 0; i < VectorSize; ++i) - generators[i] = calcSeed(rand_seed, random_numbers[VectorSize] + reinterpret_cast(output)); + UInt64 a = LinearCongruentialGenerator::a; + // TODO(dakovalkov): try to remove this. + /// Note: GCC likes to expand multiplication by a constant into shifts + additions. + /// In this case a few multiplications become tens of shifts and additions. That leads to a huge slow down. + /// To avoid it we pretend that 'a' is not a constant. Actually we hope that rand_seed is never 0. + if (rand_seed == 0) + a = LinearCongruentialGenerator::a + 2; + + constexpr UInt64 c = LinearCongruentialGenerator::c; + + VecUInt64 gens1{}; + VecUInt64 gens2{}; + for (int i = 0; i < VecSize; ++i) + { + gens1[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); + gens2[i] = calcSeed(rand_seed, random_numbers[i + VecSize] + reinterpret_cast(output)); + } while ((end - output) + safe_overwrite >= bytes_per_write) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); - unalignedStore>(output, values); + gens1 = gens1 * a + c;; + VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); + unalignedStore(output, values1); + gens2 = gens2 * a + c;; + VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); + unalignedStore(output + sizeof(VecUInt32), values2); output += bytes_per_write; } - if ((end - output) > 0) + // Process tail + while ((end - output) > 0) { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); - for (int i = 0; (end - output) > 0; ++i) + gens1 = gens1 * a + c;; + VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); + for (int i = 0; i < VecSize && (end - output) > 0; ++i) { unalignedStore(output, values[i]); output += sizeof(UInt32); @@ -361,14 +211,14 @@ void RandVecImpl::execute(char * output, size_t size) } } -template struct RandVecImpl<4>; -template struct RandVecImpl<8>; -template struct RandVecImpl<16>; - -template -void RandVecImpl2::execute(char * output, size_t size) +template +void RandVecImpl4::execute(char * output, size_t size) { - static_assert(VectorSize >= 4); + static_assert(VecSize >= 4); + static_assert(4 * VecSize <= random_numbers.size()); + + using VecUInt64 = UInt64x; + using VecUInt32 = UInt32x; if (size == 0) return; @@ -376,35 +226,55 @@ void RandVecImpl2::execute(char * output, size_t size) char * end = output + size; constexpr int safe_overwrite = 15; - constexpr int bytes_per_write = 2 * sizeof(VecUInt32); + constexpr int bytes_per_write = 4 * sizeof(VecUInt32); UInt64 rand_seed = randomSeed(); - VecUInt64 gens1{}, gens2{}; - for (int i = 0; i < VectorSize; ++i) + + UInt64 a = LinearCongruentialGenerator::a; + // TODO(dakovalkov): try to remove this. + /// Note: GCC likes to expand multiplication by a constant into shifts + additions. + /// In this case a few multiplications become tens of shifts and additions. That leads to a huge slow down. + /// To avoid it we pretend that 'a' is not a constant. Actually we hope that rand_seed is never 0. + if (rand_seed == 0) + a = LinearCongruentialGenerator::a + 2; + + constexpr UInt64 c = LinearCongruentialGenerator::c; + + VecUInt64 gens1{}; + VecUInt64 gens2{}; + VecUInt64 gens3{}; + VecUInt64 gens4{}; + for (int i = 0; i < VecSize; ++i) { - gens1[i] = calcSeed(rand_seed, i * 1123465ull * reinterpret_cast(output)); - gens2[i] = calcSeed(rand_seed, i * 6432453ull * reinterpret_cast(output)); + gens1[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); + gens2[i] = calcSeed(rand_seed, random_numbers[i + VecSize] + reinterpret_cast(output)); + gens3[i] = calcSeed(rand_seed, random_numbers[i + 2 * VecSize] + reinterpret_cast(output)); + gens4[i] = calcSeed(rand_seed, random_numbers[i + 3 * VecSize] + reinterpret_cast(output)); } while ((end - output) + safe_overwrite >= bytes_per_write) { - gens1 *= LinearCongruentialGenerator::a; - gens1 += LinearCongruentialGenerator::c; - VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); - unalignedStore>(output, values1); - gens2 *= LinearCongruentialGenerator::a; - gens2 += LinearCongruentialGenerator::c; - VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); - unalignedStore>(output, values2); + gens1 = gens1 * a + c; + VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); + unalignedStore(output, values1); + gens2 = gens2 * a + c; + VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); + unalignedStore(output + sizeof(VecUInt32), values2); + gens3 = gens3 * a + c; + VecUInt32 values3 = __builtin_convertvector(gens3 >> 16, VecUInt32); + unalignedStore(output + 2 * sizeof(VecUInt32), values3); + gens4 = gens4 * a + c; + VecUInt32 values4 = __builtin_convertvector(gens4 >> 16, VecUInt32); + unalignedStore(output + 3 * sizeof(VecUInt32), values4); output += bytes_per_write; } - + + // Process tail while ((end - output) > 0) { - gens1 *= LinearCongruentialGenerator::a; - gens1 += LinearCongruentialGenerator::c; - VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); - for (int i = 0; (end - output) > 0 && i < VectorSize; ++i) + gens1 = gens1 * a + c;; + VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); + for (int i = 0; i < VecSize && (end - output) > 0; ++i) { unalignedStore(output, values[i]); output += sizeof(UInt32); @@ -412,144 +282,15 @@ void RandVecImpl2::execute(char * output, size_t size) } } -template struct RandVecImpl2<4>; -template struct RandVecImpl2<8>; -template struct RandVecImpl2<16>; - -// template -// void RandVecImpl4::execute(char * output, size_t size) -// { -// static_assert(VectorSize >= 4); - -// if (size == 0) -// return; - -// char * end = output + size; - -// constexpr int safe_overwrite = 15; -// constexpr int bytes_per_write = 4 * sizeof(VecUInt32); - -// VecUInt64 gens1{}, gens2{}, gens3{}, gens4{}; -// for (int i = 0; i < VectorSize; ++i) -// { -// gens1[i] = calcSeed(i * 1123465ull * reinterpret_cast(output)); -// gens2[i] = calcSeed(i * 6432453ull * reinterpret_cast(output)); -// gens3[i] = calcSeed(i * 1346434ull * reinterpret_cast(output)); -// gens4[i] = calcSeed(i * 5344753ull * reinterpret_cast(output)); -// } - -// while ((end - output) + safe_overwrite >= bytes_per_write) -// { -// gens1 *= LinearCongruentialGenerator::a; -// gens1 += LinearCongruentialGenerator::c; -// VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); -// unalignedStore>(output, values1); -// gens2 *= LinearCongruentialGenerator::a; -// gens2 += LinearCongruentialGenerator::c; -// VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); -// unalignedStore>(output, values2); -// gens3 *= LinearCongruentialGenerator::a; -// gens3 += LinearCongruentialGenerator::c; -// VecUInt32 values3 = __builtin_convertvector(gens3 >> 16, VecUInt32); -// unalignedStore>(output, values3); -// gens4 *= LinearCongruentialGenerator::a; -// gens4 += LinearCongruentialGenerator::c; -// VecUInt32 values4 = __builtin_convertvector(gens4 >> 16, VecUInt32); -// unalignedStore>(output, values4); -// output += bytes_per_write; -// } - -// while ((end - output) > 0) -// { -// gens1 *= LinearCongruentialGenerator::a; -// gens1 += LinearCongruentialGenerator::c; -// VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); -// for (int i = 0; (end - output) > 0 && i < VectorSize; i += 4) -// { -// unalignedStore(output, values[i]); -// unalignedStore(output + 4, values[i + 1]); -// unalignedStore(output + 8, values[i + 2]); -// unalignedStore(output + 12, values[i + 3]); -// output += 16; -// } -// } -// } - -// template struct RandVecImpl2<4>; -// template struct RandVecImpl2<8>; -// template struct RandVecImpl2<16>; - -) //DECLARE_MULTITARGET_CODE +) // DECLARE_MULTITARGET_CODE DECLARE_AVX2_SPECIFIC_CODE( + template struct RandVecImpl4<4>; +) // DECLARE_AVX2_SPECIFIC_CODE -void RandImpl6::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; - - UInt64x8 generators = { - 0x5f186ce5faee450bULL + reinterpret_cast(output), - 0x9adb2ca3c72ac2eeULL + reinterpret_cast(output), - 0x07acf8bfa2537705ULL + reinterpret_cast(output), - 0x692b1b533834db92ULL + reinterpret_cast(output), - 0x5148b84cdda30081ULL + reinterpret_cast(output), - 0xe17b8a75a301ad47ULL + reinterpret_cast(output), - 0x6d4a5d69ed2a5f56ULL + reinterpret_cast(output), - 0x114e23266201b333ULL + reinterpret_cast(output), - }; - - union { - UInt64x8 vec; - __m256i mm[2]; - } gens {generators}; - - constexpr int bytes_per_write = sizeof(UInt32x8); - constexpr int safe_overwrite = 15; - - const auto low_a = _mm256_set1_epi64x(0xDEECE66D); - // const auto high_a = _mm256_set1_epi64x(5); - const auto c = _mm256_set1_epi64x(11); - - while ((end - output) + safe_overwrite >= bytes_per_write) - { - { - auto gens_high = _mm256_srli_epi64(gens.mm[0], 32); - auto low_low_res = _mm256_mul_epu32(gens.mm[0], low_a); - auto high_low_res = _mm256_slli_epi64(_mm256_mul_epu32(gens_high, low_a), 32); - auto low_high_res = _mm256_slli_epi64(gens.mm[0], 32) + _mm256_slli_epi64(gens.mm[0], 34); - gens.mm[0] = _mm256_add_epi64(_mm256_add_epi64(low_low_res, high_low_res), - _mm256_add_epi64(low_high_res, c)); - } - { - auto gens_high = _mm256_srli_epi64(gens.mm[1], 32); - auto low_low_res = _mm256_mul_epu32(gens.mm[1], low_a); - auto high_low_res = _mm256_slli_epi64(_mm256_mul_epu32(gens_high, low_a), 32); - auto low_high_res = _mm256_slli_epi64(gens.mm[1], 32) + _mm256_slli_epi64(gens.mm[1], 34); - gens.mm[1] = _mm256_add_epi64(_mm256_add_epi64(low_low_res, high_low_res), - _mm256_add_epi64(low_high_res, c)); - } - // generators *= LinearCongruentialGenerator::a; - // generators += LinearCongruentialGenerator::c; - unalignedStore(output, __builtin_convertvector(gens.vec >> 16, UInt32x8)); - output += bytes_per_write; - } - - if ((end - output) > 0) - { - generators *= LinearCongruentialGenerator::a; - generators += LinearCongruentialGenerator::c; - UInt32x8 values = __builtin_convertvector(generators >> 16, UInt32x8); - for (int i = 0; (end - output) > 0; ++i) - { - unalignedStore(output, values[i]); - output += sizeof(UInt32); - } - } -} +DECLARE_AVX512F_SPECIFIC_CODE( + template struct RandVecImpl4<8>; +) // DECLARE_AVX512F_SPECIFIC_CODE -) // DECLARE_AVX2_SPECIFIC_CODE } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 557e1fbe8688..a82f199356eb 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -43,49 +43,26 @@ struct RandImpl static void execute(char * output, size_t size); static String getImplementationTag() { return ToString(BuildArch); } }; - -struct RandImpl2 -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } -}; - -struct RandImpl3 +// Isn't used now. +template +struct RandVecImpl { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v3"; } + static String getImplementationTag() { return ToString(BuildArch) + "_vec_" + toString(VecSize); } }; - -struct RandImpl4 +// Isn't used now. +template +struct RandVecImpl2 { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v4"; } + static String getImplementationTag() { return ToString(BuildArch) + "_vec2_" + toString(VecSize); } }; -struct RandImpl5 +template +struct RandVecImpl4 { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v5"; } -}; - -template -struct RandVecImpl -{ - static void execute(char * outpu, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_vec_" + toString(VectorSize); } -}; - -template -struct RandVecImpl2 -{ - static void execute(char * outpu, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_vec2_" + toString(VectorSize); } -}; - -struct RandImpl6 -{ - static void execute(char * outpu, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v6"; } + static String getImplementationTag() { return ToString(BuildArch) + "_vec4_" + toString(VecSize); } }; ) // DECLARE_MULTITARGET_CODE @@ -144,72 +121,16 @@ class FunctionRandom : public FunctionRandomImpl>(); - selector.registerImplementation>(); if constexpr (UseMultitargetCode) { - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - - selector.registerImplementation>(); - - selector.registerImplementation>(); - selector.registerImplementation>(); - - selector.registerImplementation>(); + // vec impl 4 selector.registerImplementation>(); - - selector.registerImplementation>(); - selector.registerImplementation>(); - - // vec impl - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); + FunctionRandomImpl, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); - - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); - - // vec impl 2 - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); - - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); - - selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation, ToType, Name>>(); + selector.registerImplementation, ToType, Name>>(); - selector.registerImplementation>(); } } diff --git a/src/Functions/VectorExtension.h b/src/Functions/VectorExtension.h new file mode 100644 index 000000000000..49a029bb0d90 --- /dev/null +++ b/src/Functions/VectorExtension.h @@ -0,0 +1,101 @@ +#pragma once + +#include +// Contains types declarations and wrappers for GCC vector extension. + +// TODO(dakovalkov): remove this workaround. +#if !defined(__clang__) +# pragma GCC diagnostic ignored "-Wvector-operation-performance" +#endif + +namespace DB::VectorExtension +{ + +typedef UInt64 UInt64x2 __attribute__ ((vector_size (sizeof(UInt64) * 2))); +typedef UInt64 UInt64x4 __attribute__ ((vector_size (sizeof(UInt64) * 4))); +typedef UInt64 UInt64x8 __attribute__ ((vector_size (sizeof(UInt64) * 8))); +typedef UInt64 UInt64x16 __attribute__ ((vector_size (sizeof(UInt64) * 16))); +typedef UInt64 UInt64x32 __attribute__ ((vector_size (sizeof(UInt64) * 32))); + +typedef UInt32 UInt32x2 __attribute__ ((vector_size (sizeof(UInt32) * 2))); +typedef UInt32 UInt32x4 __attribute__ ((vector_size (sizeof(UInt32) * 4))); +typedef UInt32 UInt32x8 __attribute__ ((vector_size (sizeof(UInt32) * 8))); +typedef UInt32 UInt32x16 __attribute__ ((vector_size (sizeof(UInt32) * 16))); +typedef UInt32 UInt32x32 __attribute__ ((vector_size (sizeof(UInt32) * 32))); +typedef UInt32 UInt32x64 __attribute__ ((vector_size (sizeof(UInt32) * 64))); + +typedef UInt16 UInt16x2 __attribute__ ((vector_size (sizeof(UInt16) * 2))); +typedef UInt16 UInt16x4 __attribute__ ((vector_size (sizeof(UInt16) * 4))); +typedef UInt16 UInt16x8 __attribute__ ((vector_size (sizeof(UInt16) * 8))); +typedef UInt16 UInt16x16 __attribute__ ((vector_size (sizeof(UInt16) * 16))); +typedef UInt16 UInt16x32 __attribute__ ((vector_size (sizeof(UInt16) * 32))); +typedef UInt16 UInt16x64 __attribute__ ((vector_size (sizeof(UInt16) * 64))); + +typedef UInt8 UInt8x2 __attribute__ ((vector_size (sizeof(UInt8) * 2))); +typedef UInt8 UInt8x4 __attribute__ ((vector_size (sizeof(UInt8) * 4))); +typedef UInt8 UInt8x8 __attribute__ ((vector_size (sizeof(UInt8) * 8))); +typedef UInt8 UInt8x16 __attribute__ ((vector_size (sizeof(UInt8) * 16))); +typedef UInt8 UInt8x32 __attribute__ ((vector_size (sizeof(UInt8) * 32))); +typedef UInt8 UInt8x64 __attribute__ ((vector_size (sizeof(UInt8) * 64))); + +namespace detail +{ + template + struct DummyStruct; + + template <> + struct DummyStruct<4> + { + using UInt8Type = UInt8x4; + using UInt16Type = UInt16x4; + using UInt32Type = UInt32x4; + using UInt64Type = UInt64x4; + }; + template <> + struct DummyStruct<8> + { + using UInt8Type = UInt8x8; + using UInt16Type = UInt16x8; + using UInt32Type = UInt32x8; + using UInt64Type = UInt64x8; + }; + template <> + struct DummyStruct<16> + { + using UInt8Type = UInt8x16; + using UInt16Type = UInt16x16; + using UInt32Type = UInt32x16; + using UInt64Type = UInt64x16; + }; + template <> + struct DummyStruct<32> + { + using UInt8Type = UInt8x32; + using UInt16Type = UInt16x32; + using UInt32Type = UInt32x32; + using UInt64Type = UInt64x32; + }; + +} + +// Same as above via template, e.g. UInt64x<8> +template +using UInt8x = typename detail::DummyStruct::UInt8Type; +template +using UInt16x = typename detail::DummyStruct::UInt16Type; +template +using UInt32x = typename detail::DummyStruct::UInt32Type; +template +using UInt64x = typename detail::DummyStruct::UInt64Type; + +/* Casts vectors of the same size. + * UInt32x4 x{}; + * UInt64x4 y = ConvertVector(x); + */ +// template +// inline To ConvertVector(From a) +// { +// return __builtin_convertvector(a, To); +// } + +} From cfc87767c5803f253f80f0ddb4725e4ae4ff1b00 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 20 May 2020 18:16:11 +0200 Subject: [PATCH 0181/1102] Fix bug in collecting statistics --- src/Functions/PerformanceAdaptors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 717ad196e614..efe4243be79c 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -186,6 +186,7 @@ class ImplementationSelector { rows_summary += block.getByPosition(i).column->size(); } + rows_summary += block.getByPosition(result).column->size(); if (rows_summary >= 1000) { From d4a8d91789c949e9bfddc606152426e80bdc0c6c Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Wed, 20 May 2020 18:44:01 +0200 Subject: [PATCH 0182/1102] Fix style --- src/Functions/FunctionsRandom.cpp | 15 ++++++++------- src/Functions/FunctionsRandom.h | 4 +--- src/Functions/PerformanceAdaptors.h | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 5ab51e9e3b85..0f7359f835f7 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -76,7 +76,8 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } -namespace { +namespace +{ // The array of random numbers from 'head -c8 /dev/urandom | xxd -p'. // Can be used for creating seeds for random generators. @@ -103,11 +104,11 @@ void RandVecImpl::execute(char * output, size_t size) static_assert(VecSize <= random_numbers.size()); using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; + using VecUInt32 = UInt32x; if (size == 0) return; - + char * end = output + size; constexpr int safe_overwrite = 15; @@ -157,11 +158,11 @@ void RandVecImpl2::execute(char * output, size_t size) static_assert(2 * VecSize <= random_numbers.size()); using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; + using VecUInt32 = UInt32x; if (size == 0) return; - + char * end = output + size; constexpr int safe_overwrite = 15; @@ -218,11 +219,11 @@ void RandVecImpl4::execute(char * output, size_t size) static_assert(4 * VecSize <= random_numbers.size()); using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; + using VecUInt32 = UInt32x; if (size == 0) return; - + char * end = output + size; constexpr int safe_overwrite = 15; diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index a82f199356eb..5f1e549d961b 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -124,13 +124,11 @@ class FunctionRandom : public FunctionRandomImpl, ToType, Name>>(); - + selector.registerImplementation, ToType, Name>>(); - } } diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index efe4243be79c..daa653005706 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -177,7 +177,7 @@ class ImplementationSelector implementations[id]->executeImpl(block, arguments, result, input_rows_count); else implementations[id]->execute(block, arguments, result, input_rows_count); - + watch.stop(); // TODO(dakovalkov): Calculate something more informative. From 310ca8562c19fd063de47906f59f66bd3ccb6834 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 21 May 2020 09:32:42 +0200 Subject: [PATCH 0183/1102] Add auto-vectorization support for binary operations --- src/Functions/FunctionBinaryArithmetic.h | 71 ++++++++++++++++++++++-- src/Functions/FunctionsRandom.cpp | 1 - 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index 30b6da8b6962..aba64a525190 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -28,6 +28,8 @@ #include #include +#include + #if !defined(ARCADIA_BUILD) # include #endif @@ -52,12 +54,7 @@ namespace ErrorCodes extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES; } - -/** Arithmetic operations: +, -, *, /, %, - * intDiv (integer division) - * Bitwise operations: |, &, ^, ~. - * Etc. - */ +DECLARE_MULTITARGET_CODE( template struct BinaryOperationImplBase @@ -89,6 +86,68 @@ struct BinaryOperationImplBase } }; +) // DECLARE_MULTITARGET_CODE + + +/** Arithmetic operations: +, -, *, /, %, + * intDiv (integer division) + * Bitwise operations: |, &, ^, ~. + * Etc. + */ +template +struct BinaryOperationImplBase +{ + using ResultType = ResultType_; + static const constexpr bool allow_fixed_string = false; + + static void vectorVector(const A * __restrict a, const B * __restrict b, ResultType * __restrict c, size_t size) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); + } + + static void vectorConstant(const A * __restrict a, B b, ResultType * __restrict c, size_t size) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); + } + + static void constantVector(A a, const B * __restrict b, ResultType * __restrict c, size_t size) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); + } + + static ResultType constantConstant(A a, B b) + { + return Op::template apply(a, b); + } +}; + template struct FixedStringOperationImpl { diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 0f7359f835f7..f673b6c5f816 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -293,5 +293,4 @@ DECLARE_AVX512F_SPECIFIC_CODE( template struct RandVecImpl4<8>; ) // DECLARE_AVX512F_SPECIFIC_CODE - } From 8483dfa272fee7063f5cfc24bb7066bf48cf78cc Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 21 May 2020 09:40:27 +0200 Subject: [PATCH 0184/1102] Delete needless rand implementations --- src/Functions/FunctionsRandom.cpp | 119 +----------------------------- src/Functions/FunctionsRandom.h | 22 ++---- tests/performance/rand.xml | 4 +- 3 files changed, 8 insertions(+), 137 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index f673b6c5f816..cfb3b92c818b 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -99,121 +99,6 @@ using namespace VectorExtension; template void RandVecImpl::execute(char * output, size_t size) -{ - static_assert(VecSize >= 4); - static_assert(VecSize <= random_numbers.size()); - - using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; - - if (size == 0) - return; - - char * end = output + size; - - constexpr int safe_overwrite = 15; - constexpr int bytes_per_write = sizeof(VecUInt32); - - UInt64 rand_seed = randomSeed(); - - UInt64 a = LinearCongruentialGenerator::a; - // TODO(dakovalkov): try to remove this. - /// Note: GCC likes to expand multiplication by a constant into shifts + additions. - /// In this case a few multiplications become tens of shifts and additions. That leads to a huge slow down. - /// To avoid it we pretend that 'a' is not a constant. Actually we hope that rand_seed is never 0. - if (rand_seed == 0) - a = LinearCongruentialGenerator::a + 2; - - constexpr UInt64 c = LinearCongruentialGenerator::c; - - VecUInt64 generators{}; - for (int i = 0; i < VecSize; ++i) - generators[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); - - while ((end - output) + safe_overwrite >= bytes_per_write) - { - generators = generators * a + c;; - VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); - unalignedStore(output, values); - output += bytes_per_write; - } - - // Process tail - while ((end - output) > 0) - { - generators = generators * a + c;; - VecUInt32 values = __builtin_convertvector(generators >> 16, VecUInt32); - for (int i = 0; i < VecSize && (end - output) > 0; ++i) - { - unalignedStore(output, values[i]); - output += sizeof(UInt32); - } - } -} - -template -void RandVecImpl2::execute(char * output, size_t size) -{ - static_assert(VecSize >= 4); - static_assert(2 * VecSize <= random_numbers.size()); - - using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; - - if (size == 0) - return; - - char * end = output + size; - - constexpr int safe_overwrite = 15; - constexpr int bytes_per_write = 2 * sizeof(VecUInt32); - - UInt64 rand_seed = randomSeed(); - - UInt64 a = LinearCongruentialGenerator::a; - // TODO(dakovalkov): try to remove this. - /// Note: GCC likes to expand multiplication by a constant into shifts + additions. - /// In this case a few multiplications become tens of shifts and additions. That leads to a huge slow down. - /// To avoid it we pretend that 'a' is not a constant. Actually we hope that rand_seed is never 0. - if (rand_seed == 0) - a = LinearCongruentialGenerator::a + 2; - - constexpr UInt64 c = LinearCongruentialGenerator::c; - - VecUInt64 gens1{}; - VecUInt64 gens2{}; - for (int i = 0; i < VecSize; ++i) - { - gens1[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); - gens2[i] = calcSeed(rand_seed, random_numbers[i + VecSize] + reinterpret_cast(output)); - } - - while ((end - output) + safe_overwrite >= bytes_per_write) - { - gens1 = gens1 * a + c;; - VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); - unalignedStore(output, values1); - gens2 = gens2 * a + c;; - VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); - unalignedStore(output + sizeof(VecUInt32), values2); - output += bytes_per_write; - } - - // Process tail - while ((end - output) > 0) - { - gens1 = gens1 * a + c;; - VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); - for (int i = 0; i < VecSize && (end - output) > 0; ++i) - { - unalignedStore(output, values[i]); - output += sizeof(UInt32); - } - } -} - -template -void RandVecImpl4::execute(char * output, size_t size) { static_assert(VecSize >= 4); static_assert(4 * VecSize <= random_numbers.size()); @@ -286,11 +171,11 @@ void RandVecImpl4::execute(char * output, size_t size) ) // DECLARE_MULTITARGET_CODE DECLARE_AVX2_SPECIFIC_CODE( - template struct RandVecImpl4<4>; + template struct RandVecImpl<4>; ) // DECLARE_AVX2_SPECIFIC_CODE DECLARE_AVX512F_SPECIFIC_CODE( - template struct RandVecImpl4<8>; + template struct RandVecImpl<8>; ) // DECLARE_AVX512F_SPECIFIC_CODE } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 5f1e549d961b..5251f8fd6222 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -43,27 +43,15 @@ struct RandImpl static void execute(char * output, size_t size); static String getImplementationTag() { return ToString(BuildArch); } }; -// Isn't used now. + +/// Implementation is in .cpp file. +/// Every specialization should be explicitly written in .cpp file. template struct RandVecImpl { static void execute(char * output, size_t size); static String getImplementationTag() { return ToString(BuildArch) + "_vec_" + toString(VecSize); } }; -// Isn't used now. -template -struct RandVecImpl2 -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_vec2_" + toString(VecSize); } -}; - -template -struct RandVecImpl4 -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_vec4_" + toString(VecSize); } -}; ) // DECLARE_MULTITARGET_CODE @@ -125,10 +113,10 @@ class FunctionRandom : public FunctionRandomImpl, ToType, Name>>(); + FunctionRandomImpl, ToType, Name>>(); selector.registerImplementation, ToType, Name>>(); + FunctionRandomImpl, ToType, Name>>(); } } diff --git a/tests/performance/rand.xml b/tests/performance/rand.xml index a007eb501791..bd34a7a83d8f 100644 --- a/tests/performance/rand.xml +++ b/tests/performance/rand.xml @@ -13,13 +13,11 @@ table - numbers(10000000) + zeros(100000000) SELECT count() FROM (SELECT rand() FROM {table}) - SELECT count() FROM (SELECT randxorshift() FROM {table}) SELECT count() FROM (SELECT rand64() FROM {table}) - SELECT count() FROM (SELECT randxorshift64() FROM {table}) From e317dfb6e4fc5e6869ab7730ddc22b58ad9da0ca Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 21 May 2020 10:11:48 +0200 Subject: [PATCH 0185/1102] Fix FBA --- src/Functions/FunctionBinaryArithmetic.h | 70 ++++++++++++++++-------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index aba64a525190..40a387d09b24 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -102,44 +102,66 @@ struct BinaryOperationImplBase static void vectorVector(const A * __restrict a, const B * __restrict b, ResultType * __restrict c, size_t size) { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); + if constexpr (UseMultitargetCode) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); + } else + { TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); + } + } static void vectorConstant(const A * __restrict a, B b, ResultType * __restrict c, size_t size) { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); + if constexpr (UseMultitargetCode) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); + } else + { TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); + } } static void constantVector(A a, const B * __restrict b, ResultType * __restrict c, size_t size) { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); + if constexpr (UseMultitargetCode) + { + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); + else + TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); + } else + { TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); + } } static ResultType constantConstant(A a, B b) From 0d1577c5da6f54e76d447c457803800ac01b4baf Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Sun, 24 May 2020 14:25:07 +0200 Subject: [PATCH 0186/1102] Better avx2 implementation for rand(). Expected to be ~10% faster --- src/Functions/FunctionBinaryArithmetic.h | 1 - src/Functions/FunctionsRandom.cpp | 123 ++++++++++++----------- src/Functions/FunctionsRandom.h | 14 +-- 3 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index 40a387d09b24..c311b8d5d0a4 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -119,7 +119,6 @@ struct BinaryOperationImplBase { TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); } - } static void vectorConstant(const A * __restrict a, B b, ResultType * __restrict c, size_t size) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index cfb3b92c818b..9eaa44b0eb51 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -9,8 +9,6 @@ namespace DB { -DECLARE_MULTITARGET_CODE( - namespace { /// NOTE Probably @@ -50,8 +48,23 @@ namespace generator.seed(calcSeed(rand_seed, additional_seed)); } + /// The array of random numbers from 'head -c8 /dev/urandom | xxd -p'. + /// Can be used for creating seeds for random generators. + constexpr std::array random_numbers = { + 0x0c8ff307dabc0c4cULL, 0xf4bce78bf3821c1bULL, 0x4eb628a1e189c21aULL, 0x85ae000d253e0dbcULL, + 0xc98073e6480f8a10ULL, 0xb17e9b70a084d570ULL, 0x1361c752b768da8cULL, 0x3d915f60c06d144dULL, + 0xd5bc9b7aced79587ULL, 0x66c28000ba8a66cfULL, 0x0fb58da7a48820f5ULL, 0x540ee1b57aa861a1ULL, + 0x212f11936ef2db04ULL, 0xa3939cd900edcc58ULL, 0xc676c84420170102ULL, 0xcbdc824e8b4bf3edULL, + + 0x8296f9d93cc94e3bULL, 0x78a7e826d62085b2ULL, 0xaa30620211fc6c69ULL, 0xbd38de52f0a93677ULL, + 0x19983de8d79dcc4eULL, 0x8afe883ef2199e6fULL, 0xb7160f7ed022b60aULL, 0x2ce173d373ddafd4ULL, + 0x15762761bb55b9acULL, 0x3e448fc94fdd28e7ULL, 0xa5121232adfbe70aULL, 0xb1e0f6d286112804ULL, + 0x6062e96de9554806ULL, 0xcc679b329c28882aULL, 0x5c6d29f45cbc060eULL, 0x1af1325a86ffb162ULL, + }; } +DECLARE_DEFAULT_CODE( + void RandImpl::execute(char * output, size_t size) { LinearCongruentialGenerator generator0; @@ -61,10 +74,10 @@ void RandImpl::execute(char * output, size_t size) UInt64 rand_seed = randomSeed(); - seed(generator0, rand_seed, 0xfb4121280b2ab902ULL + reinterpret_cast(output)); - seed(generator1, rand_seed, 0x0121cf76df39c673ULL + reinterpret_cast(output)); - seed(generator2, rand_seed, 0x17ae86e3a19a602fULL + reinterpret_cast(output)); - seed(generator3, rand_seed, 0x8b6e16da7e06d622ULL + reinterpret_cast(output)); + seed(generator0, rand_seed, random_numbers[0] + reinterpret_cast(output)); + seed(generator1, rand_seed, random_numbers[1] + reinterpret_cast(output)); + seed(generator2, rand_seed, random_numbers[2] + reinterpret_cast(output)); + seed(generator3, rand_seed, random_numbers[3] + reinterpret_cast(output)); for (const char * end = output + size; output < end; output += 16) { @@ -76,43 +89,40 @@ void RandImpl::execute(char * output, size_t size) /// It is guaranteed (by PaddedPODArray) that we can overwrite up to 15 bytes after end. } -namespace -{ - -// The array of random numbers from 'head -c8 /dev/urandom | xxd -p'. -// Can be used for creating seeds for random generators. -constexpr std::array random_numbers = { - 0x0c8ff307dabc0c4cULL, 0xf4bce78bf3821c1bULL, 0x4eb628a1e189c21aULL, 0x85ae000d253e0dbcULL, - 0xc98073e6480f8a10ULL, 0xb17e9b70a084d570ULL, 0x1361c752b768da8cULL, 0x3d915f60c06d144dULL, - 0xd5bc9b7aced79587ULL, 0x66c28000ba8a66cfULL, 0x0fb58da7a48820f5ULL, 0x540ee1b57aa861a1ULL, - 0x212f11936ef2db04ULL, 0xa3939cd900edcc58ULL, 0xc676c84420170102ULL, 0xcbdc824e8b4bf3edULL, +) // DECLARE_DEFAULT_CODE - 0x8296f9d93cc94e3bULL, 0x78a7e826d62085b2ULL, 0xaa30620211fc6c69ULL, 0xbd38de52f0a93677ULL, - 0x19983de8d79dcc4eULL, 0x8afe883ef2199e6fULL, 0xb7160f7ed022b60aULL, 0x2ce173d373ddafd4ULL, - 0x15762761bb55b9acULL, 0x3e448fc94fdd28e7ULL, 0xa5121232adfbe70aULL, 0xb1e0f6d286112804ULL, - 0x6062e96de9554806ULL, 0xcc679b329c28882aULL, 0x5c6d29f45cbc060eULL, 0x1af1325a86ffb162ULL, -}; - -}; +DECLARE_AVX2_SPECIFIC_CODE( using namespace VectorExtension; -template -void RandVecImpl::execute(char * output, size_t size) +/* Takes 2 vectors with LinearCongruentialGenerator states and combines them into vector with random values. + * From every rand-state we use only bits 15...47 to generate random vector. + */ +inline UInt64x4 CombineValues(UInt64x4 a, UInt64x4 b) { - static_assert(VecSize >= 4); - static_assert(4 * VecSize <= random_numbers.size()); - - using VecUInt64 = UInt64x; - using VecUInt32 = UInt32x; + auto xa = reinterpret_cast<__m256i>(a); + auto xb = reinterpret_cast<__m256i>(b); + /// Every state is 8-byte value and we need to use only 4 from the middle. + /// Swap the low half and the high half of every state to move these bytes from the middle to sides. + /// xa = xa[1, 0, 3, 2, 5, 4, 7, 6] + xa = _mm256_shuffle_epi32(xa, 0xb1); + /// Now every 8-byte value in xa is xx....xx and every value in xb is ..xxxx.. where x is random byte we want to use. + /// Just blend them to get the result vector. + /// res = xa[0],xb[1,2],xa[3,4],xb[5,6],xa[7,8],xb[9,10],xa[11,12],xb[13,14],xa[15] + auto res = _mm256_blend_epi16(xa, xb, 0x66); + return reinterpret_cast(res); +} +void RandImpl::execute(char * output, size_t size) +{ if (size == 0) return; char * end = output + size; + constexpr int vec_size = 4; constexpr int safe_overwrite = 15; - constexpr int bytes_per_write = 4 * sizeof(VecUInt32); + constexpr int bytes_per_write = 4 * sizeof(UInt64x4); UInt64 rand_seed = randomSeed(); @@ -126,56 +136,49 @@ void RandVecImpl::execute(char * output, size_t size) constexpr UInt64 c = LinearCongruentialGenerator::c; - VecUInt64 gens1{}; - VecUInt64 gens2{}; - VecUInt64 gens3{}; - VecUInt64 gens4{}; - for (int i = 0; i < VecSize; ++i) + UInt64x4 gens1{}; + UInt64x4 gens2{}; + UInt64x4 gens3{}; + UInt64x4 gens4{}; + for (int i = 0; i < vec_size; ++i) { gens1[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); - gens2[i] = calcSeed(rand_seed, random_numbers[i + VecSize] + reinterpret_cast(output)); - gens3[i] = calcSeed(rand_seed, random_numbers[i + 2 * VecSize] + reinterpret_cast(output)); - gens4[i] = calcSeed(rand_seed, random_numbers[i + 3 * VecSize] + reinterpret_cast(output)); + gens2[i] = calcSeed(rand_seed, random_numbers[i + vec_size] + reinterpret_cast(output)); + gens3[i] = calcSeed(rand_seed, random_numbers[i + 2 * vec_size] + reinterpret_cast(output)); + gens4[i] = calcSeed(rand_seed, random_numbers[i + 3 * vec_size] + reinterpret_cast(output)); } while ((end - output) + safe_overwrite >= bytes_per_write) { gens1 = gens1 * a + c; - VecUInt32 values1 = __builtin_convertvector(gens1 >> 16, VecUInt32); - unalignedStore(output, values1); gens2 = gens2 * a + c; - VecUInt32 values2 = __builtin_convertvector(gens2 >> 16, VecUInt32); - unalignedStore(output + sizeof(VecUInt32), values2); + unalignedStore(output, CombineValues(gens1, gens2)); + gens3 = gens3 * a + c; + gens4 = gens4 * a + c; + unalignedStore(output + sizeof(UInt64x4), CombineValues(gens3, gens4)); + gens1 = gens1 * a + c; + gens2 = gens2 * a + c; + unalignedStore(output + 2 * sizeof(UInt64x4), CombineValues(gens1, gens2)); gens3 = gens3 * a + c; - VecUInt32 values3 = __builtin_convertvector(gens3 >> 16, VecUInt32); - unalignedStore(output + 2 * sizeof(VecUInt32), values3); gens4 = gens4 * a + c; - VecUInt32 values4 = __builtin_convertvector(gens4 >> 16, VecUInt32); - unalignedStore(output + 3 * sizeof(VecUInt32), values4); + unalignedStore(output + 3 * sizeof(UInt64x4), CombineValues(gens3, gens4)); output += bytes_per_write; } // Process tail while ((end - output) > 0) { - gens1 = gens1 * a + c;; - VecUInt32 values = __builtin_convertvector(gens1 >> 16, VecUInt32); - for (int i = 0; i < VecSize && (end - output) > 0; ++i) + gens1 = gens1 * a + c; + gens2 = gens2 * a + c; + UInt64x4 values = CombineValues(gens1, gens2); + for (int i = 0; i < vec_size && (end - output) > 0; ++i) { - unalignedStore(output, values[i]); - output += sizeof(UInt32); + unalignedStore(output, values[i]); + output += sizeof(UInt64); } } } -) // DECLARE_MULTITARGET_CODE - -DECLARE_AVX2_SPECIFIC_CODE( - template struct RandVecImpl<4>; ) // DECLARE_AVX2_SPECIFIC_CODE -DECLARE_AVX512F_SPECIFIC_CODE( - template struct RandVecImpl<8>; -) // DECLARE_AVX512F_SPECIFIC_CODE - } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 5251f8fd6222..e10b249df8ef 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -44,15 +44,6 @@ struct RandImpl static String getImplementationTag() { return ToString(BuildArch); } }; -/// Implementation is in .cpp file. -/// Every specialization should be explicitly written in .cpp file. -template -struct RandVecImpl -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_vec_" + toString(VecSize); } -}; - ) // DECLARE_MULTITARGET_CODE template @@ -113,10 +104,7 @@ class FunctionRandom : public FunctionRandomImpl, ToType, Name>>(); - - selector.registerImplementation, ToType, Name>>(); + FunctionRandomImpl>(); } } From 4a9891c601e459c6d9b9e638cd591ce7f3edbf13 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Mon, 25 May 2020 17:16:19 +0200 Subject: [PATCH 0187/1102] use vectorized rand in generateUUIDv4, add multitarget build in intHash32/64 --- src/Functions/FunctionsHashing.h | 37 +++++++++++++++++++++++++- src/Functions/FunctionsRandom.cpp | 7 ++--- src/Functions/VectorExtension.h | 23 ----------------- src/Functions/generateUUIDv4.cpp | 43 ++++++++++++++++++++++++++++--- tests/performance/rand.xml | 1 + 5 files changed, 81 insertions(+), 30 deletions(-) diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index f647390e1c89..6f00981a22a0 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include @@ -573,12 +575,13 @@ class FunctionStringHashFixedString : public IFunction }; +DECLARE_MULTITARGET_CODE( + template class FunctionIntHash : public IFunction { public: static constexpr auto name = Name::name; - static FunctionPtr create(const Context &) { return std::make_shared(); } private: using ToType = typename Impl::ReturnType; @@ -612,6 +615,8 @@ class FunctionIntHash : public IFunction return name; } + static String getImplementationTag() { return ToString(BuildArch); } + size_t getNumberOfArguments() const override { return 1; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override @@ -646,6 +651,36 @@ class FunctionIntHash : public IFunction } }; +) // DECLARE_MULTITARGET_CODE + +template +class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash +{ +public: + FunctionIntHash(const Context & context) : selector(context) + { + selector.registerImplementation>(); + + selector.registerImplementation>(); + selector.registerImplementation>(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + +private: + ImplementationSelector selector; +}; template class FunctionAnyHash : public IFunction diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 9eaa44b0eb51..6b2e79e90ca2 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -108,9 +108,9 @@ inline UInt64x4 CombineValues(UInt64x4 a, UInt64x4 b) xa = _mm256_shuffle_epi32(xa, 0xb1); /// Now every 8-byte value in xa is xx....xx and every value in xb is ..xxxx.. where x is random byte we want to use. /// Just blend them to get the result vector. - /// res = xa[0],xb[1,2],xa[3,4],xb[5,6],xa[7,8],xb[9,10],xa[11,12],xb[13,14],xa[15] - auto res = _mm256_blend_epi16(xa, xb, 0x66); - return reinterpret_cast(res); + /// result = xa[0],xb[1,2],xa[3,4],xb[5,6],xa[7,8],xb[9,10],xa[11,12],xb[13,14],xa[15] + __m256i result = _mm256_blend_epi16(xa, xb, 0x66); + return reinterpret_cast(result); } void RandImpl::execute(char * output, size_t size) @@ -140,6 +140,7 @@ void RandImpl::execute(char * output, size_t size) UInt64x4 gens2{}; UInt64x4 gens3{}; UInt64x4 gens4{}; + for (int i = 0; i < vec_size; ++i) { gens1[i] = calcSeed(rand_seed, random_numbers[i] + reinterpret_cast(output)); diff --git a/src/Functions/VectorExtension.h b/src/Functions/VectorExtension.h index 49a029bb0d90..24c2ae9a18f9 100644 --- a/src/Functions/VectorExtension.h +++ b/src/Functions/VectorExtension.h @@ -3,33 +3,23 @@ #include // Contains types declarations and wrappers for GCC vector extension. -// TODO(dakovalkov): remove this workaround. -#if !defined(__clang__) -# pragma GCC diagnostic ignored "-Wvector-operation-performance" -#endif - namespace DB::VectorExtension { typedef UInt64 UInt64x2 __attribute__ ((vector_size (sizeof(UInt64) * 2))); typedef UInt64 UInt64x4 __attribute__ ((vector_size (sizeof(UInt64) * 4))); typedef UInt64 UInt64x8 __attribute__ ((vector_size (sizeof(UInt64) * 8))); -typedef UInt64 UInt64x16 __attribute__ ((vector_size (sizeof(UInt64) * 16))); -typedef UInt64 UInt64x32 __attribute__ ((vector_size (sizeof(UInt64) * 32))); typedef UInt32 UInt32x2 __attribute__ ((vector_size (sizeof(UInt32) * 2))); typedef UInt32 UInt32x4 __attribute__ ((vector_size (sizeof(UInt32) * 4))); typedef UInt32 UInt32x8 __attribute__ ((vector_size (sizeof(UInt32) * 8))); typedef UInt32 UInt32x16 __attribute__ ((vector_size (sizeof(UInt32) * 16))); -typedef UInt32 UInt32x32 __attribute__ ((vector_size (sizeof(UInt32) * 32))); -typedef UInt32 UInt32x64 __attribute__ ((vector_size (sizeof(UInt32) * 64))); typedef UInt16 UInt16x2 __attribute__ ((vector_size (sizeof(UInt16) * 2))); typedef UInt16 UInt16x4 __attribute__ ((vector_size (sizeof(UInt16) * 4))); typedef UInt16 UInt16x8 __attribute__ ((vector_size (sizeof(UInt16) * 8))); typedef UInt16 UInt16x16 __attribute__ ((vector_size (sizeof(UInt16) * 16))); typedef UInt16 UInt16x32 __attribute__ ((vector_size (sizeof(UInt16) * 32))); -typedef UInt16 UInt16x64 __attribute__ ((vector_size (sizeof(UInt16) * 64))); typedef UInt8 UInt8x2 __attribute__ ((vector_size (sizeof(UInt8) * 2))); typedef UInt8 UInt8x4 __attribute__ ((vector_size (sizeof(UInt8) * 4))); @@ -65,15 +55,12 @@ namespace detail using UInt8Type = UInt8x16; using UInt16Type = UInt16x16; using UInt32Type = UInt32x16; - using UInt64Type = UInt64x16; }; template <> struct DummyStruct<32> { using UInt8Type = UInt8x32; using UInt16Type = UInt16x32; - using UInt32Type = UInt32x32; - using UInt64Type = UInt64x32; }; } @@ -88,14 +75,4 @@ using UInt32x = typename detail::DummyStruct::UInt32Type; template using UInt64x = typename detail::DummyStruct::UInt64Type; -/* Casts vectors of the same size. - * UInt32x4 x{}; - * UInt64x4 y = ConvertVector(x); - */ -// template -// inline To ConvertVector(From a) -// { -// return __builtin_convertvector(a, To); -// } - } diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index d543226ba5cc..04dd58775603 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -5,17 +5,24 @@ namespace DB { +#define DECLARE_SEVERAL_IMPLEMENTATIONS(...) \ +DECLARE_DEFAULT_CODE (__VA_ARGS__) \ +DECLARE_AVX2_SPECIFIC_CODE(__VA_ARGS__) + +DECLARE_SEVERAL_IMPLEMENTATIONS( + class FunctionGenerateUUIDv4 : public IFunction { public: static constexpr auto name = "generateUUIDv4"; - static FunctionPtr create(const Context &) { return std::make_shared(); } String getName() const override { return name; } + static String getImplementationTag() { return ToString(BuildArch); } + size_t getNumberOfArguments() const override { return 0; } DataTypePtr getReturnTypeImpl(const DataTypes &) const override @@ -32,8 +39,9 @@ class FunctionGenerateUUIDv4 : public IFunction size_t size = input_rows_count; vec_to.resize(size); - // TODO(dakovalkov): rewrite this workaround - TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); + + /// RandImpl is target-dependent and is not the same in different TargetSpecific namespaces. + RandImpl::execute(reinterpret_cast(vec_to.data()), vec_to.size() * sizeof(UInt128)); for (UInt128 & uuid: vec_to) { @@ -47,6 +55,35 @@ class FunctionGenerateUUIDv4 : public IFunction } }; +) // DECLARE_SEVERAL_IMPLEMENTATIONS +#undef DECLARE_SEVERAL_IMPLEMENTATIONS + +class FunctionGenerateUUIDv4 : public TargetSpecific::Default::FunctionGenerateUUIDv4 +{ +public: + FunctionGenerateUUIDv4(const Context & context) : selector(context) + { + selector.registerImplementation(); + + selector.registerImplementation(); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + +private: + ImplementationSelector selector; +}; + void registerFunctionGenerateUUIDv4(FunctionFactory & factory) { factory.registerFunction(); diff --git a/tests/performance/rand.xml b/tests/performance/rand.xml index bd34a7a83d8f..ed629e5a2a76 100644 --- a/tests/performance/rand.xml +++ b/tests/performance/rand.xml @@ -20,4 +20,5 @@ SELECT count() FROM (SELECT rand() FROM {table}) SELECT count() FROM (SELECT rand64() FROM {table}) + SELECT count() FROM (SELECT generateUUIDv4() FROM {table}) From 991cbf397aabc03782da6713d35d2e44ee6ae835 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 26 May 2020 13:15:44 +0200 Subject: [PATCH 0188/1102] Thread safe performance statistics --- src/Functions/FunctionsRandom.cpp | 2 +- src/Functions/FunctionsRandom.h | 1 + src/Functions/PerformanceAdaptors.h | 195 ++++++++++++++++------------ 3 files changed, 115 insertions(+), 83 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 6b2e79e90ca2..2c7b2e5f1f53 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -109,7 +109,7 @@ inline UInt64x4 CombineValues(UInt64x4 a, UInt64x4 b) /// Now every 8-byte value in xa is xx....xx and every value in xb is ..xxxx.. where x is random byte we want to use. /// Just blend them to get the result vector. /// result = xa[0],xb[1,2],xa[3,4],xb[5,6],xa[7,8],xb[9,10],xa[11,12],xb[13,14],xa[15] - __m256i result = _mm256_blend_epi16(xa, xb, 0x66); + __m256i result = _mm256_blend_epi16(xa, xb, 0x66); return reinterpret_cast(result); } diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index e10b249df8ef..8cbe42862858 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -40,6 +40,7 @@ DECLARE_MULTITARGET_CODE( struct RandImpl { + /// Fill memory with random data. The memory region must be 15-bytes padded. static void execute(char * output, size_t size); static String getImplementationTag() { return ToString(BuildArch); } }; diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index daa653005706..9dbc2a68f860 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -6,6 +6,7 @@ #include #include +#include #include /// This file contains Adaptors which help to combine several implementations of the function. @@ -20,104 +21,138 @@ namespace ErrorCodes extern const int NO_SUITABLE_FUNCTION_IMPLEMENTATION; } -// TODO(dakovalkov): This is copied and pasted struct from LZ4_decompress_faster.h with little changes. -struct PerformanceStatistics +namespace detail { - struct Element + class PerformanceStatistics { - double count = 0; - double sum = 0; - - double adjustedCount() const + public: + size_t select(bool considarable) { - return count - NUM_INVOCATIONS_TO_THROW_OFF; + /// We don't need to choose/measure anything if there's only one variant. + if (size() == 1) + return 0; + + std::lock_guard guard(lock); + + size_t best = 0; + double best_sample = data[0].sample(rng); + + for (size_t i = 1; i < data.size(); ++i) + { + double sample = data[i].sample(rng); + if (sample < best_sample) + { + best_sample = sample; + best = i; + } + } + + if (considarable) + data[best].run(); + + return best; } - double mean() const + void complete(size_t id, double seconds, double bytes) { - return sum / adjustedCount(); + if (size() == 1) + return; + + std::lock_guard guard(lock); + data[id].complete(seconds, bytes); } - /// For better convergence, we don't use proper estimate of stddev. - /// We want to eventually separate between two algorithms even in case - /// when there is no statistical significant difference between them. - double sigma() const + size_t size() const { - return mean() / sqrt(adjustedCount()); + return data.size(); } - void update(double seconds, double bytes) + bool empty() const { - ++count; + return size() == 0; + } - if (count > NUM_INVOCATIONS_TO_THROW_OFF) - sum += seconds / bytes; + void emplace_back() + { + data.emplace_back(); } - double sample(pcg64 & stat_rng) const + private: + struct Element { - /// If there is a variant with not enough statistics, always choose it. - /// And in that case prefer variant with less number of invocations. + int completed_count = 0; + int running_count = 0; + double sum = 0; - if (adjustedCount() < 2) - return adjustedCount() - 1; - else - return std::normal_distribution<>(mean(), sigma())(stat_rng); - } - }; + int adjustedCount() const + { + return completed_count - NUM_INVOCATIONS_TO_THROW_OFF; + } - /// Cold invocations may be affected by additional memory latencies. Don't take first invocations into account. - static constexpr double NUM_INVOCATIONS_TO_THROW_OFF = 2; + double mean() const + { + return sum / adjustedCount(); + } - /// How to select method to run. - /// -1 - automatically, based on statistics (default); - /// -2 - choose methods in round robin fashion (for performance testing). - /// >= 0 - always choose specified method (for performance testing); - ssize_t choose_method = -1; + /// For better convergence, we don't use proper estimate of stddev. + /// We want to eventually separate between two algorithms even in case + /// when there is no statistical significant difference between them. + double sigma() const + { + return mean() / sqrt(adjustedCount()); + } - std::vector data; + void run() + { + ++running_count; + } - /// It's Ok that generator is not seeded. - pcg64 rng; + void complete(double seconds, double bytes) + { + --running_count; + ++completed_count; - /// To select from different algorithms we use a kind of "bandits" algorithm. - /// Sample random values from estimated normal distributions and choose the minimal. - size_t select() - { - if (choose_method < 0) - { - std::vector samples(data.size()); - for (size_t i = 0; i < data.size(); ++i) - samples[i] = choose_method == -1 - ? data[i].sample(rng) - : data[i].adjustedCount(); + if (adjustedCount() > 0) + sum += seconds / bytes; + } - return std::min_element(samples.begin(), samples.end()) - samples.begin(); - } - else - return choose_method; - } + double sample(pcg64 & stat_rng) const + { + /// If there is a variant with not enough statistics, always choose it. + /// And in that case prefer variant with less number of invocations. + if (adjustedCount() < 2) + return adjustedCount() - 1 + running_count * 2; - size_t size() const - { - return data.size(); - } + return std::normal_distribution<>(mean(), sigma())(stat_rng); + } + }; + + std::vector data; + std::mutex lock; + /// It's Ok that generator is not seeded. + pcg64 rng; + /// Cold invocations may be affected by additional memory latencies. Don't take first invocations into account. + static constexpr int NUM_INVOCATIONS_TO_THROW_OFF = 2; + }; - bool empty() const - { - return size() == 0; - } + template + std::true_type hasImplementationTagTest(const T&); + std::false_type hasImplementationTagTest(...); + + template + constexpr bool has_implementation_tag = decltype(hasImplementationTagTest(std::declval()))::value; - void emplace_back() + template + String getImplementationTag(TargetArch arch) { - data.emplace_back(); + if constexpr (has_implementation_tag) + return ToString(arch) + "_" + T::getImplementationTag(); + else + return ToString(arch); } +} - PerformanceStatistics() {} - PerformanceStatistics(ssize_t choose_method_) : choose_method(choose_method_) {} -}; - -/* Class which is used to store implementations for the function and selecting the best one to run +/* Class which is used to store implementations for the function and to select the best one to run * based on processor architecture and statistics from previous runs. * * FunctionInterface is typically IFunction or IExecutableFunctionImpl, but practically it can be @@ -170,7 +205,10 @@ class ImplementationSelector throw Exception("There are no available implementations for function " "TODO(dakovalkov): add name", ErrorCodes::NO_SUITABLE_FUNCTION_IMPLEMENTATION); - auto id = statistics.select(); + /// Statistics shouldn't rely on small blocks. + bool considerable = (input_rows_count > 1000); + + size_t id = statistics.select(considerable); Stopwatch watch; if constexpr (std::is_same_v) @@ -180,17 +218,10 @@ class ImplementationSelector watch.stop(); - // TODO(dakovalkov): Calculate something more informative. - size_t rows_summary = 0; - for (auto i : arguments) - { - rows_summary += block.getByPosition(i).column->size(); - } - rows_summary += block.getByPosition(result).column->size(); - - if (rows_summary >= 1000) + if (considerable) { - statistics.data[id].update(watch.elapsedSeconds(), rows_summary); + // TODO(dakovalkov): Calculate something more informative than rows count. + statistics.complete(id, watch.elapsedSeconds(), input_rows_count); } } @@ -210,7 +241,7 @@ class ImplementationSelector { // TODO(dakovalkov): make this option better. const auto & choose_impl = context.getSettingsRef().function_implementation.value; - if (choose_impl.empty() || choose_impl == FunctionImpl::getImplementationTag()) + if (choose_impl.empty() || choose_impl == detail::getImplementationTag(Arch)) { implementations.emplace_back(std::make_shared(std::forward(args)...)); statistics.emplace_back(); @@ -221,7 +252,7 @@ class ImplementationSelector private: const Context & context; std::vector implementations; - PerformanceStatistics statistics; + detail::PerformanceStatistics statistics; }; } From ef030349ff29c7550b9c3b2931cf4db57d9a6e11 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 26 May 2020 17:56:46 +0200 Subject: [PATCH 0189/1102] Add hashes to multitarget code, a lot of cosmetics --- src/Functions/CMakeLists.txt | 4 +- src/Functions/FunctionBinaryArithmetic.h | 81 +++++++++---------- src/Functions/FunctionStartsEndsWith.h | 21 +++-- src/Functions/FunctionsHashing.h | 42 +++++++++- src/Functions/FunctionsRandom.h | 15 +--- src/Functions/PerformanceAdaptors.h | 28 +++++-- src/Functions/RandXorshift.h | 15 ++-- src/Functions/TargetSpecific.h | 54 ++++++++----- src/Functions/generateUUIDv4.cpp | 2 + src/Functions/randConstant.cpp | 2 +- tests/performance/arithmetic.xml | 3 +- .../synthetic_hardware_benchmark.xml | 3 +- 12 files changed, 156 insertions(+), 114 deletions(-) diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 8c9cf159e304..2cc3208f6c41 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -85,9 +85,9 @@ endif() option(ENABLE_MULTITARGET_CODE "" ON) if (ENABLE_MULTITARGET_CODE) - add_definitions(-DUSE_MULTITARGET_CODE=1) + add_definitions(-DENABLE_MULTITARGET_CODE=1) else() - add_definitions(-DUSE_MULTITARGET_CODE=0) + add_definitions(-DENABLE_MULTITARGET_CODE=0) endif() target_link_libraries(clickhouse_functions PUBLIC "simdxorshift") diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index c311b8d5d0a4..9a5d610d2af3 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -102,65 +102,56 @@ struct BinaryOperationImplBase static void vectorVector(const A * __restrict a, const B * __restrict b, ResultType * __restrict c, size_t size) { - if constexpr (UseMultitargetCode) - { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); - } + #if USE_MULTITARGET_CODE + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); else - { TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); - } + #else + TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); + #endif } static void vectorConstant(const A * __restrict a, B b, ResultType * __restrict c, size_t size) { - if constexpr (UseMultitargetCode) - { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); - } + #if USE_MULTITARGET_CODE + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); else - { TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); - } + #else + TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); + #endif } static void constantVector(A a, const B * __restrict b, ResultType * __restrict c, size_t size) { - if constexpr (UseMultitargetCode) - { - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); - } + #if USE_MULTITARGET_CODE + if (IsArchSupported(TargetArch::AVX512F)) + TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX2)) + TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::AVX)) + TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); + else if (IsArchSupported(TargetArch::SSE42)) + TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); else - { TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); - } + #else + TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); + #endif } static ResultType constantConstant(A a, B b) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 76aa4530c990..b148653e1b3f 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -151,17 +151,16 @@ class FunctionStartsEndsWith : public TargetSpecific::Default::FunctionStartsEnd selector.registerImplementation>(); - if constexpr (UseMultitargetCode) - { - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - selector.registerImplementation>(); - } + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + #endif } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index 6f00981a22a0..4562b9001a94 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -615,8 +615,6 @@ class FunctionIntHash : public IFunction return name; } - static String getImplementationTag() { return ToString(BuildArch); } - size_t getNumberOfArguments() const override { return 1; } DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override @@ -661,11 +659,13 @@ class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash>(); - + + #if USE_MULTITARGET_CODE selector.registerImplementation>(); selector.registerImplementation>(); + #endif } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override @@ -682,12 +682,13 @@ class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash selector; }; +DECLARE_MULTITARGET_CODE( + template class FunctionAnyHash : public IFunction { public: static constexpr auto name = Impl::name; - static FunctionPtr create(const Context &) { return std::make_shared(); } private: using ToType = typename Impl::ReturnType; @@ -974,6 +975,39 @@ class FunctionAnyHash : public IFunction } }; +) // DECLARE_MULTITARGET_CODE + +template +class FunctionAnyHash : public TargetSpecific::Default::FunctionAnyHash +{ +public: + FunctionAnyHash(const Context & context) : selector(context) + { + selector.registerImplementation>(); + + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + selector.registerImplementation>(); + #endif + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + +private: + ImplementationSelector selector; +}; + struct URLHashImpl { diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 8cbe42862858..346c94e1d9fd 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -42,7 +42,6 @@ struct RandImpl { /// Fill memory with random data. The memory region must be 15-bytes padded. static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch); } }; ) // DECLARE_MULTITARGET_CODE @@ -58,11 +57,6 @@ class FunctionRandomImpl : public IFunction return name; } - static String getImplementationTag() - { - return RandImpl::getImplementationTag(); - } - bool isDeterministic() const override { return false; } bool isDeterministicInScopeOfQuery() const override { return false; } bool useDefaultImplementationForNulls() const override { return false; } @@ -102,11 +96,10 @@ class FunctionRandom : public FunctionRandomImpl>(); - if constexpr (UseMultitargetCode) - { - selector.registerImplementation>(); - } + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + #endif } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 9dbc2a68f860..1d4b6be61028 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -9,9 +9,9 @@ #include #include -/// This file contains Adaptors which help to combine several implementations of the function. -/// Adaptors check that implementation can be executed on the current platform and choose -/// that one which works faster according to previous runs. +/* This file contains helper class ImplementationSelector. It makes easier to combine + * several implementations of IFunction/IExecutableFunctionImpl. + */ namespace DB { @@ -120,9 +120,16 @@ namespace detail { /// If there is a variant with not enough statistics, always choose it. /// And in that case prefer variant with less number of invocations. - if (adjustedCount() < 2) - return adjustedCount() - 1 + running_count * 2; + if (adjustedCount() < 2) + { + // TODO(dakovalkov): rewrite it. + int all_count = adjustedCount() + running_count; + if (all_count < 3) + return all_count - 2; + else + return adjustedCount() + running_count * 100; + } return std::normal_distribution<>(mean(), sigma())(stat_rng); } }; @@ -142,6 +149,9 @@ namespace detail template constexpr bool has_implementation_tag = decltype(hasImplementationTagTest(std::declval()))::value; + /* Implementation tag is used to run specific implementation (for debug/testing purposes). + * It can be specified via static method ::getImplementationTag() in Function (optional). + */ template String getImplementationTag(TargetArch arch) { @@ -161,8 +171,9 @@ namespace detail * Example of usage: * * class MyDefaulImpl : public IFunction {...}; - * class MySecondImpl : public IFunction {...}; + * DECLARE_AVX2_SPECIFIC_CODE( * class MyAVX2Impl : public IFunction {...}; + * ) * * /// All methods but execute/executeImpl are usually not bottleneck, so just use them from * /// default implementation. @@ -172,8 +183,9 @@ namespace detail * /// Register all implementations in constructor. * /// There could be as many implementation for every target as you want. * selector.registerImplementation(); - * selector.registerImplementation(); - * selector.registreImplementation(); + * #if USE_MULTITARGET_CODE + * selector.registreImplementation(); + * #endif * } * * void executeImpl(...) override { diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 49655d637f2d..1d109adc087c 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -23,7 +23,7 @@ struct RandXorshiftImpl struct RandXorshiftImpl2 { static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch) + "_v2"; } + static String getImplementationTag() { return "v2"; } }; ) // DECLARE_MULTITARGET_CODE @@ -37,13 +37,12 @@ class FunctionRandomXorshift : public FunctionRandomImpl>(); - if constexpr (UseMultitargetCode) - { - selector.registerImplementation>(); - selector.registerImplementation>(); - } + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + selector.registerImplementation>(); + #endif } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/TargetSpecific.h b/src/Functions/TargetSpecific.h index 11dae939bbd9..ed9c0d3c2443 100644 --- a/src/Functions/TargetSpecific.h +++ b/src/Functions/TargetSpecific.h @@ -4,16 +4,22 @@ /* This file contains macros and helpers for writing platform-dependent code. * - * Macros DECLARE__SPECIFIC_CODE will wrap code inside them into the + * Macros DECLARE__SPECIFIC_CODE will wrap code inside it into the * namespace TargetSpecific:: and enable Arch-specific compile options. * Thus, it's allowed to call functions inside these namespaces only after * checking platform in runtime (see IsArchSupported() below). * + * If compiler is not gcc/clang or target isn't x86_64 or ENABLE_MULTITARGET_CODE + * was set to OFF in cmake, all code inside these macroses will be removed and + * USE_MUTLITARGE_CODE will be set to 0. Use #if USE_MUTLITARGE_CODE whenever you + * use anything from this namespaces. + * * For similarities there is a macros DECLARE_DEFAULT_CODE, which wraps code * into the namespace TargetSpecific::Default but dosn't specify any additional - * copile options. + * copile options. Functions and classes inside this macros are available regardless + * of USE_MUTLITARGE_CODE. * - * Example: + * Example of usage: * * DECLARE_DEFAULT_CODE ( * int funcImpl() { @@ -28,15 +34,17 @@ * ) // DECLARE_DEFAULT_CODE * * int func() { + * #if USE_MULTITARGET_CODE * if (IsArchSupported(TargetArch::AVX2)) * return TargetSpecifc::AVX2::funcImpl(); + * #endif * return TargetSpecifc::Default::funcImpl(); * } * * Sometimes code may benefit from compiling with different options. - * For these purposes use DECLARE_MULTITARGET_CODE macros. It will create several - * copies of the code and compile it with different options. These copies are - * available via TargetSpecifc namespaces described above. + * For these purposes use DECLARE_MULTITARGET_CODE macros. It will create a copy + * of the code for every supported target and compile it with different options. + * These copies are available via TargetSpecifc namespaces described above. * * Inside every TargetSpecific namespace there is a constexpr variable BuildArch, * which indicates the target platform for current code. @@ -50,16 +58,16 @@ * iteration_size = 2 * else if constexpr (BuildArch == TargetArch::AVX || BuildArch == TargetArch::AVX2) * iteration_size = 4; - * else if constexpr (BuildArch == TargetArch::AVX512) - * iteration_size = 8; * for (int i = 0; i < size; i += iteration_size) * ... * } * ) // DECLARE_MULTITARGET_CODE * - * // All 5 versions of func are available here. Use runtime detection to choose one. + * // All target-specific and default implementations are available here via + * TargetSpecific::::funcImpl. Use runtime detection to choose one. * - * If you want to write IFunction or IExecutableFuncionImpl with runtime dispatching, see PerformanceAdaptors.h. + * If you want to write IFunction or IExecutableFuncionImpl with several implementations + * see PerformanceAdaptors.h. */ namespace DB @@ -74,24 +82,24 @@ enum class TargetArch : UInt32 AVX512F = (1 << 3), }; -// Runtime detection. +/// Runtime detection. bool IsArchSupported(TargetArch arch); String ToString(TargetArch arch); -#if USE_MULTITARGET_CODE && defined(__GNUC__) && defined(__x86_64__) +#if ENABLE_MULTITARGET_CODE && defined(__GNUC__) && defined(__x86_64__) -constexpr bool UseMultitargetCode = true; +#define USE_MULTITARGET_CODE 1 #if defined(__clang__) # define BEGIN_AVX512F_SPECIFIC_CODE \ - _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2,avx512f\"))),apply_to=function)") + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx,avx2,avx512f\"))),apply_to=function)") # define BEGIN_AVX2_SPECIFIC_CODE \ - _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx,avx2\"))),apply_to=function)") + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx,avx2\"))),apply_to=function)") # define BEGIN_AVX_SPECIFIC_CODE \ - _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx,avx\"))),apply_to=function)") + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx\"))),apply_to=function)") # define BEGIN_SSE42_SPECIFIC_CODE \ - _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt,mmx\"))),apply_to=function)") + _Pragma("clang attribute push(__attribute__((target(\"sse,sse2,sse3,ssse3,sse4,popcnt\"))),apply_to=function)") # define END_TARGET_SPECIFIC_CODE \ _Pragma("clang attribute pop") @@ -102,16 +110,16 @@ constexpr bool UseMultitargetCode = true; #else # define BEGIN_AVX512F_SPECIFIC_CODE \ _Pragma("GCC push_options") \ - _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,avx512f,tune=native\")") + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx,avx2,avx512f,tune=native\")") # define BEGIN_AVX2_SPECIFIC_CODE \ _Pragma("GCC push_options") \ - _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native\")") + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx,avx2,tune=native\")") # define BEGIN_AVX_SPECIFIC_CODE \ _Pragma("GCC push_options") \ - _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native\")") + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,avx,tune=native\")") # define BEGIN_SSE42_SPECIFIC_CODE \ _Pragma("GCC push_options") \ - _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,tune=native\")") + _Pragma("GCC target(\"sse,sse2,sse3,ssse3,sse4,popcnt,tune=native\")") # define END_TARGET_SPECIFIC_CODE \ _Pragma("GCC pop_options") @@ -158,8 +166,10 @@ END_TARGET_SPECIFIC_CODE #else -constexpr bool UseMultitargetCode = false; +#define USE_MULTITARGET_CODE 0 +/* Multitarget code is disabled, just delete target-specific code. + */ #define DECLARE_SSE42_SPECIFIC_CODE(...) #define DECLARE_AVX_SPECIFIC_CODE(...) #define DECLARE_AVX2_SPECIFIC_CODE(...) diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index 04dd58775603..a205f853d2ab 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -66,8 +66,10 @@ class FunctionGenerateUUIDv4 : public TargetSpecific::Default::FunctionGenerateU selector.registerImplementation(); + #if USE_MULTITARGET_CODE selector.registerImplementation(); + #endif } void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override diff --git a/src/Functions/randConstant.cpp b/src/Functions/randConstant.cpp index 3eba5abf10d6..ebf2f752b66f 100644 --- a/src/Functions/randConstant.cpp +++ b/src/Functions/randConstant.cpp @@ -99,7 +99,7 @@ class RandomConstantOverloadResolver : public IFunctionOverloadResolverImpl argument_types.emplace_back(arguments.back().type); typename ColumnVector::Container vec_to(1); - // TODO(dakovalkov): Rewrite this workaround + TargetSpecific::Default::RandImpl::execute(reinterpret_cast(vec_to.data()), sizeof(ToType)); ToType value = vec_to[0]; diff --git a/tests/performance/arithmetic.xml b/tests/performance/arithmetic.xml index 45f0d62f227a..e56d35d43b99 100644 --- a/tests/performance/arithmetic.xml +++ b/tests/performance/arithmetic.xml @@ -1,4 +1,5 @@ - + 30000000000 diff --git a/tests/performance/synthetic_hardware_benchmark.xml b/tests/performance/synthetic_hardware_benchmark.xml index 2233bfeca8d8..2688c5a1aecf 100644 --- a/tests/performance/synthetic_hardware_benchmark.xml +++ b/tests/performance/synthetic_hardware_benchmark.xml @@ -1,4 +1,5 @@ - + 30000000000 From 71fabcedc4939b6ef917072d3e224de4bc65f6b8 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Tue, 26 May 2020 18:29:37 +0200 Subject: [PATCH 0190/1102] Fix test --- tests/performance/arithmetic.xml | 1 + tests/performance/synthetic_hardware_benchmark.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/performance/arithmetic.xml b/tests/performance/arithmetic.xml index e56d35d43b99..69b9bf30e45d 100644 --- a/tests/performance/arithmetic.xml +++ b/tests/performance/arithmetic.xml @@ -1,5 +1,6 @@ + 30000000000 diff --git a/tests/performance/synthetic_hardware_benchmark.xml b/tests/performance/synthetic_hardware_benchmark.xml index 2688c5a1aecf..deae39ab80fc 100644 --- a/tests/performance/synthetic_hardware_benchmark.xml +++ b/tests/performance/synthetic_hardware_benchmark.xml @@ -1,5 +1,6 @@ + 30000000000 From cdb353856dee0ef0cb39022cc4b6623469a92d15 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 13:39:48 +0200 Subject: [PATCH 0191/1102] remove vectorization from binary arithmetic --- src/Functions/FunctionBinaryArithmetic.h | 80 ++---------------------- tests/performance/arithmetic.xml | 4 +- 2 files changed, 6 insertions(+), 78 deletions(-) diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index 9a5d610d2af3..292f7da0475e 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -54,8 +54,11 @@ namespace ErrorCodes extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES; } -DECLARE_MULTITARGET_CODE( - +/** Arithmetic operations: +, -, *, /, %, + * intDiv (integer division) + * Bitwise operations: |, &, ^, ~. + * Etc. + */ template struct BinaryOperationImplBase { @@ -86,79 +89,6 @@ struct BinaryOperationImplBase } }; -) // DECLARE_MULTITARGET_CODE - - -/** Arithmetic operations: +, -, *, /, %, - * intDiv (integer division) - * Bitwise operations: |, &, ^, ~. - * Etc. - */ -template -struct BinaryOperationImplBase -{ - using ResultType = ResultType_; - static const constexpr bool allow_fixed_string = false; - - static void vectorVector(const A * __restrict a, const B * __restrict b, ResultType * __restrict c, size_t size) - { - #if USE_MULTITARGET_CODE - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorVector(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); - #else - TargetSpecific::Default::BinaryOperationImplBase::vectorVector(a, b, c, size); - #endif - } - - static void vectorConstant(const A * __restrict a, B b, ResultType * __restrict c, size_t size) - { - #if USE_MULTITARGET_CODE - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::vectorConstant(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); - #else - TargetSpecific::Default::BinaryOperationImplBase::vectorConstant(a, b, c, size); - #endif - } - - static void constantVector(A a, const B * __restrict b, ResultType * __restrict c, size_t size) - { - #if USE_MULTITARGET_CODE - if (IsArchSupported(TargetArch::AVX512F)) - TargetSpecific::AVX512F::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX2)) - TargetSpecific::AVX2::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::AVX)) - TargetSpecific::AVX::BinaryOperationImplBase::constantVector(a, b, c, size); - else if (IsArchSupported(TargetArch::SSE42)) - TargetSpecific::SSE42::BinaryOperationImplBase::constantVector(a, b, c, size); - else - TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); - #else - TargetSpecific::Default::BinaryOperationImplBase::constantVector(a, b, c, size); - #endif - } - - static ResultType constantConstant(A a, B b) - { - return Op::template apply(a, b); - } -}; template struct FixedStringOperationImpl diff --git a/tests/performance/arithmetic.xml b/tests/performance/arithmetic.xml index 69b9bf30e45d..45f0d62f227a 100644 --- a/tests/performance/arithmetic.xml +++ b/tests/performance/arithmetic.xml @@ -1,6 +1,4 @@ - - + 30000000000 From 13e1102f03e35dce5f29d9434ff84ee9dff3c605 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 13:46:47 +0200 Subject: [PATCH 0192/1102] Disable xorshift --- contrib/CMakeLists.txt | 1 - src/Functions/CMakeLists.txt | 3 --- src/Functions/RandXorshift.cpp | 3 +++ src/Functions/RandXorshift.h | 5 +++++ src/Functions/registerFunctionsRandom.cpp | 2 -- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 344a06f29b76..d122188ad0b6 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -27,7 +27,6 @@ add_subdirectory (murmurhash) add_subdirectory (replxx-cmake) add_subdirectory (ryu-cmake) add_subdirectory (unixodbc-cmake) -add_subdirectory (SIMDxorshift-cmake) add_subdirectory (poco-cmake) diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 2cc3208f6c41..e999955086e9 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -90,9 +90,6 @@ else() add_definitions(-DENABLE_MULTITARGET_CODE=0) endif() -target_link_libraries(clickhouse_functions PUBLIC "simdxorshift") -message(STATUS "Using SIMDXORSHIFT ${SIMDXORSHIFT_LIBRARY}") - add_subdirectory(GatherUtils) target_link_libraries(clickhouse_functions PRIVATE clickhouse_functions_gatherutils) diff --git a/src/Functions/RandXorshift.cpp b/src/Functions/RandXorshift.cpp index 9f1dded700ca..4fa2280861f8 100644 --- a/src/Functions/RandXorshift.cpp +++ b/src/Functions/RandXorshift.cpp @@ -1,3 +1,5 @@ +/// Disable xorshift +#if 0 #include #include #include @@ -162,3 +164,4 @@ void registerFunctionRandXorshift(FunctionFactory & factory) } } +#endif diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 1d109adc087c..8e068cf5dffe 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -1,5 +1,8 @@ #pragma once +/// disable xorshift +#if 0 + #include #include #include @@ -60,3 +63,5 @@ class FunctionRandomXorshift : public FunctionRandomImpl Date: Thu, 28 May 2020 13:48:56 +0200 Subject: [PATCH 0193/1102] Fix style issues --- src/Functions/FunctionsHashing.h | 6 +++--- src/Functions/PerformanceAdaptors.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index 4562b9001a94..7edb8937275c 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -659,7 +659,7 @@ class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash>(); - + #if USE_MULTITARGET_CODE selector.registerImplementation>(); @@ -670,9 +670,9 @@ class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash(context); diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index 1d4b6be61028..de321ee5605f 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -32,7 +32,7 @@ namespace detail if (size() == 1) return 0; - std::lock_guard guard(lock); + std::lock_guard guard(lock); size_t best = 0; double best_sample = data[0].sample(rng); @@ -57,8 +57,8 @@ namespace detail { if (size() == 1) return; - - std::lock_guard guard(lock); + + std::lock_guard guard(lock); data[id].complete(seconds, bytes); } From 43b836adc13e7a5556fce396ee7e6a47f10413d3 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 13:50:59 +0200 Subject: [PATCH 0194/1102] cosmetics --- src/Functions/FunctionStartsEndsWith.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index b148653e1b3f..30d6a1506201 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -41,11 +41,6 @@ class FunctionStartsEndsWith : public IFunction return name; } - static String getImplementationTag() - { - return ToString(BuildArch); - } - size_t getNumberOfArguments() const override { return 2; From 4c16f7a70ffe244f1e82f3ff06482288f212290a Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 15:01:00 +0200 Subject: [PATCH 0195/1102] cosmetics --- src/Functions/FunctionsRandom.cpp | 2 +- src/Functions/PerformanceAdaptors.h | 9 +-------- tests/performance/general_purpose_hashes.xml | 4 +++- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index 2c7b2e5f1f53..ced87d08cfa7 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include #include #include diff --git a/src/Functions/PerformanceAdaptors.h b/src/Functions/PerformanceAdaptors.h index de321ee5605f..bbe50d2e9941 100644 --- a/src/Functions/PerformanceAdaptors.h +++ b/src/Functions/PerformanceAdaptors.h @@ -122,14 +122,7 @@ namespace detail /// And in that case prefer variant with less number of invocations. if (adjustedCount() < 2) - { - // TODO(dakovalkov): rewrite it. - int all_count = adjustedCount() + running_count; - if (all_count < 3) - return all_count - 2; - else - return adjustedCount() + running_count * 100; - } + return adjustedCount() - 1 + running_count; return std::normal_distribution<>(mean(), sigma())(stat_rng); } }; diff --git a/tests/performance/general_purpose_hashes.xml b/tests/performance/general_purpose_hashes.xml index 31a1bd658353..ada1df439fe3 100644 --- a/tests/performance/general_purpose_hashes.xml +++ b/tests/performance/general_purpose_hashes.xml @@ -1,4 +1,6 @@ - + + gp_hash_func From 0f730b2ace141c5b17a1a76568bfd2aefd72e976 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 17:35:05 +0200 Subject: [PATCH 0196/1102] multitarget great circle --- src/Functions/greatCircleDistance.cpp | 45 +++++++++++++++++++++++-- tests/performance/great_circle_dist.xml | 4 ++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/Functions/greatCircleDistance.cpp b/src/Functions/greatCircleDistance.cpp index 238499f8def1..89337f83ddff 100644 --- a/src/Functions/greatCircleDistance.cpp +++ b/src/Functions/greatCircleDistance.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -153,6 +155,12 @@ enum class Method WGS84_METERS, }; +} + +DECLARE_MULTITARGET_CODE( + +namespace +{ template float distance(float lon1deg, float lat1deg, float lon2deg, float lat2deg) @@ -220,7 +228,6 @@ float distance(float lon1deg, float lat1deg, float lon2deg, float lat2deg) } - template class FunctionGeoDistance : public IFunction { @@ -230,8 +237,6 @@ class FunctionGeoDistance : public IFunction : ((method == Method::SPHERE_METERS) ? "greatCircleDistance" : "geoDistance"); - static FunctionPtr create(const Context &) { return std::make_shared>(); } - private: String getName() const override { return name; } size_t getNumberOfArguments() const override { return 4; } @@ -272,6 +277,40 @@ class FunctionGeoDistance : public IFunction } }; +) // DECLARE_MULTITARGET_CODE + +template +class FunctionGeoDistance : public TargetSpecific::Default::FunctionGeoDistance +{ +public: + explicit FunctionGeoDistance(const Context & context) : selector(context) + { + selector.registerImplementation>(); + + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + selector.registerImplementation>(); + selector.registerImplementation>(); + #endif + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared>(context); + } + +private: + ImplementationSelector selector; +}; void registerFunctionGeoDistance(FunctionFactory & factory) { diff --git a/tests/performance/great_circle_dist.xml b/tests/performance/great_circle_dist.xml index 13f9e6fde56f..a57097bcbe75 100644 --- a/tests/performance/great_circle_dist.xml +++ b/tests/performance/great_circle_dist.xml @@ -1,4 +1,6 @@ - + + SELECT count() FROM numbers(1000000) WHERE NOT ignore(greatCircleDistance((rand(1) % 360) * 1. - 180, (number % 150) * 1.2 - 90, (number % 360) + toFloat64(rand(2)) / 4294967296 - 180, (rand(3) % 180) * 1. - 90)) From b0537bf31e1de4e5f52a399ff877a2313c11d31b Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 17:38:07 +0200 Subject: [PATCH 0197/1102] Fix clang builds --- src/Functions/FunctionStartsEndsWith.h | 2 +- src/Functions/FunctionsHashing.h | 4 ++-- src/Functions/FunctionsRandom.cpp | 4 +++- src/Functions/FunctionsRandom.h | 2 +- src/Functions/RandXorshift.h | 2 +- src/Functions/generateUUIDv4.cpp | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Functions/FunctionStartsEndsWith.h b/src/Functions/FunctionStartsEndsWith.h index 30d6a1506201..69627eb2ead6 100644 --- a/src/Functions/FunctionStartsEndsWith.h +++ b/src/Functions/FunctionStartsEndsWith.h @@ -141,7 +141,7 @@ template class FunctionStartsEndsWith : public TargetSpecific::Default::FunctionStartsEndsWith { public: - FunctionStartsEndsWith(const Context & context) : selector(context) + explicit FunctionStartsEndsWith(const Context & context) : selector(context) { selector.registerImplementation>(); diff --git a/src/Functions/FunctionsHashing.h b/src/Functions/FunctionsHashing.h index 7edb8937275c..b4c87dd761a2 100644 --- a/src/Functions/FunctionsHashing.h +++ b/src/Functions/FunctionsHashing.h @@ -655,7 +655,7 @@ template class FunctionIntHash : public TargetSpecific::Default::FunctionIntHash { public: - FunctionIntHash(const Context & context) : selector(context) + explicit FunctionIntHash(const Context & context) : selector(context) { selector.registerImplementation>(); @@ -981,7 +981,7 @@ template class FunctionAnyHash : public TargetSpecific::Default::FunctionAnyHash { public: - FunctionAnyHash(const Context & context) : selector(context) + explicit FunctionAnyHash(const Context & context) : selector(context) { selector.registerImplementation>(); diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index ced87d08cfa7..fba44d458bba 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -4,7 +4,9 @@ #include #include #include -#include +#if USE_MULTITARGET_CODE +# include +#endif namespace DB { diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index 346c94e1d9fd..bc11f671c1b7 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -91,7 +91,7 @@ template class FunctionRandom : public FunctionRandomImpl { public: - FunctionRandom(const Context & context) : selector(context) + explicit FunctionRandom(const Context & context) : selector(context) { selector.registerImplementation>(); diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h index 8e068cf5dffe..c005d7377dd5 100644 --- a/src/Functions/RandXorshift.h +++ b/src/Functions/RandXorshift.h @@ -35,7 +35,7 @@ template class FunctionRandomXorshift : public FunctionRandomImpl { public: - FunctionRandomXorshift(const Context & context) : selector(context) + explicit FunctionRandomXorshift(const Context & context) : selector(context) { selector.registerImplementation>(); diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index a205f853d2ab..53113a772730 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -61,7 +61,7 @@ class FunctionGenerateUUIDv4 : public IFunction class FunctionGenerateUUIDv4 : public TargetSpecific::Default::FunctionGenerateUUIDv4 { public: - FunctionGenerateUUIDv4(const Context & context) : selector(context) + explicit FunctionGenerateUUIDv4(const Context & context) : selector(context) { selector.registerImplementation(); From 278592106cd29732df3474a1da4a01a4d3d16dd0 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Thu, 28 May 2020 18:21:23 +0200 Subject: [PATCH 0198/1102] cosmetics --- src/Functions/FunctionBinaryArithmetic.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index 292f7da0475e..30b6da8b6962 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -28,8 +28,6 @@ #include #include -#include - #if !defined(ARCADIA_BUILD) # include #endif @@ -54,11 +52,13 @@ namespace ErrorCodes extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES; } + /** Arithmetic operations: +, -, *, /, %, * intDiv (integer division) * Bitwise operations: |, &, ^, ~. * Etc. */ + template struct BinaryOperationImplBase { @@ -89,7 +89,6 @@ struct BinaryOperationImplBase } }; - template struct FixedStringOperationImpl { From 478ee2c185870bf9bd719abfb6951879b9a4e730 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 29 May 2020 06:54:18 +0200 Subject: [PATCH 0199/1102] delete SIMDxorshift --- .gitmodules | 3 - contrib/SIMDxorshift | 1 - contrib/SIMDxorshift-cmake/CMakeLists.txt | 12 -- src/Functions/RandXorshift.cpp | 167 ---------------------- src/Functions/RandXorshift.h | 67 --------- src/Functions/generateUUIDv4.cpp | 2 - 6 files changed, 252 deletions(-) delete mode 160000 contrib/SIMDxorshift delete mode 100644 contrib/SIMDxorshift-cmake/CMakeLists.txt delete mode 100644 src/Functions/RandXorshift.cpp delete mode 100644 src/Functions/RandXorshift.h diff --git a/.gitmodules b/.gitmodules index c14fef404579..7f5d1307a6e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -160,6 +160,3 @@ [submodule "contrib/fmtlib"] path = contrib/fmtlib url = https://github.com/fmtlib/fmt.git -[submodule "contrib/SIMDxorshift"] - path = contrib/SIMDxorshift - url = https://github.com/lemire/SIMDxorshift diff --git a/contrib/SIMDxorshift b/contrib/SIMDxorshift deleted file mode 160000 index 270eb8936c9b..000000000000 --- a/contrib/SIMDxorshift +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 270eb8936c9b4bd038c39f1783a8eba6b8f15b09 diff --git a/contrib/SIMDxorshift-cmake/CMakeLists.txt b/contrib/SIMDxorshift-cmake/CMakeLists.txt deleted file mode 100644 index 573173ff1b4a..000000000000 --- a/contrib/SIMDxorshift-cmake/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(SIMDXORSHIFT_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/SIMDxorshift/include") -set(SIMDXORSHIFT_SRC_DIR "${SIMDXORSHIFT_INCLUDE_DIR}/../src") -set(SIMDXORSHIFT_SRC - ${SIMDXORSHIFT_SRC_DIR}/xorshift128plus.c - ${SIMDXORSHIFT_SRC_DIR}/simdxorshift128plus.c -) - -set(SIMDXORSHIFT_LIBRARY "simdxorshift") - -add_library(${SIMDXORSHIFT_LIBRARY} ${SIMDXORSHIFT_SRC}) -target_include_directories(${SIMDXORSHIFT_LIBRARY} PUBLIC "${SIMDXORSHIFT_INCLUDE_DIR}") -target_compile_options(${SIMDXORSHIFT_LIBRARY} PRIVATE -mavx2) diff --git a/src/Functions/RandXorshift.cpp b/src/Functions/RandXorshift.cpp deleted file mode 100644 index 4fa2280861f8..000000000000 --- a/src/Functions/RandXorshift.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/// Disable xorshift -#if 0 -#include -#include -#include -#include -#include - -#include - -extern "C" -{ -#include -#include -} - -namespace DB -{ - -DECLARE_DEFAULT_CODE( - -void RandXorshiftImpl::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; - - xorshift128plus_key_s mykey; - - xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), - 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), - &mykey); - - constexpr int bytes_per_write = 8; - constexpr intptr_t mask = bytes_per_write - 1; - - // Process head to make output aligned. - unalignedStore(output, xorshift128plus(&mykey)); - output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); - - while (end - output > 0) - { - *reinterpret_cast(output) = xorshift128plus(&mykey); - output += bytes_per_write; - } -} - -) // DECLARE_DEFAULT_CODE - -DECLARE_AVX2_SPECIFIC_CODE( - -void RandXorshiftImpl::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; - - avx_xorshift128plus_key_t mykey; - avx_xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), - 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), - &mykey); - - constexpr int safe_overwrite = 15; /// How many bytes we can write behind the end. - constexpr int bytes_per_write = 32; - constexpr intptr_t mask = bytes_per_write - 1; - - if (size + safe_overwrite < bytes_per_write) - { - /// size <= 16. - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), - _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); - return; - } - - /// Process head to make output aligned. - _mm256_storeu_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); - output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); - - while ((end - output) + safe_overwrite >= bytes_per_write) - { - _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); - output += bytes_per_write; - } - - /// Process tail. (end - output) <= 16. - if ((end - output) > 0) - { - _mm_store_si128(reinterpret_cast<__m128i*>(output), - _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); - } -} - -) // DECLARE_AVX2_SPECIFIC_CODE - -DECLARE_AVX2_SPECIFIC_CODE( - -void RandXorshiftImpl2::execute(char * output, size_t size) -{ - if (size == 0) - return; - - char * end = output + size; - - avx_xorshift128plus_key_t mykey; - avx_xorshift128plus_init(0xe9ef384566799595ULL ^ reinterpret_cast(output), - 0xa321e1523f4f88c7ULL ^ reinterpret_cast(output), - &mykey); - - avx_xorshift128plus_key_t mykey2; - avx_xorshift128plus_init(0xdfe532a6b5a5eb2cULL ^ reinterpret_cast(output), - 0x21cdf6cd1e22bf9cULL ^ reinterpret_cast(output), - &mykey2); - - constexpr int safe_overwrite = 15; /// How many bytes we can write behind the end. - constexpr int bytes_per_write = 32; - constexpr intptr_t mask = bytes_per_write - 1; - - if (size + safe_overwrite < bytes_per_write) - { - /// size <= 16. - _mm_storeu_si128(reinterpret_cast<__m128i*>(output), - _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); - return; - } - - /// Process head to make output aligned. - _mm256_storeu_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); - output = reinterpret_cast((reinterpret_cast(output) | mask) + 1); - - while ((end - output) + safe_overwrite >= bytes_per_write * 2) - { - _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); - _mm256_store_si256(reinterpret_cast<__m256i*>(output + bytes_per_write), avx_xorshift128plus(&mykey2)); - output += bytes_per_write * 2; - } - - if ((end - output) + safe_overwrite >= bytes_per_write) - { - _mm256_store_si256(reinterpret_cast<__m256i*>(output), avx_xorshift128plus(&mykey)); - output += bytes_per_write; - } - - /// Process tail. (end - output) <= 16. - if ((end - output) > 0) - { - _mm_store_si128(reinterpret_cast<__m128i*>(output), - _mm256_extracti128_si256(avx_xorshift128plus(&mykey), 0)); - } -} - -) // DECLARE_AVX2_SPECIFIC_CODE - -struct NameRandXorshift { static constexpr auto name = "randxorshift"; }; -using FunctionRandXorshift = FunctionRandomXorshift; -struct NameRandXorshift64 { static constexpr auto name = "randxorshift64"; }; -using FunctionRandXorshift64 = FunctionRandomXorshift; - -void registerFunctionRandXorshift(FunctionFactory & factory) -{ - factory.registerFunction(); - factory.registerFunction(); -} - -} -#endif diff --git a/src/Functions/RandXorshift.h b/src/Functions/RandXorshift.h deleted file mode 100644 index c005d7377dd5..000000000000 --- a/src/Functions/RandXorshift.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -/// disable xorshift -#if 0 - -#include -#include -#include -#include - -#include -#include -#include - -namespace DB -{ - -DECLARE_MULTITARGET_CODE( - -struct RandXorshiftImpl -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return ToString(BuildArch); } -}; - -struct RandXorshiftImpl2 -{ - static void execute(char * output, size_t size); - static String getImplementationTag() { return "v2"; } -}; - -) // DECLARE_MULTITARGET_CODE - -template -class FunctionRandomXorshift : public FunctionRandomImpl -{ -public: - explicit FunctionRandomXorshift(const Context & context) : selector(context) - { - selector.registerImplementation>(); - - #if USE_MULTITARGET_CODE - selector.registerImplementation>(); - selector.registerImplementation>(); - #endif - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - selector.selectAndExecute(block, arguments, result, input_rows_count); - } - - static FunctionPtr create(const Context & context) - { - return std::make_shared>(context); - } - -private: - ImplementationSelector selector; -}; - -} - -#endif diff --git a/src/Functions/generateUUIDv4.cpp b/src/Functions/generateUUIDv4.cpp index 53113a772730..7dbb73c0cf34 100644 --- a/src/Functions/generateUUIDv4.cpp +++ b/src/Functions/generateUUIDv4.cpp @@ -21,8 +21,6 @@ class FunctionGenerateUUIDv4 : public IFunction return name; } - static String getImplementationTag() { return ToString(BuildArch); } - size_t getNumberOfArguments() const override { return 0; } DataTypePtr getReturnTypeImpl(const DataTypes &) const override From 1bf8940c18a19ff3fbc0b95cc56669bc9f4300da Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 29 May 2020 11:12:08 +0200 Subject: [PATCH 0200/1102] better randomFixedString and randomString --- src/Functions/randomFixedString.cpp | 45 +++++++++++++++++++-------- src/Functions/randomString.cpp | 47 +++++++++++++++++++++-------- 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/Functions/randomFixedString.cpp b/src/Functions/randomFixedString.cpp index 9fb7550346be..16b6726b5d10 100644 --- a/src/Functions/randomFixedString.cpp +++ b/src/Functions/randomFixedString.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -21,13 +23,12 @@ namespace ErrorCodes /* Generate random fixed string with fully random bytes (including zero). */ -class FunctionRandomFixedString : public IFunction +template +class FunctionRandomFixedStringImpl : public IFunction { public: static constexpr auto name = "randomFixedString"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - String getName() const override { return name; } bool isVariadic() const override { return false; } @@ -68,20 +69,40 @@ class FunctionRandomFixedString : public IFunction /// Fill random bytes. data_to.resize(total_size); - pcg64_fast rng(randomSeed()); /// TODO It is inefficient. We should use SIMD PRNG instead. - - auto * pos = data_to.data(); - auto * end = pos + data_to.size(); - while (pos < end) - { - unalignedStore(pos, rng()); - pos += sizeof(UInt64); // We have padding in column buffers that we can overwrite. - } + RandImpl::execute(reinterpret_cast(data_to.data()), total_size); block.getByPosition(result).column = std::move(col_to); } }; +class FunctionRandomFixedString : public FunctionRandomFixedStringImpl +{ +public: + explicit FunctionRandomFixedString(const Context & context) : selector(context) + { + selector.registerImplementation>(); + + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + #endif + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + +private: + ImplementationSelector selector; +}; + void registerFunctionRandomFixedString(FunctionFactory & factory) { factory.registerFunction(); diff --git a/src/Functions/randomString.cpp b/src/Functions/randomString.cpp index 4ea470f0a65e..5ed8e4595493 100644 --- a/src/Functions/randomString.cpp +++ b/src/Functions/randomString.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,13 +21,12 @@ namespace ErrorCodes /* Generate random string of specified length with fully random bytes (including zero). */ -class FunctionRandomString : public IFunction +template +class FunctionRandomStringImpl : public IFunction { public: static constexpr auto name = "randomString"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - String getName() const override { return name; } bool isVariadic() const override { return true; } @@ -83,18 +84,10 @@ class FunctionRandomString : public IFunction /// Fill random bytes. data_to.resize(offsets_to.back()); - pcg64_fast rng(randomSeed()); /// TODO It is inefficient. We should use SIMD PRNG instead. - - auto * pos = data_to.data(); - auto * end = pos + data_to.size(); - while (pos < end) - { - unalignedStore(pos, rng()); - pos += sizeof(UInt64); // We have padding in column buffers that we can overwrite. - } + RandImpl::execute(reinterpret_cast(data_to.data()), data_to.size()); /// Put zero bytes in between. - pos = data_to.data(); + auto * pos = data_to.data(); for (size_t row_num = 0; row_num < input_rows_count; ++row_num) pos[offsets_to[row_num] - 1] = 0; @@ -102,6 +95,34 @@ class FunctionRandomString : public IFunction } }; +class FunctionRandomString : public FunctionRandomStringImpl +{ +public: + explicit FunctionRandomString(const Context & context) : selector(context) + { + selector.registerImplementation>(); + + #if USE_MULTITARGET_CODE + selector.registerImplementation>(); + #endif + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + selector.selectAndExecute(block, arguments, result, input_rows_count); + } + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + +private: + ImplementationSelector selector; +}; + void registerFunctionRandomString(FunctionFactory & factory) { factory.registerFunction(); From 8390fcaa23ab7d78922c6b9817387fd0eea28178 Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 29 May 2020 11:18:39 +0200 Subject: [PATCH 0201/1102] Cosmetics --- src/Functions/FunctionsRandom.cpp | 1 + src/Functions/FunctionsRandom.h | 4 ++-- src/Functions/randomFixedString.cpp | 2 +- src/Functions/randomString.cpp | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Functions/FunctionsRandom.cpp b/src/Functions/FunctionsRandom.cpp index fba44d458bba..e77bab9c0a57 100644 --- a/src/Functions/FunctionsRandom.cpp +++ b/src/Functions/FunctionsRandom.cpp @@ -8,6 +8,7 @@ # include #endif + namespace DB { diff --git a/src/Functions/FunctionsRandom.h b/src/Functions/FunctionsRandom.h index bc11f671c1b7..b80ddb6f59e2 100644 --- a/src/Functions/FunctionsRandom.h +++ b/src/Functions/FunctionsRandom.h @@ -3,10 +3,10 @@ #include #include #include -#include - #include #include +#include + namespace DB { diff --git a/src/Functions/randomFixedString.cpp b/src/Functions/randomFixedString.cpp index 16b6726b5d10..669dc0849992 100644 --- a/src/Functions/randomFixedString.cpp +++ b/src/Functions/randomFixedString.cpp @@ -82,7 +82,7 @@ class FunctionRandomFixedString : public FunctionRandomFixedStringImpl>(); - + #if USE_MULTITARGET_CODE selector.registerImplementation>(); diff --git a/src/Functions/randomString.cpp b/src/Functions/randomString.cpp index 5ed8e4595493..df3278c38004 100644 --- a/src/Functions/randomString.cpp +++ b/src/Functions/randomString.cpp @@ -102,7 +102,7 @@ class FunctionRandomString : public FunctionRandomStringImpl>(); - + #if USE_MULTITARGET_CODE selector.registerImplementation>(); From e185f3d157295d33698d58975061eec382df47bf Mon Sep 17 00:00:00 2001 From: Dmitrii Kovalkov Date: Fri, 29 May 2020 11:23:22 +0200 Subject: [PATCH 0202/1102] Fix style issue --- src/Functions/greatCircleDistance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Functions/greatCircleDistance.cpp b/src/Functions/greatCircleDistance.cpp index 89337f83ddff..bff92d7738d3 100644 --- a/src/Functions/greatCircleDistance.cpp +++ b/src/Functions/greatCircleDistance.cpp @@ -287,7 +287,7 @@ class FunctionGeoDistance : public TargetSpecific::Default::FunctionGeoDistance< { selector.registerImplementation>(); - + #if USE_MULTITARGET_CODE selector.registerImplementation>(); From 0362bb2d2f54ec90c2a71a9f446d2aec41bf920a Mon Sep 17 00:00:00 2001 From: kssenii Date: Fri, 29 May 2020 16:04:44 +0000 Subject: [PATCH 0203/1102] Make connection between concurrent consumers shared - not private --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 15 +++- src/Storages/RabbitMQ/RabbitMQHandler.h | 4 +- .../ReadBufferFromRabbitMQConsumer.cpp | 72 +++++++------------ .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 14 ++-- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 24 ++++++- src/Storages/RabbitMQ/StorageRabbitMQ.h | 4 ++ 6 files changed, 76 insertions(+), 57 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index cde43862ede8..1f6e9ce1bb15 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -19,13 +19,26 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * , const char * message) } -void RabbitMQHandler::start() +void RabbitMQHandler::start(std::atomic & check_param) { + /* The object of this class is shared between concurrent consumers, who call this method repeatedly at the same time. + * But the loop should not be attempted to start if it is already running. Also note that the loop is blocking to + * the thread that has started it. + */ + std::lock_guard lock(mutex); + + /* The callback, which changes this variable, could have already been activated by another thread while we waited for the + * mutex to unlock (as it runs all active events on the connection). This means that there is no need to start event loop again. + */ + if (check_param) + return; + event_base_loop(evbase, EVLOOP_NONBLOCK); } void RabbitMQHandler::stop() { + std::lock_guard lock(mutex); event_base_loopbreak(evbase); } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index 5b8a08be548e..a70b08aba556 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -17,12 +17,14 @@ class RabbitMQHandler : public AMQP::LibEventHandler RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); void onError(AMQP::TcpConnection * connection, const char * message) override; - void start(); + void start(std::atomic & check_param); void stop(); private: event_base * evbase; Poco::Logger * log; + + std::mutex mutex; }; } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 945de989b575..d6da58504724 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -7,17 +7,12 @@ #include -enum -{ - Connection_setup_sleep = 200, - Connection_setup_retries_max = 1000 -}; - namespace DB { ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( - std::pair & parsed_address, + ChannelPtr consumer_channel_, + RabbitMQHandler & eventHandler_, const String & exchange_name_, const String & routing_key_, const size_t channel_id_, @@ -28,10 +23,8 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( const size_t num_queues_, const std::atomic & stopped_) : ReadBuffer(nullptr, 0) - , evbase(event_base_new()) - , eventHandler(evbase, log) - , connection(&eventHandler, - AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) + , consumer_channel(std::move(consumer_channel_)) + , eventHandler(eventHandler_) , exchange_name(exchange_name_) , routing_key(routing_key_) , channel_id(channel_id_) @@ -41,29 +34,9 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , hash_exchange(hash_exchange_) , num_queues(num_queues_) , stopped(stopped_) + , exchange_declared(false) + , false_param(false) { - /* It turned out to be very important to make a different connection each time the object of this class is created, - * because in case when num_consumers > 1 - inputStreams run asynchronously and if they share the same connection, - * then they also will share the same event loop. But it will mean that if one stream's consumer starts event loop, - * then it will run all callbacks on the connection - including other stream's consumer's callbacks - - * as a result local variables can be updated both by the current thread and in callbacks by another thread during - * event loop, which is blocking only to the thread that has started the loop. Therefore sharing the connection - * (== sharing event loop) results in occasional seg faults in case of asynchronous run of objects that share the connection. - */ - size_t cnt_retries = 0; - while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) - { - event_base_loop(evbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); - std::this_thread::sleep_for(std::chrono::milliseconds(Connection_setup_sleep)); - } - - if (!connection.ready()) - { - LOG_ERROR(log, "Cannot set up connection for consumer"); - } - - consumer_channel = std::make_shared(&connection); - messages.clear(); current = messages.begin(); @@ -79,7 +52,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() { - connection.close(); + consumer_channel->close(); messages.clear(); current = messages.begin(); @@ -139,7 +112,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) exchange_declared = true; } - bool bindings_created = false, bindings_error = false; + std::atomic bindings_created = false, bindings_error = false; consumer_channel->declareQueue(AMQP::exclusive) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) @@ -189,7 +162,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) while (!bindings_created && !bindings_error) { /// No need for timeouts as this event loop is blocking for the current thread and quits in case there are no active events - startEventLoop(); + startEventLoop(bindings_created); } } @@ -212,7 +185,7 @@ void ReadBufferFromRabbitMQConsumer::subscribeConsumer() void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) { - bool consumer_created = false, consumer_error = false; + std::atomic consumer_created = false, consumer_error = false; consumer_channel->consume(queue_name, AMQP::noack) .onSuccess([&](const std::string & /* consumer */) @@ -224,7 +197,6 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) { size_t message_size = message.bodySize(); - if (message_size && message.body() != nullptr) { String message_received = std::string(message.body(), message.body() + message_size); @@ -232,8 +204,10 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) if (row_delimiter != '\0') message_received += row_delimiter; - //LOG_TRACE(log, "Consumer " + consumerTag + " received the message " + message_received); - + /* Needed because this vector can be used at the same time by another thread in nextImpl() (below). + * So we lock mutex here and there so that they do not use it asynchronosly. + */ + std::lock_guard lock(mutex); received.push_back(message_received); } }) @@ -245,14 +219,15 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) while (!consumer_created && !consumer_error) { - startEventLoop(); + /// No need for timeouts as this event loop is blocking for the current thread and quits in case there are no active events + startEventLoop(consumer_created); } } -void ReadBufferFromRabbitMQConsumer::startEventLoop() +void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & check_param) { - eventHandler.start(); + eventHandler.start(check_param); } @@ -265,9 +240,8 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() { if (received.empty()) { - /* Run the onReceived callbacks to save the messages that have been received by now - */ - startEventLoop(); + /// Run the onReceived callbacks to save the messages that have been received by now + startEventLoop(false_param); } if (received.empty()) @@ -277,6 +251,12 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() } messages.clear(); + + /* Needed because this vector can be used at the same time by another thread in onReceived callback (above). + * So we lock mutex here and there so that they do not use it asynchronosly. + */ + std::lock_guard lock(mutex); + messages.swap(received); current = messages.begin(); } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 5e4318246a66..31babc5033f7 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -22,7 +22,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer public: ReadBufferFromRabbitMQConsumer( - std::pair & parsed_address, + ChannelPtr consumer_channel_, + RabbitMQHandler & eventHandler_, const String & exchange_name_, const String & routing_key_, const size_t channel_id_, @@ -42,10 +43,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer using Messages = std::vector; using Queues = std::vector; - event_base * evbase; - RabbitMQHandler eventHandler; - AMQP::TcpConnection connection; ChannelPtr consumer_channel; + RabbitMQHandler & eventHandler; const String & exchange_name; const String & routing_key; @@ -59,7 +58,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer bool allowed = true; const std::atomic & stopped; - bool exchange_declared = false; + std::atomic exchange_declared; + std::atomic false_param; const size_t num_queues; Queues queues; bool subscribed = false; @@ -69,12 +69,14 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer Messages messages; Messages::iterator current; + std::mutex mutex; + bool nextImpl() override; void initExchange(); void initQueueBindings(const size_t queue_id); void subscribe(const String & queue_name); - void startEventLoop(); + void startEventLoop(std::atomic & check_param); }; } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index fb20569200d1..ed486e8e7093 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -35,7 +35,9 @@ enum { - RESCHEDULE_WAIT = 500 + RESCHEDULE_WAIT = 500, + Connection_setup_sleep = 200, + Connection_setup_retries_max = 1000 }; namespace DB @@ -75,10 +77,26 @@ StorageRabbitMQ::StorageRabbitMQ( , log(&Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) , semaphore(0, num_consumers_) , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) + , evbase(event_base_new()) + , eventHandler(evbase, log) + , connection(&eventHandler, + AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) { - rabbitmq_context.makeQueryContext(); + size_t cnt_retries = 0; + while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) + { + event_base_loop(evbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); + std::this_thread::sleep_for(std::chrono::milliseconds(Connection_setup_sleep)); + } + + if (!connection.ready()) + { + LOG_ERROR(log, "Cannot set up connection for consumer"); + } + rabbitmq_context.makeQueryContext(); setColumns(columns_); + task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); task->deactivate(); @@ -184,7 +202,7 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() update_channel_id = true; return std::make_shared( - parsed_address, exchange_name, routing_key, next_channel_id, + std::make_shared(&connection), eventHandler, exchange_name, routing_key, next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index b334b48a301d..fc098b168f15 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -79,6 +79,10 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Poco::Logger * log; std::pair parsed_address; + event_base * evbase; + RabbitMQHandler eventHandler; + AMQP::TcpConnection connection; + Poco::Semaphore semaphore; std::mutex mutex; std::vector buffers; /// available buffers for RabbitMQ consumers From d9bb3ef91ba801f4752dde77032b43e892395e14 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 22:48:32 +0300 Subject: [PATCH 0204/1102] Add logging and adjust initialization --- base/daemon/BaseDaemon.cpp | 2 +- base/daemon/SentryWriter.cpp | 38 ++++++++++++++++++++++++++++++++++-- base/daemon/ya.make | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index a8a798275521..4fd5bfa1379c 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -532,7 +532,6 @@ void BaseDaemon::initialize(Application & self) } reloadConfiguration(); - SentryWriter::initialize(config()); /// This must be done before creation of any files (including logs). mode_t umask_num = 0027; @@ -658,6 +657,7 @@ void BaseDaemon::initialize(Application & self) void BaseDaemon::initializeTerminationAndSignalProcessing() { + SentryWriter::initialize(config()); std::set_terminate(terminate_handler); /// We want to avoid SIGPIPE when working with sockets and pipes, and just handle return value/errno instead. diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 7e2a95c83698..d5c2766cf214 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -1,7 +1,11 @@ #include +#include +#include + #include #include +#include #if !defined(ARCADIA_BUILD) # include "Common/config_version.h" #endif @@ -44,20 +48,45 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { "send_crash_reports.endpoint", "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277" ); + const std::string & temp_folder_path = config.getString( + "send_crash_reports.tmp_path", + config.getString("tmp_path", Poco::Path::temp()) + "sentry/" + ); + Poco::File(temp_folder_path).createDirectories(); + sentry_options_t * options = sentry_options_new(); sentry_options_set_release(options, VERSION_STRING); if (debug) { sentry_options_set_debug(options, 1); } - sentry_init(options); sentry_options_set_dsn(options, endpoint.c_str()); + sentry_options_set_database_path(options, temp_folder_path.c_str()); if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { sentry_options_set_environment(options, "prod"); } else { sentry_options_set_environment(options, "test"); } - initialized = true; + int init_status = sentry_init(options); + if (!init_status) + { + initialized = true; + LOG_INFO( + &Logger::get("SentryWriter"), + "Sending crash reports is initialized with {} endpoint and {} temp folder", + endpoint, + temp_folder_path + ); + } + else + { + LOG_WARNING(&Logger::get("SentryWriter"), "Sending crash reports failed to initialized with {} status", init_status); + } + + } + else + { + LOG_INFO(&Logger::get("SentryWriter"), "Sending crash reports is disabled"); } #endif } @@ -140,8 +169,13 @@ void SentryWriter::onFault( sentry_value_set_by_key(event, "threads", threads); + LOG_INFO(&Logger::get("SentryWriter"), "Sending crash report"); sentry_capture_event(event); shutdown(); } + else + { + LOG_INFO(&Logger::get("SentryWriter"), "Not sending crash report"); + } #endif } diff --git a/base/daemon/ya.make b/base/daemon/ya.make index 1c72af3ed53e..125417adca53 100644 --- a/base/daemon/ya.make +++ b/base/daemon/ya.make @@ -9,6 +9,7 @@ PEERDIR( SRCS( BaseDaemon.cpp GraphiteWriter.cpp + SentryWriter.cpp ) END() From 4ef322274d117ecb6d04f79c4f73d0447b961c64 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 22:53:16 +0300 Subject: [PATCH 0205/1102] Add integration test --- .gitignore | 1 + programs/server/config.xml | 6 +++ .../configs/config_send_crash_reports.xml | 8 ++++ .../test_send_crash_reports/http_server.py | 43 ++++++++++++++++++ .../test_send_crash_reports/test.py | 44 +++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 tests/integration/test_send_crash_reports/configs/config_send_crash_reports.xml create mode 100644 tests/integration/test_send_crash_reports/http_server.py create mode 100644 tests/integration/test_send_crash_reports/test.py diff --git a/.gitignore b/.gitignore index 6bd57911ac8e..afb4e67a1b82 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /build /build_* /build-* +/tests/venv /docs/build /docs/publish diff --git a/programs/server/config.xml b/programs/server/config.xml index e16af9d75d75..d07f20aa0e06 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -42,6 +42,12 @@ --> + + + + false + + 8123 9000 diff --git a/tests/integration/test_send_crash_reports/configs/config_send_crash_reports.xml b/tests/integration/test_send_crash_reports/configs/config_send_crash_reports.xml new file mode 100644 index 000000000000..10f559b00547 --- /dev/null +++ b/tests/integration/test_send_crash_reports/configs/config_send_crash_reports.xml @@ -0,0 +1,8 @@ + + + + true + true + http://6f33034cfe684dd7a3ab9875e57b1c8d@localhost:9500/5226277 + + diff --git a/tests/integration/test_send_crash_reports/http_server.py b/tests/integration/test_send_crash_reports/http_server.py new file mode 100644 index 000000000000..e3fa2e1cb576 --- /dev/null +++ b/tests/integration/test_send_crash_reports/http_server.py @@ -0,0 +1,43 @@ +import BaseHTTPServer + +RESULT_PATH = '/result.txt' + +class SentryHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_POST(self): + post_data = self.__read_and_decode_post_data() + with open(RESULT_PATH, 'w') as f: + if self.headers.get("content-type") != "application/x-sentry-envelope": + f.write("INCORRECT_CONTENT_TYPE") + elif self.headers.get("content-length") < 3000: + f.write("INCORRECT_CONTENT_LENGTH") + elif '"http://6f33034cfe684dd7a3ab9875e57b1c8d@localhost:9500/5226277"' not in post_data: + f.write('INCORRECT_POST_DATA') + else: + f.write("OK") + self.send_response(200) + + def __read_and_decode_post_data(self): + transfer_encoding = self.headers.get("transfer-Encoding") + decoded = "" + if transfer_encoding == "chunked": + while True: + s = self.rfile.readline() + chunk_length = int(s, 16) + if not chunk_length: + break + decoded += self.rfile.read(chunk_length) + self.rfile.readline() + else: + content_length = int(self.headers.get("content-length", 0)) + decoded = self.rfile.read(content_length) + return decoded + + +if __name__ == "__main__": + with open(RESULT_PATH, 'w') as f: + f.write("INITIAL_STATE") + httpd = BaseHTTPServer.HTTPServer(("localhost", 9500,), SentryHandler) + try: + httpd.serve_forever() + finally: + httpd.server_close() \ No newline at end of file diff --git a/tests/integration/test_send_crash_reports/test.py b/tests/integration/test_send_crash_reports/test.py new file mode 100644 index 000000000000..f9e95f953d0b --- /dev/null +++ b/tests/integration/test_send_crash_reports/test.py @@ -0,0 +1,44 @@ +import os +import time + +import pytest + +import helpers.cluster +import helpers.test_tools +import http_server + + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + + +@pytest.fixture(scope="module") +def started_node(): + cluster = helpers.cluster.ClickHouseCluster(__file__) + try: + node = cluster.add_instance("node", main_configs=[ + os.path.join(SCRIPT_DIR, "configs", "config_send_crash_reports.xml") + ]) + cluster.start() + yield node + finally: + cluster.shutdown() + + +def test_send_segfault(started_node,): + started_node.copy_file_to_container(os.path.join(SCRIPT_DIR, "http_server.py"), "/http_server.py") + started_node.exec_in_container(["bash", "-c", "python2 /http_server.py"], detach=True, user="root") + time.sleep(0.5) + started_node.exec_in_container(["bash", "-c", "pkill -11 clickhouse"], user="root") + + result = None + for attempt in range(1, 6): + time.sleep(0.25 * attempt) + result = started_node.exec_in_container(['cat', http_server.RESULT_PATH], user='root') + if result == 'OK': + break + elif result == 'INITIAL_STATE': + continue + elif result: + assert False, 'Unexpected state: ' + result + + assert result == 'OK', 'Crash report not sent' From 0386e526b2c7cbf13e017f5445ea79eb4f24f67a Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 23:03:59 +0300 Subject: [PATCH 0206/1102] grammar --- programs/server/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/server/config.xml b/programs/server/config.xml index d07f20aa0e06..6086fcd7b1d1 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -43,7 +43,7 @@ - + false From a84123195b7fe4677c417de9fe5483c5c283ec13 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 23:08:05 +0300 Subject: [PATCH 0207/1102] adjust comments --- base/daemon/CMakeLists.txt | 1 - base/daemon/SentryWriter.h | 1 + src/Common/config.h.in | 3 --- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt index 46fa4a0fe340..0b6a7188c839 100644 --- a/base/daemon/CMakeLists.txt +++ b/base/daemon/CMakeLists.txt @@ -9,5 +9,4 @@ target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickh if (USE_SENTRY) target_link_libraries (daemon PRIVATE curl) target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) -# target_include_directories (daemon SYSTEM BEFORE PRIVATE ${SENTRY_INCLUDE_DIR}) endif () \ No newline at end of file diff --git a/base/daemon/SentryWriter.h b/base/daemon/SentryWriter.h index ee45ae4f203a..0b3f1ddd2b76 100644 --- a/base/daemon/SentryWriter.h +++ b/base/daemon/SentryWriter.h @@ -7,6 +7,7 @@ #include +/// Sends crash reports to ClickHouse core developer team via https://sentry.io class SentryWriter { public: diff --git a/src/Common/config.h.in b/src/Common/config.h.in index 08fa03d659f9..ed818b53167c 100644 --- a/src/Common/config.h.in +++ b/src/Common/config.h.in @@ -9,9 +9,6 @@ #cmakedefine01 USE_BROTLI #cmakedefine01 USE_UNWIND #cmakedefine01 USE_OPENCL -<<<<<<< HEAD #cmakedefine01 USE_SENTRY -======= #cmakedefine01 USE_GRPC ->>>>>>> a4e40fb5f209539cfee6af5da7f27c1c96e02eac #cmakedefine01 CLICKHOUSE_SPLIT_BINARY From d0339413993371c37577ce513ecf0555683878b8 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 23:20:28 +0300 Subject: [PATCH 0208/1102] try to fix merge issues --- contrib/grpc | 2 +- contrib/jemalloc | 2 +- programs/server/Server.cpp | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/contrib/grpc b/contrib/grpc index c1d176528fd8..8aea4e168e78 160000 --- a/contrib/grpc +++ b/contrib/grpc @@ -1 +1 @@ -Subproject commit c1d176528fd8da9dd4066d16554bcd216d29033f +Subproject commit 8aea4e168e78f3eb9828080740fc8cb73d53bf79 diff --git a/contrib/jemalloc b/contrib/jemalloc index cd2931ad9bbd..ea6b3e973b47 160000 --- a/contrib/jemalloc +++ b/contrib/jemalloc @@ -1 +1 @@ -Subproject commit cd2931ad9bbd78208565716ab102e86d858c2fff +Subproject commit ea6b3e973b477b8061e0076bb257dbd7f3faa756 diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 77dc5305fa8d..ce1d35e65d40 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -59,13 +59,7 @@ #include #include #include -<<<<<<< HEAD -#include "MySQLHandlerFactory.h" -#include - -======= #include ->>>>>>> a4e40fb5f209539cfee6af5da7f27c1c96e02eac #if !defined(ARCADIA_BUILD) # include "config_core.h" From b6e4a2ec61c928a433037fefa0657df7ebf8b8ac Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 29 May 2020 23:21:53 +0300 Subject: [PATCH 0209/1102] one more merge issue --- contrib/cppkafka | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cppkafka b/contrib/cppkafka index 9b184d881c15..f555ee36aaa7 160000 --- a/contrib/cppkafka +++ b/contrib/cppkafka @@ -1 +1 @@ -Subproject commit 9b184d881c15cc50784b28688c7c99d3d764db24 +Subproject commit f555ee36aaa74d17ca0dab3ce472070a610b2966 From 69dedcbe21bd323f3d87508ba78f36b587a7dff5 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 00:28:55 +0300 Subject: [PATCH 0210/1102] Move sending crash reports below logging --- base/daemon/BaseDaemon.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index 4fd5bfa1379c..72da19842872 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -249,7 +249,6 @@ class SignalListener : public Poco::Runnable UInt32 thread_num, const std::string & query_id) const { - SentryWriter::onFault(sig, info, context, stack_trace); LOG_FATAL(log, "########################################"); { @@ -282,6 +281,9 @@ class SignalListener : public Poco::Runnable /// Write symbolized stack trace line by line for better grep-ability. stack_trace.toStringEveryLine([&](const std::string & s) { LOG_FATAL(log, s); }); + + /// Send crash report if configured + SentryWriter::onFault(sig, info, context, stack_trace); } }; From f88b85625a44cbcb1628dc76283567c6fceeedd7 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 00:36:47 +0300 Subject: [PATCH 0211/1102] style --- base/daemon/SentryWriter.cpp | 71 +++++++++++++++++------------------- src/Common/StackTrace.cpp | 52 +++++++++++++------------- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index d5c2766cf214..7bbf3c62e975 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -3,35 +3,38 @@ #include #include -#include #include #include #if !defined(ARCADIA_BUILD) -# include "Common/config_version.h" +# include "Common/config_version.h" +# include #endif #if USE_SENTRY -#include +# include #endif -namespace { - static bool initialized = false; +namespace +{ +static bool initialized = false; - void setExtras() { +void setExtras() +{ #if USE_SENTRY - sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); - sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); - sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); - sentry_set_extra("version_revision", sentry_value_new_int32(VERSION_REVISION)); - sentry_set_extra("version_major", sentry_value_new_int32(VERSION_MAJOR)); - sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); - sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); + sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); + sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); + sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); + sentry_set_extra("version_revision", sentry_value_new_int32(VERSION_REVISION)); + sentry_set_extra("version_major", sentry_value_new_int32(VERSION_MAJOR)); + sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); + sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); #endif - } +} } -void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { +void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) +{ #if USE_SENTRY bool enabled = false; bool debug = config.getBool("send_crash_reports.debug", false); @@ -44,14 +47,10 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { } if (enabled) { - const std::string & endpoint = config.getString( - "send_crash_reports.endpoint", - "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277" - ); - const std::string & temp_folder_path = config.getString( - "send_crash_reports.tmp_path", - config.getString("tmp_path", Poco::Path::temp()) + "sentry/" - ); + const std::string & endpoint + = config.getString("send_crash_reports.endpoint", "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277"); + const std::string & temp_folder_path + = config.getString("send_crash_reports.tmp_path", config.getString("tmp_path", Poco::Path::temp()) + "sentry/"); Poco::File(temp_folder_path).createDirectories(); sentry_options_t * options = sentry_options_new(); @@ -62,9 +61,12 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { } sentry_options_set_dsn(options, endpoint.c_str()); sentry_options_set_database_path(options, temp_folder_path.c_str()); - if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) { + if (strstr(VERSION_DESCRIBE, "-stable") || strstr(VERSION_DESCRIBE, "-lts")) + { sentry_options_set_environment(options, "prod"); - } else { + } + else + { sentry_options_set_environment(options, "test"); } int init_status = sentry_init(options); @@ -75,14 +77,12 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { &Logger::get("SentryWriter"), "Sending crash reports is initialized with {} endpoint and {} temp folder", endpoint, - temp_folder_path - ); + temp_folder_path); } else { LOG_WARNING(&Logger::get("SentryWriter"), "Sending crash reports failed to initialized with {} status", init_status); } - } else { @@ -91,20 +91,17 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { #endif } -void SentryWriter::shutdown() { +void SentryWriter::shutdown() +{ #if USE_SENTRY - if (initialized) { + if (initialized) + { sentry_shutdown(); } #endif } -void SentryWriter::onFault( - int sig, - const siginfo_t & info, - const ucontext_t & context, - const StackTrace & stack_trace - ) +void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & context, const StackTrace & stack_trace) { #if USE_SENTRY if (initialized) @@ -178,4 +175,4 @@ void SentryWriter::onFault( LOG_INFO(&Logger::get("SentryWriter"), "Not sending crash report"); } #endif -} +} \ No newline at end of file diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 5cc8c43a27a4..2fd554fd0083 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -1,12 +1,12 @@ #include +#include #include #include -#include #include +#include #include #include -#include #include #include @@ -26,8 +26,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext std::stringstream error; switch (sig) { - case SIGSEGV: - { + case SIGSEGV: { /// Print info about address and reason. if (nullptr == info.si_addr) error << "Address: NULL pointer."; @@ -59,8 +58,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGBUS: - { + case SIGBUS: { switch (info.si_code) { case BUS_ADRALN: @@ -92,8 +90,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGILL: - { + case SIGILL: { switch (info.si_code) { case ILL_ILLOPC: @@ -127,8 +124,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGFPE: - { + case SIGFPE: { switch (info.si_code) { case FPE_INTDIV: @@ -162,8 +158,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGTSTP: - { + case SIGTSTP: { error << "This is a signal used for debugging purposes by the user."; break; } @@ -176,13 +171,13 @@ static void * getCallerAddress(const ucontext_t & context) { #if defined(__x86_64__) /// Get the address at the time the signal was raised from the RIP (x86-64) -#if defined(__FreeBSD__) +# if defined(__FreeBSD__) return reinterpret_cast(context.uc_mcontext.mc_rip); -#elif defined(__APPLE__) +# elif defined(__APPLE__) return reinterpret_cast(context.uc_mcontext->__ss.__rip); -#else +# else return reinterpret_cast(context.uc_mcontext.gregs[REG_RIP]); -#endif +# endif #elif defined(__aarch64__) return reinterpret_cast(context.uc_mcontext.pc); #else @@ -197,7 +192,8 @@ static void symbolize(const void * const * frame_pointers, size_t offset, size_t const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); std::unordered_map dwarfs; - for (size_t i = 0; i < offset; ++i) { + for (size_t i = 0; i < offset; ++i) + { frames.value()[i].virtual_addr = frame_pointers[i]; } @@ -217,7 +213,8 @@ static void symbolize(const void * const * frame_pointers, size_t offset, size_t auto dwarf_it = dwarfs.try_emplace(object->name, *object->elf).first; DB::Dwarf::LocationInfo location; - if (dwarf_it->second.findAddress(uintptr_t(current_frame.physical_addr), location, DB::Dwarf::LocationInfoMode::FAST)) { + if (dwarf_it->second.findAddress(uintptr_t(current_frame.physical_addr), location, DB::Dwarf::LocationInfoMode::FAST)) + { current_frame.file = location.file.toString(); current_frame.line = location.line; } @@ -239,8 +236,9 @@ static void symbolize(const void * const * frame_pointers, size_t offset, size_t current_frame.symbol = "?"; } } -# else - for (size_t i = 0; i < size; ++i) { +#else + for (size_t i = 0; i < size; ++i) + { frames.value()[i].virtual_addr = frame_pointers[i]; } UNUSED(offset); @@ -308,14 +306,16 @@ const StackTrace::FramePointers & StackTrace::getFramePointers() const const StackTrace::Frames & StackTrace::getFrames() const { - if (!frames.has_value()) { + if (!frames.has_value()) + { frames = {{}}; symbolize(frame_pointers.data(), offset, size, frames); } return frames; } -static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function callback) +static void +toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function callback) { if (size == 0) return callback(""); @@ -324,7 +324,7 @@ static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offs for (size_t i = offset; i < size; ++i) { - const StackTrace::Frame& current_frame = frames.value()[i]; + const StackTrace::Frame & current_frame = frames.value()[i]; out << i << ". "; if (current_frame.file.has_value() && current_frame.line.has_value()) @@ -338,7 +338,8 @@ static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offs } out << " @ " << current_frame.physical_addr; - if (current_frame.object.has_value()) { + if (current_frame.object.has_value()) + { out << " in " << current_frame.object.value(); } @@ -362,7 +363,8 @@ void StackTrace::toStringEveryLine(std::function call toStringEveryLineImpl(getFrames(), offset, size, std::move(callback)); } -void StackTrace::resetFrames() { +void StackTrace::resetFrames() +{ frames.reset(); } From 444026494f9383d465ab9b9611c8bfc935661d85 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 00:52:49 +0300 Subject: [PATCH 0212/1102] brief docs --- .../settings.md | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index 5961c7012835..a103473a4ea0 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -307,11 +307,11 @@ Logging settings. Keys: -- level – Logging level. Acceptable values: `trace`, `debug`, `information`, `warning`, `error`. -- log – The log file. Contains all the entries according to `level`. -- errorlog – Error log file. -- size – Size of the file. Applies to `log`and`errorlog`. Once the file reaches `size`, ClickHouse archives and renames it, and creates a new log file in its place. -- count – The number of archived log files that ClickHouse stores. +- `level` – Logging level. Acceptable values: `trace`, `debug`, `information`, `warning`, `error`. +- `log` – The log file. Contains all the entries according to `level`. +- `errorlog` – Error log file. +- `size` – Size of the file. Applies to `log`and`errorlog`. Once the file reaches `size`, ClickHouse archives and renames it, and creates a new log file in its place. +- `count` – The number of archived log files that ClickHouse stores. **Example** @@ -348,6 +348,27 @@ Keys: Default value: `LOG_USER` if `address` is specified, `LOG_DAEMON otherwise.` - format – Message format. Possible values: `bsd` and `syslog.` +## send_crash_reports {#server_configuration_parameters-logger} + +Settings for opt-in sending crash reports to the ClickHouse core developers team via [Sentry](https://sentry.io). +Enabling it, especially in pre-production environments, is strongly appreciated. + +Keys: + +- `enabled` – Boolean flag to enable the feature. Set to `true` to allow sending crash reports. +- `endpoint` – Overrides the Sentry endpoint. +- `debug` - Sets the Sentry client into debug mode. +- `tmp_path` - Filesystem path for temporary crash report state. + + +**Recommended way to use** + +``` xml + + true + +``` + ## macros {#macros} Parameter substitutions for replicated tables. From 95ca1c648da4e95ec1cb252afd6e79216f4f5aec Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 10:59:43 +0300 Subject: [PATCH 0213/1102] fix __msan_unpoison --- src/Common/StackTrace.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 2fd554fd0083..e38bfa25dffe 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -378,13 +378,13 @@ std::string StackTrace::toString() const return func_cached(frame_pointers.data(), offset, size); } -std::string StackTrace::toString(void ** frame_pointers, size_t offset, size_t size) +std::string StackTrace::toString(void ** frame_pointers_, size_t offset, size_t size) { - __msan_unpoison(frames_, size * sizeof(*frames_)); + __msan_unpoison(frame_pointers_, size * sizeof(*frame_pointers_)); StackTrace::FramePointers frame_pointers_copy{}; for (size_t i = 0; i < size; ++i) - frame_pointers_copy[i] = frame_pointers[i]; + frame_pointers_copy[i] = frame_pointers_[i]; static SimpleCache func_cached; return func_cached(frame_pointers_copy.data(), offset, size); From 6dfe44f437c393ded72f8859ca16d9625b8fbb53 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 11:01:15 +0300 Subject: [PATCH 0214/1102] style --- src/Common/StackTrace.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index e38bfa25dffe..aa78ab62f9b4 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -26,7 +26,8 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext std::stringstream error; switch (sig) { - case SIGSEGV: { + case SIGSEGV: + { /// Print info about address and reason. if (nullptr == info.si_addr) error << "Address: NULL pointer."; @@ -58,7 +59,8 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGBUS: { + case SIGBUS: + { switch (info.si_code) { case BUS_ADRALN: @@ -90,7 +92,8 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGILL: { + case SIGILL: + { switch (info.si_code) { case ILL_ILLOPC: @@ -124,7 +127,8 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGFPE: { + case SIGFPE: + { switch (info.si_code) { case FPE_INTDIV: @@ -158,7 +162,8 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext break; } - case SIGTSTP: { + case SIGTSTP: + { error << "This is a signal used for debugging purposes by the user."; break; } From 77d8c9bacae6b833a28a85ab45442355c0f4b2df Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 11:02:13 +0300 Subject: [PATCH 0215/1102] Add anonymize option and version tag --- base/daemon/SentryWriter.cpp | 17 +++++++++++++---- cmake/version.cmake | 1 + .../server-configuration-parameters/settings.md | 1 + src/Common/config_version.h.in | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 7bbf3c62e975..878ce6548aac 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -18,10 +18,16 @@ namespace { static bool initialized = false; +static bool anonymize = false; void setExtras() { #if USE_SENTRY + if (!anonymize) + { + sentry_set_extra("server_name", sentry_value_new_string(getFQDNOrHostName().c_str())); + } + sentry_set_tag("version", VERSION_STRING_SHORT); sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); @@ -69,15 +75,19 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { sentry_options_set_environment(options, "test"); } + int init_status = sentry_init(options); if (!init_status) { initialized = true; + anonymize = config.getBool("send_crash_reports.anonymize", false); + const std::string& anonymize_status = anonymize ? " (anonymized)" : ""; LOG_INFO( &Logger::get("SentryWriter"), - "Sending crash reports is initialized with {} endpoint and {} temp folder", + "Sending crash reports is initialized with {} endpoint and {} temp folder{}", endpoint, - temp_folder_path); + temp_folder_path, + anonymize_status); } else { @@ -109,7 +119,6 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c const std::string & error_message = signalToErrorMessage(sig, info, context); sentry_value_t event = sentry_value_new_message_event(SENTRY_LEVEL_FATAL, "fault", error_message.c_str()); sentry_set_tag("signal", strsignal(sig)); - sentry_set_tag("server_name", getFQDNOrHostName().c_str()); sentry_set_extra("signal_number", sentry_value_new_int32(sig)); setExtras(); @@ -175,4 +184,4 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c LOG_INFO(&Logger::get("SentryWriter"), "Not sending crash report"); } #endif -} \ No newline at end of file +} diff --git a/cmake/version.cmake b/cmake/version.cmake index eea17f68c47c..963f291c0f30 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -14,6 +14,7 @@ endif () set (VERSION_NAME "${PROJECT_NAME}") set (VERSION_FULL "${VERSION_NAME} ${VERSION_STRING}") set (VERSION_SO "${VERSION_STRING}") +set (VERSION_STRING_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}") math (EXPR VERSION_INTEGER "${VERSION_PATCH} + ${VERSION_MINOR}*1000 + ${VERSION_MAJOR}*1000000") diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index a103473a4ea0..ba8f3df9ad0d 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -357,6 +357,7 @@ Keys: - `enabled` – Boolean flag to enable the feature. Set to `true` to allow sending crash reports. - `endpoint` – Overrides the Sentry endpoint. +- `anonymize` - Avoid attaching the server hostname to crash report. - `debug` - Sets the Sentry client into debug mode. - `tmp_path` - Filesystem path for temporary crash report state. diff --git a/src/Common/config_version.h.in b/src/Common/config_version.h.in index bc90e63e39c0..c3c0c6df87b0 100644 --- a/src/Common/config_version.h.in +++ b/src/Common/config_version.h.in @@ -20,6 +20,7 @@ #cmakedefine VERSION_MINOR @VERSION_MINOR@ #cmakedefine VERSION_PATCH @VERSION_PATCH@ #cmakedefine VERSION_STRING "@VERSION_STRING@" +#cmakedefine VERSION_STRING_SHORT "@VERSION_STRING_SHORT@" #cmakedefine VERSION_OFFICIAL "@VERSION_OFFICIAL@" #cmakedefine VERSION_FULL "@VERSION_FULL@" #cmakedefine VERSION_DESCRIBE "@VERSION_DESCRIBE@" From d154415a5bedf24a0217306c1d7798b718a2995a Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 11:13:04 +0300 Subject: [PATCH 0216/1102] adjust comments --- .../en/operations/server-configuration-parameters/settings.md | 4 +++- programs/server/config.xml | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index ba8f3df9ad0d..3dc68e7fa6a2 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -351,7 +351,9 @@ Keys: ## send_crash_reports {#server_configuration_parameters-logger} Settings for opt-in sending crash reports to the ClickHouse core developers team via [Sentry](https://sentry.io). -Enabling it, especially in pre-production environments, is strongly appreciated. +Enabling it, especially in pre-production environments, is greatly appreciated. + +The server will need an access to public Internet for this feature to be functioning properly. Keys: diff --git a/programs/server/config.xml b/programs/server/config.xml index 6086fcd7b1d1..d8d75222bc03 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -46,6 +46,8 @@ false + + false From 52f7b9545b17304aa8e23373b77ab620fb338d50 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 11:24:21 +0300 Subject: [PATCH 0217/1102] Add http_proxy option --- base/daemon/SentryWriter.cpp | 6 ++++++ .../operations/server-configuration-parameters/settings.md | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 878ce6548aac..b2b1c69af8ca 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -76,6 +76,12 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) sentry_options_set_environment(options, "test"); } + const std::string & http_proxy = config.getString("send_crash_reports.http_proxy", ""); + if (!http_proxy.empty()) + { + sentry_options_set_http_proxy(options, http_proxy.c_str()); + } + int init_status = sentry_init(options); if (!init_status) { diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index 3dc68e7fa6a2..194293d5a194 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -359,7 +359,8 @@ Keys: - `enabled` – Boolean flag to enable the feature. Set to `true` to allow sending crash reports. - `endpoint` – Overrides the Sentry endpoint. -- `anonymize` - Avoid attaching the server hostname to crash report. +- `anonymize` - Avoid attaching the server hostname to crash report. +- `http_proxy` - Configure HTTP proxy for sending crash reports. - `debug` - Sets the Sentry client into debug mode. - `tmp_path` - Filesystem path for temporary crash report state. From be94d8454dcefa117d43a8d96a01e4164be4ea51 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 30 May 2020 13:54:57 +0300 Subject: [PATCH 0218/1102] fix Arcadia build --- base/daemon/SentryWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index b2b1c69af8ca..2fd846b720a6 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -11,7 +11,7 @@ #endif #if USE_SENTRY -# include +# include // Y_IGNORE #endif From b4b5c90343be4391404aaae381fa96590a0bbf32 Mon Sep 17 00:00:00 2001 From: MovElb Date: Sat, 30 May 2020 20:04:02 +0300 Subject: [PATCH 0219/1102] squash --- programs/server/PostgreSQLHandler.cpp | 306 ++++++ programs/server/PostgreSQLHandler.h | 75 ++ programs/server/PostgreSQLHandlerFactory.cpp | 28 + programs/server/PostgreSQLHandlerFactory.h | 29 + programs/server/Server.cpp | 1 + src/Common/CurrentMetrics.cpp | 2 +- src/Core/PostgreSQLProtocol.cpp | 50 + src/Core/PostgreSQLProtocol.h | 914 ++++++++++++++++++ src/Core/ya.make | 1 + src/Formats/FormatFactory.cpp | 2 + src/Formats/FormatFactory.h | 1 + src/IO/WriteHelpers.h | 20 + .../Formats/Impl/PostgreSQLOutputFormat.cpp | 79 ++ .../Formats/Impl/PostgreSQLOutputFormat.h | 33 + src/Processors/ya.make | 1 + .../test_postgresql_protocol/__init__.py | 0 .../clients/java/0.reference | 15 + .../clients/java/Dockerfile | 18 + .../clients/java/Test.java | 83 ++ .../clients/java/docker_compose.yml | 8 + .../clients/psql/docker_compose.yml | 14 + .../configs/config.xml | 35 + .../configs/dhparam.pem | 25 + .../configs/server.crt | 18 + .../configs/server.key | 28 + .../configs/users.xml | 13 + .../test_postgresql_protocol/test.py | 148 +++ 27 files changed, 1946 insertions(+), 1 deletion(-) create mode 100644 programs/server/PostgreSQLHandler.cpp create mode 100644 programs/server/PostgreSQLHandler.h create mode 100644 programs/server/PostgreSQLHandlerFactory.cpp create mode 100644 programs/server/PostgreSQLHandlerFactory.h create mode 100644 src/Core/PostgreSQLProtocol.cpp create mode 100644 src/Core/PostgreSQLProtocol.h create mode 100644 src/Processors/Formats/Impl/PostgreSQLOutputFormat.cpp create mode 100644 src/Processors/Formats/Impl/PostgreSQLOutputFormat.h create mode 100644 tests/integration/test_postgresql_protocol/__init__.py create mode 100644 tests/integration/test_postgresql_protocol/clients/java/0.reference create mode 100644 tests/integration/test_postgresql_protocol/clients/java/Dockerfile create mode 100644 tests/integration/test_postgresql_protocol/clients/java/Test.java create mode 100644 tests/integration/test_postgresql_protocol/clients/java/docker_compose.yml create mode 100644 tests/integration/test_postgresql_protocol/clients/psql/docker_compose.yml create mode 100644 tests/integration/test_postgresql_protocol/configs/config.xml create mode 100644 tests/integration/test_postgresql_protocol/configs/dhparam.pem create mode 100644 tests/integration/test_postgresql_protocol/configs/server.crt create mode 100644 tests/integration/test_postgresql_protocol/configs/server.key create mode 100644 tests/integration/test_postgresql_protocol/configs/users.xml create mode 100644 tests/integration/test_postgresql_protocol/test.py diff --git a/programs/server/PostgreSQLHandler.cpp b/programs/server/PostgreSQLHandler.cpp new file mode 100644 index 000000000000..843135ed3a1d --- /dev/null +++ b/programs/server/PostgreSQLHandler.cpp @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include "PostgreSQLHandler.h" +#include +#include + +#if !defined(ARCADIA_BUILD) +# include +#endif + +#if USE_SSL +# include +# include +#endif + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int SYNTAX_ERROR; +} + +PostgreSQLHandler::PostgreSQLHandler( + const Poco::Net::StreamSocket & socket_, + IServer & server_, + bool ssl_enabled_, + Int32 connection_id_, + std::vector> & auth_methods_) + : Poco::Net::TCPServerConnection(socket_) + , server(server_) + , connection_context(server.context()) + , ssl_enabled(ssl_enabled_) + , connection_id(connection_id_) + , authentication_manager(auth_methods_) +{ + changeIO(socket()); +} + +void PostgreSQLHandler::changeIO(Poco::Net::StreamSocket & socket) +{ + in = std::make_shared(socket); + out = std::make_shared(socket); + message_transport = std::make_shared(in.get(), out.get()); +} + +void PostgreSQLHandler::run() +{ + connection_context.makeSessionContext(); + connection_context.setDefaultFormat("PostgreSQLWire"); + + try + { + if (!startUp()) + return; + + while (true) + { + message_transport->send(PostgreSQLProtocol::Messaging::ReadyForQuery(), true); + PostgreSQLProtocol::Messaging::FrontMessageType message_type = message_transport->receiveMessageType(); + + switch (message_type) + { + case PostgreSQLProtocol::Messaging::FrontMessageType::QUERY: + processQuery(); + break; + case PostgreSQLProtocol::Messaging::FrontMessageType::TERMINATE: + LOG_INFO(log, "Client closed the connection"); + return; + case PostgreSQLProtocol::Messaging::FrontMessageType::PARSE: + case PostgreSQLProtocol::Messaging::FrontMessageType::BIND: + case PostgreSQLProtocol::Messaging::FrontMessageType::DESCRIBE: + case PostgreSQLProtocol::Messaging::FrontMessageType::SYNC: + case PostgreSQLProtocol::Messaging::FrontMessageType::FLUSH: + case PostgreSQLProtocol::Messaging::FrontMessageType::CLOSE: + message_transport->send( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse::ERROR, + "0A000", + "ClickHouse doesn't support exteneded query mechanism"), + true); + LOG_ERROR(log, "Client tried to access via extended query protocol"); + message_transport->dropMessage(); + break; + default: + message_transport->send( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse::ERROR, + "0A000", + "Command is not supported"), + true); + LOG_ERROR(log, Poco::format("Command is not supported. Command code %d", static_cast(message_type))); + message_transport->dropMessage(); + } + } + } + catch (const Poco::Exception &exc) + { + log->log(exc); + } + +} + +bool PostgreSQLHandler::startUp() +{ + Int32 payload_size; + Int32 info; + establishSecureConnection(payload_size, info); + + if (static_cast(info) == PostgreSQLProtocol::Messaging::FrontMessageType::CANCEL_REQUEST) + { + LOG_INFO(log, "Client issued request canceling"); + cancelRequest(); + return false; + } + + std::unique_ptr start_up_msg = receiveStartUpMessage(payload_size); + authentication_manager.authenticate(start_up_msg->user, connection_context, *message_transport, socket().peerAddress()); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(0, INT32_MAX); + secret_key = dis(gen); + + try + { + if (!start_up_msg->database.empty()) + connection_context.setCurrentDatabase(start_up_msg->database); + connection_context.setCurrentQueryId(Poco::format("postgres:%d:%d", connection_id, secret_key)); + } + catch (const Exception & exc) + { + message_transport->send( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse::ERROR, "XX000", exc.message()), + true); + throw; + } + + sendParameterStatusData(*start_up_msg); + + message_transport->send( + PostgreSQLProtocol::Messaging::BackendKeyData(connection_id, secret_key), true); + + LOG_INFO(log, "Successfully finished StartUp stage"); + return true; +} + +void PostgreSQLHandler::establishSecureConnection(Int32 & payload_size, Int32 & info) +{ + bool was_encryption_req = true; + readBinaryBigEndian(payload_size, *in); + readBinaryBigEndian(info, *in); + + switch (static_cast(info)) + { + case PostgreSQLProtocol::Messaging::FrontMessageType::SSL_REQUEST: + LOG_INFO(log, "Client requested SSL"); + if (ssl_enabled) + makeSecureConnectionSSL(); + else + message_transport->send('N', true); + break; + case PostgreSQLProtocol::Messaging::FrontMessageType::GSSENC_REQUEST: + LOG_INFO(log, "Client requested GSSENC"); + message_transport->send('N', true); + break; + default: + was_encryption_req = false; + } + if (was_encryption_req) + { + readBinaryBigEndian(payload_size, *in); + readBinaryBigEndian(info, *in); + } +} + +#if USE_SSL +void PostgreSQLHandler::makeSecureConnectionSSL() +{ + message_transport->send('S'); + ss = std::make_shared( + Poco::Net::SecureStreamSocket::attach(socket(), Poco::Net::SSLManager::instance().defaultServerContext())); + changeIO(*ss); +} +#else +void PostgreSQLHandler::makeSecureConnectionSSL() {} +#endif + +void PostgreSQLHandler::sendParameterStatusData(PostgreSQLProtocol::Messaging::StartUpMessage & start_up_message) +{ + std::unordered_map & parameters = start_up_message.parameters; + + if (parameters.find("application_name") != parameters.end()) + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("application_name", parameters["application_name"])); + if (parameters.find("client_encoding") != parameters.end()) + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("client_encoding", parameters["client_encoding"])); + else + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("client_encoding", "UTF8")); + + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("server_version", VERSION_STRING)); + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("server_encoding", "UTF8")); + message_transport->send(PostgreSQLProtocol::Messaging::ParameterStatus("DateStyle", "ISO")); + message_transport->flush(); +} + +void PostgreSQLHandler::cancelRequest() +{ + connection_context.setCurrentQueryId(""); + connection_context.setDefaultFormat("Null"); + + std::unique_ptr msg = + message_transport->receiveWithPayloadSize(8); + + String query = Poco::format("KILL QUERY WHERE query_id = 'postgres:%d:%d'", msg->process_id, msg->secret_key); + ReadBufferFromString replacement(query); + + executeQuery( + replacement, *out, true, connection_context, + [](const String &, const String &, const String &, const String &) {} + ); +} + +inline std::unique_ptr PostgreSQLHandler::receiveStartUpMessage(int payload_size) +{ + std::unique_ptr message; + try + { + message = message_transport->receiveWithPayloadSize(payload_size - 8); + } + catch (const Exception &) + { + message_transport->send( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse::ERROR, "08P01", "Can't correctly handle StartUp message"), + true); + throw; + } + + LOG_INFO(log, "Successfully received StartUp message"); + return message; +} + +void PostgreSQLHandler::processQuery() +{ + try + { + std::unique_ptr query = + message_transport->receive(); + + if (isEmptyQuery(query->query)) + { + message_transport->send(PostgreSQLProtocol::Messaging::EmptyQueryResponse()); + return; + } + + bool psycopg2_cond = query->query == "BEGIN" || query->query == "COMMIT"; // psycopg2 starts and ends queries with BEGIN/COMMIT commands + bool jdbc_cond = query->query.find("SET extra_float_digits") != String::npos || query->query.find("SET application_name") != String::npos; // jdbc starts with setting this parameter + if (psycopg2_cond || jdbc_cond) + { + message_transport->send( + PostgreSQLProtocol::Messaging::CommandComplete( + PostgreSQLProtocol::Messaging::CommandComplete::classifyQuery(query->query), 0)); + return; + } + + const auto & settings = connection_context.getSettingsRef(); + std::vector queries; + auto parse_res = splitMultipartQuery(query->query, queries, settings.max_query_size, settings.max_parser_depth); + if (!parse_res.second) + throw Exception("Cannot parse and execute the following part of query: " + String(parse_res.first), ErrorCodes::SYNTAX_ERROR); + + for (const auto & spl_query : queries) + { + ReadBufferFromString read_buf(spl_query); + executeQuery(read_buf, *out, true, connection_context, {}); + + PostgreSQLProtocol::Messaging::CommandComplete::Command command = + PostgreSQLProtocol::Messaging::CommandComplete::classifyQuery(spl_query); + message_transport->send(PostgreSQLProtocol::Messaging::CommandComplete(command, 0), true); + } + + } + catch (const Exception & e) + { + message_transport->send( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse( + PostgreSQLProtocol::Messaging::ErrorOrNoticeResponse::ERROR, "2F000", "Query execution failed.\n" + e.displayText()), + true); + throw; + } +} + +bool PostgreSQLHandler::isEmptyQuery(const String & query) +{ + if (query.empty()) + return true; + + Poco::RegularExpression regex(R"(\A\s*\z)"); + return regex.match(query); +} + +} diff --git a/programs/server/PostgreSQLHandler.h b/programs/server/PostgreSQLHandler.h new file mode 100644 index 000000000000..1062fed5cbbd --- /dev/null +++ b/programs/server/PostgreSQLHandler.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include "IServer.h" + +#if USE_SSL +# include +#endif + +namespace CurrentMetrics +{ + extern const Metric PostgreSQLConnection; +} + +namespace DB +{ + +/** PostgreSQL wire protocol implementation. + * For more info see https://www.postgresql.org/docs/current/protocol.html + */ +class PostgreSQLHandler : public Poco::Net::TCPServerConnection +{ +public: + PostgreSQLHandler( + const Poco::Net::StreamSocket & socket_, + IServer & server_, + bool ssl_enabled_, + Int32 connection_id_, + std::vector> & auth_methods_); + + void run() final; + +private: + Poco::Logger * log = &Poco::Logger::get("PostgreSQLHandler"); + + IServer & server; + Context connection_context; + bool ssl_enabled; + Int32 connection_id; + Int32 secret_key; + + std::shared_ptr in; + std::shared_ptr out; + std::shared_ptr message_transport; + +#if USE_SSL + std::shared_ptr ss; +#endif + + PostgreSQLProtocol::PGAuthentication::AuthenticationManager authentication_manager; + + CurrentMetrics::Increment metric_increment{CurrentMetrics::PostgreSQLConnection}; + + void changeIO(Poco::Net::StreamSocket & socket); + + bool startUp(); + + void establishSecureConnection(Int32 & payload_size, Int32 & info); + + void makeSecureConnectionSSL(); + + void sendParameterStatusData(PostgreSQLProtocol::Messaging::StartUpMessage & start_up_message); + + void cancelRequest(); + + std::unique_ptr receiveStartUpMessage(int payload_size); + + void processQuery(); + + bool isEmptyQuery(const String & query); +}; + +} diff --git a/programs/server/PostgreSQLHandlerFactory.cpp b/programs/server/PostgreSQLHandlerFactory.cpp new file mode 100644 index 000000000000..210d8558bfd8 --- /dev/null +++ b/programs/server/PostgreSQLHandlerFactory.cpp @@ -0,0 +1,28 @@ +#include "PostgreSQLHandlerFactory.h" +#include "IServer.h" +#include +#include +#include "PostgreSQLHandler.h" + +namespace DB +{ + +PostgreSQLHandlerFactory::PostgreSQLHandlerFactory(IServer & server_) + : server(server_) + , log(&Logger::get("PostgreSQLHandlerFactory")) +{ + auth_methods = + { + std::make_shared(), + std::make_shared(), + }; +} + +Poco::Net::TCPServerConnection * PostgreSQLHandlerFactory::createConnection(const Poco::Net::StreamSocket & socket) +{ + Int32 connection_id = last_connection_id++; + LOG_TRACE(log, "PostgreSQL connection. Id: " << connection_id << ". Address: " << socket.peerAddress().toString()); + return new PostgreSQLHandler(socket, server, ssl_enabled, connection_id, auth_methods); +} + +} diff --git a/programs/server/PostgreSQLHandlerFactory.h b/programs/server/PostgreSQLHandlerFactory.h new file mode 100644 index 000000000000..a95a22c162ca --- /dev/null +++ b/programs/server/PostgreSQLHandlerFactory.h @@ -0,0 +1,29 @@ +#pragma once + +#include "IServer.h" +#include +#include + +namespace DB +{ + +class PostgreSQLHandlerFactory : public Poco::Net::TCPServerConnectionFactory +{ +private: +#if USE_SSL + IServer & server; + Poco::Logger * log; + bool ssl_enabled = true; +#else + bool ssl_enabled = false; +#endif + + std::atomic last_connection_id = 0; + std::vector> auth_methods; + +public: + explicit PostgreSQLHandlerFactory(IServer & server_); + + Poco::Net::TCPServerConnection * createConnection(const Poco::Net::StreamSocket & socket) override; +}; +} diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 29096327a71b..04324d345740 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -61,6 +61,7 @@ #include #include + #if !defined(ARCADIA_BUILD) # include "config_core.h" # include "Common/config_version.h" diff --git a/src/Common/CurrentMetrics.cpp b/src/Common/CurrentMetrics.cpp index 1eb401905f6c..489598a5b269 100644 --- a/src/Common/CurrentMetrics.cpp +++ b/src/Common/CurrentMetrics.cpp @@ -23,6 +23,7 @@ M(MySQLConnection, "Number of client connections using MySQL protocol") \ M(HTTPConnection, "Number of connections to HTTP server") \ M(InterserverConnection, "Number of connections from other replicas to fetch parts") \ + M(PostgreSQLConnection, "Number of client connections using PostgreSQL protocol") \ M(OpenFileForRead, "Number of files open for reading") \ M(OpenFileForWrite, "Number of files open for writing") \ M(Read, "Number of read (read, pread, io_getevents, etc.) syscalls in fly") \ @@ -61,7 +62,6 @@ M(LocalThreadActive, "Number of threads in local thread pools running a task.") \ M(DistributedFilesToInsert, "Number of pending files to process for asynchronous insertion into Distributed tables. Number of files for every shard is summed.") \ - namespace CurrentMetrics { #define M(NAME, DOCUMENTATION) extern const Metric NAME = __COUNTER__; diff --git a/src/Core/PostgreSQLProtocol.cpp b/src/Core/PostgreSQLProtocol.cpp new file mode 100644 index 000000000000..553d195605a1 --- /dev/null +++ b/src/Core/PostgreSQLProtocol.cpp @@ -0,0 +1,50 @@ +#include "PostgreSQLProtocol.h" + +namespace DB::PostgreSQLProtocol::Messaging +{ + +ColumnTypeSpec convertTypeIndexToPostgresColumnTypeSpec(TypeIndex type_index) +{ + switch (type_index) + { + case TypeIndex::Int8: + return {ColumnType::CHAR, 1}; + + case TypeIndex::UInt8: + case TypeIndex::Int16: + return {ColumnType::INT2, 2}; + + case TypeIndex::UInt16: + case TypeIndex::Int32: + return {ColumnType::INT4, 4}; + + case TypeIndex::UInt32: + case TypeIndex::Int64: + return {ColumnType::INT8, 8}; + + case TypeIndex::Float32: + return {ColumnType::FLOAT4, 4}; + case TypeIndex::Float64: + return {ColumnType::FLOAT8, 8}; + + case TypeIndex::FixedString: + case TypeIndex::String: + return {ColumnType::VARCHAR, -1}; + + case TypeIndex::Date: + return {ColumnType::DATE, 4}; + + case TypeIndex::Decimal32: + case TypeIndex::Decimal64: + case TypeIndex::Decimal128: + return {ColumnType::NUMERIC, -1}; + + case TypeIndex::UUID: + return {ColumnType::UUID, 16}; + + default: + return {ColumnType::VARCHAR, -1}; + } +} + +} diff --git a/src/Core/PostgreSQLProtocol.h b/src/Core/PostgreSQLProtocol.h new file mode 100644 index 000000000000..b5fa67d68ea3 --- /dev/null +++ b/src/Core/PostgreSQLProtocol.h @@ -0,0 +1,914 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Types.h" +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int UNKNOWN_PACKET_FROM_CLIENT; + extern const int UNEXPECTED_PACKET_FROM_CLIENT; + extern const int NOT_IMPLEMENTED; + extern const int UNKNOWN_TYPE; +} + + +namespace PostgreSQLProtocol +{ + +namespace Messaging +{ + +enum class FrontMessageType : Int32 +{ +// first message types + CANCEL_REQUEST = 80877102, + SSL_REQUEST = 80877103, + GSSENC_REQUEST = 80877104, + +// other front message types + PASSWORD_MESSAGE = 'p', + QUERY = 'Q', + TERMINATE = 'X', + PARSE = 'P', + BIND = 'B', + DESCRIBE = 'D', + SYNC = 'S', + FLUSH = 'H', + CLOSE = 'C', +}; + +enum class MessageType : Int32 +{ +// common + ERROR_RESPONSE = 0, + CANCEL_REQUEST = 1, + COMMAND_COMPLETE = 2, + NOTICE_RESPONSE = 3, + NOTIFICATION_RESPONSE = 4, + PARAMETER_STATUS = 5, + READY_FOR_QUERY = 6, + SYNC = 7, + TERMINATE = 8, + +// start up and authentication + AUTHENTICATION_OK = 30, + AUTHENTICATION_KERBEROS_V5 = 31, + AUTHENTICATION_CLEARTEXT_PASSWORD = 32, + AUTHENTICATION_MD5_PASSWORD = 33, + AUTHENTICATION_SCM_CREDENTIAL = 34, + AUTHENTICATION_GSS = 35, + AUTHENTICATION_SSPI = 36, + AUTHENTICATION_GSS_CONTINUE = 37, + AUTHENTICATION_SASL = 38, + AUTHENTICATION_SASL_CONTINUE = 39, + AUTHENTICATION_SASL_FINAL = 40, + BACKEND_KEY_DATA = 41, + GSSENC_REQUEST = 42, + GSS_RESPONSE = 43, + NEGOTIATE_PROTOCOL_VERSION = 44, + PASSWORD_MESSAGE = 45, + SASL_INITIAL_RESPONSE = 46, + SASL_RESPONSE = 47, + SSL_REQUEST = 48, + STARTUP_MESSAGE = 49, + +// simple query + DATA_ROW = 100, + EMPTY_QUERY_RESPONSE = 101, + ROW_DESCRIPTION = 102, + QUERY = 103, + +// extended query + BIND = 120, + BIND_COMPLETE = 121, + CLOSE = 122, + CLOSE_COMPLETE = 123, + DESCRIBE = 124, + EXECUTE = 125, + FLUSH = 126, + NODATA = 127, + PARAMETER_DESCRIPTION = 128, + PARSE = 129, + PARSE_COMPLETE = 130, + PORTAL_SUSPENDED = 131, + +// copy query + COPY_DATA = 171, + COPY_DONE = 172, + COPY_FAIL = 173, + COPY_IN_RESPONSE = 174, + COPY_OUT_RESPONSE = 175, + COPY_BOTH_RESPONSE = 176, + +// function query (deprecated by the protocol) + FUNCTION_CALL = 190, + FUNCTION_CALL_RESPONSE = 191, +}; + +//// Column 'typelem' from 'pg_type' table. NB: not all types are compatible with PostgreSQL's ones +enum class ColumnType : Int32 +{ + CHAR = 18, + INT8 = 20, + INT2 = 21, + INT4 = 23, + FLOAT4 = 700, + FLOAT8 = 701, + VARCHAR = 1043, + DATE = 1082, + NUMERIC = 1700, + UUID = 2950, +}; + +class ColumnTypeSpec +{ +public: + ColumnType type; + Int16 len; + + ColumnTypeSpec(ColumnType type_, Int16 len_) : type(type_), len(len_) {} +}; + +ColumnTypeSpec convertTypeIndexToPostgresColumnTypeSpec(TypeIndex type_index); + +class MessageTransport +{ +private: + ReadBuffer * in; + WriteBuffer * out; + +public: + MessageTransport(WriteBuffer * out_) : in(nullptr), out(out_) {} + + MessageTransport(ReadBuffer * in_, WriteBuffer * out_): in(in_), out(out_) {} + + template + std::unique_ptr receiveWithPayloadSize(Int32 payload_size) + { + std::unique_ptr message = std::make_unique(payload_size); + message->deserialize(*in); + return message; + } + + template + std::unique_ptr receive() + { + std::unique_ptr message = std::make_unique(); + message->deserialize(*in); + return message; + } + + FrontMessageType receiveMessageType() + { + char type = 0; + in->read(type); + return static_cast(type); + } + + template + void send(TMessage & message, bool flush=false) + { + message.serialize(*out); + if (flush) + out->next(); + } + + template + void send(TMessage && message, bool flush=false) + { + send(message, flush); + } + + void send(char message, bool flush=false) + { + out->write(message); + if (flush) + out->next(); + } + + void dropMessage() + { + Int32 size; + readBinaryBigEndian(size, *in); + in->ignore(size - 4); + } + + void flush() + { + out->next(); + } +}; + +/** Basic class for messages sent by client or server. */ +class IMessage +{ +public: + virtual MessageType getMessageType() const = 0; + + virtual ~IMessage() = default; +}; + +class ISerializable +{ +public: + /** Should be overridden for sending the message */ + virtual void serialize(WriteBuffer & out) const = 0; + + /** Size of the message in bytes including message length part (4 bytes) */ + virtual Int32 size() const = 0; + + virtual ~ISerializable() = default; +}; + +class FrontMessage : public IMessage +{ +public: + /** Should be overridden for receiving the message + * NB: This method should not read the first byte, which means the type of the message + * (if type is provided for the message by the protocol). + */ + virtual void deserialize(ReadBuffer & in) = 0; +}; + +class BackendMessage : public IMessage, public ISerializable +{}; + +class FirstMessage : public FrontMessage +{ +public: + Int32 payload_size; + FirstMessage() = delete; + FirstMessage(int payload_size_) : payload_size(payload_size_) {} +}; + +class CancelRequest : public FirstMessage +{ +public: + Int32 process_id; + Int32 secret_key; + CancelRequest(int payload_size_) : FirstMessage(payload_size_) {} + + void deserialize(ReadBuffer & in) override + { + readBinaryBigEndian(process_id, in); + readBinaryBigEndian(secret_key, in); + } + + MessageType getMessageType() const override + { + return MessageType::CANCEL_REQUEST; + } +}; + +class ErrorOrNoticeResponse : BackendMessage +{ +public: + enum Severity {ERROR = 0, FATAL = 1, PANIC = 2, WARNING = 3, NOTICE = 4, DEBUG = 5, INFO = 6, LOG = 7}; + +private: + Severity severity; + String sql_state; + String message; + + String enum_to_string[8] = {"ERROR", "FATAL", "PANIC", "WARNING", "NOTICE", "DEBUG", "INFO", "LOG"}; + + char isErrorOrNotice() const + { + switch (severity) + { + case ERROR: + case FATAL: + case PANIC: + return 'E'; + case WARNING: + case NOTICE: + case DEBUG: + case INFO: + case LOG: + return 'N'; + } + throw Exception("Unknown severity type " + std::to_string(severity), ErrorCodes::UNKNOWN_TYPE); + } + +public: + ErrorOrNoticeResponse(const Severity & severity_, const String & sql_state_, const String & message_) + : severity(severity_) + , sql_state(sql_state_) + , message(message_) + {} + + void serialize(WriteBuffer & out) const override + { + out.write(isErrorOrNotice()); + Int32 sz = size(); + writeBinaryBigEndian(sz, out); + + out.write('S'); + writeNullTerminatedString(enum_to_string[severity], out); + out.write('C'); + writeNullTerminatedString(sql_state, out); + out.write('M'); + writeNullTerminatedString(message, out); + + out.write(0); + } + + Int32 size() const override + { + // message length part + (1 + sizes of other fields + 1) + null byte in the end of the message + return 4 + (1 + enum_to_string[severity].size() + 1) + (1 + sql_state.size() + 1) + (1 + message.size() + 1) + 1; + } + + MessageType getMessageType() const override + { + if (isErrorOrNotice() == 'E') + return MessageType::ERROR_RESPONSE; + return MessageType::NOTICE_RESPONSE; + } +}; + +class ReadyForQuery : BackendMessage +{ +public: + void serialize(WriteBuffer &out) const override + { + out.write('Z'); + writeBinaryBigEndian(size(), out); + // 'I' means that we are not in a transaction block. We use it here, because ClickHouse doesn't support transactions. + out.write('I'); + } + + Int32 size() const override + { + return 4 + 1; + } + + MessageType getMessageType() const override + { + return MessageType::READY_FOR_QUERY; + } +}; + +class Terminate : FrontMessage +{ +public: + void deserialize(ReadBuffer & in) override + { + in.ignore(4); + } + + MessageType getMessageType() const override + { + return MessageType::TERMINATE; + } +}; + +class StartUpMessage : FirstMessage +{ +public: + String user; + String database; + // includes username, may also include database and other runtime parameters + std::unordered_map parameters; + + StartUpMessage(Int32 payload_size_) : FirstMessage(payload_size_) {} + + void deserialize(ReadBuffer & in) override + { + Int32 ps = payload_size - 1; + while (ps > 0) + { + String parameter_name; + String parameter_value; + readNullTerminated(parameter_name, in); + readNullTerminated(parameter_value, in); + ps -= parameter_name.size() + 1; + ps -= parameter_value.size() + 1; + + if (parameter_name == "user") + { + user = parameter_value; + } + else if (parameter_name == "database") + { + database = parameter_value; + } + + parameters.insert({std::move(parameter_name), std::move(parameter_value)}); + + if (payload_size < 0) + { + throw Exception( + Poco::format( + "Size of payload is larger than one declared in the message of type %d.", + getMessageType()), + ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT); + } + } + in.ignore(); + } + + MessageType getMessageType() const override + { + return MessageType::STARTUP_MESSAGE; + } +}; + +class AuthenticationCleartextPassword : public Messaging::BackendMessage +{ +public: + void serialize(WriteBuffer & out) const override + { + out.write('R'); + writeBinaryBigEndian(size(), out); + writeBinaryBigEndian(static_cast(3), out); // specifies that a clear-text password is required (by protocol) + } + + Int32 size() const override + { + // length of message + special int32 + return 4 + 4; + } + + MessageType getMessageType() const override + { + return MessageType::AUTHENTICATION_CLEARTEXT_PASSWORD; + } +}; + +class AuthenticationOk : BackendMessage +{ +public: + void serialize(WriteBuffer & out) const override + { + out.write('R'); + writeBinaryBigEndian(size(), out); + writeBinaryBigEndian(0, out); // specifies that the authentication was successful (by protocol) + } + + Int32 size() const override + { + // length of message + special int32 + return 4 + 4; + } + + MessageType getMessageType() const override + { + return MessageType::AUTHENTICATION_OK; + } +}; + +class PasswordMessage : FrontMessage +{ +public: + String password; + + void deserialize(ReadBuffer & in) override + { + Int32 sz; + readBinaryBigEndian(sz, in); + readNullTerminated(password, in); + } + + MessageType getMessageType() const override + { + return MessageType::PASSWORD_MESSAGE; + } +}; + +class ParameterStatus : BackendMessage +{ +private: + String name; + String value; + +public: + ParameterStatus(String name_, String value_) + : name(name_) + , value(value_) + {} + + void serialize(WriteBuffer & out) const override + { + out.write('S'); + writeBinaryBigEndian(size(), out); + writeNullTerminatedString(name, out); + writeNullTerminatedString(value, out); + } + + Int32 size() const override + { + return 4 + name.size() + 1 + value.size() + 1; + } + + MessageType getMessageType() const override + { + return MessageType::PARAMETER_STATUS; + } +}; + +class BackendKeyData : BackendMessage +{ +private: + Int32 process_id; + Int32 secret_key; + +public: + BackendKeyData(Int32 process_id_, Int32 secret_key_) + : process_id(process_id_) + , secret_key(secret_key_) + {} + + void serialize(WriteBuffer & out) const override + { + out.write('K'); + writeBinaryBigEndian(size(), out); + writeBinaryBigEndian(process_id, out); + writeBinaryBigEndian(secret_key, out); + } + + Int32 size() const override + { + return 4 + 4 + 4; + } + + MessageType getMessageType() const override + { + return MessageType::BACKEND_KEY_DATA; + } +}; + +class Query : FrontMessage +{ +public: + String query; + + void deserialize(ReadBuffer & in) override + { + Int32 sz; + readBinaryBigEndian(sz, in); + readNullTerminated(query, in); + } + + MessageType getMessageType() const override + { + return MessageType::QUERY; + } +}; + +class EmptyQueryResponse : public BackendMessage +{ +public: + void serialize(WriteBuffer & out) const override + { + out.write('I'); + writeBinaryBigEndian(size(), out); + } + + Int32 size() const override + { + return 4; + } + + MessageType getMessageType() const override + { + return MessageType::EMPTY_QUERY_RESPONSE; + } +}; + +enum class FormatCode : Int16 +{ + TEXT = 0, + BINARY = 1, +}; + +class FieldDescription : ISerializable +{ +private: + const String & name; + ColumnTypeSpec type_spec; + FormatCode format_code; + +public: + FieldDescription(const String & name_, TypeIndex type_index, FormatCode format_code_ = FormatCode::TEXT) + : name(name_) + , type_spec(convertTypeIndexToPostgresColumnTypeSpec(type_index)) + , format_code(format_code_) + {} + + void serialize(WriteBuffer & out) const override + { + writeNullTerminatedString(name, out); + writeBinaryBigEndian(static_cast(0), out); + writeBinaryBigEndian(static_cast(0), out); + writeBinaryBigEndian(static_cast(type_spec.type), out); + writeBinaryBigEndian(type_spec.len, out); + writeBinaryBigEndian(static_cast(-1), out); + writeBinaryBigEndian(static_cast(format_code), out); + } + + Int32 size() const override + { + // size of name (C string) + // + object ID of the table (Int32 and always zero) + attribute number of the column (Int16 and always zero) + // + type object id (Int32) + data type size (Int16) + // + type modifier (Int32 and always -1) + format code (Int16) + return (name.size() + 1) + 4 + 2 + 4 + 2 + 4 + 2; + } +}; + +class RowDescription : BackendMessage +{ +private: + const std::vector & fields_descr; + +public: + RowDescription(const std::vector & fields_descr_) : fields_descr(fields_descr_) {} + + void serialize(WriteBuffer & out) const override + { + out.write('T'); + writeBinaryBigEndian(size(), out); + writeBinaryBigEndian(static_cast(fields_descr.size()), out); + for (const FieldDescription & field : fields_descr) + field.serialize(out); + } + + Int32 size() const override + { + Int32 sz = 4 + 2; // size of message + number of fields + for (const FieldDescription & field : fields_descr) + sz += field.size(); + return sz; + } + + MessageType getMessageType() const override + { + return MessageType::ROW_DESCRIPTION; + } +}; + +class StringField : public ISerializable +{ +private: + String str; +public: + StringField(String str_) : str(str_) {} + + void serialize(WriteBuffer & out) const override + { + writeString(str, out); + } + + Int32 size() const override + { + return str.size(); + } +}; + +class NullField : public ISerializable +{ +public: + void serialize(WriteBuffer & /* out */) const override {} + + Int32 size() const override + { + return -1; + } +}; + +class DataRow : BackendMessage +{ +private: + const std::vector> & row; + +public: + DataRow(const std::vector> & row_) : row(row_) {} + + void serialize(WriteBuffer & out) const override + { + out.write('D'); + writeBinaryBigEndian(size(), out); + writeBinaryBigEndian(static_cast(row.size()), out); + for (const std::shared_ptr & field : row) + { + Int32 sz = field->size(); + writeBinaryBigEndian(sz, out); + if (sz > 0) + field->serialize(out); + } + } + + Int32 size() const override + { + Int32 sz = 4 + 2; // size of message + number of fields + for (const std::shared_ptr & field : row) + sz += 4 + field->size(); + return sz; + } + + MessageType getMessageType() const override + { + return MessageType::DATA_ROW; + } +}; + +class CommandComplete : BackendMessage +{ +public: + enum Command {BEGIN = 0, COMMIT = 1, INSERT = 2, DELETE = 3, UPDATE = 4, SELECT = 5, MOVE = 6, FETCH = 7, COPY = 8}; +private: + String enum_to_string[9] = {"BEGIN", "COMMIT", "INSERT", "DELETE", "UPDATE", "SELECT", "MOVE", "FETCH", "COPY"}; + + String value; + +public: + CommandComplete(Command cmd_, Int32 rows_count_) + { + value = enum_to_string[cmd_]; + String add = " "; + if (cmd_ == Command::INSERT) + add = " 0 "; + value += add + std::to_string(rows_count_); + } + + void serialize(WriteBuffer & out) const override + { + out.write('C'); + writeBinaryBigEndian(size(), out); + writeNullTerminatedString(value, out); + } + + Int32 size() const override + { + return 4 + value.size() + 1; + } + + MessageType getMessageType() const override + { + return MessageType::COMMAND_COMPLETE; + } + + static Command classifyQuery(const String & query) + { + std::vector query_types({"BEGIN", "COMMIT", "INSERT", "DELETE", "UPDATE", "SELECT", "MOVE", "FETCH", "COPY"}); + for (size_t i = 0; i != query_types.size(); ++i) + { + String::const_iterator iter = std::search( + query.begin(), + query.end(), + query_types[i].begin(), + query_types[i].end(), + [](char a, char b){return std::toupper(a) == b;}); + + if (iter != query.end()) + return static_cast(i); + } + + return Command::SELECT; + } +}; + +} + +namespace PGAuthentication +{ + +class AuthenticationMethod +{ +protected: + void setPassword( + const String & user_name, + const String & password, + Context & context, + Messaging::MessageTransport & mt, + const Poco::Net::SocketAddress & address) + { + try { + context.setUser(user_name, password, address, ""); + } + catch (const Exception &) + { + mt.send( + Messaging::ErrorOrNoticeResponse(Messaging::ErrorOrNoticeResponse::ERROR, "28P01", "Invalid user or password"), + true); + throw; + } + } + +public: + virtual void authenticate( + const String & user_name, + Context & context, + Messaging::MessageTransport & mt, + const Poco::Net::SocketAddress & address) = 0; + + virtual Authentication::Type getType() const = 0; + + virtual ~AuthenticationMethod() = default; +}; + +class NoPasswordAuth : public AuthenticationMethod +{ +public: + void authenticate( + const String & /* user_name */, + Context & /* context */, + Messaging::MessageTransport & /* mt */, + const Poco::Net::SocketAddress & /* address */) override {} + + Authentication::Type getType() const override + { + return Authentication::Type::NO_PASSWORD; + } +}; + +class CleartextPasswordAuth : public AuthenticationMethod +{ +public: + void authenticate( + const String & user_name, + Context & context, + Messaging::MessageTransport & mt, + const Poco::Net::SocketAddress & address) override + { + mt.send(Messaging::AuthenticationCleartextPassword(), true); + + Messaging::FrontMessageType type = mt.receiveMessageType(); + if (type == Messaging::FrontMessageType::PASSWORD_MESSAGE) + { + std::unique_ptr password = mt.receive(); + setPassword(user_name, password->password, context, mt, address); + } + else + throw Exception( + Poco::format( + "Client sent wrong message or closed the connection. Message byte was %d.", + static_cast(type)), + ErrorCodes::UNEXPECTED_PACKET_FROM_CLIENT); + } + + Authentication::Type getType() const override + { + return Authentication::Type::PLAINTEXT_PASSWORD; + } +}; + +class AuthenticationManager +{ +private: + Poco::Logger * log = &Poco::Logger::get("AuthenticationManager"); + std::unordered_map> type_to_method = {}; + +public: + AuthenticationManager(const std::vector> & auth_methods) + { + for (const std::shared_ptr & method : auth_methods) + { + type_to_method[method->getType()] = method; + } + } + + void authenticate( + const String & user_name, + Context & context, + Messaging::MessageTransport & mt, + const Poco::Net::SocketAddress & address) + { + auto user = context.getAccessControlManager().read(user_name); + Authentication::Type user_auth_type = user->authentication.getType(); + + if (type_to_method.find(user_auth_type) != type_to_method.end()) + { + type_to_method[user_auth_type]->authenticate(user_name, context, mt, address); + mt.send(Messaging::AuthenticationOk(), true); + LOG_INFO(log, "Authentication for user " << user_name << " was successful."); + return; + } + + mt.send( + Messaging::ErrorOrNoticeResponse(Messaging::ErrorOrNoticeResponse::ERROR, "0A000", "Authentication method is not supported"), + true); + + throw Exception(Poco::format("Authentication type %d is not supported.", user_auth_type), ErrorCodes::NOT_IMPLEMENTED); + } +}; +} + +} +} diff --git a/src/Core/ya.make b/src/Core/ya.make index 4999fe334bc9..06fed2dc2573 100644 --- a/src/Core/ya.make +++ b/src/Core/ya.make @@ -16,6 +16,7 @@ SRCS( Field.cpp iostream_debug_helpers.cpp MySQLProtocol.cpp + PostgreSQLProtocol.cpp NamesAndTypes.cpp Settings.cpp SettingsCollection.cpp diff --git a/src/Formats/FormatFactory.cpp b/src/Formats/FormatFactory.cpp index 669baace2f5b..9b4d7940efee 100644 --- a/src/Formats/FormatFactory.cpp +++ b/src/Formats/FormatFactory.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #if !defined(ARCADIA_BUILD) @@ -393,6 +394,7 @@ FormatFactory::FormatFactory() registerOutputFormatProcessorNull(*this); registerOutputFormatProcessorMySQLWrite(*this); registerOutputFormatProcessorMarkdown(*this); + registerOutputFormatProcessorPostgreSQLWrite(*this); } FormatFactory & FormatFactory::instance() diff --git a/src/Formats/FormatFactory.h b/src/Formats/FormatFactory.h index c8dd97aa9402..87925ca1d75a 100644 --- a/src/Formats/FormatFactory.h +++ b/src/Formats/FormatFactory.h @@ -201,6 +201,7 @@ void registerOutputFormatProcessorODBCDriver2(FormatFactory & factory); void registerOutputFormatProcessorNull(FormatFactory & factory); void registerOutputFormatProcessorMySQLWrite(FormatFactory & factory); void registerOutputFormatProcessorMarkdown(FormatFactory & factory); +void registerOutputFormatProcessorPostgreSQLWrite(FormatFactory & factory); /// Input only formats. void registerInputFormatProcessorCapnProto(FormatFactory & factory); diff --git a/src/IO/WriteHelpers.h b/src/IO/WriteHelpers.h index 10918fb7b61e..ac3266765111 100644 --- a/src/IO/WriteHelpers.h +++ b/src/IO/WriteHelpers.h @@ -998,4 +998,24 @@ inline String toString(const T & x) return buf.str(); } +inline void writeNullTerminatedString(const String & s, WriteBuffer & buffer) +{ + buffer.write(s.data(), s.size()); + buffer.write(0); +} + +template +inline std::enable_if_t && (sizeof(T) <= 8), void> +writeBinaryBigEndian(T x, WriteBuffer & buf) /// Assuming little endian architecture. +{ + if constexpr (sizeof(x) == 2) + x = __builtin_bswap16(x); + else if constexpr (sizeof(x) == 4) + x = __builtin_bswap32(x); + else if constexpr (sizeof(x) == 8) + x = __builtin_bswap64(x); + + writePODBinary(x, buf); +} + } diff --git a/src/Processors/Formats/Impl/PostgreSQLOutputFormat.cpp b/src/Processors/Formats/Impl/PostgreSQLOutputFormat.cpp new file mode 100644 index 000000000000..f03656ec304f --- /dev/null +++ b/src/Processors/Formats/Impl/PostgreSQLOutputFormat.cpp @@ -0,0 +1,79 @@ +#include +#include "PostgreSQLOutputFormat.h" + +namespace DB +{ + +PostgreSQLOutputFormat::PostgreSQLOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & settings_) + : IOutputFormat(header_, out_) + , format_settings(settings_) + , message_transport(&out) +{ +} + +void PostgreSQLOutputFormat::doWritePrefix() +{ + if (initialized) + return; + + initialized = true; + const auto & header = getPort(PortKind::Main).getHeader(); + data_types = header.getDataTypes(); + + if (header.columns()) + { + std::vector columns; + columns.reserve(header.columns()); + + for (size_t i = 0; i < header.columns(); i++) + { + const auto & column_name = header.getColumnsWithTypeAndName()[i].name; + columns.emplace_back(column_name, data_types[i]->getTypeId()); + } + message_transport.send(PostgreSQLProtocol::Messaging::RowDescription(columns)); + } +} + +void PostgreSQLOutputFormat::consume(Chunk chunk) +{ + doWritePrefix(); + + for (size_t i = 0; i != chunk.getNumRows(); ++i) + { + const Columns & columns = chunk.getColumns(); + std::vector> row; + row.reserve(chunk.getNumColumns()); + + for (size_t j = 0; j != chunk.getNumColumns(); ++j) + { + if (columns[j]->isNullAt(i)) + row.push_back(std::make_shared()); + else + { + WriteBufferFromOwnString ostr; + data_types[j]->serializeAsText(*columns[j], i, ostr, format_settings); + row.push_back(std::make_shared(std::move(ostr.str()))); + } + } + + message_transport.send(PostgreSQLProtocol::Messaging::DataRow(row)); + } +} + +void PostgreSQLOutputFormat::finalize() {} + +void PostgreSQLOutputFormat::flush() +{ + message_transport.flush(); +} + +void registerOutputFormatProcessorPostgreSQLWrite(FormatFactory & factory) +{ + factory.registerOutputFormatProcessor( + "PostgreSQLWire", + [](WriteBuffer & buf, + const Block & sample, + const FormatFactory::WriteCallback &, + const FormatSettings & settings) { return std::make_shared(buf, sample, settings); }); +} +} diff --git a/src/Processors/Formats/Impl/PostgreSQLOutputFormat.h b/src/Processors/Formats/Impl/PostgreSQLOutputFormat.h new file mode 100644 index 000000000000..8ff5aae5067b --- /dev/null +++ b/src/Processors/Formats/Impl/PostgreSQLOutputFormat.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include +#include + +namespace DB +{ + +//// https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.5.7.4 +class PostgreSQLOutputFormat final : public IOutputFormat +{ +public: + PostgreSQLOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & settings_); + + String getName() const override {return "PostgreSQLOutputFormat";} + + void doWritePrefix() override; + void consume(Chunk) override; + void finalize() override; + void flush() override; + +private: + bool initialized = false; + + FormatSettings format_settings; + PostgreSQLProtocol::Messaging::MessageTransport message_transport; + DataTypes data_types; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 62320f1c1476..b709b0e44b7b 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -38,6 +38,7 @@ SRCS( Formats/Impl/NullFormat.cpp Formats/Impl/ODBCDriver2BlockOutputFormat.cpp Formats/Impl/ODBCDriverBlockOutputFormat.cpp + Formats/Impl/PostgreSQLOutputFormat.cpp Formats/Impl/PrettyBlockOutputFormat.cpp Formats/Impl/PrettyCompactBlockOutputFormat.cpp Formats/Impl/PrettySpaceBlockOutputFormat.cpp diff --git a/tests/integration/test_postgresql_protocol/__init__.py b/tests/integration/test_postgresql_protocol/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/integration/test_postgresql_protocol/clients/java/0.reference b/tests/integration/test_postgresql_protocol/clients/java/0.reference new file mode 100644 index 000000000000..3e3e20d1ebb8 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/clients/java/0.reference @@ -0,0 +1,15 @@ +33jdbcnull +44cknull +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 diff --git a/tests/integration/test_postgresql_protocol/clients/java/Dockerfile b/tests/integration/test_postgresql_protocol/clients/java/Dockerfile new file mode 100644 index 000000000000..f08470ee8057 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/clients/java/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get install -y software-properties-common build-essential openjdk-8-jdk curl + +RUN rm -rf \ + /var/lib/apt/lists/* \ + /var/cache/debconf \ + /tmp/* \ +RUN apt-get clean + +ARG ver=42.2.12 +RUN curl -L -o /postgresql-java-${ver}.jar https://repo1.maven.org/maven2/org/postgresql/postgresql/${ver}/postgresql-${ver}.jar +ENV CLASSPATH=$CLASSPATH:/postgresql-java-${ver}.jar + +WORKDIR /jdbc +COPY Test.java Test.java +RUN javac Test.java diff --git a/tests/integration/test_postgresql_protocol/clients/java/Test.java b/tests/integration/test_postgresql_protocol/clients/java/Test.java new file mode 100644 index 000000000000..772a749711a8 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/clients/java/Test.java @@ -0,0 +1,83 @@ +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +class JavaConnectorTest { + private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS default.test1 (`age` Int32, `name` String, `int_nullable` Nullable(Int32)) Engine = Memory"; + private static final String INSERT_SQL = "INSERT INTO default.test1(`age`, `name`) VALUES(33, 'jdbc'),(44, 'ck')"; + private static final String SELECT_SQL = "SELECT * FROM default.test1"; + private static final String SELECT_NUMBER_SQL = "SELECT * FROM system.numbers LIMIT 13"; + private static final String DROP_TABLE_SQL = "DROP TABLE default.test1"; + + public static void main(String[] args) { + int i = 0; + String host = "127.0.0.1"; + String port = "5432"; + String user = "default"; + String password = ""; + String database = "default"; + while (i < args.length) { + switch (args[i]) { + case "--host": + host = args[++i]; + break; + case "--port": + port = args[++i]; + break; + case "--user": + user = args[++i]; + break; + case "--password": + password = args[++i]; + break; + case "--database": + database = args[++i]; + break; + default: + i++; + break; + } + } + + String jdbcUrl = String.format("jdbc:postgresql://%s:%s/%s", host, port, database); + + Connection conn = null; + Statement stmt = null; + Properties props = new Properties(); + props.setProperty("user", user); + props.setProperty("password", password); + props.setProperty("preferQueryMode", "simple"); + props.setProperty("sslmode", "disable"); + try { + conn = DriverManager.getConnection(jdbcUrl, props); + stmt = conn.createStatement(); + stmt.executeUpdate(CREATE_TABLE_SQL); + stmt.executeUpdate(INSERT_SQL); + + ResultSet rs = stmt.executeQuery(SELECT_SQL); + while (rs.next()) { + System.out.print(rs.getString("age")); + System.out.print(rs.getString("name")); + System.out.print(rs.getString("int_nullable")); + System.out.println(); + } + + stmt.executeUpdate(DROP_TABLE_SQL); + + rs = stmt.executeQuery(SELECT_NUMBER_SQL); + while (rs.next()) { + System.out.print(rs.getString(1)); + System.out.println(); + } + + stmt.close(); + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/tests/integration/test_postgresql_protocol/clients/java/docker_compose.yml b/tests/integration/test_postgresql_protocol/clients/java/docker_compose.yml new file mode 100644 index 000000000000..7094c8b2359c --- /dev/null +++ b/tests/integration/test_postgresql_protocol/clients/java/docker_compose.yml @@ -0,0 +1,8 @@ +version: '2.2' +services: + java: + build: + context: ./ + network: host + # to keep container running + command: sleep infinity diff --git a/tests/integration/test_postgresql_protocol/clients/psql/docker_compose.yml b/tests/integration/test_postgresql_protocol/clients/psql/docker_compose.yml new file mode 100644 index 000000000000..984f5f973848 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/clients/psql/docker_compose.yml @@ -0,0 +1,14 @@ +version: '2.2' +services: + psql: + image: postgres:12.2-alpine + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + ports: + - "5433:5433" + environment: + POSTGRES_HOST_AUTH_METHOD: "trust" \ No newline at end of file diff --git a/tests/integration/test_postgresql_protocol/configs/config.xml b/tests/integration/test_postgresql_protocol/configs/config.xml new file mode 100644 index 000000000000..678b48425b18 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/configs/config.xml @@ -0,0 +1,35 @@ + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + 9000 + 5433 + 127.0.0.1 + + 500 + 5368709120 + ./clickhouse/ + users.xml + diff --git a/tests/integration/test_postgresql_protocol/configs/dhparam.pem b/tests/integration/test_postgresql_protocol/configs/dhparam.pem new file mode 100644 index 000000000000..a59693e9bfc7 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/configs/dhparam.pem @@ -0,0 +1,25 @@ +-----BEGIN X9.42 DH PARAMETERS----- +MIIELAKCAgEAkX9p27H48x6pBuiT5i7yVvSXjMaCnGkViPCL/R6+FdSpv/MVs0WX +dBq1uQWjin2AL7T4uHOyhd1sD4MrzgzPGR5q7lJr6CjvRtqj5ZjBX/xbo/N4xeix +VL+UTCpvfPwwkve7UL6C4v79f7AIH34ie+Ew2H5Bvy8RraFL5zrfhDWjdMPVk+Kz +Y4+GAXKEzB6CaXzpXBv/s5w7vXO11+EIXgWn2z6lJ2rEkEdT7hCamzNGy+ajH8on +FIvxrvvEQ1oLcMYPu6OB6PEGxonAjTrwIwYth1+4lnG0A4X5Bn1Bx0DKEyCAZSHw +ByjDZ9ZCspqY4b/auRKRnWSWDLYPkW4YtNCVV/+5pJydcL511gQ2WQs7quZEsGem +4x14xpIM5qDvF3bzFuDpVMuuzlf6AB9dEMSms6iIwuWpSxck6AydII0okxUaxSlW +QJxZGQBE/2m9DwFmMHDBWUYBGvevX51RjQCsJsmgZPlwnY7hnZ29sB7MeVzqF26d +103byJBUq+rWUkxzKrYKbm+FjOz84/hv3ONxoxBI0FstKdaEr7PnpDLyLmZCheeL +tz0RzNM3h9AEno1SJNrzWaVI5s5L6QjM9QRRWfF2JB5QyhQjc++FgRGk3SDBbcW5 +IhHVcboq/MppZiE82FSwMtCkmPvB7KrPoYx8fmrTs7zfHtx+glsMguMCggIAY32m +/EZbhpvmmbq0G/zjh1Nkdvj0IOQdkxnz7FhnKviKNgqWTbgHSaE+pcubK8XVsuAj +NLOp5AMpccV9h02ABGRdaSSyMeJxfnYRUhuSWHN+i/rqL3Xtv7w/BQXsUZd3tQQ+ +I4UhnC/VUlGgndL5b8TbYOA/9CXPItGRMQb3S9SzijzEeKwWHu0n+j4Nwbl3nrtk +epWey/Wv0SU1d07es9vXxob/iPZSwM1E9SDjRFrqokLQCWFzaELzOF14TBXUn1RT +1agpxeux9UQpPS1ELjReh+c94BWQh5Soj/HJ2L76EgWkKM0se7uD6AhZee+b22YM +KKqbWWetStSjSSsLxR4yvPMct/eUS8V9UCQfPuY3DpLZi3+F5hAMcKqV3gGHJBrD +82MkQUj8eJaz3qEocG3zzYnxZ3sXze9HYpGCVIXX6b5p8yg9R1I8mNLo9w0IS2mU +5rmw2YdioZKUTN+jMVP79GFgsoGTPAf9sFDdswwD1ie1MYG/sw1K/Jxw3MPED4y5 +we+bBaaa2WLaSB32eEnyxZBd8OOQOmTunp/zw12BAC485mF9Innr1fAhic8t+LOB +CyVAF02HA0puj365kGsZDjcXn+EEuwK+VeStERTXApcbwL+78VW+DQ1J/vBjkt4Z +ustnEMN3HdfV3DTBBRxmEj34MuEhrz0WjhgRskACIQCU5YbOgiW+L9L/mDwyGARK +jZ/2Z6yJuWyeim3EVpWG2Q== +-----END X9.42 DH PARAMETERS----- diff --git a/tests/integration/test_postgresql_protocol/configs/server.crt b/tests/integration/test_postgresql_protocol/configs/server.crt new file mode 100644 index 000000000000..070d37f3b778 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/configs/server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC+zCCAeOgAwIBAgIJANhP897Se2gmMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV +BAMMCWxvY2FsaG9zdDAeFw0yMDA0MTgyMTE2NDBaFw0yMTA0MTgyMTE2NDBaMBQx +EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAM92kcojQoMsjZ9YGhPMY6h/fDUsZeSKHLxgqE6wbmfU1oZKCPWqnvl+4n0J +pnT5h1ETxxYZLepimKq0DEVPUTmCl0xmcKbtUNiaTUKYKsdita6b2vZCX9wUPN9p +2Kjnm41l+aZNqIEBhIgHNWg9qowi20y0EIXR79jQLwwaInHAaJLZxVsqY2zjQ/D7 +1Zh82MXud7iqxBQiEfw9Cz35UFA239R8QTlPkVQfsN1gfLxnLk24QUX3o+hbUI1g +nlSpyYDHYQlOmwz8doDs6THHAZNJ4bPE9xHNFpw6dGZdbtH+IKQ/qRZIiOaiNuzJ +IOHl6XQDRDkW2LMTiCQ6fjC7Pz8CAwEAAaNQME4wHQYDVR0OBBYEFFvhaA/Eguyf +BXkMj8BkNLBqMnz2MB8GA1UdIwQYMBaAFFvhaA/EguyfBXkMj8BkNLBqMnz2MAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACeU/oL48eVAKH7NQntHhRaJ +ZGeQzKIjrSBjFo8BGXD1nJZhUeFsylLrhCkC8/5/3grE3BNVX9bxcGjO81C9Mn4U +t0z13d6ovJjCZSQArtLwgeJGlpH7gNdD3DyT8DQmrqYVnmnB7UmBu45XH1LWGQZr +FAOhGRVs6s6mNj8QlLMgdmsOeOQnsGCMdoss8zV9vO2dc4A5SDSSL2mqGGY4Yjtt +X+XlEhXXnksGyx8NGVOZX4wcj8WeCAj/lihQ7Zh6XYwZH9i+E46ompUwoziZnNPu +2RH63tLNCxkOY2HF5VMlbMmzer3FkhlM6TAZZRPcvSphKPwXK4A33yqc6wnWvpc= +-----END CERTIFICATE----- diff --git a/tests/integration/test_postgresql_protocol/configs/server.key b/tests/integration/test_postgresql_protocol/configs/server.key new file mode 100644 index 000000000000..b3dee82dcda4 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/configs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPdpHKI0KDLI2f +WBoTzGOof3w1LGXkihy8YKhOsG5n1NaGSgj1qp75fuJ9CaZ0+YdRE8cWGS3qYpiq +tAxFT1E5gpdMZnCm7VDYmk1CmCrHYrWum9r2Ql/cFDzfadio55uNZfmmTaiBAYSI +BzVoPaqMIttMtBCF0e/Y0C8MGiJxwGiS2cVbKmNs40Pw+9WYfNjF7ne4qsQUIhH8 +PQs9+VBQNt/UfEE5T5FUH7DdYHy8Zy5NuEFF96PoW1CNYJ5UqcmAx2EJTpsM/HaA +7OkxxwGTSeGzxPcRzRacOnRmXW7R/iCkP6kWSIjmojbsySDh5el0A0Q5FtizE4gk +On4wuz8/AgMBAAECggEAJ54J2yL+mZQRe2NUn4FBarTloDXZQ1pIgISov1Ybz0Iq +sTxEF728XAKp95y3J9Fa0NXJB+RJC2BGrRpy2W17IlNY1yMc0hOxg5t7s4LhcG/e +J/jlSG+GZL2MnlFVKXQJFWhq0yIzUmdayqstvLlB7z7cx/n+yb88YRfoVBRNjZEL +Tdrsw+087igDjrIxZJ3eMN5Wi434n9s4yAoRQC1bP5wcWx0gD4MzdmL8ip6suiRc +LRuBAhV/Op812xlxUhrF5dInUM9OLlGTXpUzexAS8Cyy7S4bfkW2BaCxTF7I7TFw +Whx28CKn/G49tIuU0m6AlxWbXpLVePTFyMb7RJz5cQKBgQD7VQd2u3HM6eE3PcXD +p6ObdLTUk8OAJ5BMmADFc71W0Epyo26/e8KXKGYGxE2W3fr13y+9b0fl5fxZPuhS +MgvXEO7rItAVsLcp0IzaqY0WUee2b4XWPAU0XuPqvjYMpx8H5OEHqFK6lhZysAqM +X7Ot3/Hux9X0MC4v5a/HNbDUOQKBgQDTUPaP3ADRrmpmE2sWuzWEnCSEz5f0tCLO +wTqhV/UraWUNlAbgK5NB790IjH/gotBSqqNPLJwJh0LUfClKM4LiaHsEag0OArOF +GhPMK1Ohps8c2RRsiG8+hxX2HEHeAVbkouEDPDiHdIW/92pBViDoETXL6qxDKbm9 +LkOcVeDfNwKBgQChh1xsqrvQ/t+IKWNZA/zahH9TwEP9sW/ESkz0mhYuHWA7nV4o +ItpFW+l2n+Nd+vy32OFN1p9W2iD9GrklWpTRfEiRRqaFyjVt4mMkhaPvnGRXlAVo +Utrldbb1v5ntN9txr2ARE9VXpe53dzzQSxGnxi4vUK/paK3GitAWMCOdwQKBgQCi +hmGsUXQb0P6qVYMGr6PAw2re7t8baLRguoMCdqjs45nCMLh9D2apzvb8TTtJJU/+ +VJlYGqJEPdDrpjcHh8jBo8QBqCM0RGWYGG9jl2syKB6hPGCV/PU6bSE58Y/DVNpk +7NUM7PM5UyhPddY2PC0A78Ole29UFLJzSzLa+b4DTwKBgH9Wh2k4YPnPcRrX89UL +eSwWa1CGq6HWX8Kd5qyz256aeHWuG5nv15+rBt+D7nwajUsqeVkAXz5H/dHuG1xz +jb7RW+pEjx0GVAmIbkM9vOLqEUfHHHPuk4AXCGGZ5sarPiKg4BHKBBsY1dpoO5UH +0j71fRA6zurHnTXDaCLWlUpZ +-----END PRIVATE KEY----- diff --git a/tests/integration/test_postgresql_protocol/configs/users.xml b/tests/integration/test_postgresql_protocol/configs/users.xml new file mode 100644 index 000000000000..86f5b6657c2c --- /dev/null +++ b/tests/integration/test_postgresql_protocol/configs/users.xml @@ -0,0 +1,13 @@ + + + + + + + + + + 123 + + + diff --git a/tests/integration/test_postgresql_protocol/test.py b/tests/integration/test_postgresql_protocol/test.py new file mode 100644 index 000000000000..d9e2dfe32287 --- /dev/null +++ b/tests/integration/test_postgresql_protocol/test.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +from __future__ import print_function + +import datetime +import decimal +import docker +import psycopg2 as py_psql +import psycopg2.extras +import pytest +import os +import sys +import subprocess +import time +import uuid + +from helpers.cluster import ClickHouseCluster + +psycopg2.extras.register_uuid() + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +config_dir = os.path.join(SCRIPT_DIR, './configs') + +cluster = ClickHouseCluster(__file__) +node = cluster.add_instance('node', config_dir=config_dir, env_variables={'UBSAN_OPTIONS': 'print_stacktrace=1'}) + +server_port = 5433 + + +@pytest.fixture(scope="module") +def server_address(): + cluster.start() + try: + yield cluster.get_instance_ip('node') + finally: + cluster.shutdown() + + +@pytest.fixture(scope='module') +def psql_client(): + docker_compose = os.path.join(SCRIPT_DIR, 'clients', 'psql', 'docker_compose.yml') + subprocess.check_call(['docker-compose', '-p', cluster.project_name, '-f', docker_compose, 'up', '--no-recreate', '-d', '--build']) + yield docker.from_env().containers.get(cluster.project_name + '_psql_1') + + +@pytest.fixture(scope='module') +def psql_server(psql_client): + """Return PostgreSQL container when it is healthy.""" + retries = 30 + for i in range(retries): + info = psql_client.client.api.inspect_container(psql_client.name) + if info['State']['Health']['Status'] == 'healthy': + break + time.sleep(1) + else: + print(info['State']) + raise Exception('PostgreSQL server has not started after {} retries.'.format(retries)) + + return psql_client + + +@pytest.fixture(scope='module') +def java_container(): + docker_compose = os.path.join(SCRIPT_DIR, 'clients', 'java', 'docker_compose.yml') + subprocess.check_call(['docker-compose', '-p', cluster.project_name, '-f', docker_compose, 'up', '--no-recreate', '-d', '--build']) + yield docker.from_env().containers.get(cluster.project_name + '_java_1') + + +def test_psql_is_ready(psql_server): + pass + + +def test_psql_client(psql_client, server_address): + cmd_prefix = 'psql "sslmode=require host={server_address} port={server_port} user=default dbname=default password=123" '\ + .format(server_address=server_address, server_port=server_port) + cmd_prefix += "--no-align --field-separator=' ' " + + code, (stdout, stderr) = psql_client.exec_run(cmd_prefix + '-c "SELECT 1 as a"', demux=True) + assert stdout == '\n'.join(['a', '1', '(1 row)', '']) + + code, (stdout, stderr) = psql_client.exec_run(cmd_prefix + '''-c "SELECT 'колонка' as a"''', demux=True) + assert stdout == '\n'.join(['a', 'колонка', '(1 row)', '']) + + code, (stdout, stderr) = psql_client.exec_run( + cmd_prefix + '-c ' + + ''' + "CREATE DATABASE x; + USE x; + CREATE TABLE table1 (column UInt32) ENGINE = Memory; + INSERT INTO table1 VALUES (0), (1), (5); + INSERT INTO table1 VALUES (0), (1), (5); + SELECT * FROM table1 ORDER BY column;" + ''', + demux=True + ) + assert stdout == '\n'.join(['column', '0', '0', '1', '1', '5', '5', '(6 rows)', '']) + + code, (stdout, stderr) = psql_client.exec_run( + cmd_prefix + '-c ' + + ''' + "DROP DATABASE x; + CREATE TEMPORARY TABLE tmp (tmp_column UInt32); + INSERT INTO tmp VALUES (0), (1); + SELECT * FROM tmp ORDER BY tmp_column;" + ''', + demux=True + ) + assert stdout == '\n'.join(['tmp_column', '0', '1', '(2 rows)', '']) + + +def test_python_client(server_address): + with pytest.raises(py_psql.InternalError) as exc_info: + ch = py_psql.connect(host=server_address, port=server_port, user='default', password='123', database='') + cur = ch.cursor() + cur.execute('select name from tables;') + + assert exc_info.value.args == ("Query execution failed.\nDB::Exception: Table default.tables doesn't exist.\nSSL connection has been closed unexpectedly\n",) + + ch = py_psql.connect(host=server_address, port=server_port, user='default', password='123', database='') + cur = ch.cursor() + + cur.execute('select 1 as a, 2 as b') + assert (cur.description[0].name, cur.description[1].name) == ('a', 'b') + assert cur.fetchall() == [(1, 2)] + + cur.execute('CREATE DATABASE x') + cur.execute('USE x') + cur.execute('CREATE TEMPORARY TABLE tmp2 (ch Int8, i64 Int64, f64 Float64, str String, date Date, dec Decimal(19, 10), uuid UUID) ENGINE = Memory') + cur.execute("insert into tmp2 (ch, i64, f64, str, date, dec, uuid) values (44, 534324234, 0.32423423, 'hello', '2019-01-23', 0.333333, '61f0c404-5cb3-11e7-907b-a6006ad3dba0')") + cur.execute('select * from tmp2') + assert cur.fetchall()[0] == ('44', 534324234, 0.32423423, 'hello', datetime.date(2019, 1, 23), decimal.Decimal('0.3333330000'), uuid.UUID('61f0c404-5cb3-11e7-907b-a6006ad3dba0')) + + +def test_java_client(server_address, java_container): + with open(os.path.join(SCRIPT_DIR, 'clients', 'java', '0.reference')) as fp: + reference = fp.read() + + # database not exists exception. + code, (stdout, stderr) = java_container.exec_run('java JavaConnectorTest --host {host} --port {port} --user default --database ' + 'abc'.format(host=server_address, port=server_port), demux=True) + assert code == 1 + + # non-empty password passed. + code, (stdout, stderr) = java_container.exec_run('java JavaConnectorTest --host {host} --port {port} --user default --password 123 --database ' + 'default'.format(host=server_address, port=server_port), demux=True) + print(stdout, stderr, file=sys.stderr) + assert code == 0 + assert stdout == reference From 142c63e487c0bc0adacf64773113272fadff1875 Mon Sep 17 00:00:00 2001 From: MovElb Date: Sat, 30 May 2020 23:02:11 +0300 Subject: [PATCH 0220/1102] done rebase --- programs/server/Server.cpp | 16 ++++++++++++++++ src/Core/PostgreSQLProtocol.h | 5 +++-- .../server => src/Server}/PostgreSQLHandler.cpp | 0 .../server => src/Server}/PostgreSQLHandler.h | 1 + .../Server}/PostgreSQLHandlerFactory.cpp | 5 ++--- .../Server}/PostgreSQLHandlerFactory.h | 4 +++- src/Server/ya.make | 2 ++ 7 files changed, 27 insertions(+), 6 deletions(-) rename {programs/server => src/Server}/PostgreSQLHandler.cpp (100%) rename {programs/server => src/Server}/PostgreSQLHandler.h (98%) rename {programs/server => src/Server}/PostgreSQLHandlerFactory.cpp (81%) rename {programs/server => src/Server}/PostgreSQLHandlerFactory.h (91%) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index 04324d345740..d991c096ecaf 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #if !defined(ARCADIA_BUILD) @@ -935,6 +936,21 @@ int Server::main(const std::vector & /*args*/) LOG_INFO(log, "Listening for MySQL compatibility protocol: {}", address.toString()); }); + create_server("postgresql_port", [&](UInt16 port) + { + Poco::Net::ServerSocket socket; + auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true); + socket.setReceiveTimeout(Poco::Timespan()); + socket.setSendTimeout(settings.send_timeout); + servers.emplace_back(std::make_unique( + new PostgreSQLHandlerFactory(*this), + server_pool, + socket, + new Poco::Net::TCPServerParams)); + + LOG_INFO(log, "Listening for PostgreSQL compatibility protocol: " + address.toString()); + }); + /// Prometheus (if defined and not setup yet with http_port) create_server("prometheus.port", [&](UInt16 port) { diff --git a/src/Core/PostgreSQLProtocol.h b/src/Core/PostgreSQLProtocol.h index b5fa67d68ea3..3acf0b02ce25 100644 --- a/src/Core/PostgreSQLProtocol.h +++ b/src/Core/PostgreSQLProtocol.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -800,7 +801,7 @@ class AuthenticationMethod const Poco::Net::SocketAddress & address) { try { - context.setUser(user_name, password, address, ""); + context.setUser(user_name, password, address); } catch (const Exception &) { @@ -897,7 +898,7 @@ class AuthenticationManager { type_to_method[user_auth_type]->authenticate(user_name, context, mt, address); mt.send(Messaging::AuthenticationOk(), true); - LOG_INFO(log, "Authentication for user " << user_name << " was successful."); + LOG_INFO(log, "Authentication for user {} was successful.", user_name); return; } diff --git a/programs/server/PostgreSQLHandler.cpp b/src/Server/PostgreSQLHandler.cpp similarity index 100% rename from programs/server/PostgreSQLHandler.cpp rename to src/Server/PostgreSQLHandler.cpp diff --git a/programs/server/PostgreSQLHandler.h b/src/Server/PostgreSQLHandler.h similarity index 98% rename from programs/server/PostgreSQLHandler.h rename to src/Server/PostgreSQLHandler.h index 1062fed5cbbd..28b9ef9a3508 100644 --- a/programs/server/PostgreSQLHandler.h +++ b/src/Server/PostgreSQLHandler.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "IServer.h" #if USE_SSL diff --git a/programs/server/PostgreSQLHandlerFactory.cpp b/src/Server/PostgreSQLHandlerFactory.cpp similarity index 81% rename from programs/server/PostgreSQLHandlerFactory.cpp rename to src/Server/PostgreSQLHandlerFactory.cpp index 210d8558bfd8..ce433188c04f 100644 --- a/programs/server/PostgreSQLHandlerFactory.cpp +++ b/src/Server/PostgreSQLHandlerFactory.cpp @@ -1,8 +1,7 @@ #include "PostgreSQLHandlerFactory.h" -#include "IServer.h" #include #include -#include "PostgreSQLHandler.h" +#include namespace DB { @@ -21,7 +20,7 @@ PostgreSQLHandlerFactory::PostgreSQLHandlerFactory(IServer & server_) Poco::Net::TCPServerConnection * PostgreSQLHandlerFactory::createConnection(const Poco::Net::StreamSocket & socket) { Int32 connection_id = last_connection_id++; - LOG_TRACE(log, "PostgreSQL connection. Id: " << connection_id << ". Address: " << socket.peerAddress().toString()); + LOG_TRACE(log, "PostgreSQL connection. Id: {}. Address: {}", connection_id, socket.peerAddress().toString()); return new PostgreSQLHandler(socket, server, ssl_enabled, connection_id, auth_methods); } diff --git a/programs/server/PostgreSQLHandlerFactory.h b/src/Server/PostgreSQLHandlerFactory.h similarity index 91% rename from programs/server/PostgreSQLHandlerFactory.h rename to src/Server/PostgreSQLHandlerFactory.h index a95a22c162ca..0546b4ef8c2f 100644 --- a/programs/server/PostgreSQLHandlerFactory.h +++ b/src/Server/PostgreSQLHandlerFactory.h @@ -1,7 +1,9 @@ #pragma once -#include "IServer.h" #include +#include +#include +#include #include namespace DB diff --git a/src/Server/ya.make b/src/Server/ya.make index 1d689ee73b86..6e01e2599e4a 100644 --- a/src/Server/ya.make +++ b/src/Server/ya.make @@ -11,6 +11,8 @@ SRCS( InterserverIOHTTPHandler.cpp MySQLHandler.cpp MySQLHandlerFactory.cpp + PostgreSQLHandler.cpp + PostgreSQLHandlerFactory.cpp NotFoundHandler.cpp PrometheusMetricsWriter.cpp PrometheusRequestHandler.cpp From 8266715c492749e035f4bd764f5c75d3620af73e Mon Sep 17 00:00:00 2001 From: kssenii Date: Sun, 31 May 2020 08:39:22 +0000 Subject: [PATCH 0221/1102] Fix build & fix style --- src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp | 3 +-- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 2 +- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp | 3 +++ src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 10 ++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index d498a36f95b9..86d760be54a0 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -64,8 +64,7 @@ Block RabbitMQBlockInputStream::readImpl() MutableColumns result_columns = non_virtual_header.cloneEmptyColumns(); MutableColumns virtual_columns = virtual_header.cloneEmptyColumns(); - auto input_format = FormatFactory::instance().getInputFormat( - storage.getFormatName(), *buffer, non_virtual_header, context, 1); + auto input_format = FormatFactory::instance().getInputFormat(storage.getFormatName(), *buffer, non_virtual_header, context, 1); InputPort port(input_format->getPort().getHeader(), input_format.get()); connect(input_format->getPort(), port); diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 1f6e9ce1bb15..cebe8ee3c3ac 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -12,7 +12,7 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : } -void RabbitMQHandler::onError(AMQP::TcpConnection * , const char * message) +void RabbitMQHandler::onError(AMQP::TcpConnection * /* connection */, const char * message) { LOG_ERROR(log, "Library error report: {}", message); stop(); diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index d6da58504724..d6372dfe4d3c 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -1,6 +1,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index ed486e8e7093..5f7570dd8c12 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -45,10 +45,8 @@ namespace DB namespace ErrorCodes { - extern const int NOT_IMPLEMENTED; extern const int LOGICAL_ERROR; extern const int BAD_ARGUMENTS; - extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } @@ -157,6 +155,7 @@ void StorageRabbitMQ::shutdown() popReadBuffer(); } + connection.close(); task->deactivate(); } @@ -201,8 +200,10 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() next_channel_id += num_queues; update_channel_id = true; + ChannelPtr consumer_channel = std::make_shared(&connection); + return std::make_shared( - std::make_shared(&connection), eventHandler, exchange_name, routing_key, next_channel_id, + consumer_channel, eventHandler, exchange_name, routing_key, next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); } @@ -460,7 +461,8 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } - return StorageRabbitMQ::create(args.table_id, args.context, args.columns, host_port, routing_key, exchange, + return StorageRabbitMQ::create( + args.table_id, args.context, args.columns, host_port, routing_key, exchange, format, row_delimiter, num_consumers, num_queues, hash_exchange); }; From 037ed3a02ce5bc0ed3c49b9dd24b13359286828b Mon Sep 17 00:00:00 2001 From: kssenii Date: Sun, 31 May 2020 09:34:57 +0000 Subject: [PATCH 0222/1102] Code fix & style fix & merge fix --- .../RabbitMQ/RabbitMQBlockInputStream.cpp | 5 ++-- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 8 ++++- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 29 +++++++++---------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index 86d760be54a0..1c6eaf6f2e90 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -64,8 +64,9 @@ Block RabbitMQBlockInputStream::readImpl() MutableColumns result_columns = non_virtual_header.cloneEmptyColumns(); MutableColumns virtual_columns = virtual_header.cloneEmptyColumns(); - auto input_format = FormatFactory::instance().getInputFormat(storage.getFormatName(), *buffer, non_virtual_header, context, 1); - + auto input_format = FormatFactory::instance().getInputFormat( + storage.getFormatName(), *buffer, non_virtual_header, context, 1); + InputPort port(input_format->getPort().getHeader(), input_format.get()); connect(input_format->getPort(), port); port.setNeeded(); diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index cebe8ee3c3ac..09398da73c74 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -12,9 +12,15 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : } -void RabbitMQHandler::onError(AMQP::TcpConnection * /* connection */, const char * message) +void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * message) { LOG_ERROR(log, "Library error report: {}", message); + if (!connection->ready()) + { + std::cerr << "Connection lost, no recovery is possible"; + throw; + } + stop(); } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 5f7570dd8c12..b1060a59e006 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -32,16 +32,14 @@ #include #include +namespace DB +{ enum - { - RESCHEDULE_WAIT = 500, +{ Connection_setup_sleep = 200, Connection_setup_retries_max = 1000 - }; - -namespace DB -{ +}; namespace ErrorCodes { @@ -77,8 +75,7 @@ StorageRabbitMQ::StorageRabbitMQ( , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) , evbase(event_base_new()) , eventHandler(evbase, log) - , connection(&eventHandler, - AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) + , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) { size_t cnt_retries = 0; while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) @@ -136,9 +133,10 @@ void StorageRabbitMQ::startup() pushReadBuffer(createReadBuffer()); ++num_created_consumers; } - catch (const AMQP::Exception &) + catch (const AMQP::Exception & e) { - tryLogCurrentException(log); + std::cerr << e.what(); + throw; } } @@ -202,9 +200,8 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ChannelPtr consumer_channel = std::make_shared(&connection); - return std::make_shared( - consumer_channel, eventHandler, exchange_name, routing_key, next_channel_id, - log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); + return std::make_shared(consumer_channel, eventHandler, exchange_name, + routing_key, next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); } @@ -266,7 +263,7 @@ void StorageRabbitMQ::threadFunc() /// Wait for attached views if (!stream_cancelled) - task->scheduleAfter(RESCHEDULE_WAIT); + task->activateAndSchedule(); } @@ -462,8 +459,8 @@ void registerStorageRabbitMQ(StorageFactory & factory) } return StorageRabbitMQ::create( - args.table_id, args.context, args.columns, host_port, routing_key, exchange, - format, row_delimiter, num_consumers, num_queues, hash_exchange); + args.table_id, args.context, args.columns, + host_port, routing_key, exchange, format, row_delimiter, num_consumers, num_queues, hash_exchange); }; factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); From fd5b1741a6e94522514975ca122d8fb90f6271a3 Mon Sep 17 00:00:00 2001 From: MovElb Date: Sun, 31 May 2020 17:05:41 +0300 Subject: [PATCH 0223/1102] build fix --- src/Server/PostgreSQLHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/PostgreSQLHandler.h b/src/Server/PostgreSQLHandler.h index 28b9ef9a3508..31a96f478ecf 100644 --- a/src/Server/PostgreSQLHandler.h +++ b/src/Server/PostgreSQLHandler.h @@ -70,7 +70,7 @@ class PostgreSQLHandler : public Poco::Net::TCPServerConnection void processQuery(); - bool isEmptyQuery(const String & query); + static bool isEmptyQuery(const String & query); }; } From 3543da3ca463fc4deb1002e5b5bf91df13306931 Mon Sep 17 00:00:00 2001 From: Sofia Antipushina Date: Sun, 31 May 2020 17:44:49 +0300 Subject: [PATCH 0224/1102] fix stylecheck --- src/AggregateFunctions/AggregateFunctionDistinct.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 57e17ffb13c9..32f5df6d8f05 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -106,7 +106,8 @@ class AggregateFunctionDistinct final : public IAggregateFunctionDataHelperupdateHashWithValue(row_num, hash); } From fd3279f9f16e704d78a0fd425216679651bc45ac Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 00:02:08 +0300 Subject: [PATCH 0225/1102] trigger ci --- docs/en/operations/server-configuration-parameters/settings.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index 194293d5a194..a3a6d1a09559 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -364,7 +364,6 @@ Keys: - `debug` - Sets the Sentry client into debug mode. - `tmp_path` - Filesystem path for temporary crash report state. - **Recommended way to use** ``` xml From 81989bd95a91f0e1d70fb49bcfb4167ecbdd59c1 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 10:51:22 +0300 Subject: [PATCH 0226/1102] submodule via https --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index daa5d12a62c9..4175eb223db7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = git@github.com:getsentry/sentry-native.git + url = https://github.com/getsentry/sentry-native.git From ba112e84cb10891cfdfa561ef6da4fd40693693e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 13:30:11 +0300 Subject: [PATCH 0227/1102] trigger ci From 3e0811f297cfdc77c657aa0a8ce04eb387e55ec6 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 17:15:14 +0300 Subject: [PATCH 0228/1102] Adapt to recent logging changes --- base/daemon/SentryWriter.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 2fd846b720a6..f7edc8d1e937 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -44,6 +44,7 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) #if USE_SENTRY bool enabled = false; bool debug = config.getBool("send_crash_reports.debug", false); + auto logger = &Poco::Logger::get("SentryWriter"); if (config.getBool("send_crash_reports.enabled", false)) { if (debug || (strlen(VERSION_OFFICIAL) > 0)) @@ -89,7 +90,7 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) anonymize = config.getBool("send_crash_reports.anonymize", false); const std::string& anonymize_status = anonymize ? " (anonymized)" : ""; LOG_INFO( - &Logger::get("SentryWriter"), + logger, "Sending crash reports is initialized with {} endpoint and {} temp folder{}", endpoint, temp_folder_path, @@ -97,12 +98,12 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) } else { - LOG_WARNING(&Logger::get("SentryWriter"), "Sending crash reports failed to initialized with {} status", init_status); + LOG_WARNING(logger, "Sending crash reports failed to initialized with {} status", init_status); } } else { - LOG_INFO(&Logger::get("SentryWriter"), "Sending crash reports is disabled"); + LOG_INFO(logger, "Sending crash reports is disabled"); } #endif } @@ -120,6 +121,7 @@ void SentryWriter::shutdown() void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & context, const StackTrace & stack_trace) { #if USE_SENTRY + auto logger = &Poco::Logger::get("SentryWriter"); if (initialized) { const std::string & error_message = signalToErrorMessage(sig, info, context); @@ -181,13 +183,13 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c sentry_value_set_by_key(event, "threads", threads); - LOG_INFO(&Logger::get("SentryWriter"), "Sending crash report"); + LOG_INFO(logger, "Sending crash report"); sentry_capture_event(event); shutdown(); } else { - LOG_INFO(&Logger::get("SentryWriter"), "Not sending crash report"); + LOG_INFO(logger, "Not sending crash report"); } #endif } From 9ad1bb8d9398cb83a95b39df8d36a17d340afc16 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 18:26:20 +0300 Subject: [PATCH 0229/1102] trigger ci From acf22bfb19292c5ae56e54dcedd06895577e2914 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 19:42:59 +0300 Subject: [PATCH 0230/1102] fix sanitizers build --- base/daemon/BaseDaemon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index 3aeebd369e50..9da8849342d9 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -317,7 +317,7 @@ static void sanitizerDeathCallback() std::stringstream bare_stacktrace; bare_stacktrace << "Stack trace:"; for (size_t i = stack_trace.getOffset(); i < stack_trace.getSize(); ++i) - bare_stacktrace << ' ' << stack_trace.getFrames()[i]; + bare_stacktrace << ' ' << stack_trace.getFramePointers()[i]; LOG_FATAL(log, bare_stacktrace.str()); } From 1ce25238f80fc9435c82766f44da896639e97ee1 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 19:49:11 +0300 Subject: [PATCH 0231/1102] try fix some more build issues --- cmake/find/sentry.cmake | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index f94b53ffb009..30b8b28f6f1a 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,15 +5,17 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -option (USE_SENTRY "Use Sentry" ON) +if (NOT OS_FREEBSD AND NOT UNBUNDLED) + option (USE_SENTRY "Use Sentry" ON) -set (BUILD_SHARED_LIBS OFF) -set (SENTRY_PIC OFF) -set (SENTRY_BACKEND "none") -set (SENTRY_TRANSPORT "curl") -set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) -set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) + set (BUILD_SHARED_LIBS OFF) + set (SENTRY_PIC OFF) + set (SENTRY_BACKEND "none") + set (SENTRY_TRANSPORT "curl") + set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) + set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) -message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") + message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") -include_directories("${SENTRY_INCLUDE_DIR}") \ No newline at end of file + include_directories("${SENTRY_INCLUDE_DIR}") +endif () \ No newline at end of file From 5757dd1d57c68a8f03ddc5b9ba41e85d584f909c Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 1 Jun 2020 15:37:23 +0000 Subject: [PATCH 0232/1102] Add insert part --- src/Storages/RabbitMQ/Buffer_fwd.h | 3 + .../RabbitMQ/RabbitMQBlockOutputStream.cpp | 57 ++++++ .../RabbitMQ/RabbitMQBlockOutputStream.h | 29 +++ src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 16 ++ src/Storages/RabbitMQ/StorageRabbitMQ.h | 7 + .../WriteBufferToRabbitMQProducer.cpp | 169 ++++++++++++++++++ .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 67 +++++++ 7 files changed, 348 insertions(+) create mode 100644 src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp create mode 100644 src/Storages/RabbitMQ/RabbitMQBlockOutputStream.h create mode 100644 src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp create mode 100644 src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h diff --git a/src/Storages/RabbitMQ/Buffer_fwd.h b/src/Storages/RabbitMQ/Buffer_fwd.h index f0ef010c5182..5be2c6fdf6a7 100644 --- a/src/Storages/RabbitMQ/Buffer_fwd.h +++ b/src/Storages/RabbitMQ/Buffer_fwd.h @@ -8,4 +8,7 @@ namespace DB class ReadBufferFromRabbitMQConsumer; using ConsumerBufferPtr = std::shared_ptr; +class WriteBufferToRabbitMQProducer; +using ProducerBufferPtr = std::shared_ptr; + } diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp new file mode 100644 index 000000000000..3f940891c236 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + + +namespace DB +{ + +namespace ErrorCodes +{ + extern int CANNOT_CREATE_IO_BUFFER; +} + + +RabbitMQBlockOutputStream::RabbitMQBlockOutputStream( + StorageRabbitMQ & storage_, const Context & context_) : storage(storage_), context(context_) +{ +} + + +Block RabbitMQBlockOutputStream::getHeader() const +{ + return storage.getSampleBlockNonMaterialized(); +} + + +void RabbitMQBlockOutputStream::writePrefix() +{ + buffer = storage.createWriteBuffer(); + if (!buffer) + throw Exception("Failed to create RabbitMQ producer!", ErrorCodes::CANNOT_CREATE_IO_BUFFER); + + child = FormatFactory::instance().getOutput( + storage.getFormatName(), *buffer, getHeader(), context, [this](const Columns & /* columns */, size_t /* rows */) + { + buffer->count_row(); + }); +} + + +void RabbitMQBlockOutputStream::write(const Block & block) +{ + child->write(block); + + if (buffer) + buffer->flush(); +} + + +void RabbitMQBlockOutputStream::writeSuffix() +{ + child->writeSuffix(); +} + +} diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.h b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.h new file mode 100644 index 000000000000..2f7b89a2a305 --- /dev/null +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + + +namespace DB +{ + +class RabbitMQBlockOutputStream : public IBlockOutputStream +{ + +public: + explicit RabbitMQBlockOutputStream(StorageRabbitMQ & storage_, const Context & context_); + + Block getHeader() const override; + + void writePrefix() override; + void write(const Block & block) override; + void writeSuffix() override; + +private: + StorageRabbitMQ & storage; + Context context; + ProducerBufferPtr buffer; + BlockOutputStreamPtr child; +}; +} diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index fb705e4d1bc0..ee5dede52610 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -124,6 +126,12 @@ Pipes StorageRabbitMQ::read( } +BlockOutputStreamPtr StorageRabbitMQ::write(const ASTPtr &, const Context & context) +{ + return std::make_shared(*this, context); +} + + void StorageRabbitMQ::startup() { for (size_t i = 0; i < num_consumers; ++i) @@ -205,6 +213,14 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() } +ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() +{ + return std::make_shared(parsed_address, routing_key, exchange_name, + log, num_consumers, bind_by_id, hash_exchange, + row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); +} + + bool StorageRabbitMQ::checkDependencies(const StorageID & table_id) { // Check if all dependencies are attached diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index fc098b168f15..5aa77a9a7325 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -25,6 +25,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub std::string getName() const override { return "RabbitMQ"; } bool supportsSettings() const override { return true; } + bool noPushingToViews() const override { return true; } void startup() override; void shutdown() override; @@ -37,10 +38,16 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub size_t max_block_size, unsigned num_streams) override; + BlockOutputStreamPtr write( + const ASTPtr & query, + const Context & context) override; + void pushReadBuffer(ConsumerBufferPtr buf); ConsumerBufferPtr popReadBuffer(); ConsumerBufferPtr popReadBuffer(std::chrono::milliseconds timeout); + ProducerBufferPtr createWriteBuffer(); + const String & getExchangeName() const { return exchange_name; } const String & getRoutingKey() const { return routing_key; } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp new file mode 100644 index 000000000000..529cc5bd93b6 --- /dev/null +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -0,0 +1,169 @@ +#include +#include "Core/Block.h" +#include "Columns/ColumnString.h" +#include "Columns/ColumnsNumber.h" +#include +#include +#include +#include + + +namespace DB +{ + +enum +{ + Connection_setup_sleep = 200, + Connection_setup_retries_max = 1000, + Buffer_limit_to_flush = 50000 +}; + +WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( + std::pair & parsed_address, + const String & routing_key_, + const String & exchange_, + Poco::Logger * log_, + const size_t num_queues_, + const bool bind_by_id_, + const bool hash_exchange_, + std::optional delimiter, + size_t rows_per_message, + size_t chunk_size_) + : WriteBuffer(nullptr, 0) + , routing_key(routing_key_) + , exchange_name(exchange_) + , log(log_) + , num_queues(num_queues_) + , bind_by_id(bind_by_id_) + , hash_exchange(hash_exchange_) + , delim(delimiter) + , max_rows(rows_per_message) + , chunk_size(chunk_size_) + , producerEvbase(event_base_new()) + , eventHandler(producerEvbase, log) + , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) +{ + /* The reason behind making a separate connection for each concurrent producer is explained here: + * https://github.com/CopernicaMarketingSoftware/AMQP-CPP/issues/128#issuecomment-300780086 + * - publishing from different threads (as outputStreams are asynchronous) leads to internal libary errors. + */ + size_t cnt_retries = 0; + while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) + { + event_base_loop(producerEvbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); + std::this_thread::sleep_for(std::chrono::milliseconds(Connection_setup_sleep)); + } + + if (!connection.ready()) + { + LOG_ERROR(log, "Cannot set up connection for producer!"); + } + + producer_channel = std::make_shared(&connection); +} + + +WriteBufferToRabbitMQProducer::~WriteBufferToRabbitMQProducer() +{ + flush(); + connection.close(); + + assert(rows == 0 && chunks.empty()); +} + + +void WriteBufferToRabbitMQProducer::count_row() +{ + if (++rows % max_rows == 0) + { + const std::string & last_chunk = chunks.back(); + size_t last_chunk_size = offset(); + + if (delim && last_chunk[last_chunk_size - 1] == delim) + --last_chunk_size; + + std::string payload; + payload.reserve((chunks.size() - 1) * chunk_size + last_chunk_size); + + for (auto i = chunks.begin(), e = --chunks.end(); i != e; ++i) + payload.append(*i); + + payload.append(last_chunk, 0, last_chunk_size); + + rows = 0; + chunks.clear(); + set(nullptr, 0); + + messages.emplace_back(payload); + ++message_counter; + + if (messages.size() >= Buffer_limit_to_flush) + { + flush(); + } + } +} + + +void WriteBufferToRabbitMQProducer::flush() +{ + /* Why accumulating payloads and not publishing each of them at once in count_row()? Because publishing needs to + * be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it each time + * we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to + * exchange becoming inactive at some point and part of messages is lost as a result. + */ + std::atomic exchange_declared = false, exchange_error = false; + + producer_channel->declareExchange(exchange_name + "_direct", AMQP::direct, AMQP::passive) + .onSuccess([&]() + { + for (auto & payload : messages) + { + if (!message_counter) + return; + + next_queue = next_queue % num_queues + 1; + + if (bind_by_id || hash_exchange) + { + producer_channel->publish(exchange_name, std::to_string(next_queue), payload); + } + else + { + producer_channel->publish(exchange_name, routing_key, payload); + } + + --message_counter; + } + + exchange_declared = true; + messages.clear(); + }) + .onError([&](const char * message) + { + exchange_error = true; + exchange_declared = false; + LOG_ERROR(log, "Exchange was not declared: {}", message); + }); + + while (!exchange_declared && !exchange_error) + { + startEventLoop(exchange_declared); + } +} + + +void WriteBufferToRabbitMQProducer::nextImpl() +{ + chunks.push_back(std::string()); + chunks.back().resize(chunk_size); + set(chunks.back().data(), chunk_size); +} + + +void WriteBufferToRabbitMQProducer::startEventLoop(std::atomic & check_param) +{ + eventHandler.start(check_param); +} + +} diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h new file mode 100644 index 000000000000..d7a1715d491f --- /dev/null +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ + +using ProducerPtr = std::shared_ptr; +using Messages = std::vector; + +class WriteBufferToRabbitMQProducer : public WriteBuffer +{ +public: + WriteBufferToRabbitMQProducer( + std::pair & parsed_address, + const String & routing_key_, + const String & exchange_, + Poco::Logger * log_, + const size_t num_queues_, + const bool bind_by_id_, + const bool hash_exchange_, + std::optional delimiter, + size_t rows_per_message, + size_t chunk_size_ + ); + + ~WriteBufferToRabbitMQProducer() override; + + void count_row(); + void flush(); + +private: + void nextImpl() override; + void checkExchange(); + void startEventLoop(std::atomic & check_param); + + const String routing_key; + const String exchange_name; + const bool bind_by_id; + const bool hash_exchange; + const size_t num_queues; + + event_base * producerEvbase; + RabbitMQHandler eventHandler; + AMQP::TcpConnection connection; + ProducerPtr producer_channel; + + size_t next_queue = 0; + UInt64 message_counter = 0; + String channel_id; + + Messages messages; + + Poco::Logger * log; + const std::optional delim; + const size_t max_rows; + const size_t chunk_size; + size_t count_mes = 0; + size_t rows = 0; + std::list chunks; +}; + +} From 5939422b85c44038f2345b99810d95ed5bb090a3 Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 1 Jun 2020 16:19:59 +0000 Subject: [PATCH 0233/1102] Add tests for insert part --- .../integration/test_storage_rabbitmq/test.py | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 821c5a19e680..0533dd7e2f4b 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -838,6 +838,240 @@ def produce(): assert int(result) == messages_num * threads_num * NUM_MV, 'ClickHouse lost some messages: {}'.format(result) +@pytest.mark.timeout(180) +def test_rabbitmq_insert(rabbitmq_cluster): + instance.query(''' + CREATE TABLE test.rabbitmq (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'insert1', + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + ''') + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + consumer_connection = pika.BlockingConnection(parameters) + + consumer = consumer_connection.channel() + consumer.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + result = consumer.queue_declare(queue='') + queue_name = result.method.queue + consumer.queue_bind(exchange='clickhouse-exchange', queue=queue_name, routing_key='insert1') + + values = [] + for i in range(50): + values.append("({i}, {i})".format(i=i)) + values = ','.join(values) + + while True: + try: + instance.query("INSERT INTO test.rabbitmq VALUES {}".format(values)) + break + except QueryRuntimeException as e: + if 'Local: Timed out.' in str(e): + continue + else: + raise + + insert_messages = [] + def onReceived(channel, method, properties, body): + i = 0 + insert_messages.append(body.decode()) + if (len(insert_messages) == 50): + channel.stop_consuming() + + consumer.basic_qos(prefetch_count=50) + consumer.basic_consume(onReceived, queue_name) + consumer.start_consuming() + consumer_connection.close() + + result = '\n'.join(insert_messages) + rabbitmq_check_result(result, True) + + +@pytest.mark.timeout(240) +def test_rabbitmq_many_inserts(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.rabbitmq_many; + DROP TABLE IF EXISTS test.view_many; + DROP TABLE IF EXISTS test.consumer_many; + CREATE TABLE test.rabbitmq_many (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_routing_key = 'insert2', + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view_many (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer_many TO test.view_many AS + SELECT * FROM test.rabbitmq_many; + ''') + + messages_num = 1000 + def insert(): + values = [] + for i in range(messages_num): + values.append("({i}, {i})".format(i=i)) + values = ','.join(values) + + while True: + try: + instance.query("INSERT INTO test.rabbitmq_many VALUES {}".format(values)) + break + except QueryRuntimeException as e: + if 'Local: Timed out.' in str(e): + continue + else: + raise + + threads = [] + threads_num = 20 + for _ in range(threads_num): + threads.append(threading.Thread(target=insert)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view_many') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + instance.query(''' + DROP TABLE test.consumer_many; + DROP TABLE test.view_many; + ''') + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(240) +def test_rabbitmq_sharding_between_channels_insert(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.view_sharding; + DROP TABLE IF EXISTS test.consumer_sharding; + CREATE TABLE test.rabbitmq_sharding (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 5, + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view_sharding (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer_sharding TO test.view_sharding AS + SELECT * FROM test.rabbitmq_sharding; + ''') + + messages_num = 10000 + def insert(): + values = [] + for i in range(messages_num): + values.append("({i}, {i})".format(i=i)) + values = ','.join(values) + + while True: + try: + instance.query("INSERT INTO test.rabbitmq_sharding VALUES {}".format(values)) + break + except QueryRuntimeException as e: + if 'Local: Timed out.' in str(e): + continue + else: + raise + + threads = [] + threads_num = 20 + for _ in range(threads_num): + threads.append(threading.Thread(target=insert)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view_sharding') + time.sleep(1) + print result + if int(result) == messages_num * threads_num: + break + + instance.query(''' + DROP TABLE test.consumer_sharding; + DROP TABLE test.view_sharding; + ''') + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(420) +def test_rabbitmq_overloaded_insert(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.view_overload; + DROP TABLE IF EXISTS test.consumer_overload; + CREATE TABLE test.rabbitmq_overload (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 10, + rabbitmq_format = 'TSV', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view_overload (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.consumer_overload TO test.view_overload AS + SELECT * FROM test.rabbitmq_overload; + ''') + + messages_num = 100000 + def insert(): + values = [] + for i in range(messages_num): + values.append("({i}, {i})".format(i=i)) + values = ','.join(values) + + while True: + try: + instance.query("INSERT INTO test.rabbitmq_overload VALUES {}".format(values)) + break + except QueryRuntimeException as e: + if 'Local: Timed out.' in str(e): + continue + else: + raise + + threads = [] + threads_num = 5 + for _ in range(threads_num): + threads.append(threading.Thread(target=insert)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view_overload') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + instance.query(''' + DROP TABLE test.consumer_overload; + DROP TABLE test.view_overload; + ''') + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + if __name__ == '__main__': cluster.start() raw_input("Cluster created, press any key to destroy...") From 386dc4d95ee269c289fa338bedc4f9cb1d0b9149 Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 1 Jun 2020 16:56:16 +0000 Subject: [PATCH 0234/1102] Fixes --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 1 - src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp | 8 ++------ src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 09398da73c74..547851f349aa 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -44,7 +44,6 @@ void RabbitMQHandler::start(std::atomic & check_param) void RabbitMQHandler::stop() { - std::lock_guard lock(mutex); event_base_loopbreak(evbase); } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index d6372dfe4d3c..27c5ab800f02 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -207,9 +207,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) if (row_delimiter != '\0') message_received += row_delimiter; - /* Needed because this vector can be used at the same time by another thread in nextImpl() (below). - * So we lock mutex here and there so that they do not use it asynchronosly. - */ + /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl() (below). std::lock_guard lock(mutex); received.push_back(message_received); } @@ -255,9 +253,7 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() messages.clear(); - /* Needed because this vector can be used at the same time by another thread in onReceived callback (above). - * So we lock mutex here and there so that they do not use it asynchronosly. - */ + /// Needed to avoid data race because this vector can be used at the same time by another thread in onReceived callback (above). std::lock_guard lock(mutex); messages.swap(received); diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 529cc5bd93b6..12d6c2b0fb8c 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -15,7 +15,7 @@ enum { Connection_setup_sleep = 200, Connection_setup_retries_max = 1000, - Buffer_limit_to_flush = 50000 + Buffer_limit_to_flush = 10000 /// It is important to keep it low in order not to kill consumers }; WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( From f6e69355faa1f131fd22bfca93bc1cee0c1aca1e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 21:10:19 +0300 Subject: [PATCH 0235/1102] experiment --- src/Common/StackTrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index aa78ab62f9b4..526edd7792fc 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include #include #include From 1797a47a9f26d4a97e8aa044af5a45a6c34e6d4f Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 21:25:25 +0300 Subject: [PATCH 0236/1102] fix clang warnings --- base/daemon/SentryWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index f7edc8d1e937..6bfc07ea2fbf 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -17,8 +17,8 @@ namespace { -static bool initialized = false; -static bool anonymize = false; +bool initialized = false; +bool anonymize = false; void setExtras() { @@ -44,7 +44,7 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) #if USE_SENTRY bool enabled = false; bool debug = config.getBool("send_crash_reports.debug", false); - auto logger = &Poco::Logger::get("SentryWriter"); + auto * logger = &Poco::Logger::get("SentryWriter"); if (config.getBool("send_crash_reports.enabled", false)) { if (debug || (strlen(VERSION_OFFICIAL) > 0)) @@ -121,7 +121,7 @@ void SentryWriter::shutdown() void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & context, const StackTrace & stack_trace) { #if USE_SENTRY - auto logger = &Poco::Logger::get("SentryWriter"); + auto * logger = &Poco::Logger::get("SentryWriter"); if (initialized) { const std::string & error_message = signalToErrorMessage(sig, info, context); From 8babd4d18c093f44d96e616a31d5551b24e73de2 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 21:36:33 +0300 Subject: [PATCH 0237/1102] experiment --- cmake/find/sentry.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 30b8b28f6f1a..06312b644959 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -6,6 +6,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") endif () if (NOT OS_FREEBSD AND NOT UNBUNDLED) + cmake_policy (SET CMP0077 NEW) option (USE_SENTRY "Use Sentry" ON) set (BUILD_SHARED_LIBS OFF) From 965204dfb161953616b6fa5168bde03375d87205 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 21:48:34 +0300 Subject: [PATCH 0238/1102] Try to fix the msan build --- .gitmodules | 2 +- contrib/sentry-native | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4175eb223db7..ff4e644f657d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/getsentry/sentry-native.git + url = https://github.com/blinkov/sentry-native.git diff --git a/contrib/sentry-native b/contrib/sentry-native index 3bfce2d17c1b..9e214a1265a4 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit 3bfce2d17c1b80fbbaae83bb5ef41c1b290d34fb +Subproject commit 9e214a1265a4ea628c21045b7f43d1aec15e385d From 65ff11aeac99061b53de62ab120d9ff75ae0dc03 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 22:49:00 +0300 Subject: [PATCH 0239/1102] old cmake compatibility --- cmake/find/sentry.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 06312b644959..d10c15cd334c 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -6,7 +6,9 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") endif () if (NOT OS_FREEBSD AND NOT UNBUNDLED) - cmake_policy (SET CMP0077 NEW) + if (POLICY CMP0077) + cmake_policy (SET CMP0077 NEW) + endif () option (USE_SENTRY "Use Sentry" ON) set (BUILD_SHARED_LIBS OFF) From 806fd2739567562f62fae565fea980bdcaea051b Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 1 Jun 2020 20:48:24 +0000 Subject: [PATCH 0240/1102] Fix build & fix style & fix --- src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp | 2 +- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 3 ++- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 2 +- .../RabbitMQ/WriteBufferToRabbitMQProducer.cpp | 13 +++++++------ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp index 3f940891c236..0858e2101dfc 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp @@ -10,7 +10,7 @@ namespace DB namespace ErrorCodes { - extern int CANNOT_CREATE_IO_BUFFER; + extern const int CANNOT_CREATE_IO_BUFFER; } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 547851f349aa..6308e2e0089a 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -15,7 +15,8 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * message) { LOG_ERROR(log, "Library error report: {}", message); - if (!connection->ready()) + + if (!connection->usable() || !connection->ready()) { std::cerr << "Connection lost, no recovery is possible"; throw; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index ee5dede52610..147d3ba21155 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -72,7 +72,7 @@ StorageRabbitMQ::StorageRabbitMQ( , num_consumers(num_consumers_) , num_queues(num_queues_) , hash_exchange(hash_exchange_) - , log(&Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) + , log(&Poco::Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) , semaphore(0, num_consumers_) , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) , evbase(event_base_new()) diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 12d6c2b0fb8c..73434bc0ea66 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace DB @@ -15,7 +16,7 @@ enum { Connection_setup_sleep = 200, Connection_setup_retries_max = 1000, - Buffer_limit_to_flush = 10000 /// It is important to keep it low in order not to kill consumers + Buffer_limit_to_flush = 5000 /// It is important to keep it low in order not to kill consumers }; WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( @@ -44,8 +45,8 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) { /* The reason behind making a separate connection for each concurrent producer is explained here: - * https://github.com/CopernicaMarketingSoftware/AMQP-CPP/issues/128#issuecomment-300780086 - * - publishing from different threads (as outputStreams are asynchronous) leads to internal libary errors. + * https://github.com/CopernicaMarketingSoftware/AMQP-CPP/issues/128#issuecomment-300780086 - publishing from + * different threads (as outputStreams are asynchronous) with the same connection leads to internal libary errors. */ size_t cnt_retries = 0; while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) @@ -107,9 +108,9 @@ void WriteBufferToRabbitMQProducer::count_row() void WriteBufferToRabbitMQProducer::flush() { - /* Why accumulating payloads and not publishing each of them at once in count_row()? Because publishing needs to - * be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it each time - * we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to + /* The reason for accumulating payloads and not publishing each of them at once in count_row() is that publishing + * needs to be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it + * each time we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to * exchange becoming inactive at some point and part of messages is lost as a result. */ std::atomic exchange_declared = false, exchange_error = false; From e9a04f7741550a48d6c963fe4a225bf3ce616141 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 23:48:42 +0300 Subject: [PATCH 0241/1102] more build fixes --- base/daemon/SentryWriter.cpp | 4 ++-- src/Common/StackTrace.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 6bfc07ea2fbf..95189b72e819 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -145,8 +145,8 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c { const StackTrace::Frame & current_frame = stack_trace.getFrames().value()[i]; sentry_value_t frame = sentry_value_new_object(); - unsigned long long frame_ptr = reinterpret_cast(current_frame.virtual_addr); - snprintf(instruction_addr, sizeof(instruction_addr), "0x%llx", frame_ptr); + UInt64 frame_ptr = reinterpret_cast(current_frame.virtual_addr); + std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIu64 "x", frame_ptr); sentry_value_set_by_key(frame, "instruction_addr", sentry_value_new_string(instruction_addr)); if (current_frame.symbol.has_value()) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 526edd7792fc..e71ce1e1139b 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -192,7 +192,7 @@ static void * getCallerAddress(const ucontext_t & context) static void symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames) { -#if defined(__ELF__) && !defined(__FreeBSD__) +#if defined(__ELF__) && !defined(__FreeBSD__) && !defined(ARCADIA_BUILD) const DB::SymbolIndex & symbol_index = DB::SymbolIndex::instance(); std::unordered_map dwarfs; From 5a32d7913524a139ce43b835f53f73e1f0b42943 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Mon, 1 Jun 2020 23:55:32 +0300 Subject: [PATCH 0242/1102] experiment --- cmake/find/sentry.cmake | 2 +- src/Common/StackTrace.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index d10c15cd334c..309f63e9165a 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,7 +5,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD AND NOT UNBUNDLED) +if (NOT OS_FREEBSD AND NOT UNBUNDLED AND NOT SPLITTED AND NOT (COMPILER_CLANG AND OS_DARWIN)) if (POLICY CMP0077) cmake_policy (SET CMP0077 NEW) endif () diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index e71ce1e1139b..dbe3d005be79 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -313,7 +313,7 @@ const StackTrace::Frames & StackTrace::getFrames() const { if (!frames.has_value()) { - frames = {{}}; + frames = std::array(); symbolize(frame_pointers.data(), offset, size, frames); } return frames; From 35734aadde1b7cd0f68cc6a513bab1e8497229f8 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 08:15:11 +0300 Subject: [PATCH 0243/1102] apply comment --- src/Common/StackTrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index dbe3d005be79..793de7709cc7 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -313,7 +313,7 @@ const StackTrace::Frames & StackTrace::getFrames() const { if (!frames.has_value()) { - frames = std::array(); + frames.emplace({}); symbolize(frame_pointers.data(), offset, size, frames); } return frames; From 03f4aa19aa64e10e8433e938931e95400db7fdf6 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 08:16:41 +0300 Subject: [PATCH 0244/1102] apply comment --- src/Common/StackTrace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 793de7709cc7..819f74f37cbf 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -313,7 +313,7 @@ const StackTrace::Frames & StackTrace::getFrames() const { if (!frames.has_value()) { - frames.emplace({}); + frames.emplace(); symbolize(frame_pointers.data(), offset, size, frames); } return frames; @@ -357,7 +357,7 @@ static std::string toStringImpl(const void * const * frame_pointers, size_t offs { std::stringstream out; StackTrace::Frames frames{}; - frames = {{}}; + frames.emplace(); symbolize(frame_pointers, offset, size, frames); toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); return out.str(); From 40f6e559e2d2424e29604d5191dee6b941bc3d6e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 08:29:13 +0300 Subject: [PATCH 0245/1102] fix compiling when disabled --- base/daemon/SentryWriter.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 95189b72e819..15602be25817 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #if !defined(ARCADIA_BUILD) @@ -105,6 +106,8 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { LOG_INFO(logger, "Sending crash reports is disabled"); } +#else + UNUSED(config); #endif } @@ -191,5 +194,10 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c { LOG_INFO(logger, "Not sending crash report"); } +#else + UNUSED(sig); + UNUSED(info); + UNUSED(context); + UNUSED(stack_trace); #endif } From 9c1ac2f1c1af35b20bb7ea031370a3c8e347f4df Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 09:46:36 +0300 Subject: [PATCH 0246/1102] experiment --- cmake/find/sentry.cmake | 9 +-------- contrib/CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 309f63e9165a..94c4f4a6e93b 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,16 +5,9 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD AND NOT UNBUNDLED AND NOT SPLITTED AND NOT (COMPILER_CLANG AND OS_DARWIN)) - if (POLICY CMP0077) - cmake_policy (SET CMP0077 NEW) - endif () +if (NOT OS_FREEBSD) option (USE_SENTRY "Use Sentry" ON) - set (BUILD_SHARED_LIBS OFF) - set (SENTRY_PIC OFF) - set (SENTRY_BACKEND "none") - set (SENTRY_TRANSPORT "curl") set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index ea13969db166..d9af4bc0ac52 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -321,6 +321,8 @@ if (USE_FASTOPS) endif() if (USE_SENTRY) + set (SENTRY_BACKEND "none") + set (SENTRY_TRANSPORT "curl") add_subdirectory (sentry-native) endif() From 280eea1e12fa4770f114ad952efa0be0eecc3e34 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 10:33:11 +0300 Subject: [PATCH 0247/1102] fix compiling when disabled --- base/daemon/SentryWriter.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 15602be25817..c8197d8a1603 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -16,14 +16,16 @@ #endif +#if USE_SENTRY namespace { + bool initialized = false; bool anonymize = false; void setExtras() { -#if USE_SENTRY + if (!anonymize) { sentry_set_extra("server_name", sentry_value_new_string(getFQDNOrHostName().c_str())); @@ -36,9 +38,9 @@ void setExtras() sentry_set_extra("version_major", sentry_value_new_int32(VERSION_MAJOR)); sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); -#endif } } +#endif void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { From 0e8d559d832df40c23942673aac254728b0e77b1 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 13:13:21 +0300 Subject: [PATCH 0248/1102] disable for splitted --- cmake/find/sentry.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 94c4f4a6e93b..4a5fe6f24784 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,7 +5,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD) +if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES) option (USE_SENTRY "Use Sentry" ON) set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) From 5036ad7c6afad995da38bf38de76f1e7134a4137 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 13:13:29 +0300 Subject: [PATCH 0249/1102] back to upstream --- .gitmodules | 2 +- contrib/sentry-native | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index ff4e644f657d..4175eb223db7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/blinkov/sentry-native.git + url = https://github.com/getsentry/sentry-native.git diff --git a/contrib/sentry-native b/contrib/sentry-native index 9e214a1265a4..aed9c18536df 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit 9e214a1265a4ea628c21045b7f43d1aec15e385d +Subproject commit aed9c18536dff1851b1240f84263a55ef716acb6 From 862693d78dc1924f472646584fbd70f955239c0c Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 16:59:45 +0300 Subject: [PATCH 0250/1102] change sentry-native commit --- contrib/sentry-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/sentry-native b/contrib/sentry-native index aed9c18536df..b48c21d24409 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit aed9c18536dff1851b1240f84263a55ef716acb6 +Subproject commit b48c21d244092658d6e2d1bb243b705fd968b9f7 From 786874e86754a60f711ed2fc93399edf51eb4f35 Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 2 Jun 2020 13:15:53 +0000 Subject: [PATCH 0251/1102] Better publish & some fixes --- .../RabbitMQ/RabbitMQBlockOutputStream.cpp | 2 +- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 2 +- src/Storages/RabbitMQ/RabbitMQHandler.h | 2 + src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 2 +- .../WriteBufferToRabbitMQProducer.cpp | 70 +++++++++++++++---- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 7 +- 6 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp index 0858e2101dfc..17e4db3fb898 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp @@ -35,7 +35,7 @@ void RabbitMQBlockOutputStream::writePrefix() child = FormatFactory::instance().getOutput( storage.getFormatName(), *buffer, getHeader(), context, [this](const Columns & /* columns */, size_t /* rows */) { - buffer->count_row(); + buffer->countRow(); }); } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 6308e2e0089a..775db87a1f80 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -16,7 +16,7 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes { LOG_ERROR(log, "Library error report: {}", message); - if (!connection->usable() || !connection->ready()) + if (connection->closed()) { std::cerr << "Connection lost, no recovery is possible"; throw; diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index a70b08aba556..117f80d26f89 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include #include diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 147d3ba21155..7cbfb164a2d2 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -216,7 +216,7 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { return std::make_shared(parsed_address, routing_key, exchange_name, - log, num_consumers, bind_by_id, hash_exchange, + log, num_consumers * num_queues, bind_by_id, hash_exchange, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 73434bc0ea66..86d3b32925aa 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -46,7 +46,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( { /* The reason behind making a separate connection for each concurrent producer is explained here: * https://github.com/CopernicaMarketingSoftware/AMQP-CPP/issues/128#issuecomment-300780086 - publishing from - * different threads (as outputStreams are asynchronous) with the same connection leads to internal libary errors. + * different threads (as outputStreams are asynchronous) with the same connection leads to internal library errors. */ size_t cnt_retries = 0; while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) @@ -73,7 +73,7 @@ WriteBufferToRabbitMQProducer::~WriteBufferToRabbitMQProducer() } -void WriteBufferToRabbitMQProducer::count_row() +void WriteBufferToRabbitMQProducer::countRow() { if (++rows % max_rows == 0) { @@ -108,42 +108,88 @@ void WriteBufferToRabbitMQProducer::count_row() void WriteBufferToRabbitMQProducer::flush() { - /* The reason for accumulating payloads and not publishing each of them at once in count_row() is that publishing - * needs to be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it - * each time we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to - * exchange becoming inactive at some point and part of messages is lost as a result. - */ std::atomic exchange_declared = false, exchange_error = false; + /* The AMQP::passive flag indicates that it should only be checked if there is a valid exchange with the given name + * and makes it visible from current producer_channel. + */ + producer_channel->declareExchange(exchange_name + "_direct", AMQP::direct, AMQP::passive) .onSuccess([&]() { + exchange_declared = true; + + /// The case that should not normally happen: message was not delivered to queue (queue ttl exceeded) / not forwareded to consumer + if (flush_returned) + { + /// Needed to avoid data race because two different threads may access this vector + std::lock_guard lock(mutex); + + LOG_TRACE(log, "Redelivering returned messages"); + for (auto & payload : returned) + { + next_queue = next_queue % num_queues + 1; + + if (bind_by_id || hash_exchange) + { + producer_channel->publish(exchange_name, std::to_string(next_queue), payload); + } + else + { + producer_channel->publish(exchange_name, routing_key, payload); + } + + --message_counter; + } + + returned.clear(); + } + + /* The reason for accumulating payloads and not publishing each of them at once in count_row() is that publishing + * needs to be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it + * each time we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to + * exchange becoming inactive at some point and part of messages is lost as a result. + */ for (auto & payload : messages) { if (!message_counter) - return; + break; next_queue = next_queue % num_queues + 1; if (bind_by_id || hash_exchange) { - producer_channel->publish(exchange_name, std::to_string(next_queue), payload); + producer_channel->publish(exchange_name, std::to_string(next_queue), payload, AMQP::mandatory || AMQP::immediate) + .onReturned([&](const AMQP::Message & message, int16_t /* code */, const std::string & /* description */) + { + flush_returned = true; + + /// Needed to avoid data race because two different threads may access this variable + std::lock_guard lock(mutex); + returned.emplace_back(std::string(message.body(), message.body() + message.bodySize())); + }); } else { - producer_channel->publish(exchange_name, routing_key, payload); + producer_channel->publish(exchange_name, routing_key, payload, AMQP::mandatory || AMQP::immediate) + .onReturned([&](const AMQP::Message & message, int16_t /* code */, const std::string & /* description */) + { + flush_returned = true; + + /// Needed to avoid data race because two different threads may access this vector + std::lock_guard lock(mutex); + returned.emplace_back(std::string(message.body(), message.body() + message.bodySize())); + }); } --message_counter; } - exchange_declared = true; messages.clear(); }) .onError([&](const char * message) { exchange_error = true; - exchange_declared = false; LOG_ERROR(log, "Exchange was not declared: {}", message); }); diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index d7a1715d491f..146be0c57963 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -30,7 +32,7 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer ~WriteBufferToRabbitMQProducer() override; - void count_row(); + void countRow(); void flush(); private: @@ -52,8 +54,11 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer size_t next_queue = 0; UInt64 message_counter = 0; String channel_id; + std::atomic flush_returned = false; + std::mutex mutex; Messages messages; + Messages returned; Poco::Logger * log; const std::optional delim; From 711e7d101da19c8364b761ee09ca042bf9c680f8 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 21:50:55 +0300 Subject: [PATCH 0252/1102] experiment --- base/CMakeLists.txt | 5 +++++ base/daemon/CMakeLists.txt | 10 +++------- cmake/find/sentry.cmake | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index cfa54fe2ca46..ad3bf56cd007 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -11,3 +11,8 @@ add_subdirectory (widechar_width) if (USE_MYSQL) add_subdirectory (mysqlxx) endif () + +if (USE_SENTRY) + target_link_libraries (daemon PRIVATE curl) + target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) +endif () \ No newline at end of file diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt index 0b6a7188c839..8f70f30aeb1e 100644 --- a/base/daemon/CMakeLists.txt +++ b/base/daemon/CMakeLists.txt @@ -1,12 +1,8 @@ add_library (daemon BaseDaemon.cpp GraphiteWriter.cpp - SentryWriter.cpp) + SentryWriter.cpp +) target_include_directories (daemon PUBLIC ..) -target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES}) - -if (USE_SENTRY) - target_link_libraries (daemon PRIVATE curl) - target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) -endif () \ No newline at end of file +target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES}) \ No newline at end of file diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 4a5fe6f24784..449d995935dc 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -8,6 +8,8 @@ endif () if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES) option (USE_SENTRY "Use Sentry" ON) + set (SENTRY_TRANSPORT "url") + set (SENTRY_BACKEND "none") set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) From 921b7c748000cc4a33c9db618716922fc34f1f17 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 22:25:34 +0300 Subject: [PATCH 0253/1102] partial revert --- base/CMakeLists.txt | 5 ----- base/daemon/CMakeLists.txt | 7 ++++++- cmake/find/sentry.cmake | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index ad3bf56cd007..a8dedec92693 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -10,9 +10,4 @@ add_subdirectory (widechar_width) if (USE_MYSQL) add_subdirectory (mysqlxx) -endif () - -if (USE_SENTRY) - target_link_libraries (daemon PRIVATE curl) - target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) endif () \ No newline at end of file diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt index 8f70f30aeb1e..36de193bccd2 100644 --- a/base/daemon/CMakeLists.txt +++ b/base/daemon/CMakeLists.txt @@ -5,4 +5,9 @@ add_library (daemon ) target_include_directories (daemon PUBLIC ..) -target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES}) \ No newline at end of file +target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES}) + +if (USE_SENTRY) + target_link_libraries (daemon PRIVATE curl) + target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) +endif () \ No newline at end of file diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 449d995935dc..6848dc00b431 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,7 +5,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES) +if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILER_CLANG)) option (USE_SENTRY "Use Sentry" ON) set (SENTRY_TRANSPORT "url") From 2f74c58b0598a14db9583b660f2316b01013f052 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 2 Jun 2020 23:50:18 +0300 Subject: [PATCH 0254/1102] experiment with BUILD_SHARED_LIBS --- cmake/find/sentry.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 6848dc00b431..08f712d5574c 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -12,8 +12,11 @@ if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILE set (SENTRY_BACKEND "none") set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) + if (NOT_UNBUNDLED) + set (BUILD_SHARED_LIBS OFF) + endif() message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") include_directories("${SENTRY_INCLUDE_DIR}") -endif () \ No newline at end of file +endif () From 5624066195f94f78a876926ff88db0f1aad4ff72 Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 4 Jun 2020 06:14:09 +0000 Subject: [PATCH 0255/1102] Fix producer --- .../RabbitMQ/RabbitMQBlockOutputStream.cpp | 2 + src/Storages/RabbitMQ/StorageRabbitMQ.h | 4 +- .../WriteBufferToRabbitMQProducer.cpp | 49 ++----------------- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 3 -- 4 files changed, 8 insertions(+), 50 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp index 17e4db3fb898..8e867db6de9d 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp @@ -46,6 +46,8 @@ void RabbitMQBlockOutputStream::write(const Block & block) if (buffer) buffer->flush(); + + storage.pingConnection(); } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 5aa77a9a7325..635d53e6cf02 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -54,6 +54,8 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub const String & getFormatName() const { return format_name; } NamesAndTypesList getVirtuals() const override; + const void pingConnection() { connection.heartbeat(); } + protected: StorageRabbitMQ( const StorageID & table_id_, @@ -88,7 +90,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub event_base * evbase; RabbitMQHandler eventHandler; - AMQP::TcpConnection connection; + AMQP::TcpConnection connection; /// Connection for all consumers Poco::Semaphore semaphore; std::mutex mutex; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 86d3b32925aa..e61a8e1ccd81 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -16,7 +16,7 @@ enum { Connection_setup_sleep = 200, Connection_setup_retries_max = 1000, - Buffer_limit_to_flush = 5000 /// It is important to keep it low in order not to kill consumers + Buffer_limit_to_flush = 10000 /// It is important to keep it low in order not to kill consumers }; WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( @@ -113,38 +113,11 @@ void WriteBufferToRabbitMQProducer::flush() /* The AMQP::passive flag indicates that it should only be checked if there is a valid exchange with the given name * and makes it visible from current producer_channel. */ - producer_channel->declareExchange(exchange_name + "_direct", AMQP::direct, AMQP::passive) .onSuccess([&]() { exchange_declared = true; - /// The case that should not normally happen: message was not delivered to queue (queue ttl exceeded) / not forwareded to consumer - if (flush_returned) - { - /// Needed to avoid data race because two different threads may access this vector - std::lock_guard lock(mutex); - - LOG_TRACE(log, "Redelivering returned messages"); - for (auto & payload : returned) - { - next_queue = next_queue % num_queues + 1; - - if (bind_by_id || hash_exchange) - { - producer_channel->publish(exchange_name, std::to_string(next_queue), payload); - } - else - { - producer_channel->publish(exchange_name, routing_key, payload); - } - - --message_counter; - } - - returned.clear(); - } - /* The reason for accumulating payloads and not publishing each of them at once in count_row() is that publishing * needs to be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it * each time we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to @@ -159,27 +132,11 @@ void WriteBufferToRabbitMQProducer::flush() if (bind_by_id || hash_exchange) { - producer_channel->publish(exchange_name, std::to_string(next_queue), payload, AMQP::mandatory || AMQP::immediate) - .onReturned([&](const AMQP::Message & message, int16_t /* code */, const std::string & /* description */) - { - flush_returned = true; - - /// Needed to avoid data race because two different threads may access this variable - std::lock_guard lock(mutex); - returned.emplace_back(std::string(message.body(), message.body() + message.bodySize())); - }); + producer_channel->publish(exchange_name, std::to_string(next_queue), payload); } else { - producer_channel->publish(exchange_name, routing_key, payload, AMQP::mandatory || AMQP::immediate) - .onReturned([&](const AMQP::Message & message, int16_t /* code */, const std::string & /* description */) - { - flush_returned = true; - - /// Needed to avoid data race because two different threads may access this vector - std::lock_guard lock(mutex); - returned.emplace_back(std::string(message.body(), message.body() + message.bodySize())); - }); + producer_channel->publish(exchange_name, routing_key, payload); } --message_counter; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index 146be0c57963..c61a76a3e74e 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -54,11 +54,8 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer size_t next_queue = 0; UInt64 message_counter = 0; String channel_id; - std::atomic flush_returned = false; - std::mutex mutex; Messages messages; - Messages returned; Poco::Logger * log; const std::optional delim; From 972611e31b3c6f1ad18f94898372590eafd8e509 Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 4 Jun 2020 06:22:53 +0000 Subject: [PATCH 0256/1102] Fix consumer --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 54 +++++++++++++------ src/Storages/RabbitMQ/RabbitMQHandler.h | 4 +- .../ReadBufferFromRabbitMQConsumer.cpp | 50 +++++++++++++++-- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 3 ++ 4 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 775db87a1f80..1a3ede794203 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -4,6 +4,13 @@ namespace DB { +enum +{ + Lock_timeout = 50, + Max_threads_to_pass = 10 +}; + + RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : LibEventHandler(evbase_), evbase(evbase_), @@ -16,10 +23,9 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes { LOG_ERROR(log, "Library error report: {}", message); - if (connection->closed()) + if (!connection->usable() || !connection->ready()) { - std::cerr << "Connection lost, no recovery is possible"; - throw; + LOG_ERROR(log, "Connection lost completely"); } stop(); @@ -28,24 +34,42 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes void RabbitMQHandler::start(std::atomic & check_param) { - /* The object of this class is shared between concurrent consumers, who call this method repeatedly at the same time. - * But the loop should not be attempted to start if it is already running. Also note that the loop is blocking to - * the thread that has started it. + /* The object of this class is shared between concurrent consumers (who share the same connection == share the same + * event loop). But the loop should not be attempted to start if it is already running. */ - std::lock_guard lock(mutex); - - /* The callback, which changes this variable, could have already been activated by another thread while we waited for the - * mutex to unlock (as it runs all active events on the connection). This means that there is no need to start event loop again. - */ - if (check_param) - return; + if (mutex_before_event_loop.try_lock_for(std::chrono::milliseconds(Lock_timeout))) + { + /* The callback, which changes this variable, could have already been activated by another thread while we waited + * for the mutex to unlock (as it runs all active events on the connection). This means that there is no need to + * start event loop again. + */ + if (!check_param) + { + event_base_loop(evbase, EVLOOP_NONBLOCK); + } - event_base_loop(evbase, EVLOOP_NONBLOCK); + mutex_before_event_loop.unlock(); + } + else + { + if (++count_passed == Max_threads_to_pass) + { + /* Event loop is blocking to the thread that started it and it is not good to block one single thread as it loops + * untill there are no active events, but there can be too many of them for one thread to be blocked for so long. + */ + stop(); + count_passed = 0; + } + } } void RabbitMQHandler::stop() { - event_base_loopbreak(evbase); + if (mutex_before_loop_stop.try_lock_for(std::chrono::milliseconds(0))) + { + event_base_loopbreak(evbase); + mutex_before_loop_stop.unlock(); + } } } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index 117f80d26f89..39fccd4dace4 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -26,7 +26,9 @@ class RabbitMQHandler : public AMQP::LibEventHandler event_base * evbase; Poco::Logger * log; - std::mutex mutex; + size_t count_passed = 0; + std::timed_mutex mutex_before_event_loop; + std::timed_mutex mutex_before_loop_stop; }; } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 27c5ab800f02..f8259ce8c4c0 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -13,6 +13,11 @@ namespace DB { +enum +{ + Received_max_to_stop_loop = 10000 // Explained below +}; + ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, @@ -117,7 +122,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) std::atomic bindings_created = false, bindings_error = false; - consumer_channel->declareQueue(AMQP::exclusive) + consumer_channel->declareQueue(AMQP::durable) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) { queues.emplace_back(queue_name_); @@ -145,6 +150,12 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onSuccess([&] { bindings_created = true; + + /// Unblock current thread so that it does not continue to execute all callbacks on the connection + if (++count_bound_queues == num_queues) + { + stopEventLoop(); + } }) .onError([&](const char * message) { @@ -196,6 +207,12 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) consumer_created = true; LOG_TRACE(log, "Consumer " + std::to_string(channel_id) + " is subscribed to queue " + queue_name); + + /// Unblock current thread so that it does not continue to execute all callbacks on the connection + if (++count_subscribed == queues.size()) + { + stopEventLoop(); + } }) .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) { @@ -207,15 +224,34 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) if (row_delimiter != '\0') message_received += row_delimiter; + //LOG_TRACE(log, "Consumer {} received a message", channel_id); + + bool stop_loop = false; + /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl() (below). - std::lock_guard lock(mutex); - received.push_back(message_received); + { + std::lock_guard lock(mutex); + received.push_back(message_received); + + /* As event loop is blocking to the thread that started it and a single thread should not be blocked while + * executing all callbacks on the connection (not only its own), then there should be some point to unblock + */ + if (received.size() >= Received_max_to_stop_loop) + { + stop_loop = true; + } + } + + if (stop_loop) + { + stopEventLoop(); + } } }) .onError([&](const char * message) { consumer_error = true; - LOG_ERROR(log, "Consumer failed: {}", message); + LOG_ERROR(log, "Consumer {} failed: {}", channel_id, message); }); while (!consumer_created && !consumer_error) @@ -226,6 +262,12 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) } +void ReadBufferFromRabbitMQConsumer::stopEventLoop() +{ + eventHandler.stop(); +} + + void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & check_param) { eventHandler.start(check_param); diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 31babc5033f7..55adb39bdce9 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -64,6 +64,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer Queues queues; bool subscribed = false; String current_exchange_name; + size_t count_subscribed = 0; + size_t count_bound_queues = 0; Messages received; Messages messages; @@ -77,6 +79,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer void initQueueBindings(const size_t queue_id); void subscribe(const String & queue_name); void startEventLoop(std::atomic & check_param); + void stopEventLoop(); }; } From 6f0e754f1e6bfd5753c04b1f81b231eece4f82fa Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 4 Jun 2020 11:57:01 +0300 Subject: [PATCH 0257/1102] try to fix the glibc compatibility --- .gitmodules | 2 +- cmake/find/sentry.cmake | 9 ++++----- contrib/CMakeLists.txt | 2 -- contrib/sentry-native | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4175eb223db7..ff4e644f657d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/getsentry/sentry-native.git + url = https://github.com/blinkov/sentry-native.git diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 08f712d5574c..e1cd28c1d595 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -7,15 +7,14 @@ endif () if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILER_CLANG)) option (USE_SENTRY "Use Sentry" ON) - - set (SENTRY_TRANSPORT "url") - set (SENTRY_BACKEND "none") set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) - if (NOT_UNBUNDLED) + set (SENTRY_TRANSPORT "curl" CACHE STRING "") + set (SENTRY_BACKEND "none" CACHE STRING "") + set (SENTRY_LINK_PTHREAD OFF CACHE BOOL "") + if (OS_LINUX AND NOT_UNBUNDLED) set (BUILD_SHARED_LIBS OFF) endif() - message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") include_directories("${SENTRY_INCLUDE_DIR}") diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index d9af4bc0ac52..ea13969db166 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -321,8 +321,6 @@ if (USE_FASTOPS) endif() if (USE_SENTRY) - set (SENTRY_BACKEND "none") - set (SENTRY_TRANSPORT "curl") add_subdirectory (sentry-native) endif() diff --git a/contrib/sentry-native b/contrib/sentry-native index b48c21d24409..18835dd8c496 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit b48c21d244092658d6e2d1bb243b705fd968b9f7 +Subproject commit 18835dd8c496f22859bd6a1a7054a2bd4762e7ed From 68b94c5c20fc1ed1d222ee14d096991f2fdca705 Mon Sep 17 00:00:00 2001 From: kssenii Date: Fri, 5 Jun 2020 13:42:11 +0000 Subject: [PATCH 0258/1102] Fixes --- programs/server/config.xml | 3 ++- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 14 +------------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp | 7 +++++-- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 1 + src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 12 ++++++++---- src/Storages/RabbitMQ/StorageRabbitMQ.h | 3 ++- .../RabbitMQ/WriteBufferToRabbitMQProducer.cpp | 7 +++++-- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 5 ++++- 8 files changed, 28 insertions(+), 24 deletions(-) diff --git a/programs/server/config.xml b/programs/server/config.xml index 21605edeb36e..b39ee180466a 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -51,7 +51,8 @@ 8443 9440 --> - + root + clickhouse diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 1a3ede794203..34a77489faad 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -6,8 +6,7 @@ namespace DB enum { - Lock_timeout = 50, - Max_threads_to_pass = 10 + Lock_timeout = 50 }; @@ -50,17 +49,6 @@ void RabbitMQHandler::start(std::atomic & check_param) mutex_before_event_loop.unlock(); } - else - { - if (++count_passed == Max_threads_to_pass) - { - /* Event loop is blocking to the thread that started it and it is not good to block one single thread as it loops - * untill there are no active events, but there can be too many of them for one thread to be blocked for so long. - */ - stop(); - count_passed = 0; - } - } } void RabbitMQHandler::stop() diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index f8259ce8c4c0..1bd2c7831ff2 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -44,6 +44,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , stopped(stopped_) , exchange_declared(false) , false_param(false) + , loop_attempt(false) { messages.clear(); current = messages.begin(); @@ -225,7 +226,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) message_received += row_delimiter; //LOG_TRACE(log, "Consumer {} received a message", channel_id); - + bool stop_loop = false; /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl() (below). @@ -236,7 +237,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) /* As event loop is blocking to the thread that started it and a single thread should not be blocked while * executing all callbacks on the connection (not only its own), then there should be some point to unblock */ - if (received.size() >= Received_max_to_stop_loop) + if (!loop_attempt && received.size() % Received_max_to_stop_loop == 0) { stop_loop = true; } @@ -284,7 +285,9 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() if (received.empty()) { /// Run the onReceived callbacks to save the messages that have been received by now + loop_attempt = true; startEventLoop(false_param); + loop_attempt = false; } if (received.empty()) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 55adb39bdce9..97eca73cecee 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -66,6 +66,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer String current_exchange_name; size_t count_subscribed = 0; size_t count_bound_queues = 0; + std::atomic loop_attempt; Messages received; Messages messages; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 7cbfb164a2d2..481314a38c2f 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -74,10 +74,14 @@ StorageRabbitMQ::StorageRabbitMQ( , hash_exchange(hash_exchange_) , log(&Poco::Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) , semaphore(0, num_consumers_) + , login_password(std::make_pair( + rabbitmq_context.getConfigRef().getString("rabbitmq_username", "root"), + rabbitmq_context.getConfigRef().getString("rabbitmq_password", "clickhouse"))) , parsed_address(parseAddress(global_context.getMacros()->expand(host_port_), 5672)) , evbase(event_base_new()) , eventHandler(evbase, log) - , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) + , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, + AMQP::Login(login_password.first, login_password.second), "/")) { size_t cnt_retries = 0; while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) @@ -208,14 +212,14 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ChannelPtr consumer_channel = std::make_shared(&connection); - return std::make_shared(consumer_channel, eventHandler, exchange_name, - routing_key, next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); + return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_key, + next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); } ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { - return std::make_shared(parsed_address, routing_key, exchange_name, + return std::make_shared(parsed_address, login_password, routing_key, exchange_name, log, num_consumers * num_queues, bind_by_id, hash_exchange, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 635d53e6cf02..563f37ae6f17 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -86,7 +86,8 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub const bool hash_exchange; Poco::Logger * log; - std::pair parsed_address; + std::pair parsed_address; + std::pair login_password; event_base * evbase; RabbitMQHandler eventHandler; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index e61a8e1ccd81..7c0764853c7c 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -20,7 +20,8 @@ enum }; WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( - std::pair & parsed_address, + std::pair & parsed_address, + std::pair & login_password_, const String & routing_key_, const String & exchange_, Poco::Logger * log_, @@ -31,6 +32,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( size_t rows_per_message, size_t chunk_size_) : WriteBuffer(nullptr, 0) + , login_password(login_password_) , routing_key(routing_key_) , exchange_name(exchange_) , log(log_) @@ -42,7 +44,8 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( , chunk_size(chunk_size_) , producerEvbase(event_base_new()) , eventHandler(producerEvbase, log) - , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, AMQP::Login("root", "clickhouse"), "/")) + , connection(&eventHandler, AMQP::Address(parsed_address.first, parsed_address.second, + AMQP::Login(login_password.first, login_password.second), "/")) { /* The reason behind making a separate connection for each concurrent producer is explained here: * https://github.com/CopernicaMarketingSoftware/AMQP-CPP/issues/128#issuecomment-300780086 - publishing from diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index c61a76a3e74e..e0c48556239c 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace DB { @@ -18,7 +19,8 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer { public: WriteBufferToRabbitMQProducer( - std::pair & parsed_address, + std::pair & parsed_address, + std::pair & login_password_, const String & routing_key_, const String & exchange_, Poco::Logger * log_, @@ -40,6 +42,7 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer void checkExchange(); void startEventLoop(std::atomic & check_param); + std::pair & login_password; const String routing_key; const String exchange_name; const bool bind_by_id; From cb618a32b80df2cbaec35264df01754d35e30d6b Mon Sep 17 00:00:00 2001 From: kssenii Date: Fri, 5 Jun 2020 14:27:56 +0000 Subject: [PATCH 0259/1102] Fix style --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 4 ++-- src/Storages/RabbitMQ/RabbitMQHandler.h | 2 +- src/Storages/RabbitMQ/StorageRabbitMQ.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 34a77489faad..95d7e22d434c 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -18,7 +18,7 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : } -void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * message) +void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * message) { LOG_ERROR(log, "Library error report: {}", message); @@ -44,7 +44,7 @@ void RabbitMQHandler::start(std::atomic & check_param) */ if (!check_param) { - event_base_loop(evbase, EVLOOP_NONBLOCK); + event_base_loop(evbase, EVLOOP_NONBLOCK); } mutex_before_event_loop.unlock(); diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index 39fccd4dace4..d2d701851283 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -19,7 +19,7 @@ class RabbitMQHandler : public AMQP::LibEventHandler RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); void onError(AMQP::TcpConnection * connection, const char * message) override; - void start(std::atomic & check_param); + void start(std::atomic & check_param); void stop(); private: diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 563f37ae6f17..111e52768d03 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -63,11 +63,11 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub const ColumnsDescription & columns_, const String & host_port_, const String & routing_key_, - const String & exchange_name_, + const String & exchange_name_, const String & format_name_, char row_delimiter_, - size_t num_consumers_, - size_t num_queues_, + size_t num_consumers_, + size_t num_queues_, bool hash_exchange); private: From 108575c8adf6f2cb1d1c56c0db172b69ca2c1630 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 5 Jun 2020 18:38:03 +0300 Subject: [PATCH 0260/1102] Added IQueryPlanStep. --- src/CMakeLists.txt | 1 + src/Processors/QueryPlan/IQueryPlanStep.cpp | 19 ++++++++ src/Processors/QueryPlan/IQueryPlanStep.h | 54 +++++++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 75 insertions(+) create mode 100644 src/Processors/QueryPlan/IQueryPlanStep.cpp create mode 100644 src/Processors/QueryPlan/IQueryPlanStep.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13e8aac6906a..1dfaf68e1e1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -157,6 +157,7 @@ add_object_library(clickhouse_processors_transforms Processors/Transforms) add_object_library(clickhouse_processors_sources Processors/Sources) add_object_library(clickhouse_processors_merges Processors/Merges) add_object_library(clickhouse_processors_merges_algorithms Processors/Merges/Algorithms) +add_object_library(clickhouse_processors_queryplan Processors/QueryPlan) if (MAKE_STATIC_LIBRARIES OR NOT SPLIT_SHARED_LIBRARIES) diff --git a/src/Processors/QueryPlan/IQueryPlanStep.cpp b/src/Processors/QueryPlan/IQueryPlanStep.cpp new file mode 100644 index 000000000000..f25d17188ea1 --- /dev/null +++ b/src/Processors/QueryPlan/IQueryPlanStep.cpp @@ -0,0 +1,19 @@ +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + +const DataStream & IQueryPlanStep::getOutputStream() const +{ + if (!hasOutputStream()) + throw Exception("QueryPlanStep " + getName() + " does not have output stream.", ErrorCodes::LOGICAL_ERROR); + + return *output_stream; +} + +} diff --git a/src/Processors/QueryPlan/IQueryPlanStep.h b/src/Processors/QueryPlan/IQueryPlanStep.h new file mode 100644 index 000000000000..fe84e49672a9 --- /dev/null +++ b/src/Processors/QueryPlan/IQueryPlanStep.h @@ -0,0 +1,54 @@ +#pragma once +#include + +namespace DB +{ + +class QueryPipeline; +using QueryPipelinePtr = std::unique_ptr; +using QueryPipelines = std::vector; + +/// Description of data stream. +class DataStream +{ +public: + Block header; + + /// Only header for now. + /// Things which may be added: + /// * sort description + /// * distinct columns + /// * limit + /// * estimated rows number + /// * memory allocation context +}; + +using DataStreams = std::vector; + +/// Single step of query plan. +class IQueryPlanStep +{ +public: + virtual ~IQueryPlanStep() = default; + + virtual String getName() const = 0; + + /// Add processors from current step to QueryPipeline. + /// Calling this method, we assume and don't check that: + /// * pipelines.size() == getInputStreams.size() + /// * header from each pipeline is the same as header from corresponding input_streams + /// Result pipeline must contain any number of streams with compatible output header is hasOutputStream(), + /// or pipeline should be completed otherwise. + virtual QueryPipelinePtr updatePipeline(QueryPipelines pipelines) = 0; + + const DataStreams & getInputStreams() const { return input_streams; } + + bool hasOutputStream() const { return output_stream.has_value(); } + const DataStream & getOutputStream() const; + +protected: + DataStreams input_streams; + std::optional output_stream; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 62320f1c1476..fee4847fb56c 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -134,6 +134,7 @@ SRCS( Transforms/RollupTransform.cpp Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp + QueryPlan/IQueryPlanStep.cpp ) END() From ce448d92916b180b7891ee5e14a8a9e52c703eeb Mon Sep 17 00:00:00 2001 From: kssenii Date: Sun, 7 Jun 2020 11:14:05 +0000 Subject: [PATCH 0261/1102] Better event handler --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 25 ++++++- src/Storages/RabbitMQ/RabbitMQHandler.h | 5 +- .../ReadBufferFromRabbitMQConsumer.cpp | 70 +++++++++---------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 11 ++- .../WriteBufferToRabbitMQProducer.cpp | 7 +- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 2 +- 6 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 95d7e22d434c..d9dc19afa28c 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -6,7 +6,8 @@ namespace DB enum { - Lock_timeout = 50 + Lock_timeout = 50, + Loop_stop_timeout = 200 }; @@ -15,6 +16,8 @@ RabbitMQHandler::RabbitMQHandler(event_base * evbase_, Poco::Logger * log_) : evbase(evbase_), log(log_) { + tv.tv_sec = 0; + tv.tv_usec = Loop_stop_timeout; } @@ -31,7 +34,7 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes } -void RabbitMQHandler::start(std::atomic & check_param) +void RabbitMQHandler::startConsumerLoop(std::atomic & check_param, std::atomic & loop_started) { /* The object of this class is shared between concurrent consumers (who share the same connection == share the same * event loop). But the loop should not be attempted to start if it is already running. @@ -44,6 +47,7 @@ void RabbitMQHandler::start(std::atomic & check_param) */ if (!check_param) { + loop_started = true; event_base_loop(evbase, EVLOOP_NONBLOCK); } @@ -51,6 +55,13 @@ void RabbitMQHandler::start(std::atomic & check_param) } } + +void RabbitMQHandler::startProducerLoop() +{ + event_base_loop(evbase, EVLOOP_NONBLOCK); +} + + void RabbitMQHandler::stop() { if (mutex_before_loop_stop.try_lock_for(std::chrono::milliseconds(0))) @@ -60,4 +71,14 @@ void RabbitMQHandler::stop() } } + +void RabbitMQHandler::stopWithTimeout() +{ + if (mutex_before_loop_stop.try_lock_for(std::chrono::milliseconds(0))) + { + event_base_loopexit(evbase, &tv); + mutex_before_loop_stop.unlock(); + } +} + } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index d2d701851283..a8692a845f17 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -19,13 +19,16 @@ class RabbitMQHandler : public AMQP::LibEventHandler RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); void onError(AMQP::TcpConnection * connection, const char * message) override; - void start(std::atomic & check_param); + void startConsumerLoop(std::atomic & check_param, std::atomic & loop_started); + void startProducerLoop(); + void stopWithTimeout(); void stop(); private: event_base * evbase; Poco::Logger * log; + timeval tv; size_t count_passed = 0; std::timed_mutex mutex_before_event_loop; std::timed_mutex mutex_before_loop_stop; diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 1bd2c7831ff2..b650988dd619 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -7,17 +7,13 @@ #include #include #include +#include "Poco/Timer.h" #include namespace DB { -enum -{ - Received_max_to_stop_loop = 10000 // Explained below -}; - ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, @@ -44,7 +40,6 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , stopped(stopped_) , exchange_declared(false) , false_param(false) - , loop_attempt(false) { messages.clear(); current = messages.begin(); @@ -112,7 +107,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { - /* This varibale can be updated from a different thread in case of some error so its better to always check + /* This varibale can be updated from a different thread in case of some error so its better to check * whether exchange is in a working state and if not - declare it once again. */ if (!exchange_declared) @@ -123,7 +118,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) std::atomic bindings_created = false, bindings_error = false; - consumer_channel->declareQueue(AMQP::durable) + consumer_channel->declareQueue(AMQP::exclusive) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) { queues.emplace_back(queue_name_); @@ -151,12 +146,6 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onSuccess([&] { bindings_created = true; - - /// Unblock current thread so that it does not continue to execute all callbacks on the connection - if (++count_bound_queues == num_queues) - { - stopEventLoop(); - } }) .onError([&](const char * message) { @@ -176,8 +165,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) */ while (!bindings_created && !bindings_error) { - /// No need for timeouts as this event loop is blocking for the current thread and quits in case there are no active events - startEventLoop(bindings_created); + startEventLoop(bindings_created, loop_started); } } @@ -187,7 +175,7 @@ void ReadBufferFromRabbitMQConsumer::subscribeConsumer() if (subscribed) return; - LOG_TRACE(log, "Subscribing to " + std::to_string(queues.size()) + " queues"); + LOG_TRACE(log, "Subscribing {} to {} queues", channel_id, queues.size()); for (auto & queue : queues) { @@ -200,17 +188,19 @@ void ReadBufferFromRabbitMQConsumer::subscribeConsumer() void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) { - std::atomic consumer_created = false, consumer_error = false; + std::atomic consumer_created = false, consumer_failed = false; consumer_channel->consume(queue_name, AMQP::noack) .onSuccess([&](const std::string & /* consumer */) { consumer_created = true; + LOG_TRACE(log, "Consumer {} is subscribed to queue {}", channel_id, queue_name); - LOG_TRACE(log, "Consumer " + std::to_string(channel_id) + " is subscribed to queue " + queue_name); - - /// Unblock current thread so that it does not continue to execute all callbacks on the connection - if (++count_subscribed == queues.size()) + /* Unblock current thread if it is looping (any consumer could start the loop and only one of them) so that it does not + * continue to execute all active callbacks on the connection (=> one looping consumer will not be blocked for too + * long and events will be distributed between them) + */ + if (loop_started && ++count_subscribed == queues.size()) { stopEventLoop(); } @@ -223,9 +213,9 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) String message_received = std::string(message.body(), message.body() + message_size); if (row_delimiter != '\0') + { message_received += row_delimiter; - - //LOG_TRACE(log, "Consumer {} received a message", channel_id); + } bool stop_loop = false; @@ -235,9 +225,10 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) received.push_back(message_received); /* As event loop is blocking to the thread that started it and a single thread should not be blocked while - * executing all callbacks on the connection (not only its own), then there should be some point to unblock + * executing all callbacks on the connection (not only its own), then there should be some point to unblock. + * loop_started == 1 if current consumer is started the loop and not another. */ - if (!loop_attempt && received.size() % Received_max_to_stop_loop == 0) + if (!loop_started) { stop_loop = true; } @@ -245,20 +236,20 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) if (stop_loop) { - stopEventLoop(); + stopEventLoopWithTimeout(); } } }) .onError([&](const char * message) { - consumer_error = true; + consumer_failed = true; LOG_ERROR(log, "Consumer {} failed: {}", channel_id, message); }); - while (!consumer_created && !consumer_error) + /// These variables are updated in a separate thread. + while (!consumer_created && !consumer_failed) { - /// No need for timeouts as this event loop is blocking for the current thread and quits in case there are no active events - startEventLoop(consumer_created); + startEventLoop(consumer_created, loop_started); } } @@ -269,9 +260,15 @@ void ReadBufferFromRabbitMQConsumer::stopEventLoop() } -void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & check_param) +void ReadBufferFromRabbitMQConsumer::stopEventLoopWithTimeout() +{ + eventHandler.stopWithTimeout(); +} + + +void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & check_param, std::atomic & loop_started) { - eventHandler.start(check_param); + eventHandler.startConsumerLoop(check_param, loop_started); } @@ -284,10 +281,9 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() { if (received.empty()) { - /// Run the onReceived callbacks to save the messages that have been received by now - loop_attempt = true; - startEventLoop(false_param); - loop_attempt = false; + /// Run the onReceived callbacks to save the messages that have been received by now, blocks current thread + startEventLoop(false_param, loop_started); + loop_started = false; } if (received.empty()) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 97eca73cecee..2341c94443f5 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -41,7 +41,6 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer private: using Messages = std::vector; - using Queues = std::vector; ChannelPtr consumer_channel; RabbitMQHandler & eventHandler; @@ -51,6 +50,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const size_t channel_id; const bool bind_by_id; const bool hash_exchange; + const size_t num_queues; Poco::Logger * log; char row_delimiter; @@ -60,14 +60,12 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer std::atomic exchange_declared; std::atomic false_param; - const size_t num_queues; - Queues queues; bool subscribed = false; String current_exchange_name; size_t count_subscribed = 0; - size_t count_bound_queues = 0; - std::atomic loop_attempt; + std::atomic loop_started; + std::vector queues; Messages received; Messages messages; Messages::iterator current; @@ -79,7 +77,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer void initExchange(); void initQueueBindings(const size_t queue_id); void subscribe(const String & queue_name); - void startEventLoop(std::atomic & check_param); + void startEventLoop(std::atomic & check_param, std::atomic & loop_started); + void stopEventLoopWithTimeout(); void stopEventLoop(); }; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 7c0764853c7c..31c3dea97aae 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -153,9 +153,10 @@ void WriteBufferToRabbitMQProducer::flush() LOG_ERROR(log, "Exchange was not declared: {}", message); }); + /// These variables are updated in a separate thread and starting the loop blocks current thread while (!exchange_declared && !exchange_error) { - startEventLoop(exchange_declared); + startEventLoop(); } } @@ -168,9 +169,9 @@ void WriteBufferToRabbitMQProducer::nextImpl() } -void WriteBufferToRabbitMQProducer::startEventLoop(std::atomic & check_param) +void WriteBufferToRabbitMQProducer::startEventLoop() { - eventHandler.start(check_param); + eventHandler.startProducerLoop(); } } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index e0c48556239c..9ae3893d6ae5 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -40,7 +40,7 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer private: void nextImpl() override; void checkExchange(); - void startEventLoop(std::atomic & check_param); + void startEventLoop(); std::pair & login_password; const String routing_key; From 17e7cc03c0e090bc045a8b0d5ee720e7d8df8ca2 Mon Sep 17 00:00:00 2001 From: kssenii Date: Mon, 8 Jun 2020 01:11:48 +0000 Subject: [PATCH 0262/1102] Add consumer failure handler --- .../ReadBufferFromRabbitMQConsumer.cpp | 50 ++++++++++++++++--- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 16 ++++-- .../integration/test_storage_rabbitmq/test.py | 25 +++++++--- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index b650988dd619..5d649ab20840 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -10,10 +10,14 @@ #include "Poco/Timer.h" #include - namespace DB { +enum +{ + Loop_retries_limit = 500 +}; + ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, @@ -38,8 +42,6 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , hash_exchange(hash_exchange_) , num_queues(num_queues_) , stopped(stopped_) - , exchange_declared(false) - , false_param(false) { messages.clear(); current = messages.begin(); @@ -122,6 +124,8 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) { queues.emplace_back(queue_name_); + subscribed_queue[queue_name_] = false; + String binding_key = routing_key; /* Every consumer has at least one unique queue. Bind the queues to exchange based on the consumer_channel_id @@ -175,34 +179,43 @@ void ReadBufferFromRabbitMQConsumer::subscribeConsumer() if (subscribed) return; - LOG_TRACE(log, "Subscribing {} to {} queues", channel_id, queues.size()); - for (auto & queue : queues) { subscribe(queue); } - subscribed = true; + LOG_TRACE(log, "Consumer {} is subscribed to {} queues", channel_id, count_subscribed); + + if (count_subscribed == queues.size()) + { + subscribed = true; + } } void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) { - std::atomic consumer_created = false, consumer_failed = false; + if (subscribed_queue[queue_name]) + return; + + consumer_created = false, consumer_failed = false; consumer_channel->consume(queue_name, AMQP::noack) .onSuccess([&](const std::string & /* consumer */) { consumer_created = true; + ++count_subscribed; + LOG_TRACE(log, "Consumer {} is subscribed to queue {}", channel_id, queue_name); /* Unblock current thread if it is looping (any consumer could start the loop and only one of them) so that it does not * continue to execute all active callbacks on the connection (=> one looping consumer will not be blocked for too * long and events will be distributed between them) */ - if (loop_started && ++count_subscribed == queues.size()) + if (loop_started && count_subscribed == queues.size()) { stopEventLoop(); + subscribed = true; } }) .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) @@ -246,10 +259,31 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) LOG_ERROR(log, "Consumer {} failed: {}", channel_id, message); }); + size_t cnt_retries = 0; + /// These variables are updated in a separate thread. while (!consumer_created && !consumer_failed) { startEventLoop(consumer_created, loop_started); + + if (!consumer_created && !consumer_failed) + { + if (cnt_retries >= Loop_retries_limit) + { + /* For unknown reason there is a case when subscribtion may fail and OnError callback is not activated + * for a long time. In this case there should be resubscription. + */ + LOG_ERROR(log, "Consumer {} failed to subscride to queue {}", channel_id, queue_name); + break; + } + + ++cnt_retries; + } + } + + if (consumer_created) + { + subscribed_queue[queue_name] = true; } } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 2341c94443f5..9e0b29307c47 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -58,17 +58,23 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer bool allowed = true; const std::atomic & stopped; - std::atomic exchange_declared; - std::atomic false_param; - bool subscribed = false; String current_exchange_name; - size_t count_subscribed = 0; - std::atomic loop_started; + + /* Note: as all concurrent consumers share the same connection => they also share the same + * event loop, which can be started by any consumer and the loop is blocking only to the thread that + * started it, and the loop executes ALL active callbacks on the connection => in case num_consumers > 1, + * at most two threads will be present: main thread and the one that executes callbacks (1 thread if + * main thread is the one that started the loop). Both reference these variables. + */ + std::atomic exchange_declared = false, subscribed = false, loop_started = false, false_param = false; + std::atomic consumer_created = false, consumer_failed = false; + std::atomic count_subscribed = 0; std::vector queues; Messages received; Messages messages; Messages::iterator current; + std::unordered_map subscribed_queue; std::mutex mutex; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 0533dd7e2f4b..d7e991fe7ae8 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -145,9 +145,14 @@ def test_rabbitmq_select_from_new_syntax_table(rabbitmq_cluster): for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key='new', body=message) - result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=False) - connection.close() + + result = '' + while True: + result += instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + if rabbitmq_check_result(result): + break + rabbitmq_check_result(result, True) @@ -171,9 +176,14 @@ def test_rabbitmq_select_from_old_syntax_table(rabbitmq_cluster): for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key='old', body=message) - result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) - connection.close() + + result = '' + while True: + result += instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + if rabbitmq_check_result(result): + break + rabbitmq_check_result(result, True) @@ -294,7 +304,11 @@ def test_rabbitmq_tsv_with_delimiter(rabbitmq_cluster): for message in messages: channel.basic_publish(exchange='clickhouse-exchange', routing_key='tsv', body=message) - result = instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + result = '' + while True: + result += instance.query('SELECT * FROM test.rabbitmq', ignore_error=True) + if rabbitmq_check_result(result): + break connection.close() rabbitmq_check_result(result, True) @@ -997,7 +1011,6 @@ def insert(): while True: result = instance.query('SELECT count() FROM test.view_sharding') time.sleep(1) - print result if int(result) == messages_num * threads_num: break From 4c179e454a1a9a1beda02223de79eef225654d3e Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 8 Jun 2020 12:14:58 +0300 Subject: [PATCH 0263/1102] Add QueryPlan. --- src/Processors/QueryPlan/IQueryPlanStep.h | 1 + src/Processors/QueryPlan/QueryPlan.cpp | 112 ++++++++++++++++++++++ src/Processors/QueryPlan/QueryPlan.h | 45 +++++++++ src/Processors/ya.make | 1 + 4 files changed, 159 insertions(+) create mode 100644 src/Processors/QueryPlan/QueryPlan.cpp create mode 100644 src/Processors/QueryPlan/QueryPlan.h diff --git a/src/Processors/QueryPlan/IQueryPlanStep.h b/src/Processors/QueryPlan/IQueryPlanStep.h index fe84e49672a9..0c3b0727b019 100644 --- a/src/Processors/QueryPlan/IQueryPlanStep.h +++ b/src/Processors/QueryPlan/IQueryPlanStep.h @@ -51,4 +51,5 @@ class IQueryPlanStep std::optional output_stream; }; +using QueryPlanStepPtr = std::unique_ptr; } diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp new file mode 100644 index 000000000000..82bc42108250 --- /dev/null +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -0,0 +1,112 @@ +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; +} + +void QueryPlan::checkInitialized() const +{ + if (!isInitialized()) + throw Exception("QueryPlan was not initialized", ErrorCodes::LOGICAL_ERROR); +} + +void QueryPlan::checkNotCompleted() const +{ + if (isCompleted()) + throw Exception("QueryPlan was already completed", ErrorCodes::LOGICAL_ERROR); +} + +bool QueryPlan::isCompleted() const +{ + return isInitialized() && !root->step->hasOutputStream(); +} + +const DataStream & QueryPlan::getCurrentDataStream() const +{ + checkInitialized(); + checkNotCompleted(); + return root->step->getOutputStream(); +} + +void QueryPlan::addStep(QueryPlanStepPtr step) +{ + checkNotCompleted(); + + size_t num_input_streams = step->getInputStreams().size(); + + if (num_input_streams == 0) + { + if (isInitialized()) + throw Exception("Cannot add step " + step->getName() + " to QueryPlan because " + "step has no inputs, but QueryPlan is already initialised", ErrorCodes::LOGICAL_ERROR); + + nodes.emplace_back(Node{.step = std::move(step)}); + return; + } + + if (num_input_streams == 1) + { + if (!isInitialized()) + throw Exception("Cannot add step " + step->getName() + " to QueryPlan because " + "step has input, but QueryPlan is not initialised", ErrorCodes::LOGICAL_ERROR); + + const auto & root_header = root->step->getOutputStream().header; + const auto & step_header = step->getInputStreams().front().header; + if (!blocksHaveEqualStructure(root_header, step_header)) + throw Exception("Cannot add step " + step->getName() + " to QueryPlan because " + "it has incompatible header with root step " + root->step->getName() + " " + "root header: " + root_header.dumpStructure() + + "step header: " + step_header.dumpStructure(), ErrorCodes::LOGICAL_ERROR); + + nodes.emplace_back(Node{.step = std::move(step), .children = {root}}); + root = &nodes.back(); + return; + } + + throw Exception("Cannot add step " + step->getName() + " to QueryPlan because it has " + + std::to_string(num_input_streams) + " inputs but " + std::to_string(isInitialized() ? 1 : 0) + + " input expected", ErrorCodes::LOGICAL_ERROR); +} + +QueryPipelinePtr QueryPlan::buildQueryPipeline() +{ + checkInitialized(); + + struct Frame + { + Node * node; + QueryPipelines pipelines; + }; + + QueryPipelinePtr last_pipeline; + + std::stack stack; + stack.push({.node = root}); + + while (!stack.empty()) + { + auto & frame = stack.top(); + + if (last_pipeline) + frame.pipelines.emplace_back(std::move(last_pipeline)); + + size_t next_child = frame.pipelines.size(); + if (next_child == frame.node->children.size()) + { + last_pipeline = frame.node->step->updatePipeline(std::move(frame.pipelines)); + stack.pop(); + } + else + stack.push({.node = frame.node->children[next_child]}); + } + + return last_pipeline; +} + +} diff --git a/src/Processors/QueryPlan/QueryPlan.h b/src/Processors/QueryPlan/QueryPlan.h new file mode 100644 index 000000000000..de932524903a --- /dev/null +++ b/src/Processors/QueryPlan/QueryPlan.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include + +namespace DB +{ + +class DataStream; + +class IQueryPlanStep; +using QueryPlanStepPtr = std::unique_ptr; + +class QueryPipeline; +using QueryPipelinePtr = std::unique_ptr; + +/// A tree of query steps. +class QueryPlan +{ +public: + void addStep(QueryPlanStepPtr step); + + bool isInitialized() const { return root != nullptr; } /// Tree is not empty + bool isCompleted() const; /// Tree is not empty and root hasOutputStream() + const DataStream & getCurrentDataStream() const; /// Checks that (isInitialized() && !isCompleted()) + + QueryPipelinePtr buildQueryPipeline(); + +private: + struct Node + { + QueryPlanStepPtr step; + std::vector children; + }; + + using Nodes = std::list; + Nodes nodes; + + Node * root = nullptr; + + void checkInitialized() const; + void checkNotCompleted() const; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index fee4847fb56c..5cbc5dfd291a 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -135,6 +135,7 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp QueryPlan/IQueryPlanStep.cpp + QueryPlan/QueryPlan.cpp ) END() From fc981a28690dc1de53d148e4f3e7ab400479dc85 Mon Sep 17 00:00:00 2001 From: MovElb Date: Mon, 8 Jun 2020 15:05:49 +0300 Subject: [PATCH 0264/1102] fix for review --- src/Server/PostgreSQLHandlerFactory.h | 3 ++- tests/integration/test_postgresql_protocol/configs/config.xml | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Server/PostgreSQLHandlerFactory.h b/src/Server/PostgreSQLHandlerFactory.h index 0546b4ef8c2f..4550e9ee8e9f 100644 --- a/src/Server/PostgreSQLHandlerFactory.h +++ b/src/Server/PostgreSQLHandlerFactory.h @@ -12,9 +12,10 @@ namespace DB class PostgreSQLHandlerFactory : public Poco::Net::TCPServerConnectionFactory { private: -#if USE_SSL IServer & server; Poco::Logger * log; + +#if USE_SSL bool ssl_enabled = true; #else bool ssl_enabled = false; diff --git a/tests/integration/test_postgresql_protocol/configs/config.xml b/tests/integration/test_postgresql_protocol/configs/config.xml index 678b48425b18..a833e2282224 100644 --- a/tests/integration/test_postgresql_protocol/configs/config.xml +++ b/tests/integration/test_postgresql_protocol/configs/config.xml @@ -24,7 +24,6 @@ - 9000 5433 127.0.0.1 From ef5d03a273b2dffa6f7712aa9e65a2b8c83d6331 Mon Sep 17 00:00:00 2001 From: MovElb Date: Mon, 8 Jun 2020 16:49:10 +0300 Subject: [PATCH 0265/1102] fix build --- src/Server/PostgreSQLHandlerFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/PostgreSQLHandlerFactory.cpp b/src/Server/PostgreSQLHandlerFactory.cpp index ce433188c04f..1158cf5835e2 100644 --- a/src/Server/PostgreSQLHandlerFactory.cpp +++ b/src/Server/PostgreSQLHandlerFactory.cpp @@ -8,7 +8,7 @@ namespace DB PostgreSQLHandlerFactory::PostgreSQLHandlerFactory(IServer & server_) : server(server_) - , log(&Logger::get("PostgreSQLHandlerFactory")) + , log(&Poco::Logger::get("PostgreSQLHandlerFactory")) { auth_methods = { From e6dcece2fddf0069f241d3ff8c5336df74e09238 Mon Sep 17 00:00:00 2001 From: MovElb Date: Mon, 8 Jun 2020 22:39:02 +0300 Subject: [PATCH 0266/1102] return tcp_port --- tests/integration/test_postgresql_protocol/configs/config.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test_postgresql_protocol/configs/config.xml b/tests/integration/test_postgresql_protocol/configs/config.xml index a833e2282224..678b48425b18 100644 --- a/tests/integration/test_postgresql_protocol/configs/config.xml +++ b/tests/integration/test_postgresql_protocol/configs/config.xml @@ -24,6 +24,7 @@ + 9000 5433 127.0.0.1 From 966593e0a8e42266138cbd5af917ec0be60a3c0a Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 9 Jun 2020 15:50:18 +0300 Subject: [PATCH 0267/1102] try to completely remove sentry from odbc-bridge --- programs/odbc-bridge/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/odbc-bridge/CMakeLists.txt b/programs/odbc-bridge/CMakeLists.txt index ab8d94f2a0cf..51abf4a9adbd 100644 --- a/programs/odbc-bridge/CMakeLists.txt +++ b/programs/odbc-bridge/CMakeLists.txt @@ -10,7 +10,7 @@ set (CLICKHOUSE_ODBC_BRIDGE_SOURCES PingHandler.cpp validateODBCConnectionString.cpp ) - +set (USE_SENTRY OFF CACHE BOOL "" FORCE) set (CLICKHOUSE_ODBC_BRIDGE_LINK PRIVATE clickhouse_parsers From d91f0bd580aa8632cc89aae0531281f300b48740 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 9 Jun 2020 19:07:40 +0300 Subject: [PATCH 0268/1102] Switch back to sentry upstream --- .gitmodules | 2 +- contrib/sentry-native | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index ff4e644f657d..4175eb223db7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/blinkov/sentry-native.git + url = https://github.com/getsentry/sentry-native.git diff --git a/contrib/sentry-native b/contrib/sentry-native index 18835dd8c496..9651561d45e4 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit 18835dd8c496f22859bd6a1a7054a2bd4762e7ed +Subproject commit 9651561d45e4d00e9fe708275c086a3cfeb496bd From f872c639ed6893d0731ed61c1927f3c6f313f0d2 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 9 Jun 2020 20:44:56 +0300 Subject: [PATCH 0269/1102] Try to disable linker options from sentry --- .gitmodules | 2 +- cmake/find/sentry.cmake | 1 + contrib/sentry-native | 2 +- programs/odbc-bridge/CMakeLists.txt | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4175eb223db7..ff4e644f657d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -162,4 +162,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/getsentry/sentry-native.git + url = https://github.com/blinkov/sentry-native.git diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index e1cd28c1d595..7fa384cb906e 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -11,6 +11,7 @@ if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILE set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) set (SENTRY_TRANSPORT "curl" CACHE STRING "") set (SENTRY_BACKEND "none" CACHE STRING "") + set (SENTRY_EXPORT_SYMBOLS OFF CACHE BOOL "") set (SENTRY_LINK_PTHREAD OFF CACHE BOOL "") if (OS_LINUX AND NOT_UNBUNDLED) set (BUILD_SHARED_LIBS OFF) diff --git a/contrib/sentry-native b/contrib/sentry-native index 9651561d45e4..78fb54989cd6 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit 9651561d45e4d00e9fe708275c086a3cfeb496bd +Subproject commit 78fb54989cd61cf11dcea142e12d1ecc6940c962 diff --git a/programs/odbc-bridge/CMakeLists.txt b/programs/odbc-bridge/CMakeLists.txt index 51abf4a9adbd..af59383d0309 100644 --- a/programs/odbc-bridge/CMakeLists.txt +++ b/programs/odbc-bridge/CMakeLists.txt @@ -10,7 +10,6 @@ set (CLICKHOUSE_ODBC_BRIDGE_SOURCES PingHandler.cpp validateODBCConnectionString.cpp ) -set (USE_SENTRY OFF CACHE BOOL "" FORCE) set (CLICKHOUSE_ODBC_BRIDGE_LINK PRIVATE clickhouse_parsers From 6191d33bd9cb67d4dcabb79202bdd91baf467ddd Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 10 Jun 2020 16:30:12 +0300 Subject: [PATCH 0270/1102] Do not cache frames inside StackTrace --- base/daemon/BaseDaemon.cpp | 1 - cmake/find/sentry.cmake | 1 + src/Common/StackTrace.cpp | 30 ++++++++---------------------- src/Common/StackTrace.h | 7 +------ 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/base/daemon/BaseDaemon.cpp b/base/daemon/BaseDaemon.cpp index 9da8849342d9..1467657d31ac 100644 --- a/base/daemon/BaseDaemon.cpp +++ b/base/daemon/BaseDaemon.cpp @@ -223,7 +223,6 @@ class SignalListener : public Poco::Runnable DB::readPODBinary(stack_trace, in); DB::readBinary(thread_num, in); DB::readBinary(query_id, in); - stack_trace.resetFrames(); /// This allows to receive more signals if failure happens inside onFault function. /// Example: segfault while symbolizing stack trace. diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 7fa384cb906e..2281d870dec5 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -13,6 +13,7 @@ if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILE set (SENTRY_BACKEND "none" CACHE STRING "") set (SENTRY_EXPORT_SYMBOLS OFF CACHE BOOL "") set (SENTRY_LINK_PTHREAD OFF CACHE BOOL "") + set (SENTRY_PIC OFF CACHE BOOL "") if (OS_LINUX AND NOT_UNBUNDLED) set (BUILD_SHARED_LIBS OFF) endif() diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 819f74f37cbf..aacda116bfb0 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -199,12 +199,12 @@ static void symbolize(const void * const * frame_pointers, size_t offset, size_t for (size_t i = 0; i < offset; ++i) { - frames.value()[i].virtual_addr = frame_pointers[i]; + frames[i].virtual_addr = frame_pointers[i]; } for (size_t i = offset; i < size; ++i) { - StackTrace::Frame & current_frame = frames.value()[i]; + StackTrace::Frame & current_frame = frames[i]; current_frame.virtual_addr = frame_pointers[i]; const auto * object = symbol_index.findObject(current_frame.virtual_addr); uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; @@ -244,7 +244,7 @@ static void symbolize(const void * const * frame_pointers, size_t offset, size_t #else for (size_t i = 0; i < size; ++i) { - frames.value()[i].virtual_addr = frame_pointers[i]; + frames[i].virtual_addr = frame_pointers[i]; } UNUSED(offset); #endif @@ -309,16 +309,6 @@ const StackTrace::FramePointers & StackTrace::getFramePointers() const return frame_pointers; } -const StackTrace::Frames & StackTrace::getFrames() const -{ - if (!frames.has_value()) - { - frames.emplace(); - symbolize(frame_pointers.data(), offset, size, frames); - } - return frames; -} - static void toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t size, std::function callback) { @@ -329,7 +319,7 @@ toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t s for (size_t i = offset; i < size; ++i) { - const StackTrace::Frame & current_frame = frames.value()[i]; + const StackTrace::Frame & current_frame = frames[i]; out << i << ". "; if (current_frame.file.has_value() && current_frame.line.has_value()) @@ -356,8 +346,7 @@ toStringEveryLineImpl(const StackTrace::Frames & frames, size_t offset, size_t s static std::string toStringImpl(const void * const * frame_pointers, size_t offset, size_t size) { std::stringstream out; - StackTrace::Frames frames{}; - frames.emplace(); + StackTrace::Frames frames; symbolize(frame_pointers, offset, size, frames); toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); return out.str(); @@ -365,12 +354,9 @@ static std::string toStringImpl(const void * const * frame_pointers, size_t offs void StackTrace::toStringEveryLine(std::function callback) const { - toStringEveryLineImpl(getFrames(), offset, size, std::move(callback)); -} - -void StackTrace::resetFrames() -{ - frames.reset(); + Frames frames; + symbolize(frame_pointers.data(), offset, size, frames); + toStringEveryLineImpl(frames, offset, size, std::move(callback)); } diff --git a/src/Common/StackTrace.h b/src/Common/StackTrace.h index 27b2c44dd949..4ec63b3cf86d 100644 --- a/src/Common/StackTrace.h +++ b/src/Common/StackTrace.h @@ -36,7 +36,7 @@ class StackTrace }; static constexpr size_t capacity = 32; using FramePointers = std::array; - using Frames = std::optional>; + using Frames = std::array; /// Tries to capture stack trace StackTrace(); @@ -51,22 +51,17 @@ class StackTrace size_t getSize() const; size_t getOffset() const; const FramePointers & getFramePointers() const; - const Frames & getFrames() const; std::string toString() const; static std::string toString(void ** frame_pointers, size_t offset, size_t size); void toStringEveryLine(std::function callback) const; - - void resetFrames(); - protected: void tryCapture(); size_t size = 0; size_t offset = 0; /// How many frames to skip while displaying. FramePointers frame_pointers{}; - mutable Frames frames{}; }; std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context); From 60b40f04039702a0d8d55c44cfb81e96c932836e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 10 Jun 2020 17:51:25 +0300 Subject: [PATCH 0271/1102] Lost part of refactoring --- base/daemon/SentryWriter.cpp | 4 +++- src/Common/StackTrace.cpp | 6 +++--- src/Common/StackTrace.h | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index c8197d8a1603..eddd5bfa49c5 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -146,9 +146,11 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c offset = 1; } char instruction_addr[100]; + StackTrace::Frames frames; + StackTrace::symbolize(stack_trace.getFramePointers().data(), offset, size, frames); for (size_t i = stack_size - 1; i >= offset; --i) { - const StackTrace::Frame & current_frame = stack_trace.getFrames().value()[i]; + const StackTrace::Frame & current_frame = frames[i]; sentry_value_t frame = sentry_value_new_object(); UInt64 frame_ptr = reinterpret_cast(current_frame.virtual_addr); std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIu64 "x", frame_ptr); diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index aacda116bfb0..8e390154838c 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -190,7 +190,7 @@ static void * getCallerAddress(const ucontext_t & context) #endif } -static void symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames) +void StackTrace::symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames) { #if defined(__ELF__) && !defined(__FreeBSD__) && !defined(ARCADIA_BUILD) @@ -347,7 +347,7 @@ static std::string toStringImpl(const void * const * frame_pointers, size_t offs { std::stringstream out; StackTrace::Frames frames; - symbolize(frame_pointers, offset, size, frames); + StackTrace::symbolize(frame_pointers.data(), offset, size, frames); toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); return out.str(); } @@ -355,7 +355,7 @@ static std::string toStringImpl(const void * const * frame_pointers, size_t offs void StackTrace::toStringEveryLine(std::function callback) const { Frames frames; - symbolize(frame_pointers.data(), offset, size, frames); + StackTrace::symbolize(frame_pointers.data(), offset, size, frames); toStringEveryLineImpl(frames, offset, size, std::move(callback)); } diff --git a/src/Common/StackTrace.h b/src/Common/StackTrace.h index 4ec63b3cf86d..374f03145330 100644 --- a/src/Common/StackTrace.h +++ b/src/Common/StackTrace.h @@ -54,6 +54,7 @@ class StackTrace std::string toString() const; static std::string toString(void ** frame_pointers, size_t offset, size_t size); + static void symbolize(const void * const * frame_pointers, size_t offset, size_t size, StackTrace::Frames & frames); void toStringEveryLine(std::function callback) const; protected: From 0316464ed4eb22593bd7fc18b79584cc0f476ce0 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 10 Jun 2020 18:30:13 +0300 Subject: [PATCH 0272/1102] fix --- src/Common/StackTrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/StackTrace.cpp b/src/Common/StackTrace.cpp index 8e390154838c..cb0488b489ad 100644 --- a/src/Common/StackTrace.cpp +++ b/src/Common/StackTrace.cpp @@ -347,7 +347,7 @@ static std::string toStringImpl(const void * const * frame_pointers, size_t offs { std::stringstream out; StackTrace::Frames frames; - StackTrace::symbolize(frame_pointers.data(), offset, size, frames); + StackTrace::symbolize(frame_pointers, offset, size, frames); toStringEveryLineImpl(frames, offset, size, [&](const std::string & str) { out << str << '\n'; }); return out.str(); } From 897a592ee60b9c4308aa12bc46a2cc3d933a2f75 Mon Sep 17 00:00:00 2001 From: kssenii Date: Tue, 9 Jun 2020 21:52:06 +0000 Subject: [PATCH 0273/1102] Move subscription --- .../RabbitMQ/RabbitMQBlockInputStream.cpp | 2 +- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 14 +-- src/Storages/RabbitMQ/RabbitMQHandler.h | 2 +- .../ReadBufferFromRabbitMQConsumer.cpp | 92 ++++++------------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 23 +++-- 5 files changed, 43 insertions(+), 90 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index 1c6eaf6f2e90..245320008f37 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -50,7 +50,7 @@ void RabbitMQBlockInputStream::readPrefixImpl() if (!buffer || finished) return; - buffer->subscribeConsumer(); + buffer->checkSubscription(); } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index d9dc19afa28c..0a432e1b5ca4 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -34,23 +34,15 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes } -void RabbitMQHandler::startConsumerLoop(std::atomic & check_param, std::atomic & loop_started) +void RabbitMQHandler::startConsumerLoop(std::atomic & loop_started) { /* The object of this class is shared between concurrent consumers (who share the same connection == share the same * event loop). But the loop should not be attempted to start if it is already running. */ if (mutex_before_event_loop.try_lock_for(std::chrono::milliseconds(Lock_timeout))) { - /* The callback, which changes this variable, could have already been activated by another thread while we waited - * for the mutex to unlock (as it runs all active events on the connection). This means that there is no need to - * start event loop again. - */ - if (!check_param) - { - loop_started = true; - event_base_loop(evbase, EVLOOP_NONBLOCK); - } - + loop_started = true; + event_base_loop(evbase, EVLOOP_NONBLOCK); mutex_before_event_loop.unlock(); } } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index a8692a845f17..911651097bb2 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -19,7 +19,7 @@ class RabbitMQHandler : public AMQP::LibEventHandler RabbitMQHandler(event_base * evbase_, Poco::Logger * log_); void onError(AMQP::TcpConnection * connection, const char * message) override; - void startConsumerLoop(std::atomic & check_param, std::atomic & loop_started); + void startConsumerLoop(std::atomic & loop_started); void startProducerLoop(); void stopWithTimeout(); void stop(); diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 5d649ab20840..32dcd30e6f5c 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -13,10 +13,6 @@ namespace DB { -enum -{ - Loop_retries_limit = 500 -}; ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, @@ -109,9 +105,6 @@ void ReadBufferFromRabbitMQConsumer::initExchange() void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { - /* This varibale can be updated from a different thread in case of some error so its better to check - * whether exchange is in a working state and if not - declare it once again. - */ if (!exchange_declared) { initExchange(); @@ -144,6 +137,9 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } } + /// Must be done here, cannot be done in readPrefix() + subscribe(queues.back()); + LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); consumer_channel->bindQueue(current_exchange_name, queue_name_, binding_key) @@ -169,26 +165,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) */ while (!bindings_created && !bindings_error) { - startEventLoop(bindings_created, loop_started); - } -} - - -void ReadBufferFromRabbitMQConsumer::subscribeConsumer() -{ - if (subscribed) - return; - - for (auto & queue : queues) - { - subscribe(queue); - } - - LOG_TRACE(log, "Consumer {} is subscribed to {} queues", channel_id, count_subscribed); - - if (count_subscribed == queues.size()) - { - subscribed = true; + startEventLoop(loop_started); } } @@ -198,25 +175,13 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) if (subscribed_queue[queue_name]) return; - consumer_created = false, consumer_failed = false; - consumer_channel->consume(queue_name, AMQP::noack) .onSuccess([&](const std::string & /* consumer */) { - consumer_created = true; + subscribed_queue[queue_name] = true; ++count_subscribed; LOG_TRACE(log, "Consumer {} is subscribed to queue {}", channel_id, queue_name); - - /* Unblock current thread if it is looping (any consumer could start the loop and only one of them) so that it does not - * continue to execute all active callbacks on the connection (=> one looping consumer will not be blocked for too - * long and events will be distributed between them) - */ - if (loop_started && count_subscribed == queues.size()) - { - stopEventLoop(); - subscribed = true; - } }) .onReceived([&](const AMQP::Message & message, uint64_t /* deliveryTag */, bool /* redelivered */) { @@ -232,7 +197,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) bool stop_loop = false; - /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl() (below). + /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl(). { std::lock_guard lock(mutex); received.push_back(message_received); @@ -255,35 +220,32 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) }) .onError([&](const char * message) { - consumer_failed = true; + consumer_error = true; LOG_ERROR(log, "Consumer {} failed: {}", channel_id, message); }); +} - size_t cnt_retries = 0; - /// These variables are updated in a separate thread. - while (!consumer_created && !consumer_failed) - { - startEventLoop(consumer_created, loop_started); +void ReadBufferFromRabbitMQConsumer::checkSubscription() +{ + /// In general this condition will always be true and looping/resubscribing would not happen + if (count_subscribed == num_queues) + return; - if (!consumer_created && !consumer_failed) - { - if (cnt_retries >= Loop_retries_limit) - { - /* For unknown reason there is a case when subscribtion may fail and OnError callback is not activated - * for a long time. In this case there should be resubscription. - */ - LOG_ERROR(log, "Consumer {} failed to subscride to queue {}", channel_id, queue_name); - break; - } + wait_subscribed = num_queues; - ++cnt_retries; - } + /// These variables are updated in a separate thread + while (count_subscribed != wait_subscribed && !consumer_error) + { + startEventLoop(loop_started); } - if (consumer_created) + LOG_TRACE(log, "Consumer {} is subscribed to {} queues", channel_id, count_subscribed); + + /// A case that would not normally happen + for (auto & queue : queues) { - subscribed_queue[queue_name] = true; + subscribe(queue); } } @@ -300,9 +262,9 @@ void ReadBufferFromRabbitMQConsumer::stopEventLoopWithTimeout() } -void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & check_param, std::atomic & loop_started) +void ReadBufferFromRabbitMQConsumer::startEventLoop(std::atomic & loop_started) { - eventHandler.startConsumerLoop(check_param, loop_started); + eventHandler.startConsumerLoop(loop_started); } @@ -316,7 +278,7 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() if (received.empty()) { /// Run the onReceived callbacks to save the messages that have been received by now, blocks current thread - startEventLoop(false_param, loop_started); + startEventLoop(loop_started); loop_started = false; } @@ -328,7 +290,7 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() messages.clear(); - /// Needed to avoid data race because this vector can be used at the same time by another thread in onReceived callback (above). + /// Needed to avoid data race because this vector can be used at the same time by another thread in onReceived callback. std::lock_guard lock(mutex); messages.swap(received); diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 9e0b29307c47..7fbc1024d44b 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -37,7 +37,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer ~ReadBufferFromRabbitMQConsumer() override; void allowNext() { allowed = true; } // Allow to read next message. - void subscribeConsumer(); + void checkSubscription(); private: using Messages = std::vector; @@ -59,16 +59,9 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const std::atomic & stopped; String current_exchange_name; - - /* Note: as all concurrent consumers share the same connection => they also share the same - * event loop, which can be started by any consumer and the loop is blocking only to the thread that - * started it, and the loop executes ALL active callbacks on the connection => in case num_consumers > 1, - * at most two threads will be present: main thread and the one that executes callbacks (1 thread if - * main thread is the one that started the loop). Both reference these variables. - */ - std::atomic exchange_declared = false, subscribed = false, loop_started = false, false_param = false; - std::atomic consumer_created = false, consumer_failed = false; - std::atomic count_subscribed = 0; + bool exchange_declared = false; + std::atomic loop_started = false, consumer_error = false; + std::atomic count_subscribed = 0, wait_subscribed; std::vector queues; Messages received; @@ -76,6 +69,12 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer Messages::iterator current; std::unordered_map subscribed_queue; + /* Note: as all consumers share the same connection => they also share the same + * event loop, which can be started by any consumer and the loop is blocking only to the thread that + * started it, and the loop executes ALL active callbacks on the connection => in case num_consumers > 1, + * at most two threads will be present: main thread and the one that executes callbacks (1 thread if + * main thread is the one that started the loop). + */ std::mutex mutex; bool nextImpl() override; @@ -83,7 +82,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer void initExchange(); void initQueueBindings(const size_t queue_id); void subscribe(const String & queue_name); - void startEventLoop(std::atomic & check_param, std::atomic & loop_started); + void startEventLoop(std::atomic & loop_started); void stopEventLoopWithTimeout(); void stopEventLoop(); From 0a74c9373ec46713b3deb51e86c702418fc29a0d Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 10 Jun 2020 19:48:08 +0300 Subject: [PATCH 0274/1102] less confusing --- base/daemon/SentryWriter.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index eddd5bfa49c5..003a2816ce0e 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -136,7 +136,7 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c setExtras(); /// Prepare data for https://develop.sentry.dev/sdk/event-payloads/stacktrace/ - sentry_value_t frames = sentry_value_new_list(); + sentry_value_t sentry_frames = sentry_value_new_list(); size_t stack_size = stack_trace.getSize(); if (stack_size > 0) { @@ -151,33 +151,33 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c for (size_t i = stack_size - 1; i >= offset; --i) { const StackTrace::Frame & current_frame = frames[i]; - sentry_value_t frame = sentry_value_new_object(); + sentry_value_t sentry_frame = sentry_value_new_object(); UInt64 frame_ptr = reinterpret_cast(current_frame.virtual_addr); std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIu64 "x", frame_ptr); - sentry_value_set_by_key(frame, "instruction_addr", sentry_value_new_string(instruction_addr)); + sentry_value_set_by_key(sentry_frame, "instruction_addr", sentry_value_new_string(instruction_addr)); if (current_frame.symbol.has_value()) { - sentry_value_set_by_key(frame, "function", sentry_value_new_string(current_frame.symbol.value().c_str())); + sentry_value_set_by_key(sentry_frame, "function", sentry_value_new_string(current_frame.symbol.value().c_str())); } if (current_frame.file.has_value()) { - sentry_value_set_by_key(frame, "filename", sentry_value_new_string(current_frame.file.value().c_str())); + sentry_value_set_by_key(sentry_frame, "filename", sentry_value_new_string(current_frame.file.value().c_str())); } if (current_frame.line.has_value()) { - sentry_value_set_by_key(frame, "lineno", sentry_value_new_int32(current_frame.line.value())); + sentry_value_set_by_key(sentry_frame, "lineno", sentry_value_new_int32(current_frame.line.value())); } - sentry_value_append(frames, frame); + sentry_value_append(sentry_frames, sentry_frame); } } /// Prepare data for https://develop.sentry.dev/sdk/event-payloads/threads/ sentry_value_t stacktrace = sentry_value_new_object(); - sentry_value_set_by_key(stacktrace, "frames", frames); + sentry_value_set_by_key(stacktrace, "frames", sentry_frames); sentry_value_t thread = sentry_value_new_object(); sentry_value_set_by_key(thread, "stacktrace", stacktrace); From 5fa44019918581a4378582bcd72ff5b160e5f659 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 10 Jun 2020 21:18:34 +0300 Subject: [PATCH 0275/1102] fix --- base/daemon/SentryWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 003a2816ce0e..2ce43c9f0a21 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -147,7 +147,7 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c } char instruction_addr[100]; StackTrace::Frames frames; - StackTrace::symbolize(stack_trace.getFramePointers().data(), offset, size, frames); + StackTrace::symbolize(stack_trace.getFramePointers().data(), offset, stack_size, frames); for (size_t i = stack_size - 1; i >= offset; --i) { const StackTrace::Frame & current_frame = frames[i]; From 472b04b69c58adaac2cd1030d7921c7ceb6f09a8 Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 10 Jun 2020 19:59:37 +0000 Subject: [PATCH 0276/1102] Better producer --- .../RabbitMQ/RabbitMQBlockOutputStream.cpp | 7 +-- .../WriteBufferToRabbitMQProducer.cpp | 50 +++++++------------ .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 3 +- 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp index 8e867db6de9d..5dc2c1f8fc44 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockOutputStream.cpp @@ -37,17 +37,14 @@ void RabbitMQBlockOutputStream::writePrefix() { buffer->countRow(); }); + + buffer->startEventLoop(); } void RabbitMQBlockOutputStream::write(const Block & block) { child->write(block); - - if (buffer) - buffer->flush(); - - storage.pingConnection(); } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 31c3dea97aae..151d5fc62d47 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -16,7 +16,7 @@ enum { Connection_setup_sleep = 200, Connection_setup_retries_max = 1000, - Buffer_limit_to_flush = 10000 /// It is important to keep it low in order not to kill consumers + Batch = 10000 }; WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( @@ -64,12 +64,13 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( } producer_channel = std::make_shared(&connection); + checkExchange(); } WriteBufferToRabbitMQProducer::~WriteBufferToRabbitMQProducer() { - flush(); + checkExchange(); connection.close(); assert(rows == 0 && chunks.empty()); @@ -98,18 +99,29 @@ void WriteBufferToRabbitMQProducer::countRow() chunks.clear(); set(nullptr, 0); - messages.emplace_back(payload); + next_queue = next_queue % num_queues + 1; + + if (bind_by_id || hash_exchange) + { + producer_channel->publish(exchange_name, std::to_string(next_queue), payload); + } + else + { + producer_channel->publish(exchange_name, routing_key, payload); + } + ++message_counter; - if (messages.size() >= Buffer_limit_to_flush) + /// run event loop to actually publish, checking exchange is just a point to stop the event loop + if ((message_counter %= Batch) == 0) { - flush(); + checkExchange(); } } } -void WriteBufferToRabbitMQProducer::flush() +void WriteBufferToRabbitMQProducer::checkExchange() { std::atomic exchange_declared = false, exchange_error = false; @@ -120,32 +132,6 @@ void WriteBufferToRabbitMQProducer::flush() .onSuccess([&]() { exchange_declared = true; - - /* The reason for accumulating payloads and not publishing each of them at once in count_row() is that publishing - * needs to be wrapped inside declareExchange() callback and it is too expensive in terms of time to declare it - * each time we publish. Declaring it once and then publishing without wrapping inside onSuccess callback leads to - * exchange becoming inactive at some point and part of messages is lost as a result. - */ - for (auto & payload : messages) - { - if (!message_counter) - break; - - next_queue = next_queue % num_queues + 1; - - if (bind_by_id || hash_exchange) - { - producer_channel->publish(exchange_name, std::to_string(next_queue), payload); - } - else - { - producer_channel->publish(exchange_name, routing_key, payload); - } - - --message_counter; - } - - messages.clear(); }) .onError([&](const char * message) { diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index 9ae3893d6ae5..3cbcec9ccc2c 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -35,12 +35,11 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer ~WriteBufferToRabbitMQProducer() override; void countRow(); - void flush(); + void startEventLoop(); private: void nextImpl() override; void checkExchange(); - void startEventLoop(); std::pair & login_password; const String routing_key; From 67ccd6703ea9de805b65c7fa25a3c43620571b55 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 00:03:13 +0300 Subject: [PATCH 0277/1102] maybe fix the unbundled gcc build --- cmake/find/sentry.cmake | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 2281d870dec5..2d3aa71248af 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,7 +5,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILER_CLANG)) +if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT UNBUNDLED AND NOT (OS_DARWIN AND COMPILER_CLANG)) option (USE_SENTRY "Use Sentry" ON) set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) @@ -14,9 +14,7 @@ if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT (OS_DARWIN AND COMPILE set (SENTRY_EXPORT_SYMBOLS OFF CACHE BOOL "") set (SENTRY_LINK_PTHREAD OFF CACHE BOOL "") set (SENTRY_PIC OFF CACHE BOOL "") - if (OS_LINUX AND NOT_UNBUNDLED) - set (BUILD_SHARED_LIBS OFF) - endif() + set (BUILD_SHARED_LIBS OFF) message (STATUS "Using sentry=${USE_SENTRY}: ${SENTRY_LIBRARY}") include_directories("${SENTRY_INCLUDE_DIR}") From d7b269480641aabbcec3f1702dacfefab123e6c5 Mon Sep 17 00:00:00 2001 From: kssenii Date: Wed, 10 Jun 2020 23:01:47 +0000 Subject: [PATCH 0278/1102] Support all exchange types --- src/Storages/RabbitMQ/RabbitMQSettings.h | 3 +- .../ReadBufferFromRabbitMQConsumer.cpp | 132 +++++-- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 12 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 33 +- src/Storages/RabbitMQ/StorageRabbitMQ.h | 6 +- .../WriteBufferToRabbitMQProducer.cpp | 6 +- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 4 +- .../integration/test_storage_rabbitmq/test.py | 368 ++++++++++++++---- 8 files changed, 411 insertions(+), 153 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index 509ed68b8d36..a3f133cfed04 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -12,12 +12,13 @@ namespace DB #define LIST_OF_RABBITMQ_SETTINGS(M) \ M(SettingString, rabbitmq_host_port, "", "A host-port to connect to RabbitMQ server.", 0) \ M(SettingString, rabbitmq_routing_key, "5672", "A routing key to connect producer->exchange->queue<->consumer.", 0) \ - M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exhange name, to which messages are sent. Needed to bind queues to it.", 0) \ + M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exchange name, to which messages are sent. Needed to bind queues to it.", 0) \ M(SettingString, rabbitmq_format, "", "The message format.", 0) \ M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ M(SettingUInt64, rabbitmq_hash_exchange, 0, "A flag which indicates whether consistent-hash-exchange should be used.", 0) \ + M(SettingString, rabbitmq_exchange_type, "default", "The exchange type.", 0) \ DECLARE_SETTINGS_COLLECTION(LIST_OF_RABBITMQ_SETTINGS) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 32dcd30e6f5c..1321a4fb3b62 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -23,8 +23,9 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( Poco::Logger * log_, char row_delimiter_, const bool bind_by_id_, - const bool hash_exchange_, const size_t num_queues_, + const String & exchange_type_, + const String table_name_, const std::atomic & stopped_) : ReadBuffer(nullptr, 0) , consumer_channel(std::move(consumer_channel_)) @@ -35,18 +36,22 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , log(log_) , row_delimiter(row_delimiter_) , bind_by_id(bind_by_id_) - , hash_exchange(hash_exchange_) , num_queues(num_queues_) + , exchange_type(exchange_type_) + , table_name(table_name_) , stopped(stopped_) { messages.clear(); current = messages.begin(); + exchange_type_set = exchange_type != "default" ? 1 : 0; + /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. * By default there is one queue per consumer. */ for (size_t queue_id = 0; queue_id < num_queues; ++queue_id) { + /// Queue bingings must be declared before any publishing => it must be done here and not in readPrefix() initQueueBindings(queue_id); } } @@ -64,54 +69,88 @@ ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() void ReadBufferFromRabbitMQConsumer::initExchange() { - /* As there are 5 different types of exchanges and the type should be set as a parameter while publishing the message, - * then for uniformity this parameter should always be set as fanout-exchange type. In current implementation, the exchange, - * to which messages a published, will be bound to the exchange of the needed type, which will distribute messages according to its type. + /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest + * and it is also used for INSERT query. */ - consumer_channel->declareExchange(exchange_name, AMQP::fanout).onError([&](const char * message) + String producer_exchange = exchange_type_set ? exchange_name + "_default" : exchange_name; + consumer_channel->declareExchange(producer_exchange, AMQP::fanout).onError([&](const char * message) { - exchange_declared = false; - LOG_ERROR(log, "Failed to declare fanout exchange: {}", message); + internal_exchange_declared = false; + LOG_ERROR(log, "Failed to declare exchange: {}", message); }); - if (hash_exchange) + internal_exchange_name = producer_exchange + "_direct"; + consumer_channel->declareExchange(internal_exchange_name, AMQP::direct).onError([&](const char * message) { - current_exchange_name = exchange_name + "_hash"; - consumer_channel->declareExchange(current_exchange_name, AMQP::consistent_hash).onError([&](const char * /* message */) - { - exchange_declared = false; - }); + internal_exchange_declared = false; + LOG_ERROR(log, "Failed to declare exchange: {}", message); + }); - consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * /* message */) - { - exchange_declared = false; - }); - } - else + consumer_channel->bindExchange(producer_exchange, internal_exchange_name, routing_key).onError([&](const char * message) { - current_exchange_name = exchange_name + "_direct"; - consumer_channel->declareExchange(current_exchange_name, AMQP::direct).onError([&](const char * /* message */) - { - exchange_declared = false; - }); + internal_exchange_declared = false; + LOG_ERROR(log, "Failed to bind exchange: {}", message); + }); - consumer_channel->bindExchange(exchange_name, current_exchange_name, routing_key).onError([&](const char * /* message */) - { - exchange_declared = false; - }); - } + if (!exchange_type_set) + return; + + /// For special purposes to use the flexibility of routing provided by rabbitmq - choosing exchange types is also supported. + + AMQP::ExchangeType type; + if (exchange_type == "fanout") type = AMQP::ExchangeType::fanout; + else if (exchange_type == "direct") type = AMQP::ExchangeType::direct; + else if (exchange_type == "topic") type = AMQP::ExchangeType::topic; + else if (exchange_type == "consistent_hash") type = AMQP::ExchangeType::consistent_hash; + else return; + + /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages + * between all consumers. (This enables better scaling as without hash-echange - the only oprion to avoid getting the same + * messages more than once - is having only one consumer with one queue, which is not good.) + */ + consumer_channel->declareExchange(exchange_name, type).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); + }); + + hash_exchange = true; + + /// No need for declaring hash-exchange if there is only one consumer with one queue and exchange type is not hash + if (!bind_by_id && exchange_type != "consistent_hash") + return; + + AMQP::Table exchange_arguments; + exchange_arguments["hash-property"] = "message_id"; + + local_exchange_name = exchange_name + "_" + table_name; + consumer_channel->declareExchange(local_exchange_name, AMQP::consistent_hash, exchange_arguments) + .onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); + }); + + consumer_channel->bindExchange(exchange_name, local_exchange_name, routing_key).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); + }); } void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { - if (!exchange_declared) + /// These variables might be updated later from a separate thread in onError callbacks + if (!internal_exchange_declared || (exchange_type_set && !local_exchange_declared)) { initExchange(); - exchange_declared = true; + local_exchange_declared = true; + internal_exchange_declared = true; } - std::atomic bindings_created = false, bindings_error = false; + bool internal_bindings_created = false, internal_bindings_error = false; + bool local_bindings_created = false, local_bindings_error = false; consumer_channel->declareQueue(AMQP::exclusive) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) @@ -137,25 +176,39 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } } - /// Must be done here, cannot be done in readPrefix() + /// Must be done here and not in readPrefix() because library might fail to handle async subscription on the same connection subscribe(queues.back()); LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); - consumer_channel->bindQueue(current_exchange_name, queue_name_, binding_key) + consumer_channel->bindQueue(internal_exchange_name, queue_name_, binding_key) .onSuccess([&] { - bindings_created = true; + internal_bindings_created = true; }) .onError([&](const char * message) { - bindings_error = true; + internal_bindings_error = true; LOG_ERROR(log, "Failed to create queue binding: {}", message); }); + + if (exchange_type_set) + { + consumer_channel->bindQueue(local_exchange_name, queue_name_, binding_key) + .onSuccess([&] + { + local_bindings_created = true; + }) + .onError([&](const char * message) + { + local_bindings_error = true; + LOG_ERROR(log, "Failed to create queue binding: {}", message); + }); + } }) .onError([&](const char * message) { - bindings_error = true; + internal_bindings_error = true; LOG_ERROR(log, "Failed to declare queue on the channel: {}", message); }); @@ -163,7 +216,8 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) * It is important at this moment to make sure that queue bindings are created before any publishing can happen because * otherwise messages will be routed nowhere. */ - while (!bindings_created && !bindings_error) + while (!internal_bindings_created && !internal_bindings_error + || (exchange_type_set && !local_bindings_created && !local_bindings_error)) { startEventLoop(loop_started); } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 7fbc1024d44b..51eae60cdeb5 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -30,8 +30,9 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer Poco::Logger * log_, char row_delimiter_, const bool bind_by_id_, - const bool hash_exchange_, const size_t num_queues_, + const String & exchange_type_, + const String table_name_, const std::atomic & stopped_); ~ReadBufferFromRabbitMQConsumer() override; @@ -49,8 +50,9 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const String & routing_key; const size_t channel_id; const bool bind_by_id; - const bool hash_exchange; const size_t num_queues; + const String & exchange_type; + const String table_name; Poco::Logger * log; char row_delimiter; @@ -58,8 +60,10 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer bool allowed = true; const std::atomic & stopped; - String current_exchange_name; - bool exchange_declared = false; + String internal_exchange_name, local_exchange_name; + bool internal_exchange_declared = false, local_exchange_declared = false; + bool exchange_type_set = false, hash_exchange = false; + std::atomic loop_started = false, consumer_error = false; std::atomic count_subscribed = 0, wait_subscribed; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 481314a38c2f..895b9ca2bec7 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -59,9 +59,9 @@ StorageRabbitMQ::StorageRabbitMQ( const String & exchange_name_, const String & format_name_, char row_delimiter_, + const String & exchange_type_, size_t num_consumers_, - size_t num_queues_, - bool hash_exchange_) + size_t num_queues_) : IStorage(table_id_) , global_context(context_.getGlobalContext()) , rabbitmq_context(Context(global_context)) @@ -71,7 +71,7 @@ StorageRabbitMQ::StorageRabbitMQ( , row_delimiter(row_delimiter_) , num_consumers(num_consumers_) , num_queues(num_queues_) - , hash_exchange(hash_exchange_) + , exchange_type(exchange_type_) , log(&Poco::Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) , semaphore(0, num_consumers_) , login_password(std::make_pair( @@ -212,16 +212,20 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ChannelPtr consumer_channel = std::make_shared(&connection); + auto table_id = getStorageID(); + String table_name = table_id.getNameForLogs(); + return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_key, - next_channel_id, log, row_delimiter, bind_by_id, hash_exchange, num_queues, stream_cancelled); + next_channel_id, log, row_delimiter, bind_by_id, num_queues, exchange_type, table_name, stream_cancelled); } ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { - return std::make_shared(parsed_address, login_password, routing_key, exchange_name, - log, num_consumers * num_queues, bind_by_id, hash_exchange, - row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); + String producer_exchange = exchange_type == "default" ? exchange_name : exchange_name + "_default"; + + return std::make_shared(parsed_address, login_password, routing_key, producer_exchange, + log, num_consumers * num_queues, bind_by_id, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } @@ -436,20 +440,19 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } - bool hash_exchange = static_cast(rabbitmq_settings.rabbitmq_hash_exchange); + String exchange_type = rabbitmq_settings.rabbitmq_exchange_type.value; if (args_count >= 6) { + engine_args[5] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[5], args.local_context); + const auto * ast = engine_args[5]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - hash_exchange = static_cast(safeGet(ast->value)); - } - else + if (ast && ast->value.getType() == Field::Types::String) { - throw Exception("Hash exchange flag must be a boolean", ErrorCodes::BAD_ARGUMENTS); + exchange_type = safeGet(ast->value); } } + UInt64 num_consumers = rabbitmq_settings.rabbitmq_num_consumers; if (args_count >= 7) { @@ -480,7 +483,7 @@ void registerStorageRabbitMQ(StorageFactory & factory) return StorageRabbitMQ::create( args.table_id, args.context, args.columns, - host_port, routing_key, exchange, format, row_delimiter, num_consumers, num_queues, hash_exchange); + host_port, routing_key, exchange, format, row_delimiter, exchange_type, num_consumers, num_queues); }; factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 111e52768d03..27a9b8834f49 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -66,9 +66,9 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub const String & exchange_name_, const String & format_name_, char row_delimiter_, + const String & exchange_type_, size_t num_consumers_, - size_t num_queues_, - bool hash_exchange); + size_t num_queues_); private: Context global_context; @@ -83,7 +83,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub size_t num_created_consumers = 0; bool bind_by_id; size_t num_queues; - const bool hash_exchange; + const String exchange_type; Poco::Logger * log; std::pair parsed_address; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 151d5fc62d47..8fa241dade51 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -23,11 +23,10 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( std::pair & parsed_address, std::pair & login_password_, const String & routing_key_, - const String & exchange_, + const String exchange_, Poco::Logger * log_, const size_t num_queues_, const bool bind_by_id_, - const bool hash_exchange_, std::optional delimiter, size_t rows_per_message, size_t chunk_size_) @@ -38,7 +37,6 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( , log(log_) , num_queues(num_queues_) , bind_by_id(bind_by_id_) - , hash_exchange(hash_exchange_) , delim(delimiter) , max_rows(rows_per_message) , chunk_size(chunk_size_) @@ -101,7 +99,7 @@ void WriteBufferToRabbitMQProducer::countRow() next_queue = next_queue % num_queues + 1; - if (bind_by_id || hash_exchange) + if (bind_by_id) { producer_channel->publish(exchange_name, std::to_string(next_queue), payload); } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index 3cbcec9ccc2c..90e0d90b3564 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -22,11 +22,10 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer std::pair & parsed_address, std::pair & login_password_, const String & routing_key_, - const String & exchange_, + const String exchange_, Poco::Logger * log_, const size_t num_queues_, const bool bind_by_id_, - const bool hash_exchange_, std::optional delimiter, size_t rows_per_message, size_t chunk_size_ @@ -45,7 +44,6 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer const String routing_key; const String exchange_name; const bool bind_by_id; - const bool hash_exchange; const size_t num_queues; event_base * producerEvbase; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index d7e991fe7ae8..d9c08ef7b6bc 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -497,90 +497,6 @@ def test_rabbitmq_big_message(rabbitmq_cluster): assert int(result) == rabbitmq_messages*batch_messages, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) -def test_rabbitmq_sharding_between_tables(rabbitmq_cluster): - - NUMBER_OF_CONCURRENT_CONSUMERS = 10 - - instance.query(''' - DROP TABLE IF EXISTS test.destination; - CREATE TABLE test.destination(key UInt64, value UInt64, - _consumed_by LowCardinality(String)) - ENGINE = MergeTree() - ORDER BY key; - ''') - - for consumer_id in range(NUMBER_OF_CONCURRENT_CONSUMERS): - table_name = 'rabbitmq_consumer{}'.format(consumer_id) - print("Setting up {}".format(table_name)) - - instance.query(''' - DROP TABLE IF EXISTS test.{0}; - DROP TABLE IF EXISTS test.{0}_mv; - CREATE TABLE test.{0} (key UInt64, value UInt64) - ENGINE = RabbitMQ - SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_hash_exchange = 1, - rabbitmq_format = 'JSONEachRow', - rabbitmq_row_delimiter = '\\n'; - CREATE MATERIALIZED VIEW test.{0}_mv TO test.destination AS - SELECT key, value, '{0}' as _consumed_by FROM test.{0}; - '''.format(table_name)) - - i = [0] - messages_num = 1000 - - credentials = pika.PlainCredentials('root', 'clickhouse') - parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) - - def produce(): - # init connection here because otherwise python rabbitmq client fails sometimes - connection = pika.BlockingConnection(parameters) - channel = connection.channel() - channel.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') - messages = [] - for _ in range(messages_num): - messages.append(json.dumps({'key': i[0], 'value': i[0]})) - i[0] += 1 - key = str(randrange(1, NUMBER_OF_CONCURRENT_CONSUMERS)) - for message in messages: - channel.basic_publish(exchange='clickhouse-exchange', routing_key=key, body=message) - connection.close() - time.sleep(1) - - threads = [] - threads_num = 20 - - for _ in range(threads_num): - threads.append(threading.Thread(target=produce)) - for thread in threads: - time.sleep(random.uniform(0, 1)) - thread.start() - - while True: - result = instance.query('SELECT count() FROM test.destination') - time.sleep(1) - if int(result) == messages_num * threads_num: - break - - for consumer_id in range(NUMBER_OF_CONCURRENT_CONSUMERS): - print("dropping rabbitmq_consumer{}".format(consumer_id)) - table_name = 'rabbitmq_consumer{}'.format(consumer_id) - instance.query(''' - DROP TABLE IF EXISTS test.{0}; - DROP TABLE IF EXISTS test.{0}_mv; - '''.format(table_name)) - - instance.query(''' - DROP TABLE IF EXISTS test.destination; - ''') - - for thread in threads: - thread.join() - - assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) - - @pytest.mark.timeout(320) def test_rabbitmq_sharding_between_channels_publish(rabbitmq_cluster): @@ -1011,6 +927,7 @@ def insert(): while True: result = instance.query('SELECT count() FROM test.view_sharding') time.sleep(1) + print result if int(result) == messages_num * threads_num: break @@ -1085,6 +1002,289 @@ def insert(): assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) +@pytest.mark.timeout(420) +def test_rabbitmq_direct_exchange(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + num_tables = 5 + for consumer_id in range(num_tables): + print("Setting up table {}".format(consumer_id)) + instance.query(''' + DROP TABLE IF EXISTS test.direct_exchange_{0}; + DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; + CREATE TABLE test.direct_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 5, + rabbitmq_exchange_name = 'direct_exchange_testing', + rabbitmq_exchange_type = 'direct', + rabbitmq_routing_key = 'direct_{0}', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.direct_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.direct_exchange_{0}; + '''.format(consumer_id)) + + i = [0] + messages_num = 1000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='direct_exchange_testing', exchange_type='direct') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + + key_num = 0 + for num in range(num_tables): + key = "direct_" + str(key_num) + key_num += 1 + for message in messages: + channel.basic_publish(exchange='direct_exchange_testing', routing_key=key, body=message) + + connection.close() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * num_tables: + break + + assert int(result) == messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(420) +def test_rabbitmq_fanout_exchange(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + num_tables = 5 + for consumer_id in range(num_tables): + print("Setting up table {}".format(consumer_id)) + instance.query(''' + DROP TABLE IF EXISTS test.fanout_exchange_{0}; + DROP TABLE IF EXISTS test.fanout_exchange_{0}_mv; + CREATE TABLE test.fanout_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 5, + rabbitmq_routing_key = 'key_{0}', + rabbitmq_exchange_name = 'fanout_exchange_testing', + rabbitmq_exchange_type = 'fanout', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.fanout_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.fanout_exchange_{0}; + '''.format(consumer_id)) + + i = [0] + messages_num = 1000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='fanout_exchange_testing', exchange_type='fanout') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + + key_num = 0 + for message in messages: + channel.basic_publish(exchange='fanout_exchange_testing', routing_key='', body=message) + + connection.close() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * num_tables: + break + + assert int(result) == messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(420) +def test_rabbitmq_topic_exchange(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + num_tables = 5 + for consumer_id in range(num_tables): + print("Setting up table {}".format(consumer_id)) + instance.query(''' + DROP TABLE IF EXISTS test.topic_exchange_{0}; + DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; + CREATE TABLE test.topic_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 5, + rabbitmq_exchange_name = 'topic_exchange_testing', + rabbitmq_exchange_type = 'topic', + rabbitmq_routing_key = '*.{0}', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.topic_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.topic_exchange_{0}; + '''.format(consumer_id)) + + for consumer_id in range(num_tables): + print("Setting up table {}".format(num_tables + consumer_id)) + instance.query(''' + DROP TABLE IF EXISTS test.topic_exchange_{0}; + DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; + CREATE TABLE test.topic_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 4, + rabbitmq_exchange_name = 'topic_exchange_testing', + rabbitmq_exchange_type = 'topic', + rabbitmq_routing_key = '*.logs', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.topic_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.topic_exchange_{0}; + '''.format(num_tables + consumer_id)) + + i = [0] + messages_num = 1000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='topic_exchange_testing', exchange_type='topic') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + + key_num = 0 + for num in range(num_tables): + key = "topic." + str(key_num) + key_num += 1 + for message in messages: + channel.basic_publish(exchange='topic_exchange_testing', routing_key=key, body=message) + + key = "random.logs" + for message in messages: + channel.basic_publish(exchange='topic_exchange_testing', routing_key=key, body=message) + + connection.close() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * num_tables + messages_num * num_tables: + break + + assert int(result) == messages_num * num_tables + messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) + + +@pytest.mark.timeout(320) +def test_rabbitmq_hash_exchange(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + num_tables = 4 + for consumer_id in range(num_tables): + table_name = 'rabbitmq_consumer{}'.format(consumer_id) + print("Setting up {}".format(table_name)) + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + DROP TABLE IF EXISTS test.{0}_mv; + CREATE TABLE test.{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 10, + rabbitmq_exchange_type = 'consistent_hash', + rabbitmq_exchange_name = 'hash_exchange_testing', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.{0}; + '''.format(table_name)) + + i = [0] + messages_num = 500 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + + def produce(): + # init connection here because otherwise python rabbitmq client might fail + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='hash_exchange_testing', exchange_type='x-consistent-hash') + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + key = str(randrange(10)) + for message in messages: + channel.basic_publish(exchange='hash_exchange_testing', routing_key=key, body=message) + connection.close() + + threads = [] + threads_num = 10 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * threads_num: + break + + for consumer_id in range(num_tables): + table_name = 'rabbitmq_consumer{}'.format(consumer_id) + instance.query(''' + DROP TABLE IF EXISTS test.{0}; + DROP TABLE IF EXISTS test.{0}_mv; + '''.format(table_name)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) + + if __name__ == '__main__': cluster.start() raw_input("Cluster created, press any key to destroy...") From 22707508c1ccb85d3dd9e4fd9bbee39b73ade962 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 12:17:33 +0300 Subject: [PATCH 0279/1102] experiment --- tests/queries/0_stateless/00816_long_concurrent_alter_column.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh index 965408065cfe..d3a26b0ed755 100755 --- a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh +++ b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh @@ -59,6 +59,7 @@ wait echo "DROP TABLE concurrent_alter_column" | ${CLICKHOUSE_CLIENT} +sleep 1 # Check for deadlocks echo "SELECT * FROM system.processes WHERE query_id LIKE 'alter%'" | ${CLICKHOUSE_CLIENT} From 706c5452482d633b69b8fa54c0eb6b3ccd248d9e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 12:18:12 +0300 Subject: [PATCH 0280/1102] experiment --- tests/queries/0_stateless/00816_long_concurrent_alter_column.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh index d3a26b0ed755..3ed0c6e1a6a2 100755 --- a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh +++ b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh @@ -59,7 +59,7 @@ wait echo "DROP TABLE concurrent_alter_column" | ${CLICKHOUSE_CLIENT} -sleep 1 +sleep 7 # Check for deadlocks echo "SELECT * FROM system.processes WHERE query_id LIKE 'alter%'" | ${CLICKHOUSE_CLIENT} From 3e5d735871c9a995c5450b05d030d3046f2d3051 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 12:21:23 +0300 Subject: [PATCH 0281/1102] back to upstream --- .gitmodules | 2 +- contrib/sentry-native | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 93a0078a0514..2fed57a519d0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -170,4 +170,4 @@ url = https://github.com/fmtlib/fmt.git [submodule "contrib/sentry-native"] path = contrib/sentry-native - url = https://github.com/blinkov/sentry-native.git + url = https://github.com/getsentry/sentry-native.git diff --git a/contrib/sentry-native b/contrib/sentry-native index 78fb54989cd6..f91ed3f95b56 160000 --- a/contrib/sentry-native +++ b/contrib/sentry-native @@ -1 +1 @@ -Subproject commit 78fb54989cd61cf11dcea142e12d1ecc6940c962 +Subproject commit f91ed3f95b5653f247189d720ab00765b4899d6f From 9350472ee456bd0561e263df8b9c4f13bef3aaf6 Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 11 Jun 2020 09:23:23 +0000 Subject: [PATCH 0282/1102] Support multiple bindings --- .../RabbitMQ/RabbitMQBlockInputStream.cpp | 6 +- src/Storages/RabbitMQ/RabbitMQSettings.h | 5 +- .../ReadBufferFromRabbitMQConsumer.cpp | 77 +++++++++----- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 6 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 33 +++--- src/Storages/RabbitMQ/StorageRabbitMQ.h | 7 +- .../integration/test_storage_rabbitmq/test.py | 100 +++++++++++++++--- 7 files changed, 162 insertions(+), 72 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index 245320008f37..2d995d97f18e 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -20,7 +20,7 @@ RabbitMQBlockInputStream::RabbitMQBlockInputStream( , column_names(columns) , log(log_) , non_virtual_header(storage.getSampleBlockNonMaterialized()) - , virtual_header(storage.getSampleBlockForColumns({"_exchange", "_routingKey"})) + , virtual_header(storage.getSampleBlockForColumns({"_exchange"})) { } @@ -122,13 +122,11 @@ Block RabbitMQBlockInputStream::readImpl() auto new_rows = read_rabbitmq_message(); - auto exchange_name = storage.getExchangeName(); - auto routing_key = storage.getRoutingKey(); + auto exchange_name = buffer->getExchange(); for (size_t i = 0; i < new_rows; ++i) { virtual_columns[0]->insert(exchange_name); - virtual_columns[1]->insert(routing_key); } total_rows = total_rows + new_rows; diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index a3f133cfed04..d81a887747be 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -11,13 +11,12 @@ namespace DB #define LIST_OF_RABBITMQ_SETTINGS(M) \ M(SettingString, rabbitmq_host_port, "", "A host-port to connect to RabbitMQ server.", 0) \ - M(SettingString, rabbitmq_routing_key, "5672", "A routing key to connect producer->exchange->queue<->consumer.", 0) \ - M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exchange name, to which messages are sent. Needed to bind queues to it.", 0) \ + M(SettingString, rabbitmq_routing_key_list, "5672", "A string of routing keys, separated by dots.", 0) \ + M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exchange name, to which messages are sent.", 0) \ M(SettingString, rabbitmq_format, "", "The message format.", 0) \ M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ - M(SettingUInt64, rabbitmq_hash_exchange, 0, "A flag which indicates whether consistent-hash-exchange should be used.", 0) \ M(SettingString, rabbitmq_exchange_type, "default", "The exchange type.", 0) \ DECLARE_SETTINGS_COLLECTION(LIST_OF_RABBITMQ_SETTINGS) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 1321a4fb3b62..967da1a75adc 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -18,7 +18,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, const String & exchange_name_, - const String & routing_key_, + const Names & routing_keys_, const size_t channel_id_, Poco::Logger * log_, char row_delimiter_, @@ -31,7 +31,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , consumer_channel(std::move(consumer_channel_)) , eventHandler(eventHandler_) , exchange_name(exchange_name_) - , routing_key(routing_key_) + , routing_keys(routing_keys_) , channel_id(channel_id_) , log(log_) , row_delimiter(row_delimiter_) @@ -44,7 +44,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( messages.clear(); current = messages.begin(); - exchange_type_set = exchange_type != "default" ? 1 : 0; + exchange_type_set = exchange_type != "default" ? true : false; /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. * By default there is one queue per consumer. @@ -69,8 +69,8 @@ ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() void ReadBufferFromRabbitMQConsumer::initExchange() { - /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest - * and it is also used for INSERT query. + /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest (also due to different + * binding algorithm this default behaviuor is much faster). It is also used in INSERT query. */ String producer_exchange = exchange_type_set ? exchange_name + "_default" : exchange_name; consumer_channel->declareExchange(producer_exchange, AMQP::fanout).onError([&](const char * message) @@ -86,7 +86,8 @@ void ReadBufferFromRabbitMQConsumer::initExchange() LOG_ERROR(log, "Failed to declare exchange: {}", message); }); - consumer_channel->bindExchange(producer_exchange, internal_exchange_name, routing_key).onError([&](const char * message) + /// With fanout exchange the binding key is ignored - a parameter might be arbitrary + consumer_channel->bindExchange(producer_exchange, internal_exchange_name, routing_keys[0]).onError([&](const char * message) { internal_exchange_declared = false; LOG_ERROR(log, "Failed to bind exchange: {}", message); @@ -95,7 +96,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() if (!exchange_type_set) return; - /// For special purposes to use the flexibility of routing provided by rabbitmq - choosing exchange types is also supported. + /// For special purposes to use the flexibility of routing provided by rabbitmq - choosing exchange types is supported. AMQP::ExchangeType type; if (exchange_type == "fanout") type = AMQP::ExchangeType::fanout; @@ -131,11 +132,14 @@ void ReadBufferFromRabbitMQConsumer::initExchange() LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); }); - consumer_channel->bindExchange(exchange_name, local_exchange_name, routing_key).onError([&](const char * message) + for (auto & routing_key : routing_keys) { - local_exchange_declared = false; - LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); - }); + consumer_channel->bindExchange(exchange_name, local_exchange_name, routing_key).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); + }); + } } @@ -158,7 +162,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) queues.emplace_back(queue_name_); subscribed_queue[queue_name_] = false; - String binding_key = routing_key; + String binding_key = routing_keys[0]; /* Every consumer has at least one unique queue. Bind the queues to exchange based on the consumer_channel_id * in case there is one queue per consumer and bind by queue_id in case there is more than 1 queue per consumer. @@ -176,11 +180,6 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } } - /// Must be done here and not in readPrefix() because library might fail to handle async subscription on the same connection - subscribe(queues.back()); - - LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); - consumer_channel->bindQueue(internal_exchange_name, queue_name_, binding_key) .onSuccess([&] { @@ -189,21 +188,47 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { internal_bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding: {}", message); + LOG_ERROR(log, "Failed to bind to key {}, the reason is: {}", binding_key, message); }); + /// Must be done here and not in readPrefix() because library might fail to handle async subscription on the same connection + subscribe(queues.back()); + + LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); + if (exchange_type_set) { - consumer_channel->bindQueue(local_exchange_name, queue_name_, binding_key) - .onSuccess([&] + /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary + if (hash_exchange) { - local_bindings_created = true; - }) - .onError([&](const char * message) + consumer_channel->bindQueue(local_exchange_name, queue_name_, binding_key) + .onSuccess([&] + { + local_bindings_created = true; + }) + .onError([&](const char * message) + { + local_bindings_error = true; + LOG_ERROR(log, "Failed to create queue binding: {}", message); + }); + } + else { - local_bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding: {}", message); - }); + /// means there is only one queue with one consumer - no even distribution needed - no hash-exchange + for (auto & routing_key : routing_keys) + { + consumer_channel->bindQueue(local_exchange_name, queue_name_, routing_key) + .onSuccess([&] + { + local_bindings_created = true; + }) + .onError([&](const char * message) + { + local_bindings_error = true; + LOG_ERROR(log, "Failed to create queue binding: {}", message); + }); + } + } } }) .onError([&](const char * message) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 51eae60cdeb5..3d02eeab7611 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -25,7 +25,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, const String & exchange_name_, - const String & routing_key_, + const Names & routing_keys_, const size_t channel_id_, Poco::Logger * log_, char row_delimiter_, @@ -40,6 +40,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer void allowNext() { allowed = true; } // Allow to read next message. void checkSubscription(); + auto getExchange() const { return exchange_name; } + private: using Messages = std::vector; @@ -47,7 +49,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer RabbitMQHandler & eventHandler; const String & exchange_name; - const String & routing_key; + const Names & routing_keys; const size_t channel_id; const bool bind_by_id; const size_t num_queues; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 895b9ca2bec7..e17d541b661e 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -55,7 +55,7 @@ StorageRabbitMQ::StorageRabbitMQ( Context & context_, const ColumnsDescription & columns_, const String & host_port_, - const String & routing_key_, + const Names & routing_keys_, const String & exchange_name_, const String & format_name_, char row_delimiter_, @@ -65,7 +65,7 @@ StorageRabbitMQ::StorageRabbitMQ( : IStorage(table_id_) , global_context(context_.getGlobalContext()) , rabbitmq_context(Context(global_context)) - , routing_key(global_context.getMacros()->expand(routing_key_)) + , routing_keys(global_context.getMacros()->expand(routing_keys_)) , exchange_name(exchange_name_) , format_name(global_context.getMacros()->expand(format_name_)) , row_delimiter(row_delimiter_) @@ -215,7 +215,7 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() auto table_id = getStorageID(); String table_name = table_id.getNameForLogs(); - return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_key, + return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_keys, next_channel_id, log, row_delimiter, bind_by_id, num_queues, exchange_type, table_name, stream_cancelled); } @@ -224,7 +224,7 @@ ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { String producer_exchange = exchange_type == "default" ? exchange_name : exchange_name + "_default"; - return std::make_shared(parsed_address, login_password, routing_key, producer_exchange, + return std::make_shared(parsed_address, login_password, routing_keys[0], producer_exchange, log, num_consumers * num_queues, bind_by_id, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } @@ -369,18 +369,18 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } - String routing_key = rabbitmq_settings.rabbitmq_routing_key.value; + String routing_key_list = rabbitmq_settings.rabbitmq_routing_key_list.value; if (args_count >= 2) { - const auto * ast = engine_args[1]->as(); - if (ast && ast->value.getType() == Field::Types::String) - { - routing_key = safeGet(ast->value); - } - else - { - throw Exception(String("RabbitMQ routing key must be a string"), ErrorCodes::BAD_ARGUMENTS); - } + engine_args[1] = evaluateConstantExpressionAsLiteral(engine_args[1], args.local_context); + routing_key_list = engine_args[1]->as().value.safeGet(); + } + + Names routing_keys; + boost::split(routing_keys, routing_key_list, [](char c){ return c == ','; }); + for (String & key : routing_keys) + { + boost::trim(key); } String exchange = rabbitmq_settings.rabbitmq_exchange_name.value; @@ -483,7 +483,7 @@ void registerStorageRabbitMQ(StorageFactory & factory) return StorageRabbitMQ::create( args.table_id, args.context, args.columns, - host_port, routing_key, exchange, format, row_delimiter, exchange_type, num_consumers, num_queues); + host_port, routing_keys, exchange, format, row_delimiter, exchange_type, num_consumers, num_queues); }; factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); @@ -494,8 +494,7 @@ void registerStorageRabbitMQ(StorageFactory & factory) NamesAndTypesList StorageRabbitMQ::getVirtuals() const { return NamesAndTypesList{ - {"_exchange", std::make_shared()}, - {"_routingKey", std::make_shared()} + {"_exchange", std::make_shared()} }; } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 27a9b8834f49..45ced9d247bc 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -48,9 +48,6 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub ProducerBufferPtr createWriteBuffer(); - const String & getExchangeName() const { return exchange_name; } - const String & getRoutingKey() const { return routing_key; } - const String & getFormatName() const { return format_name; } NamesAndTypesList getVirtuals() const override; @@ -62,7 +59,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Context & context_, const ColumnsDescription & columns_, const String & host_port_, - const String & routing_key_, + const Names & routing_keys_, const String & exchange_name_, const String & format_name_, char row_delimiter_, @@ -74,7 +71,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Context global_context; Context rabbitmq_context; - String routing_key; + Names routing_keys; const String exchange_name; const String format_name; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index d9c08ef7b6bc..46b622bde8a0 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -120,7 +120,7 @@ def test_rabbitmq_select_from_new_syntax_table(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'new', + rabbitmq_routing_key_list = 'new', rabbitmq_exchange_name = 'clickhouse-exchange', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; @@ -193,7 +193,7 @@ def test_rabbitmq_select_empty(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'empty', + rabbitmq_routing_key_list = 'empty', rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; ''') @@ -207,7 +207,7 @@ def test_rabbitmq_json_without_delimiter(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'json', + rabbitmq_routing_key_list = 'json', rabbitmq_exchange_name = 'clickhouse-exchange', rabbitmq_format = 'JSONEachRow' ''') @@ -249,7 +249,7 @@ def test_rabbitmq_csv_with_delimiter(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'csv', + rabbitmq_routing_key_list = 'csv', rabbitmq_exchange_name = 'clickhouse-exchange', rabbitmq_format = 'CSV', rabbitmq_row_delimiter = '\\n'; @@ -285,7 +285,7 @@ def test_rabbitmq_tsv_with_delimiter(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'tsv', + rabbitmq_routing_key_list = 'tsv', rabbitmq_exchange_name = 'clickhouse-exchange', rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; @@ -322,7 +322,7 @@ def test_rabbitmq_materialized_view(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'mv', + rabbitmq_routing_key_list = 'mv', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE TABLE test.view (key UInt64, value UInt64) @@ -365,7 +365,7 @@ def test_rabbitmq_materialized_view_with_subquery(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'mvsq', + rabbitmq_routing_key_list = 'mvsq', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE TABLE test.view (key UInt64, value UInt64) @@ -410,7 +410,7 @@ def test_rabbitmq_many_materialized_views(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'mmv', + rabbitmq_routing_key_list = 'mmv', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE TABLE test.view1 (key UInt64, value UInt64) @@ -471,7 +471,7 @@ def test_rabbitmq_big_message(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value String) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'big', + rabbitmq_routing_key_list = 'big', rabbitmq_format = 'JSONEachRow'; CREATE TABLE test.view (key UInt64, value String) ENGINE = MergeTree @@ -774,7 +774,7 @@ def test_rabbitmq_insert(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'insert1', + rabbitmq_routing_key_list = 'insert1', rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; ''') @@ -829,7 +829,7 @@ def test_rabbitmq_many_inserts(rabbitmq_cluster): CREATE TABLE test.rabbitmq_many (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', - rabbitmq_routing_key = 'insert2', + rabbitmq_routing_key_list = 'insert2', rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; CREATE TABLE test.view_many (key UInt64, value UInt64) @@ -1024,7 +1024,7 @@ def test_rabbitmq_direct_exchange(rabbitmq_cluster): rabbitmq_num_consumers = 5, rabbitmq_exchange_name = 'direct_exchange_testing', rabbitmq_exchange_type = 'direct', - rabbitmq_routing_key = 'direct_{0}', + rabbitmq_routing_key_list = 'direct_{0}', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE MATERIALIZED VIEW test.direct_exchange_{0}_mv TO test.destination AS @@ -1083,7 +1083,7 @@ def test_rabbitmq_fanout_exchange(rabbitmq_cluster): ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_consumers = 5, - rabbitmq_routing_key = 'key_{0}', + rabbitmq_routing_key_list = 'key_{0}', rabbitmq_exchange_name = 'fanout_exchange_testing', rabbitmq_exchange_type = 'fanout', rabbitmq_format = 'JSONEachRow', @@ -1143,7 +1143,7 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): rabbitmq_num_consumers = 5, rabbitmq_exchange_name = 'topic_exchange_testing', rabbitmq_exchange_type = 'topic', - rabbitmq_routing_key = '*.{0}', + rabbitmq_routing_key_list = '*.{0}', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE MATERIALIZED VIEW test.topic_exchange_{0}_mv TO test.destination AS @@ -1161,7 +1161,7 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): rabbitmq_num_consumers = 4, rabbitmq_exchange_name = 'topic_exchange_testing', rabbitmq_exchange_type = 'topic', - rabbitmq_routing_key = '*.logs', + rabbitmq_routing_key_list = '*.logs', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; CREATE MATERIALIZED VIEW test.topic_exchange_{0}_mv TO test.destination AS @@ -1285,6 +1285,76 @@ def produce(): assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) +@pytest.mark.timeout(420) +def test_rabbitmq_multiple_bindings(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.bindings; + DROP TABLE IF EXISTS test.bindings_mv; + CREATE TABLE test.bindings (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 5, + rabbitmq_num_queues = 2, + rabbitmq_exchange_name = 'multiple_bindings_testing', + rabbitmq_exchange_type = 'direct', + rabbitmq_routing_key_list = 'key1,key2,key3,key4,key5', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE TABLE test.view_bindings (key UInt64, value UInt64) + ENGINE = MergeTree + ORDER BY key; + CREATE MATERIALIZED VIEW test.bindings_mv TO test.view_bindings AS + SELECT * FROM test.bindings; + ''') + + + i = [0] + messages_num = 500 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + + def produce(): + # init connection here because otherwise python rabbitmq client might fail + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='hash_exchange_testing', exchange_type='x-consistent-hash') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + + keys = ['key1', 'key2', 'key3', 'key4', 'key5'] + + for key in keys: + for message in messages: + channel.basic_publish(exchange='multiple_bindings_testing', routing_key=key, body=message) + + connection.close() + + threads = [] + threads_num = 10 + + for _ in range(threads_num): + threads.append(threading.Thread(target=produce)) + for thread in threads: + time.sleep(random.uniform(0, 1)) + thread.start() + + while True: + result = instance.query('SELECT count() FROM test.view_bindings') + time.sleep(1) + print result + if int(result) == messages_num * threads_num * 5: + break + + for thread in threads: + thread.join() + + assert int(result) == messages_num * threads_num * 5, 'ClickHouse lost some messages: {}'.format(result) + + if __name__ == '__main__': cluster.start() raw_input("Cluster created, press any key to destroy...") From 626eb53baae96de1bfbd2f736ad444ecee34827f Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 11 Jun 2020 10:56:40 +0000 Subject: [PATCH 0283/1102] Fix multiple bindings for single queue & rm hardcoded strings --- .../ReadBufferFromRabbitMQConsumer.cpp | 48 ++++++++++++------- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 2 +- .../integration/test_storage_rabbitmq/test.py | 46 ++++++++++++------ 3 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 967da1a75adc..5d2e3073d41c 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -14,6 +14,17 @@ namespace DB { +namespace Exchange +{ + /// Note that default here means default by implementation and not by rabbitmq settings + static const String DEFAULT = "default"; + static const String FANOUT = "fanout"; + static const String DIRECT = "direct"; + static const String TOPIC = "topic"; + static const String HASH = "consistent_hash"; +} + + ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( ChannelPtr consumer_channel_, RabbitMQHandler & eventHandler_, @@ -44,7 +55,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( messages.clear(); current = messages.begin(); - exchange_type_set = exchange_type != "default" ? true : false; + exchange_type_set = exchange_type != Exchange::DEFAULT ? true : false; /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. * By default there is one queue per consumer. @@ -72,14 +83,14 @@ void ReadBufferFromRabbitMQConsumer::initExchange() /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest (also due to different * binding algorithm this default behaviuor is much faster). It is also used in INSERT query. */ - String producer_exchange = exchange_type_set ? exchange_name + "_default" : exchange_name; + String producer_exchange = exchange_type_set ? exchange_name + "_" + Exchange::DEFAULT : exchange_name; consumer_channel->declareExchange(producer_exchange, AMQP::fanout).onError([&](const char * message) { internal_exchange_declared = false; LOG_ERROR(log, "Failed to declare exchange: {}", message); }); - internal_exchange_name = producer_exchange + "_direct"; + internal_exchange_name = producer_exchange + "_" + Exchange::DIRECT; consumer_channel->declareExchange(internal_exchange_name, AMQP::direct).onError([&](const char * message) { internal_exchange_declared = false; @@ -99,11 +110,11 @@ void ReadBufferFromRabbitMQConsumer::initExchange() /// For special purposes to use the flexibility of routing provided by rabbitmq - choosing exchange types is supported. AMQP::ExchangeType type; - if (exchange_type == "fanout") type = AMQP::ExchangeType::fanout; - else if (exchange_type == "direct") type = AMQP::ExchangeType::direct; - else if (exchange_type == "topic") type = AMQP::ExchangeType::topic; - else if (exchange_type == "consistent_hash") type = AMQP::ExchangeType::consistent_hash; - else return; + if (exchange_type == Exchange::FANOUT) type = AMQP::ExchangeType::fanout; + else if (exchange_type == Exchange::DIRECT) type = AMQP::ExchangeType::direct; + else if (exchange_type == Exchange::TOPIC) type = AMQP::ExchangeType::topic; + else if (exchange_type == Exchange::HASH) type = AMQP::ExchangeType::consistent_hash; + else return; /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages * between all consumers. (This enables better scaling as without hash-echange - the only oprion to avoid getting the same @@ -115,12 +126,12 @@ void ReadBufferFromRabbitMQConsumer::initExchange() LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); }); - hash_exchange = true; - /// No need for declaring hash-exchange if there is only one consumer with one queue and exchange type is not hash - if (!bind_by_id && exchange_type != "consistent_hash") + if (!bind_by_id && exchange_type != Exchange::HASH) return; + hash_exchange = true; + AMQP::Table exchange_arguments; exchange_arguments["hash-property"] = "message_id"; @@ -153,6 +164,10 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) internal_exchange_declared = true; } + /* Internal exchange is a default exchange (by implementstion, not by rabbitmq settings) and is used for INSERT query + * and if exchange_type is not set - there is no local exchange. If it is set - then local exchange is a distributor + * exchange, which is bound to the exchange specified by the client. + */ bool internal_bindings_created = false, internal_bindings_error = false; bool local_bindings_created = false, local_bindings_error = false; @@ -188,7 +203,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { internal_bindings_error = true; - LOG_ERROR(log, "Failed to bind to key {}, the reason is: {}", binding_key, message); + LOG_ERROR(log, "Failed to bind to key {}. Reason: {}", binding_key, message); }); /// Must be done here and not in readPrefix() because library might fail to handle async subscription on the same connection @@ -209,15 +224,16 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { local_bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding: {}", message); + LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", binding_key, message); }); } else { - /// means there is only one queue with one consumer - no even distribution needed - no hash-exchange + /// Means there is only one queue with one consumer - no even distribution needed - no hash-exchange for (auto & routing_key : routing_keys) { - consumer_channel->bindQueue(local_exchange_name, queue_name_, routing_key) + /// Binding directly to exchange, specified by the client + consumer_channel->bindQueue(exchange_name, queue_name_, routing_key) .onSuccess([&] { local_bindings_created = true; @@ -225,7 +241,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { local_bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding: {}", message); + LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", routing_key, message); }); } } diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index e17d541b661e..212d1fbc783f 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -213,7 +213,7 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ChannelPtr consumer_channel = std::make_shared(&connection); auto table_id = getStorageID(); - String table_name = table_id.getNameForLogs(); + String table_name = table_id.getNameForLogs(); return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_keys, next_channel_id, log, row_delimiter, bind_by_id, num_queues, exchange_type, table_name, stream_cancelled); diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 46b622bde8a0..1a56395eb29a 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -927,7 +927,6 @@ def insert(): while True: result = instance.query('SELECT count() FROM test.view_sharding') time.sleep(1) - print result if int(result) == messages_num * threads_num: break @@ -1288,9 +1287,17 @@ def produce(): @pytest.mark.timeout(420) def test_rabbitmq_multiple_bindings(rabbitmq_cluster): instance.query(''' - DROP TABLE IF EXISTS test.bindings; - DROP TABLE IF EXISTS test.bindings_mv; - CREATE TABLE test.bindings (key UInt64, value UInt64) + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + instance.query(''' + DROP TABLE IF EXISTS test.bindings_1; + DROP TABLE IF EXISTS test.bindings_1_mv; + CREATE TABLE test.bindings_1 (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_consumers = 5, @@ -1300,13 +1307,25 @@ def test_rabbitmq_multiple_bindings(rabbitmq_cluster): rabbitmq_routing_key_list = 'key1,key2,key3,key4,key5', rabbitmq_format = 'JSONEachRow', rabbitmq_row_delimiter = '\\n'; - CREATE TABLE test.view_bindings (key UInt64, value UInt64) - ENGINE = MergeTree - ORDER BY key; - CREATE MATERIALIZED VIEW test.bindings_mv TO test.view_bindings AS - SELECT * FROM test.bindings; + CREATE MATERIALIZED VIEW test.bindings_1_mv TO test.destination AS + SELECT * FROM test.bindings_1; ''') + # in case num_consumers and num_queues are not set - multiple bindings are implemented differently, so test them too + instance.query(''' + DROP TABLE IF EXISTS test.bindings_2; + DROP TABLE IF EXISTS test.bindings_2_mv; + CREATE TABLE test.bindings_2 (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_exchange_name = 'multiple_bindings_testing', + rabbitmq_exchange_type = 'direct', + rabbitmq_routing_key_list = 'key1,key2,key3,key4,key5', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.bindings_2_mv TO test.destination AS + SELECT * FROM test.bindings_2; + ''') i = [0] messages_num = 500 @@ -1318,7 +1337,7 @@ def produce(): # init connection here because otherwise python rabbitmq client might fail connection = pika.BlockingConnection(parameters) channel = connection.channel() - channel.exchange_declare(exchange='hash_exchange_testing', exchange_type='x-consistent-hash') + channel.exchange_declare(exchange='multiple_bindings_testing', exchange_type='direct') messages = [] for _ in range(messages_num): @@ -1343,16 +1362,15 @@ def produce(): thread.start() while True: - result = instance.query('SELECT count() FROM test.view_bindings') + result = instance.query('SELECT count() FROM test.destination') time.sleep(1) - print result - if int(result) == messages_num * threads_num * 5: + if int(result) == messages_num * threads_num * 5 * 2: break for thread in threads: thread.join() - assert int(result) == messages_num * threads_num * 5, 'ClickHouse lost some messages: {}'.format(result) + assert int(result) == messages_num * threads_num * 5 * 2, 'ClickHouse lost some messages: {}'.format(result) if __name__ == '__main__': From 5f73c87c7142e7d137a29f827d8fab8ebdc10ad2 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 15:18:19 +0300 Subject: [PATCH 0284/1102] change used flag --- cmake/find/sentry.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/find/sentry.cmake b/cmake/find/sentry.cmake index 2d3aa71248af..eadf071141ef 100644 --- a/cmake/find/sentry.cmake +++ b/cmake/find/sentry.cmake @@ -5,7 +5,7 @@ if (NOT EXISTS "${SENTRY_INCLUDE_DIR}/sentry.h") return() endif () -if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT UNBUNDLED AND NOT (OS_DARWIN AND COMPILER_CLANG)) +if (NOT OS_FREEBSD AND NOT SPLIT_SHARED_LIBRARIES AND NOT_UNBUNDLED AND NOT (OS_DARWIN AND COMPILER_CLANG)) option (USE_SENTRY "Use Sentry" ON) set (CURL_LIBRARY ${ClickHouse_SOURCE_DIR}/contrib/curl/lib) set (CURL_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/curl/include) From fa47fc3f30eb144aa1593ebbfc630fdb4f095c39 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 15:34:02 +0300 Subject: [PATCH 0285/1102] fix address formatting --- base/daemon/SentryWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 2ce43c9f0a21..45f5bd56ca1e 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -153,7 +153,7 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c const StackTrace::Frame & current_frame = frames[i]; sentry_value_t sentry_frame = sentry_value_new_object(); UInt64 frame_ptr = reinterpret_cast(current_frame.virtual_addr); - std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIu64 "x", frame_ptr); + std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIx64, frame_ptr); sentry_value_set_by_key(sentry_frame, "instruction_addr", sentry_value_new_string(instruction_addr)); if (current_frame.symbol.has_value()) From 47a902a6ce593d9ee55a29fdb0b35bc6f44152a7 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 11 Jun 2020 18:55:44 +0300 Subject: [PATCH 0286/1102] Simple github hook --- utils/github-hook/hook.py | 195 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 utils/github-hook/hook.py diff --git a/utils/github-hook/hook.py b/utils/github-hook/hook.py new file mode 100644 index 000000000000..13d62b311f70 --- /dev/null +++ b/utils/github-hook/hook.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +import json +import requests +import time +import os + +DB = 'gh-data' +RETRIES = 5 + + +def process_issue_event(response): + issue = response['issue'] + return dict( + action=response['action'], + sender=response['sender']['login'], + updated_at=issue['updated_at'], + url=issue['url'], + number=issue['number'], + author=issue['user']['login'], + labels=[label['name'] for label in issue['labels']], + state=issue['state'], + assignees=[assignee['login'] for assignee in issue['assignees']], + created_at=issue['created_at'], + body=issue['body'], + title=issue['title'], + comments=issue['comments'], + raw_json=json.dumps(response),) + + +def process_issue_comment_event(response): + issue = response['issue'] + comment = response['comment'] + + return dict( + action='comment_' + response['action'], + sender=response['sender']['login'], + updated_at=issue['updated_at'], + url=issue['url'], + number=issue['number'], + author=issue['user']['login'], + labels=[label['name'] for label in issue['labels']], + state=issue['state'], + assignees=[assignee['login'] for assignee in issue['assignees']], + created_at=issue['created_at'], + body=issue['body'], + title=issue['title'], + comments=issue['comments'], + comment_body=comment['body'], + comment_author=comment['user']['login'], + comment_url=comment['url'], + comment_created_at=comment['created_at'], + comment_updated_at=comment['updated_at'], + raw_json=json.dumps(response),) + + +def process_pull_request_event(response): + pull_request = response['pull_request'] + result = dict( + updated_at=pull_request['updated_at'], + number=pull_request['number'], + action=response['action'], + sender=response['sender']['login'], + url=pull_request['url'], + author=pull_request['user']['login'], + labels=[label['name'] for label in pull_request['labels']], + state=pull_request['state'], + body=pull_request['body'], + title=pull_request['title'], + created_at=pull_request['created_at'], + assignees=[assignee['login'] for assignee in pull_request['assignees']], + requested_reviewers=[reviewer['login'] for reviewer in pull_request['requested_reviewers']], + head_repo=pull_request['head']['repo']['full_name'], + head_ref=pull_request['head']['ref'], + head_clone_url=pull_request['head']['repo']['clone_url'], + head_ssh_url=pull_request['head']['repo']['ssh_url'], + base_repo=pull_request['base']['repo']['full_name'], + base_ref=pull_request['base']['ref'], + base_clone_url=pull_request['base']['repo']['clone_url'], + base_ssh_url=pull_request['base']['repo']['ssh_url'], + raw_json=json.dumps(response), + ) + + if 'mergeable' in pull_request and pull_request['mergeable'] is not None: + result['mergeable'] = 1 if pull_request['mergeable'] else 0 + + if 'merged_by' in pull_request and pull_request['merged_by'] is not None: + result['merged_by'] = pull_request['merged_by']['login'] + + if 'merged_at' in pull_request and pull_request['merged_at'] is not None: + result['merged_at'] = pull_request['merged_at'] + + if 'closed_at' in pull_request and pull_request['closed_at'] is not None: + result['closed_at'] = pull_request['closed_at'] + + if 'merge_commit_sha' in pull_request and pull_request['merge_commit_sha'] is not None: + result['merge_commit_sha'] = pull_request['merge_commit_sha'] + + if 'draft' in pull_request: + result['draft'] = 1 if pull_request['draft'] else 0 + + for field in ['comments', 'review_comments', 'commits', 'additions', 'deletions', 'changed_files']: + if field in pull_request: + result[field] = pull_request[field] + + return result + + +def process_pull_request_review(response): + result = process_pull_request_event(response) + review = response['review'] + result['action'] = 'review_' + result['action'] + result['review_body'] = review['body'] if review['body'] is not None else '' + result['review_id'] = review['id'] + result['review_author'] = review['user']['login'] + result['review_commit_sha'] = review['commit_id'] + result['review_submitted_at'] = review['submitted_at'] + result['review_state'] = review['state'] + return result + + +def process_pull_request_review_comment(response): + result = process_pull_request_event(response) + comment = response['comment'] + result['action'] = 'review_comment_' + result['action'] + result['review_id'] = comment['pull_request_review_id'] + result['review_comment_path'] = comment['path'] + result['review_commit_sha'] = comment['commit_id'] + result['review_comment_body'] = comment['body'] + result['review_comment_author'] = comment['user']['login'] + result['review_comment_created_at'] = comment['created_at'] + result['review_comment_updated_at'] = comment['updated_at'] + return result + + +def event_processor_dispatcher(headers, body, inserter): + if 'X-Github-Event' in headers: + if headers['X-Github-Event'] == 'issues': + result = process_issue_event(body) + inserter.insert_event_into(DB, 'issues', result) + elif headers['X-Github-Event'] == 'issue_comment': + result = process_issue_comment_event(body) + inserter.insert_event_into(DB, 'issues', result) + elif headers['X-Github-Event'] == 'pull_request': + result = process_pull_request_event(body) + inserter.insert_event_into(DB, 'pull_requests', result) + elif headers['X-Github-Event'] == 'pull_request_review': + result = process_pull_request_review(body) + inserter.insert_event_into(DB, 'pull_requests', result) + elif headers['X-Github-Event'] == 'pull_request_review_comment': + result = process_pull_request_review_comment(body) + inserter.insert_event_into(DB, 'pull_requests', result) + + +class ClickHouseInserter(object): + def __init__(self, url, user, password): + self.url = url + self.auth = { + 'X-ClickHouse-User': user, + 'X-ClickHouse-Key': password + } + + def insert_event_into(self, db, table, event): + params = { + 'database': db, + 'query': 'INSERT INTO {table} FORMAT JSONEachRow'.format(table=table), + 'date_time_input_format': 'best_effort' + } + event_str = json.dumps(event) + for i in range(RETRIES): + try: + response = requests.post(self.url, params=params, data=event_str, headers=self.auth, verify=False) + response.raise_for_status() + break + except Exception as ex: + print("Exception inseting into ClickHouse:", ex) + time.sleep(0.1) + + +def test(event, context): + inserter = ClickHouseInserter( + os.getenv('CLICKHOUSE_URL'), + os.getenv('CLICKHOUSE_USER'), + os.getenv('CLICKHOUSE_PASSWORD')) + + body = json.loads(event['body'], strict=False) + headers = event['headers'] + event_processor_dispatcher(headers, body, inserter) + + return { + 'statusCode': 200, + 'headers': { + 'Content-Type': 'text/plain' + }, + 'isBase64Encoded': False, + } From ebc781a0c84350b82113cd921feee028c7a5f6a7 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Thu, 11 Jun 2020 19:14:22 +0300 Subject: [PATCH 0287/1102] make polymorphic_parts perf test not blazing fast --- ...phic_parts.xml => polymorphic_parts_l.xml} | 22 +++++------- tests/performance/polymorphic_parts_m.xml | 35 +++++++++++++++++++ tests/performance/polymorphic_parts_s.xml | 35 +++++++++++++++++++ 3 files changed, 79 insertions(+), 13 deletions(-) rename tests/performance/{polymorphic_parts.xml => polymorphic_parts_l.xml} (60%) create mode 100644 tests/performance/polymorphic_parts_m.xml create mode 100644 tests/performance/polymorphic_parts_s.xml diff --git a/tests/performance/polymorphic_parts.xml b/tests/performance/polymorphic_parts_l.xml similarity index 60% rename from tests/performance/polymorphic_parts.xml rename to tests/performance/polymorphic_parts_l.xml index a8e305953d0c..75ad857c9a8d 100644 --- a/tests/performance/polymorphic_parts.xml +++ b/tests/performance/polymorphic_parts_l.xml @@ -18,20 +18,16 @@ ENGINE = Buffer(merge, hits, 16, 10, 100, 10000, 1000000, 10000000, 100000000) - INSERT INTO hits_wide(UserID) VALUES (rand()) - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(100) - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(1000) - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(10000) + + + 1000 + 1000 + - INSERT INTO hits_compact(UserID) VALUES (rand()) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(100) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(1000) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(10000) - - INSERT INTO hits_buffer(UserID) VALUES (rand()) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(100) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(1000) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(10000) + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(50000) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(50000) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(50000) DROP TABLE IF EXISTS hits_wide DROP TABLE IF EXISTS hits_compact diff --git a/tests/performance/polymorphic_parts_m.xml b/tests/performance/polymorphic_parts_m.xml new file mode 100644 index 000000000000..fbe0c18d07e3 --- /dev/null +++ b/tests/performance/polymorphic_parts_m.xml @@ -0,0 +1,35 @@ + + + CREATE TABLE hits_wide AS hits_10m_single ENGINE = MergeTree() + PARTITION BY toYYYYMM(EventDate) + ORDER BY (CounterID, EventDate, intHash32(UserID)) + SAMPLE BY intHash32(UserID) + SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0 + + + CREATE TABLE hits_compact AS hits_10m_single ENGINE = MergeTree() + PARTITION BY toYYYYMM(EventDate) + ORDER BY (CounterID, EventDate, intHash32(UserID)) + SAMPLE BY intHash32(UserID) + SETTINGS min_bytes_for_wide_part = '10M' + + + CREATE TABLE hits_buffer AS hits_10m_single + ENGINE = Buffer(merge, hits, 16, 10, 100, 10000, 1000000, 10000000, 100000000) + + + + + 100 + 100 + + + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(5000) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(5000) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(5000) + + DROP TABLE IF EXISTS hits_wide + DROP TABLE IF EXISTS hits_compact + DROP TABLE IF EXISTS hits_buffer + diff --git a/tests/performance/polymorphic_parts_s.xml b/tests/performance/polymorphic_parts_s.xml new file mode 100644 index 000000000000..085295af8421 --- /dev/null +++ b/tests/performance/polymorphic_parts_s.xml @@ -0,0 +1,35 @@ + + + CREATE TABLE hits_wide AS hits_10m_single ENGINE = MergeTree() + PARTITION BY toYYYYMM(EventDate) + ORDER BY (CounterID, EventDate, intHash32(UserID)) + SAMPLE BY intHash32(UserID) + SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0 + + + CREATE TABLE hits_compact AS hits_10m_single ENGINE = MergeTree() + PARTITION BY toYYYYMM(EventDate) + ORDER BY (CounterID, EventDate, intHash32(UserID)) + SAMPLE BY intHash32(UserID) + SETTINGS min_bytes_for_wide_part = '10M' + + + CREATE TABLE hits_buffer AS hits_10m_single + ENGINE = Buffer(merge, hits, 16, 10, 100, 10000, 1000000, 10000000, 100000000) + + + + + 1 + 1 + + + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(50) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(50) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(50) + + DROP TABLE IF EXISTS hits_wide + DROP TABLE IF EXISTS hits_compact + DROP TABLE IF EXISTS hits_buffer + From bbeb768a1952541a895b51ecb70eee5dd4532224 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Thu, 11 Jun 2020 21:12:48 +0300 Subject: [PATCH 0288/1102] use the sentry logger hook --- base/daemon/SentryWriter.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 45f5bd56ca1e..bb176db813cb 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -13,6 +13,7 @@ #if USE_SENTRY # include // Y_IGNORE +# include #endif @@ -39,6 +40,33 @@ void setExtras() sentry_set_extra("version_minor", sentry_value_new_int32(VERSION_MINOR)); sentry_set_extra("version_patch", sentry_value_new_int32(VERSION_PATCH)); } + +void sentry_logger(sentry_level_t level, const char * message, va_list args) +{ + auto * logger = &Poco::Logger::get("SentryWriter"); + size_t size = 1024; + char buffer[size]; + if (vsnprintf(buffer, size, message, args) >= 0) { + switch (level) { + case SENTRY_LEVEL_DEBUG: + logger->debug(buffer); + break; + case SENTRY_LEVEL_INFO: + logger->information(buffer); + break; + case SENTRY_LEVEL_WARNING: + logger->warning(buffer); + break; + case SENTRY_LEVEL_ERROR: + logger->error(buffer); + break; + case SENTRY_LEVEL_FATAL: + logger->fatal(buffer); + break; + } + } +} +} } #endif @@ -65,6 +93,7 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) sentry_options_t * options = sentry_options_new(); sentry_options_set_release(options, VERSION_STRING); + sentry_options_set_logger(options, &sentry_logger); if (debug) { sentry_options_set_debug(options, 1); From 7ba5063b7a6c80ea07ec30b473e329bf11c93879 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Fri, 12 Jun 2020 00:24:56 +0300 Subject: [PATCH 0289/1102] Add concurrent benchmark to performance test After the main test, run queries from `website.xml` in parallel using `clickhouse-benchmark`. This can be useful to test the effects of concurrency on performance. Comparison test can miss some effects because it always runs queries sequentially, and many of them are even single-threaded. --- docker/test/performance-comparison/compare.sh | 17 ++++ docker/test/performance-comparison/perf.py | 96 ++++++++----------- docker/test/performance-comparison/report.py | 31 ++++++ 3 files changed, 88 insertions(+), 56 deletions(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index a2760907cb36..3d49e9e841ac 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -161,6 +161,20 @@ function run_tests wait } +# Run some queries concurrently and report the resulting TPS. This additional +# (relatively) short test helps detect concurrency-related effects, because the +# main performance comparison testing is done query-by-query. +function run_benchmark +{ + rm -rf benchmark ||: + mkdir bencmhark ||: + + # TODO disable this when there is an explicit list of tests to run + "$script_dir/perf.py" --print right/performance/website.xml > benchmark/website-queries.tsv + clickhouse-benchmark --port 9001 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-left.json < benchmark/website-queries.tsv + clickhouse-benchmark --port 9002 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-right.json < benchmark/website-queries.tsv +} + function get_profiles_watchdog { sleep 6000 @@ -716,6 +730,9 @@ case "$stage" in # Ignore the errors to collect the log and build at least some report, anyway time run_tests ||: ;& +"run_benchmark") + time run_benchmark 2> >(tee -a run-errors.tsv 1>&2) ||: + ;& "get_profiles") # Getting profiles inexplicably hangs sometimes, so try to save some logs if # this happens again. Give the servers some time to collect all info, then diff --git a/docker/test/performance-comparison/perf.py b/docker/test/performance-comparison/perf.py index 308d4760b487..74d0300b0745 100755 --- a/docker/test/performance-comparison/perf.py +++ b/docker/test/performance-comparison/perf.py @@ -14,22 +14,14 @@ def tsv_escape(s): return s.replace('\\', '\\\\').replace('\t', '\\t').replace('\n', '\\n').replace('\r','') -stage_start_seconds = time.perf_counter() - -def report_stage_end(stage_name): - global stage_start_seconds - print('{}\t{}'.format(stage_name, time.perf_counter() - stage_start_seconds)) - stage_start_seconds = time.perf_counter() - -report_stage_end('start') - parser = argparse.ArgumentParser(description='Run performance test.') # Explicitly decode files as UTF-8 because sometimes we have Russian characters in queries, and LANG=C is set. parser.add_argument('file', metavar='FILE', type=argparse.FileType('r', encoding='utf-8'), nargs=1, help='test description file') parser.add_argument('--host', nargs='*', default=['localhost'], help="Server hostname(s). Corresponds to '--port' options.") parser.add_argument('--port', nargs='*', default=[9000], help="Server port(s). Corresponds to '--host' options.") -parser.add_argument('--runs', type=int, default=int(os.environ.get('CHPC_RUNS', 13)), help='Number of query runs per server. Defaults to CHPC_RUNS environment variable.') -parser.add_argument('--no-long', type=bool, default=True, help='Skip the tests tagged as long.') +parser.add_argument('--runs', type=int, default=int(os.environ.get('CHPC_RUNS', 17)), help='Number of query runs per server. Defaults to CHPC_RUNS environment variable.') +parser.add_argument('--long', action='store_true', help='Do not skip the tests tagged as long.') +parser.add_argument('--print', action='store_true', help='Print test queries and exit.') args = parser.parse_args() test_name = os.path.splitext(os.path.basename(args.file[0].name))[0] @@ -37,18 +29,49 @@ def report_stage_end(stage_name): tree = et.parse(args.file[0]) root = tree.getroot() +# Process query parameters +subst_elems = root.findall('substitutions/substitution') +available_parameters = {} # { 'table': ['hits_10m', 'hits_100m'], ... } +for e in subst_elems: + available_parameters[e.find('name').text] = [v.text for v in e.findall('values/value')] + +# Take care to keep the order of queries -- sometimes we have DROP IF EXISTS +# followed by CREATE in create queries section, so the order matters. +def substitute_parameters(query_templates): + result = [] + for q in query_templates: + keys = set(n for _, n, _, _ in string.Formatter().parse(q) if n) + values = [available_parameters[k] for k in keys] + result.extend([ + q.format(**dict(zip(keys, values_combo))) + for values_combo in itertools.product(*values)]) + return result + +# Build a list of test queries, processing all substitutions +test_query_templates = [q.text for q in root.findall('query')] +test_queries = substitute_parameters(test_query_templates) + +# If we're only asked to print the queries, do that and exit +if args.print: + for q in test_queries: + print(q) + exit(0) + # Skip long tests -for tag in root.findall('.//tag'): - if tag.text == 'long': - print('skipped\tTest is tagged as long.') - sys.exit(0) +if not args.long: + for tag in root.findall('.//tag'): + if tag.text == 'long': + print('skipped\tTest is tagged as long.') + sys.exit(0) -# Check main metric +# Check main metric to detect infinite tests. We shouldn't have such tests anymore, +# but we did in the past, and it is convenient to be able to process old tests. main_metric_element = root.find('main_metric/*') if main_metric_element is not None and main_metric_element.tag != 'min_time': raise Exception('Only the min_time main metric is supported. This test uses \'{}\''.format(main_metric_element.tag)) -# FIXME another way to detect infinite tests. They should have an appropriate main_metric but sometimes they don't. +# Another way to detect infinite tests. They should have an appropriate main_metric +# but sometimes they don't. infinite_sign = root.find('.//average_speed_not_changing_for_ms') if infinite_sign is not None: raise Exception('Looks like the test is infinite (sign 1)') @@ -64,28 +87,6 @@ def report_stage_end(stage_name): for s in servers: print('server\t{}\t{}'.format(s['host'], s['port'])) -report_stage_end('connect') - -# Process query parameters -subst_elems = root.findall('substitutions/substitution') -available_parameters = {} # { 'table': ['hits_10m', 'hits_100m'], ... } -for e in subst_elems: - available_parameters[e.find('name').text] = [v.text for v in e.findall('values/value')] - -# Take care to keep the order of queries -- sometimes we have DROP IF EXISTS -# followed by CREATE in create queries section, so the order matters. -def substitute_parameters(query_templates): - result = [] - for q in query_templates: - keys = set(n for _, n, _, _ in string.Formatter().parse(q) if n) - values = [available_parameters[k] for k in keys] - result.extend([ - q.format(**dict(zip(keys, values_combo))) - for values_combo in itertools.product(*values)]) - return result - -report_stage_end('substitute') - # Run drop queries, ignoring errors. Do this before all other activity, because # clickhouse_driver disconnects on error (this is not configurable), and the new # connection loses the changes in settings. @@ -98,8 +99,6 @@ def substitute_parameters(query_templates): except: pass -report_stage_end('drop1') - # Apply settings. # If there are errors, report them and continue -- maybe a new test uses a setting # that is not in master, but the queries can still run. If we have multiple @@ -115,8 +114,6 @@ def substitute_parameters(query_templates): except: print(traceback.format_exc(), file=sys.stderr) -report_stage_end('settings') - # Check tables that should exist. If they don't exist, just skip this test. tables = [e.text for e in root.findall('preconditions/table_exists')] for t in tables: @@ -129,8 +126,6 @@ def substitute_parameters(query_templates): print(f'skipped\t{tsv_escape(skipped_message)}') sys.exit(0) -report_stage_end('preconditions') - # Run create queries create_query_templates = [q.text for q in root.findall('create_query')] create_queries = substitute_parameters(create_query_templates) @@ -145,14 +140,7 @@ def substitute_parameters(query_templates): for q in fill_queries: c.execute(q) -report_stage_end('fill') - # Run test queries -test_query_templates = [q.text for q in root.findall('query')] -test_queries = substitute_parameters(test_query_templates) - -report_stage_end('substitute2') - for query_index, q in enumerate(test_queries): query_prefix = f'{test_name}.query{query_index}' @@ -199,13 +187,9 @@ def substitute_parameters(query_templates): client_seconds = time.perf_counter() - start_seconds print(f'client-time\t{query_index}\t{client_seconds}\t{server_seconds}') -report_stage_end('benchmark') - # Run drop queries drop_query_templates = [q.text for q in root.findall('drop_query')] drop_queries = substitute_parameters(drop_query_templates) for c in connections: for q in drop_queries: c.execute(q) - -report_stage_end('drop2') diff --git a/docker/test/performance-comparison/report.py b/docker/test/performance-comparison/report.py index 9db37932aea1..d7e30190aefe 100755 --- a/docker/test/performance-comparison/report.py +++ b/docker/test/performance-comparison/report.py @@ -5,6 +5,7 @@ import collections import csv import itertools +import json import os import sys import traceback @@ -321,6 +322,36 @@ def print_test_times(): print_test_times() + def print_benchmark_results(): + left_json = json.load(open('benchmark/website-left.json')); + right_json = json.load(open('benchmark/website-right.json')); + left_qps = left_json["statistics"]["QPS"] + right_qps = right_json["statistics"]["QPS"] + relative_diff = (right_qps - left_qps) / left_qps; + times_diff = max(right_qps, left_qps) / max(0.01, min(right_qps, left_qps)) + print(tableStart('Concurrent benchmarks')) + print(tableHeader(['Benchmark', 'Old, queries/s', 'New, queries/s', 'Relative difference', 'Times difference'])) + row = ['website', f'{left_qps:.3f}', f'{right_qps:.3f}', f'{relative_diff:.3f}', f'x{times_diff:.3f}'] + attrs = ['' for r in row] + if abs(relative_diff) > 0.1: + # More queries per second is better. + if relative_diff > 0.: + attrs[3] = f'style="background: {color_good}"' + else: + attrs[3] = f'style="background: {color_bad}"' + else: + attrs[3] = '' + print(tableRow(row, attrs)) + print(tableEnd()) + + try: + print_benchmark_results() + except: + report_errors.append( + traceback.format_exception_only( + *sys.exc_info()[:2])[-1]) + pass + print_report_errors() print(""" From 7fb8a985281f9c663817d32d7939f61f3e258217 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Fri, 12 Jun 2020 00:33:30 +0300 Subject: [PATCH 0290/1102] increase number of rows --- tests/performance/polymorphic_parts_l.xml | 8 ++++---- tests/performance/polymorphic_parts_m.xml | 8 ++++---- tests/performance/polymorphic_parts_s.xml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/performance/polymorphic_parts_l.xml b/tests/performance/polymorphic_parts_l.xml index 75ad857c9a8d..acda0de281a4 100644 --- a/tests/performance/polymorphic_parts_l.xml +++ b/tests/performance/polymorphic_parts_l.xml @@ -24,10 +24,10 @@ 1000 - - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(50000) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(50000) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(50000) + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(100000) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(100000) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(100000) DROP TABLE IF EXISTS hits_wide DROP TABLE IF EXISTS hits_compact diff --git a/tests/performance/polymorphic_parts_m.xml b/tests/performance/polymorphic_parts_m.xml index fbe0c18d07e3..a9842496de07 100644 --- a/tests/performance/polymorphic_parts_m.xml +++ b/tests/performance/polymorphic_parts_m.xml @@ -24,10 +24,10 @@ 100 - - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(5000) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(5000) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(5000) + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(10000) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(10000) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(10000) DROP TABLE IF EXISTS hits_wide DROP TABLE IF EXISTS hits_compact diff --git a/tests/performance/polymorphic_parts_s.xml b/tests/performance/polymorphic_parts_s.xml index 085295af8421..3b9eea91b1d6 100644 --- a/tests/performance/polymorphic_parts_s.xml +++ b/tests/performance/polymorphic_parts_s.xml @@ -24,10 +24,10 @@ 1 - - INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(50) - INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(50) - INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(50) + + INSERT INTO hits_wide(UserID) SELECT rand() FROM numbers(100) + INSERT INTO hits_compact(UserID) SELECT rand() FROM numbers(100) + INSERT INTO hits_buffer(UserID) SELECT rand() FROM numbers(100) DROP TABLE IF EXISTS hits_wide DROP TABLE IF EXISTS hits_compact From e92641858e6fdf132c4b1f9ecf401fc985d2693e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 12 Jun 2020 01:00:35 +0300 Subject: [PATCH 0291/1102] fixes --- base/daemon/SentryWriter.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index bb176db813cb..d7f08864e962 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -46,8 +46,10 @@ void sentry_logger(sentry_level_t level, const char * message, va_list args) auto * logger = &Poco::Logger::get("SentryWriter"); size_t size = 1024; char buffer[size]; - if (vsnprintf(buffer, size, message, args) >= 0) { - switch (level) { + if (vsnprintf(buffer, size, message, args) >= 0) + { + switch (level) + { case SENTRY_LEVEL_DEBUG: logger->debug(buffer); break; @@ -67,7 +69,6 @@ void sentry_logger(sentry_level_t level, const char * message, va_list args) } } } -} #endif void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) From 395ef1ecafff89a880bd47c2b11aa12b42e52c03 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 12 Jun 2020 09:35:31 +0300 Subject: [PATCH 0292/1102] experiment --- base/daemon/SentryWriter.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index d7f08864e962..88639d8bf94a 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -46,8 +46,15 @@ void sentry_logger(sentry_level_t level, const char * message, va_list args) auto * logger = &Poco::Logger::get("SentryWriter"); size_t size = 1024; char buffer[size]; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif if (vsnprintf(buffer, size, message, args) >= 0) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif switch (level) { case SENTRY_LEVEL_DEBUG: From 0da6e1c9de6d5cdd7b24e08de815589c6d2d36cc Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Fri, 12 Jun 2020 15:12:12 +0300 Subject: [PATCH 0293/1102] typo --- docker/test/performance-comparison/compare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index 3d49e9e841ac..241fdaec70d2 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -167,7 +167,7 @@ function run_tests function run_benchmark { rm -rf benchmark ||: - mkdir bencmhark ||: + mkdir benchmark ||: # TODO disable this when there is an explicit list of tests to run "$script_dir/perf.py" --print right/performance/website.xml > benchmark/website-queries.tsv From a9514d725768d0025ed848e2a5ea016fb8ac5001 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Fri, 12 Jun 2020 16:52:41 +0300 Subject: [PATCH 0294/1102] trigger ci From 5101708831d6550ac061b9d8b070c3439aad4968 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Fri, 12 Jun 2020 18:11:33 +0300 Subject: [PATCH 0295/1102] fixup --- docker/test/performance-comparison/compare.sh | 7 +++++-- .../config/users.d/perf-comparison-tweaks-users.xml | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index 241fdaec70d2..5435d37e2e0b 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -171,8 +171,11 @@ function run_benchmark # TODO disable this when there is an explicit list of tests to run "$script_dir/perf.py" --print right/performance/website.xml > benchmark/website-queries.tsv - clickhouse-benchmark --port 9001 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-left.json < benchmark/website-queries.tsv - clickhouse-benchmark --port 9002 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-right.json < benchmark/website-queries.tsv + # TODO things to fix in clickhouse-benchmark: + # - --max_memory_usage setting does nothing + # - no way to continue on error + clickhouse-benchmark --port 9001 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-left.json -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv + clickhouse-benchmark --port 9002 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-right.json -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv } function get_profiles_watchdog diff --git a/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml b/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml index 6e3e3df5d391..1bde2a1388be 100644 --- a/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml +++ b/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml @@ -6,6 +6,8 @@ 1 1 1 + + 30000000000 From 3b0a3e00c0c1a902622aabf277b2ab0bb2a07571 Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 11 Jun 2020 20:05:35 +0000 Subject: [PATCH 0296/1102] Some fixes --- src/Storages/RabbitMQ/RabbitMQHandler.cpp | 8 +-- src/Storages/RabbitMQ/RabbitMQHandler.h | 5 +- src/Storages/RabbitMQ/RabbitMQSettings.h | 2 +- .../ReadBufferFromRabbitMQConsumer.cpp | 53 +++++++++++-------- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 9 +++- .../WriteBufferToRabbitMQProducer.cpp | 4 +- .../integration/test_storage_rabbitmq/test.py | 3 +- 7 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 0a432e1b5ca4..8667427ee632 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -37,11 +37,12 @@ void RabbitMQHandler::onError(AMQP::TcpConnection * connection, const char * mes void RabbitMQHandler::startConsumerLoop(std::atomic & loop_started) { /* The object of this class is shared between concurrent consumers (who share the same connection == share the same - * event loop). But the loop should not be attempted to start if it is already running. + * event loop and handler). But the loop should not be attempted to start if it is already running. */ if (mutex_before_event_loop.try_lock_for(std::chrono::milliseconds(Lock_timeout))) { loop_started = true; + stop_scheduled.store(false); event_base_loop(evbase, EVLOOP_NONBLOCK); mutex_before_event_loop.unlock(); } @@ -56,7 +57,7 @@ void RabbitMQHandler::startProducerLoop() void RabbitMQHandler::stop() { - if (mutex_before_loop_stop.try_lock_for(std::chrono::milliseconds(0))) + if (mutex_before_loop_stop.try_lock()) { event_base_loopbreak(evbase); mutex_before_loop_stop.unlock(); @@ -66,8 +67,9 @@ void RabbitMQHandler::stop() void RabbitMQHandler::stopWithTimeout() { - if (mutex_before_loop_stop.try_lock_for(std::chrono::milliseconds(0))) + if (mutex_before_loop_stop.try_lock()) { + stop_scheduled.store(true); event_base_loopexit(evbase, &tv); mutex_before_loop_stop.unlock(); } diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.h b/src/Storages/RabbitMQ/RabbitMQHandler.h index 911651097bb2..9b2d273422d2 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.h +++ b/src/Storages/RabbitMQ/RabbitMQHandler.h @@ -23,15 +23,16 @@ class RabbitMQHandler : public AMQP::LibEventHandler void startProducerLoop(); void stopWithTimeout(); void stop(); + std::atomic & checkStopIsScheduled() { return stop_scheduled; }; private: event_base * evbase; Poco::Logger * log; timeval tv; - size_t count_passed = 0; + std::atomic stop_scheduled = false; std::timed_mutex mutex_before_event_loop; - std::timed_mutex mutex_before_loop_stop; + std::mutex mutex_before_loop_stop; }; } diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index d81a887747be..c9f09489f774 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -15,9 +15,9 @@ namespace DB M(SettingString, rabbitmq_exchange_name, "clickhouse-exchange", "The exchange name, to which messages are sent.", 0) \ M(SettingString, rabbitmq_format, "", "The message format.", 0) \ M(SettingChar, rabbitmq_row_delimiter, '\0', "The character to be considered as a delimiter.", 0) \ + M(SettingString, rabbitmq_exchange_type, "default", "The exchange type.", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ - M(SettingString, rabbitmq_exchange_type, "default", "The exchange type.", 0) \ DECLARE_SETTINGS_COLLECTION(LIST_OF_RABBITMQ_SETTINGS) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 5d2e3073d41c..6b8763138a4b 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -13,6 +13,10 @@ namespace DB { +namespace ErrorCodes +{ + extern const int BAD_ARGUMENTS; +} namespace Exchange { @@ -22,6 +26,7 @@ namespace Exchange static const String DIRECT = "direct"; static const String TOPIC = "topic"; static const String HASH = "consistent_hash"; + static const String HEADERS = "headers"; } @@ -55,7 +60,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( messages.clear(); current = messages.begin(); - exchange_type_set = exchange_type != Exchange::DEFAULT ? true : false; + exchange_type_set = exchange_type != Exchange::DEFAULT; /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. * By default there is one queue per consumer. @@ -81,7 +86,7 @@ ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() void ReadBufferFromRabbitMQConsumer::initExchange() { /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest (also due to different - * binding algorithm this default behaviuor is much faster). It is also used in INSERT query. + * binding algorithm this default behaviuor is much faster). It is also used in INSERT query (so it is always declared). */ String producer_exchange = exchange_type_set ? exchange_name + "_" + Exchange::DEFAULT : exchange_name; consumer_channel->declareExchange(producer_exchange, AMQP::fanout).onError([&](const char * message) @@ -114,10 +119,12 @@ void ReadBufferFromRabbitMQConsumer::initExchange() else if (exchange_type == Exchange::DIRECT) type = AMQP::ExchangeType::direct; else if (exchange_type == Exchange::TOPIC) type = AMQP::ExchangeType::topic; else if (exchange_type == Exchange::HASH) type = AMQP::ExchangeType::consistent_hash; - else return; + else if (exchange_type == Exchange::HEADERS) + throw Exception("Headers exchange is not supported", ErrorCodes::BAD_ARGUMENTS); + else throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages - * between all consumers. (This enables better scaling as without hash-echange - the only oprion to avoid getting the same + * between all consumers. (This enables better scaling as without hash-exchange - the only option to avoid getting the same * messages more than once - is having only one consumer with one queue, which is not good.) */ consumer_channel->declareExchange(exchange_name, type).onError([&](const char * message) @@ -156,7 +163,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { - /// These variables might be updated later from a separate thread in onError callbacks + /// These variables might be updated later from a separate thread in onError callbacks. if (!internal_exchange_declared || (exchange_type_set && !local_exchange_declared)) { initExchange(); @@ -206,7 +213,10 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) LOG_ERROR(log, "Failed to bind to key {}. Reason: {}", binding_key, message); }); - /// Must be done here and not in readPrefix() because library might fail to handle async subscription on the same connection + /* Subscription can probably be moved back to readPrefix(), but not sure whether it is better in regard to speed. Also note + * that if moved there, it must(!) be wrapped inside a channel->onReady callback or any other, otherwise consumer might fail + * to subscribe and no resubscription will help. + */ subscribe(queues.back()); LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); @@ -229,7 +239,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } else { - /// Means there is only one queue with one consumer - no even distribution needed - no hash-exchange + /// Means there is only one queue with one consumer - no even distribution needed - no hash-exchange. for (auto & routing_key : routing_keys) { /// Binding directly to exchange, specified by the client @@ -274,6 +284,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) .onSuccess([&](const std::string & /* consumer */) { subscribed_queue[queue_name] = true; + consumer_error = false; ++count_subscribed; LOG_TRACE(log, "Consumer {} is subscribed to queue {}", channel_id, queue_name); @@ -290,24 +301,17 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) message_received += row_delimiter; } - bool stop_loop = false; - /// Needed to avoid data race because this vector can be used at the same time by another thread in nextImpl(). { std::lock_guard lock(mutex); received.push_back(message_received); - - /* As event loop is blocking to the thread that started it and a single thread should not be blocked while - * executing all callbacks on the connection (not only its own), then there should be some point to unblock. - * loop_started == 1 if current consumer is started the loop and not another. - */ - if (!loop_started) - { - stop_loop = true; - } } - if (stop_loop) + /* As event loop is blocking to the thread that started it and a single thread should not be blocked while + * executing all callbacks on the connection (not only its own), then there should be some point to unblock. + * loop_started == 1 if current consumer is started the loop and not another. + */ + if (!loop_started.load() && !eventHandler.checkStopIsScheduled().load()) { stopEventLoopWithTimeout(); } @@ -323,7 +327,6 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) void ReadBufferFromRabbitMQConsumer::checkSubscription() { - /// In general this condition will always be true and looping/resubscribing would not happen if (count_subscribed == num_queues) return; @@ -337,7 +340,11 @@ void ReadBufferFromRabbitMQConsumer::checkSubscription() LOG_TRACE(log, "Consumer {} is subscribed to {} queues", channel_id, count_subscribed); - /// A case that would not normally happen + /// Updated in callbacks which are run by the loop + if (count_subscribed == num_queues) + return; + + /// A case that should never normally happen for (auto & queue : queues) { subscribe(queue); @@ -372,9 +379,9 @@ bool ReadBufferFromRabbitMQConsumer::nextImpl() { if (received.empty()) { - /// Run the onReceived callbacks to save the messages that have been received by now, blocks current thread + /// Run the onReceived callbacks to save the messages that have been received by now, blocks current thread. startEventLoop(loop_started); - loop_started = false; + loop_started.store(false); } if (received.empty()) diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 212d1fbc783f..af8ad50e4e11 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -450,8 +450,15 @@ void registerStorageRabbitMQ(StorageFactory & factory) { exchange_type = safeGet(ast->value); } - } + if (exchange_type != "fanout" && exchange_type != "direct" && exchange_type != "topic" && exchange_type != "consistent_hash") + { + if (exchange_type == "headers") + throw Exception("Headers exchange is not supported", ErrorCodes::BAD_ARGUMENTS); + else + throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); + } + } UInt64 num_consumers = rabbitmq_settings.rabbitmq_num_consumers; if (args_count >= 7) diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 8fa241dade51..6e2b6f21f1d2 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -33,7 +33,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( : WriteBuffer(nullptr, 0) , login_password(login_password_) , routing_key(routing_key_) - , exchange_name(exchange_) + , exchange_name(exchange_ + "_direct") , log(log_) , num_queues(num_queues_) , bind_by_id(bind_by_id_) @@ -126,7 +126,7 @@ void WriteBufferToRabbitMQProducer::checkExchange() /* The AMQP::passive flag indicates that it should only be checked if there is a valid exchange with the given name * and makes it visible from current producer_channel. */ - producer_channel->declareExchange(exchange_name + "_direct", AMQP::direct, AMQP::passive) + producer_channel->declareExchange(exchange_name, AMQP::direct, AMQP::passive) .onSuccess([&]() { exchange_declared = true; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 1a56395eb29a..37163db06f44 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -882,7 +882,7 @@ def insert(): @pytest.mark.timeout(240) -def test_rabbitmq_sharding_between_channels_insert(rabbitmq_cluster): +def test_rabbitmq_sharding_between_channels_and_queues_insert(rabbitmq_cluster): instance.query(''' DROP TABLE IF EXISTS test.view_sharding; DROP TABLE IF EXISTS test.consumer_sharding; @@ -890,6 +890,7 @@ def test_rabbitmq_sharding_between_channels_insert(rabbitmq_cluster): ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', rabbitmq_num_consumers = 5, + rabbitmq_num_queues = 2, rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; CREATE TABLE test.view_sharding (key UInt64, value UInt64) From 462e8bcdc97dfd094747b682f19cabbc8e4b74bc Mon Sep 17 00:00:00 2001 From: kssenii Date: Thu, 11 Jun 2020 21:03:53 +0000 Subject: [PATCH 0297/1102] Support transactions for publishing --- src/Storages/RabbitMQ/RabbitMQSettings.h | 1 + src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 24 ++++++++-- src/Storages/RabbitMQ/StorageRabbitMQ.h | 4 +- .../WriteBufferToRabbitMQProducer.cpp | 46 +++++++++++++++++-- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 3 ++ 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQSettings.h b/src/Storages/RabbitMQ/RabbitMQSettings.h index c9f09489f774..5cd52ed9ef71 100644 --- a/src/Storages/RabbitMQ/RabbitMQSettings.h +++ b/src/Storages/RabbitMQ/RabbitMQSettings.h @@ -18,6 +18,7 @@ namespace DB M(SettingString, rabbitmq_exchange_type, "default", "The exchange type.", 0) \ M(SettingUInt64, rabbitmq_num_consumers, 1, "The number of consumer channels per table.", 0) \ M(SettingUInt64, rabbitmq_num_queues, 1, "The number of queues per consumer.", 0) \ + M(SettingBool, rabbitmq_transactional_channel, false, "Use transactional channel for publishing.", 0) \ DECLARE_SETTINGS_COLLECTION(LIST_OF_RABBITMQ_SETTINGS) diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index af8ad50e4e11..669cfe19aa55 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -61,7 +61,8 @@ StorageRabbitMQ::StorageRabbitMQ( char row_delimiter_, const String & exchange_type_, size_t num_consumers_, - size_t num_queues_) + size_t num_queues_, + const bool use_transactional_channel_) : IStorage(table_id_) , global_context(context_.getGlobalContext()) , rabbitmq_context(Context(global_context)) @@ -72,6 +73,7 @@ StorageRabbitMQ::StorageRabbitMQ( , num_consumers(num_consumers_) , num_queues(num_queues_) , exchange_type(exchange_type_) + , use_transactional_channel(use_transactional_channel_) , log(&Poco::Logger::get("StorageRabbitMQ (" + table_id_.table_name + ")")) , semaphore(0, num_consumers_) , login_password(std::make_pair( @@ -225,7 +227,8 @@ ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() String producer_exchange = exchange_type == "default" ? exchange_name : exchange_name + "_default"; return std::make_shared(parsed_address, login_password, routing_keys[0], producer_exchange, - log, num_consumers * num_queues, bind_by_id, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); + log, num_consumers * num_queues, bind_by_id, use_transactional_channel, + row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } @@ -488,9 +491,24 @@ void registerStorageRabbitMQ(StorageFactory & factory) } } + bool use_transactional_channel = static_cast(rabbitmq_settings.rabbitmq_transactional_channel); + if (args_count >= 9) + { + const auto * ast = engine_args[8]->as(); + if (ast && ast->value.getType() == Field::Types::UInt64) + { + use_transactional_channel = static_cast(safeGet(ast->value)); + } + else + { + throw Exception("Transactional channel parameter is a bool", ErrorCodes::BAD_ARGUMENTS); + } + } + return StorageRabbitMQ::create( args.table_id, args.context, args.columns, - host_port, routing_keys, exchange, format, row_delimiter, exchange_type, num_consumers, num_queues); + host_port, routing_keys, exchange, format, row_delimiter, exchange_type, num_consumers, + num_queues, use_transactional_channel); }; factory.registerStorage("RabbitMQ", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index 45ced9d247bc..e056faa0d65a 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -65,7 +65,8 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub char row_delimiter_, const String & exchange_type_, size_t num_consumers_, - size_t num_queues_); + size_t num_queues_, + const bool use_transactional_channel_); private: Context global_context; @@ -81,6 +82,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub bool bind_by_id; size_t num_queues; const String exchange_type; + const bool use_transactional_channel; Poco::Logger * log; std::pair parsed_address; diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 6e2b6f21f1d2..09179b95a153 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -15,7 +15,8 @@ namespace DB enum { Connection_setup_sleep = 200, - Connection_setup_retries_max = 1000, + Loop_retries_max = 1000, + Loop_wait = 10, Batch = 10000 }; @@ -27,6 +28,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( Poco::Logger * log_, const size_t num_queues_, const bool bind_by_id_, + const bool use_transactional_channel_, std::optional delimiter, size_t rows_per_message, size_t chunk_size_) @@ -37,6 +39,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( , log(log_) , num_queues(num_queues_) , bind_by_id(bind_by_id_) + , use_transactional_channel(use_transactional_channel_) , delim(delimiter) , max_rows(rows_per_message) , chunk_size(chunk_size_) @@ -50,7 +53,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( * different threads (as outputStreams are asynchronous) with the same connection leads to internal library errors. */ size_t cnt_retries = 0; - while (!connection.ready() && ++cnt_retries != Connection_setup_retries_max) + while (!connection.ready() && ++cnt_retries != Loop_retries_max) { event_base_loop(producerEvbase, EVLOOP_NONBLOCK | EVLOOP_ONCE); std::this_thread::sleep_for(std::chrono::milliseconds(Connection_setup_sleep)); @@ -63,14 +66,19 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( producer_channel = std::make_shared(&connection); checkExchange(); + + /// If publishing should be wrapped in transactions + if (use_transactional_channel) + { + producer_channel->startTransaction(); + } } WriteBufferToRabbitMQProducer::~WriteBufferToRabbitMQProducer() { - checkExchange(); + finilize(); connection.close(); - assert(rows == 0 && chunks.empty()); } @@ -145,6 +153,36 @@ void WriteBufferToRabbitMQProducer::checkExchange() } +void WriteBufferToRabbitMQProducer::finilize() +{ + checkExchange(); + + if (use_transactional_channel) + { + std::atomic answer_received = false; + producer_channel->commitTransaction() + .onSuccess([&]() + { + answer_received = true; + LOG_TRACE(log, "All messages were successfully published"); + }) + .onError([&](const char * message) + { + answer_received = true; + LOG_TRACE(log, "None of messages were publishd: {}", message); + /// Probably should do something here + }); + + size_t count_retries = 0; + while (!answer_received && ++count_retries != Loop_retries_max) + { + startEventLoop(); + std::this_thread::sleep_for(std::chrono::milliseconds(Loop_wait)); + } + } +} + + void WriteBufferToRabbitMQProducer::nextImpl() { chunks.push_back(std::string()); diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index 90e0d90b3564..9fd36257561b 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -26,6 +26,7 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer Poco::Logger * log_, const size_t num_queues_, const bool bind_by_id_, + const bool use_transactional_channel_, std::optional delimiter, size_t rows_per_message, size_t chunk_size_ @@ -39,12 +40,14 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer private: void nextImpl() override; void checkExchange(); + void finilize(); std::pair & login_password; const String routing_key; const String exchange_name; const bool bind_by_id; const size_t num_queues; + const bool use_transactional_channel; event_base * producerEvbase; RabbitMQHandler eventHandler; From 56869228a2db535d27bd2ab7767cf2ed64247ac9 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Fri, 12 Jun 2020 21:28:07 +0300 Subject: [PATCH 0298/1102] add flag to continue on errors --- docker/test/performance-comparison/compare.sh | 5 +- programs/benchmark/Benchmark.cpp | 62 ++++++++++++------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index 5435d37e2e0b..1dbf712ff505 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -173,9 +173,8 @@ function run_benchmark "$script_dir/perf.py" --print right/performance/website.xml > benchmark/website-queries.tsv # TODO things to fix in clickhouse-benchmark: # - --max_memory_usage setting does nothing - # - no way to continue on error - clickhouse-benchmark --port 9001 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-left.json -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv - clickhouse-benchmark --port 9002 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-right.json -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv + clickhouse-benchmark --port 9001 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-left.json --continue_on_errors -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv + clickhouse-benchmark --port 9002 --concurrency 6 --cumulative --iterations 1000 --randomize 1 --delay 0 --json benchmark/website-right.json --continue_on_errors -- --max_memory_usage 30000000000 < benchmark/website-queries.tsv } function get_profiles_watchdog diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index e17320b39ea7..6884f6faed36 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -59,11 +59,14 @@ class Benchmark : public Poco::Util::Application bool cumulative_, bool secure_, const String & default_database_, const String & user_, const String & password_, const String & stage, bool randomize_, size_t max_iterations_, double max_time_, - const String & json_path_, size_t confidence_, const String & query_id_, const Settings & settings_) + const String & json_path_, size_t confidence_, + const String & query_id_, bool continue_on_errors_, + const Settings & settings_) : concurrency(concurrency_), delay(delay_), queue(concurrency), randomize(randomize_), cumulative(cumulative_), max_iterations(max_iterations_), max_time(max_time_), - json_path(json_path_), confidence(confidence_), query_id(query_id_), settings(settings_), + json_path(json_path_), confidence(confidence_), query_id(query_id_), + continue_on_errors(continue_on_errors_), settings(settings_), shared_context(Context::createShared()), global_context(Context::createGlobal(shared_context.get())), pool(concurrency) { @@ -149,6 +152,7 @@ class Benchmark : public Poco::Util::Application String json_path; size_t confidence; std::string query_id; + bool continue_on_errors; Settings settings; SharedContextHolder shared_context; Context global_context; @@ -332,35 +336,45 @@ class Benchmark : public Poco::Util::Application pcg64 generator(randomSeed()); std::uniform_int_distribution distribution(0, connection_entries.size() - 1); - try + /// In these threads we do not accept INT signal. + sigset_t sig_set; + if (sigemptyset(&sig_set) + || sigaddset(&sig_set, SIGINT) + || pthread_sigmask(SIG_BLOCK, &sig_set, nullptr)) { - /// In these threads we do not accept INT signal. - sigset_t sig_set; - if (sigemptyset(&sig_set) - || sigaddset(&sig_set, SIGINT) - || pthread_sigmask(SIG_BLOCK, &sig_set, nullptr)) - throwFromErrno("Cannot block signal.", ErrorCodes::CANNOT_BLOCK_SIGNAL); - - while (true) + throwFromErrno("Cannot block signal.", ErrorCodes::CANNOT_BLOCK_SIGNAL); + } + + while (true) + { + bool extracted = false; + + while (!extracted) { - bool extracted = false; + extracted = queue.tryPop(query, 100); - while (!extracted) + if (shutdown + || (max_iterations && queries_executed == max_iterations)) { - extracted = queue.tryPop(query, 100); - - if (shutdown || (max_iterations && queries_executed == max_iterations)) - return; + return; } + } + + try + { execute(connection_entries, query, distribution(generator)); ++queries_executed; } - } - catch (...) - { - shutdown = true; - std::cerr << "An error occurred while processing query:\n" << query << "\n"; - throw; + catch (...) + { + std::cerr << "An error occurred while processing query:\n" + << query << "\n"; + if (!continue_on_errors) + { + shutdown = true; + throw; + } + } } } @@ -541,6 +555,7 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) ("stacktrace", "print stack traces of exceptions") ("confidence", value()->default_value(5), "set the level of confidence for T-test [0=80%, 1=90%, 2=95%, 3=98%, 4=99%, 5=99.5%(default)") ("query_id", value()->default_value(""), "") + ("continue_on_errors", "continue testing even if a query fails") ; Settings settings; @@ -580,6 +595,7 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) options["json"].as(), options["confidence"].as(), options["query_id"].as(), + options.count("continue_on_errors") > 0, settings); return benchmark.run(); } From 62eaeac713a130f73c74e6efc6d831009a582a5e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 13 Jun 2020 05:35:42 +0300 Subject: [PATCH 0299/1102] trigger ci From b8a4c7708ac8724dd0aac5ca957a9aa46132af47 Mon Sep 17 00:00:00 2001 From: kssenii Date: Sat, 13 Jun 2020 18:15:59 +0000 Subject: [PATCH 0300/1102] Make local exchanges unique for each table --- .../ReadBufferFromRabbitMQConsumer.cpp | 94 ++++++++++--------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 8 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 14 ++- src/Storages/RabbitMQ/StorageRabbitMQ.h | 1 + 4 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 6b8763138a4b..90485b28a968 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -41,7 +41,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( const bool bind_by_id_, const size_t num_queues_, const String & exchange_type_, - const String table_name_, + const String & local_exchange_name_, const std::atomic & stopped_) : ReadBuffer(nullptr, 0) , consumer_channel(std::move(consumer_channel_)) @@ -54,7 +54,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , bind_by_id(bind_by_id_) , num_queues(num_queues_) , exchange_type(exchange_type_) - , table_name(table_name_) + , local_exchange_name(local_exchange_name_) , stopped(stopped_) { messages.clear(); @@ -85,28 +85,31 @@ ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() void ReadBufferFromRabbitMQConsumer::initExchange() { - /* If exchange_type is not set - then direct-exchange is used - this type of exchange is the fastest (also due to different - * binding algorithm this default behaviuor is much faster). It is also used in INSERT query (so it is always declared). + /* This direct-exchange is used for default implemenation and for INSERT query (so it is always declared). If exchange_type + * is not set, then there are only two exchanges - external, defined by the client, and local, unique for each table. + * This strict division to external and local exchanges is needed to avoid too much complexity with defining exchange_name + * for INSERT query producer and, in general, it is much better to distinguish them into separate ones. */ - String producer_exchange = exchange_type_set ? exchange_name + "_" + Exchange::DEFAULT : exchange_name; - consumer_channel->declareExchange(producer_exchange, AMQP::fanout).onError([&](const char * message) + String default_exchange = exchange_type_set ? exchange_name + "_" + Exchange::DEFAULT : exchange_name; + consumer_channel->declareExchange(default_exchange, AMQP::fanout).onError([&](const char * message) { - internal_exchange_declared = false; - LOG_ERROR(log, "Failed to declare exchange: {}", message); + local_exchange_declared = false; + LOG_ERROR(log, "Failed to declare exchange {}. Reason: {}", default_exchange, message); }); - internal_exchange_name = producer_exchange + "_" + Exchange::DIRECT; - consumer_channel->declareExchange(internal_exchange_name, AMQP::direct).onError([&](const char * message) + default_local_exchange = local_exchange_name; + default_local_exchange += exchange_type_set ? "_default_" + Exchange::DIRECT : "_" + Exchange::DIRECT; + consumer_channel->declareExchange(default_local_exchange, AMQP::direct).onError([&](const char * message) { - internal_exchange_declared = false; - LOG_ERROR(log, "Failed to declare exchange: {}", message); + local_exchange_declared = false; + LOG_ERROR(log, "Failed to declare exchange {}. Reason: {}", default_local_exchange, message); }); - /// With fanout exchange the binding key is ignored - a parameter might be arbitrary - consumer_channel->bindExchange(producer_exchange, internal_exchange_name, routing_keys[0]).onError([&](const char * message) + /// With fanout exchange the binding key is ignored - a parameter might be arbitrary. All distribution lies on local_exchange. + consumer_channel->bindExchange(default_exchange, default_local_exchange, routing_keys[0]).onError([&](const char * message) { - internal_exchange_declared = false; - LOG_ERROR(log, "Failed to bind exchange: {}", message); + local_exchange_declared = false; + LOG_ERROR(log, "Failed to bind {} exchange to {} exchange. Reason: {}", default_exchange, default_local_exchange, message); }); if (!exchange_type_set) @@ -124,26 +127,29 @@ void ReadBufferFromRabbitMQConsumer::initExchange() else throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages - * between all consumers. (This enables better scaling as without hash-exchange - the only option to avoid getting the same - * messages more than once - is having only one consumer with one queue, which is not good.) + * between all consumers. (This enables better scaling as without hash-exchange - the only option to avoid getting + * the same messages more than once - is having only one consumer with one queue, which is not good.) */ consumer_channel->declareExchange(exchange_name, type).onError([&](const char * message) { local_exchange_declared = false; - LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); + LOG_ERROR(log, "Failed to declare client's {} exchange: {}", exchange_type, message); }); - /// No need for declaring hash-exchange if there is only one consumer with one queue and exchange type is not hash - if (!bind_by_id && exchange_type != Exchange::HASH) + /// No need for declaring hash-exchange if there is only one consumer with one queue or exchange type is already hash + if (!bind_by_id) return; hash_exchange = true; + if (exchange_type == Exchange::HASH) + return; + AMQP::Table exchange_arguments; exchange_arguments["hash-property"] = "message_id"; - local_exchange_name = exchange_name + "_" + table_name; - consumer_channel->declareExchange(local_exchange_name, AMQP::consistent_hash, exchange_arguments) + String local_hash_exchange_name = local_exchange_name + "_hash"; + consumer_channel->declareExchange(local_hash_exchange_name, AMQP::consistent_hash, exchange_arguments) .onError([&](const char * message) { local_exchange_declared = false; @@ -152,7 +158,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() for (auto & routing_key : routing_keys) { - consumer_channel->bindExchange(exchange_name, local_exchange_name, routing_key).onError([&](const char * message) + consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_key).onError([&](const char * message) { local_exchange_declared = false; LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); @@ -164,19 +170,15 @@ void ReadBufferFromRabbitMQConsumer::initExchange() void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) { /// These variables might be updated later from a separate thread in onError callbacks. - if (!internal_exchange_declared || (exchange_type_set && !local_exchange_declared)) + if (!local_exchange_declared || (exchange_type_set && !local_hash_exchange_declared)) { initExchange(); local_exchange_declared = true; - internal_exchange_declared = true; + local_hash_exchange_declared = true; } - /* Internal exchange is a default exchange (by implementstion, not by rabbitmq settings) and is used for INSERT query - * and if exchange_type is not set - there is no local exchange. If it is set - then local exchange is a distributor - * exchange, which is bound to the exchange specified by the client. - */ - bool internal_bindings_created = false, internal_bindings_error = false; - bool local_bindings_created = false, local_bindings_error = false; + bool default_bindings_created = false, default_bindings_error = false; + bool bindings_created = false, bindings_error = false; consumer_channel->declareQueue(AMQP::exclusive) .onSuccess([&](const std::string & queue_name_, int /* msgcount */, int /* consumercount */) @@ -202,14 +204,14 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } } - consumer_channel->bindQueue(internal_exchange_name, queue_name_, binding_key) + consumer_channel->bindQueue(default_local_exchange, queue_name_, binding_key) .onSuccess([&] { - internal_bindings_created = true; + default_bindings_created = true; }) .onError([&](const char * message) { - internal_bindings_error = true; + default_bindings_error = true; LOG_ERROR(log, "Failed to bind to key {}. Reason: {}", binding_key, message); }); @@ -223,17 +225,22 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) if (exchange_type_set) { - /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary if (hash_exchange) { - consumer_channel->bindQueue(local_exchange_name, queue_name_, binding_key) + /* If exchange_type == hash, then bind directly to this client's exchange (because there is no need for a distributor + * exchange as it is already hash-exchange), otherwise hash-exchange is a local distributor exchange. + */ + String hash_exchange_name = exchange_type == Exchange::HASH ? exchange_name : local_exchange_name + "_hash"; + + /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary + consumer_channel->bindQueue(hash_exchange_name, queue_name_, binding_key) .onSuccess([&] { - local_bindings_created = true; + bindings_created = true; }) .onError([&](const char * message) { - local_bindings_error = true; + bindings_error = true; LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", binding_key, message); }); } @@ -246,11 +253,11 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) consumer_channel->bindQueue(exchange_name, queue_name_, routing_key) .onSuccess([&] { - local_bindings_created = true; + bindings_created = true; }) .onError([&](const char * message) { - local_bindings_error = true; + bindings_error = true; LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", routing_key, message); }); } @@ -259,7 +266,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) }) .onError([&](const char * message) { - internal_bindings_error = true; + default_bindings_error = true; LOG_ERROR(log, "Failed to declare queue on the channel: {}", message); }); @@ -267,8 +274,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) * It is important at this moment to make sure that queue bindings are created before any publishing can happen because * otherwise messages will be routed nowhere. */ - while (!internal_bindings_created && !internal_bindings_error - || (exchange_type_set && !local_bindings_created && !local_bindings_error)) + while (!default_bindings_created && !default_bindings_error || (exchange_type_set && !bindings_created && !bindings_error)) { startEventLoop(loop_started); } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 3d02eeab7611..6a2c847357d2 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -32,7 +32,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const bool bind_by_id_, const size_t num_queues_, const String & exchange_type_, - const String table_name_, + const String & local_exchange_name_, const std::atomic & stopped_); ~ReadBufferFromRabbitMQConsumer() override; @@ -54,7 +54,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const bool bind_by_id; const size_t num_queues; const String & exchange_type; - const String table_name; + const String & local_exchange_name; Poco::Logger * log; char row_delimiter; @@ -62,8 +62,8 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer bool allowed = true; const std::atomic & stopped; - String internal_exchange_name, local_exchange_name; - bool internal_exchange_declared = false, local_exchange_declared = false; + String default_local_exchange; + bool local_exchange_declared = false, local_hash_exchange_declared = false; bool exchange_type_set = false, hash_exchange = false; std::atomic loop_started = false, consumer_error = false; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 669cfe19aa55..d3811bdb0d2d 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -104,6 +104,10 @@ StorageRabbitMQ::StorageRabbitMQ( task->deactivate(); bind_by_id = num_consumers > 1 || num_queues > 1; + + auto table_id = getStorageID(); + String table_name = table_id.table_name; + local_exchange_name = exchange_name + "_" + table_name; } @@ -214,17 +218,17 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ChannelPtr consumer_channel = std::make_shared(&connection); - auto table_id = getStorageID(); - String table_name = table_id.getNameForLogs(); - return std::make_shared(consumer_channel, eventHandler, exchange_name, routing_keys, - next_channel_id, log, row_delimiter, bind_by_id, num_queues, exchange_type, table_name, stream_cancelled); + next_channel_id, log, row_delimiter, bind_by_id, num_queues, exchange_type, local_exchange_name, stream_cancelled); } ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { - String producer_exchange = exchange_type == "default" ? exchange_name : exchange_name + "_default"; + /* If exchange type is set, then there are different exchanges for external publishing and for INSERT query + * as in this case they are of different types. + */ + String producer_exchange = exchange_type == "default" ? local_exchange_name : local_exchange_name + "_default"; return std::make_shared(parsed_address, login_password, routing_keys[0], producer_exchange, log, num_consumers * num_queues, bind_by_id, use_transactional_channel, diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.h b/src/Storages/RabbitMQ/StorageRabbitMQ.h index e056faa0d65a..79e4d5e4ca25 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.h +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.h @@ -74,6 +74,7 @@ class StorageRabbitMQ final: public ext::shared_ptr_helper, pub Names routing_keys; const String exchange_name; + String local_exchange_name; const String format_name; char row_delimiter; From 9c49398728909f0ae375aed8e3de17673405cc3c Mon Sep 17 00:00:00 2001 From: kssenii Date: Sat, 13 Jun 2020 18:44:17 +0000 Subject: [PATCH 0301/1102] Fix tests --- .../integration/test_storage_rabbitmq/test.py | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 37163db06f44..8442a7ecb0a5 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -33,7 +33,7 @@ def check_rabbitmq_is_available(): 'exec', '-i', rabbitmq_id, - 'rabbitmqctl', + 'rabbitmqctl', 'await_startup'), stdout=subprocess.PIPE) p.communicate() @@ -774,6 +774,7 @@ def test_rabbitmq_insert(rabbitmq_cluster): CREATE TABLE test.rabbitmq (key UInt64, value UInt64) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_exchange_name = 'insert', rabbitmq_routing_key_list = 'insert1', rabbitmq_format = 'TSV', rabbitmq_row_delimiter = '\\n'; @@ -784,10 +785,10 @@ def test_rabbitmq_insert(rabbitmq_cluster): consumer_connection = pika.BlockingConnection(parameters) consumer = consumer_connection.channel() - consumer.exchange_declare(exchange='clickhouse-exchange', exchange_type='fanout') + consumer.exchange_declare(exchange='insert_rabbitmq_direct', exchange_type='direct') result = consumer.queue_declare(queue='') queue_name = result.method.queue - consumer.queue_bind(exchange='clickhouse-exchange', queue=queue_name, routing_key='insert1') + consumer.queue_bind(exchange='insert_rabbitmq_direct', queue=queue_name, routing_key='insert1') values = [] for i in range(50): @@ -871,8 +872,9 @@ def insert(): break instance.query(''' - DROP TABLE test.consumer_many; - DROP TABLE test.view_many; + DROP TABLE IF EXISTS test.rabbitmq_many; + DROP TABLE IF EXISTS test.consumer_many; + DROP TABLE IF EXISTS test.view_many; ''') for thread in threads: @@ -932,8 +934,9 @@ def insert(): break instance.query(''' - DROP TABLE test.consumer_sharding; - DROP TABLE test.view_sharding; + DROP TABLE IF EXISTS test.rabbitmq_sharding; + DROP TABLE IF EXISTS test.consumer_sharding; + DROP TABLE IF EXISTS test.view_sharding; ''') for thread in threads: @@ -992,8 +995,9 @@ def insert(): break instance.query(''' - DROP TABLE test.consumer_overload; - DROP TABLE test.view_overload; + DROP TABLE IF EXISTS test.rabbitmq_overload; + DROP TABLE IF EXISTS test.consumer_overload; + DROP TABLE IF EXISTS test.view_overload; ''') for thread in threads: @@ -1060,6 +1064,16 @@ def test_rabbitmq_direct_exchange(rabbitmq_cluster): if int(result) == messages_num * num_tables: break + for consumer_id in range(num_tables): + instance.query(''' + DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; + DROP TABLE IF EXISTS test.direct_exchange_{0}; + '''.format(consumer_id)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + assert int(result) == messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) @@ -1118,6 +1132,16 @@ def test_rabbitmq_fanout_exchange(rabbitmq_cluster): if int(result) == messages_num * num_tables: break + for consumer_id in range(num_tables): + instance.query(''' + DROP TABLE IF EXISTS test.fanout_exchange_{0}; + DROP TABLE IF EXISTS test.fanout_exchange_{0}_mv; + '''.format(consumer_id)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + assert int(result) == messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) @@ -1201,6 +1225,22 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): if int(result) == messages_num * num_tables + messages_num * num_tables: break + for consumer_id in range(num_tables): + instance.query(''' + DROP TABLE IF EXISTS test.topic_exchange_{0}; + DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; + '''.format(consumer_id)) + + for consumer_id in range(num_tables): + instance.query(''' + DROP TABLE IF EXISTS test.topic_exchange_{0}; + DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; + '''.format(num_tables + consumer_id)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + assert int(result) == messages_num * num_tables + messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) @@ -1371,6 +1411,12 @@ def produce(): for thread in threads: thread.join() + instance.query(''' + DROP TABLE IF EXISTS test.bindings_1; + DROP TABLE IF EXISTS test.bindings_2; + DROP TABLE IF EXISTS test.destination; + ''') + assert int(result) == messages_num * threads_num * 5 * 2, 'ClickHouse lost some messages: {}'.format(result) From c70f7778fc6144d22c69b3a1972b4720e3b6788e Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sat, 13 Jun 2020 23:05:13 +0300 Subject: [PATCH 0302/1102] trigger ci From dcd7b7351c23bbe01e81493ecbad6fc2504822e4 Mon Sep 17 00:00:00 2001 From: kssenii Date: Sat, 13 Jun 2020 21:37:37 +0000 Subject: [PATCH 0303/1102] Support headers-exchange type --- .../ReadBufferFromRabbitMQConsumer.cpp | 57 ++++++++++- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 10 +- .../integration/test_storage_rabbitmq/test.py | 97 +++++++++++++++++++ 3 files changed, 152 insertions(+), 12 deletions(-) diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 90485b28a968..31ca4f280e38 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "Poco/Timer.h" #include @@ -122,8 +123,7 @@ void ReadBufferFromRabbitMQConsumer::initExchange() else if (exchange_type == Exchange::DIRECT) type = AMQP::ExchangeType::direct; else if (exchange_type == Exchange::TOPIC) type = AMQP::ExchangeType::topic; else if (exchange_type == Exchange::HASH) type = AMQP::ExchangeType::consistent_hash; - else if (exchange_type == Exchange::HEADERS) - throw Exception("Headers exchange is not supported", ErrorCodes::BAD_ARGUMENTS); + else if (exchange_type == Exchange::HEADERS) type = AMQP::ExchangeType::headers; else throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages @@ -156,14 +156,37 @@ void ReadBufferFromRabbitMQConsumer::initExchange() LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); }); - for (auto & routing_key : routing_keys) + if (exchange_type == Exchange::HEADERS) { - consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_key).onError([&](const char * message) + AMQP::Table binding_arguments; + std::vector matching; + + for (auto & header : routing_keys) + { + boost::split(matching, header, [](char c){ return c == '='; }); + binding_arguments[matching[0]] = matching[1]; + matching.clear(); + } + + /// Routing key can be arbitrary here. + consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_keys[0], binding_arguments) + .onError([&](const char * message) { local_exchange_declared = false; LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); }); } + else + { + for (auto & routing_key : routing_keys) + { + consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_key).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); + }); + } + } } @@ -232,7 +255,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) */ String hash_exchange_name = exchange_type == Exchange::HASH ? exchange_name : local_exchange_name + "_hash"; - /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary + /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary. consumer_channel->bindQueue(hash_exchange_name, queue_name_, binding_key) .onSuccess([&] { @@ -244,6 +267,30 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", binding_key, message); }); } + else if (exchange_type == Exchange::HEADERS) + { + AMQP::Table binding_arguments; + std::vector matching; + + /// It is not parsed for the second time - if it was parsed above, then it would go to the first if statement, not here. + for (auto & header : routing_keys) + { + boost::split(matching, header, [](char c){ return c == '='; }); + binding_arguments[matching[0]] = matching[1]; + matching.clear(); + } + + consumer_channel->bindQueue(exchange_name, queue_name_, routing_keys[0], binding_arguments) + .onSuccess([&] + { + bindings_created = true; + }) + .onError([&](const char * message) + { + bindings_error = true; + LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", routing_keys[0], message); + }); + } else { /// Means there is only one queue with one consumer - no even distribution needed - no hash-exchange. diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index d3811bdb0d2d..852edd24726c 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -458,13 +458,9 @@ void registerStorageRabbitMQ(StorageFactory & factory) exchange_type = safeGet(ast->value); } - if (exchange_type != "fanout" && exchange_type != "direct" && exchange_type != "topic" && exchange_type != "consistent_hash") - { - if (exchange_type == "headers") - throw Exception("Headers exchange is not supported", ErrorCodes::BAD_ARGUMENTS); - else - throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); - } + if (exchange_type != "fanout" && exchange_type != "direct" && exchange_type != "topic" + && exchange_type != "headers" && exchange_type != "consistent_hash") + throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); } UInt64 num_consumers = rabbitmq_settings.rabbitmq_num_consumers; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index 8442a7ecb0a5..f58e898a45f2 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -1420,6 +1420,103 @@ def produce(): assert int(result) == messages_num * threads_num * 5 * 2, 'ClickHouse lost some messages: {}'.format(result) +@pytest.mark.timeout(420) +def test_rabbitmq_headers_exchange(rabbitmq_cluster): + instance.query(''' + DROP TABLE IF EXISTS test.destination; + CREATE TABLE test.destination(key UInt64, value UInt64, + _consumed_by LowCardinality(String)) + ENGINE = MergeTree() + ORDER BY key; + ''') + + num_tables_to_receive = 3 + for consumer_id in range(num_tables_to_receive): + print("Setting up table {}".format(consumer_id)) + instance.query(''' + DROP TABLE IF EXISTS test.headers_exchange_{0}; + DROP TABLE IF EXISTS test.headers_exchange_{0}_mv; + CREATE TABLE test.headers_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_num_consumers = 4, + rabbitmq_exchange_name = 'headers_exchange_testing', + rabbitmq_exchange_type = 'headers', + rabbitmq_routing_key_list = 'x-match=all,format=logs,type=report,year=2020', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.headers_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.headers_exchange_{0}; + '''.format(consumer_id)) + + num_tables_to_ignore = 2 + for consumer_id in range(num_tables_to_ignore): + print("Setting up table {}".format(consumer_id + num_tables_to_receive)) + instance.query(''' + DROP TABLE IF EXISTS test.headers_exchange_{0}; + DROP TABLE IF EXISTS test.headers_exchange_{0}_mv; + CREATE TABLE test.headers_exchange_{0} (key UInt64, value UInt64) + ENGINE = RabbitMQ + SETTINGS rabbitmq_host_port = 'rabbitmq1:5672', + rabbitmq_exchange_name = 'headers_exchange_testing', + rabbitmq_exchange_type = 'headers', + rabbitmq_routing_key_list = 'x-match=all,format=logs,type=report,year=2019', + rabbitmq_format = 'JSONEachRow', + rabbitmq_row_delimiter = '\\n'; + CREATE MATERIALIZED VIEW test.headers_exchange_{0}_mv TO test.destination AS + SELECT key, value, '{0}' as _consumed_by FROM test.headers_exchange_{0}; + '''.format(consumer_id + num_tables_to_receive)) + + i = [0] + messages_num = 1000 + + credentials = pika.PlainCredentials('root', 'clickhouse') + parameters = pika.ConnectionParameters('localhost', 5672, '/', credentials) + connection = pika.BlockingConnection(parameters) + channel = connection.channel() + channel.exchange_declare(exchange='headers_exchange_testing', exchange_type='headers') + + messages = [] + for _ in range(messages_num): + messages.append(json.dumps({'key': i[0], 'value': i[0]})) + i[0] += 1 + + fields={} + fields['format']='logs' + fields['type']='report' + fields['year']='2020' + + key_num = 0 + for message in messages: + channel.basic_publish(exchange='headers_exchange_testing', routing_key='', + properties=pika.BasicProperties(headers=fields), body=message) + + connection.close() + + while True: + result = instance.query('SELECT count() FROM test.destination') + time.sleep(1) + if int(result) == messages_num * num_tables_to_receive: + break + + for consumer_id in range(num_tables_to_receive): + instance.query(''' + DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; + DROP TABLE IF EXISTS test.direct_exchange_{0}; + '''.format(consumer_id)) + for consumer_id in range(num_tables_to_ignore): + instance.query(''' + DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; + DROP TABLE IF EXISTS test.direct_exchange_{0}; + '''.format(consumer_id + num_tables_to_receive)) + + instance.query(''' + DROP TABLE IF EXISTS test.destination; + ''') + + assert int(result) == messages_num * num_tables_to_receive, 'ClickHouse lost some messages: {}'.format(result) + + if __name__ == '__main__': cluster.start() raw_input("Cluster created, press any key to destroy...") From f9431e88861455ca2e93a39a164bb3c0a9914e76 Mon Sep 17 00:00:00 2001 From: Yuntao Wu Date: Sun, 14 Jun 2020 11:17:51 +0800 Subject: [PATCH 0304/1102] =?UTF-8?q?repacle=20means=20"=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E2=80=98=E2=80=99=20or=20=E2=80=9C=E4=BB=A3=E6=9B=BF=E2=80=9D?= =?UTF-8?q?=20in=20Chinese?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table-engines/mergetree-family/replacingmergetree.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh/engines/table-engines/mergetree-family/replacingmergetree.md b/docs/zh/engines/table-engines/mergetree-family/replacingmergetree.md index 8cf1ab8af57b..67d2f4dd56f3 100644 --- a/docs/zh/engines/table-engines/mergetree-family/replacingmergetree.md +++ b/docs/zh/engines/table-engines/mergetree-family/replacingmergetree.md @@ -1,4 +1,4 @@ -# 更换麦树 {#replacingmergetree} +# 替换合并树 {#replacingmergetree} 该引擎和[MergeTree](mergetree.md)的不同之处在于它会删除具有相同主键的重复项。 @@ -23,7 +23,7 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] 请求参数的描述,参考[请求参数](../../../engines/table-engines/mergetree-family/replacingmergetree.md)。 -**替换树参数** +**替换合并树参数** - `ver` — 版本列。类型为 `UInt*`, `Date` 或 `DateTime`。可选参数。 From ad321966f09e8801b902dffa650b8beb57924454 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sun, 14 Jun 2020 09:43:40 +0300 Subject: [PATCH 0305/1102] trigger ci From b8611cf46cd2d6f15f2a6e678961c06c686fa9ed Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sun, 14 Jun 2020 18:05:15 +0300 Subject: [PATCH 0306/1102] experiment --- tests/queries/0_stateless/00600_replace_running_query.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/00600_replace_running_query.sh b/tests/queries/0_stateless/00600_replace_running_query.sh index 1331dd3c15b7..75006cc56cec 100755 --- a/tests/queries/0_stateless/00600_replace_running_query.sh +++ b/tests/queries/0_stateless/00600_replace_running_query.sh @@ -36,5 +36,5 @@ wait ${CLICKHOUSE_CLIENT} --query_id=42 --query='SELECT 3, count() FROM system.numbers' 2>&1 | grep -cF 'was cancelled' & wait_for_query_to_start '42' ${CLICKHOUSE_CLIENT} --query_id=42 --replace_running_query=1 --replace_running_query_max_wait_ms=500 --query='SELECT 43' 2>&1 | grep -F "can't be stopped" > /dev/null -${CLICKHOUSE_CLIENT} --query_id=42 --replace_running_query=1 --query='SELECT 44' wait +${CLICKHOUSE_CLIENT} --query_id=42 --replace_running_query=1 --query='SELECT 44' From 93aee32ae4b5846a08069ccb1e7154c2b9418f8b Mon Sep 17 00:00:00 2001 From: Avogar Date: Sun, 14 Jun 2020 18:35:32 +0300 Subject: [PATCH 0307/1102] Add ORCBlockOutputFormat --- src/Formats/FormatFactory.cpp | 1 + src/Formats/FormatFactory.h | 4 +- .../Formats/Impl/ORCBlockOutputFormat.cpp | 409 ++++++++++++++++++ .../Formats/Impl/ORCBlockOutputFormat.h | 70 +++ .../01307_orc_output_format.reference | 6 + .../0_stateless/01307_orc_output_format.sh | 20 + 6 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp create mode 100644 src/Processors/Formats/Impl/ORCBlockOutputFormat.h create mode 100644 tests/queries/0_stateless/01307_orc_output_format.reference create mode 100755 tests/queries/0_stateless/01307_orc_output_format.sh diff --git a/src/Formats/FormatFactory.cpp b/src/Formats/FormatFactory.cpp index 9182c728600b..e1bb40c737cf 100644 --- a/src/Formats/FormatFactory.cpp +++ b/src/Formats/FormatFactory.cpp @@ -394,6 +394,7 @@ FormatFactory::FormatFactory() registerOutputFormatProcessorNull(*this); registerOutputFormatProcessorMySQLWrite(*this); registerOutputFormatProcessorMarkdown(*this); + registerOutputFormatProcessorORC(*this); } FormatFactory & FormatFactory::instance() diff --git a/src/Formats/FormatFactory.h b/src/Formats/FormatFactory.h index c8dd97aa9402..9c1a23d71640 100644 --- a/src/Formats/FormatFactory.h +++ b/src/Formats/FormatFactory.h @@ -175,6 +175,9 @@ void registerInputFormatProcessorTemplate(FormatFactory & factory); void registerOutputFormatProcessorTemplate(FormatFactory & factory); void registerInputFormatProcessorMsgPack(FormatFactory & factory); void registerOutputFormatProcessorMsgPack(FormatFactory & factory); +void registerInputFormatProcessorORC(FormatFactory & factory); +void registerOutputFormatProcessorORC(FormatFactory & factory); + /// File Segmentation Engines for parallel reading @@ -206,6 +209,5 @@ void registerOutputFormatProcessorMarkdown(FormatFactory & factory); void registerInputFormatProcessorCapnProto(FormatFactory & factory); void registerInputFormatProcessorRegexp(FormatFactory & factory); void registerInputFormatProcessorJSONAsString(FormatFactory & factory); -void registerInputFormatProcessorORC(FormatFactory & factory); } diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp new file mode 100644 index 000000000000..3745ee229a86 --- /dev/null +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -0,0 +1,409 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int ILLEGAL_COLUMN; +} + +ORCOutputStream::ORCOutputStream(WriteBuffer & out_) : out(out_) {} + +uint64_t ORCOutputStream::getLength() const +{ + return out.count(); +} + +uint64_t ORCOutputStream::getNaturalWriteSize() const +{ + out.nextIfAtEnd(); + return out.available(); +} + +void ORCOutputStream::write(const void* buf, size_t length) +{ + out.write(static_cast(buf), length); +} + +ORCBlockOutputFormat::ORCBlockOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & format_settings_) + : IOutputFormat(header_, out_), format_settings{format_settings_}, output_stream(out_), data_types(header_.getDataTypes()) +{ + schema = orc::createStructType(); + size_t columns_count = header_.columns(); + for (size_t i = 0; i != columns_count; ++i) + { + schema->addStructField(header_.safeGetByPosition(i).name, getORCType(data_types[i])); + } + writer = orc::createWriter(*schema, &output_stream, options); +} + +ORC_UNIQUE_PTR ORCBlockOutputFormat::getORCType(const DataTypePtr & type) +{ + switch (type->getTypeId()) + { + case TypeIndex::UInt8: [[fallthrough]]; + case TypeIndex::Int8: + { + return orc::createPrimitiveType(orc::TypeKind::BYTE); + } + case TypeIndex::UInt16: [[fallthrough]]; + case TypeIndex::Int16: + { + return orc::createPrimitiveType(orc::TypeKind::SHORT); + } + case TypeIndex::UInt32: [[fallthrough]]; + case TypeIndex::Int32: + { + return orc::createPrimitiveType(orc::TypeKind::INT); + } + case TypeIndex::UInt64: [[fallthrough]]; + case TypeIndex::Int64: + { + return orc::createPrimitiveType(orc::TypeKind::LONG); + } + case TypeIndex::Float32: + { + return orc::createPrimitiveType(orc::TypeKind::FLOAT); + } + case TypeIndex::Float64: + { + return orc::createPrimitiveType(orc::TypeKind::DOUBLE); + } + case TypeIndex::Date: + { + return orc::createPrimitiveType(orc::TypeKind::DATE); + } + case TypeIndex::DateTime: [[fallthrough]]; + case TypeIndex::DateTime64: + { + return orc::createPrimitiveType(orc::TypeKind::TIMESTAMP); + } + case TypeIndex::FixedString: [[fallthrough]]; + case TypeIndex::String: + { + return orc::createPrimitiveType(orc::TypeKind::STRING); + } + case TypeIndex::Nullable: + { + return getORCType(removeNullable(type)); + } + /* + case TypeIndex::Array: + { + const auto * array_type = typeid_cast(type.get()); + return orc::createListType(getORCType(array_type->getNestedType())); + } + */ + case TypeIndex::Decimal32: + { + const auto * decimal_type = typeid_cast *>(type.get()); + return orc::createDecimalType(decimal_type->getPrecision(), decimal_type->getScale()); + } + case TypeIndex::Decimal64: + { + const auto * decimal_type = typeid_cast *>(type.get()); + return orc::createDecimalType(decimal_type->getPrecision(), decimal_type->getScale()); + } + case TypeIndex::Decimal128: + { + const auto * decimal_type = typeid_cast *>(type.get()); + return orc::createDecimalType(decimal_type->getPrecision(), decimal_type->getScale()); + } + default: + { + throw Exception("Type " + type->getName() + " is not supported for ORC output format", ErrorCodes::ILLEGAL_COLUMN); + } + } +} + +template +void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + const PaddedPODArray * null_bytemap, + size_t rows_num) +{ + NumberVectorBatch * number_orc_column = dynamic_cast(orc_column); + const auto & number_column = assert_cast &>(column); + number_orc_column->resize(rows_num); + + for (size_t i = 0; i != rows_num; ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + number_orc_column->notNull[i] = 0; + continue; + } + number_orc_column->data[i] = number_column.getElement(i); + } + number_orc_column->numElements = rows_num; +} + +template +void ORCBlockOutputFormat::ORCBlockOutputFormat::writeDecimals( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + DataTypePtr & type, + const PaddedPODArray * null_bytemap, + size_t rows_num, + ConvertFunc convert) +{ + DecimalVectorBatch *decimal_orc_column = dynamic_cast(orc_column); + const auto & decimal_column = assert_cast &>(column); + const auto * decimal_type = typeid_cast *>(type.get()); + decimal_orc_column->precision = decimal_type->getPrecision(); + decimal_orc_column->scale = decimal_type->getScale(); + decimal_orc_column->resize(rows_num); + for (size_t i = 0; i != rows_num; ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + decimal_orc_column->notNull[i] = 0; + continue; + } + decimal_orc_column->values[i] = convert(decimal_column.getElement(i).value); + } + decimal_orc_column->numElements = rows_num; +} + +void ORCBlockOutputFormat::writeColumn( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + DataTypePtr & type, + const PaddedPODArray * null_bytemap, + size_t rows_num) +{ + if (null_bytemap) + { + orc_column->hasNulls = true; + } + switch (type->getTypeId()) + { + case TypeIndex::Int8: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::UInt8: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Int16: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Date: [[fallthrough]]; + case TypeIndex::UInt16: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Int32: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::UInt32: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Int64: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::UInt64: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Float32: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::Float64: + { + writeNumbers(orc_column, column, null_bytemap, rows_num); + break; + } + case TypeIndex::FixedString: [[fallthrough]]; + case TypeIndex::String: + { + orc::StringVectorBatch * string_orc_column = dynamic_cast(orc_column); + const auto & string_column = assert_cast(column); + string_orc_column->resize(rows_num); + + for (size_t i = 0; i != rows_num; ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + string_orc_column->notNull[i] = 0; + continue; + } + const StringRef & string = string_column.getDataAt(i); + string_orc_column->data[i] = const_cast(string.data); + string_orc_column->length[i] = string.size; + } + string_orc_column->numElements = rows_num; + break; + } + case TypeIndex::DateTime: + { + orc::TimestampVectorBatch * timestamp_orc_column = dynamic_cast(orc_column); + const auto & timestamp_column = assert_cast(column); + timestamp_orc_column->resize(rows_num); + + for (size_t i = 0; i != rows_num; ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + timestamp_orc_column->notNull[i] = 0; + continue; + } + timestamp_orc_column->data[i] = timestamp_column.getElement(i); + timestamp_orc_column->nanoseconds[i] = 0; + } + timestamp_orc_column->numElements = rows_num; + break; + } + case TypeIndex::DateTime64: + { + orc::TimestampVectorBatch * timestamp_orc_column = dynamic_cast(orc_column); + const auto & timestamp_column = assert_cast(column); + const auto * timestamp_type = assert_cast(type.get()); + + UInt32 scale = timestamp_type->getScale(); + timestamp_orc_column->resize(rows_num); + + for (size_t i = 0; i != rows_num; ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + timestamp_orc_column->notNull[i] = 0; + continue; + } + UInt64 value = timestamp_column.getElement(i); + timestamp_orc_column->data[i] = value / std::pow(10, scale); + timestamp_orc_column->nanoseconds[i] = (value % UInt64(std::pow(10, scale))) * std::pow(10, 9 - scale); + } + timestamp_orc_column->numElements = rows_num; + break; + } + case TypeIndex::Decimal32:; + { + writeDecimals( + orc_column, + column, + type, + null_bytemap, + rows_num, + [](Int32 value){ return value; }); + break; + } + case TypeIndex::Decimal64: + { + writeDecimals( + orc_column, + column, + type, + null_bytemap, + rows_num, + [](Int64 value){ return value; }); + break; + } + case TypeIndex::Decimal128: + { + writeDecimals( + orc_column, + column, + type, + null_bytemap, + rows_num, + [](Int128 value){ return orc::Int128(value >> 64, (value << 64) >> 64); }); + break; + } + case TypeIndex::Nullable: + { + const auto & nullable_column = assert_cast(column); + const PaddedPODArray & new_null_bytemap = assert_cast &>(*nullable_column.getNullMapColumnPtr()).getData(); + auto nested_type = removeNullable(type); + writeColumn(orc_column, nullable_column.getNestedColumn(), nested_type, &new_null_bytemap, rows_num); + break; + } + /* Doesn't work + case TypeIndex::Array: + { + orc::ListVectorBatch * list_orc_column = dynamic_cast(orc_column); + const auto & list_column = assert_cast(column); + auto nested_type = assert_cast(*type).getNestedType(); + const ColumnArray::Offsets & offsets = list_column.getOffsets(); + list_orc_column->resize(rows_num); + list_orc_column->offsets[0] = 0; + for (size_t i = 0; i != rows_num; ++i) + { + list_orc_column->offsets[i + 1] = offsets[i]; + } + const IColumn & nested_column = list_column.getData(); + orc::ColumnVectorBatch * nested_orc_column = list_orc_column->elements.get(); + writeColumn(nested_orc_column, nested_column, nested_type, null_bytemap, nested_column.size()); + list_orc_column->numElements = rows_num; + break; + } + */ + default: + throw Exception("Type " + type->getName() + " is not supported for ORC output format", ErrorCodes::ILLEGAL_COLUMN); + } +} + +void ORCBlockOutputFormat::consume(Chunk chunk) +{ + size_t columns_num = chunk.getNumColumns(); + size_t rows_num = chunk.getNumRows(); + ORC_UNIQUE_PTR batch = writer->createRowBatch(rows_num); + orc::StructVectorBatch *root = dynamic_cast(batch.get()); + for (size_t i = 0; i != columns_num; ++i) + { + writeColumn(root->fields[i], *chunk.getColumns()[i], data_types[i], nullptr, rows_num); + } + root->numElements = rows_num; + writer->add(*batch); +} + +void ORCBlockOutputFormat::finalize() +{ + writer->close(); +} + +void registerOutputFormatProcessorORC(FormatFactory & factory) +{ + factory.registerOutputFormatProcessor("ORC", []( + WriteBuffer & buf, + const Block & sample, + FormatFactory::WriteCallback, + const FormatSettings & format_settings) + { + return std::make_shared(buf, sample, format_settings); + }); +} + +} diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h new file mode 100644 index 000000000000..e075169b66f9 --- /dev/null +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace DB +{ + +class WriteBuffer; + +class ORCOutputStream : public orc::OutputStream +{ +public: + ORCOutputStream(WriteBuffer & out_); + + uint64_t getLength() const override; + uint64_t getNaturalWriteSize() const override; + void write(const void* buf, size_t length) override; + + void close() override {}; + const std::string& getName() const override { return "ORCOutputStream"; }; + +private: + WriteBuffer & out; +}; + +class ORCBlockOutputFormat : public IOutputFormat +{ +public: + ORCBlockOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & format_settings_); + + String getName() const override { return "ORCBlockOutputFormat"; } + void consume(Chunk chunk) override; + void finalize() override; + + String getContentType() const override { return "application/octet-stream"; } + +private: + ORC_UNIQUE_PTR getORCType(const DataTypePtr & type); + template + void writeDecimals( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + DataTypePtr & type, + const PaddedPODArray * null_bytemap, + size_t rows_num, + ConvertFunc convert); + template + void writeNumbers( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + const PaddedPODArray * null_bytemap, + size_t rows_num); + void writeColumn( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, DataTypePtr & type, + const PaddedPODArray * null_bytemap, + size_t rows_num); + + const FormatSettings format_settings; + ORCOutputStream output_stream; + DataTypes data_types; + ORC_UNIQUE_PTR writer; + ORC_UNIQUE_PTR schema; + orc::WriterOptions options; +}; + +} diff --git a/tests/queries/0_stateless/01307_orc_output_format.reference b/tests/queries/0_stateless/01307_orc_output_format.reference new file mode 100644 index 000000000000..bd62476c2dfd --- /dev/null +++ b/tests/queries/0_stateless/01307_orc_output_format.reference @@ -0,0 +1,6 @@ +255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2021-12-19 2021-12-19 03:00:00 2021-12-19 03:00:00.000 1.0001 1.0000000100 100000.00000000000001000000 1 +4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2024-10-04 2028-04-21 01:20:00 2021-12-19 03:14:51.000 34.1234 123123.1231231230 123123123.12312312312312300000 \N +42 42 42 42 42 42 42 42 42.42 42.42 42 1970-02-12 1970-01-01 03:00:42 0000-00-00 00:00:00.000 42.4200 42.4242424200 424242.42424242424242000000 42 +255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2021-12-19 2021-12-19 03:00:00 2021-12-19 03:00:00.000 1.0001 1.0000000100 100000.00000000000001000000 1 +4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2024-10-04 2028-04-21 01:20:00 2021-12-19 03:14:51.123 34.1234 123123.1231231230 123123123.12312312312312300000 \N +42 42 42 42 42 42 42 42 42.42 42.42 42 1970-02-12 1970-01-01 03:00:42 1970-01-01 03:00:00.042 42.4200 42.4242424200 424242.42424242424242000000 42 diff --git a/tests/queries/0_stateless/01307_orc_output_format.sh b/tests/queries/0_stateless/01307_orc_output_format.sh new file mode 100755 index 000000000000..8d7e85a03ded --- /dev/null +++ b/tests/queries/0_stateless/01307_orc_output_format.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +$CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS orc"; + +$CLICKHOUSE_CLIENT --query="CREATE TABLE orc (uint8 UInt8, uint16 UInt16, uint32 UInt32, uint64 UInt64, int8 Int8, int16 Int16, int32 Int32, int64 Int64, float Float32, double Float64, string String, date Date, datetime DateTime, datetime64 DateTime64, decimal32 Decimal32(4), decimal64 Decimal64(10), decimal128 Decimal128(20), nullable Nullable(Int32)) ENGINE = Memory"; + +$CLICKHOUSE_CLIENT --query="INSERT INTO orc VALUES (255, 65535, 4294967295, 100000000000, -128, -32768, -2147483648, -100000000000, 2.02, 10000.0000001, 'String', 18980, 1639872000, 1639872000000, 1.0001, 1.00000001, 100000.00000000000001, 1), (4, 1234, 3244467295, 500000000000, -1, -256, -14741221, -7000000000, 100.1, 14321.032141201, 'Another string', 20000, 1839882000, 1639872891123, 34.1234, 123123.123123123, 123123123.123123123123123, NULL), (42, 42, 42, 42, 42, 42, 42, 42, 42.42, 42.42, '42', 42, 42, 42, 42.42, 42.42424242, 424242.42424242424242, 42)"; + +$CLICKHOUSE_CLIENT --query="SELECT * FROM orc FORMAT ORC" > $CURDIR/tmp_orc_test_all_types.orc; + +cat $CURDIR/tmp_orc_test_all_types.orc | $CLICKHOUSE_CLIENT --query="INSERT INTO orc FORMAT ORC"; + +rm $CURDIR/tmp_orc_test_all_types.orc + +$CLICKHOUSE_CLIENT --query="SELECT * FROM orc"; + +$CLICKHOUSE_CLIENT --query="DROP TABLE orc"; From c30457a3edf98c61cd45b4c4269f3f5d4679e3ba Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Sun, 14 Jun 2020 20:34:59 +0300 Subject: [PATCH 0308/1102] trigger ci From 9e1b8b2872e366663707109eaee60a4df577865f Mon Sep 17 00:00:00 2001 From: kssenii Date: Sun, 14 Jun 2020 16:26:37 +0000 Subject: [PATCH 0309/1102] Better exchanges, fix build, better comments, better tests --- .../RabbitMQ/RabbitMQBlockInputStream.cpp | 2 - src/Storages/RabbitMQ/RabbitMQHandler.cpp | 13 +- .../ReadBufferFromRabbitMQConsumer.cpp | 132 ++++++++++-------- .../RabbitMQ/ReadBufferFromRabbitMQConsumer.h | 7 +- src/Storages/RabbitMQ/StorageRabbitMQ.cpp | 10 +- .../WriteBufferToRabbitMQProducer.cpp | 12 +- .../RabbitMQ/WriteBufferToRabbitMQProducer.h | 5 +- .../integration/test_storage_rabbitmq/test.py | 55 ++++---- 8 files changed, 120 insertions(+), 116 deletions(-) diff --git a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp index 2d995d97f18e..6257a60d678b 100644 --- a/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp +++ b/src/Storages/RabbitMQ/RabbitMQBlockInputStream.cpp @@ -142,8 +142,6 @@ Block RabbitMQBlockInputStream::readImpl() auto result_block = non_virtual_header.cloneWithColumns(std::move(result_columns)); auto virtual_block = virtual_header.cloneWithColumns(std::move(virtual_columns)); - LOG_DEBUG(log, "Total amount of rows is " + std::to_string(result_block.rows())); - for (const auto & column : virtual_block.getColumnsWithTypeAndName()) { result_block.insert(column); diff --git a/src/Storages/RabbitMQ/RabbitMQHandler.cpp b/src/Storages/RabbitMQ/RabbitMQHandler.cpp index 8667427ee632..71c23bb9bc41 100644 --- a/src/Storages/RabbitMQ/RabbitMQHandler.cpp +++ b/src/Storages/RabbitMQ/RabbitMQHandler.cpp @@ -41,8 +41,9 @@ void RabbitMQHandler::startConsumerLoop(std::atomic & loop_started) */ if (mutex_before_event_loop.try_lock_for(std::chrono::milliseconds(Lock_timeout))) { - loop_started = true; - stop_scheduled.store(false); + loop_started.store(true); + stop_scheduled = false; + event_base_loop(evbase, EVLOOP_NONBLOCK); mutex_before_event_loop.unlock(); } @@ -67,12 +68,8 @@ void RabbitMQHandler::stop() void RabbitMQHandler::stopWithTimeout() { - if (mutex_before_loop_stop.try_lock()) - { - stop_scheduled.store(true); - event_base_loopexit(evbase, &tv); - mutex_before_loop_stop.unlock(); - } + stop_scheduled = true; + event_base_loopexit(evbase, &tv); } } diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp index 31ca4f280e38..ef4398753c27 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.cpp @@ -19,7 +19,7 @@ namespace ErrorCodes extern const int BAD_ARGUMENTS; } -namespace Exchange +namespace ExchangeType { /// Note that default here means default by implementation and not by rabbitmq settings static const String DEFAULT = "default"; @@ -42,7 +42,7 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( const bool bind_by_id_, const size_t num_queues_, const String & exchange_type_, - const String & local_exchange_name_, + const String & local_exchange_, const std::atomic & stopped_) : ReadBuffer(nullptr, 0) , consumer_channel(std::move(consumer_channel_)) @@ -55,13 +55,15 @@ ReadBufferFromRabbitMQConsumer::ReadBufferFromRabbitMQConsumer( , bind_by_id(bind_by_id_) , num_queues(num_queues_) , exchange_type(exchange_type_) - , local_exchange_name(local_exchange_name_) + , local_exchange(local_exchange_) + , local_default_exchange(local_exchange + "_" + ExchangeType::DIRECT) + , local_hash_exchange(local_exchange + "_" + ExchangeType::HASH) , stopped(stopped_) { messages.clear(); current = messages.begin(); - exchange_type_set = exchange_type != Exchange::DEFAULT; + exchange_type_set = exchange_type != ExchangeType::DEFAULT; /* One queue per consumer can handle up to 50000 messages. More queues per consumer can be added. * By default there is one queue per consumer. @@ -87,53 +89,52 @@ ReadBufferFromRabbitMQConsumer::~ReadBufferFromRabbitMQConsumer() void ReadBufferFromRabbitMQConsumer::initExchange() { /* This direct-exchange is used for default implemenation and for INSERT query (so it is always declared). If exchange_type - * is not set, then there are only two exchanges - external, defined by the client, and local, unique for each table. + * is not set, then there are only two exchanges - external, defined by the client, and local, unique for each table (default). * This strict division to external and local exchanges is needed to avoid too much complexity with defining exchange_name - * for INSERT query producer and, in general, it is much better to distinguish them into separate ones. + * for INSERT query producer and, in general, it is better to distinguish them into separate ones. */ - String default_exchange = exchange_type_set ? exchange_name + "_" + Exchange::DEFAULT : exchange_name; - consumer_channel->declareExchange(default_exchange, AMQP::fanout).onError([&](const char * message) + consumer_channel->declareExchange(local_default_exchange, AMQP::direct).onError([&](const char * message) { local_exchange_declared = false; - LOG_ERROR(log, "Failed to declare exchange {}. Reason: {}", default_exchange, message); + LOG_ERROR(log, "Failed to declare local direct-exchange. Reason: {}", message); }); - default_local_exchange = local_exchange_name; - default_local_exchange += exchange_type_set ? "_default_" + Exchange::DIRECT : "_" + Exchange::DIRECT; - consumer_channel->declareExchange(default_local_exchange, AMQP::direct).onError([&](const char * message) + if (!exchange_type_set) { - local_exchange_declared = false; - LOG_ERROR(log, "Failed to declare exchange {}. Reason: {}", default_local_exchange, message); - }); + consumer_channel->declareExchange(exchange_name, AMQP::fanout).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to declare default fanout-exchange. Reason: {}", message); + }); - /// With fanout exchange the binding key is ignored - a parameter might be arbitrary. All distribution lies on local_exchange. - consumer_channel->bindExchange(default_exchange, default_local_exchange, routing_keys[0]).onError([&](const char * message) - { - local_exchange_declared = false; - LOG_ERROR(log, "Failed to bind {} exchange to {} exchange. Reason: {}", default_exchange, default_local_exchange, message); - }); + /// With fanout exchange the binding key is ignored - a parameter might be arbitrary. All distribution lies on local_exchange. + consumer_channel->bindExchange(exchange_name, local_default_exchange, routing_keys[0]).onError([&](const char * message) + { + local_exchange_declared = false; + LOG_ERROR(log, "Failed to bind local direct-exchange to fanout-exchange. Reason: {}", message); + }); - if (!exchange_type_set) return; + } /// For special purposes to use the flexibility of routing provided by rabbitmq - choosing exchange types is supported. AMQP::ExchangeType type; - if (exchange_type == Exchange::FANOUT) type = AMQP::ExchangeType::fanout; - else if (exchange_type == Exchange::DIRECT) type = AMQP::ExchangeType::direct; - else if (exchange_type == Exchange::TOPIC) type = AMQP::ExchangeType::topic; - else if (exchange_type == Exchange::HASH) type = AMQP::ExchangeType::consistent_hash; - else if (exchange_type == Exchange::HEADERS) type = AMQP::ExchangeType::headers; + if (exchange_type == ExchangeType::FANOUT) type = AMQP::ExchangeType::fanout; + else if (exchange_type == ExchangeType::DIRECT) type = AMQP::ExchangeType::direct; + else if (exchange_type == ExchangeType::TOPIC) type = AMQP::ExchangeType::topic; + else if (exchange_type == ExchangeType::HASH) type = AMQP::ExchangeType::consistent_hash; + else if (exchange_type == ExchangeType::HEADERS) type = AMQP::ExchangeType::headers; else throw Exception("Invalid exchange type", ErrorCodes::BAD_ARGUMENTS); - /* Declare exchange of the specified type and bind it to hash-exchange, which will evenly distribute messages - * between all consumers. (This enables better scaling as without hash-exchange - the only option to avoid getting - * the same messages more than once - is having only one consumer with one queue, which is not good.) + /* Declare client's exchange of the specified type and bind it to hash-exchange (if it is not already hash-exchange), which + * will evenly distribute messages between all consumers. (This enables better scaling as without hash-exchange - the only + * option to avoid getting the same messages more than once - is having only one consumer with one queue, which is not good.) */ consumer_channel->declareExchange(exchange_name, type).onError([&](const char * message) { local_exchange_declared = false; - LOG_ERROR(log, "Failed to declare client's {} exchange: {}", exchange_type, message); + LOG_ERROR(log, "Failed to declare client's {} exchange. Reason: {}", exchange_type, message); }); /// No need for declaring hash-exchange if there is only one consumer with one queue or exchange type is already hash @@ -142,26 +143,32 @@ void ReadBufferFromRabbitMQConsumer::initExchange() hash_exchange = true; - if (exchange_type == Exchange::HASH) + if (exchange_type == ExchangeType::HASH) return; - AMQP::Table exchange_arguments; - exchange_arguments["hash-property"] = "message_id"; + /* By default hash exchange distributes messages based on a hash value of a routing key, which must be a string integer. But + * in current case we use hash exchange for binding to another exchange of some other type, which needs its own routing keys + * of other types: headers, patterns and string-keys. This means that hash property must be changed. + */ + AMQP::Table binding_arguments; + binding_arguments["hash-property"] = "message_id"; - String local_hash_exchange_name = local_exchange_name + "_hash"; - consumer_channel->declareExchange(local_hash_exchange_name, AMQP::consistent_hash, exchange_arguments) + /// Declare exchange for sharding. + consumer_channel->declareExchange(local_hash_exchange, AMQP::consistent_hash, binding_arguments) .onError([&](const char * message) { local_exchange_declared = false; LOG_ERROR(log, "Failed to declare {} exchange: {}", exchange_type, message); }); - if (exchange_type == Exchange::HEADERS) + /// Then bind client's exchange to sharding exchange (by keys, specified by the client): + + if (exchange_type == ExchangeType::HEADERS) { AMQP::Table binding_arguments; std::vector matching; - for (auto & header : routing_keys) + for (const auto & header : routing_keys) { boost::split(matching, header, [](char c){ return c == '='; }); binding_arguments[matching[0]] = matching[1]; @@ -169,21 +176,21 @@ void ReadBufferFromRabbitMQConsumer::initExchange() } /// Routing key can be arbitrary here. - consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_keys[0], binding_arguments) + consumer_channel->bindExchange(exchange_name, local_hash_exchange, routing_keys[0], binding_arguments) .onError([&](const char * message) { local_exchange_declared = false; - LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); + LOG_ERROR(log, "Failed to bind local hash exchange to client's exchange. Reason: {}", message); }); } else { - for (auto & routing_key : routing_keys) + for (const auto & routing_key : routing_keys) { - consumer_channel->bindExchange(exchange_name, local_hash_exchange_name, routing_key).onError([&](const char * message) + consumer_channel->bindExchange(exchange_name, local_hash_exchange, routing_key).onError([&](const char * message) { local_exchange_declared = false; - LOG_ERROR(log, "Failed to bind {} exchange to {} exchange: {}", local_exchange_name, exchange_name, message); + LOG_ERROR(log, "Failed to bind local hash exchange to client's exchange. Reason: {}", message); }); } } @@ -227,7 +234,8 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) } } - consumer_channel->bindQueue(default_local_exchange, queue_name_, binding_key) + /// Bind queue to exchange that is used for INSERT query and also for default implementation. + consumer_channel->bindQueue(local_default_exchange, queue_name_, binding_key) .onSuccess([&] { default_bindings_created = true; @@ -238,13 +246,13 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) LOG_ERROR(log, "Failed to bind to key {}. Reason: {}", binding_key, message); }); - /* Subscription can probably be moved back to readPrefix(), but not sure whether it is better in regard to speed. Also note - * that if moved there, it must(!) be wrapped inside a channel->onReady callback or any other, otherwise consumer might fail - * to subscribe and no resubscription will help. + /* Subscription can probably be moved back to readPrefix(), but not sure whether it is better in regard to speed, because + * if moved there, it must(!) be wrapped inside a channel->onReady callback or any other (and the looping), otherwise + * consumer might fail to subscribe and no resubscription will help. */ subscribe(queues.back()); - LOG_TRACE(log, "Queue " + queue_name_ + " is bound by key " + binding_key); + LOG_DEBUG(log, "Queue " + queue_name_ + " is declared"); if (exchange_type_set) { @@ -253,10 +261,10 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) /* If exchange_type == hash, then bind directly to this client's exchange (because there is no need for a distributor * exchange as it is already hash-exchange), otherwise hash-exchange is a local distributor exchange. */ - String hash_exchange_name = exchange_type == Exchange::HASH ? exchange_name : local_exchange_name + "_hash"; + String current_hash_exchange = exchange_type == ExchangeType::HASH ? exchange_name : local_hash_exchange; /// If hash-exchange is used for messages distribution, then the binding key is ignored - can be arbitrary. - consumer_channel->bindQueue(hash_exchange_name, queue_name_, binding_key) + consumer_channel->bindQueue(current_hash_exchange, queue_name_, binding_key) .onSuccess([&] { bindings_created = true; @@ -267,13 +275,13 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", binding_key, message); }); } - else if (exchange_type == Exchange::HEADERS) + else if (exchange_type == ExchangeType::HEADERS) { AMQP::Table binding_arguments; std::vector matching; /// It is not parsed for the second time - if it was parsed above, then it would go to the first if statement, not here. - for (auto & header : routing_keys) + for (const auto & header : routing_keys) { boost::split(matching, header, [](char c){ return c == '='; }); binding_arguments[matching[0]] = matching[1]; @@ -288,15 +296,15 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", routing_keys[0], message); + LOG_ERROR(log, "Failed to bind queue to key. Reason: {}", message); }); } else { /// Means there is only one queue with one consumer - no even distribution needed - no hash-exchange. - for (auto & routing_key : routing_keys) + for (const auto & routing_key : routing_keys) { - /// Binding directly to exchange, specified by the client + /// Binding directly to exchange, specified by the client. consumer_channel->bindQueue(exchange_name, queue_name_, routing_key) .onSuccess([&] { @@ -305,7 +313,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { bindings_error = true; - LOG_ERROR(log, "Failed to create queue binding to key {}. Reason: {}", routing_key, message); + LOG_ERROR(log, "Failed to bind queue to key. Reason: {}", message); }); } } @@ -314,7 +322,7 @@ void ReadBufferFromRabbitMQConsumer::initQueueBindings(const size_t queue_id) .onError([&](const char * message) { default_bindings_error = true; - LOG_ERROR(log, "Failed to declare queue on the channel: {}", message); + LOG_ERROR(log, "Failed to declare queue on the channel. Reason: {}", message); }); /* Run event loop (which updates local variables in a separate thread) until bindings are created or failed to be created. @@ -364,7 +372,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) * executing all callbacks on the connection (not only its own), then there should be some point to unblock. * loop_started == 1 if current consumer is started the loop and not another. */ - if (!loop_started.load() && !eventHandler.checkStopIsScheduled().load()) + if (!loop_started.load() && !eventHandler.checkStopIsScheduled()) { stopEventLoopWithTimeout(); } @@ -373,7 +381,7 @@ void ReadBufferFromRabbitMQConsumer::subscribe(const String & queue_name) .onError([&](const char * message) { consumer_error = true; - LOG_ERROR(log, "Consumer {} failed: {}", channel_id, message); + LOG_ERROR(log, "Consumer {} failed. Reason: {}", channel_id, message); }); } @@ -385,7 +393,7 @@ void ReadBufferFromRabbitMQConsumer::checkSubscription() wait_subscribed = num_queues; - /// These variables are updated in a separate thread + /// These variables are updated in a separate thread. while (count_subscribed != wait_subscribed && !consumer_error) { startEventLoop(loop_started); @@ -393,11 +401,11 @@ void ReadBufferFromRabbitMQConsumer::checkSubscription() LOG_TRACE(log, "Consumer {} is subscribed to {} queues", channel_id, count_subscribed); - /// Updated in callbacks which are run by the loop + /// Updated in callbacks which are run by the loop. if (count_subscribed == num_queues) return; - /// A case that should never normally happen + /// A case that should never normally happen. for (auto & queue : queues) { subscribe(queue); diff --git a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h index 6a2c847357d2..d4bf35c00b81 100644 --- a/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h +++ b/src/Storages/RabbitMQ/ReadBufferFromRabbitMQConsumer.h @@ -32,7 +32,7 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const bool bind_by_id_, const size_t num_queues_, const String & exchange_type_, - const String & local_exchange_name_, + const String & local_exchange_, const std::atomic & stopped_); ~ReadBufferFromRabbitMQConsumer() override; @@ -53,8 +53,11 @@ class ReadBufferFromRabbitMQConsumer : public ReadBuffer const size_t channel_id; const bool bind_by_id; const size_t num_queues; + const String & exchange_type; - const String & local_exchange_name; + const String & local_exchange; + const String local_default_exchange; + const String local_hash_exchange; Poco::Logger * log; char row_delimiter; diff --git a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp index 852edd24726c..3de8d1933027 100644 --- a/src/Storages/RabbitMQ/StorageRabbitMQ.cpp +++ b/src/Storages/RabbitMQ/StorageRabbitMQ.cpp @@ -107,6 +107,8 @@ StorageRabbitMQ::StorageRabbitMQ( auto table_id = getStorageID(); String table_name = table_id.table_name; + + /// Make sure that local exchange name is unique for each table and is not the same as client's exchange name local_exchange_name = exchange_name + "_" + table_name; } @@ -132,6 +134,7 @@ Pipes StorageRabbitMQ::read( } LOG_DEBUG(log, "Starting reading {} streams", pipes.size()); + return pipes; } @@ -225,12 +228,7 @@ ConsumerBufferPtr StorageRabbitMQ::createReadBuffer() ProducerBufferPtr StorageRabbitMQ::createWriteBuffer() { - /* If exchange type is set, then there are different exchanges for external publishing and for INSERT query - * as in this case they are of different types. - */ - String producer_exchange = exchange_type == "default" ? local_exchange_name : local_exchange_name + "_default"; - - return std::make_shared(parsed_address, login_password, routing_keys[0], producer_exchange, + return std::make_shared(parsed_address, login_password, routing_keys[0], local_exchange_name, log, num_consumers * num_queues, bind_by_id, use_transactional_channel, row_delimiter ? std::optional{row_delimiter} : std::nullopt, 1, 1024); } diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp index 09179b95a153..6d74e2c8298e 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.cpp @@ -77,7 +77,7 @@ WriteBufferToRabbitMQProducer::WriteBufferToRabbitMQProducer( WriteBufferToRabbitMQProducer::~WriteBufferToRabbitMQProducer() { - finilize(); + finilizeProducer(); connection.close(); assert(rows == 0 && chunks.empty()); } @@ -118,7 +118,9 @@ void WriteBufferToRabbitMQProducer::countRow() ++message_counter; - /// run event loop to actually publish, checking exchange is just a point to stop the event loop + /* Run event loop to actually publish, checking exchange is just a point to stop the event loop. Messages are not sent + * without looping and looping after every batch is much better than processing all the messages in one time. + */ if ((message_counter %= Batch) == 0) { checkExchange(); @@ -132,7 +134,7 @@ void WriteBufferToRabbitMQProducer::checkExchange() std::atomic exchange_declared = false, exchange_error = false; /* The AMQP::passive flag indicates that it should only be checked if there is a valid exchange with the given name - * and makes it visible from current producer_channel. + * and makes it declared on the current producer_channel. */ producer_channel->declareExchange(exchange_name, AMQP::direct, AMQP::passive) .onSuccess([&]() @@ -142,7 +144,7 @@ void WriteBufferToRabbitMQProducer::checkExchange() .onError([&](const char * message) { exchange_error = true; - LOG_ERROR(log, "Exchange was not declared: {}", message); + LOG_ERROR(log, "Exchange for INSERT query was not declared. Reason: {}", message); }); /// These variables are updated in a separate thread and starting the loop blocks current thread @@ -153,7 +155,7 @@ void WriteBufferToRabbitMQProducer::checkExchange() } -void WriteBufferToRabbitMQProducer::finilize() +void WriteBufferToRabbitMQProducer::finilizeProducer() { checkExchange(); diff --git a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h index 9fd36257561b..7d2bb6e598f4 100644 --- a/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h +++ b/src/Storages/RabbitMQ/WriteBufferToRabbitMQProducer.h @@ -40,7 +40,7 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer private: void nextImpl() override; void checkExchange(); - void finilize(); + void finilizeProducer(); std::pair & login_password; const String routing_key; @@ -56,9 +56,6 @@ class WriteBufferToRabbitMQProducer : public WriteBuffer size_t next_queue = 0; UInt64 message_counter = 0; - String channel_id; - - Messages messages; Poco::Logger * log; const std::optional delim; diff --git a/tests/integration/test_storage_rabbitmq/test.py b/tests/integration/test_storage_rabbitmq/test.py index f58e898a45f2..3c4c0b3215b0 100644 --- a/tests/integration/test_storage_rabbitmq/test.py +++ b/tests/integration/test_storage_rabbitmq/test.py @@ -497,7 +497,7 @@ def test_rabbitmq_big_message(rabbitmq_cluster): assert int(result) == rabbitmq_messages*batch_messages, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) +@pytest.mark.timeout(420) def test_rabbitmq_sharding_between_channels_publish(rabbitmq_cluster): NUM_CHANNELS = 5 @@ -560,7 +560,7 @@ def produce(): assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) +@pytest.mark.timeout(420) def test_rabbitmq_sharding_between_queues_publish(rabbitmq_cluster): NUM_QUEUES = 4 @@ -623,7 +623,7 @@ def produce(): assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) +@pytest.mark.timeout(420) def test_rabbitmq_sharding_between_channels_and_queues_publish(rabbitmq_cluster): NUM_CONSUMERS = 10 @@ -688,7 +688,7 @@ def produce(): assert int(result) == messages_num * threads_num, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) +@pytest.mark.timeout(420) def test_rabbitmq_read_only_combo(rabbitmq_cluster): NUM_MV = 5; @@ -768,7 +768,7 @@ def produce(): assert int(result) == messages_num * threads_num * NUM_MV, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(180) +@pytest.mark.timeout(240) def test_rabbitmq_insert(rabbitmq_cluster): instance.query(''' CREATE TABLE test.rabbitmq (key UInt64, value UInt64) @@ -1054,7 +1054,10 @@ def test_rabbitmq_direct_exchange(rabbitmq_cluster): key = "direct_" + str(key_num) key_num += 1 for message in messages: - channel.basic_publish(exchange='direct_exchange_testing', routing_key=key, body=message) + mes_id = str(randrange(10)) + channel.basic_publish( + exchange='direct_exchange_testing', routing_key=key, + properties=pika.BasicProperties(message_id=mes_id), body=message) connection.close() @@ -1066,8 +1069,8 @@ def test_rabbitmq_direct_exchange(rabbitmq_cluster): for consumer_id in range(num_tables): instance.query(''' - DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; DROP TABLE IF EXISTS test.direct_exchange_{0}; + DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; '''.format(consumer_id)) instance.query(''' @@ -1122,7 +1125,10 @@ def test_rabbitmq_fanout_exchange(rabbitmq_cluster): key_num = 0 for message in messages: - channel.basic_publish(exchange='fanout_exchange_testing', routing_key='', body=message) + mes_id = str(randrange(10)) + channel.basic_publish( + exchange='fanout_exchange_testing', routing_key='', + properties=pika.BasicProperties(message_id=mes_id), body=message) connection.close() @@ -1215,7 +1221,10 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): key = "random.logs" for message in messages: - channel.basic_publish(exchange='topic_exchange_testing', routing_key=key, body=message) + mes_id = str(randrange(10)) + channel.basic_publish( + exchange='topic_exchange_testing', routing_key=key, + properties=pika.BasicProperties(message_id=mes_id), body=message) connection.close() @@ -1225,18 +1234,12 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): if int(result) == messages_num * num_tables + messages_num * num_tables: break - for consumer_id in range(num_tables): + for consumer_id in range(num_tables * 2): instance.query(''' DROP TABLE IF EXISTS test.topic_exchange_{0}; DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; '''.format(consumer_id)) - for consumer_id in range(num_tables): - instance.query(''' - DROP TABLE IF EXISTS test.topic_exchange_{0}; - DROP TABLE IF EXISTS test.topic_exchange_{0}_mv; - '''.format(num_tables + consumer_id)) - instance.query(''' DROP TABLE IF EXISTS test.destination; ''') @@ -1244,7 +1247,7 @@ def test_rabbitmq_topic_exchange(rabbitmq_cluster): assert int(result) == messages_num * num_tables + messages_num * num_tables, 'ClickHouse lost some messages: {}'.format(result) -@pytest.mark.timeout(320) +@pytest.mark.timeout(420) def test_rabbitmq_hash_exchange(rabbitmq_cluster): instance.query(''' DROP TABLE IF EXISTS test.destination; @@ -1288,8 +1291,8 @@ def produce(): for _ in range(messages_num): messages.append(json.dumps({'key': i[0], 'value': i[0]})) i[0] += 1 - key = str(randrange(10)) for message in messages: + key = str(randrange(10)) channel.basic_publish(exchange='hash_exchange_testing', routing_key=key, body=message) connection.close() @@ -1389,7 +1392,9 @@ def produce(): for key in keys: for message in messages: - channel.basic_publish(exchange='multiple_bindings_testing', routing_key=key, body=message) + mes_id = str(randrange(10)) + channel.basic_publish(exchange='multiple_bindings_testing', routing_key=key, + properties=pika.BasicProperties(message_id=mes_id), body=message) connection.close() @@ -1488,8 +1493,9 @@ def test_rabbitmq_headers_exchange(rabbitmq_cluster): key_num = 0 for message in messages: + mes_id = str(randrange(10)) channel.basic_publish(exchange='headers_exchange_testing', routing_key='', - properties=pika.BasicProperties(headers=fields), body=message) + properties=pika.BasicProperties(headers=fields, message_id=mes_id), body=message) connection.close() @@ -1499,16 +1505,11 @@ def test_rabbitmq_headers_exchange(rabbitmq_cluster): if int(result) == messages_num * num_tables_to_receive: break - for consumer_id in range(num_tables_to_receive): + for consumer_id in range(num_tables_to_receive + num_tables_to_ignore): instance.query(''' - DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; DROP TABLE IF EXISTS test.direct_exchange_{0}; - '''.format(consumer_id)) - for consumer_id in range(num_tables_to_ignore): - instance.query(''' DROP TABLE IF EXISTS test.direct_exchange_{0}_mv; - DROP TABLE IF EXISTS test.direct_exchange_{0}; - '''.format(consumer_id + num_tables_to_receive)) + '''.format(consumer_id)) instance.query(''' DROP TABLE IF EXISTS test.destination; From c33b472f9a02c19b65cea68f57a6fcfa5e59be66 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Mon, 15 Jun 2020 17:25:42 +0300 Subject: [PATCH 0310/1102] fixup --- programs/benchmark/Benchmark.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index 6884f6faed36..590e1496fd60 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -363,7 +363,6 @@ class Benchmark : public Poco::Util::Application try { execute(connection_entries, query, distribution(generator)); - ++queries_executed; } catch (...) { @@ -374,7 +373,14 @@ class Benchmark : public Poco::Util::Application shutdown = true; throw; } + else + { + std::cerr << getCurrentExceptionMessage(print_stacktrace, true) ; + } } + // Count failed queries toward executed, so that we'd reach + // max_iterations even if every run fails. + ++queries_executed; } } From b725df63667f7cb55a0ecc6d4ebbd4723385a7da Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 15 Jun 2020 18:17:44 +0300 Subject: [PATCH 0311/1102] Added ReadFromStorageStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 82 +--------- .../QueryPlan/ReadFromStorageStep.cpp | 154 ++++++++++++++++++ .../QueryPlan/ReadFromStorageStep.h | 50 ++++++ .../Transforms/ExpressionTransform.cpp | 2 +- .../Transforms/ExpressionTransform.h | 2 + src/Processors/Transforms/FilterTransform.cpp | 2 +- src/Processors/Transforms/FilterTransform.h | 6 + src/Processors/ya.make | 1 + 8 files changed, 220 insertions(+), 79 deletions(-) create mode 100644 src/Processors/QueryPlan/ReadFromStorageStep.cpp create mode 100644 src/Processors/QueryPlan/ReadFromStorageStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 23cb753e96fd..cadeb57c03ca 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -71,6 +71,7 @@ #include #include #include +#include namespace DB @@ -1273,84 +1274,11 @@ void InterpreterSelectQuery::executeFetchColumns( query_info.input_sorting_info = query_info.order_by_optimizer->getInputOrder(storage); } - Pipes pipes = storage->read(required_columns, query_info, *context, processing_stage, max_block_size, max_streams); + ReadFromStorageStep read_step( + table_lock, options, storage, + required_columns, query_info, *context, processing_stage, max_block_size, max_streams); - if (pipes.empty()) - { - Pipe pipe(std::make_shared(storage->getSampleBlockForColumns(required_columns))); - - if (query_info.prewhere_info) - { - if (query_info.prewhere_info->alias_actions) - pipe.addSimpleTransform(std::make_shared( - pipe.getHeader(), query_info.prewhere_info->alias_actions)); - - pipe.addSimpleTransform(std::make_shared( - pipe.getHeader(), - prewhere_info->prewhere_actions, - prewhere_info->prewhere_column_name, - prewhere_info->remove_prewhere_column)); - - // To remove additional columns - // In some cases, we did not read any marks so that the pipeline.streams is empty - // Thus, some columns in prewhere are not removed as expected - // This leads to mismatched header in distributed table - if (query_info.prewhere_info->remove_columns_actions) - pipe.addSimpleTransform(std::make_shared(pipe.getHeader(), query_info.prewhere_info->remove_columns_actions)); - } - - pipes.emplace_back(std::move(pipe)); - } - - /// Table lock is stored inside pipeline here. - pipeline.addTableLock(table_lock); - - /// Set the limits and quota for reading data, the speed and time of the query. - { - IBlockInputStream::LocalLimits limits; - limits.mode = IBlockInputStream::LIMITS_TOTAL; - limits.size_limits = SizeLimits(settings.max_rows_to_read, settings.max_bytes_to_read, settings.read_overflow_mode); - limits.speed_limits.max_execution_time = settings.max_execution_time; - limits.timeout_overflow_mode = settings.timeout_overflow_mode; - - /** Quota and minimal speed restrictions are checked on the initiating server of the request, and not on remote servers, - * because the initiating server has a summary of the execution of the request on all servers. - * - * But limits on data size to read and maximum execution time are reasonable to check both on initiator and - * additionally on each remote server, because these limits are checked per block of data processed, - * and remote servers may process way more blocks of data than are received by initiator. - * - * The limits to throttle maximum execution speed is also checked on all servers. - */ - if (options.to_stage == QueryProcessingStage::Complete) - { - limits.speed_limits.min_execution_rps = settings.min_execution_speed; - limits.speed_limits.min_execution_bps = settings.min_execution_speed_bytes; - } - - limits.speed_limits.max_execution_rps = settings.max_execution_speed; - limits.speed_limits.max_execution_bps = settings.max_execution_speed_bytes; - limits.speed_limits.timeout_before_checking_execution_speed = settings.timeout_before_checking_execution_speed; - - auto quota = context->getQuota(); - - for (auto & pipe : pipes) - { - if (!options.ignore_limits) - pipe.setLimits(limits); - - if (!options.ignore_quota && (options.to_stage == QueryProcessingStage::Complete)) - pipe.setQuota(quota); - } - } - - if (pipes.size() == 1) - pipeline.setMaxThreads(1); - - for (auto & pipe : pipes) - pipe.enableQuota(); - - pipeline.init(std::move(pipes)); + pipeline = std::move(*read_step.updatePipeline({})); } else throw Exception("Logical error in InterpreterSelectQuery: nowhere to read", ErrorCodes::LOGICAL_ERROR); diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.cpp b/src/Processors/QueryPlan/ReadFromStorageStep.cpp new file mode 100644 index 000000000000..191f501e9faf --- /dev/null +++ b/src/Processors/QueryPlan/ReadFromStorageStep.cpp @@ -0,0 +1,154 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +ReadFromStorageStep::ReadFromStorageStep( + TableStructureReadLockHolder table_lock_, + SelectQueryOptions options_, + StoragePtr storage_, + const Names & required_columns_, + const SelectQueryInfo & query_info_, + const Context & context_, + QueryProcessingStage::Enum processing_stage_, + size_t max_block_size_, + size_t max_streams_) + : table_lock(std::move(table_lock_)) + , options(std::move(options_)) + , storage(std::move(storage_)) + , required_columns(required_columns_) + , query_info(query_info_) + , context(context_) + , processing_stage(processing_stage_) + , max_block_size(max_block_size_) + , max_streams(max_streams_) +{ + Block header = storage->getSampleBlockForColumns(required_columns); + + if (query_info.prewhere_info) + { + if (query_info.prewhere_info->alias_actions) + header = ExpressionTransform::transformHeader(std::move(header), query_info.prewhere_info->alias_actions); + + header = FilterTransform::transformHeader( + std::move(header), + query_info.prewhere_info->prewhere_actions, + query_info.prewhere_info->prewhere_column_name, + query_info.prewhere_info->remove_prewhere_column); + + if (query_info.prewhere_info->remove_columns_actions) + header = ExpressionTransform::transformHeader( + std::move(header), + query_info.prewhere_info->remove_columns_actions); + } + + input_streams.emplace_back(DataStream{.header = std::move(header)}); +} + +ReadFromStorageStep::~ReadFromStorageStep() = default; + +QueryPipelinePtr ReadFromStorageStep::updatePipeline(QueryPipelines) +{ + Pipes pipes = storage->read(required_columns, query_info, context, processing_stage, max_block_size, max_streams); + + if (pipes.empty()) + { + Pipe pipe(std::make_shared(storage->getSampleBlockForColumns(required_columns))); + + if (query_info.prewhere_info) + { + if (query_info.prewhere_info->alias_actions) + pipe.addSimpleTransform(std::make_shared( + pipe.getHeader(), query_info.prewhere_info->alias_actions)); + + pipe.addSimpleTransform(std::make_shared( + pipe.getHeader(), + query_info.prewhere_info->prewhere_actions, + query_info.prewhere_info->prewhere_column_name, + query_info.prewhere_info->remove_prewhere_column)); + + // To remove additional columns + // In some cases, we did not read any marks so that the pipeline.streams is empty + // Thus, some columns in prewhere are not removed as expected + // This leads to mismatched header in distributed table + if (query_info.prewhere_info->remove_columns_actions) + pipe.addSimpleTransform(std::make_shared( + pipe.getHeader(), query_info.prewhere_info->remove_columns_actions)); + } + + pipes.emplace_back(std::move(pipe)); + } + + if (!blocksHaveEqualStructure(pipes.front().getHeader(), input_streams.front().header)) + { + for (auto & pipe : pipes) + pipe.addSimpleTransform(std::make_shared( + pipe.getHeader(), input_streams.front().header, ConvertingTransform::MatchColumnsMode::Name)); + } + + auto pipeline = std::make_unique(); + + /// Table lock is stored inside pipeline here. + pipeline->addTableLock(table_lock); + + /// Set the limits and quota for reading data, the speed and time of the query. + { + const Settings & settings = context.getSettingsRef(); + + IBlockInputStream::LocalLimits limits; + limits.mode = IBlockInputStream::LIMITS_TOTAL; + limits.size_limits = SizeLimits(settings.max_rows_to_read, settings.max_bytes_to_read, settings.read_overflow_mode); + limits.speed_limits.max_execution_time = settings.max_execution_time; + limits.timeout_overflow_mode = settings.timeout_overflow_mode; + + /** Quota and minimal speed restrictions are checked on the initiating server of the request, and not on remote servers, + * because the initiating server has a summary of the execution of the request on all servers. + * + * But limits on data size to read and maximum execution time are reasonable to check both on initiator and + * additionally on each remote server, because these limits are checked per block of data processed, + * and remote servers may process way more blocks of data than are received by initiator. + * + * The limits to throttle maximum execution speed is also checked on all servers. + */ + if (options.to_stage == QueryProcessingStage::Complete) + { + limits.speed_limits.min_execution_rps = settings.min_execution_speed; + limits.speed_limits.min_execution_bps = settings.min_execution_speed_bytes; + } + + limits.speed_limits.max_execution_rps = settings.max_execution_speed; + limits.speed_limits.max_execution_bps = settings.max_execution_speed_bytes; + limits.speed_limits.timeout_before_checking_execution_speed = settings.timeout_before_checking_execution_speed; + + auto quota = context.getQuota(); + + for (auto & pipe : pipes) + { + if (!options.ignore_limits) + pipe.setLimits(limits); + + if (!options.ignore_quota && (options.to_stage == QueryProcessingStage::Complete)) + pipe.setQuota(quota); + } + } + + if (pipes.size() == 1) + pipeline->setMaxThreads(1); + + for (auto & pipe : pipes) + pipe.enableQuota(); + + pipeline->init(std::move(pipes)); + return pipeline; +} + +} diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.h b/src/Processors/QueryPlan/ReadFromStorageStep.h new file mode 100644 index 000000000000..180cf47e6d7d --- /dev/null +++ b/src/Processors/QueryPlan/ReadFromStorageStep.h @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +namespace DB +{ + +class IStorage; +using StoragePtr = std::shared_ptr; + +class SelectQueryInfo; + +class PrewhereInfo; + +/// Reads from storage. +class ReadFromStorageStep : public IQueryPlanStep +{ +public: + ReadFromStorageStep( + TableStructureReadLockHolder table_lock, + SelectQueryOptions options, + StoragePtr storage, + const Names & required_columns, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processing_stage, + size_t max_block_size, + size_t max_streams); + + virtual ~ReadFromStorageStep(); + + String getName() const override { return "ReadFromStorage"; } + + QueryPipelinePtr updatePipeline(QueryPipelines) override; + +private: + TableStructureReadLockHolder table_lock; + SelectQueryOptions options; + + StoragePtr storage; + const Names & required_columns; + const SelectQueryInfo & query_info; + const Context & context; + QueryProcessingStage::Enum processing_stage; + size_t max_block_size; + size_t max_streams; +}; + +} diff --git a/src/Processors/Transforms/ExpressionTransform.cpp b/src/Processors/Transforms/ExpressionTransform.cpp index bf52a13f08a3..bf523d6d7a3e 100644 --- a/src/Processors/Transforms/ExpressionTransform.cpp +++ b/src/Processors/Transforms/ExpressionTransform.cpp @@ -5,7 +5,7 @@ namespace DB { -static Block transformHeader(Block header, const ExpressionActionsPtr & expression) +Block ExpressionTransform::transformHeader(Block header, const ExpressionActionsPtr & expression) { expression->execute(header, true); return header; diff --git a/src/Processors/Transforms/ExpressionTransform.h b/src/Processors/Transforms/ExpressionTransform.h index 87f2c01ea1da..60d6dc0f777d 100644 --- a/src/Processors/Transforms/ExpressionTransform.h +++ b/src/Processors/Transforms/ExpressionTransform.h @@ -18,6 +18,8 @@ class ExpressionTransform : public ISimpleTransform String getName() const override { return "ExpressionTransform"; } + static Block transformHeader(Block header, const ExpressionActionsPtr & expression); + protected: void transform(Chunk & chunk) override; diff --git a/src/Processors/Transforms/FilterTransform.cpp b/src/Processors/Transforms/FilterTransform.cpp index e35a399a0eec..aaa442602348 100644 --- a/src/Processors/Transforms/FilterTransform.cpp +++ b/src/Processors/Transforms/FilterTransform.cpp @@ -27,7 +27,7 @@ static void replaceFilterToConstant(Block & block, const String & filter_column_ } } -static Block transformHeader( +Block FilterTransform::transformHeader( Block header, const ExpressionActionsPtr & expression, const String & filter_column_name, diff --git a/src/Processors/Transforms/FilterTransform.h b/src/Processors/Transforms/FilterTransform.h index 45ec9da2ea3f..0497a339c82d 100644 --- a/src/Processors/Transforms/FilterTransform.h +++ b/src/Processors/Transforms/FilterTransform.h @@ -19,6 +19,12 @@ class FilterTransform : public ISimpleTransform const Block & header_, ExpressionActionsPtr expression_, String filter_column_name_, bool remove_filter_column_, bool on_totals_ = false); + static Block transformHeader( + Block header, + const ExpressionActionsPtr & expression, + const String & filter_column_name, + bool remove_filter_column); + String getName() const override { return "FilterTransform"; } Status prepare() override; diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 5cbc5dfd291a..6ced4fc28ccb 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -135,6 +135,7 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp QueryPlan/IQueryPlanStep.cpp + QueryPlan/ReadFromStorageStep.cpp QueryPlan/QueryPlan.cpp ) From 857582245e894ef71e59decef44555760f8f9908 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Mon, 15 Jun 2020 19:39:00 +0300 Subject: [PATCH 0312/1102] fixup --- programs/benchmark/Benchmark.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index af56aaa6db53..b8e4a0c346a7 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -62,12 +62,13 @@ class Benchmark : public Poco::Util::Application bool randomize_, size_t max_iterations_, double max_time_, const String & json_path_, size_t confidence_, const String & query_id_, bool continue_on_errors_, - const Settings & settings_) + bool print_stacktrace_, const Settings & settings_) : concurrency(concurrency_), delay(delay_), queue(concurrency), randomize(randomize_), cumulative(cumulative_), max_iterations(max_iterations_), max_time(max_time_), json_path(json_path_), confidence(confidence_), query_id(query_id_), - continue_on_errors(continue_on_errors_), settings(settings_), + continue_on_errors(continue_on_errors_), + print_stacktrace(print_stacktrace_), settings(settings_), shared_context(Context::createShared()), global_context(Context::createGlobal(shared_context.get())), pool(concurrency) { @@ -154,6 +155,7 @@ class Benchmark : public Poco::Util::Application size_t confidence; std::string query_id; bool continue_on_errors; + bool print_stacktrace; Settings settings; SharedContextHolder shared_context; Context global_context; @@ -376,7 +378,8 @@ class Benchmark : public Poco::Util::Application } else { - std::cerr << getCurrentExceptionMessage(print_stacktrace, true) ; + std::cerr << getCurrentExceptionMessage(print_stacktrace, + true /*check embedded stack trace*/) ; } } // Count failed queries toward executed, so that we'd reach @@ -605,6 +608,7 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) options["confidence"].as(), options["query_id"].as(), options.count("continue_on_errors") > 0, + print_stacktrace, settings); return benchmark.run(); } From af2fe2ba553e7112ef474d73473fbff047c5ae60 Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 19:55:33 +0300 Subject: [PATCH 0313/1102] Compilable setColumns, setConstraints, setIndices --- src/Storages/IStorage.cpp | 105 ++++++++---------- src/Storages/IStorage.h | 21 ++-- src/Storages/Kafka/StorageKafka.cpp | 4 +- src/Storages/LiveView/StorageBlocks.h | 4 +- src/Storages/LiveView/StorageLiveView.cpp | 4 +- src/Storages/MergeTree/MergeTreeData.cpp | 6 +- .../MergeTree/StorageFromMergeTreeDataPart.h | 7 +- src/Storages/StorageBuffer.cpp | 8 +- src/Storages/StorageDictionary.cpp | 4 +- src/Storages/StorageDistributed.cpp | 8 +- src/Storages/StorageFile.cpp | 11 +- src/Storages/StorageGenerateRandom.cpp | 4 +- src/Storages/StorageHDFS.cpp | 7 +- src/Storages/StorageInMemoryMetadata.cpp | 15 +++ src/Storages/StorageInMemoryMetadata.h | 13 +++ src/Storages/StorageInput.cpp | 4 +- src/Storages/StorageLog.cpp | 6 +- src/Storages/StorageMaterializedView.cpp | 6 +- src/Storages/StorageMemory.cpp | 6 +- src/Storages/StorageMerge.cpp | 6 +- src/Storages/StorageMySQL.cpp | 6 +- src/Storages/StorageNull.cpp | 2 +- src/Storages/StorageNull.h | 6 +- src/Storages/StorageS3.cpp | 6 +- src/Storages/StorageSet.cpp | 7 +- src/Storages/StorageStripeLog.cpp | 6 +- src/Storages/StorageTinyLog.cpp | 6 +- src/Storages/StorageURL.cpp | 7 +- src/Storages/StorageValues.cpp | 4 +- src/Storages/StorageView.cpp | 4 +- src/Storages/System/IStorageSystemOneBlock.h | 4 +- src/Storages/System/StorageSystemColumns.cpp | 4 +- .../System/StorageSystemDetachedParts.cpp | 4 +- src/Storages/System/StorageSystemDisks.cpp | 4 +- src/Storages/System/StorageSystemNumbers.cpp | 4 +- src/Storages/System/StorageSystemOne.cpp | 4 +- .../System/StorageSystemPartsBase.cpp | 4 +- src/Storages/System/StorageSystemReplicas.cpp | 4 +- .../System/StorageSystemStoragePolicies.cpp | 4 +- src/Storages/System/StorageSystemTables.cpp | 4 +- src/Storages/System/StorageSystemZeros.cpp | 5 +- 41 files changed, 211 insertions(+), 137 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index a244f836f5ce..6c045a6f3652 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -34,22 +34,22 @@ namespace ErrorCodes const ColumnsDescription & IStorage::getColumns() const { - return metadata.columns; + return metadata->columns; } const IndicesDescription & IStorage::getSecondaryIndices() const { - return metadata.secondary_indices; + return metadata->secondary_indices; } bool IStorage::hasSecondaryIndices() const { - return !metadata.secondary_indices.empty(); + return !metadata->secondary_indices.empty(); } const ConstraintsDescription & IStorage::getConstraints() const { - return metadata.constraints; + return metadata->constraints; } Block IStorage::getSampleBlock() const @@ -287,23 +287,6 @@ void IStorage::check(const Block & block, bool need_all) const } } -void IStorage::setColumns(ColumnsDescription columns_) -{ - if (columns_.getOrdinary().empty()) - throw Exception("Empty list of columns passed", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED); - metadata.columns = std::move(columns_); -} - -void IStorage::setSecondaryIndices(IndicesDescription secondary_indices_) -{ - metadata.secondary_indices = std::move(secondary_indices_); -} - -void IStorage::setConstraints(ConstraintsDescription constraints_) -{ - metadata.constraints = std::move(constraints_); -} - bool IStorage::isVirtualColumn(const String & column_name) const { /// Virtual column maybe overriden by real column @@ -382,7 +365,7 @@ void IStorage::alter( StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); - setColumns(std::move(new_metadata.columns)); + setInMemoryMetadata(new_metadata); } @@ -417,137 +400,137 @@ NamesAndTypesList IStorage::getVirtuals() const const KeyDescription & IStorage::getPartitionKey() const { - return metadata.partition_key; + return metadata->partition_key; } void IStorage::setPartitionKey(const KeyDescription & partition_key_) { - metadata.partition_key = partition_key_; + metadata->partition_key = partition_key_; } bool IStorage::isPartitionKeyDefined() const { - return metadata.partition_key.definition_ast != nullptr; + return metadata->partition_key.definition_ast != nullptr; } bool IStorage::hasPartitionKey() const { - return !metadata.partition_key.column_names.empty(); + return !metadata->partition_key.column_names.empty(); } Names IStorage::getColumnsRequiredForPartitionKey() const { if (hasPartitionKey()) - return metadata.partition_key.expression->getRequiredColumns(); + return metadata->partition_key.expression->getRequiredColumns(); return {}; } const KeyDescription & IStorage::getSortingKey() const { - return metadata.sorting_key; + return metadata->sorting_key; } void IStorage::setSortingKey(const KeyDescription & sorting_key_) { - metadata.sorting_key = sorting_key_; + metadata->sorting_key = sorting_key_; } bool IStorage::isSortingKeyDefined() const { - return metadata.sorting_key.definition_ast != nullptr; + return metadata->sorting_key.definition_ast != nullptr; } bool IStorage::hasSortingKey() const { - return !metadata.sorting_key.column_names.empty(); + return !metadata->sorting_key.column_names.empty(); } Names IStorage::getColumnsRequiredForSortingKey() const { if (hasSortingKey()) - return metadata.sorting_key.expression->getRequiredColumns(); + return metadata->sorting_key.expression->getRequiredColumns(); return {}; } Names IStorage::getSortingKeyColumns() const { if (hasSortingKey()) - return metadata.sorting_key.column_names; + return metadata->sorting_key.column_names; return {}; } const KeyDescription & IStorage::getPrimaryKey() const { - return metadata.primary_key; + return metadata->primary_key; } void IStorage::setPrimaryKey(const KeyDescription & primary_key_) { - metadata.primary_key = primary_key_; + metadata->primary_key = primary_key_; } bool IStorage::isPrimaryKeyDefined() const { - return metadata.primary_key.definition_ast != nullptr; + return metadata->primary_key.definition_ast != nullptr; } bool IStorage::hasPrimaryKey() const { - return !metadata.primary_key.column_names.empty(); + return !metadata->primary_key.column_names.empty(); } Names IStorage::getColumnsRequiredForPrimaryKey() const { if (hasPrimaryKey()) - return metadata.primary_key.expression->getRequiredColumns(); + return metadata->primary_key.expression->getRequiredColumns(); return {}; } Names IStorage::getPrimaryKeyColumns() const { - if (!metadata.primary_key.column_names.empty()) - return metadata.primary_key.column_names; + if (!metadata->primary_key.column_names.empty()) + return metadata->primary_key.column_names; return {}; } const KeyDescription & IStorage::getSamplingKey() const { - return metadata.sampling_key; + return metadata->sampling_key; } void IStorage::setSamplingKey(const KeyDescription & sampling_key_) { - metadata.sampling_key = sampling_key_; + metadata->sampling_key = sampling_key_; } bool IStorage::isSamplingKeyDefined() const { - return metadata.sampling_key.definition_ast != nullptr; + return metadata->sampling_key.definition_ast != nullptr; } bool IStorage::hasSamplingKey() const { - return !metadata.sampling_key.column_names.empty(); + return !metadata->sampling_key.column_names.empty(); } Names IStorage::getColumnsRequiredForSampling() const { if (hasSamplingKey()) - return metadata.sampling_key.expression->getRequiredColumns(); + return metadata->sampling_key.expression->getRequiredColumns(); return {}; } TTLTableDescription IStorage::getTableTTLs() const { std::lock_guard lock(ttl_mutex); - return metadata.table_ttl; + return metadata->table_ttl; } void IStorage::setTableTTLs(const TTLTableDescription & table_ttl_) { std::lock_guard lock(ttl_mutex); - metadata.table_ttl = table_ttl_; + metadata->table_ttl = table_ttl_; } bool IStorage::hasAnyTableTTL() const @@ -558,43 +541,43 @@ bool IStorage::hasAnyTableTTL() const TTLColumnsDescription IStorage::getColumnTTLs() const { std::lock_guard lock(ttl_mutex); - return metadata.column_ttls_by_name; + return metadata->column_ttls_by_name; } void IStorage::setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_) { std::lock_guard lock(ttl_mutex); - metadata.column_ttls_by_name = column_ttls_by_name_; + metadata->column_ttls_by_name = column_ttls_by_name_; } bool IStorage::hasAnyColumnTTL() const { std::lock_guard lock(ttl_mutex); - return !metadata.column_ttls_by_name.empty(); + return !metadata->column_ttls_by_name.empty(); } TTLDescription IStorage::getRowsTTL() const { std::lock_guard lock(ttl_mutex); - return metadata.table_ttl.rows_ttl; + return metadata->table_ttl.rows_ttl; } bool IStorage::hasRowsTTL() const { std::lock_guard lock(ttl_mutex); - return metadata.table_ttl.rows_ttl.expression != nullptr; + return metadata->table_ttl.rows_ttl.expression != nullptr; } TTLDescriptions IStorage::getMoveTTLs() const { std::lock_guard lock(ttl_mutex); - return metadata.table_ttl.move_ttl; + return metadata->table_ttl.move_ttl; } bool IStorage::hasAnyMoveTTL() const { std::lock_guard lock(ttl_mutex); - return !metadata.table_ttl.move_ttl.empty(); + return !metadata->table_ttl.move_ttl.empty(); } @@ -660,32 +643,32 @@ ColumnDependencies IStorage::getColumnDependencies(const NameSet & updated_colum ASTPtr IStorage::getSettingsChanges() const { - if (metadata.settings_changes) - return metadata.settings_changes->clone(); + if (metadata->settings_changes) + return metadata->settings_changes->clone(); return nullptr; } void IStorage::setSettingsChanges(const ASTPtr & settings_changes_) { if (settings_changes_) - metadata.settings_changes = settings_changes_->clone(); + metadata->settings_changes = settings_changes_->clone(); else - metadata.settings_changes = nullptr; + metadata->settings_changes = nullptr; } const SelectQueryDescription & IStorage::getSelectQuery() const { - return metadata.select; + return metadata->select; } void IStorage::setSelectQuery(const SelectQueryDescription & select_) { - metadata.select = select_; + metadata->select = select_; } bool IStorage::hasSelectQuery() const { - return metadata.select.select_query != nullptr; + return metadata->select.select_query != nullptr; } } diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index c7c8e382a87f..4d01bb5370da 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -140,27 +140,24 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired const ColumnsDescription & getColumns() const; /// returns combined set of columns - void setColumns(ColumnsDescription columns_); /// sets only real columns, possibly overwrites virtual ones. - - void setSecondaryIndices(IndicesDescription secondary_indices_); const IndicesDescription & getSecondaryIndices() const; /// Has at least one non primary index bool hasSecondaryIndices() const; const ConstraintsDescription & getConstraints() const; - void setConstraints(ConstraintsDescription constraints_); /// Storage settings ASTPtr getSettingsChanges() const; void setSettingsChanges(const ASTPtr & settings_changes_); - bool hasSettingsChanges() const { return metadata.settings_changes != nullptr; } + bool hasSettingsChanges() const { return metadata->settings_changes != nullptr; } /// Select query for *View storages. const SelectQueryDescription & getSelectQuery() const; void setSelectQuery(const SelectQueryDescription & select_); bool hasSelectQuery() const; - StorageInMemoryMetadata getInMemoryMetadata() const { return metadata; } + StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } + void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } Block getSampleBlock() const; /// ordinary + materialized. Block getSampleBlockWithVirtuals() const; /// ordinary + materialized + virtuals. @@ -207,7 +204,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// TODO (alesap) just use multiversion for atomic metadata mutable std::mutex ttl_mutex; - StorageInMemoryMetadata metadata; + StorageMetadataPtr metadata; private: RWLockImpl::LockHolder tryLockTimed( const RWLock & rwlock, RWLockImpl::Type type, const String & query_id, const SettingSeconds & acquire_timeout) const; @@ -354,7 +351,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /** ALTER tables in the form of column changes that do not affect the change to Storage or its parameters. * This method must fully execute the ALTER query, taking care of the locks itself. - * To update the table metadata on disk, this method should call InterpreterAlterQuery::updateMetadata. + * To update the table metadata on disk, this method should call InterpreterAlterQuery::updateMetadata-> */ virtual void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder); @@ -445,7 +442,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// struct). void setPartitionKey(const KeyDescription & partition_key_); /// Returns ASTExpressionList of partition key expression for storage or nullptr if there is none. - ASTPtr getPartitionKeyAST() const { return metadata.partition_key.definition_ast; } + ASTPtr getPartitionKeyAST() const { return metadata->partition_key.definition_ast; } /// Storage has user-defined (in CREATE query) partition key. bool isPartitionKeyDefined() const; /// Storage has partition key. @@ -460,7 +457,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// struct). void setSortingKey(const KeyDescription & sorting_key_); /// Returns ASTExpressionList of sorting key expression for storage or nullptr if there is none. - ASTPtr getSortingKeyAST() const { return metadata.sorting_key.definition_ast; } + ASTPtr getSortingKeyAST() const { return metadata->sorting_key.definition_ast; } /// Storage has user-defined (in CREATE query) sorting key. bool isSortingKeyDefined() const; /// Storage has sorting key. It means, that it contains at least one column. @@ -477,7 +474,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// struct). void setPrimaryKey(const KeyDescription & primary_key_); /// Returns ASTExpressionList of primary key expression for storage or nullptr if there is none. - ASTPtr getPrimaryKeyAST() const { return metadata.primary_key.definition_ast; } + ASTPtr getPrimaryKeyAST() const { return metadata->primary_key.definition_ast; } /// Storage has user-defined (in CREATE query) sorting key. bool isPrimaryKeyDefined() const; /// Storage has primary key (maybe part of some other key). It means, that @@ -495,7 +492,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// struct). void setSamplingKey(const KeyDescription & sampling_key_); /// Returns sampling expression AST for storage or nullptr if there is none. - ASTPtr getSamplingKeyAST() const { return metadata.sampling_key.definition_ast; } + ASTPtr getSamplingKeyAST() const { return metadata->sampling_key.definition_ast; } /// Storage has user-defined (in CREATE query) sampling key. bool isSamplingKeyDefined() const; /// Storage has sampling key. diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index bb721417c5b3..2109afed9324 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -137,7 +137,9 @@ StorageKafka::StorageKafka( , intermediate_commit(kafka_settings->kafka_commit_every_batch.value) , settings_adjustments(createSettingsAdjustments()) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); task->deactivate(); diff --git a/src/Storages/LiveView/StorageBlocks.h b/src/Storages/LiveView/StorageBlocks.h index 2a9d7766fd7e..78d60163d5e9 100644 --- a/src/Storages/LiveView/StorageBlocks.h +++ b/src/Storages/LiveView/StorageBlocks.h @@ -18,7 +18,9 @@ class StorageBlocks : public IStorage QueryProcessingStage::Enum to_stage_) : IStorage(table_id_), pipes(std::move(pipes_)), to_stage(to_stage_) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); } static StoragePtr createStorage(const StorageID & table_id, const ColumnsDescription & columns, Pipes pipes, QueryProcessingStage::Enum to_stage) diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index 8a04a9e49e47..ade2d1c967d1 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -251,7 +251,9 @@ StorageLiveView::StorageLiveView( live_view_context = std::make_unique(global_context); live_view_context->makeQueryContext(); - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 84470088ebe6..14be526d7f6f 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -387,11 +387,7 @@ void MergeTreeData::setProperties(const StorageInMemoryMetadata & new_metadata, checkProperties(new_metadata, attach); /// Other parts of metadata initialized is separate methods - setColumns(std::move(new_metadata.columns)); - setSecondaryIndices(std::move(new_metadata.secondary_indices)); - setConstraints(std::move(new_metadata.constraints)); - setSortingKey(std::move(new_metadata.sorting_key)); - setPrimaryKey(std::move(new_metadata.primary_key)); + setInMemoryMetadata(new_metadata); } namespace diff --git a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h index 3031402715a2..342a89c38ea2 100644 --- a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h +++ b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h @@ -49,12 +49,7 @@ class StorageFromMergeTreeDataPart final : public ext::shared_ptr_helperstorage.getColumns()); - setSecondaryIndices(part_->storage.getSecondaryIndices()); - setPrimaryKey(part_->storage.getPrimaryKey()); - setSortingKey(part_->storage.getSortingKey()); - setColumnTTLs(part->storage.getColumnTTLs()); - setTableTTLs(part->storage.getTableTTLs()); + setInMemoryMetadata(part_->storage.getInMemoryMetadata()); } private: diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 08dc81b4945b..007625790f44 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -77,8 +77,10 @@ StorageBuffer::StorageBuffer( , log(&Poco::Logger::get("StorageBuffer (" + table_id_.getFullTableName() + ")")) , bg_pool(global_context.getBufferFlushSchedulePool()) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); } @@ -778,7 +780,7 @@ void StorageBuffer::alter(const AlterCommands & params, const Context & context, StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); - setColumns(std::move(new_metadata.columns)); + setInMemoryMetadata(new_metadata); } diff --git a/src/Storages/StorageDictionary.cpp b/src/Storages/StorageDictionary.cpp index 9b2c5784d855..4348973ec604 100644 --- a/src/Storages/StorageDictionary.cpp +++ b/src/Storages/StorageDictionary.cpp @@ -100,7 +100,9 @@ StorageDictionary::StorageDictionary( : IStorage(table_id_) , dictionary_name(dictionary_name_) { - setColumns(ColumnsDescription{getNamesAndTypes(dictionary_structure_)}); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription{getNamesAndTypes(dictionary_structure_)}); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 3383c6095200..bf5f729ed198 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -283,8 +283,10 @@ StorageDistributed::StorageDistributed( , storage_policy(storage_policy_) , relative_data_path(relative_data_path_) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); if (sharding_key_) { @@ -562,7 +564,7 @@ void StorageDistributed::alter(const AlterCommands & params, const Context & con StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); - setColumns(std::move(new_metadata.columns)); + setInMemoryMetadata(new_metadata); } diff --git a/src/Storages/StorageFile.cpp b/src/Storages/StorageFile.cpp index 0bcb624bec41..f94a7b71e566 100644 --- a/src/Storages/StorageFile.cpp +++ b/src/Storages/StorageFile.cpp @@ -166,7 +166,10 @@ StorageFile::StorageFile(const std::string & table_path_, const std::string & us auto & first_path = paths[0]; Block header = StorageDistributedDirectoryMonitor::createStreamFromFile(first_path)->getHeader(); - setColumns(ColumnsDescription(header.getNamesAndTypesList())); + + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription(header.getNamesAndTypesList())); + setInMemoryMetadata(metadata_); } } } @@ -188,10 +191,12 @@ StorageFile::StorageFile(CommonArguments args) , compression_method(args.compression_method) , base_path(args.context.getPath()) { + StorageInMemoryMetadata metadata_; if (args.format_name != "Distributed") - setColumns(args.columns); + metadata_.setColumns(args.columns); - setConstraints(args.constraints); + metadata_.setConstraints(args.constraints); + setInMemoryMetadata(metadata_); } class StorageFileSource : public SourceWithProgress diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index 8c186f38943a..f69478a4bddd 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -388,7 +388,9 @@ StorageGenerateRandom::StorageGenerateRandom(const StorageID & table_id_, const : IStorage(table_id_), max_array_length(max_array_length_), max_string_length(max_string_length_) { random_seed = random_seed_ ? sipHash64(*random_seed_) : randomSeed(); - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/StorageHDFS.cpp b/src/Storages/StorageHDFS.cpp index 352e0a43f393..082e40f6d6d6 100644 --- a/src/Storages/StorageHDFS.cpp +++ b/src/Storages/StorageHDFS.cpp @@ -49,8 +49,11 @@ StorageHDFS::StorageHDFS(const String & uri_, , compression_method(compression_method_) { context.getRemoteHostFilter().checkURL(Poco::URI(uri)); - setColumns(columns_); - setConstraints(constraints_); + + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); } namespace diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 8d23bd7bccfe..ac2c0417c450 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -51,4 +51,19 @@ StorageInMemoryMetadata & StorageInMemoryMetadata::operator=(const StorageInMemo } +void StorageInMemoryMetadata::setColumns(ColumnsDescription columns_) +{ + columns = std::move(columns_); +} + +void StorageInMemoryMetadata::setSecondaryIndices(IndicesDescription secondary_indices_) +{ + secondary_indices = std::move(secondary_indices_); +} + +void StorageInMemoryMetadata::setConstraints(ConstraintsDescription constraints_) +{ + constraints = std::move(constraints_); +} + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 889f8e49f693..f4d6e9b38b33 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -8,6 +8,7 @@ #include #include +#include namespace DB { @@ -47,6 +48,18 @@ struct StorageInMemoryMetadata StorageInMemoryMetadata(const StorageInMemoryMetadata & other); StorageInMemoryMetadata & operator=(const StorageInMemoryMetadata & other); + + + //////////////////////////////////////////////////////////////////////// + void setColumns(ColumnsDescription columns_); /// sets only real columns, possibly overwrites virtual ones. + + void setSecondaryIndices(IndicesDescription secondary_indices_); + + void setConstraints(ConstraintsDescription constraints_); + }; +using StorageMetadataPtr = std::shared_ptr; +using MultiVersionStorageMetadataPtr = MultiVersion; + } diff --git a/src/Storages/StorageInput.cpp b/src/Storages/StorageInput.cpp index e30ae55e715a..92287051bf3c 100644 --- a/src/Storages/StorageInput.cpp +++ b/src/Storages/StorageInput.cpp @@ -21,7 +21,9 @@ namespace ErrorCodes StorageInput::StorageInput(const StorageID & table_id, const ColumnsDescription & columns_) : IStorage(table_id) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index bc2bbb2ce67c..09be868bcfa6 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -431,8 +431,10 @@ StorageLog::StorageLog( , max_compress_block_size(max_compress_block_size_) , file_checker(disk, table_path + "sizes.json") { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index a0c2fa87eb22..34d5e1d43749 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -50,7 +50,9 @@ StorageMaterializedView::StorageMaterializedView( bool attach_) : IStorage(table_id_), global_context(local_context.getGlobalContext()) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); @@ -209,7 +211,7 @@ void StorageMaterializedView::alter( /// end modify query DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); - setColumns(std::move(new_metadata.columns)); + setInMemoryMetadata(new_metadata); } diff --git a/src/Storages/StorageMemory.cpp b/src/Storages/StorageMemory.cpp index b55a6227127c..bb89bdb5c489 100644 --- a/src/Storages/StorageMemory.cpp +++ b/src/Storages/StorageMemory.cpp @@ -78,8 +78,10 @@ class MemoryBlockOutputStream : public IBlockOutputStream StorageMemory::StorageMemory(const StorageID & table_id_, ColumnsDescription columns_description_, ConstraintsDescription constraints_) : IStorage(table_id_) { - setColumns(std::move(columns_description_)); - setConstraints(std::move(constraints_)); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(std::move(columns_description_)); + metadata_.setConstraints(std::move(constraints_)); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 8264eaa4cb67..3685a777bf0a 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -51,7 +51,9 @@ StorageMerge::StorageMerge( , table_name_regexp(table_name_regexp_) , global_context(context_) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); } template @@ -393,7 +395,7 @@ void StorageMerge::alter( StorageInMemoryMetadata storage_metadata = getInMemoryMetadata(); params.apply(storage_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, storage_metadata); - setColumns(storage_metadata.columns); + setInMemoryMetadata(storage_metadata); } Block StorageMerge::getQueryHeader( diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index ee44ca7948ed..f9aad8a58a7d 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -56,8 +56,10 @@ StorageMySQL::StorageMySQL( , pool(std::move(pool_)) , global_context(context_) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/StorageNull.cpp b/src/Storages/StorageNull.cpp index 182ce09ef969..7589c4b44dcc 100644 --- a/src/Storages/StorageNull.cpp +++ b/src/Storages/StorageNull.cpp @@ -54,7 +54,7 @@ void StorageNull::alter( StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); - setColumns(std::move(new_metadata.columns)); + setInMemoryMetadata(new_metadata); } } diff --git a/src/Storages/StorageNull.h b/src/Storages/StorageNull.h index 5fb4a16a24bd..fe8bd05d53a3 100644 --- a/src/Storages/StorageNull.h +++ b/src/Storages/StorageNull.h @@ -59,8 +59,10 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt StorageNull(const StorageID & table_id_, ColumnsDescription columns_description_, ConstraintsDescription constraints_) : IStorage(table_id_) { - setColumns(std::move(columns_description_)); - setConstraints(std::move(constraints_)); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_description_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); } }; diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index 397d064ba151..acaa2bcc7d62 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -206,8 +206,10 @@ StorageS3::StorageS3( , compression_method(compression_method_) { context_global.getRemoteHostFilter().checkURL(uri_.uri); - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); auto settings = context_.getStorageS3Settings().getSettings(uri.endpoint); Aws::Auth::AWSCredentials credentials(access_key_id_, secret_access_key_); diff --git a/src/Storages/StorageSet.cpp b/src/Storages/StorageSet.cpp index 86bfed5ac84b..38b4d30c25b7 100644 --- a/src/Storages/StorageSet.cpp +++ b/src/Storages/StorageSet.cpp @@ -96,8 +96,11 @@ StorageSetOrJoinBase::StorageSetOrJoinBase( const Context & context_) : IStorage(table_id_) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); + if (relative_path_.empty()) throw Exception("Join and Set storages require data path", ErrorCodes::INCORRECT_FILE_NAME); diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index b61d52657dde..b68505fa147e 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -223,8 +223,10 @@ StorageStripeLog::StorageStripeLog( , file_checker(disk, table_path + "sizes.json") , log(&Poco::Logger::get("StorageStripeLog")) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 2a62068516ed..5bca6072da01 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -336,8 +336,10 @@ StorageTinyLog::StorageTinyLog( , file_checker(disk, table_path + "sizes.json") , log(&Poco::Logger::get("StorageTinyLog")) { - setColumns(columns_); - setConstraints(constraints_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index a69e140fe5a4..0301412e0297 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -43,8 +43,11 @@ IStorageURLBase::IStorageURLBase( , format_name(format_name_) { context_global.getRemoteHostFilter().checkURL(uri); - setColumns(columns_); - setConstraints(constraints_); + + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + metadata_.setConstraints(constraints_); + setInMemoryMetadata(metadata_); } namespace diff --git a/src/Storages/StorageValues.cpp b/src/Storages/StorageValues.cpp index cf0b39df8f18..5ba36a936e23 100644 --- a/src/Storages/StorageValues.cpp +++ b/src/Storages/StorageValues.cpp @@ -16,7 +16,9 @@ StorageValues::StorageValues( const NamesAndTypesList & virtuals_) : IStorage(table_id_), res_block(res_block_), virtuals(virtuals_) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); } Pipes StorageValues::read( diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 055faed5899d..60ae681e0022 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -38,7 +38,9 @@ StorageView::StorageView( const ColumnsDescription & columns_) : IStorage(table_id_) { - setColumns(columns_); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(columns_); + setInMemoryMetadata(metadata_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); diff --git a/src/Storages/System/IStorageSystemOneBlock.h b/src/Storages/System/IStorageSystemOneBlock.h index b3951bc3f757..1ceff26ba83f 100644 --- a/src/Storages/System/IStorageSystemOneBlock.h +++ b/src/Storages/System/IStorageSystemOneBlock.h @@ -23,7 +23,9 @@ class IStorageSystemOneBlock : public IStorage public: IStorageSystemOneBlock(const String & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription(Self::getNamesAndTypes())); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription(Self::getNamesAndTypes())); + setInMemoryMetadata(metadata_); } Pipes read(const Names & column_names, diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 90e52ad373e7..6359e367106e 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -26,7 +26,8 @@ namespace ErrorCodes StorageSystemColumns::StorageSystemColumns(const std::string & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription( + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription( { { "database", std::make_shared() }, { "table", std::make_shared() }, @@ -45,6 +46,7 @@ StorageSystemColumns::StorageSystemColumns(const std::string & name_) { "is_in_sampling_key", std::make_shared() }, { "compression_codec", std::make_shared() }, })); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/System/StorageSystemDetachedParts.cpp b/src/Storages/System/StorageSystemDetachedParts.cpp index f3fd51330d96..ef88c3ca0580 100644 --- a/src/Storages/System/StorageSystemDetachedParts.cpp +++ b/src/Storages/System/StorageSystemDetachedParts.cpp @@ -30,7 +30,8 @@ class StorageSystemDetachedParts final : explicit StorageSystemDetachedParts() : IStorage({"system", "detached_parts"}) { - setColumns(ColumnsDescription{{ + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription{{ {"database", std::make_shared()}, {"table", std::make_shared()}, {"partition_id", std::make_shared(std::make_shared())}, @@ -41,6 +42,7 @@ class StorageSystemDetachedParts final : {"max_block_number", std::make_shared(std::make_shared())}, {"level", std::make_shared(std::make_shared())} }}); + setInMemoryMetadata(metadata_); } Pipes read( diff --git a/src/Storages/System/StorageSystemDisks.cpp b/src/Storages/System/StorageSystemDisks.cpp index b5a5026b2e70..5905080539e6 100644 --- a/src/Storages/System/StorageSystemDisks.cpp +++ b/src/Storages/System/StorageSystemDisks.cpp @@ -14,7 +14,8 @@ namespace ErrorCodes StorageSystemDisks::StorageSystemDisks(const std::string & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription( + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription( { {"name", std::make_shared()}, {"path", std::make_shared()}, @@ -22,6 +23,7 @@ StorageSystemDisks::StorageSystemDisks(const std::string & name_) {"total_space", std::make_shared()}, {"keep_free_space", std::make_shared()}, })); + setInMemoryMetadata(metadata_); } Pipes StorageSystemDisks::read( diff --git a/src/Storages/System/StorageSystemNumbers.cpp b/src/Storages/System/StorageSystemNumbers.cpp index 20dcc58f6520..0fa7b71555e2 100644 --- a/src/Storages/System/StorageSystemNumbers.cpp +++ b/src/Storages/System/StorageSystemNumbers.cpp @@ -118,7 +118,9 @@ class NumbersMultiThreadedSource : public SourceWithProgress StorageSystemNumbers::StorageSystemNumbers(const StorageID & table_id, bool multithreaded_, std::optional limit_, UInt64 offset_, bool even_distribution_) : IStorage(table_id), multithreaded(multithreaded_), even_distribution(even_distribution_), limit(limit_), offset(offset_) { - setColumns(ColumnsDescription({{"number", std::make_shared()}})); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription({{"number", std::make_shared()}})); + setInMemoryMetadata(metadata_); } Pipes StorageSystemNumbers::read( diff --git a/src/Storages/System/StorageSystemOne.cpp b/src/Storages/System/StorageSystemOne.cpp index 6cbb634d2b7a..e7c8c446847c 100644 --- a/src/Storages/System/StorageSystemOne.cpp +++ b/src/Storages/System/StorageSystemOne.cpp @@ -14,7 +14,9 @@ namespace DB StorageSystemOne::StorageSystemOne(const std::string & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription({{"dummy", std::make_shared()}})); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription({{"dummy", std::make_shared()}})); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index 925a5df889e7..42a432489f41 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -277,7 +277,9 @@ StorageSystemPartsBase::StorageSystemPartsBase(std::string name_, NamesAndTypesL add_alias("bytes", "bytes_on_disk"); add_alias("marks_size", "marks_bytes"); - setColumns(tmp_columns); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(tmp_columns); + setInMemoryMetadata(metadata_); } NamesAndTypesList StorageSystemPartsBase::getVirtuals() const diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index c2cd3a1e4b1f..ca71e7e5f749 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -19,7 +19,8 @@ namespace DB StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription({ + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription({ { "database", std::make_shared() }, { "table", std::make_shared() }, { "engine", std::make_shared() }, @@ -52,6 +53,7 @@ StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) { "active_replicas", std::make_shared() }, { "zookeeper_exception", std::make_shared() }, })); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/System/StorageSystemStoragePolicies.cpp b/src/Storages/System/StorageSystemStoragePolicies.cpp index acbc9d72a20a..dbb47dc771a5 100644 --- a/src/Storages/System/StorageSystemStoragePolicies.cpp +++ b/src/Storages/System/StorageSystemStoragePolicies.cpp @@ -17,7 +17,8 @@ namespace ErrorCodes StorageSystemStoragePolicies::StorageSystemStoragePolicies(const std::string & name_) : IStorage({"system", name_}) { - setColumns( + StorageInMemoryMetadata metadata_; + metadata_.setColumns( ColumnsDescription({ {"policy_name", std::make_shared()}, {"volume_name", std::make_shared()}, @@ -26,6 +27,7 @@ StorageSystemStoragePolicies::StorageSystemStoragePolicies(const std::string & n {"max_data_part_size", std::make_shared()}, {"move_factor", std::make_shared()} })); + setInMemoryMetadata(metadata_); } Pipes StorageSystemStoragePolicies::read( diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index 2bf6595bf539..84d441a8c6e7 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -33,7 +33,8 @@ namespace ErrorCodes StorageSystemTables::StorageSystemTables(const std::string & name_) : IStorage({"system", name_}) { - setColumns(ColumnsDescription( + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription( { {"database", std::make_shared()}, {"name", std::make_shared()}, @@ -55,6 +56,7 @@ StorageSystemTables::StorageSystemTables(const std::string & name_) {"total_rows", std::make_shared(std::make_shared())}, {"total_bytes", std::make_shared(std::make_shared())}, })); + setInMemoryMetadata(metadata_); } diff --git a/src/Storages/System/StorageSystemZeros.cpp b/src/Storages/System/StorageSystemZeros.cpp index cd2fa0a60593..438d31e7e02d 100644 --- a/src/Storages/System/StorageSystemZeros.cpp +++ b/src/Storages/System/StorageSystemZeros.cpp @@ -84,7 +84,10 @@ class ZerosSource : public SourceWithProgress StorageSystemZeros::StorageSystemZeros(const StorageID & table_id_, bool multithreaded_, std::optional limit_) : IStorage(table_id_), multithreaded(multithreaded_), limit(limit_) { - setColumns(ColumnsDescription({{"zero", std::make_shared()}})); + StorageInMemoryMetadata metadata_; + metadata_.setColumns(ColumnsDescription({{"zero", std::make_shared()}})); + setInMemoryMetadata(metadata_); + } Pipes StorageSystemZeros::read( From aa30649ce5eb3edc14641b595ccca6c3cba38dfa Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 20:10:14 +0300 Subject: [PATCH 0314/1102] Working setColumns, setConstraints, setIndices --- src/Storages/IStorage.h | 2 +- src/Storages/MergeTree/MergeTreeData.cpp | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 4d01bb5370da..403f5293588b 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -82,7 +82,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo IStorage() = delete; /// Storage fields should be initialized in separate methods like setColumns /// or setTableTTLs. - explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)) {} //-V730 + explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)), metadata(std::make_shared()) {} //-V730 virtual ~IStorage() = default; IStorage(const IStorage &) = delete; diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 14be526d7f6f..3414143c46b2 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -142,9 +142,8 @@ MergeTreeData::MergeTreeData( if (relative_data_path.empty()) throw Exception("MergeTree storages require data path", ErrorCodes::INCORRECT_FILE_NAME); - setSettingsChanges(metadata_.settings_changes); - const auto settings = getSettings(); setProperties(metadata_, attach); + const auto settings = getSettings(); /// NOTE: using the same columns list as is read when performing actual merges. merging_params.check(getColumns().getAllPhysical()); @@ -385,8 +384,6 @@ void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata void MergeTreeData::setProperties(const StorageInMemoryMetadata & new_metadata, bool attach) { checkProperties(new_metadata, attach); - - /// Other parts of metadata initialized is separate methods setInMemoryMetadata(new_metadata); } From 5fc41c7eccc9d98d524ee997ae689d5e5333820a Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 20:17:06 +0300 Subject: [PATCH 0315/1102] Move set*Key methods to StorageInMemoryMetadata --- src/Storages/IStorage.cpp | 21 --------------------- src/Storages/IStorage.h | 12 ------------ src/Storages/MergeTree/MergeTreeData.cpp | 3 --- src/Storages/StorageInMemoryMetadata.h | 12 ++++++++++++ 4 files changed, 12 insertions(+), 36 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 6c045a6f3652..2bbbabbff08c 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -403,11 +403,6 @@ const KeyDescription & IStorage::getPartitionKey() const return metadata->partition_key; } -void IStorage::setPartitionKey(const KeyDescription & partition_key_) -{ - metadata->partition_key = partition_key_; -} - bool IStorage::isPartitionKeyDefined() const { return metadata->partition_key.definition_ast != nullptr; @@ -430,11 +425,6 @@ const KeyDescription & IStorage::getSortingKey() const return metadata->sorting_key; } -void IStorage::setSortingKey(const KeyDescription & sorting_key_) -{ - metadata->sorting_key = sorting_key_; -} - bool IStorage::isSortingKeyDefined() const { return metadata->sorting_key.definition_ast != nullptr; @@ -464,11 +454,6 @@ const KeyDescription & IStorage::getPrimaryKey() const return metadata->primary_key; } -void IStorage::setPrimaryKey(const KeyDescription & primary_key_) -{ - metadata->primary_key = primary_key_; -} - bool IStorage::isPrimaryKeyDefined() const { return metadata->primary_key.definition_ast != nullptr; @@ -498,12 +483,6 @@ const KeyDescription & IStorage::getSamplingKey() const return metadata->sampling_key; } -void IStorage::setSamplingKey(const KeyDescription & sampling_key_) -{ - metadata->sampling_key = sampling_key_; -} - - bool IStorage::isSamplingKeyDefined() const { return metadata->sampling_key.definition_ast != nullptr; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 403f5293588b..ec7e8fc1795c 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -438,9 +438,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns structure with partition key. const KeyDescription & getPartitionKey() const; - /// Set partition key for storage (methods bellow, are just wrappers for this - /// struct). - void setPartitionKey(const KeyDescription & partition_key_); /// Returns ASTExpressionList of partition key expression for storage or nullptr if there is none. ASTPtr getPartitionKeyAST() const { return metadata->partition_key.definition_ast; } /// Storage has user-defined (in CREATE query) partition key. @@ -453,9 +450,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns structure with sorting key. const KeyDescription & getSortingKey() const; - /// Set sorting key for storage (methods bellow, are just wrappers for this - /// struct). - void setSortingKey(const KeyDescription & sorting_key_); /// Returns ASTExpressionList of sorting key expression for storage or nullptr if there is none. ASTPtr getSortingKeyAST() const { return metadata->sorting_key.definition_ast; } /// Storage has user-defined (in CREATE query) sorting key. @@ -470,9 +464,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns structure with primary key. const KeyDescription & getPrimaryKey() const; - /// Set primary key for storage (methods bellow, are just wrappers for this - /// struct). - void setPrimaryKey(const KeyDescription & primary_key_); /// Returns ASTExpressionList of primary key expression for storage or nullptr if there is none. ASTPtr getPrimaryKeyAST() const { return metadata->primary_key.definition_ast; } /// Storage has user-defined (in CREATE query) sorting key. @@ -488,9 +479,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns structure with sampling key. const KeyDescription & getSamplingKey() const; - /// Set sampling key for storage (methods bellow, are just wrappers for this - /// struct). - void setSamplingKey(const KeyDescription & sampling_key_); /// Returns sampling expression AST for storage or nullptr if there is none. ASTPtr getSamplingKeyAST() const { return metadata->sampling_key.definition_ast; } /// Storage has user-defined (in CREATE query) sampling key. diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 3414143c46b2..ab0544c641b7 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -154,8 +154,6 @@ MergeTreeData::MergeTreeData( if (!pk_sample_block.has(metadata_.sampling_key.column_names[0]) && !attach && !settings->compatibility_allow_sampling_expression_not_in_primary_key) /// This is for backward compatibility. throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); - - setSamplingKey(metadata_.sampling_key); } MergeTreeDataFormatVersion min_format_version(0); @@ -472,7 +470,6 @@ void MergeTreeData::initPartitionKey(const KeyDescription & new_partition_key) } } } - setPartitionKey(new_partition_key); } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index f4d6e9b38b33..3b3c9d07c893 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -57,6 +57,18 @@ struct StorageInMemoryMetadata void setConstraints(ConstraintsDescription constraints_); + /// Set partition key for storage (methods bellow, are just wrappers for this + /// struct). + void setPartitionKey(const KeyDescription & partition_key_); + /// Set sorting key for storage (methods bellow, are just wrappers for this + /// struct). + void setSortingKey(const KeyDescription & sorting_key_); + /// Set primary key for storage (methods bellow, are just wrappers for this + /// struct). + void setPrimaryKey(const KeyDescription & primary_key_); + /// Set sampling key for storage (methods bellow, are just wrappers for this + /// struct). + void setSamplingKey(const KeyDescription & sampling_key_); }; using StorageMetadataPtr = std::shared_ptr; From e667eb57b2aeaea856c24ebbb6e869ee508a368d Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 20:24:49 +0300 Subject: [PATCH 0316/1102] Working set*Keys methods --- src/Storages/MergeTree/MergeTreeData.cpp | 36 +++++++++++++----------- src/Storages/MergeTree/MergeTreeData.h | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index ab0544c641b7..8af38bdf5007 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -118,7 +118,7 @@ const char * DELETE_ON_DESTROY_MARKER_PATH = "delete-on-destroy.txt"; MergeTreeData::MergeTreeData( const StorageID & table_id_, const String & relative_data_path_, - const StorageInMemoryMetadata & metadata_, + StorageInMemoryMetadata metadata_, Context & context_, const String & date_column_name, const MergingParams & merging_params_, @@ -142,28 +142,15 @@ MergeTreeData::MergeTreeData( if (relative_data_path.empty()) throw Exception("MergeTree storages require data path", ErrorCodes::INCORRECT_FILE_NAME); - setProperties(metadata_, attach); - const auto settings = getSettings(); - - /// NOTE: using the same columns list as is read when performing actual merges. - merging_params.check(getColumns().getAllPhysical()); - - if (metadata_.sampling_key.definition_ast != nullptr) - { - const auto & pk_sample_block = getPrimaryKey().sample_block; - if (!pk_sample_block.has(metadata_.sampling_key.column_names[0]) && !attach - && !settings->compatibility_allow_sampling_expression_not_in_primary_key) /// This is for backward compatibility. - throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); - } - MergeTreeDataFormatVersion min_format_version(0); + /// TODO(alesap) Move to register methods if (!date_column_name.empty()) { try { auto partition_by_ast = makeASTFunction("toYYYYMM", std::make_shared(date_column_name)); - auto partition_key = KeyDescription::getKeyFromAST(partition_by_ast, getColumns(), global_context); - initPartitionKey(partition_key); + metadata_.partition_key = KeyDescription::getKeyFromAST(partition_by_ast, metadata_.columns, global_context); + initPartitionKey(metadata_.partition_key); if (minmax_idx_date_column_pos == -1) throw Exception("Could not find Date column", ErrorCodes::BAD_TYPE_OF_FIELD); @@ -182,6 +169,21 @@ MergeTreeData::MergeTreeData( min_format_version = MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING; } + setProperties(metadata_, attach); + const auto settings = getSettings(); + + /// NOTE: using the same columns list as is read when performing actual merges. + merging_params.check(getColumns().getAllPhysical()); + + if (metadata_.sampling_key.definition_ast != nullptr) + { + const auto & pk_sample_block = getPrimaryKey().sample_block; + if (!pk_sample_block.has(metadata_.sampling_key.column_names[0]) && !attach + && !settings->compatibility_allow_sampling_expression_not_in_primary_key) /// This is for backward compatibility. + throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); + } + + setTTLExpressions(metadata_); /// format_file always contained on any data path diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index 007c6898e608..12350b7bd100 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -323,7 +323,7 @@ class MergeTreeData : public IStorage /// attach - whether the existing table is attached or the new table is created. MergeTreeData(const StorageID & table_id_, const String & relative_data_path_, - const StorageInMemoryMetadata & metadata_, + StorageInMemoryMetadata metadata_, Context & context_, const String & date_column_name, const MergingParams & merging_params_, From 33a74a3ea05ee7ff405e7255c7faeeae08de144c Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 20:50:53 +0300 Subject: [PATCH 0317/1102] TTL methods in StorageInMemoryMetadata --- src/Storages/IStorage.cpp | 11 ----------- src/Storages/IStorage.h | 2 -- src/Storages/MergeTree/MergeTreeData.cpp | 4 ++-- src/Storages/StorageInMemoryMetadata.cpp | 10 ++++++++++ src/Storages/StorageInMemoryMetadata.h | 4 ++++ src/Storages/StorageMergeTree.cpp | 1 - 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 2bbbabbff08c..afe610085536 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -506,11 +506,6 @@ TTLTableDescription IStorage::getTableTTLs() const return metadata->table_ttl; } -void IStorage::setTableTTLs(const TTLTableDescription & table_ttl_) -{ - std::lock_guard lock(ttl_mutex); - metadata->table_ttl = table_ttl_; -} bool IStorage::hasAnyTableTTL() const { @@ -523,12 +518,6 @@ TTLColumnsDescription IStorage::getColumnTTLs() const return metadata->column_ttls_by_name; } -void IStorage::setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_) -{ - std::lock_guard lock(ttl_mutex); - metadata->column_ttls_by_name = column_ttls_by_name_; -} - bool IStorage::hasAnyColumnTTL() const { std::lock_guard lock(ttl_mutex); diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index ec7e8fc1795c..f3081386c767 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -500,12 +500,10 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Common tables TTLs (for rows and moves). TTLTableDescription getTableTTLs() const; - void setTableTTLs(const TTLTableDescription & table_ttl_); bool hasAnyTableTTL() const; /// Separate TTLs for columns. TTLColumnsDescription getColumnTTLs() const; - void setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_); bool hasAnyColumnTTL() const; /// Just wrapper for table TTLs, return rows part of table TTLs. diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 8af38bdf5007..24c787e7c635 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -520,8 +520,8 @@ void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_meta void MergeTreeData::setTTLExpressions(const StorageInMemoryMetadata & new_metadata) { checkTTLExpressions(new_metadata); - setColumnTTLs(new_metadata.column_ttls_by_name); - setTableTTLs(new_metadata.table_ttl); + //setColumnTTLs(new_metadata.column_ttls_by_name); + //setTableTTLs(new_metadata.table_ttl); } diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index ac2c0417c450..2d29ac433e9b 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -66,4 +66,14 @@ void StorageInMemoryMetadata::setConstraints(ConstraintsDescription constraints_ constraints = std::move(constraints_); } +void StorageInMemoryMetadata::setTableTTLs(const TTLTableDescription & table_ttl_) +{ + table_ttl = table_ttl_; +} + +void StorageInMemoryMetadata::setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_) +{ + column_ttls_by_name = column_ttls_by_name_; +} + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 3b3c9d07c893..b5c1a1997b63 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -69,6 +69,10 @@ struct StorageInMemoryMetadata /// Set sampling key for storage (methods bellow, are just wrappers for this /// struct). void setSamplingKey(const KeyDescription & sampling_key_); + + void setTableTTLs(const TTLTableDescription & table_ttl_); + + void setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_); }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 2a7efa164d4c..7007a544eac7 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -281,7 +281,6 @@ void StorageMergeTree::alter( changeSettings(new_metadata.settings_changes, table_lock_holder); /// Reinitialize primary key because primary key column types might have changed. setProperties(new_metadata); - setTTLExpressions(new_metadata); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); From b47a7327fdbd5f2753b84aed98595c1a7d4df5e3 Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 21:08:05 +0300 Subject: [PATCH 0318/1102] All set methods in metadata --- src/Storages/IStorage.cpp | 13 ------------- src/Storages/IStorage.h | 2 -- src/Storages/MergeTree/MergeTreeData.cpp | 4 +++- src/Storages/StorageInMemoryMetadata.cpp | 13 +++++++++++++ src/Storages/StorageInMemoryMetadata.h | 4 ++++ src/Storages/StorageMaterializedView.cpp | 6 +++--- src/Storages/StorageReplicatedMergeTree.cpp | 1 - src/Storages/StorageView.cpp | 4 ++-- 8 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index afe610085536..e5ab14e046e2 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -616,24 +616,11 @@ ASTPtr IStorage::getSettingsChanges() const return nullptr; } -void IStorage::setSettingsChanges(const ASTPtr & settings_changes_) -{ - if (settings_changes_) - metadata->settings_changes = settings_changes_->clone(); - else - metadata->settings_changes = nullptr; -} - const SelectQueryDescription & IStorage::getSelectQuery() const { return metadata->select; } -void IStorage::setSelectQuery(const SelectQueryDescription & select_) -{ - metadata->select = select_; -} - bool IStorage::hasSelectQuery() const { return metadata->select.select_query != nullptr; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index f3081386c767..0f48f3bf63ce 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -148,12 +148,10 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Storage settings ASTPtr getSettingsChanges() const; - void setSettingsChanges(const ASTPtr & settings_changes_); bool hasSettingsChanges() const { return metadata->settings_changes != nullptr; } /// Select query for *View storages. const SelectQueryDescription & getSelectQuery() const; - void setSelectQuery(const SelectQueryDescription & select_); bool hasSelectQuery() const; StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 24c787e7c635..8971b50a0fdc 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -1474,7 +1474,9 @@ void MergeTreeData::changeSettings( MergeTreeSettings copy = *getSettings(); copy.applyChanges(new_changes); storage_settings.set(std::make_unique(copy)); - setSettingsChanges(new_settings); + StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + new_metadata.setSettingsChanges(new_settings); + setInMemoryMetadata(new_metadata); } } diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 2d29ac433e9b..b6dd2f38c4e9 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -76,4 +76,17 @@ void StorageInMemoryMetadata::setColumnTTLs(const TTLColumnsDescription & column column_ttls_by_name = column_ttls_by_name_; } +void StorageInMemoryMetadata::setSettingsChanges(const ASTPtr & settings_changes_) +{ + if (settings_changes_) + settings_changes = settings_changes_; + else + settings_changes = nullptr; +} + +void StorageInMemoryMetadata::setSelectQuery(const SelectQueryDescription & select_) +{ + select = select_; +} + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index b5c1a1997b63..b129cdc7756f 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -73,6 +73,10 @@ struct StorageInMemoryMetadata void setTableTTLs(const TTLTableDescription & table_ttl_); void setColumnTTLs(const TTLColumnsDescription & column_ttls_by_name_); + + void setSettingsChanges(const ASTPtr & settings_changes_); + + void setSelectQuery(const SelectQueryDescription & select_); }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 34d5e1d43749..638a13612f29 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -52,7 +52,6 @@ StorageMaterializedView::StorageMaterializedView( { StorageInMemoryMetadata metadata_; metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); @@ -68,7 +67,8 @@ StorageMaterializedView::StorageMaterializedView( throw Exception("UNION is not supported for MATERIALIZED VIEW", ErrorCodes::QUERY_IS_NOT_SUPPORTED_IN_MATERIALIZED_VIEW); auto select = SelectQueryDescription::getSelectQueryFromASTForMatView(query.select->clone(), local_context); - setSelectQuery(select); + metadata_.setSelectQuery(select); + setInMemoryMetadata(metadata_); if (!has_inner_table) target_table_id = query.to_table_id; @@ -206,7 +206,7 @@ void StorageMaterializedView::alter( DatabaseCatalog::instance().updateDependency(old_select.select_table_id, table_id, new_select.select_table_id, table_id); - setSelectQuery(new_select); + new_metadata.setSelectQuery(new_select); } /// end modify query diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index d8e45b974385..cb5e5aaf701d 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3648,7 +3648,6 @@ void StorageReplicatedMergeTree::alter( StorageInMemoryMetadata future_metadata = getInMemoryMetadata(); params.apply(future_metadata, query_context); - changeSettings(future_metadata.settings_changes, table_lock_holder); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(query_context, table_id, future_metadata); diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 60ae681e0022..d8392b2edd85 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -40,7 +40,6 @@ StorageView::StorageView( { StorageInMemoryMetadata metadata_; metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); @@ -48,7 +47,8 @@ StorageView::StorageView( SelectQueryDescription description; description.inner_query = query.select->ptr(); - setSelectQuery(description); + metadata_.setSelectQuery(description); + setInMemoryMetadata(metadata_); } From 68f8372c129527fdd7e1246e309751b676b71885 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 15 Jun 2020 21:41:47 +0300 Subject: [PATCH 0319/1102] Fix build. --- src/Processors/QueryPlan/ReadFromStorageStep.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.h b/src/Processors/QueryPlan/ReadFromStorageStep.h index 180cf47e6d7d..ac662c4b06f3 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.h +++ b/src/Processors/QueryPlan/ReadFromStorageStep.h @@ -9,9 +9,9 @@ namespace DB class IStorage; using StoragePtr = std::shared_ptr; -class SelectQueryInfo; +struct SelectQueryInfo; -class PrewhereInfo; +struct PrewhereInfo; /// Reads from storage. class ReadFromStorageStep : public IQueryPlanStep @@ -28,7 +28,7 @@ class ReadFromStorageStep : public IQueryPlanStep size_t max_block_size, size_t max_streams); - virtual ~ReadFromStorageStep(); + ~ReadFromStorageStep() override; String getName() const override { return "ReadFromStorage"; } From 012fb819dbaf1005946040f0b5dbc17a63e398f7 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 15 Jun 2020 21:49:41 +0300 Subject: [PATCH 0320/1102] Fix build. --- src/Processors/QueryPlan/QueryPlan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 82bc42108250..6e3535f27d4f 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace DB From 36ba0192df07424d5c5b7c1ca8a197648238c38a Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 15 Jun 2020 22:08:58 +0300 Subject: [PATCH 0321/1102] Metadata in read and write methods of IStorage --- src/Core/ExternalTable.cpp | 2 +- .../CreatingSetsBlockInputStream.cpp | 2 +- .../PushingToViewsBlockOutputStream.cpp | 2 +- src/DataStreams/RemoteQueryExecutor.cpp | 7 +++++-- src/Interpreters/InterpreterInsertQuery.cpp | 3 ++- src/Interpreters/InterpreterSelectQuery.cpp | 4 ++-- src/Interpreters/InterpreterSelectQuery.h | 1 + .../Transforms/CreatingSetsTransform.cpp | 2 +- src/Server/TCPHandler.cpp | 3 ++- src/Storages/IStorage.h | 3 +++ src/Storages/Kafka/StorageKafka.cpp | 3 ++- src/Storages/Kafka/StorageKafka.h | 2 ++ src/Storages/LiveView/StorageBlocks.h | 1 + src/Storages/LiveView/StorageLiveView.cpp | 1 + src/Storages/LiveView/StorageLiveView.h | 1 + .../MergeTree/StorageFromMergeTreeDataPart.h | 1 + src/Storages/StorageBuffer.cpp | 13 ++++++++++--- src/Storages/StorageBuffer.h | 3 ++- src/Storages/StorageDictionary.cpp | 1 + src/Storages/StorageDictionary.h | 4 +++- src/Storages/StorageDistributed.cpp | 3 ++- src/Storages/StorageDistributed.h | 3 ++- src/Storages/StorageFile.cpp | 2 ++ src/Storages/StorageFile.h | 2 ++ src/Storages/StorageGenerateRandom.cpp | 1 + src/Storages/StorageGenerateRandom.h | 1 + src/Storages/StorageHDFS.cpp | 5 +++-- src/Storages/StorageHDFS.h | 6 ++++-- src/Storages/StorageInput.cpp | 4 +++- src/Storages/StorageInput.h | 1 + src/Storages/StorageJoin.cpp | 1 + src/Storages/StorageJoin.h | 1 + src/Storages/StorageLog.cpp | 4 ++-- src/Storages/StorageLog.h | 3 ++- src/Storages/StorageMaterializedView.cpp | 11 ++++++++--- src/Storages/StorageMaterializedView.h | 3 ++- src/Storages/StorageMemory.cpp | 4 ++-- src/Storages/StorageMemory.h | 3 ++- src/Storages/StorageMerge.cpp | 4 +++- src/Storages/StorageMerge.h | 1 + src/Storages/StorageMergeTree.cpp | 3 ++- src/Storages/StorageMergeTree.h | 3 ++- src/Storages/StorageMySQL.cpp | 4 ++-- src/Storages/StorageMySQL.h | 3 ++- src/Storages/StorageNull.h | 3 ++- src/Storages/StorageReplicatedMergeTree.cpp | 3 ++- src/Storages/StorageReplicatedMergeTree.h | 3 ++- src/Storages/StorageS3.cpp | 3 ++- src/Storages/StorageS3.h | 3 ++- src/Storages/StorageSet.cpp | 2 +- src/Storages/StorageSet.h | 2 +- src/Storages/StorageStripeLog.cpp | 4 ++-- src/Storages/StorageStripeLog.h | 3 ++- src/Storages/StorageTinyLog.cpp | 4 ++-- src/Storages/StorageTinyLog.h | 3 ++- src/Storages/StorageURL.cpp | 6 ++++-- src/Storages/StorageURL.h | 3 ++- src/Storages/StorageValues.cpp | 1 + src/Storages/StorageValues.h | 1 + src/Storages/StorageView.cpp | 1 + src/Storages/StorageView.h | 1 + src/Storages/StorageXDBC.cpp | 8 +++++--- src/Storages/StorageXDBC.h | 17 +++++++++-------- src/Storages/System/IStorageSystemOneBlock.h | 4 +++- src/Storages/System/StorageSystemColumns.cpp | 1 + src/Storages/System/StorageSystemColumns.h | 1 + .../System/StorageSystemDetachedParts.cpp | 13 +++++++------ src/Storages/System/StorageSystemDisks.cpp | 1 + src/Storages/System/StorageSystemDisks.h | 1 + src/Storages/System/StorageSystemNumbers.cpp | 1 + src/Storages/System/StorageSystemNumbers.h | 1 + src/Storages/System/StorageSystemOne.cpp | 1 + src/Storages/System/StorageSystemOne.h | 1 + src/Storages/System/StorageSystemPartsBase.cpp | 13 +++++++------ src/Storages/System/StorageSystemPartsBase.h | 13 +++++++------ src/Storages/System/StorageSystemReplicas.cpp | 1 + src/Storages/System/StorageSystemReplicas.h | 1 + .../System/StorageSystemStoragePolicies.cpp | 13 +++++++------ .../System/StorageSystemStoragePolicies.h | 13 +++++++------ src/Storages/System/StorageSystemTables.cpp | 1 + src/Storages/System/StorageSystemTables.h | 1 + src/Storages/System/StorageSystemZeros.cpp | 13 +++++++------ src/Storages/System/StorageSystemZeros.h | 13 +++++++------ src/Storages/tests/gtest_storage_log.cpp | 6 ++++-- 84 files changed, 208 insertions(+), 111 deletions(-) diff --git a/src/Core/ExternalTable.cpp b/src/Core/ExternalTable.cpp index 5ec6980dbfa4..3639a109b429 100644 --- a/src/Core/ExternalTable.cpp +++ b/src/Core/ExternalTable.cpp @@ -167,7 +167,7 @@ void ExternalTablesHandler::handlePart(const Poco::Net::MessageHeader & header, auto temporary_table = TemporaryTableHolder(context, ColumnsDescription{columns}, {}); auto storage = temporary_table.getTable(); context.addExternalTable(data->table_name, std::move(temporary_table)); - BlockOutputStreamPtr output = storage->write(ASTPtr(), context); + BlockOutputStreamPtr output = storage->write(ASTPtr(), storage->getInMemoryMetadataPtr(), context); /// Write data auto sink = std::make_shared(std::move(output)); diff --git a/src/DataStreams/CreatingSetsBlockInputStream.cpp b/src/DataStreams/CreatingSetsBlockInputStream.cpp index e40b5979b056..2a2275a4e892 100644 --- a/src/DataStreams/CreatingSetsBlockInputStream.cpp +++ b/src/DataStreams/CreatingSetsBlockInputStream.cpp @@ -101,7 +101,7 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery) BlockOutputStreamPtr table_out; if (subquery.table) - table_out = subquery.table->write({}, context); + table_out = subquery.table->write({}, subquery.table->getInMemoryMetadataPtr(), context); bool done_with_set = !subquery.set; bool done_with_join = !subquery.join; diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index 2c2e69721585..fa213b054df6 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -107,7 +107,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( /// Do not push to destination table if the flag is set if (!no_destination) { - output = storage->write(query_ptr, context); + output = storage->write(query_ptr, storage->getInMemoryMetadataPtr(), context); replicated_output = dynamic_cast(output.get()); } } diff --git a/src/DataStreams/RemoteQueryExecutor.cpp b/src/DataStreams/RemoteQueryExecutor.cpp index cf3b2c4abcdb..071cb6e9aba4 100644 --- a/src/DataStreams/RemoteQueryExecutor.cpp +++ b/src/DataStreams/RemoteQueryExecutor.cpp @@ -319,12 +319,15 @@ void RemoteQueryExecutor::sendExternalTables() for (const auto & table : external_tables) { StoragePtr cur = table.second; + auto metadata_snapshot = cur->getInMemoryMetadataPtr(); QueryProcessingStage::Enum read_from_table_stage = cur->getQueryProcessingStage(context); Pipes pipes; - pipes = cur->read(cur->getColumns().getNamesOfPhysical(), {}, context, - read_from_table_stage, DEFAULT_BLOCK_SIZE, 1); + pipes = cur->read( + cur->getColumns().getNamesOfPhysical(), + metadata_snapshot, {}, context, + read_from_table_stage, DEFAULT_BLOCK_SIZE, 1); auto data = std::make_unique(); data->table_name = table.first; diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index 7deed262eda6..1841c82b7101 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -117,6 +117,7 @@ BlockIO InterpreterInsertQuery::execute() StoragePtr table = getTable(query); auto table_lock = table->lockStructureForShare( true, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); auto query_sample_block = getSampleBlock(query, table); if (!query.table_function) @@ -226,7 +227,7 @@ BlockIO InterpreterInsertQuery::execute() /// NOTE: we explicitly ignore bound materialized views when inserting into Kafka Storage. /// Otherwise we'll get duplicates when MV reads same rows again from Kafka. if (table->noPushingToViews() && !no_destination) - out = table->write(query_ptr, context); + out = table->write(query_ptr, metadata_snapshot, context); else out = std::make_shared(table, context, query_ptr, no_destination); diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index ac17a3042d86..c22296cfb266 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -255,6 +255,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( table_lock = storage->lockStructureForShare( false, context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout); table_id = storage->getStorageID(); + metadata_snapshot = storage->getInMemoryMetadataPtr(); } if (has_input || !joined_tables.resolveTables()) @@ -1293,7 +1294,6 @@ void InterpreterSelectQuery::executeFetchColumns( else if (storage) { /// Table. - if (max_streams == 0) throw Exception("Logical error: zero number of streams requested", ErrorCodes::LOGICAL_ERROR); @@ -1324,7 +1324,7 @@ void InterpreterSelectQuery::executeFetchColumns( query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); } - Pipes pipes = storage->read(required_columns, query_info, *context, processing_stage, max_block_size, max_streams); + Pipes pipes = storage->read(required_columns, metadata_snapshot, query_info, *context, processing_stage, max_block_size, max_streams); if (pipes.empty()) { diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 8ed775f60ae4..8f7237ffd7ee 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -184,6 +184,7 @@ class InterpreterSelectQuery : public IInterpreter StoragePtr storage; StorageID table_id = StorageID::createEmpty(); /// Will be initialized if storage is not nullptr TableStructureReadLockHolder table_lock; + StorageMetadataPtr metadata_snapshot; /// Used when we read from prepared input, not table or subquery. BlockInputStreamPtr input; diff --git a/src/Processors/Transforms/CreatingSetsTransform.cpp b/src/Processors/Transforms/CreatingSetsTransform.cpp index af8fa4097df7..c0e34d9fbd4c 100644 --- a/src/Processors/Transforms/CreatingSetsTransform.cpp +++ b/src/Processors/Transforms/CreatingSetsTransform.cpp @@ -66,7 +66,7 @@ void CreatingSetsTransform::startSubquery(SubqueryForSet & subquery) elapsed_nanoseconds = 0; if (subquery.table) - table_out = subquery.table->write({}, context); + table_out = subquery.table->write({}, subquery.table->getInMemoryMetadataPtr(), context); done_with_set = !subquery.set; done_with_join = !subquery.join; diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 7e17604c4c7c..a01cc4fa0aae 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -952,8 +952,9 @@ bool TCPHandler::receiveData(bool scalar) storage = temporary_table.getTable(); query_context->addExternalTable(temporary_id.table_name, std::move(temporary_table)); } + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); /// The data will be written directly to the table. - state.io.out = storage->write(ASTPtr(), *query_context); + state.io.out = storage->write(ASTPtr(), metadata_snapshot, *query_context); } if (state.need_receive_data_for_input) state.block_for_input = block; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 0f48f3bf63ce..28ad7b0ea8b0 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -155,6 +155,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo bool hasSelectQuery() const; StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } + StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } Block getSampleBlock() const; /// ordinary + materialized. @@ -292,6 +293,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo */ virtual Pipes read( const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, @@ -309,6 +311,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo */ virtual BlockOutputStreamPtr write( const ASTPtr & /*query*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { throw Exception("Method write is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index 2109afed9324..190397bc6751 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -201,6 +201,7 @@ String StorageKafka::getDefaultClientId(const StorageID & table_id_) Pipes StorageKafka::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /* query_info */, const Context & context, QueryProcessingStage::Enum /* processed_stage */, @@ -231,7 +232,7 @@ Pipes StorageKafka::read( } -BlockOutputStreamPtr StorageKafka::write(const ASTPtr &, const Context & context) +BlockOutputStreamPtr StorageKafka::write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { auto modified_context = std::make_shared(context); modified_context->applySettingsChanges(settings_adjustments); diff --git a/src/Storages/Kafka/StorageKafka.h b/src/Storages/Kafka/StorageKafka.h index be3f89687fef..6f479ba20892 100644 --- a/src/Storages/Kafka/StorageKafka.h +++ b/src/Storages/Kafka/StorageKafka.h @@ -39,6 +39,7 @@ class StorageKafka final : public ext::shared_ptr_helper, public I Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -47,6 +48,7 @@ class StorageKafka final : public ext::shared_ptr_helper, public I BlockOutputStreamPtr write( const ASTPtr & query, + const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void pushReadBuffer(ConsumerBufferPtr buf); diff --git a/src/Storages/LiveView/StorageBlocks.h b/src/Storages/LiveView/StorageBlocks.h index 78d60163d5e9..56fd0c620c2f 100644 --- a/src/Storages/LiveView/StorageBlocks.h +++ b/src/Storages/LiveView/StorageBlocks.h @@ -37,6 +37,7 @@ class StorageBlocks : public IStorage Pipes read( const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index ade2d1c967d1..cb4964f3c55f 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -520,6 +520,7 @@ void StorageLiveView::refresh(const Context & context) Pipes StorageLiveView::read( const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/LiveView/StorageLiveView.h b/src/Storages/LiveView/StorageLiveView.h index 458e74eb5062..13386c7a4e69 100644 --- a/src/Storages/LiveView/StorageLiveView.h +++ b/src/Storages/LiveView/StorageLiveView.h @@ -126,6 +126,7 @@ friend class LiveViewBlockOutputStream; Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h index 342a89c38ea2..826af505b120 100644 --- a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h +++ b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h @@ -21,6 +21,7 @@ class StorageFromMergeTreeDataPart final : public ext::shared_ptr_helpergetInMemoryMetadataPtr(); if (destination.get() == this) throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP); @@ -177,7 +179,9 @@ Pipes StorageBuffer::read( query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination); /// The destination table has the same structure of the requested columns and we can simply read blocks from there. - pipes_from_dst = destination->read(column_names, query_info, context, processed_stage, max_block_size, num_streams); + pipes_from_dst = destination->read( + column_names, destination_metadata_snapshot, query_info, + context, processed_stage, max_block_size, num_streams); } else { @@ -210,7 +214,10 @@ Pipes StorageBuffer::read( } else { - pipes_from_dst = destination->read(columns_intersection, query_info, context, processed_stage, max_block_size, num_streams); + pipes_from_dst = destination->read( + columns_intersection, destination_metadata_snapshot, query_info, + context, processed_stage, max_block_size, num_streams); + for (auto & pipe : pipes_from_dst) { pipe.addSimpleTransform(std::make_shared( @@ -425,7 +432,7 @@ class BufferBlockOutputStream : public IBlockOutputStream }; -BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { return std::make_shared(*this); } diff --git a/src/Storages/StorageBuffer.h b/src/Storages/StorageBuffer.h index 02fd35136bf0..7cd73dc556c0 100644 --- a/src/Storages/StorageBuffer.h +++ b/src/Storages/StorageBuffer.h @@ -57,13 +57,14 @@ friend class BufferBlockOutputStream; Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void startup() override; /// Flush all buffers into the subordinate table and stop background thread. diff --git a/src/Storages/StorageDictionary.cpp b/src/Storages/StorageDictionary.cpp index 4348973ec604..25126ad951da 100644 --- a/src/Storages/StorageDictionary.cpp +++ b/src/Storages/StorageDictionary.cpp @@ -113,6 +113,7 @@ void StorageDictionary::checkTableCanBeDropped() const Pipes StorageDictionary::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageDictionary.h b/src/Storages/StorageDictionary.h index 7bb6fc224808..6175902381b8 100644 --- a/src/Storages/StorageDictionary.h +++ b/src/Storages/StorageDictionary.h @@ -16,7 +16,9 @@ class StorageDictionary final : public ext::shared_ptr_helper void checkTableCanBeDropped() const override; - Pipes read(const Names & column_names, + Pipes read( + const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index bf5f729ed198..719811bbc6b2 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -464,6 +464,7 @@ QueryProcessingStage::Enum StorageDistributed::getQueryProcessingStage(const Con Pipes StorageDistributed::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -510,7 +511,7 @@ Pipes StorageDistributed::read( } -BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const Context & context) +BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { auto cluster = getCluster(); const auto & settings = context.getSettingsRef(); diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index 63021e0a1696..3f148cfff018 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -70,13 +70,14 @@ class StorageDistributed final : public ext::shared_ptr_helper, public ISt Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -34,6 +35,7 @@ class StorageFile final : public ext::shared_ptr_helper, public ISt BlockOutputStreamPtr write( const ASTPtr & query, + const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void truncate(const ASTPtr & /*query*/, const Context & /* context */, TableStructureWriteLockHolder &) override; diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index f69478a4bddd..f1d97a4e5c4f 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -429,6 +429,7 @@ void registerStorageGenerateRandom(StorageFactory & factory) Pipes StorageGenerateRandom::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageGenerateRandom.h b/src/Storages/StorageGenerateRandom.h index 955b8bd671dc..0d068eb951e4 100644 --- a/src/Storages/StorageGenerateRandom.h +++ b/src/Storages/StorageGenerateRandom.h @@ -17,6 +17,7 @@ class StorageGenerateRandom final : public ext::shared_ptr_helper(uri, format_name, diff --git a/src/Storages/StorageHDFS.h b/src/Storages/StorageHDFS.h index 5b250247b84e..62425cc518ff 100644 --- a/src/Storages/StorageHDFS.h +++ b/src/Storages/StorageHDFS.h @@ -19,14 +19,16 @@ class StorageHDFS final : public ext::shared_ptr_helper, public ISt public: String getName() const override { return "HDFS"; } - Pipes read(const Names & column_names, + Pipes read( + const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; NamesAndTypesList getVirtuals() const override; diff --git a/src/Storages/StorageInput.cpp b/src/Storages/StorageInput.cpp index 92287051bf3c..4117a6b3a370 100644 --- a/src/Storages/StorageInput.cpp +++ b/src/Storages/StorageInput.cpp @@ -58,7 +58,9 @@ void StorageInput::setInputStream(BlockInputStreamPtr input_stream_) } -Pipes StorageInput::read(const Names & /*column_names*/, +Pipes StorageInput::read( + const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageInput.h b/src/Storages/StorageInput.h index 60bda222c2e8..f4425ee8cd5e 100644 --- a/src/Storages/StorageInput.h +++ b/src/Storages/StorageInput.h @@ -19,6 +19,7 @@ class StorageInput final : public ext::shared_ptr_helper, public I Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 5cceefe907bf..7ed4c1c110b2 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -435,6 +435,7 @@ class JoinSource : public SourceWithProgress // TODO: multiple stream read and index read Pipes StorageJoin::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageJoin.h b/src/Storages/StorageJoin.h index f956abb4d3b6..40dbf1b44dd9 100644 --- a/src/Storages/StorageJoin.h +++ b/src/Storages/StorageJoin.h @@ -38,6 +38,7 @@ class StorageJoin final : public ext::shared_ptr_helper, public Sto Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index 09be868bcfa6..a09a99b30e12 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -577,6 +577,7 @@ const StorageLog::Marks & StorageLog::getMarksWithRealRowCount() const Pipes StorageLog::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -620,8 +621,7 @@ Pipes StorageLog::read( return pipes; } -BlockOutputStreamPtr StorageLog::write( - const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { loadMarks(); return std::make_shared(*this); diff --git a/src/Storages/StorageLog.h b/src/Storages/StorageLog.h index 2c2abdb02756..60f885ce45c3 100644 --- a/src/Storages/StorageLog.h +++ b/src/Storages/StorageLog.h @@ -26,13 +26,14 @@ class StorageLog final : public ext::shared_ptr_helper, public IStor Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 638a13612f29..a387eadabe00 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -108,6 +108,7 @@ QueryProcessingStage::Enum StorageMaterializedView::getQueryProcessingStage(cons Pipes StorageMaterializedView::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -117,11 +118,12 @@ Pipes StorageMaterializedView::read( auto storage = getTargetTable(); auto lock = storage->lockStructureForShare( false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); if (query_info.order_optimizer) query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); - Pipes pipes = storage->read(column_names, query_info, context, processed_stage, max_block_size, num_streams); + Pipes pipes = storage->read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); for (auto & pipe : pipes) pipe.addTableLock(lock); @@ -129,12 +131,15 @@ Pipes StorageMaterializedView::read( return pipes; } -BlockOutputStreamPtr StorageMaterializedView::write(const ASTPtr & query, const Context & context) +BlockOutputStreamPtr StorageMaterializedView::write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { auto storage = getTargetTable(); auto lock = storage->lockStructureForShare( true, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - auto stream = storage->write(query, context); + + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); + auto stream = storage->write(query, metadata_snapshot, context); + stream->addTableLock(lock); return stream; } diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index 480c75aa114d..42fe186a068a 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -31,7 +31,7 @@ class StorageMaterializedView final : public ext::shared_ptr_helpermayBenefitFromIndexForIn(left_in_operand, query_context); } - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void drop() override; @@ -63,6 +63,7 @@ class StorageMaterializedView final : public ext::shared_ptr_helper(*this); } diff --git a/src/Storages/StorageMemory.h b/src/Storages/StorageMemory.h index 5a79358d76dd..3c5835334623 100644 --- a/src/Storages/StorageMemory.h +++ b/src/Storages/StorageMemory.h @@ -30,13 +30,14 @@ friend struct ext::shared_ptr_helper; Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void drop() override; diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 3685a777bf0a..6656e91189cd 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -129,6 +129,7 @@ QueryProcessingStage::Enum StorageMerge::getQueryProcessingStage(const Context & Pipes StorageMerge::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -243,6 +244,7 @@ Pipes StorageMerge::createSources(const SelectQueryInfo & query_info, const Quer return pipes; } + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); auto storage_stage = storage->getQueryProcessingStage(*modified_context, QueryProcessingStage::Complete, query_info.query); if (processed_stage <= storage_stage) { @@ -250,7 +252,7 @@ Pipes StorageMerge::createSources(const SelectQueryInfo & query_info, const Quer if (real_column_names.empty()) real_column_names.push_back(ExpressionActions::getSmallestColumn(storage->getColumns().getAllPhysical())); - pipes = storage->read(real_column_names, modified_query_info, *modified_context, processed_stage, max_block_size, UInt32(streams_num)); + pipes = storage->read(real_column_names, metadata_snapshot, modified_query_info, *modified_context, processed_stage, max_block_size, UInt32(streams_num)); } else if (processed_stage > storage_stage) { diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index adf4a40e675b..a5d3b8d26677 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -31,6 +31,7 @@ class StorageMerge final : public ext::shared_ptr_helper, public I Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 7007a544eac7..e3f48a05d6e0 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -177,6 +177,7 @@ StorageMergeTree::~StorageMergeTree() Pipes StorageMergeTree::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -196,7 +197,7 @@ std::optional StorageMergeTree::totalBytes() const return getTotalActiveSizeInBytes(); } -BlockOutputStreamPtr StorageMergeTree::write(const ASTPtr & /*query*/, const Context & context) +BlockOutputStreamPtr StorageMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { return std::make_shared(*this, context.getSettingsRef().max_partitions_per_insert_block); } diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index c6c8f99a62a3..679726826d40 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -39,6 +39,7 @@ class StorageMergeTree final : public ext::shared_ptr_helper, Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -48,7 +49,7 @@ class StorageMergeTree final : public ext::shared_ptr_helper, std::optional totalRows() const override; std::optional totalBytes() const override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; /** Perform the next step in combining the parts. */ diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index f9aad8a58a7d..dce9e0f38ecd 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -65,6 +65,7 @@ StorageMySQL::StorageMySQL( Pipes StorageMySQL::read( const Names & column_names_, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info_, const Context & context_, QueryProcessingStage::Enum /*processed_stage*/, @@ -198,8 +199,7 @@ class StorageMySQLBlockOutputStream : public IBlockOutputStream }; -BlockOutputStreamPtr StorageMySQL::write( - const ASTPtr & /*query*/, const Context & context) +BlockOutputStreamPtr StorageMySQL::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { return std::make_shared(*this, remote_database_name, remote_table_name, pool.get(), context.getSettingsRef().mysql_max_rows_to_insert); } diff --git a/src/Storages/StorageMySQL.h b/src/Storages/StorageMySQL.h index 8b98536e4d78..287c65db6f33 100644 --- a/src/Storages/StorageMySQL.h +++ b/src/Storages/StorageMySQL.h @@ -39,13 +39,14 @@ class StorageMySQL final : public ext::shared_ptr_helper, public I Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; private: friend class StorageMySQLBlockOutputStream; diff --git a/src/Storages/StorageNull.h b/src/Storages/StorageNull.h index fe8bd05d53a3..72934d185c72 100644 --- a/src/Storages/StorageNull.h +++ b/src/Storages/StorageNull.h @@ -24,6 +24,7 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo &, const Context & /*context*/, QueryProcessingStage::Enum /*processing_stage*/, @@ -35,7 +36,7 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt return pipes; } - BlockOutputStreamPtr write(const ASTPtr &, const Context &) override + BlockOutputStreamPtr write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context &) override { return std::make_shared(getSampleBlock()); } diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index cb5e5aaf701d..650578d75602 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3381,6 +3381,7 @@ ReplicatedMergeTreeQuorumAddedParts::PartitionIdToMaxBlock StorageReplicatedMerg Pipes StorageReplicatedMergeTree::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -3442,7 +3443,7 @@ void StorageReplicatedMergeTree::assertNotReadonly() const } -BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, const Context & context) +BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { const auto storage_settings_ptr = getSettings(); assertNotReadonly(); diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index aae0b9c81b82..5fcfd98e71d2 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -89,6 +89,7 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helper totalRows() const override; std::optional totalBytes() const override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & query_context) override; diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index acaa2bcc7d62..093f4450ecbf 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -285,6 +285,7 @@ Strings listFilesWithRegexpMatching(Aws::S3::S3Client & client, const S3::URI & Pipes StorageS3::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -320,7 +321,7 @@ Pipes StorageS3::read( return narrowPipes(std::move(pipes), num_streams); } -BlockOutputStreamPtr StorageS3::write(const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageS3::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { return std::make_shared( format_name, min_upload_part_size, getSampleBlock(), context_global, diff --git a/src/Storages/StorageS3.h b/src/Storages/StorageS3.h index fc19fe06da0f..665c00b8033c 100644 --- a/src/Storages/StorageS3.h +++ b/src/Storages/StorageS3.h @@ -48,13 +48,14 @@ class StorageS3 final : public ext::shared_ptr_helper, public IStorag Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; NamesAndTypesList getVirtuals() const override; diff --git a/src/Storages/StorageSet.cpp b/src/Storages/StorageSet.cpp index 38b4d30c25b7..cddd4657cd19 100644 --- a/src/Storages/StorageSet.cpp +++ b/src/Storages/StorageSet.cpp @@ -81,7 +81,7 @@ void SetOrJoinBlockOutputStream::writeSuffix() } -BlockOutputStreamPtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { UInt64 id = ++increment; return std::make_shared(*this, path, path + "tmp/", toString(id) + ".bin"); diff --git a/src/Storages/StorageSet.h b/src/Storages/StorageSet.h index cf85dfd5d5b0..b7785aadc6a2 100644 --- a/src/Storages/StorageSet.h +++ b/src/Storages/StorageSet.h @@ -21,7 +21,7 @@ class StorageSetOrJoinBase : public IStorage public: void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; Strings getDataPaths() const override { return {path}; } diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index b68505fa147e..c320d0afb42f 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -253,6 +253,7 @@ void StorageStripeLog::rename(const String & new_path_to_table_data, const Stora Pipes StorageStripeLog::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -299,8 +300,7 @@ Pipes StorageStripeLog::read( } -BlockOutputStreamPtr StorageStripeLog::write( - const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageStripeLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { return std::make_shared(*this); } diff --git a/src/Storages/StorageStripeLog.h b/src/Storages/StorageStripeLog.h index ed8e5da081e9..d06758a60e82 100644 --- a/src/Storages/StorageStripeLog.h +++ b/src/Storages/StorageStripeLog.h @@ -27,13 +27,14 @@ class StorageStripeLog final : public ext::shared_ptr_helper, Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 5bca6072da01..42b70f716f42 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -394,6 +394,7 @@ void StorageTinyLog::rename(const String & new_path_to_table_data, const Storage Pipes StorageTinyLog::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -413,8 +414,7 @@ Pipes StorageTinyLog::read( } -BlockOutputStreamPtr StorageTinyLog::write( - const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr StorageTinyLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { return std::make_shared(*this); } diff --git a/src/Storages/StorageTinyLog.h b/src/Storages/StorageTinyLog.h index 102ec76fda3f..a55bf6d0dcf9 100644 --- a/src/Storages/StorageTinyLog.h +++ b/src/Storages/StorageTinyLog.h @@ -26,13 +26,14 @@ class StorageTinyLog final : public ext::shared_ptr_helper, publ Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index 0301412e0297..0361718c6160 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -155,7 +155,9 @@ std::function IStorageURLBase::getReadPOSTDataCallback(con } -Pipes IStorageURLBase::read(const Names & column_names, +Pipes IStorageURLBase::read( + const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -183,7 +185,7 @@ Pipes IStorageURLBase::read(const Names & column_names, return pipes; } -BlockOutputStreamPtr IStorageURLBase::write(const ASTPtr & /*query*/, const Context & /*context*/) +BlockOutputStreamPtr IStorageURLBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) { return std::make_shared( uri, format_name, getSampleBlock(), context_global, diff --git a/src/Storages/StorageURL.h b/src/Storages/StorageURL.h index 5a6584f03015..ecd57024a44a 100644 --- a/src/Storages/StorageURL.h +++ b/src/Storages/StorageURL.h @@ -21,13 +21,14 @@ class IStorageURLBase : public IStorage public: Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; protected: IStorageURLBase( diff --git a/src/Storages/StorageValues.cpp b/src/Storages/StorageValues.cpp index 5ba36a936e23..bb29b4a0932c 100644 --- a/src/Storages/StorageValues.cpp +++ b/src/Storages/StorageValues.cpp @@ -23,6 +23,7 @@ StorageValues::StorageValues( Pipes StorageValues::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageValues.h b/src/Storages/StorageValues.h index 254f3bfa8aaa..88fb023fb2b8 100644 --- a/src/Storages/StorageValues.h +++ b/src/Storages/StorageValues.h @@ -17,6 +17,7 @@ class StorageValues final : public ext::shared_ptr_helper, public Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index d8392b2edd85..52b7e8764d9f 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -54,6 +54,7 @@ StorageView::StorageView( Pipes StorageView::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/StorageView.h b/src/Storages/StorageView.h index c6b48d4d357f..143ed3c06c41 100644 --- a/src/Storages/StorageView.h +++ b/src/Storages/StorageView.h @@ -23,6 +23,7 @@ class StorageView final : public ext::shared_ptr_helper, public ISt Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/StorageXDBC.cpp b/src/Storages/StorageXDBC.cpp index 085387983891..c7fa8a882516 100644 --- a/src/Storages/StorageXDBC.cpp +++ b/src/Storages/StorageXDBC.cpp @@ -82,7 +82,9 @@ std::function StorageXDBC::getReadPOSTDataCallback(const N return [query](std::ostream & os) { os << "query=" << query; }; } -Pipes StorageXDBC::read(const Names & column_names, +Pipes StorageXDBC::read( + const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -92,10 +94,10 @@ Pipes StorageXDBC::read(const Names & column_names, check(column_names); bridge_helper->startBridgeSync(); - return IStorageURLBase::read(column_names, query_info, context, processed_stage, max_block_size, num_streams); + return IStorageURLBase::read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); } -BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const Context & context) +BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { bridge_helper->startBridgeSync(); diff --git a/src/Storages/StorageXDBC.h b/src/Storages/StorageXDBC.h index afc61dac5cd4..4488122656df 100644 --- a/src/Storages/StorageXDBC.h +++ b/src/Storages/StorageXDBC.h @@ -15,13 +15,14 @@ namespace DB class StorageXDBC : public IStorageURLBase { public: - - Pipes read(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum processed_stage, - size_t max_block_size, - unsigned num_streams) override; + Pipes read( + const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) override; StorageXDBC(const StorageID & table_id_, const std::string & remote_database_name, @@ -29,7 +30,7 @@ class StorageXDBC : public IStorageURLBase const ColumnsDescription & columns_, const Context & context_, BridgeHelperPtr bridge_helper_); - BlockOutputStreamPtr write(const ASTPtr & query, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; private: diff --git a/src/Storages/System/IStorageSystemOneBlock.h b/src/Storages/System/IStorageSystemOneBlock.h index 1ceff26ba83f..7644f62b96d5 100644 --- a/src/Storages/System/IStorageSystemOneBlock.h +++ b/src/Storages/System/IStorageSystemOneBlock.h @@ -28,7 +28,9 @@ class IStorageSystemOneBlock : public IStorage setInMemoryMetadata(metadata_); } - Pipes read(const Names & column_names, + Pipes read( + const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 6359e367106e..646a5434b647 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -242,6 +242,7 @@ class ColumnsSource : public SourceWithProgress Pipes StorageSystemColumns::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/System/StorageSystemColumns.h b/src/Storages/System/StorageSystemColumns.h index 66b423efb963..7336b4061832 100644 --- a/src/Storages/System/StorageSystemColumns.h +++ b/src/Storages/System/StorageSystemColumns.h @@ -19,6 +19,7 @@ class StorageSystemColumns final : public ext::shared_ptr_helper, Pipes read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index 42a432489f41..e599bbb19e3f 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -224,12 +224,13 @@ StoragesInfo StoragesInfoStream::next() } Pipes StorageSystemPartsBase::read( - const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum /*processed_stage*/, - const size_t /*max_block_size*/, - const unsigned /*num_streams*/) + const Names & column_names, + const StorageMetadataPtr & /*metadata_*/, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum /*processed_stage*/, + const size_t /*max_block_size*/, + const unsigned /*num_streams*/) { bool has_state_column = hasStateColumn(column_names); diff --git a/src/Storages/System/StorageSystemPartsBase.h b/src/Storages/System/StorageSystemPartsBase.h index 7b9ce7cbae24..a46cecec9dd1 100644 --- a/src/Storages/System/StorageSystemPartsBase.h +++ b/src/Storages/System/StorageSystemPartsBase.h @@ -56,12 +56,13 @@ class StorageSystemPartsBase : public IStorage { public: Pipes read( - const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum processed_stage, - size_t max_block_size, - unsigned num_streams) override; + const Names & column_names, + const StorageMetadataPtr & metadata_, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage, + size_t max_block_size, + unsigned num_streams) override; NamesAndTypesList getVirtuals() const override; diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index ca71e7e5f749..24861fcbd6a1 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -59,6 +59,7 @@ StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) Pipes StorageSystemReplicas::read( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, diff --git a/src/Storages/System/StorageSystemReplicas.h b/src/Storages/System/StorageSystemReplicas.h index 94b0d6c9d06a..b068ebc8b0ab 100644 --- a/src/Storages/System/StorageSystemReplicas.h +++ b/src/Storages/System/StorageSystemReplicas.h @@ -20,6 +20,7 @@ class StorageSystemReplicas final : public ext::shared_ptr_helpergetInMemoryMetadataPtr(); std::string data; @@ -97,7 +98,7 @@ std::string writeData(int rows, DB::StoragePtr & table, const DB::Context & cont block.insert(column); } - BlockOutputStreamPtr out = table->write({}, context); + BlockOutputStreamPtr out = table->write({}, metadata_snapshot, context); out->write(block); return data; @@ -107,13 +108,14 @@ std::string writeData(int rows, DB::StoragePtr & table, const DB::Context & cont std::string readData(DB::StoragePtr & table, const DB::Context & context) { using namespace DB; + auto metadata_snapshot = table->getInMemoryMetadataPtr(); Names column_names; column_names.push_back("a"); QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - BlockInputStreamPtr in = std::make_shared(std::move(table->read(column_names, {}, context, stage, 8192, 1)[0])); + BlockInputStreamPtr in = std::make_shared(std::move(table->read(column_names, metadata_snapshot, {}, context, stage, 8192, 1)[0])); Block sample; { From dbef88e073feed3a8531b7dea5297af9d7c81f12 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 15 Jun 2020 22:33:24 +0300 Subject: [PATCH 0322/1102] Fix tests. --- .../QueryPlan/ReadFromStorageStep.cpp | 39 ++++++------------- .../QueryPlan/ReadFromStorageStep.h | 2 + 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.cpp b/src/Processors/QueryPlan/ReadFromStorageStep.cpp index 191f501e9faf..bc108286ba16 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.cpp +++ b/src/Processors/QueryPlan/ReadFromStorageStep.cpp @@ -32,32 +32,9 @@ ReadFromStorageStep::ReadFromStorageStep( , max_block_size(max_block_size_) , max_streams(max_streams_) { - Block header = storage->getSampleBlockForColumns(required_columns); + /// Note: we read from storage in constructor of step because we don't know real header before reading. + /// It will be fixed when storage return QueryPlanStep itself. - if (query_info.prewhere_info) - { - if (query_info.prewhere_info->alias_actions) - header = ExpressionTransform::transformHeader(std::move(header), query_info.prewhere_info->alias_actions); - - header = FilterTransform::transformHeader( - std::move(header), - query_info.prewhere_info->prewhere_actions, - query_info.prewhere_info->prewhere_column_name, - query_info.prewhere_info->remove_prewhere_column); - - if (query_info.prewhere_info->remove_columns_actions) - header = ExpressionTransform::transformHeader( - std::move(header), - query_info.prewhere_info->remove_columns_actions); - } - - input_streams.emplace_back(DataStream{.header = std::move(header)}); -} - -ReadFromStorageStep::~ReadFromStorageStep() = default; - -QueryPipelinePtr ReadFromStorageStep::updatePipeline(QueryPipelines) -{ Pipes pipes = storage->read(required_columns, query_info, context, processing_stage, max_block_size, max_streams); if (pipes.empty()) @@ -95,7 +72,7 @@ QueryPipelinePtr ReadFromStorageStep::updatePipeline(QueryPipelines) pipe.getHeader(), input_streams.front().header, ConvertingTransform::MatchColumnsMode::Name)); } - auto pipeline = std::make_unique(); + pipeline = std::make_unique(); /// Table lock is stored inside pipeline here. pipeline->addTableLock(table_lock); @@ -148,7 +125,15 @@ QueryPipelinePtr ReadFromStorageStep::updatePipeline(QueryPipelines) pipe.enableQuota(); pipeline->init(std::move(pipes)); - return pipeline; + + input_streams.emplace_back(DataStream{.header = pipeline->getHeader()}); +} + +ReadFromStorageStep::~ReadFromStorageStep() = default; + +QueryPipelinePtr ReadFromStorageStep::updatePipeline(QueryPipelines) +{ + return std::move(pipeline); } } diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.h b/src/Processors/QueryPlan/ReadFromStorageStep.h index ac662c4b06f3..13d6b9e255d4 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.h +++ b/src/Processors/QueryPlan/ReadFromStorageStep.h @@ -45,6 +45,8 @@ class ReadFromStorageStep : public IQueryPlanStep QueryProcessingStage::Enum processing_stage; size_t max_block_size; size_t max_streams; + + QueryPipelinePtr pipeline; }; } From da111906774d997a8ce86cc8e35d4be02cffcd37 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Mon, 15 Jun 2020 23:20:45 +0300 Subject: [PATCH 0323/1102] Fix tests. --- src/Processors/QueryPlan/ReadFromStorageStep.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.cpp b/src/Processors/QueryPlan/ReadFromStorageStep.cpp index bc108286ba16..14c95cbe2c23 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.cpp +++ b/src/Processors/QueryPlan/ReadFromStorageStep.cpp @@ -65,13 +65,6 @@ ReadFromStorageStep::ReadFromStorageStep( pipes.emplace_back(std::move(pipe)); } - if (!blocksHaveEqualStructure(pipes.front().getHeader(), input_streams.front().header)) - { - for (auto & pipe : pipes) - pipe.addSimpleTransform(std::make_shared( - pipe.getHeader(), input_streams.front().header, ConvertingTransform::MatchColumnsMode::Name)); - } - pipeline = std::make_unique(); /// Table lock is stored inside pipeline here. From 977fd3e44fa08fe8427e04e75386894c785fba1f Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Tue, 16 Jun 2020 02:45:05 +0300 Subject: [PATCH 0324/1102] Update CMakeLists.txt --- base/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index a8dedec92693..cfa54fe2ca46 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -10,4 +10,4 @@ add_subdirectory (widechar_width) if (USE_MYSQL) add_subdirectory (mysqlxx) -endif () \ No newline at end of file +endif () From 92c7760c6e87f64dca55b78c12176c4f35b5de6c Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Tue, 16 Jun 2020 02:51:33 +0300 Subject: [PATCH 0325/1102] Update CMakeLists.txt --- base/daemon/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt index 36de193bccd2..04d2f059b392 100644 --- a/base/daemon/CMakeLists.txt +++ b/base/daemon/CMakeLists.txt @@ -10,4 +10,4 @@ target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickh if (USE_SENTRY) target_link_libraries (daemon PRIVATE curl) target_link_libraries (daemon PRIVATE ${SENTRY_LIBRARY}) -endif () \ No newline at end of file +endif () From 9b734ffded4cbf4e929bcfa2bb545c6d3e67938a Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Tue, 16 Jun 2020 03:21:20 +0300 Subject: [PATCH 0326/1102] Update http_server.py --- tests/integration/test_send_crash_reports/http_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_send_crash_reports/http_server.py b/tests/integration/test_send_crash_reports/http_server.py index e3fa2e1cb576..74f0592504f8 100644 --- a/tests/integration/test_send_crash_reports/http_server.py +++ b/tests/integration/test_send_crash_reports/http_server.py @@ -40,4 +40,4 @@ def __read_and_decode_post_data(self): try: httpd.serve_forever() finally: - httpd.server_close() \ No newline at end of file + httpd.server_close() From c43bd228ab6609d5971ff4002240697d817f8a31 Mon Sep 17 00:00:00 2001 From: Bharat Nallan Date: Sat, 13 Jun 2020 23:43:01 -0700 Subject: [PATCH 0327/1102] make max global thread pool setting configurable This PR adds a server level config for overriding the default max number of threads in global thread pool that is currently allowed (10,000). This might be useful in scenarios where there are a large number of distributed queries that are executing concurrently and where the default number of max threads might not be necessarily be sufficient. --- programs/server/config.xml | 10 ++++++++++ src/Common/ThreadPool.cpp | 14 +++++++++++++- src/Common/ThreadPool.h | 4 +++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/programs/server/config.xml b/programs/server/config.xml index 0ceba85593a4..f4c0f5a22fca 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -136,6 +136,16 @@ --> 0 + + + + 10000 + 0.9 diff --git a/src/Common/ThreadPool.cpp b/src/Common/ThreadPool.cpp index 3e6e31ed3fc1..edfb52e01acb 100644 --- a/src/Common/ThreadPool.cpp +++ b/src/Common/ThreadPool.cpp @@ -3,6 +3,10 @@ #include +#include +#include + + namespace DB { @@ -264,6 +268,14 @@ template class ThreadPoolImpl; GlobalThreadPool & GlobalThreadPool::instance() { - static GlobalThreadPool ret; + const Poco::Util::LayeredConfiguration & config = Poco::Util::Application::instance().config(); + + UInt64 max_threads = config.getUInt64("max_thread_pool_size", 10000); + size_t max_free_threads = 1000; + size_t max_queue_size = 10000; + const bool shutdown_on_exception = false; + + static GlobalThreadPool ret(max_threads, max_free_threads, max_queue_size, shutdown_on_exception); + return ret; } diff --git a/src/Common/ThreadPool.h b/src/Common/ThreadPool.h index 9d5582db50cf..3d1169d618d4 100644 --- a/src/Common/ThreadPool.h +++ b/src/Common/ThreadPool.h @@ -129,7 +129,9 @@ using FreeThreadPool = ThreadPoolImpl; class GlobalThreadPool : public FreeThreadPool, private boost::noncopyable { public: - GlobalThreadPool() : FreeThreadPool(10000, 1000, 10000, false) {} + GlobalThreadPool(size_t max_threads_, size_t max_free_threads_, size_t queue_size_, + const bool shutdown_on_exception_) : + FreeThreadPool(max_threads_, max_free_threads_, queue_size_, shutdown_on_exception_) {} static GlobalThreadPool & instance(); }; From aa2d724ea13804ddd5b3cfc223ba005757b992d0 Mon Sep 17 00:00:00 2001 From: Bharat Nallan Date: Sun, 14 Jun 2020 13:35:09 -0700 Subject: [PATCH 0328/1102] add max_thread_pool_size setting to tests This adds the `max_thread_pool_size` config to tests/server-test.xml file. --- tests/server-test.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/server-test.xml b/tests/server-test.xml index 7f7924790652..721d62ef3016 100644 --- a/tests/server-test.xml +++ b/tests/server-test.xml @@ -17,6 +17,7 @@ 58443 59440 59009 + 10000 From 09e3975b9778d5849a1cd9b8cd4f156b10311cb9 Mon Sep 17 00:00:00 2001 From: Bharat Nallan Date: Sun, 14 Jun 2020 13:44:39 -0700 Subject: [PATCH 0329/1102] docs for max_thread_pool_size This adds the docs for the new server level setting `max_thread_pool_size`. --- .../server-configuration-parameters/settings.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index b90b432da6cc..b43d6bf847ae 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -426,6 +426,18 @@ The value 0 means that you can delete all tables without any restrictions. 0 ``` +## max\_thread\_pool\_size {#max-thread-pool-size} + +The maximum number of threads in the Global Thread pool. + +Default value: 10000. + +**Example** + +``` xml +12000 +``` + ## merge\_tree {#server_configuration_parameters-merge_tree} Fine tuning for tables in the [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md). From 334c5abe9b37a314f3a7ad4d7a783ad08bfa724c Mon Sep 17 00:00:00 2001 From: Bharat Nallan Date: Sun, 14 Jun 2020 17:09:59 -0700 Subject: [PATCH 0330/1102] remove extra vertical space --- src/Common/ThreadPool.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Common/ThreadPool.cpp b/src/Common/ThreadPool.cpp index edfb52e01acb..3a669056f211 100644 --- a/src/Common/ThreadPool.cpp +++ b/src/Common/ThreadPool.cpp @@ -6,8 +6,6 @@ #include #include - - namespace DB { namespace ErrorCodes From 61070a03cd2a012eec065f19dce081e66ed2cb62 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 10:56:22 +0300 Subject: [PATCH 0331/1102] Fix build. --- src/Processors/QueryPlan/QueryPlan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 6e3535f27d4f..360f47d345c0 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -88,7 +88,7 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() QueryPipelinePtr last_pipeline; std::stack stack; - stack.push({.node = root}); + stack.push(Frame{.node = root}); while (!stack.empty()) { @@ -104,7 +104,7 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() stack.pop(); } else - stack.push({.node = frame.node->children[next_child]}); + stack.push(Frame{.node = frame.node->children[next_child]}); } return last_pipeline; From cd769e5ebf2e2cdd1e4340e292412fafd6a6add9 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Tue, 16 Jun 2020 11:21:15 +0300 Subject: [PATCH 0332/1102] fixup --- docker/test/performance-comparison/compare.sh | 2 +- docker/test/performance-comparison/report.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index 1dbf712ff505..c9beff6d7dba 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -235,7 +235,7 @@ function build_log_column_definitions { # FIXME This loop builds column definitons from TSVWithNamesAndTypes in an # absolutely atrocious way. This should be done by the file() function itself. -for x in {right,left}-{addresses,{query,query-thread,trace,metric}-log}.tsv +for x in {right,left}-{addresses,{query,query-thread,trace,{async-,}metric}-log}.tsv do paste -d' ' \ <(sed -n '1{s/\t/\n/g;p;q}' "$x" | sed 's/\(^.*$\)/"\1"/') \ diff --git a/docker/test/performance-comparison/report.py b/docker/test/performance-comparison/report.py index d7e30190aefe..d830b6e65fc9 100755 --- a/docker/test/performance-comparison/report.py +++ b/docker/test/performance-comparison/report.py @@ -325,8 +325,8 @@ def print_test_times(): def print_benchmark_results(): left_json = json.load(open('benchmark/website-left.json')); right_json = json.load(open('benchmark/website-right.json')); - left_qps = left_json["statistics"]["QPS"] - right_qps = right_json["statistics"]["QPS"] + left_qps = next(iter(left_json.values()))["statistics"]["QPS"] + right_qps = next(iter(right_json.values()))["statistics"]["QPS"] relative_diff = (right_qps - left_qps) / left_qps; times_diff = max(right_qps, left_qps) / max(0.01, min(right_qps, left_qps)) print(tableStart('Concurrent benchmarks')) From 1bb72205bd2bac7f1182b820407fa429f42e6a67 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 12:42:25 +0300 Subject: [PATCH 0333/1102] Expression and Filter steps. --- src/Interpreters/InterpreterSelectQuery.cpp | 37 +++++++++++-------- src/Processors/QueryPlan/ExpressionStep.cpp | 25 +++++++++++++ src/Processors/QueryPlan/ExpressionStep.h | 21 +++++++++++ src/Processors/QueryPlan/FilterStep.cpp | 32 ++++++++++++++++ src/Processors/QueryPlan/FilterStep.h | 27 ++++++++++++++ src/Processors/QueryPlan/IQueryPlanStep.h | 7 ++++ .../QueryPlan/ITransformingStep.cpp | 18 +++++++++ src/Processors/QueryPlan/ITransformingStep.h | 16 ++++++++ src/Processors/ya.make | 3 ++ 9 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 src/Processors/QueryPlan/ExpressionStep.cpp create mode 100644 src/Processors/QueryPlan/ExpressionStep.h create mode 100644 src/Processors/QueryPlan/FilterStep.cpp create mode 100644 src/Processors/QueryPlan/FilterStep.h create mode 100644 src/Processors/QueryPlan/ITransformingStep.cpp create mode 100644 src/Processors/QueryPlan/ITransformingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index e1f649ec3ba6..16b189e0af4b 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -75,6 +75,8 @@ #include #include #include +#include +#include namespace DB @@ -708,23 +710,25 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (expressions.prewhere_info) { - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared( - header, - expressions.prewhere_info->prewhere_actions, - expressions.prewhere_info->prewhere_column_name, - expressions.prewhere_info->remove_prewhere_column); - }); + FilterStep prewhere_step( + DataStream{.header = pipeline.getHeader()}, + expressions.prewhere_info->prewhere_actions, + expressions.prewhere_info->prewhere_column_name, + expressions.prewhere_info->remove_prewhere_column); + + prewhere_step.setStepDescription("PREWHERE"); + prewhere_step.transformPipeline(pipeline); // To remove additional columns in dry run // For example, sample column which can be removed in this stage if (expressions.prewhere_info->remove_columns_actions) { - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, expressions.prewhere_info->remove_columns_actions); - }); + ExpressionStep remove_columns( + DataStream{.header = pipeline.getHeader()}, + expressions.prewhere_info->remove_columns_actions); + + remove_columns.setStepDescription("Remove unnecessary columns after PREWHERE"); + remove_columns.transformPipeline(pipeline); } } } @@ -1329,6 +1333,8 @@ void InterpreterSelectQuery::executeFetchColumns( table_lock, options, storage, required_columns, query_info, *context, processing_stage, max_block_size, max_streams); + read_step.setStepDescription("Read from " + storage->getName()); + pipeline = std::move(*read_step.updatePipeline({})); } else @@ -1337,10 +1343,9 @@ void InterpreterSelectQuery::executeFetchColumns( /// Aliases in table declaration. if (processing_stage == QueryProcessingStage::FetchColumns && alias_actions) { - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, alias_actions); - }); + ExpressionStep table_aliases(DataStream{.header = pipeline.getHeader()}, alias_actions); + table_aliases.setStepDescription("Add table aliases"); + table_aliases.transformPipeline(pipeline); } } diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp new file mode 100644 index 000000000000..96f7521e2b34 --- /dev/null +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +namespace DB +{ + +ExpressionStep::ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_) + : ITransformingStep( + input_stream_, + DataStream{.header = ExpressionTransform::transformHeader(input_stream_.header, expression_)}) + , expression(std::move(expression_)) +{ +} + +void ExpressionStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) + { + bool on_totals = stream_type == QueryPipeline::StreamType::Totals; + return std::make_shared(header, expression, on_totals); + }); +} + +} diff --git a/src/Processors/QueryPlan/ExpressionStep.h b/src/Processors/QueryPlan/ExpressionStep.h new file mode 100644 index 000000000000..997569c6b6f8 --- /dev/null +++ b/src/Processors/QueryPlan/ExpressionStep.h @@ -0,0 +1,21 @@ +#include + +namespace DB +{ + +class ExpressionActions; +using ExpressionActionsPtr = std::shared_ptr; + +class ExpressionStep : public ITransformingStep +{ +public: + explicit ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_); + String getName() const override { return "Expression"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + ExpressionActionsPtr expression; +}; + +} diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp new file mode 100644 index 000000000000..7fc39723684d --- /dev/null +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +namespace DB +{ + +FilterStep::FilterStep( + const DataStream & input_stream_, + ExpressionActionsPtr expression_, + String filter_column_name_, + bool remove_filter_column_) + : ITransformingStep( + input_stream_, + DataStream{.header = FilterTransform::transformHeader( + input_stream_.header, expression_, filter_column_name_, remove_filter_column_)}) + , expression(std::move(expression_)) + , filter_column_name(std::move(filter_column_name_)) + , remove_filter_column(remove_filter_column_) +{ +} + +void FilterStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) + { + bool on_totals = stream_type == QueryPipeline::StreamType::Totals; + return std::make_shared(header, expression, filter_column_name, remove_filter_column, on_totals); + }); +} + +} diff --git a/src/Processors/QueryPlan/FilterStep.h b/src/Processors/QueryPlan/FilterStep.h new file mode 100644 index 000000000000..2dd4ca2b71d1 --- /dev/null +++ b/src/Processors/QueryPlan/FilterStep.h @@ -0,0 +1,27 @@ +#include + +namespace DB +{ + +class ExpressionActions; +using ExpressionActionsPtr = std::shared_ptr; + +class FilterStep : public ITransformingStep +{ +public: + FilterStep( + const DataStream & input_stream_, + ExpressionActionsPtr expression_, + String filter_column_name_, + bool remove_filter_column_); + + String getName() const override { return "Filter"; } + void transformPipeline(QueryPipeline & pipeline) override; + +private: + ExpressionActionsPtr expression; + String filter_column_name; + bool remove_filter_column; +}; + +} diff --git a/src/Processors/QueryPlan/IQueryPlanStep.h b/src/Processors/QueryPlan/IQueryPlanStep.h index 0c3b0727b019..a33dcca4bef9 100644 --- a/src/Processors/QueryPlan/IQueryPlanStep.h +++ b/src/Processors/QueryPlan/IQueryPlanStep.h @@ -46,9 +46,16 @@ class IQueryPlanStep bool hasOutputStream() const { return output_stream.has_value(); } const DataStream & getOutputStream() const; + /// Methods to describe what this step is needed for. + const std::string & getStepDescription() const { return step_description; } + void setStepDescription(std::string description) { step_description = std::move(description); } + protected: DataStreams input_streams; std::optional output_stream; + + /// Text description about what current step does. + std::string step_description; }; using QueryPlanStepPtr = std::unique_ptr; diff --git a/src/Processors/QueryPlan/ITransformingStep.cpp b/src/Processors/QueryPlan/ITransformingStep.cpp new file mode 100644 index 000000000000..409cfa58079e --- /dev/null +++ b/src/Processors/QueryPlan/ITransformingStep.cpp @@ -0,0 +1,18 @@ +#include + +namespace DB +{ + +ITransformingStep::ITransformingStep(DataStream input_stream, DataStream output_stream_) +{ + input_streams.emplace_back(std::move(input_stream)); + output_stream = std::move(output_stream_); +} + +QueryPipelinePtr ITransformingStep::updatePipeline(QueryPipelines pipelines) +{ + transformPipeline(*pipelines.front()); + return std::move(pipelines.front()); +} + +} diff --git a/src/Processors/QueryPlan/ITransformingStep.h b/src/Processors/QueryPlan/ITransformingStep.h new file mode 100644 index 000000000000..14a08844b363 --- /dev/null +++ b/src/Processors/QueryPlan/ITransformingStep.h @@ -0,0 +1,16 @@ +#include + +namespace DB +{ + +class ITransformingStep : public IQueryPlanStep +{ +public: + ITransformingStep(DataStream input_stream, DataStream output_stream); + + QueryPipelinePtr updatePipeline(QueryPipelines pipelines) override; + + virtual void transformPipeline(QueryPipeline & pipeline) = 0; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 8ab4990b15ad..11e7894dbe12 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -137,6 +137,9 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp Transforms/AggregatingInOrderTransform.cpp + QueryPlan/ExpressionStep.cpp + QueryPlan/FilterStep.cpp + QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/QueryPlan.cpp From e28552ec71e41c7513fd9b4228b359fdd7f3b8fb Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 12:53:59 +0300 Subject: [PATCH 0334/1102] Fix build. --- src/Processors/QueryPlan/ExpressionStep.h | 1 + src/Processors/QueryPlan/FilterStep.h | 1 + src/Processors/QueryPlan/ITransformingStep.h | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Processors/QueryPlan/ExpressionStep.h b/src/Processors/QueryPlan/ExpressionStep.h index 997569c6b6f8..aa3eae6da436 100644 --- a/src/Processors/QueryPlan/ExpressionStep.h +++ b/src/Processors/QueryPlan/ExpressionStep.h @@ -1,3 +1,4 @@ +#pragma once #include namespace DB diff --git a/src/Processors/QueryPlan/FilterStep.h b/src/Processors/QueryPlan/FilterStep.h index 2dd4ca2b71d1..faadd41a58d7 100644 --- a/src/Processors/QueryPlan/FilterStep.h +++ b/src/Processors/QueryPlan/FilterStep.h @@ -1,3 +1,4 @@ +#pragma once #include namespace DB diff --git a/src/Processors/QueryPlan/ITransformingStep.h b/src/Processors/QueryPlan/ITransformingStep.h index 14a08844b363..3e722cf764cc 100644 --- a/src/Processors/QueryPlan/ITransformingStep.h +++ b/src/Processors/QueryPlan/ITransformingStep.h @@ -1,3 +1,4 @@ +#pragma once #include namespace DB From be8029e54f8856145404a030c347af2bc0c58454 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 15:02:10 +0300 Subject: [PATCH 0335/1102] Expression and Filter steps. --- src/Interpreters/InterpreterSelectQuery.cpp | 80 ++++++++++++--------- src/Interpreters/InterpreterSelectQuery.h | 2 +- src/Processors/QueryPlan/ExpressionStep.cpp | 24 ++++++- src/Processors/QueryPlan/ExpressionStep.h | 17 ++++- 4 files changed, 84 insertions(+), 39 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 16b189e0af4b..377b9d52ab9a 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -789,7 +789,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (expressions.hasLimitBy()) { - executeExpression(pipeline, expressions.before_limit_by); + executeExpression(pipeline, expressions.before_limit_by, "Before LIMIT BY"); executeLimitBy(pipeline); } @@ -812,17 +812,14 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn { if (expressions.hasFilter()) { - pipeline.addSimpleTransform([&](const Block & block, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - bool on_totals = stream_type == QueryPipeline::StreamType::Totals; - - return std::make_shared( - block, + FilterStep row_level_security_step( + DataStream{.header = pipeline.getHeader()}, expressions.filter_info->actions, expressions.filter_info->column_name, - expressions.filter_info->do_remove_column, - on_totals); - }); + expressions.filter_info->do_remove_column); + + row_level_security_step.setStepDescription("Row-level security filter"); + row_level_security_step.transformPipeline(pipeline); } if (expressions.hasJoin()) @@ -830,8 +827,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn Block join_result_sample; JoinPtr join = expressions.before_join->getTableJoinAlgo(); - join_result_sample = ExpressionBlockInputStream( - std::make_shared(pipeline.getHeader()), expressions.before_join).getHeader(); + join_result_sample = ExpressionTransform::transformHeader(pipeline.getHeader(), expressions.before_join); /// In case joined subquery has totals, and we don't, add default chunk to totals. bool default_totals = false; @@ -849,17 +845,26 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn inflating_join = isCross(hash_join->getKind()); } - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType type) + if (inflating_join) { - bool on_totals = type == QueryPipeline::StreamType::Totals; - std::shared_ptr ret; - if (inflating_join) - ret = std::make_shared(header, expressions.before_join, on_totals, default_totals); - else - ret = std::make_shared(header, expressions.before_join, on_totals, default_totals); - - return ret; - }); + InflatingExpressionStep before_join_step( + DataStream{.header = pipeline.getHeader()}, + expressions.before_join, + default_totals); + + before_join_step.setStepDescription("JOIN"); + before_join_step.transformPipeline(pipeline); + } + else + { + ExpressionStep before_join_step( + DataStream{.header = pipeline.getHeader()}, + expressions.before_join, + default_totals); + + before_join_step.setStepDescription("JOIN"); + before_join_step.transformPipeline(pipeline); + } if (join) { @@ -882,7 +887,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn } else { - executeExpression(pipeline, expressions.before_order_and_select); + executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); executeDistinct(pipeline, true, expressions.selected_columns); } @@ -926,7 +931,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn else if (expressions.hasHaving()) executeHaving(pipeline, expressions.before_having); - executeExpression(pipeline, expressions.before_order_and_select); + executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); executeDistinct(pipeline, true, expressions.selected_columns); } @@ -972,7 +977,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (expressions.hasLimitBy()) { - executeExpression(pipeline, expressions.before_limit_by); + executeExpression(pipeline, expressions.before_limit_by, "Before LIMIT BY"); executeLimitBy(pipeline); } @@ -1352,11 +1357,14 @@ void InterpreterSelectQuery::executeFetchColumns( void InterpreterSelectQuery::executeWhere(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool remove_filter) { - pipeline.addSimpleTransform([&](const Block & block, QueryPipeline::StreamType stream_type) - { - bool on_totals = stream_type == QueryPipeline::StreamType::Totals; - return std::make_shared(block, expression, getSelectQuery().where()->getColumnName(), remove_filter, on_totals); - }); + FilterStep where_step( + DataStream{.header = pipeline.getHeader()}, + expression, + getSelectQuery().where()->getColumnName(), + remove_filter); + + where_step.setStepDescription("WHERE"); + where_step.transformPipeline(pipeline); } @@ -1607,12 +1615,14 @@ void InterpreterSelectQuery::executeRollupOrCube(QueryPipeline & pipeline, Modif } -void InterpreterSelectQuery::executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression) +void InterpreterSelectQuery::executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, const std::string & description) { - pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr - { - return std::make_shared(header, expression); - }); + ExpressionStep expression_step( + DataStream{.header = pipeline.getHeader()}, + expression); + + expression_step.setStepDescription(description); + expression_step.transformPipeline(pipeline); } diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 8ed775f60ae4..bdf888c61294 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -119,7 +119,7 @@ class InterpreterSelectQuery : public IInterpreter void executeMergeAggregated(QueryPipeline & pipeline, bool overflow_row, bool final); void executeTotalsAndHaving(QueryPipeline & pipeline, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final); void executeHaving(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); - static void executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); + static void executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, const std::string & description); void executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info); void executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info, UInt64 limit, SortDescription & output_order_descr); void executeWithFill(QueryPipeline & pipeline); diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 96f7521e2b34..8265a9884b96 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -1,15 +1,17 @@ #include #include #include +#include namespace DB { -ExpressionStep::ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_) +ExpressionStep::ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_) : ITransformingStep( input_stream_, DataStream{.header = ExpressionTransform::transformHeader(input_stream_.header, expression_)}) , expression(std::move(expression_)) + , default_totals(default_totals_) { } @@ -18,7 +20,25 @@ void ExpressionStep::transformPipeline(QueryPipeline & pipeline) pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) { bool on_totals = stream_type == QueryPipeline::StreamType::Totals; - return std::make_shared(header, expression, on_totals); + return std::make_shared(header, expression, on_totals, default_totals); + }); +} + +InflatingExpressionStep::InflatingExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_) + : ITransformingStep( + input_stream_, + DataStream{.header = ExpressionTransform::transformHeader(input_stream_.header, expression_)}) + , expression(std::move(expression_)) + , default_totals(default_totals_) +{ +} + +void InflatingExpressionStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) + { + bool on_totals = stream_type == QueryPipeline::StreamType::Totals; + return std::make_shared(header, expression, on_totals, default_totals); }); } diff --git a/src/Processors/QueryPlan/ExpressionStep.h b/src/Processors/QueryPlan/ExpressionStep.h index aa3eae6da436..4f268944c958 100644 --- a/src/Processors/QueryPlan/ExpressionStep.h +++ b/src/Processors/QueryPlan/ExpressionStep.h @@ -10,13 +10,28 @@ using ExpressionActionsPtr = std::shared_ptr; class ExpressionStep : public ITransformingStep { public: - explicit ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_); + explicit ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_ = false); String getName() const override { return "Expression"; } void transformPipeline(QueryPipeline & pipeline) override; private: ExpressionActionsPtr expression; + bool default_totals; /// See ExpressionTransform +}; + +/// TODO: add separate step for join. +class InflatingExpressionStep : public ITransformingStep +{ +public: + explicit InflatingExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_ = false); + String getName() const override { return "Expression"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + ExpressionActionsPtr expression; + bool default_totals; /// See ExpressionTransform }; } From 0f286ac133fa360b997d3edbc2891b016c6134c5 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 15:03:27 +0300 Subject: [PATCH 0336/1102] Copy some methods to metdata --- src/Storages/StorageInMemoryMetadata.cpp | 121 +++++++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 31 +++++- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index b6dd2f38c4e9..bf747fb9b5ab 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -89,4 +89,125 @@ void StorageInMemoryMetadata::setSelectQuery(const SelectQueryDescription & sele select = select_; } +const ColumnsDescription & StorageInMemoryMetadata::getColumns() const +{ + return columns; +} + +const IndicesDescription & StorageInMemoryMetadata::getSecondaryIndices() const +{ + return secondary_indices; +} + +bool StorageInMemoryMetadata::hasSecondaryIndices() const +{ + return !secondary_indices.empty(); +} + +const ConstraintsDescription & StorageInMemoryMetadata::getConstraints() const +{ + return constraints; +} + +TTLTableDescription StorageInMemoryMetadata::getTableTTLs() const +{ + return table_ttl; +} + +bool StorageInMemoryMetadata::hasAnyTableTTL() const +{ + return hasAnyMoveTTL() || hasRowsTTL(); +} + +TTLColumnsDescription StorageInMemoryMetadata::getColumnTTLs() const +{ + return column_ttls_by_name; +} + +bool StorageInMemoryMetadata::hasAnyColumnTTL() const +{ + return !column_ttls_by_name.empty(); +} + +TTLDescription StorageInMemoryMetadata::getRowsTTL() const +{ + return table_ttl.rows_ttl; +} + +bool StorageInMemoryMetadata::hasRowsTTL() const +{ + return table_ttl.rows_ttl.expression != nullptr; +} + +TTLDescriptions StorageInMemoryMetadata::getMoveTTLs() const +{ + return table_ttl.move_ttl; +} + +bool StorageInMemoryMetadata::hasAnyMoveTTL() const +{ + return !table_ttl.move_ttl.empty(); +} + +ColumnDependencies StorageInMemoryMetadata::getColumnDependencies(const NameSet & updated_columns) const +{ + if (updated_columns.empty()) + return {}; + + ColumnDependencies res; + + NameSet indices_columns; + NameSet required_ttl_columns; + NameSet updated_ttl_columns; + + auto add_dependent_columns = [&updated_columns](const auto & expression, auto & to_set) + { + auto requiered_columns = expression->getRequiredColumns(); + for (const auto & dependency : requiered_columns) + { + if (updated_columns.count(dependency)) + { + to_set.insert(requiered_columns.begin(), requiered_columns.end()); + return true; + } + } + + return false; + }; + + for (const auto & index : getSecondaryIndices()) + add_dependent_columns(index.expression, indices_columns); + + if (hasRowsTTL()) + { + auto rows_expression = getRowsTTL().expression; + if (add_dependent_columns(rows_expression, required_ttl_columns)) + { + /// Filter all columns, if rows TTL expression have to be recalculated. + for (const auto & column : getColumns().getAllPhysical()) + updated_ttl_columns.insert(column.name); + } + } + + for (const auto & [name, entry] : getColumnTTLs()) + { + if (add_dependent_columns(entry.expression, required_ttl_columns)) + updated_ttl_columns.insert(name); + } + + for (const auto & entry : getMoveTTLs()) + add_dependent_columns(entry.expression, required_ttl_columns); + + for (const auto & column : indices_columns) + res.emplace(column, ColumnDependency::SKIP_INDEX); + for (const auto & column : required_ttl_columns) + res.emplace(column, ColumnDependency::TTL_EXPRESSION); + for (const auto & column : updated_ttl_columns) + res.emplace(column, ColumnDependency::TTL_TARGET); + + return res; + +} + + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index b129cdc7756f..fb7bcbaa349f 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -1,12 +1,13 @@ #pragma once #include +#include #include #include #include #include -#include #include +#include #include @@ -77,6 +78,34 @@ struct StorageInMemoryMetadata void setSettingsChanges(const ASTPtr & settings_changes_); void setSelectQuery(const SelectQueryDescription & select_); + + const ColumnsDescription & getColumns() const; /// returns combined set of columns + const IndicesDescription & getSecondaryIndices() const; + /// Has at least one non primary index + bool hasSecondaryIndices() const; + + const ConstraintsDescription & getConstraints() const; + + /// Common tables TTLs (for rows and moves). + TTLTableDescription getTableTTLs() const; + bool hasAnyTableTTL() const; + + /// Separate TTLs for columns. + TTLColumnsDescription getColumnTTLs() const; + bool hasAnyColumnTTL() const; + + /// Just wrapper for table TTLs, return rows part of table TTLs. + TTLDescription getRowsTTL() const; + bool hasRowsTTL() const; + + /// Just wrapper for table TTLs, return moves (to disks or volumes) parts of + /// table TTL. + TTLDescriptions getMoveTTLs() const; + bool hasAnyMoveTTL() const; + + /// Returns columns, which will be needed to calculate dependencies (skip + /// indices, TTL expressions) if we update @updated_columns set of columns. + ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const; }; using StorageMetadataPtr = std::shared_ptr; From 0bcd22008a2e42c79c2ff8724b065a854e335335 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 15:19:21 +0300 Subject: [PATCH 0337/1102] Get column dependencies in StorageInMemoryMetadata --- src/Interpreters/MutationsInterpreter.cpp | 11 ++-- src/Interpreters/MutationsInterpreter.h | 1 + src/Storages/IStorage.cpp | 61 ------------------- src/Storages/IStorage.h | 4 -- .../MergeTree/MergeTreeDataMergerMutator.cpp | 17 ++++-- .../MergeTree/MergeTreeDataMergerMutator.h | 12 +++- src/Storages/StorageMergeTree.cpp | 7 ++- src/Storages/StorageReplicatedMergeTree.cpp | 10 ++- 8 files changed, 41 insertions(+), 82 deletions(-) diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 123de1b6e840..ce47ce6e4762 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -137,13 +137,13 @@ ASTPtr prepareQueryAffectedAST(const std::vector & commands) return select; } -ColumnDependencies getAllColumnDependencies(const StoragePtr & storage, const NameSet & updated_columns) +ColumnDependencies getAllColumnDependencies(const StorageMetadataPtr & metadata_snapshot, const NameSet & updated_columns) { NameSet new_updated_columns = updated_columns; ColumnDependencies dependencies; while (!new_updated_columns.empty()) { - auto new_dependencies = storage->getColumnDependencies(new_updated_columns); + auto new_dependencies = metadata_snapshot->getColumnDependencies(new_updated_columns); new_updated_columns.clear(); for (const auto & dependency : new_dependencies) { @@ -204,6 +204,7 @@ MutationsInterpreter::MutationsInterpreter( const Context & context_, bool can_execute_) : storage(std::move(storage_)) + , metadata_snapshot(storage->getInMemoryMetadataPtr()) , commands(std::move(commands_)) , context(context_) , can_execute(can_execute_) @@ -329,7 +330,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } /// Columns, that we need to read for calculation of skip indices or TTL expressions. - auto dependencies = getAllColumnDependencies(storage, updated_columns); + auto dependencies = getAllColumnDependencies(metadata_snapshot, updated_columns); /// First, break a sequence of commands into stages. for (const auto & command : commands) @@ -423,7 +424,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } auto all_columns_vec = all_columns.getNames(); - auto all_dependencies = getAllColumnDependencies(storage, NameSet(all_columns_vec.begin(), all_columns_vec.end())); + auto all_dependencies = getAllColumnDependencies(metadata_snapshot, NameSet(all_columns_vec.begin(), all_columns_vec.end())); for (const auto & dependency : all_dependencies) { @@ -432,7 +433,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } /// Recalc only skip indices of columns, that could be updated by TTL. - auto new_dependencies = storage->getColumnDependencies(new_updated_columns); + auto new_dependencies = metadata_snapshot->getColumnDependencies(new_updated_columns); for (const auto & dependency : new_dependencies) { if (dependency.kind == ColumnDependency::SKIP_INDEX) diff --git a/src/Interpreters/MutationsInterpreter.h b/src/Interpreters/MutationsInterpreter.h index 35c4f8ece0ab..158ed8d55af8 100644 --- a/src/Interpreters/MutationsInterpreter.h +++ b/src/Interpreters/MutationsInterpreter.h @@ -47,6 +47,7 @@ class MutationsInterpreter std::optional getStorageSortDescriptionIfPossible(const Block & header) const; StoragePtr storage; + StorageMetadataPtr metadata_snapshot; MutationCommands commands; Context context; bool can_execute; diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index e5ab14e046e2..6dae96a3322c 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -548,67 +548,6 @@ bool IStorage::hasAnyMoveTTL() const return !metadata->table_ttl.move_ttl.empty(); } - -ColumnDependencies IStorage::getColumnDependencies(const NameSet & updated_columns) const -{ - if (updated_columns.empty()) - return {}; - - ColumnDependencies res; - - NameSet indices_columns; - NameSet required_ttl_columns; - NameSet updated_ttl_columns; - - auto add_dependent_columns = [&updated_columns](const auto & expression, auto & to_set) - { - auto requiered_columns = expression->getRequiredColumns(); - for (const auto & dependency : requiered_columns) - { - if (updated_columns.count(dependency)) - { - to_set.insert(requiered_columns.begin(), requiered_columns.end()); - return true; - } - } - - return false; - }; - - for (const auto & index : getSecondaryIndices()) - add_dependent_columns(index.expression, indices_columns); - - if (hasRowsTTL()) - { - auto rows_expression = getRowsTTL().expression; - if (add_dependent_columns(rows_expression, required_ttl_columns)) - { - /// Filter all columns, if rows TTL expression have to be recalculated. - for (const auto & column : getColumns().getAllPhysical()) - updated_ttl_columns.insert(column.name); - } - } - - for (const auto & [name, entry] : getColumnTTLs()) - { - if (add_dependent_columns(entry.expression, required_ttl_columns)) - updated_ttl_columns.insert(name); - } - - for (const auto & entry : getMoveTTLs()) - add_dependent_columns(entry.expression, required_ttl_columns); - - for (const auto & column : indices_columns) - res.emplace(column, ColumnDependency::SKIP_INDEX); - for (const auto & column : required_ttl_columns) - res.emplace(column, ColumnDependency::TTL_EXPRESSION); - for (const auto & column : updated_ttl_columns) - res.emplace(column, ColumnDependency::TTL_TARGET); - - return res; - -} - ASTPtr IStorage::getSettingsChanges() const { if (metadata->settings_changes) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 28ad7b0ea8b0..d3e65b6a8453 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -492,10 +492,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns column names that need to be read for FINAL to work. Names getColumnsRequiredForFinal() const { return getColumnsRequiredForSortingKey(); } - /// Returns columns, which will be needed to calculate dependencies (skip - /// indices, TTL expressions) if we update @updated_columns set of columns. - ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const; - /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index d861173d8a04..595370e7eccb 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -578,8 +578,14 @@ class MergeProgressCallback /// parts should be sorted. MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTemporaryPart( - const FutureMergedMutatedPart & future_part, MergeList::Entry & merge_entry, TableStructureReadLockHolder &, - time_t time_of_merge, const ReservationPtr & space_reservation, bool deduplicate, bool force_ttl) + const FutureMergedMutatedPart & future_part, + const StorageMetadataPtr & /*metadata_snapshot*/, + MergeList::Entry & merge_entry, + TableStructureReadLockHolder &, + time_t time_of_merge, + const ReservationPtr & space_reservation, + bool deduplicate, + bool force_ttl) { static const String TMP_PREFIX = "tmp_merge_"; @@ -975,6 +981,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTemporaryPart( const FutureMergedMutatedPart & future_part, + const StorageMetadataPtr & metadata_snapshot, const MutationCommands & commands, MergeListEntry & merge_entry, time_t time_of_mutation, @@ -1069,7 +1076,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor bool need_remove_expired_values = false; - if (in && shouldExecuteTTL(in->getHeader().getNamesAndTypesList().getNames(), commands_for_part)) + if (in && shouldExecuteTTL(metadata_snapshot, in->getHeader().getNamesAndTypesList().getNames(), commands_for_part)) need_remove_expired_values = true; /// All columns from part are changed and may be some more that were missing before in part @@ -1556,7 +1563,7 @@ std::set MergeTreeDataMergerMutator::getIndicesToRecalculate( return indices_to_recalc; } -bool MergeTreeDataMergerMutator::shouldExecuteTTL(const Names & columns, const MutationCommands & commands) const +bool MergeTreeDataMergerMutator::shouldExecuteTTL(const StorageMetadataPtr & metadata_snapshot, const Names & columns, const MutationCommands & commands) const { if (!data.hasAnyTTL()) return false; @@ -1565,7 +1572,7 @@ bool MergeTreeDataMergerMutator::shouldExecuteTTL(const Names & columns, const M if (command.type == MutationCommand::MATERIALIZE_TTL) return true; - auto dependencies = data.getColumnDependencies(NameSet(columns.begin(), columns.end())); + auto dependencies = metadata_snapshot->getColumnDependencies(NameSet(columns.begin(), columns.end())); for (const auto & dependency : dependencies) if (dependency.kind == ColumnDependency::TTL_EXPRESSION || dependency.kind == ColumnDependency::TTL_TARGET) return true; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h index 7c2ee53fc1de..185961972a8a 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h @@ -105,12 +105,18 @@ class MergeTreeDataMergerMutator */ MergeTreeData::MutableDataPartPtr mergePartsToTemporaryPart( const FutureMergedMutatedPart & future_part, - MergeListEntry & merge_entry, TableStructureReadLockHolder & table_lock_holder, time_t time_of_merge, - const ReservationPtr & space_reservation, bool deduplicate, bool force_ttl); + const StorageMetadataPtr & metadata_snapshot, + MergeListEntry & merge_entry, + TableStructureReadLockHolder & table_lock_holder, + time_t time_of_merge, + const ReservationPtr & space_reservation, + bool deduplicate, + bool force_ttl); /// Mutate a single data part with the specified commands. Will create and return a temporary part. MergeTreeData::MutableDataPartPtr mutatePartToTemporaryPart( const FutureMergedMutatedPart & future_part, + const StorageMetadataPtr & metadata_snapshot, const MutationCommands & commands, MergeListEntry & merge_entry, time_t time_of_mutation, @@ -164,7 +170,7 @@ class MergeTreeDataMergerMutator const IndicesDescription & all_indices, const MutationCommands & commands_for_removes); - bool shouldExecuteTTL(const Names & columns, const MutationCommands & commands) const; + bool shouldExecuteTTL(const StorageMetadataPtr & metadata_snapshot, const Names & columns, const MutationCommands & commands) const; /// Return set of indices which should be recalculated during mutation also /// wraps input stream into additional expression stream diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index e3f48a05d6e0..1b00487c8163 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -591,6 +591,7 @@ bool StorageMergeTree::merge( { auto table_lock_holder = lockStructureForShare( true, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto metadata_snapshot = getInMemoryMetadataPtr(); FutureMergedMutatedPart future_part; @@ -693,7 +694,7 @@ bool StorageMergeTree::merge( bool force_ttl = (final && hasAnyTTL()); new_part = merger_mutator.mergePartsToTemporaryPart( - future_part, *merge_entry, table_lock_holder, time(nullptr), + future_part, metadata_snapshot, *merge_entry, table_lock_holder, time(nullptr), merging_tagger->reserved_space, deduplicate, force_ttl); merger_mutator.renameMergedTemporaryPart(new_part, future_part.parts, nullptr); @@ -739,6 +740,7 @@ bool StorageMergeTree::tryMutatePart() { auto table_lock_holder = lockStructureForShare( true, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); size_t max_ast_elements = global_context.getSettingsRef().max_expanded_ast_elements; FutureMergedMutatedPart future_part; @@ -832,7 +834,8 @@ bool StorageMergeTree::tryMutatePart() try { - new_part = merger_mutator.mutatePartToTemporaryPart(future_part, commands, *merge_entry, + new_part = merger_mutator.mutatePartToTemporaryPart( + future_part, metadata_snapshot, commands, *merge_entry, time(nullptr), global_context, tagger->reserved_space, table_lock_holder); renameTempPartAndReplace(new_part); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 7abf90d3eacf..810a4fa5c971 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -1304,6 +1304,7 @@ bool StorageReplicatedMergeTree::tryExecuteMerge(const LogEntry & entry) auto table_lock = lockStructureForShare( false, RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); + StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); FutureMergedMutatedPart future_merged_part(parts, entry.new_part_type); if (future_merged_part.name != entry.new_part_name) @@ -1331,7 +1332,9 @@ bool StorageReplicatedMergeTree::tryExecuteMerge(const LogEntry & entry) try { part = merger_mutator.mergePartsToTemporaryPart( - future_merged_part, *merge_entry, table_lock, entry.create_time, reserved_space, entry.deduplicate, entry.force_ttl); + future_merged_part, metadata_snapshot, *merge_entry, + table_lock, entry.create_time, reserved_space, entry.deduplicate, + entry.force_ttl); merger_mutator.renameMergedTemporaryPart(part, parts, &transaction); @@ -1428,6 +1431,7 @@ bool StorageReplicatedMergeTree::tryExecutePartMutation(const StorageReplicatedM auto table_lock = lockStructureForShare( false, RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); + StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); MutableDataPartPtr new_part; Transaction transaction(*this); @@ -1454,7 +1458,9 @@ bool StorageReplicatedMergeTree::tryExecutePartMutation(const StorageReplicatedM try { - new_part = merger_mutator.mutatePartToTemporaryPart(future_mutated_part, commands, *merge_entry, entry.create_time, global_context, reserved_space, table_lock); + new_part = merger_mutator.mutatePartToTemporaryPart( + future_mutated_part, metadata_snapshot, commands, *merge_entry, + entry.create_time, global_context, reserved_space, table_lock); renameTempPartAndReplace(new_part, nullptr, &transaction); try From 53cb5210debb5baa10d521d90bd6afb7988245e2 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 15:48:10 +0300 Subject: [PATCH 0338/1102] Move getSampleBlockNonMaterialized to StorageInMemoryMetadata --- src/Interpreters/InterpreterInsertQuery.cpp | 9 ++++++--- src/Interpreters/InterpreterInsertQuery.h | 3 ++- src/Interpreters/SystemLog.h | 3 ++- src/Storages/IStorage.cpp | 9 --------- src/Storages/IStorage.h | 1 - src/Storages/Kafka/KafkaBlockInputStream.cpp | 13 ++++++++++--- src/Storages/Kafka/KafkaBlockInputStream.h | 8 +++++++- src/Storages/Kafka/KafkaBlockOutputStream.cpp | 10 ++++++++-- src/Storages/Kafka/KafkaBlockOutputStream.h | 6 +++++- src/Storages/Kafka/StorageKafka.cpp | 11 ++++++----- src/Storages/StorageBuffer.cpp | 4 +++- src/Storages/StorageDistributed.cpp | 4 ++-- src/Storages/StorageInMemoryMetadata.cpp | 8 ++++++++ src/Storages/StorageInMemoryMetadata.h | 2 ++ 14 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index 1841c82b7101..d281dc5ccca9 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -73,9 +73,12 @@ StoragePtr InterpreterInsertQuery::getTable(ASTInsertQuery & query) return DatabaseCatalog::instance().getTable(query.table_id, context); } -Block InterpreterInsertQuery::getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table) const +Block InterpreterInsertQuery::getSampleBlock( + const ASTInsertQuery & query, + const StoragePtr & table, + const StorageMetadataPtr & metadata_snapshot) const { - Block table_sample_non_materialized = table->getSampleBlockNonMaterialized(); + Block table_sample_non_materialized = metadata_snapshot->getSampleBlockNonMaterialized(); /// If the query does not include information about columns if (!query.columns) { @@ -119,7 +122,7 @@ BlockIO InterpreterInsertQuery::execute() true, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = table->getInMemoryMetadataPtr(); - auto query_sample_block = getSampleBlock(query, table); + auto query_sample_block = getSampleBlock(query, table, metadata_snapshot); if (!query.table_function) context.checkAccess(AccessType::INSERT, query.table_id, query_sample_block.getNames()); diff --git a/src/Interpreters/InterpreterInsertQuery.h b/src/Interpreters/InterpreterInsertQuery.h index fef962d24a39..3386b471d26c 100644 --- a/src/Interpreters/InterpreterInsertQuery.h +++ b/src/Interpreters/InterpreterInsertQuery.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace DB { @@ -34,7 +35,7 @@ class InterpreterInsertQuery : public IInterpreter private: StoragePtr getTable(ASTInsertQuery & query); - Block getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table) const; + Block getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table, const StorageMetadataPtr & metadata_snapshot) const; ASTPtr query_ptr; const Context & context; diff --git a/src/Interpreters/SystemLog.h b/src/Interpreters/SystemLog.h index e49ce574478d..cf163226b935 100644 --- a/src/Interpreters/SystemLog.h +++ b/src/Interpreters/SystemLog.h @@ -438,8 +438,9 @@ void SystemLog::prepareTable() if (table) { + auto metadata_snapshot = table->getInMemoryMetadataPtr(); const Block expected = LogElement::createBlock(); - const Block actual = table->getSampleBlockNonMaterialized(); + const Block actual = metadata_snapshot->getSampleBlockNonMaterialized(); if (!blocksHaveEqualStructure(actual, expected)) { diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 6dae96a3322c..e675d51b4b7f 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -74,15 +74,6 @@ Block IStorage::getSampleBlockWithVirtuals() const return res; } -Block IStorage::getSampleBlockNonMaterialized() const -{ - Block res; - - for (const auto & column : getColumns().getOrdinary()) - res.insert({column.type->createColumn(), column.type, column.name}); - - return res; -} Block IStorage::getSampleBlockForColumns(const Names & column_names) const { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index d3e65b6a8453..42581ebb63b7 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -160,7 +160,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo Block getSampleBlock() const; /// ordinary + materialized. Block getSampleBlockWithVirtuals() const; /// ordinary + materialized + virtuals. - Block getSampleBlockNonMaterialized() const; /// ordinary. Block getSampleBlockForColumns(const Names & column_names) const; /// ordinary + materialized + aliases + virtuals. /// Verify that all the requested names are in the table and are set correctly: diff --git a/src/Storages/Kafka/KafkaBlockInputStream.cpp b/src/Storages/Kafka/KafkaBlockInputStream.cpp index 3edfcc7b9d2e..dd2bb68c11af 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockInputStream.cpp @@ -13,14 +13,21 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } KafkaBlockInputStream::KafkaBlockInputStream( - StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix_) + StorageKafka & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const std::shared_ptr & context_, + const Names & columns, + size_t max_block_size_, + bool commit_in_suffix_) : storage(storage_) + , metadata_snapshot(metadata_snapshot_) , context(context_) , column_names(columns) , max_block_size(max_block_size_) , commit_in_suffix(commit_in_suffix_) - , non_virtual_header(storage.getSampleBlockNonMaterialized()) - , virtual_header(storage.getSampleBlockForColumns({"_topic", "_key", "_offset", "_partition", "_timestamp","_timestamp_ms","_headers.name","_headers.value"})) + , non_virtual_header(metadata_snapshot->getSampleBlockNonMaterialized()) + , virtual_header(storage.getSampleBlockForColumns( + {"_topic", "_key", "_offset", "_partition", "_timestamp", "_timestamp_ms", "_headers.name", "_headers.value"})) { } diff --git a/src/Storages/Kafka/KafkaBlockInputStream.h b/src/Storages/Kafka/KafkaBlockInputStream.h index 387f5088721d..4851050a56e7 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.h +++ b/src/Storages/Kafka/KafkaBlockInputStream.h @@ -14,7 +14,12 @@ class KafkaBlockInputStream : public IBlockInputStream { public: KafkaBlockInputStream( - StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix = true); + StorageKafka & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const std::shared_ptr & context_, + const Names & columns, + size_t max_block_size_, + bool commit_in_suffix = true); ~KafkaBlockInputStream() override; String getName() const override { return storage.getName(); } @@ -29,6 +34,7 @@ class KafkaBlockInputStream : public IBlockInputStream private: StorageKafka & storage; + StorageMetadataPtr metadata_snapshot; const std::shared_ptr context; Names column_names; UInt64 max_block_size; diff --git a/src/Storages/Kafka/KafkaBlockOutputStream.cpp b/src/Storages/Kafka/KafkaBlockOutputStream.cpp index 17ef5aa104cb..60ac714bd52c 100644 --- a/src/Storages/Kafka/KafkaBlockOutputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockOutputStream.cpp @@ -11,13 +11,19 @@ namespace ErrorCodes extern const int CANNOT_CREATE_IO_BUFFER; } -KafkaBlockOutputStream::KafkaBlockOutputStream(StorageKafka & storage_, const std::shared_ptr & context_) : storage(storage_), context(context_) +KafkaBlockOutputStream::KafkaBlockOutputStream( + StorageKafka & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const std::shared_ptr & context_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , context(context_) { } Block KafkaBlockOutputStream::getHeader() const { - return storage.getSampleBlockNonMaterialized(); + return metadata_snapshot->getSampleBlockNonMaterialized(); } void KafkaBlockOutputStream::writePrefix() diff --git a/src/Storages/Kafka/KafkaBlockOutputStream.h b/src/Storages/Kafka/KafkaBlockOutputStream.h index 7a973724f1b3..1121d2a119ec 100644 --- a/src/Storages/Kafka/KafkaBlockOutputStream.h +++ b/src/Storages/Kafka/KafkaBlockOutputStream.h @@ -10,7 +10,10 @@ namespace DB class KafkaBlockOutputStream : public IBlockOutputStream { public: - explicit KafkaBlockOutputStream(StorageKafka & storage_, const std::shared_ptr & context_); + explicit KafkaBlockOutputStream( + StorageKafka & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const std::shared_ptr & context_); Block getHeader() const override; @@ -22,6 +25,7 @@ class KafkaBlockOutputStream : public IBlockOutputStream private: StorageKafka & storage; + StorageMetadataPtr metadata_snapshot; const std::shared_ptr context; ProducerBufferPtr buffer; BlockOutputStreamPtr child; diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index 190397bc6751..b46cf0579ec1 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -201,7 +201,7 @@ String StorageKafka::getDefaultClientId(const StorageID & table_id_) Pipes StorageKafka::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /* query_info */, const Context & context, QueryProcessingStage::Enum /* processed_stage */, @@ -224,7 +224,7 @@ Pipes StorageKafka::read( /// TODO: probably that leads to awful performance. /// FIXME: seems that doesn't help with extra reading and committing unprocessed messages. /// TODO: rewrite KafkaBlockInputStream to KafkaSource. Now it is used in other place. - pipes.emplace_back(std::make_shared(std::make_shared(*this, modified_context, column_names, 1))); + pipes.emplace_back(std::make_shared(std::make_shared(*this, metadata_snapshot, modified_context, column_names, 1))); } LOG_DEBUG(log, "Starting reading {} streams", pipes.size()); @@ -232,14 +232,14 @@ Pipes StorageKafka::read( } -BlockOutputStreamPtr StorageKafka::write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageKafka::write(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context & context) { auto modified_context = std::make_shared(context); modified_context->applySettingsChanges(settings_adjustments); if (topics.size() > 1) throw Exception("Can't write to Kafka table with multiple topics!", ErrorCodes::NOT_IMPLEMENTED); - return std::make_shared(*this, modified_context); + return std::make_shared(*this, metadata_snapshot, modified_context); } @@ -519,6 +519,7 @@ bool StorageKafka::streamToViews() auto table = DatabaseCatalog::instance().getTable(table_id, global_context); if (!table) throw Exception("Engine table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::LOGICAL_ERROR); + auto metadata_snapshot = getInMemoryMetadataPtr(); // Create an INSERT query for streaming data auto insert = std::make_shared(); @@ -538,7 +539,7 @@ bool StorageKafka::streamToViews() for (size_t i = 0; i < num_created_consumers; ++i) { auto stream - = std::make_shared(*this, kafka_context, block_io.out->getHeader().getNames(), block_size, false); + = std::make_shared(*this, metadata_snapshot, kafka_context, block_io.out->getHeader().getNames(), block_size, false); streams.emplace_back(stream); // Limit read batch to maximum block size to allow DDL diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 3e419921115d..4754732159c8 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -642,6 +642,7 @@ void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr tabl LOG_ERROR(log, "Destination table {} doesn't exist. Block of data is discarded.", destination_id.getNameForLogs()); return; } + auto destination_metadata_snapshot = table->getInMemoryMetadataPtr(); auto temporarily_disable_memory_tracker = getCurrentMemoryTrackerActionLock(); @@ -651,7 +652,8 @@ void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr tabl /** We will insert columns that are the intersection set of columns of the buffer table and the subordinate table. * This will support some of the cases (but not all) when the table structure does not match. */ - Block structure_of_destination_table = allow_materialized ? table->getSampleBlock() : table->getSampleBlockNonMaterialized(); + Block structure_of_destination_table + = allow_materialized ? table->getSampleBlock() : destination_metadata_snapshot->getSampleBlockNonMaterialized(); Block block_to_write; for (size_t i : ext::range(0, structure_of_destination_table.columns())) { diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 719811bbc6b2..66066ec3c186 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -511,7 +511,7 @@ Pipes StorageDistributed::read( } -BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context & context) { auto cluster = getCluster(); const auto & settings = context.getSettingsRef(); @@ -536,7 +536,7 @@ BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const StorageMeta /// DistributedBlockOutputStream will not own cluster, but will own ConnectionPools of the cluster return std::make_shared( - context, *this, createInsertToRemoteTableQuery(remote_database, remote_table, getSampleBlockNonMaterialized()), cluster, + context, *this, createInsertToRemoteTableQuery(remote_database, remote_table, metadata_snapshot->getSampleBlockNonMaterialized()), cluster, insert_sync, timeout); } diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index bf747fb9b5ab..2c5b6279e10a 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -209,5 +209,13 @@ ColumnDependencies StorageInMemoryMetadata::getColumnDependencies(const NameSet } +Block StorageInMemoryMetadata::getSampleBlockNonMaterialized() const +{ + Block res; + + for (const auto & column : getColumns().getOrdinary()) + res.insert({column.type->createColumn(), column.type, column.name}); + return res; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index fb7bcbaa349f..d6c00bb35c89 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -106,6 +106,8 @@ struct StorageInMemoryMetadata /// Returns columns, which will be needed to calculate dependencies (skip /// indices, TTL expressions) if we update @updated_columns set of columns. ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const; + + Block getSampleBlockNonMaterialized() const; /// ordinary. }; using StorageMetadataPtr = std::shared_ptr; From 0e77692a278b818d03bfd0987c7115802f89a648 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 16 Jun 2020 15:56:28 +0300 Subject: [PATCH 0339/1102] improvements after review comments --- base/daemon/SentryWriter.cpp | 32 ++++++++++--------- base/daemon/SentryWriter.h | 10 +++++- .../settings.md | 2 +- programs/server/config.xml | 3 +- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 88639d8bf94a..0524285ea42b 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -14,6 +14,7 @@ #if USE_SENTRY # include // Y_IGNORE # include +# include #endif @@ -31,7 +32,7 @@ void setExtras() { sentry_set_extra("server_name", sentry_value_new_string(getFQDNOrHostName().c_str())); } - sentry_set_tag("version", VERSION_STRING_SHORT); + sentry_set_tag("version", VERSION_STRING); sentry_set_extra("version_githash", sentry_value_new_string(VERSION_GITHASH)); sentry_set_extra("version_describe", sentry_value_new_string(VERSION_DESCRIBE)); sentry_set_extra("version_integer", sentry_value_new_int32(VERSION_INTEGER)); @@ -93,14 +94,15 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) } if (enabled) { + const std::filesystem::path & default_tmp_path = std::filesystem::path(config.getString("tmp_path", Poco::Path::temp())) / "sentry"; const std::string & endpoint = config.getString("send_crash_reports.endpoint", "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277"); const std::string & temp_folder_path - = config.getString("send_crash_reports.tmp_path", config.getString("tmp_path", Poco::Path::temp()) + "sentry/"); + = config.getString("send_crash_reports.tmp_path", default_tmp_path); Poco::File(temp_folder_path).createDirectories(); - sentry_options_t * options = sentry_options_new(); - sentry_options_set_release(options, VERSION_STRING); + sentry_options_t * options = sentry_options_new(); /// will be freed by sentry_init or sentry_shutdown + sentry_options_set_release(options, VERSION_STRING_SHORT); sentry_options_set_logger(options, &sentry_logger); if (debug) { @@ -128,17 +130,16 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { initialized = true; anonymize = config.getBool("send_crash_reports.anonymize", false); - const std::string& anonymize_status = anonymize ? " (anonymized)" : ""; LOG_INFO( logger, "Sending crash reports is initialized with {} endpoint and {} temp folder{}", endpoint, temp_folder_path, - anonymize_status); + anonymize ? " (anonymized)" : ""); } else { - LOG_WARNING(logger, "Sending crash reports failed to initialized with {} status", init_status); + LOG_WARNING(logger, "Sending crash reports failed to initialize with {} status", init_status); } } else @@ -177,21 +178,20 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c size_t stack_size = stack_trace.getSize(); if (stack_size > 0) { - size_t offset = stack_trace.getOffset(); - if (stack_size == 1) - { - offset = 1; - } + ssize_t offset = stack_trace.getOffset(); char instruction_addr[100]; StackTrace::Frames frames; StackTrace::symbolize(stack_trace.getFramePointers().data(), offset, stack_size, frames); - for (size_t i = stack_size - 1; i >= offset; --i) + for (ssize_t i = stack_size - 1; i >= offset; --i) { const StackTrace::Frame & current_frame = frames[i]; sentry_value_t sentry_frame = sentry_value_new_object(); UInt64 frame_ptr = reinterpret_cast(current_frame.virtual_addr); - std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIx64, frame_ptr); - sentry_value_set_by_key(sentry_frame, "instruction_addr", sentry_value_new_string(instruction_addr)); + + if (std::snprintf(instruction_addr, sizeof(instruction_addr), "0x%" PRIx64, frame_ptr) >= 0) + { + sentry_value_set_by_key(sentry_frame, "instruction_addr", sentry_value_new_string(instruction_addr)); + } if (current_frame.symbol.has_value()) { @@ -213,6 +213,7 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c } /// Prepare data for https://develop.sentry.dev/sdk/event-payloads/threads/ + /// Stacktrace is filled only for a single thread that failed sentry_value_t stacktrace = sentry_value_new_object(); sentry_value_set_by_key(stacktrace, "frames", sentry_frames); @@ -225,6 +226,7 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c sentry_value_t threads = sentry_value_new_object(); sentry_value_set_by_key(threads, "values", values); + sentry_value_set_by_key(event, "threads", threads); LOG_INFO(logger, "Sending crash report"); diff --git a/base/daemon/SentryWriter.h b/base/daemon/SentryWriter.h index 0b3f1ddd2b76..655a4e93bfd2 100644 --- a/base/daemon/SentryWriter.h +++ b/base/daemon/SentryWriter.h @@ -7,7 +7,13 @@ #include -/// Sends crash reports to ClickHouse core developer team via https://sentry.io +/// \brief Sends crash reports to ClickHouse core developer team via https://sentry.io +/// +/// This feature can enabled with "send_crash_reports.enabled" server setting, +/// in this case reports are sent only for official ClickHouse builds. +/// +/// It is possible to send those reports to your own sentry account or account of consulting company you hired +/// by overriding "send_crash_reports.endpoint" setting. "send_crash_reports.debug" setting will allow to do that for class SentryWriter { public: @@ -15,6 +21,8 @@ class SentryWriter static void initialize(Poco::Util::LayeredConfiguration & config); static void shutdown(); + + /// Not signal safe and can't be called from a signal handler static void onFault( int sig, const siginfo_t & info, diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index e1ff3a872d16..58a02b8266ab 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -353,7 +353,7 @@ Keys: Settings for opt-in sending crash reports to the ClickHouse core developers team via [Sentry](https://sentry.io). Enabling it, especially in pre-production environments, is greatly appreciated. -The server will need an access to public Internet for this feature to be functioning properly. +The server will need an access to public Internet via IPv4 (at the time of writing IPv6 is not supported by Sentry) for this feature to be functioning properly. Keys: diff --git a/programs/server/config.xml b/programs/server/config.xml index afb44989bbeb..e5482a074a30 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -46,7 +46,8 @@ - + + false From 08b9aa6b2ed0b39e542e0077efea231374a1ba32 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 15:58:05 +0300 Subject: [PATCH 0340/1102] getSampleBlockWithVirtuals in StorageInMemoryMetadata --- .../PushingToViewsBlockOutputStream.cpp | 21 +++++++++++++----- .../PushingToViewsBlockOutputStream.h | 9 ++++++-- src/Interpreters/InterpreterInsertQuery.cpp | 4 ++-- src/Storages/IStorage.cpp | 13 ----------- src/Storages/IStorage.h | 1 - src/Storages/StorageInMemoryMetadata.cpp | 22 +++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 2 ++ 7 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index fa213b054df6..2e02c26d38cf 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -19,8 +19,14 @@ namespace DB PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( const StoragePtr & storage_, - const Context & context_, const ASTPtr & query_ptr_, bool no_destination) - : storage(storage_), context(context_), query_ptr(query_ptr_) + const StorageMetadataPtr & metadata_snapshot_, + const Context & context_, + const ASTPtr & query_ptr_, + bool no_destination) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , context(context_) + , query_ptr(query_ptr_) { /** TODO This is a very important line. At any insertion into the table one of streams should own lock. * Although now any insertion into the table is done via PushingToViewsBlockOutputStream, @@ -60,6 +66,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( for (const auto & database_table : dependencies) { auto dependent_table = DatabaseCatalog::instance().getTable(database_table, context); + auto dependent_metadata_snapshot = dependent_table->getInMemoryMetadataPtr(); ASTPtr query; BlockOutputStreamPtr out; @@ -97,9 +104,11 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( out = io.out; } else if (dynamic_cast(dependent_table.get())) - out = std::make_shared(dependent_table, *insert_context, ASTPtr(), true); + out = std::make_shared( + dependent_table, dependent_metadata_snapshot, *insert_context, ASTPtr(), true); else - out = std::make_shared(dependent_table, *insert_context, ASTPtr()); + out = std::make_shared( + dependent_table, dependent_metadata_snapshot, *insert_context, ASTPtr()); views.emplace_back(ViewInfo{std::move(query), database_table, std::move(out), nullptr}); } @@ -118,9 +127,9 @@ Block PushingToViewsBlockOutputStream::getHeader() const /// If we don't write directly to the destination /// then expect that we're inserting with precalculated virtual columns if (output) - return storage->getSampleBlock(); + return metadata_snapshot->getSampleBlock(); else - return storage->getSampleBlockWithVirtuals(); + return metadata_snapshot->getSampleBlockWithVirtuals(storage->getVirtuals()); } diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.h b/src/DataStreams/PushingToViewsBlockOutputStream.h index c5fef413a23f..ca09126a5612 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.h +++ b/src/DataStreams/PushingToViewsBlockOutputStream.h @@ -17,8 +17,12 @@ class ReplicatedMergeTreeBlockOutputStream; class PushingToViewsBlockOutputStream : public IBlockOutputStream { public: - PushingToViewsBlockOutputStream(const StoragePtr & storage_, - const Context & context_, const ASTPtr & query_ptr_, bool no_destination = false); + PushingToViewsBlockOutputStream( + const StoragePtr & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const Context & context_, + const ASTPtr & query_ptr_, + bool no_destination = false); Block getHeader() const override; void write(const Block & block) override; @@ -29,6 +33,7 @@ class PushingToViewsBlockOutputStream : public IBlockOutputStream private: StoragePtr storage; + StorageMetadataPtr metadata_snapshot; BlockOutputStreamPtr output; ReplicatedMergeTreeBlockOutputStream * replicated_output = nullptr; diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index d281dc5ccca9..f61ef0e73812 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -83,7 +83,7 @@ Block InterpreterInsertQuery::getSampleBlock( if (!query.columns) { if (no_destination) - return table->getSampleBlockWithVirtuals(); + return metadata_snapshot->getSampleBlockWithVirtuals(table->getVirtuals()); else return table_sample_non_materialized; } @@ -232,7 +232,7 @@ BlockIO InterpreterInsertQuery::execute() if (table->noPushingToViews() && !no_destination) out = table->write(query_ptr, metadata_snapshot, context); else - out = std::make_shared(table, context, query_ptr, no_destination); + out = std::make_shared(table, metadata_snapshot, context, query_ptr, no_destination); /// Note that we wrap transforms one on top of another, so we write them in reverse of data processing order. diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index e675d51b4b7f..fd012c3cd756 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -62,19 +62,6 @@ Block IStorage::getSampleBlock() const return res; } -Block IStorage::getSampleBlockWithVirtuals() const -{ - auto res = getSampleBlock(); - - /// Virtual columns must be appended after ordinary, because user can - /// override them. - for (const auto & column : getVirtuals()) - res.insert({column.type->createColumn(), column.type, column.name}); - - return res; -} - - Block IStorage::getSampleBlockForColumns(const Names & column_names) const { Block res; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 42581ebb63b7..e7a7786c2d6d 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -159,7 +159,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } Block getSampleBlock() const; /// ordinary + materialized. - Block getSampleBlockWithVirtuals() const; /// ordinary + materialized + virtuals. Block getSampleBlockForColumns(const Names & column_names) const; /// ordinary + materialized + aliases + virtuals. /// Verify that all the requested names are in the table and are set correctly: diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 2c5b6279e10a..f3719562af7b 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -218,4 +218,26 @@ Block StorageInMemoryMetadata::getSampleBlockNonMaterialized() const return res; } + +Block StorageInMemoryMetadata::getSampleBlockWithVirtuals(const NamesAndTypesList & virtuals) const +{ + auto res = getSampleBlock(); + + /// Virtual columns must be appended after ordinary, because user can + /// override them. + for (const auto & column : virtuals) + res.insert({column.type->createColumn(), column.type, column.name}); + + return res; +} + +Block StorageInMemoryMetadata::getSampleBlock() const +{ + Block res; + + for (const auto & column : getColumns().getAllPhysical()) + res.insert({column.type->createColumn(), column.type, column.name}); + + return res; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index d6c00bb35c89..2da766caacd4 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -107,7 +107,9 @@ struct StorageInMemoryMetadata /// indices, TTL expressions) if we update @updated_columns set of columns. ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const; + Block getSampleBlock() const; /// ordinary + materialized. Block getSampleBlockNonMaterialized() const; /// ordinary. + Block getSampleBlockWithVirtuals(const NamesAndTypesList & virtuals) const; /// ordinary + materialized + virtuals. }; using StorageMetadataPtr = std::shared_ptr; From 4407bd7daac911b299df639254503b6b554d83ab Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 17:11:19 +0300 Subject: [PATCH 0341/1102] Added source steps. --- src/Interpreters/InterpreterSelectQuery.cpp | 15 +++++++++---- src/Processors/QueryPlan/ISourceStep.cpp | 19 +++++++++++++++++ src/Processors/QueryPlan/ISourceStep.h | 17 +++++++++++++++ src/Processors/QueryPlan/QueryPlan.cpp | 2 +- src/Processors/QueryPlan/QueryPlan.h | 2 +- .../QueryPlan/ReadFromPreparedSource.cpp | 18 ++++++++++++++++ .../QueryPlan/ReadFromPreparedSource.h | 21 +++++++++++++++++++ src/Processors/QueryPlan/ReadNothingStep.cpp | 18 ++++++++++++++++ src/Processors/QueryPlan/ReadNothingStep.h | 17 +++++++++++++++ src/Processors/ya.make | 2 ++ 10 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 src/Processors/QueryPlan/ISourceStep.cpp create mode 100644 src/Processors/QueryPlan/ISourceStep.h create mode 100644 src/Processors/QueryPlan/ReadFromPreparedSource.cpp create mode 100644 src/Processors/QueryPlan/ReadFromPreparedSource.h create mode 100644 src/Processors/QueryPlan/ReadNothingStep.cpp create mode 100644 src/Processors/QueryPlan/ReadNothingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 377b9d52ab9a..51ddc5284dc8 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -77,6 +77,8 @@ #include #include #include +#include +#include namespace DB @@ -706,7 +708,8 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (options.only_analyze) { - pipeline.init(Pipe(std::make_shared(source_header))); + ReadNothingStep read_nothing(DataStream{.header = source_header}); + read_nothing.initializePipeline(pipeline); if (expressions.prewhere_info) { @@ -736,11 +739,13 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn { if (prepared_input) { - pipeline.init(Pipe(std::make_shared(prepared_input))); + ReadFromPreparedSource prepared_source_step(Pipe(std::make_shared(prepared_input))); + prepared_source_step.initializePipeline(pipeline); } else if (prepared_pipe) { - pipeline.init(std::move(*prepared_pipe)); + ReadFromPreparedSource prepared_source_step(std::move(*prepared_pipe)); + prepared_source_step.initializePipeline(pipeline); } if (from_stage == QueryProcessingStage::WithMergeableState && @@ -1050,7 +1055,9 @@ void InterpreterSelectQuery::executeFetchColumns( {std::move(column), std::make_shared(func, argument_types, desc.parameters), desc.column_name}}; auto istream = std::make_shared(block_with_count); - pipeline.init(Pipe(std::make_shared(istream))); + ReadFromPreparedSource prepared_count(Pipe(std::make_shared(istream))); + prepared_count.setStepDescription("Optimized trivial count"); + prepared_count.initializePipeline(pipeline); from_stage = QueryProcessingStage::WithMergeableState; analysis_result.first_stage = false; return; diff --git a/src/Processors/QueryPlan/ISourceStep.cpp b/src/Processors/QueryPlan/ISourceStep.cpp new file mode 100644 index 000000000000..9909a7772675 --- /dev/null +++ b/src/Processors/QueryPlan/ISourceStep.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace DB +{ + +ISourceStep::ISourceStep(DataStream output_stream_) +{ + output_stream = std::move(output_stream_); +} + +QueryPipelinePtr ISourceStep::updatePipeline(QueryPipelines) +{ + auto pipeline = std::make_unique(); + initializePipeline(*pipeline); + return pipeline; +} + +} diff --git a/src/Processors/QueryPlan/ISourceStep.h b/src/Processors/QueryPlan/ISourceStep.h new file mode 100644 index 000000000000..8373a34b57fe --- /dev/null +++ b/src/Processors/QueryPlan/ISourceStep.h @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace DB +{ + +class ISourceStep : public IQueryPlanStep +{ +public: + explicit ISourceStep(DataStream output_stream_); + + QueryPipelinePtr updatePipeline(QueryPipelines pipelines) override; + + virtual void initializePipeline(QueryPipeline & pipeline) = 0; +}; + +} diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 360f47d345c0..d29b66876a51 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -82,7 +82,7 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() struct Frame { Node * node; - QueryPipelines pipelines; + QueryPipelines pipelines = {}; }; QueryPipelinePtr last_pipeline; diff --git a/src/Processors/QueryPlan/QueryPlan.h b/src/Processors/QueryPlan/QueryPlan.h index de932524903a..168cfc3665a5 100644 --- a/src/Processors/QueryPlan/QueryPlan.h +++ b/src/Processors/QueryPlan/QueryPlan.h @@ -30,7 +30,7 @@ class QueryPlan struct Node { QueryPlanStepPtr step; - std::vector children; + std::vector children = {}; }; using Nodes = std::list; diff --git a/src/Processors/QueryPlan/ReadFromPreparedSource.cpp b/src/Processors/QueryPlan/ReadFromPreparedSource.cpp new file mode 100644 index 000000000000..fc88e13906b1 --- /dev/null +++ b/src/Processors/QueryPlan/ReadFromPreparedSource.cpp @@ -0,0 +1,18 @@ +#include +#include + +namespace DB +{ + +ReadFromPreparedSource::ReadFromPreparedSource(Pipe pipe_) + : ISourceStep(DataStream{.header = pipe_.getHeader()}) + , pipe(std::move(pipe_)) +{ +} + +void ReadFromPreparedSource::initializePipeline(QueryPipeline & pipeline) +{ + pipeline.init(std::move(pipe)); +} + +} diff --git a/src/Processors/QueryPlan/ReadFromPreparedSource.h b/src/Processors/QueryPlan/ReadFromPreparedSource.h new file mode 100644 index 000000000000..bf3c36f072fd --- /dev/null +++ b/src/Processors/QueryPlan/ReadFromPreparedSource.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace DB +{ + +class ReadFromPreparedSource : public ISourceStep +{ +public: + explicit ReadFromPreparedSource(Pipe pipe_); + + String getName() const override { return "ReadNothing"; } + + void initializePipeline(QueryPipeline & pipeline) override; + +private: + Pipe pipe; +}; + +} diff --git a/src/Processors/QueryPlan/ReadNothingStep.cpp b/src/Processors/QueryPlan/ReadNothingStep.cpp new file mode 100644 index 000000000000..f6ca25105ae2 --- /dev/null +++ b/src/Processors/QueryPlan/ReadNothingStep.cpp @@ -0,0 +1,18 @@ +#include +#include +#include + +namespace DB +{ + +ReadNothingStep::ReadNothingStep(DataStream output_stream_) + : ISourceStep(std::move(output_stream_)) +{ +} + +void ReadNothingStep::initializePipeline(QueryPipeline & pipeline) +{ + pipeline.init(Pipe(std::make_shared(output_stream.header))); +} + +} diff --git a/src/Processors/QueryPlan/ReadNothingStep.h b/src/Processors/QueryPlan/ReadNothingStep.h new file mode 100644 index 000000000000..b881e1fad29e --- /dev/null +++ b/src/Processors/QueryPlan/ReadNothingStep.h @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace DB +{ + +class ReadNothingStep : public ISourceStep +{ +public: + explicit ReadNothingStep(DataStream output_stream_); + + String getName() const override { return "ReadNothing"; } + + void initializePipeline(QueryPipeline & pipeline) override; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 11e7894dbe12..cb938dd5f97b 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -139,9 +139,11 @@ SRCS( Transforms/AggregatingInOrderTransform.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp + QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp QueryPlan/ReadFromStorageStep.cpp + QueryPlan/ReadNothingStep.cpp QueryPlan/QueryPlan.cpp ) From ab452e93917a10c2d9ebfe1facea98e18d5996a0 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 17:14:14 +0300 Subject: [PATCH 0342/1102] Added source steps. --- src/Processors/QueryPlan/ReadNothingStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/QueryPlan/ReadNothingStep.cpp b/src/Processors/QueryPlan/ReadNothingStep.cpp index f6ca25105ae2..153fe60301fb 100644 --- a/src/Processors/QueryPlan/ReadNothingStep.cpp +++ b/src/Processors/QueryPlan/ReadNothingStep.cpp @@ -12,7 +12,7 @@ ReadNothingStep::ReadNothingStep(DataStream output_stream_) void ReadNothingStep::initializePipeline(QueryPipeline & pipeline) { - pipeline.init(Pipe(std::make_shared(output_stream.header))); + pipeline.init(Pipe(std::make_shared(getOutputStream().header))); } } From 71f99a274dae57e78738159792e18ee3707a865c Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 17:25:08 +0300 Subject: [PATCH 0343/1102] Compileable getSampleBlockWithColumns in StorageInMemoryMetadata --- src/Interpreters/InterpreterSelectQuery.cpp | 6 +- src/Storages/IStorage.cpp | 31 -------- src/Storages/IStorage.h | 1 - src/Storages/Kafka/KafkaBlockInputStream.cpp | 6 +- .../MergeTreeBaseSelectProcessor.cpp | 21 +++--- .../MergeTree/MergeTreeBaseSelectProcessor.h | 2 + .../MergeTree/MergeTreeDataMergerMutator.cpp | 6 +- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 74 ++++++++++++++----- .../MergeTree/MergeTreeDataSelectExecutor.h | 5 ++ src/Storages/MergeTree/MergeTreeReadPool.cpp | 31 +++++--- src/Storages/MergeTree/MergeTreeReadPool.h | 3 +- .../MergeTreeReverseSelectProcessor.cpp | 5 +- .../MergeTreeReverseSelectProcessor.h | 1 + .../MergeTree/MergeTreeSelectProcessor.cpp | 5 +- .../MergeTree/MergeTreeSelectProcessor.h | 1 + .../MergeTree/MergeTreeSequentialSource.cpp | 6 +- .../MergeTree/MergeTreeSequentialSource.h | 5 +- ...rgeTreeThreadSelectBlockInputProcessor.cpp | 5 +- ...MergeTreeThreadSelectBlockInputProcessor.h | 1 + .../MergeTree/StorageFromMergeTreeDataPart.h | 6 +- src/Storages/StorageBuffer.cpp | 12 +-- src/Storages/StorageInMemoryMetadata.cpp | 50 +++++++++++++ src/Storages/StorageInMemoryMetadata.h | 2 + src/Storages/StorageJoin.cpp | 4 +- src/Storages/StorageMemory.cpp | 34 ++++++--- src/Storages/StorageMerge.cpp | 14 ++-- src/Storages/StorageMerge.h | 8 +- src/Storages/StorageMergeTree.cpp | 5 +- src/Storages/StorageNull.h | 9 ++- src/Storages/StorageReplicatedMergeTree.cpp | 6 +- src/Storages/StorageS3.cpp | 12 +-- src/Storages/StorageS3.h | 5 -- src/Storages/StorageStripeLog.cpp | 27 +++++-- src/Storages/StorageURL.cpp | 9 ++- src/Storages/StorageURL.h | 6 +- src/Storages/StorageView.cpp | 7 +- src/Storages/StorageXDBC.cpp | 19 +++-- src/Storages/StorageXDBC.h | 35 +++++---- 38 files changed, 309 insertions(+), 176 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 40d7ed9ecc9d..f73245179ce9 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -377,14 +377,14 @@ InterpreterSelectQuery::InterpreterSelectQuery( if (storage) { - source_header = storage->getSampleBlockForColumns(required_columns); + source_header = metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals()); /// Fix source_header for filter actions. if (row_policy_filter) { filter_info = std::make_shared(); filter_info->column_name = generateFilterActions(filter_info->actions, row_policy_filter, required_columns); - source_header = storage->getSampleBlockForColumns(filter_info->actions->getRequiredColumns()); + source_header = metadata_snapshot->getSampleBlockForColumns(filter_info->actions->getRequiredColumns(), storage->getVirtuals()); } } @@ -1336,7 +1336,7 @@ void InterpreterSelectQuery::executeFetchColumns( if (pipes.empty()) { - Pipe pipe(std::make_shared(storage->getSampleBlockForColumns(required_columns))); + Pipe pipe(std::make_shared(metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals()))); if (query_info.prewhere_info) { diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index fd012c3cd756..d090dc9e51d1 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -62,37 +62,6 @@ Block IStorage::getSampleBlock() const return res; } -Block IStorage::getSampleBlockForColumns(const Names & column_names) const -{ - Block res; - - std::unordered_map columns_map; - - NamesAndTypesList all_columns = getColumns().getAll(); - for (const auto & elem : all_columns) - columns_map.emplace(elem.name, elem.type); - - /// Virtual columns must be appended after ordinary, because user can - /// override them. - for (const auto & column : getVirtuals()) - columns_map.emplace(column.name, column.type); - - for (const auto & name : column_names) - { - auto it = columns_map.find(name); - if (it != columns_map.end()) - { - res.insert({it->second->createColumn(), it->second, it->first}); - } - else - { - throw Exception( - "Column " + backQuote(name) + " not found in table " + getStorageID().getNameForLogs(), ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK); - } - } - - return res; -} namespace { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index e7a7786c2d6d..a4173c1c9fad 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -159,7 +159,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } Block getSampleBlock() const; /// ordinary + materialized. - Block getSampleBlockForColumns(const Names & column_names) const; /// ordinary + materialized + aliases + virtuals. /// Verify that all the requested names are in the table and are set correctly: /// list of names is not empty and the names do not repeat. diff --git a/src/Storages/Kafka/KafkaBlockInputStream.cpp b/src/Storages/Kafka/KafkaBlockInputStream.cpp index dd2bb68c11af..847b0d915cd5 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockInputStream.cpp @@ -26,8 +26,8 @@ KafkaBlockInputStream::KafkaBlockInputStream( , max_block_size(max_block_size_) , commit_in_suffix(commit_in_suffix_) , non_virtual_header(metadata_snapshot->getSampleBlockNonMaterialized()) - , virtual_header(storage.getSampleBlockForColumns( - {"_topic", "_key", "_offset", "_partition", "_timestamp", "_timestamp_ms", "_headers.name", "_headers.value"})) + , virtual_header(metadata_snapshot->getSampleBlockForColumns( + {"_topic", "_key", "_offset", "_partition", "_timestamp", "_timestamp_ms", "_headers.name", "_headers.value"}, storage.getVirtuals())) { } @@ -44,7 +44,7 @@ KafkaBlockInputStream::~KafkaBlockInputStream() Block KafkaBlockInputStream::getHeader() const { - return storage.getSampleBlockForColumns(column_names); + return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals()); } void KafkaBlockInputStream::readPrefixImpl() diff --git a/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.cpp index a2a3ca3a6cf8..ec24c9ad652a 100644 --- a/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.cpp @@ -20,6 +20,7 @@ namespace ErrorCodes MergeTreeBaseSelectProcessor::MergeTreeBaseSelectProcessor( Block header, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const PrewhereInfoPtr & prewhere_info_, UInt64 max_block_size_rows_, UInt64 preferred_block_size_bytes_, @@ -27,16 +28,16 @@ MergeTreeBaseSelectProcessor::MergeTreeBaseSelectProcessor( const MergeTreeReaderSettings & reader_settings_, bool use_uncompressed_cache_, const Names & virt_column_names_) -: - SourceWithProgress(getHeader(std::move(header), prewhere_info_, virt_column_names_)), - storage(storage_), - prewhere_info(prewhere_info_), - max_block_size_rows(max_block_size_rows_), - preferred_block_size_bytes(preferred_block_size_bytes_), - preferred_max_column_in_block_size_bytes(preferred_max_column_in_block_size_bytes_), - reader_settings(reader_settings_), - use_uncompressed_cache(use_uncompressed_cache_), - virt_column_names(virt_column_names_) + : SourceWithProgress(getHeader(std::move(header), prewhere_info_, virt_column_names_)) + , storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , prewhere_info(prewhere_info_) + , max_block_size_rows(max_block_size_rows_) + , preferred_block_size_bytes(preferred_block_size_bytes_) + , preferred_max_column_in_block_size_bytes(preferred_max_column_in_block_size_bytes_) + , reader_settings(reader_settings_) + , use_uncompressed_cache(use_uncompressed_cache_) + , virt_column_names(virt_column_names_) { header_without_virtual_columns = getPort().getHeader(); diff --git a/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.h b/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.h index 8fe8296381a4..00ef131ae45e 100644 --- a/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.h +++ b/src/Storages/MergeTree/MergeTreeBaseSelectProcessor.h @@ -22,6 +22,7 @@ class MergeTreeBaseSelectProcessor : public SourceWithProgress MergeTreeBaseSelectProcessor( Block header, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const PrewhereInfoPtr & prewhere_info_, UInt64 max_block_size_rows_, UInt64 preferred_block_size_bytes_, @@ -54,6 +55,7 @@ class MergeTreeBaseSelectProcessor : public SourceWithProgress protected: const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; PrewhereInfoPtr prewhere_info; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 595370e7eccb..829f7cac5286 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -579,7 +579,7 @@ class MergeProgressCallback /// parts should be sorted. MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTemporaryPart( const FutureMergedMutatedPart & future_part, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, MergeList::Entry & merge_entry, TableStructureReadLockHolder &, time_t time_of_merge, @@ -712,7 +712,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor for (const auto & part : parts) { auto input = std::make_unique( - data, part, merging_column_names, read_with_direct_io, true); + data, metadata_snapshot, part, merging_column_names, read_with_direct_io, true); input->setProgressCallback( MergeProgressCallback(merge_entry, watch_prev_elapsed, horizontal_stage_progress)); @@ -898,7 +898,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor for (size_t part_num = 0; part_num < parts.size(); ++part_num) { auto column_part_source = std::make_shared( - data, parts[part_num], column_names, read_with_direct_io, true); + data, metadata_snapshot, parts[part_num], column_names, read_with_direct_io, true); column_part_source->setProgressCallback( MergeProgressCallback(merge_entry, watch_prev_elapsed, column_progress)); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index c1fc8184206f..ac2f48511855 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -147,6 +147,7 @@ static RelativeSize convertAbsoluteSampleSizeToRelative(const ASTPtr & node, siz Pipes MergeTreeDataSelectExecutor::read( const Names & column_names_to_return, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, const UInt64 max_block_size, @@ -154,13 +155,15 @@ Pipes MergeTreeDataSelectExecutor::read( const PartitionIdToMaxBlock * max_block_numbers_to_read) const { return readFromParts( - data.getDataPartsVector(), column_names_to_return, query_info, context, - max_block_size, num_streams, max_block_numbers_to_read); + data.getDataPartsVector(), column_names_to_return, metadata_snapshot, + query_info, context, max_block_size, num_streams, + max_block_numbers_to_read); } Pipes MergeTreeDataSelectExecutor::readFromParts( MergeTreeData::DataPartsVector parts, const Names & column_names_to_return, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, const UInt64 max_block_size, @@ -205,7 +208,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( } } - NamesAndTypesList available_real_columns = data.getColumns().getAllPhysical(); + NamesAndTypesList available_real_columns = metadata_snapshot->getColumns().getAllPhysical(); /// If there are only virtual columns in the query, you must request at least one non-virtual one. if (real_column_names.empty()) @@ -629,6 +632,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::move(parts_with_ranges), num_streams, column_names_to_read, + metadata_snapshot, max_block_size, settings.use_uncompressed_cache, query_info, @@ -650,6 +654,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::move(parts_with_ranges), num_streams, column_names_to_read, + metadata_snapshot, max_block_size, settings.use_uncompressed_cache, query_info, @@ -665,6 +670,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::move(parts_with_ranges), num_streams, column_names_to_read, + metadata_snapshot, max_block_size, settings.use_uncompressed_cache, query_info, @@ -727,6 +733,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreams( RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, @@ -783,8 +790,18 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreams( num_streams = std::max((sum_marks + min_marks_for_concurrent_read - 1) / min_marks_for_concurrent_read, parts.size()); MergeTreeReadPoolPtr pool = std::make_shared( - num_streams, sum_marks, min_marks_for_concurrent_read, parts, data, query_info.prewhere_info, true, - column_names, MergeTreeReadPool::BackoffSettings(settings), settings.preferred_block_size_bytes, false); + num_streams, + sum_marks, + min_marks_for_concurrent_read, + parts, + data, + metadata_snapshot, + query_info.prewhere_info, + true, + column_names, + MergeTreeReadPool::BackoffSettings(settings), + settings.preferred_block_size_bytes, + false); /// Let's estimate total number of rows for progress bar. LOG_TRACE(log, "Reading approx. {} rows with {} streams", total_rows, num_streams); @@ -792,8 +809,9 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreams( for (size_t i = 0; i < num_streams; ++i) { auto source = std::make_shared( - i, pool, min_marks_for_concurrent_read, max_block_size, settings.preferred_block_size_bytes, - settings.preferred_max_column_in_block_size_bytes, data, use_uncompressed_cache, + i, pool, min_marks_for_concurrent_read, max_block_size, + settings.preferred_block_size_bytes, settings.preferred_max_column_in_block_size_bytes, + data, metadata_snapshot, use_uncompressed_cache, query_info.prewhere_info, reader_settings, virt_columns); if (i == 0) @@ -812,7 +830,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreams( for (const auto & part : parts) { auto source = std::make_shared( - data, part.data_part, max_block_size, settings.preferred_block_size_bytes, + data, metadata_snapshot, part.data_part, max_block_size, settings.preferred_block_size_bytes, settings.preferred_max_column_in_block_size_bytes, column_names, part.ranges, use_uncompressed_cache, query_info.prewhere_info, true, reader_settings, virt_columns, part.part_index_in_query); @@ -845,6 +863,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, @@ -1004,18 +1023,38 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( if (input_order_info->direction == 1) { pipes.emplace_back(std::make_shared( - data, part.data_part, max_block_size, settings.preferred_block_size_bytes, - settings.preferred_max_column_in_block_size_bytes, column_names, ranges_to_get_from_part, - use_uncompressed_cache, query_info.prewhere_info, true, reader_settings, - virt_columns, part.part_index_in_query)); + data, + metadata_snapshot, + part.data_part, + max_block_size, + settings.preferred_block_size_bytes, + settings.preferred_max_column_in_block_size_bytes, + column_names, + ranges_to_get_from_part, + use_uncompressed_cache, + query_info.prewhere_info, + true, + reader_settings, + virt_columns, + part.part_index_in_query)); } else { pipes.emplace_back(std::make_shared( - data, part.data_part, max_block_size, settings.preferred_block_size_bytes, - settings.preferred_max_column_in_block_size_bytes, column_names, ranges_to_get_from_part, - use_uncompressed_cache, query_info.prewhere_info, true, reader_settings, - virt_columns, part.part_index_in_query)); + data, + metadata_snapshot, + part.data_part, + max_block_size, + settings.preferred_block_size_bytes, + settings.preferred_max_column_in_block_size_bytes, + column_names, + ranges_to_get_from_part, + use_uncompressed_cache, + query_info.prewhere_info, + true, + reader_settings, + virt_columns, + part.part_index_in_query)); pipes.back().addSimpleTransform(std::make_shared(pipes.back().getHeader())); } @@ -1050,6 +1089,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsFinal( RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, @@ -1088,7 +1128,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsFinal( for (const auto & part : parts) { auto source_processor = std::make_shared( - data, part.data_part, max_block_size, settings.preferred_block_size_bytes, + data, metadata_snapshot, part.data_part, max_block_size, settings.preferred_block_size_bytes, settings.preferred_max_column_in_block_size_bytes, column_names, part.ranges, use_uncompressed_cache, query_info.prewhere_info, true, reader_settings, virt_columns, part.part_index_in_query); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h index 85d69ead181c..7811eb53b713 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -26,6 +26,7 @@ class MergeTreeDataSelectExecutor Pipes read( const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, UInt64 max_block_size, @@ -35,6 +36,7 @@ class MergeTreeDataSelectExecutor Pipes readFromParts( MergeTreeData::DataPartsVector parts, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, UInt64 max_block_size, @@ -50,6 +52,7 @@ class MergeTreeDataSelectExecutor RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, @@ -62,6 +65,7 @@ class MergeTreeDataSelectExecutor RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, @@ -75,6 +79,7 @@ class MergeTreeDataSelectExecutor RangesInDataParts && parts, size_t num_streams, const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, UInt64 max_block_size, bool use_uncompressed_cache, const SelectQueryInfo & query_info, diff --git a/src/Storages/MergeTree/MergeTreeReadPool.cpp b/src/Storages/MergeTree/MergeTreeReadPool.cpp index 9ca1446ef643..eb0b51235ad3 100644 --- a/src/Storages/MergeTree/MergeTreeReadPool.cpp +++ b/src/Storages/MergeTree/MergeTreeReadPool.cpp @@ -17,17 +17,28 @@ namespace ErrorCodes namespace DB { - - MergeTreeReadPool::MergeTreeReadPool( - const size_t threads_, const size_t sum_marks_, const size_t min_marks_for_concurrent_read_, - RangesInDataParts parts_, const MergeTreeData & data_, const PrewhereInfoPtr & prewhere_info_, - const bool check_columns_, const Names & column_names_, - const BackoffSettings & backoff_settings_, size_t preferred_block_size_bytes_, + const size_t threads_, + const size_t sum_marks_, + const size_t min_marks_for_concurrent_read_, + RangesInDataParts parts_, + const MergeTreeData & data_, + const StorageMetadataPtr & metadata_snapshot_, + const PrewhereInfoPtr & prewhere_info_, + const bool check_columns_, + const Names & column_names_, + const BackoffSettings & backoff_settings_, + size_t preferred_block_size_bytes_, const bool do_not_steal_tasks_) - : backoff_settings{backoff_settings_}, backoff_state{threads_}, data{data_}, - column_names{column_names_}, do_not_steal_tasks{do_not_steal_tasks_}, - predict_block_size_bytes{preferred_block_size_bytes_ > 0}, prewhere_info{prewhere_info_}, parts_ranges{parts_} + : backoff_settings{backoff_settings_} + , backoff_state{threads_} + , data{data_} + , metadata_snapshot{metadata_snapshot_} + , column_names{column_names_} + , do_not_steal_tasks{do_not_steal_tasks_} + , predict_block_size_bytes{preferred_block_size_bytes_ > 0} + , prewhere_info{prewhere_info_} + , parts_ranges{parts_} { /// parts don't contain duplicate MergeTreeDataPart's. const auto per_part_sum_marks = fillPerPartInfo(parts_, check_columns_); @@ -139,7 +150,7 @@ MarkRanges MergeTreeReadPool::getRestMarks(const IMergeTreeDataPart & part, cons Block MergeTreeReadPool::getHeader() const { - return data.getSampleBlockForColumns(column_names); + return metadata_snapshot->getSampleBlockForColumns(column_names, data.getVirtuals()); } void MergeTreeReadPool::profileFeedback(const ReadBufferFromFileBase::ProfileInfo info) diff --git a/src/Storages/MergeTree/MergeTreeReadPool.h b/src/Storages/MergeTree/MergeTreeReadPool.h index c43074f19624..c0b04c6a2280 100644 --- a/src/Storages/MergeTree/MergeTreeReadPool.h +++ b/src/Storages/MergeTree/MergeTreeReadPool.h @@ -68,7 +68,7 @@ class MergeTreeReadPool : private boost::noncopyable public: MergeTreeReadPool( const size_t threads_, const size_t sum_marks_, const size_t min_marks_for_concurrent_read_, - RangesInDataParts parts_, const MergeTreeData & data_, const PrewhereInfoPtr & prewhere_info_, + RangesInDataParts parts_, const MergeTreeData & data_, const StorageMetadataPtr & metadata_snapshot_, const PrewhereInfoPtr & prewhere_info_, const bool check_columns_, const Names & column_names_, const BackoffSettings & backoff_settings_, size_t preferred_block_size_bytes_, const bool do_not_steal_tasks_ = false); @@ -95,6 +95,7 @@ class MergeTreeReadPool : private boost::noncopyable RangesInDataParts & parts, const size_t min_marks_for_concurrent_read); const MergeTreeData & data; + StorageMetadataPtr metadata_snapshot; Names column_names; bool do_not_steal_tasks; bool predict_block_size_bytes; diff --git a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp index 09bf784a2934..813666149886 100644 --- a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp @@ -34,6 +34,7 @@ static Block replaceTypes(Block && header, const MergeTreeData::DataPartPtr & da MergeTreeReverseSelectProcessor::MergeTreeReverseSelectProcessor( const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const MergeTreeData::DataPartPtr & owned_data_part_, UInt64 max_block_size_rows_, size_t preferred_block_size_bytes_, @@ -49,8 +50,8 @@ MergeTreeReverseSelectProcessor::MergeTreeReverseSelectProcessor( bool quiet) : MergeTreeBaseSelectProcessor{ - replaceTypes(storage_.getSampleBlockForColumns(required_columns_), owned_data_part_), - storage_, prewhere_info_, max_block_size_rows_, + replaceTypes(metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals()), owned_data_part_), + storage_, metadata_snapshot_, prewhere_info_, max_block_size_rows_, preferred_block_size_bytes_, preferred_max_column_in_block_size_bytes_, reader_settings_, use_uncompressed_cache_, virt_column_names_}, required_columns{std::move(required_columns_)}, diff --git a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.h b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.h index ea603bd468f0..c9fd06c55345 100644 --- a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.h +++ b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.h @@ -18,6 +18,7 @@ class MergeTreeReverseSelectProcessor : public MergeTreeBaseSelectProcessor public: MergeTreeReverseSelectProcessor( const MergeTreeData & storage, + const StorageMetadataPtr & metadata_snapshot, const MergeTreeData::DataPartPtr & owned_data_part, UInt64 max_block_size_rows, size_t preferred_block_size_bytes, diff --git a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp index df471a8b8ec3..e32fa70cb973 100644 --- a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp @@ -14,6 +14,7 @@ namespace ErrorCodes MergeTreeSelectProcessor::MergeTreeSelectProcessor( const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const MergeTreeData::DataPartPtr & owned_data_part_, UInt64 max_block_size_rows_, size_t preferred_block_size_bytes_, @@ -29,8 +30,8 @@ MergeTreeSelectProcessor::MergeTreeSelectProcessor( bool quiet) : MergeTreeBaseSelectProcessor{ - storage_.getSampleBlockForColumns(required_columns_), - storage_, prewhere_info_, max_block_size_rows_, + metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals()), + storage_, metadata_snapshot_, prewhere_info_, max_block_size_rows_, preferred_block_size_bytes_, preferred_max_column_in_block_size_bytes_, reader_settings_, use_uncompressed_cache_, virt_column_names_}, required_columns{std::move(required_columns_)}, diff --git a/src/Storages/MergeTree/MergeTreeSelectProcessor.h b/src/Storages/MergeTree/MergeTreeSelectProcessor.h index d2438e20192c..dff4ebc2627a 100644 --- a/src/Storages/MergeTree/MergeTreeSelectProcessor.h +++ b/src/Storages/MergeTree/MergeTreeSelectProcessor.h @@ -18,6 +18,7 @@ class MergeTreeSelectProcessor : public MergeTreeBaseSelectProcessor public: MergeTreeSelectProcessor( const MergeTreeData & storage, + const StorageMetadataPtr & metadata_snapshot, const MergeTreeData::DataPartPtr & owned_data_part, UInt64 max_block_size_rows, size_t preferred_block_size_bytes, diff --git a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp index 045962f44dd4..dfd60bd50ef6 100644 --- a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp +++ b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp @@ -11,13 +11,15 @@ namespace ErrorCodes MergeTreeSequentialSource::MergeTreeSequentialSource( const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, MergeTreeData::DataPartPtr data_part_, Names columns_to_read_, bool read_with_direct_io_, bool take_column_types_from_storage, bool quiet) - : SourceWithProgress(storage_.getSampleBlockForColumns(columns_to_read_)) + : SourceWithProgress(metadata_snapshot_->getSampleBlockForColumns(columns_to_read_, storage_.getVirtuals())) , storage(storage_) + , metadata_snapshot(metadata_snapshot_) , data_part(std::move(data_part_)) , columns_to_read(std::move(columns_to_read_)) , read_with_direct_io(read_with_direct_io_) @@ -41,7 +43,7 @@ MergeTreeSequentialSource::MergeTreeSequentialSource( NamesAndTypesList columns_for_reader; if (take_column_types_from_storage) { - const NamesAndTypesList & physical_columns = storage.getColumns().getAllPhysical(); + const NamesAndTypesList & physical_columns = metadata_snapshot->getColumns().getAllPhysical(); columns_for_reader = physical_columns.addTypes(columns_to_read); } else diff --git a/src/Storages/MergeTree/MergeTreeSequentialSource.h b/src/Storages/MergeTree/MergeTreeSequentialSource.h index 6155fef200ac..7eefdd9335bd 100644 --- a/src/Storages/MergeTree/MergeTreeSequentialSource.h +++ b/src/Storages/MergeTree/MergeTreeSequentialSource.h @@ -14,12 +14,12 @@ class MergeTreeSequentialSource : public SourceWithProgress public: MergeTreeSequentialSource( const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, MergeTreeData::DataPartPtr data_part_, Names columns_to_read_, bool read_with_direct_io_, bool take_column_types_from_storage, - bool quiet = false - ); + bool quiet = false); ~MergeTreeSequentialSource() override; @@ -35,6 +35,7 @@ class MergeTreeSequentialSource : public SourceWithProgress private: const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; /// Data part will not be removed if the pointer owns it MergeTreeData::DataPartPtr data_part; diff --git a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp index 0b09fad91d17..784c842d7d60 100644 --- a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp @@ -16,12 +16,15 @@ MergeTreeThreadSelectBlockInputProcessor::MergeTreeThreadSelectBlockInputProcess size_t preferred_block_size_bytes_, size_t preferred_max_column_in_block_size_bytes_, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const bool use_uncompressed_cache_, const PrewhereInfoPtr & prewhere_info_, const MergeTreeReaderSettings & reader_settings_, const Names & virt_column_names_) : - MergeTreeBaseSelectProcessor{pool_->getHeader(), storage_, prewhere_info_, max_block_size_rows_, + MergeTreeBaseSelectProcessor{ + pool_->getHeader(), storage_, metadata_snapshot_, prewhere_info_, + max_block_size_rows_, preferred_block_size_bytes_, preferred_max_column_in_block_size_bytes_, reader_settings_, use_uncompressed_cache_, virt_column_names_}, thread{thread_}, diff --git a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.h b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.h index e214696b7054..d5a11f3d93b2 100644 --- a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.h +++ b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.h @@ -22,6 +22,7 @@ class MergeTreeThreadSelectBlockInputProcessor : public MergeTreeBaseSelectProce size_t preferred_block_size_bytes_, size_t preferred_max_column_in_block_size_bytes_, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const bool use_uncompressed_cache_, const PrewhereInfoPtr & prewhere_info_, const MergeTreeReaderSettings & reader_settings_, diff --git a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h index 826af505b120..45ee947b81fc 100644 --- a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h +++ b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h @@ -21,15 +21,15 @@ class StorageFromMergeTreeDataPart final : public ext::shared_ptr_helperstorage).readFromParts( - {part}, column_names, query_info, context, max_block_size, num_streams); + return MergeTreeDataSelectExecutor(part->storage) + .readFromParts({part}, column_names, metadata_snapshot, query_info, context, max_block_size, num_streams); } diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 4754732159c8..42eab838f32b 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -88,9 +88,11 @@ StorageBuffer::StorageBuffer( class BufferSource : public SourceWithProgress { public: - BufferSource(const Names & column_names_, StorageBuffer::Buffer & buffer_, const StorageBuffer & storage) - : SourceWithProgress(storage.getSampleBlockForColumns(column_names_)) - , column_names(column_names_.begin(), column_names_.end()), buffer(buffer_) {} + BufferSource(const Names & column_names_, StorageBuffer::Buffer & buffer_, const StorageBuffer & storage, const StorageMetadataPtr & metadata_snapshot) + : SourceWithProgress( + metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals())) + , column_names(column_names_.begin(), column_names_.end()) + , buffer(buffer_) {} String getName() const override { return "Buffer"; } @@ -145,7 +147,7 @@ QueryProcessingStage::Enum StorageBuffer::getQueryProcessingStage(const Context Pipes StorageBuffer::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -236,7 +238,7 @@ Pipes StorageBuffer::read( Pipes pipes_from_buffers; pipes_from_buffers.reserve(num_shards); for (auto & buf : buffers) - pipes_from_buffers.emplace_back(std::make_shared(column_names, buf, *this)); + pipes_from_buffers.emplace_back(std::make_shared(column_names, buf, *this, metadata_snapshot)); /** If the sources from the table were processed before some non-initial stage of query execution, * then sources from the buffers must also be wrapped in the processing pipeline before the same stage. diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index f3719562af7b..cce3911370d4 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -1,7 +1,24 @@ #include +#include + namespace DB { +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; + extern const int COLUMN_QUERIED_MORE_THAN_ONCE; + extern const int DUPLICATE_COLUMN; + extern const int EMPTY_LIST_OF_COLUMNS_PASSED; + extern const int EMPTY_LIST_OF_COLUMNS_QUERIED; + extern const int NO_SUCH_COLUMN_IN_TABLE; + extern const int NOT_FOUND_COLUMN_IN_BLOCK; + extern const int TYPE_MISMATCH; + extern const int TABLE_IS_DROPPED; + extern const int NOT_IMPLEMENTED; + extern const int DEADLOCK_AVOIDED; +} + StorageInMemoryMetadata::StorageInMemoryMetadata( const ColumnsDescription & columns_, @@ -240,4 +257,37 @@ Block StorageInMemoryMetadata::getSampleBlock() const return res; } + +Block StorageInMemoryMetadata::getSampleBlockForColumns(const Names & column_names, const NamesAndTypesList & virtuals) const +{ + Block res; + + std::unordered_map columns_map; + + NamesAndTypesList all_columns = getColumns().getAll(); + for (const auto & elem : all_columns) + columns_map.emplace(elem.name, elem.type); + + /// Virtual columns must be appended after ordinary, because user can + /// override them. + for (const auto & column : virtuals) + columns_map.emplace(column.name, column.type); + + for (const auto & name : column_names) + { + auto it = columns_map.find(name); + if (it != columns_map.end()) + { + res.insert({it->second->createColumn(), it->second, it->first}); + } + else + { + throw Exception( + "Column " + backQuote(name) + " not found in table " /*+ getStorageID().getNameForLogs() TODO(alesap)*/, + ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK); + } + } + + return res; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 2da766caacd4..9f9154c48fb8 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -110,6 +110,8 @@ struct StorageInMemoryMetadata Block getSampleBlock() const; /// ordinary + materialized. Block getSampleBlockNonMaterialized() const; /// ordinary. Block getSampleBlockWithVirtuals(const NamesAndTypesList & virtuals) const; /// ordinary + materialized + virtuals. + Block getSampleBlockForColumns( + const Names & column_names, const NamesAndTypesList & virtuals) const; /// ordinary + materialized + aliases + virtuals. }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 7ed4c1c110b2..7d481395ef47 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -435,7 +435,7 @@ class JoinSource : public SourceWithProgress // TODO: multiple stream read and index read Pipes StorageJoin::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, @@ -445,7 +445,7 @@ Pipes StorageJoin::read( check(column_names); Pipes pipes; - pipes.emplace_back(std::make_shared(*join, max_block_size, getSampleBlockForColumns(column_names))); + pipes.emplace_back(std::make_shared(*join, max_block_size, metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); return pipes; } diff --git a/src/Storages/StorageMemory.cpp b/src/Storages/StorageMemory.cpp index f9c39d78a051..442c5a3d67bf 100644 --- a/src/Storages/StorageMemory.cpp +++ b/src/Storages/StorageMemory.cpp @@ -22,9 +22,19 @@ namespace ErrorCodes class MemorySource : public SourceWithProgress { public: - MemorySource(Names column_names_, BlocksList::iterator begin_, BlocksList::iterator end_, const StorageMemory & storage) - : SourceWithProgress(storage.getSampleBlockForColumns(column_names_)) - , column_names(std::move(column_names_)), begin(begin_), end(end_), it(begin) {} + MemorySource( + Names column_names_, + BlocksList::iterator begin_, + BlocksList::iterator end_, + const StorageMemory & storage, + const StorageMetadataPtr & metadata_snapshot) + : SourceWithProgress(metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals())) + , column_names(std::move(column_names_)) + , begin(begin_) + , end(end_) + , it(begin) + { + } String getName() const override { return "Memory"; } @@ -60,9 +70,14 @@ class MemorySource : public SourceWithProgress class MemoryBlockOutputStream : public IBlockOutputStream { public: - explicit MemoryBlockOutputStream(StorageMemory & storage_) : storage(storage_) {} + explicit MemoryBlockOutputStream( + StorageMemory & storage_, + const StorageMetadataPtr & metadata_snapshot_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + {} - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override { @@ -72,6 +87,7 @@ class MemoryBlockOutputStream : public IBlockOutputStream } private: StorageMemory & storage; + StorageMetadataPtr metadata_snapshot; }; @@ -87,7 +103,7 @@ StorageMemory::StorageMemory(const StorageID & table_id_, ColumnsDescription col Pipes StorageMemory::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, @@ -113,16 +129,16 @@ Pipes StorageMemory::read( std::advance(begin, stream * size / num_streams); std::advance(end, (stream + 1) * size / num_streams); - pipes.emplace_back(std::make_shared(column_names, begin, end, *this)); + pipes.emplace_back(std::make_shared(column_names, begin, end, *this, metadata_snapshot)); } return pipes; } -BlockOutputStreamPtr StorageMemory::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageMemory::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { - return std::make_shared(*this); + return std::make_shared(*this, metadata_snapshot); } diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 6656e91189cd..c5a3c20bb0c6 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -129,7 +129,7 @@ QueryProcessingStage::Enum StorageMerge::getQueryProcessingStage(const Context & Pipes StorageMerge::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -157,7 +157,7 @@ Pipes StorageMerge::read( modified_context->setSetting("optimize_move_to_prewhere", false); /// What will be result structure depending on query processed stage in source tables? - Block header = getQueryHeader(column_names, query_info, context, processed_stage); + Block header = getQueryHeader(column_names, metadata_snapshot, query_info, context, processed_stage); /** First we make list of selected tables to find out its size. * This is necessary to correctly pass the recommended number of threads to each table. @@ -401,13 +401,17 @@ void StorageMerge::alter( } Block StorageMerge::getQueryHeader( - const Names & column_names, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage) + const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage) { switch (processed_stage) { case QueryProcessingStage::FetchColumns: { - Block header = getSampleBlockForColumns(column_names); + Block header = metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()); if (query_info.prewhere_info) { query_info.prewhere_info->prewhere_actions->execute(header); @@ -420,7 +424,7 @@ Block StorageMerge::getQueryHeader( case QueryProcessingStage::WithMergeableState: case QueryProcessingStage::Complete: return materializeBlock(InterpreterSelectQuery( - query_info.query, context, std::make_shared(getSampleBlockForColumns(column_names)), + query_info.query, context, std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals())), SelectQueryOptions(processed_stage).analyze()).getSampleBlock()); } throw Exception("Logical Error: unknown processed stage.", ErrorCodes::LOGICAL_ERROR); diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index a5d3b8d26677..350f7a124fe4 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -74,8 +74,12 @@ class StorageMerge final : public ext::shared_ptr_helper, public I const String & table_name_regexp_, const Context & context_); - Block getQueryHeader(const Names & column_names, const SelectQueryInfo & query_info, - const Context & context, QueryProcessingStage::Enum processed_stage); + Block getQueryHeader( + const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum processed_stage); Pipes createSources( const SelectQueryInfo & query_info, const QueryProcessingStage::Enum & processed_stage, diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 1b00487c8163..9c37cdd2b7c7 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -177,14 +177,15 @@ StorageMergeTree::~StorageMergeTree() Pipes StorageMergeTree::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, const size_t max_block_size, const unsigned num_streams) { - return reader.read(column_names, query_info, context, max_block_size, num_streams); + return reader.read(column_names, metadata_snapshot, query_info, + context, max_block_size, num_streams); } std::optional StorageMergeTree::totalRows() const diff --git a/src/Storages/StorageNull.h b/src/Storages/StorageNull.h index 72934d185c72..6bd102bdcdac 100644 --- a/src/Storages/StorageNull.h +++ b/src/Storages/StorageNull.h @@ -24,7 +24,7 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt Pipes read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo &, const Context & /*context*/, QueryProcessingStage::Enum /*processing_stage*/, @@ -32,13 +32,14 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt unsigned) override { Pipes pipes; - pipes.emplace_back(std::make_shared(getSampleBlockForColumns(column_names))); + pipes.emplace_back( + std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); return pipes; } - BlockOutputStreamPtr write(const ASTPtr &, const StorageMetadataPtr & /*metadata_snapshot*/, const Context &) override + BlockOutputStreamPtr write(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &) override { - return std::make_shared(getSampleBlock()); + return std::make_shared(metadata_snapshot->getSampleBlock()); } void checkAlterIsPossible(const AlterCommands & commands, const Settings & /* settings */) const override; diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 810a4fa5c971..a6f84ffe4df7 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3387,7 +3387,7 @@ ReplicatedMergeTreeQuorumAddedParts::PartitionIdToMaxBlock StorageReplicatedMerg Pipes StorageReplicatedMergeTree::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -3402,10 +3402,10 @@ Pipes StorageReplicatedMergeTree::read( if (context.getSettingsRef().select_sequential_consistency) { auto max_added_blocks = getMaxAddedBlocks(); - return reader.read(column_names, query_info, context, max_block_size, num_streams, &max_added_blocks); + return reader.read(column_names, metadata_snapshot, query_info, context, max_block_size, num_streams, &max_added_blocks); } - return reader.read(column_names, query_info, context, max_block_size, num_streams); + return reader.read(column_names, metadata_snapshot, query_info, context, max_block_size, num_streams); } diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index 093f4450ecbf..7f237fd551f0 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -285,7 +285,7 @@ Strings listFilesWithRegexpMatching(Aws::S3::S3Client & client, const S3::URI & Pipes StorageS3::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -309,9 +309,9 @@ Pipes StorageS3::read( need_file_column, format_name, getName(), - getHeaderBlock(column_names), + metadata_snapshot->getSampleBlock(), context, - getColumns().getDefaults(), + metadata_snapshot->getColumns().getDefaults(), max_block_size, chooseCompressionMethod(uri.endpoint, compression_method), client, @@ -321,11 +321,11 @@ Pipes StorageS3::read( return narrowPipes(std::move(pipes), num_streams); } -BlockOutputStreamPtr StorageS3::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageS3::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { return std::make_shared( - format_name, min_upload_part_size, getSampleBlock(), context_global, - chooseCompressionMethod(uri.endpoint, compression_method), + format_name, min_upload_part_size, metadata_snapshot->getSampleBlock(), + context_global, chooseCompressionMethod(uri.endpoint, compression_method), client, uri.bucket, uri.key); } diff --git a/src/Storages/StorageS3.h b/src/Storages/StorageS3.h index 665c00b8033c..a468d69d223b 100644 --- a/src/Storages/StorageS3.h +++ b/src/Storages/StorageS3.h @@ -41,11 +41,6 @@ class StorageS3 final : public ext::shared_ptr_helper, public IStorag return "S3"; } - Block getHeaderBlock(const Names & /*column_names*/) const - { - return getSampleBlock(); - } - Pipes read( const Names & column_names, const StorageMetadataPtr & /*metadata_snapshot*/, diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index c320d0afb42f..b0c5bcfd669d 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -54,12 +54,13 @@ class StripeLogSource final : public SourceWithProgress static Block getHeader( StorageStripeLog & storage, + const StorageMetadataPtr & metadata_snapshot, const Names & column_names, IndexForNativeFormat::Blocks::const_iterator index_begin, IndexForNativeFormat::Blocks::const_iterator index_end) { if (index_begin == index_end) - return storage.getSampleBlockForColumns(column_names); + return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals()); /// TODO: check if possible to always return storage.getSampleBlock() @@ -74,13 +75,22 @@ class StripeLogSource final : public SourceWithProgress return header; } - StripeLogSource(StorageStripeLog & storage_, const Names & column_names, size_t max_read_buffer_size_, + StripeLogSource( + StorageStripeLog & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const Names & column_names, + size_t max_read_buffer_size_, std::shared_ptr & index_, IndexForNativeFormat::Blocks::const_iterator index_begin_, IndexForNativeFormat::Blocks::const_iterator index_end_) - : SourceWithProgress(getHeader(storage_, column_names, index_begin_, index_end_)) - , storage(storage_), max_read_buffer_size(max_read_buffer_size_) - , index(index_), index_begin(index_begin_), index_end(index_end_) + : SourceWithProgress( + getHeader(storage_, metadata_snapshot_, column_names, index_begin_, index_end_)) + , storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , max_read_buffer_size(max_read_buffer_size_) + , index(index_) + , index_begin(index_begin_) + , index_end(index_end_) { } @@ -110,6 +120,7 @@ class StripeLogSource final : public SourceWithProgress private: StorageStripeLog & storage; + StorageMetadataPtr metadata_snapshot; size_t max_read_buffer_size; std::shared_ptr index; @@ -253,7 +264,7 @@ void StorageStripeLog::rename(const String & new_path_to_table_data, const Stora Pipes StorageStripeLog::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -271,7 +282,7 @@ Pipes StorageStripeLog::read( String index_file = table_path + "index.mrk"; if (!disk->exists(index_file)) { - pipes.emplace_back(std::make_shared(getSampleBlockForColumns(column_names))); + pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); return pipes; } @@ -291,7 +302,7 @@ Pipes StorageStripeLog::read( std::advance(end, (stream + 1) * size / num_streams); pipes.emplace_back(std::make_shared( - *this, column_names, context.getSettingsRef().max_read_buffer_size, index, begin, end)); + *this, metadata_snapshot, column_names, context.getSettingsRef().max_read_buffer_size, index, begin, end)); } /// We do not keep read lock directly at the time of reading, because we read ranges of data that do not change. diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index 0361718c6160..6cea71150662 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -157,7 +157,7 @@ std::function IStorageURLBase::getReadPOSTDataCallback(con Pipes IStorageURLBase::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -170,14 +170,15 @@ Pipes IStorageURLBase::read( request_uri.addQueryParameter(param, value); Pipes pipes; - pipes.emplace_back(std::make_shared(request_uri, + pipes.emplace_back(std::make_shared( + request_uri, getReadMethod(), getReadPOSTDataCallback(column_names, query_info, context, processed_stage, max_block_size), format_name, getName(), - getHeaderBlock(column_names), + getHeaderBlock(column_names, metadata_snapshot), context, - getColumns().getDefaults(), + metadata_snapshot->getColumns().getDefaults(), max_block_size, ConnectionTimeouts::getHTTPTimeouts(context), chooseCompressionMethod(request_uri.getPath(), compression_method))); diff --git a/src/Storages/StorageURL.h b/src/Storages/StorageURL.h index ecd57024a44a..04cbb278c379 100644 --- a/src/Storages/StorageURL.h +++ b/src/Storages/StorageURL.h @@ -62,7 +62,7 @@ class IStorageURLBase : public IStorage QueryProcessingStage::Enum & processed_stage, size_t max_block_size) const; - virtual Block getHeaderBlock(const Names & column_names) const = 0; + virtual Block getHeaderBlock(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const = 0; }; class StorageURLBlockOutputStream : public IBlockOutputStream @@ -124,9 +124,9 @@ class StorageURL final : public ext::shared_ptr_helper, public IStor return "URL"; } - Block getHeaderBlock(const Names & /*column_names*/) const override + Block getHeaderBlock(const Names & /*column_names*/, const StorageMetadataPtr & metadata_snapshot) const override { - return getSampleBlock(); + return metadata_snapshot->getSampleBlock(); } }; } diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 52b7e8764d9f..7e49580d6c2a 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -54,7 +54,7 @@ StorageView::StorageView( Pipes StorageView::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -86,8 +86,9 @@ Pipes StorageView::read( /// And also convert to expected structure. pipeline.addSimpleTransform([&](const Block & header) { - return std::make_shared(header, getSampleBlockForColumns(column_names), - ConvertingTransform::MatchColumnsMode::Name); + return std::make_shared( + header, metadata_snapshot->getSampleBlockForColumns( + column_names, getVirtuals()), ConvertingTransform::MatchColumnsMode::Name); }); pipes = std::move(pipeline).getPipes(); diff --git a/src/Storages/StorageXDBC.cpp b/src/Storages/StorageXDBC.cpp index c7fa8a882516..a837cf5b4ba5 100644 --- a/src/Storages/StorageXDBC.cpp +++ b/src/Storages/StorageXDBC.cpp @@ -97,16 +97,16 @@ Pipes StorageXDBC::read( return IStorageURLBase::read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); } -BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & context) { bridge_helper->startBridgeSync(); NamesAndTypesList cols; Poco::URI request_uri = uri; request_uri.setPath("/write"); - for (const String & name : getSampleBlock().getNames()) + for (const String & name : metadata_snapshot->getSampleBlock().getNames()) { - auto column_data = getColumns().getPhysical(name); + auto column_data = metadata_snapshot->getColumns().getPhysical(name); cols.emplace_back(column_data.name, column_data.type); } auto url_params = bridge_helper->getURLParams(cols.toString(), 65536); @@ -117,14 +117,17 @@ BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const StorageM request_uri.addQueryParameter("format_name", format_name); return std::make_shared( - request_uri, format_name, getSampleBlock(), context, - ConnectionTimeouts::getHTTPTimeouts(context), - chooseCompressionMethod(uri.toString(), compression_method)); + request_uri, + format_name, + metadata_snapshot->getSampleBlock(), + context, + ConnectionTimeouts::getHTTPTimeouts(context), + chooseCompressionMethod(uri.toString(), compression_method)); } -Block StorageXDBC::getHeaderBlock(const Names & column_names) const +Block StorageXDBC::getHeaderBlock(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const { - return getSampleBlockForColumns(column_names); + return metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()); } std::string StorageXDBC::getName() const diff --git a/src/Storages/StorageXDBC.h b/src/Storages/StorageXDBC.h index 4488122656df..0e227d7d4323 100644 --- a/src/Storages/StorageXDBC.h +++ b/src/Storages/StorageXDBC.h @@ -24,11 +24,12 @@ class StorageXDBC : public IStorageURLBase size_t max_block_size, unsigned num_streams) override; - StorageXDBC(const StorageID & table_id_, - const std::string & remote_database_name, - const std::string & remote_table_name, - const ColumnsDescription & columns_, - const Context & context_, BridgeHelperPtr bridge_helper_); + StorageXDBC( + const StorageID & table_id_, + const std::string & remote_database_name, + const std::string & remote_table_name, + const ColumnsDescription & columns_, + const Context & context_, BridgeHelperPtr bridge_helper_); BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; @@ -42,19 +43,21 @@ class StorageXDBC : public IStorageURLBase std::string getReadMethod() const override; - std::vector> getReadURIParams(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size) const override; + std::vector> getReadURIParams( + const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size) const override; - std::function getReadPOSTDataCallback(const Names & column_names, - const SelectQueryInfo & query_info, - const Context & context, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size) const override; + std::function getReadPOSTDataCallback( + const Names & column_names, + const SelectQueryInfo & query_info, + const Context & context, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size) const override; - Block getHeaderBlock(const Names & column_names) const override; + Block getHeaderBlock(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const override; std::string getName() const override; }; From 824d6667d97c945d72aafc87b531eba177305f9e Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 17:27:44 +0300 Subject: [PATCH 0344/1102] Seems to be working getSampleBlockWithColumns in StorageInMemoryMetadata From 815ac038924644db3326ddc5b6da104cb71cf8bc Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 17:45:52 +0300 Subject: [PATCH 0345/1102] Add PartialSortingStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 29 ++++-------- .../QueryPlan/PartialSortingStep.cpp | 45 +++++++++++++++++++ src/Processors/QueryPlan/PartialSortingStep.h | 28 ++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 83 insertions(+), 20 deletions(-) create mode 100644 src/Processors/QueryPlan/PartialSortingStep.cpp create mode 100644 src/Processors/QueryPlan/PartialSortingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 51ddc5284dc8..ab64d56a2453 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -79,6 +79,7 @@ #include #include #include +#include namespace DB @@ -1679,12 +1680,6 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderIn SortDescription output_order_descr = getSortDescription(query, *context); UInt64 limit = getLimitForSorting(query, *context); - const Settings & settings = context->getSettingsRef(); - - IBlockInputStream::LocalLimits limits; - limits.mode = IBlockInputStream::LIMITS_CURRENT; - limits.size_limits = SizeLimits(settings.max_rows_to_sort, settings.max_bytes_to_sort, settings.sort_overflow_mode); - if (input_sorting_info) { /* Case of sorting with optimization using sorting key. @@ -1697,22 +1692,16 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderIn return; } - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type != QueryPipeline::StreamType::Main) - return nullptr; - - return std::make_shared(header, output_order_descr, limit); - }); + const Settings & settings = context->getSettingsRef(); - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type == QueryPipeline::StreamType::Totals) - return nullptr; + PartialSortingStep partial_sorting( + DataStream{.header = pipeline.getHeader()}, + output_order_descr, + limit, + SizeLimits(settings.max_rows_to_sort, settings.max_bytes_to_sort, settings.sort_overflow_mode)); - auto transform = std::make_shared(header, limits); - return transform; - }); + partial_sorting.setStepDescription("Sort each block before ORDER BY"); + partial_sorting.transformPipeline(pipeline); /// Merge the sorted blocks. pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr diff --git a/src/Processors/QueryPlan/PartialSortingStep.cpp b/src/Processors/QueryPlan/PartialSortingStep.cpp new file mode 100644 index 000000000000..984d34e8e8e3 --- /dev/null +++ b/src/Processors/QueryPlan/PartialSortingStep.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +namespace DB +{ + +PartialSortingStep::PartialSortingStep( + const DataStream & input_stream, + SortDescription sort_description_, + UInt64 limit_, + SizeLimits size_limits_) + : ITransformingStep(input_stream, input_stream) + , sort_description(std::move(sort_description_)) + , limit(limit_) + , size_limits(size_limits_) +{ +} + +void PartialSortingStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, sort_description, limit); + }); + + IBlockInputStream::LocalLimits limits; + limits.mode = IBlockInputStream::LIMITS_CURRENT; + limits.size_limits = size_limits; + + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + auto transform = std::make_shared(header, limits); + return transform; + }); +} + +} diff --git a/src/Processors/QueryPlan/PartialSortingStep.h b/src/Processors/QueryPlan/PartialSortingStep.h new file mode 100644 index 000000000000..c4967e8ec301 --- /dev/null +++ b/src/Processors/QueryPlan/PartialSortingStep.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +namespace DB +{ + +class PartialSortingStep : public ITransformingStep +{ +public: + explicit PartialSortingStep( + const DataStream & input_stream, + SortDescription sort_description_, + UInt64 limit_, + SizeLimits size_limits_); + + String getName() const override { return "PartialSorting"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SortDescription sort_description; + UInt64 limit; + SizeLimits size_limits; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index cb938dd5f97b..cda984253e7e 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -142,6 +142,7 @@ SRCS( QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp + QueryPlan/PartialSortingStep.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp QueryPlan/QueryPlan.cpp From f5631939675e365692891bb2b9f4f3550e4cab42 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 18:08:01 +0300 Subject: [PATCH 0346/1102] Add MergeSortingStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 19 ++++---- src/Processors/QueryPlan/MergeSortingStep.cpp | 43 +++++++++++++++++++ src/Processors/QueryPlan/MergeSortingStep.h | 39 +++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/Processors/QueryPlan/MergeSortingStep.cpp create mode 100644 src/Processors/QueryPlan/MergeSortingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index ab64d56a2453..50e64dbd4361 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -80,6 +80,7 @@ #include #include #include +#include namespace DB @@ -1704,17 +1705,15 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderIn partial_sorting.transformPipeline(pipeline); /// Merge the sorted blocks. - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type == QueryPipeline::StreamType::Totals) - return nullptr; + MergeSortingStep merge_sorting_step( + DataStream{.header = pipeline.getHeader()}, + output_order_descr, settings.max_block_size, limit, + settings.max_bytes_before_remerge_sort / pipeline.getNumStreams(), + settings.max_bytes_before_external_sort, context->getTemporaryVolume(), + settings.min_free_disk_space_for_temporary_data); - return std::make_shared( - header, output_order_descr, settings.max_block_size, limit, - settings.max_bytes_before_remerge_sort / pipeline.getNumStreams(), - settings.max_bytes_before_external_sort, context->getTemporaryVolume(), - settings.min_free_disk_space_for_temporary_data); - }); + merge_sorting_step.setStepDescription("Merge sorted blocks before ORDER BY"); + merge_sorting_step.transformPipeline(pipeline); /// If there are several streams, we merge them into one executeMergeSorted(pipeline, output_order_descr, limit); diff --git a/src/Processors/QueryPlan/MergeSortingStep.cpp b/src/Processors/QueryPlan/MergeSortingStep.cpp new file mode 100644 index 000000000000..d2a3f01b0609 --- /dev/null +++ b/src/Processors/QueryPlan/MergeSortingStep.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +namespace DB +{ + +MergeSortingStep::MergeSortingStep( + const DataStream & input_stream, + const SortDescription & description_, + size_t max_merged_block_size_, + UInt64 limit_, + size_t max_bytes_before_remerge_, + size_t max_bytes_before_external_sort_, + VolumePtr tmp_volume_, + size_t min_free_disk_space_) + : ITransformingStep(input_stream, input_stream) + , description(description_) + , max_merged_block_size(max_merged_block_size_) + , limit(limit_) + , max_bytes_before_remerge(max_bytes_before_remerge_) + , max_bytes_before_external_sort(max_bytes_before_external_sort_), tmp_volume(tmp_volume_) + , min_free_disk_space(min_free_disk_space_) +{ +} + +void MergeSortingStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type == QueryPipeline::StreamType::Totals) + return nullptr; + + return std::make_shared( + header, description, max_merged_block_size, limit, + max_bytes_before_remerge, + max_bytes_before_external_sort, + tmp_volume, + min_free_disk_space); + }); +} + +} diff --git a/src/Processors/QueryPlan/MergeSortingStep.h b/src/Processors/QueryPlan/MergeSortingStep.h new file mode 100644 index 000000000000..3d12bda31392 --- /dev/null +++ b/src/Processors/QueryPlan/MergeSortingStep.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include +#include + +namespace DB +{ + +class MergeSortingStep : public ITransformingStep +{ +public: + explicit MergeSortingStep( + const DataStream & input_stream, + const SortDescription & description_, + size_t max_merged_block_size_, + UInt64 limit_, + size_t max_bytes_before_remerge_, + size_t max_bytes_before_external_sort_, + VolumePtr tmp_volume_, + size_t min_free_disk_space_); + + String getName() const override { return "MergeSorting"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SortDescription description; + size_t max_merged_block_size; + UInt64 limit; + + size_t max_bytes_before_remerge; + size_t max_bytes_before_external_sort; + VolumePtr tmp_volume; + size_t min_free_disk_space; +}; + +} + diff --git a/src/Processors/ya.make b/src/Processors/ya.make index cda984253e7e..d5e474947959 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -142,6 +142,7 @@ SRCS( QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp + QueryPlan/MergeSortingStep.cpp QueryPlan/PartialSortingStep.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp From a0a1445da9a9af651530aeb1b30a307323367dd5 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 18:10:40 +0300 Subject: [PATCH 0347/1102] Fix build. --- src/Processors/QueryPlan/ITransformingStep.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Processors/QueryPlan/ITransformingStep.cpp b/src/Processors/QueryPlan/ITransformingStep.cpp index 409cfa58079e..a7c756dddb27 100644 --- a/src/Processors/QueryPlan/ITransformingStep.cpp +++ b/src/Processors/QueryPlan/ITransformingStep.cpp @@ -1,4 +1,5 @@ #include +#include namespace DB { From 08e9ce866123c4cef1bae36d6e8dee374492138e Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 18:47:40 +0300 Subject: [PATCH 0348/1102] Add MergingSortedStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 29 ++++++-------- src/Interpreters/InterpreterSelectQuery.h | 4 +- .../QueryPlan/MergingSortedStep.cpp | 38 +++++++++++++++++++ src/Processors/QueryPlan/MergingSortedStep.h | 32 ++++++++++++++++ src/Processors/ya.make | 1 + 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 src/Processors/QueryPlan/MergingSortedStep.cpp create mode 100644 src/Processors/QueryPlan/MergingSortedStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 50e64dbd4361..6730fe693377 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -81,6 +81,7 @@ #include #include #include +#include namespace DB @@ -955,7 +956,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn */ if (!expressions.first_stage && !expressions.need_aggregate && !(query.group_by_with_totals && !aggregate_final)) - executeMergeSorted(pipeline); + executeMergeSorted(pipeline, "before ORDER BY"); else /// Otherwise, just sort. executeOrder(pipeline, query_info.input_order_info); } @@ -1716,36 +1717,30 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderIn merge_sorting_step.transformPipeline(pipeline); /// If there are several streams, we merge them into one - executeMergeSorted(pipeline, output_order_descr, limit); + executeMergeSorted(pipeline, output_order_descr, limit, "before ORDER BY"); } -void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const std::string & description) { auto & query = getSelectQuery(); SortDescription order_descr = getSortDescription(query, *context); UInt64 limit = getLimitForSorting(query, *context); - executeMergeSorted(pipeline, order_descr, limit); + executeMergeSorted(pipeline, order_descr, limit, description); } -void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit) +void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit, const std::string & description) { - /// If there are several streams, then we merge them into one - if (pipeline.getNumStreams() > 1) - { - const Settings & settings = context->getSettingsRef(); + const Settings & settings = context->getSettingsRef(); - auto transform = std::make_shared( - pipeline.getHeader(), - pipeline.getNumStreams(), + MergingSortedStep merging_sorted( + DataStream{.header = pipeline.getHeader()}, sort_description, settings.max_block_size, limit); - pipeline.addPipe({ std::move(transform) }); - - pipeline.enableQuotaForCurrentStreams(); - } + merging_sorted.setStepDescription("Merge sorted streams " + description); + merging_sorted.transformPipeline(pipeline); } @@ -1955,7 +1950,7 @@ void InterpreterSelectQuery::executeExtremes(QueryPipeline & pipeline) void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const SubqueriesForSets & subqueries_for_sets) { if (query_info.input_order_info) - executeMergeSorted(pipeline, query_info.input_order_info->order_key_prefix_descr, 0); + executeMergeSorted(pipeline, query_info.input_order_info->order_key_prefix_descr, 0, "before creating sets for subqueries and joins"); const Settings & settings = context->getSettingsRef(); diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index bdf888c61294..c47b0131ed06 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -123,7 +123,7 @@ class InterpreterSelectQuery : public IInterpreter void executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info); void executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info, UInt64 limit, SortDescription & output_order_descr); void executeWithFill(QueryPipeline & pipeline); - void executeMergeSorted(QueryPipeline & pipeline); + void executeMergeSorted(QueryPipeline & pipeline, const std::string & description); void executePreLimit(QueryPipeline & pipeline, bool do_not_skip_offset); void executeLimitBy(QueryPipeline & pipeline); void executeLimit(QueryPipeline & pipeline); @@ -132,7 +132,7 @@ class InterpreterSelectQuery : public IInterpreter void executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns); void executeExtremes(QueryPipeline & pipeline); void executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const std::unordered_map & subqueries_for_sets); - void executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit); + void executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit, const std::string & description); String generateFilterActions( ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns = {}) const; diff --git a/src/Processors/QueryPlan/MergingSortedStep.cpp b/src/Processors/QueryPlan/MergingSortedStep.cpp new file mode 100644 index 000000000000..e43c5a598aed --- /dev/null +++ b/src/Processors/QueryPlan/MergingSortedStep.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +namespace DB +{ + +MergingSortedStep::MergingSortedStep( + DataStream input_stream, + SortDescription sort_description_, + size_t max_block_size_, + UInt64 limit_) + : ITransformingStep(input_stream, input_stream) + , sort_description(std::move(sort_description_)) + , max_block_size(max_block_size_) + , limit(limit_) +{ +} + +void MergingSortedStep::transformPipeline(QueryPipeline & pipeline) +{ + /// If there are several streams, then we merge them into one + if (pipeline.getNumStreams() > 1) + { + + auto transform = std::make_shared( + pipeline.getHeader(), + pipeline.getNumStreams(), + sort_description, + max_block_size, limit); + + pipeline.addPipe({ std::move(transform) }); + + pipeline.enableQuotaForCurrentStreams(); + } +} + +} diff --git a/src/Processors/QueryPlan/MergingSortedStep.h b/src/Processors/QueryPlan/MergingSortedStep.h new file mode 100644 index 000000000000..fe62c4bc79ea --- /dev/null +++ b/src/Processors/QueryPlan/MergingSortedStep.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include +#include + +namespace DB +{ + +class MergingSortedStep : public ITransformingStep +{ +public: + explicit MergingSortedStep( + DataStream input_stream, + SortDescription sort_description_, + size_t max_block_size_, + UInt64 limit_ = 0); + + String getName() const override { return "MergingSorted"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + size_t num_inputs; + SortDescription sort_description; + size_t max_block_size; + UInt64 limit; +}; + +} + + diff --git a/src/Processors/ya.make b/src/Processors/ya.make index d5e474947959..b1e203b75b70 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -143,6 +143,7 @@ SRCS( QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp QueryPlan/MergeSortingStep.cpp + QueryPlan/MergingSortedStep.cpp QueryPlan/PartialSortingStep.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp From cfe87a77eae6596a9cb185be13d1562e74ee8dce Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 18:49:09 +0300 Subject: [PATCH 0349/1102] Add MergingSortedStep. --- src/Processors/QueryPlan/MergingSortedStep.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Processors/QueryPlan/MergingSortedStep.h b/src/Processors/QueryPlan/MergingSortedStep.h index fe62c4bc79ea..3b586e85a3eb 100644 --- a/src/Processors/QueryPlan/MergingSortedStep.h +++ b/src/Processors/QueryPlan/MergingSortedStep.h @@ -21,7 +21,6 @@ class MergingSortedStep : public ITransformingStep void transformPipeline(QueryPipeline & pipeline) override; private: - size_t num_inputs; SortDescription sort_description; size_t max_block_size; UInt64 limit; From 1ddeb3d149a19b21adc1c426287995dbfd4b3249 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 18:51:29 +0300 Subject: [PATCH 0350/1102] Buildable getSampleBlock in StorageInMemoryMetadata --- src/DataStreams/RemoteQueryExecutor.cpp | 3 +- src/Interpreters/InterpreterAlterQuery.cpp | 3 +- src/Interpreters/InterpreterInsertQuery.cpp | 2 +- src/Interpreters/executeQuery.cpp | 5 ++- src/Server/TCPHandler.cpp | 2 +- .../DistributedBlockOutputStream.cpp | 25 +++++++---- .../DistributedBlockOutputStream.h | 12 +++++- src/Storages/IStorage.cpp | 10 ----- src/Storages/IStorage.h | 4 +- .../MergeTree/IMergedBlockOutputStream.cpp | 5 ++- .../MergeTree/IMergedBlockOutputStream.h | 4 +- .../MergeTree/MergeTreeBlockOutputStream.cpp | 4 +- .../MergeTree/MergeTreeBlockOutputStream.h | 13 +++++- .../MergeTree/MergeTreeDataMergerMutator.cpp | 8 ++++ .../MergeTree/MergeTreeDataMergerMutator.h | 2 + .../MergeTree/MergeTreeDataWriter.cpp | 4 +- src/Storages/MergeTree/MergeTreeDataWriter.h | 2 +- src/Storages/MergeTree/MergeTreeReadPool.cpp | 2 +- .../MergeTreeReverseSelectProcessor.cpp | 2 +- .../MergeTree/MergeTreeSelectProcessor.cpp | 2 +- .../MergeTree/MergedBlockOutputStream.cpp | 11 ++++- .../MergeTree/MergedBlockOutputStream.h | 4 +- .../MergedColumnOnlyOutputStream.cpp | 4 +- .../MergeTree/MergedColumnOnlyOutputStream.h | 1 + .../ReplicatedMergeTreeBlockOutputStream.cpp | 20 ++++++--- .../ReplicatedMergeTreeBlockOutputStream.h | 9 +++- src/Storages/StorageBuffer.cpp | 24 +++++++---- src/Storages/StorageDistributed.cpp | 2 +- src/Storages/StorageFile.cpp | 32 +++++++++------ src/Storages/StorageHDFS.cpp | 8 ++-- src/Storages/StorageInput.cpp | 4 +- src/Storages/StorageJoin.cpp | 12 ++++-- src/Storages/StorageLog.cpp | 17 ++++---- src/Storages/StorageMaterializedView.cpp | 5 ++- src/Storages/StorageMaterializedView.h | 2 +- src/Storages/StorageMerge.cpp | 36 +++++++++++----- src/Storages/StorageMerge.h | 18 +++++--- src/Storages/StorageMergeTree.cpp | 12 +++--- src/Storages/StorageMergeTree.h | 6 ++- src/Storages/StorageMySQL.cpp | 25 +++++++---- src/Storages/StorageReplicatedMergeTree.cpp | 17 ++++---- src/Storages/StorageReplicatedMergeTree.h | 8 +++- src/Storages/StorageSet.cpp | 41 ++++++++++++------- src/Storages/StorageStripeLog.cpp | 27 ++++++------ src/Storages/StorageTinyLog.cpp | 15 +++---- src/Storages/StorageURL.cpp | 4 +- src/Storages/System/IStorageSystemOneBlock.h | 4 +- src/Storages/System/StorageSystemColumns.cpp | 4 +- .../System/StorageSystemDetachedParts.cpp | 4 +- src/Storages/System/StorageSystemDisks.cpp | 4 +- .../System/StorageSystemPartsBase.cpp | 6 +-- src/Storages/System/StorageSystemReplicas.cpp | 8 ++-- .../System/StorageSystemStoragePolicies.cpp | 4 +- src/Storages/System/StorageSystemTables.cpp | 4 +- 54 files changed, 328 insertions(+), 188 deletions(-) diff --git a/src/DataStreams/RemoteQueryExecutor.cpp b/src/DataStreams/RemoteQueryExecutor.cpp index 071cb6e9aba4..45ddd7c08932 100644 --- a/src/DataStreams/RemoteQueryExecutor.cpp +++ b/src/DataStreams/RemoteQueryExecutor.cpp @@ -333,7 +333,8 @@ void RemoteQueryExecutor::sendExternalTables() data->table_name = table.first; if (pipes.empty()) - data->pipe = std::make_unique(std::make_shared(cur->getSampleBlock(), Chunk())); + data->pipe = std::make_unique( + std::make_shared(metadata_snapshot->getSampleBlock(), Chunk())); else if (pipes.size() == 1) data->pipe = std::make_unique(std::move(pipes.front())); else diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index bd20d78279dd..869c3ae98d38 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -43,6 +43,7 @@ BlockIO InterpreterAlterQuery::execute() context.checkAccess(getRequiredAccess()); auto table_id = context.resolveStorageID(alter, Context::ResolveOrdinary); StoragePtr table = DatabaseCatalog::instance().getTable(table_id, context); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); /// Add default database to table identifiers that we can encounter in e.g. default expressions, /// mutation expression, etc. @@ -91,7 +92,7 @@ BlockIO InterpreterAlterQuery::execute() if (!partition_commands.empty()) { - table->alterPartition(query_ptr, partition_commands, context); + table->alterPartition(query_ptr, metadata_snapshot, partition_commands, context); } if (!live_view_commands.empty()) diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index f61ef0e73812..443e2714ec7a 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -88,7 +88,7 @@ Block InterpreterInsertQuery::getSampleBlock( return table_sample_non_materialized; } - Block table_sample = table->getSampleBlock(); + Block table_sample = metadata_snapshot->getSampleBlock(); /// Form the block based on the column names from the query Block res; for (const auto & identifier : query.columns->children) diff --git a/src/Interpreters/executeQuery.cpp b/src/Interpreters/executeQuery.cpp index e1e2108c0fce..2b8ebf12a200 100644 --- a/src/Interpreters/executeQuery.cpp +++ b/src/Interpreters/executeQuery.cpp @@ -326,8 +326,9 @@ static std::tuple executeQueryImpl( { StoragePtr storage = context.executeTableFunction(input_function); auto & input_storage = dynamic_cast(*storage); - BlockInputStreamPtr input_stream = std::make_shared(ast, istr, - input_storage.getSampleBlock(), context, input_function); + auto input_metadata_snapshot = input_storage.getInMemoryMetadataPtr(); + BlockInputStreamPtr input_stream = std::make_shared( + ast, istr, input_metadata_snapshot->getSampleBlock(), context, input_function); input_storage.setInputStream(input_stream); } } diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index a01cc4fa0aae..009f7ad80f01 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -223,7 +223,7 @@ void TCPHandler::runImpl() } /// Send block to the client - input storage structure. - state.input_header = input_storage->getSampleBlock(); + state.input_header = input_storage->getInMemoryMetadataPtr()->getSampleBlock(); sendData(state.input_header); }); diff --git a/src/Storages/Distributed/DistributedBlockOutputStream.cpp b/src/Storages/Distributed/DistributedBlockOutputStream.cpp index 4e28923ebfc1..bf9efef1ba6d 100644 --- a/src/Storages/Distributed/DistributedBlockOutputStream.cpp +++ b/src/Storages/Distributed/DistributedBlockOutputStream.cpp @@ -83,18 +83,29 @@ static void writeBlockConvert(const BlockOutputStreamPtr & out, const Block & bl DistributedBlockOutputStream::DistributedBlockOutputStream( - const Context & context_, StorageDistributed & storage_, const ASTPtr & query_ast_, const ClusterPtr & cluster_, - bool insert_sync_, UInt64 insert_timeout_) - : context(context_), storage(storage_), query_ast(query_ast_), query_string(queryToString(query_ast_)), - cluster(cluster_), insert_sync(insert_sync_), - insert_timeout(insert_timeout_), log(&Poco::Logger::get("DistributedBlockOutputStream")) + const Context & context_, + StorageDistributed & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const ASTPtr & query_ast_, + const ClusterPtr & cluster_, + bool insert_sync_, + UInt64 insert_timeout_) + : context(context_) + , storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , query_ast(query_ast_) + , query_string(queryToString(query_ast_)) + , cluster(cluster_) + , insert_sync(insert_sync_) + , insert_timeout(insert_timeout_) + , log(&Poco::Logger::get("DistributedBlockOutputStream")) { } Block DistributedBlockOutputStream::getHeader() const { - return storage.getSampleBlock(); + return metadata_snapshot->getSampleBlock(); } @@ -109,7 +120,7 @@ void DistributedBlockOutputStream::write(const Block & block) /* They are added by the AddingDefaultBlockOutputStream, and we will get * different number of columns eventually */ - for (const auto & col : storage.getColumns().getMaterialized()) + for (const auto & col : metadata_snapshot->getColumns().getMaterialized()) { if (ordinary_block.has(col.name)) { diff --git a/src/Storages/Distributed/DistributedBlockOutputStream.h b/src/Storages/Distributed/DistributedBlockOutputStream.h index 17db955431c9..53d71ffc4244 100644 --- a/src/Storages/Distributed/DistributedBlockOutputStream.h +++ b/src/Storages/Distributed/DistributedBlockOutputStream.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -36,8 +37,14 @@ class StorageDistributed; class DistributedBlockOutputStream : public IBlockOutputStream { public: - DistributedBlockOutputStream(const Context & context_, StorageDistributed & storage_, const ASTPtr & query_ast_, - const ClusterPtr & cluster_, bool insert_sync_, UInt64 insert_timeout_); + DistributedBlockOutputStream( + const Context & context_, + StorageDistributed & storage_, + const StorageMetadataPtr & metadata_snapshot_, + const ASTPtr & query_ast_, + const ClusterPtr & cluster_, + bool insert_sync_, + UInt64 insert_timeout_); Block getHeader() const override; void write(const Block & block) override; @@ -79,6 +86,7 @@ class DistributedBlockOutputStream : public IBlockOutputStream private: const Context & context; StorageDistributed & storage; + StorageMetadataPtr metadata_snapshot; ASTPtr query_ast; String query_string; ClusterPtr cluster; diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index d090dc9e51d1..43e9a5dd040e 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -52,16 +52,6 @@ const ConstraintsDescription & IStorage::getConstraints() const return metadata->constraints; } -Block IStorage::getSampleBlock() const -{ - Block res; - - for (const auto & column : getColumns().getAllPhysical()) - res.insert({column.type->createColumn(), column.type, column.name}); - - return res; -} - namespace { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index a4173c1c9fad..df1e1685a2ed 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -158,8 +158,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } - Block getSampleBlock() const; /// ordinary + materialized. - /// Verify that all the requested names are in the table and are set correctly: /// list of names is not empty and the names do not repeat. void check(const Names & column_names, bool include_virtuals = false) const; @@ -361,7 +359,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /** ALTER tables with regard to its partitions. * Should handle locks for each command on its own. */ - virtual void alterPartition(const ASTPtr & /* query */, const PartitionCommands & /* commands */, const Context & /* context */) + virtual void alterPartition(const ASTPtr & /* query */, const StorageMetadataPtr & /* metadata_snapshot */, const PartitionCommands & /* commands */, const Context & /* context */) { throw Exception("Partition operations are not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } diff --git a/src/Storages/MergeTree/IMergedBlockOutputStream.cpp b/src/Storages/MergeTree/IMergedBlockOutputStream.cpp index 329a8ee45080..cfb4d8ba4bac 100644 --- a/src/Storages/MergeTree/IMergedBlockOutputStream.cpp +++ b/src/Storages/MergeTree/IMergedBlockOutputStream.cpp @@ -5,10 +5,11 @@ namespace DB { - IMergedBlockOutputStream::IMergedBlockOutputStream( - const MergeTreeDataPartPtr & data_part) + const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_) : storage(data_part->storage) + , metadata_snapshot(metadata_snapshot_) , volume(data_part->volume) , part_path(data_part->getFullRelativePath()) { diff --git a/src/Storages/MergeTree/IMergedBlockOutputStream.h b/src/Storages/MergeTree/IMergedBlockOutputStream.h index 6a06d4b0c75d..7ec9f85ff281 100644 --- a/src/Storages/MergeTree/IMergedBlockOutputStream.h +++ b/src/Storages/MergeTree/IMergedBlockOutputStream.h @@ -13,7 +13,8 @@ class IMergedBlockOutputStream : public IBlockOutputStream { public: IMergedBlockOutputStream( - const MergeTreeDataPartPtr & data_part); + const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_); using WrittenOffsetColumns = std::set; @@ -36,6 +37,7 @@ class IMergedBlockOutputStream : public IBlockOutputStream protected: const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; VolumePtr volume; String part_path; diff --git a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp index b6376dd3779e..1ea6b049bf68 100644 --- a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp @@ -8,7 +8,7 @@ namespace DB Block MergeTreeBlockOutputStream::getHeader() const { - return storage.getSampleBlock(); + return metadata_snapshot->getSampleBlock(); } @@ -21,7 +21,7 @@ void MergeTreeBlockOutputStream::write(const Block & block) { Stopwatch watch; - MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block); + MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, metadata_snapshot); storage.renameTempPartAndAdd(part, &storage.increment); PartLog::addNewPart(storage.global_context, part, watch.elapsed()); diff --git a/src/Storages/MergeTree/MergeTreeBlockOutputStream.h b/src/Storages/MergeTree/MergeTreeBlockOutputStream.h index 8f957d631d3f..71e126b07ef6 100644 --- a/src/Storages/MergeTree/MergeTreeBlockOutputStream.h +++ b/src/Storages/MergeTree/MergeTreeBlockOutputStream.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace DB @@ -13,14 +14,22 @@ class StorageMergeTree; class MergeTreeBlockOutputStream : public IBlockOutputStream { public: - MergeTreeBlockOutputStream(StorageMergeTree & storage_, size_t max_parts_per_block_) - : storage(storage_), max_parts_per_block(max_parts_per_block_) {} + MergeTreeBlockOutputStream( + StorageMergeTree & storage_, + const StorageMetadataPtr metadata_snapshot_, + size_t max_parts_per_block_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , max_parts_per_block(max_parts_per_block_) + { + } Block getHeader() const override; void write(const Block & block) override; private: StorageMergeTree & storage; + StorageMetadataPtr metadata_snapshot; size_t max_parts_per_block; }; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 829f7cac5286..39e6cfdc2753 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -808,6 +808,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor const auto & index_factory = MergeTreeIndexFactory::instance(); MergedBlockOutputStream to{ new_data_part, + metadata_snapshot, merging_columns, index_factory.getMany(data.getSecondaryIndices()), compression_codec, @@ -912,6 +913,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor MergedColumnOnlyOutputStream column_to( new_data_part, + metadata_snapshot, column_gathered_stream.getHeader(), compression_codec, /// we don't need to recalc indices here @@ -1085,6 +1087,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor auto part_indices = getIndicesForNewDataPart(data.getSecondaryIndices(), for_file_renames); mutateAllPartColumns( new_data_part, + metadata_snapshot, part_indices, in, time_of_mutation, @@ -1137,6 +1140,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor { mutateSomePartColumns( source_part, + metadata_snapshot, indices_to_recalc, updated_header, new_data_part, @@ -1582,6 +1586,7 @@ bool MergeTreeDataMergerMutator::shouldExecuteTTL(const StorageMetadataPtr & met void MergeTreeDataMergerMutator::mutateAllPartColumns( MergeTreeData::MutableDataPartPtr new_data_part, + const StorageMetadataPtr & metadata_snapshot, const MergeTreeIndices & skip_indices, BlockInputStreamPtr mutating_stream, time_t time_of_mutation, @@ -1603,6 +1608,7 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( MergedBlockOutputStream out{ new_data_part, + metadata_snapshot, new_data_part->getColumns(), skip_indices, compression_codec}; @@ -1629,6 +1635,7 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( void MergeTreeDataMergerMutator::mutateSomePartColumns( const MergeTreeDataPartPtr & source_part, + const StorageMetadataPtr & metadata_snapshot, const std::set & indices_to_recalc, const Block & mutation_header, MergeTreeData::MutableDataPartPtr new_data_part, @@ -1647,6 +1654,7 @@ void MergeTreeDataMergerMutator::mutateSomePartColumns( IMergedBlockOutputStream::WrittenOffsetColumns unused_written_offsets; MergedColumnOnlyOutputStream out( new_data_part, + metadata_snapshot, mutation_header, compression_codec, std::vector(indices_to_recalc.begin(), indices_to_recalc.end()), diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h index 185961972a8a..3625c9bbe265 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h @@ -182,6 +182,7 @@ class MergeTreeDataMergerMutator /// Override all columns of new part using mutating_stream void mutateAllPartColumns( MergeTreeData::MutableDataPartPtr new_data_part, + const StorageMetadataPtr & metadata_snapshot, const MergeTreeIndices & skip_indices, BlockInputStreamPtr mutating_stream, time_t time_of_mutation, @@ -192,6 +193,7 @@ class MergeTreeDataMergerMutator /// Mutate some columns of source part with mutation_stream void mutateSomePartColumns( const MergeTreeDataPartPtr & source_part, + const StorageMetadataPtr & metadata_snapshot, const std::set & indices_to_recalc, const Block & mutation_header, MergeTreeData::MutableDataPartPtr new_data_part, diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index f3da98f0ba35..71501a0e19ae 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -192,7 +192,7 @@ BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block return result; } -MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPartition & block_with_partition) +MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPartition & block_with_partition, const StorageMetadataPtr & metadata_snapshot) { Block & block = block_with_partition.block; @@ -302,7 +302,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa auto compression_codec = data.global_context.chooseCompressionCodec(0, 0); const auto & index_factory = MergeTreeIndexFactory::instance(); - MergedBlockOutputStream out(new_data_part, columns, index_factory.getMany(data.getSecondaryIndices()), compression_codec); + MergedBlockOutputStream out(new_data_part, metadata_snapshot, columns, index_factory.getMany(data.getSecondaryIndices()), compression_codec); out.writePrefix(); out.writeWithPermutation(block, perm_ptr); diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.h b/src/Storages/MergeTree/MergeTreeDataWriter.h index ffaa227641ef..dabdcbd21488 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.h +++ b/src/Storages/MergeTree/MergeTreeDataWriter.h @@ -45,7 +45,7 @@ class MergeTreeDataWriter /** All rows must correspond to same partition. * Returns part with unique name starting with 'tmp_', yet not added to MergeTreeData. */ - MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithPartition & block); + MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithPartition & block, const StorageMetadataPtr & metadata_snapshot); private: MergeTreeData & data; diff --git a/src/Storages/MergeTree/MergeTreeReadPool.cpp b/src/Storages/MergeTree/MergeTreeReadPool.cpp index eb0b51235ad3..8c73dc39dfbe 100644 --- a/src/Storages/MergeTree/MergeTreeReadPool.cpp +++ b/src/Storages/MergeTree/MergeTreeReadPool.cpp @@ -197,7 +197,7 @@ std::vector MergeTreeReadPool::fillPerPartInfo( RangesInDataParts & parts, const bool check_columns) { std::vector per_part_sum_marks; - Block sample_block = data.getSampleBlock(); + Block sample_block = metadata_snapshot->getSampleBlock(); for (const auto i : ext::range(0, parts.size())) { diff --git a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp index 813666149886..c47dd7fb669a 100644 --- a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp @@ -115,7 +115,7 @@ try auto size_predictor = (preferred_block_size_bytes == 0) ? nullptr - : std::make_unique(data_part, ordered_names, data_part->storage.getSampleBlock()); + : std::make_unique(data_part, ordered_names, metadata_snapshot->getSampleBlock()); task = std::make_unique( data_part, mark_ranges_for_task, part_index_in_query, ordered_names, column_name_set, diff --git a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp index e32fa70cb973..84c0f44c109e 100644 --- a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp @@ -72,7 +72,7 @@ try auto size_predictor = (preferred_block_size_bytes == 0) ? nullptr - : std::make_unique(data_part, ordered_names, data_part->storage.getSampleBlock()); + : std::make_unique(data_part, ordered_names, metadata_snapshot->getSampleBlock()); /// will be used to distinguish between PREWHERE and WHERE columns when applying filter const auto & column_names = task_columns.columns.getNames(); diff --git a/src/Storages/MergeTree/MergedBlockOutputStream.cpp b/src/Storages/MergeTree/MergedBlockOutputStream.cpp index bce50918ac05..c768678c4540 100644 --- a/src/Storages/MergeTree/MergedBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergedBlockOutputStream.cpp @@ -15,12 +15,18 @@ namespace ErrorCodes MergedBlockOutputStream::MergedBlockOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const NamesAndTypesList & columns_list_, const MergeTreeIndices & skip_indices, CompressionCodecPtr default_codec, bool blocks_are_granules_size) : MergedBlockOutputStream( - data_part, columns_list_, skip_indices, default_codec, {}, + data_part, + metadata_snapshot_, + columns_list_, + skip_indices, + default_codec, + {}, data_part->storage.global_context.getSettings().min_bytes_to_use_direct_io, blocks_are_granules_size) { @@ -28,13 +34,14 @@ MergedBlockOutputStream::MergedBlockOutputStream( MergedBlockOutputStream::MergedBlockOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const NamesAndTypesList & columns_list_, const MergeTreeIndices & skip_indices, CompressionCodecPtr default_codec, const MergeTreeData::DataPart::ColumnToSize & merged_column_to_size, size_t aio_threshold, bool blocks_are_granules_size) - : IMergedBlockOutputStream(data_part) + : IMergedBlockOutputStream(data_part, metadata_snapshot_) , columns_list(columns_list_) { MergeTreeWriterSettings writer_settings(data_part->storage.global_context.getSettings(), diff --git a/src/Storages/MergeTree/MergedBlockOutputStream.h b/src/Storages/MergeTree/MergedBlockOutputStream.h index 5a92977640ed..1a8bf9da822e 100644 --- a/src/Storages/MergeTree/MergedBlockOutputStream.h +++ b/src/Storages/MergeTree/MergedBlockOutputStream.h @@ -15,6 +15,7 @@ class MergedBlockOutputStream final : public IMergedBlockOutputStream public: MergedBlockOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const NamesAndTypesList & columns_list_, const MergeTreeIndices & skip_indices, CompressionCodecPtr default_codec, @@ -22,6 +23,7 @@ class MergedBlockOutputStream final : public IMergedBlockOutputStream MergedBlockOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const NamesAndTypesList & columns_list_, const MergeTreeIndices & skip_indices, CompressionCodecPtr default_codec, @@ -29,7 +31,7 @@ class MergedBlockOutputStream final : public IMergedBlockOutputStream size_t aio_threshold, bool blocks_are_granules_size = false); - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } /// If the data is pre-sorted. void write(const Block & block) override; diff --git a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp index b5eefbe3f0c7..b74a8243437c 100644 --- a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp +++ b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp @@ -10,13 +10,15 @@ namespace ErrorCodes MergedColumnOnlyOutputStream::MergedColumnOnlyOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const Block & header_, CompressionCodecPtr default_codec, const std::vector & indices_to_recalc, WrittenOffsetColumns * offset_columns_, const MergeTreeIndexGranularity & index_granularity, const MergeTreeIndexGranularityInfo * index_granularity_info) - : IMergedBlockOutputStream(data_part), header(header_) + : IMergedBlockOutputStream(data_part, metadata_snapshot_) + , header(header_) { const auto & global_settings = data_part->storage.global_context.getSettings(); MergeTreeWriterSettings writer_settings( diff --git a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.h b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.h index 2c5024bbcfe5..902138ced9d6 100644 --- a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.h +++ b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.h @@ -15,6 +15,7 @@ class MergedColumnOnlyOutputStream final : public IMergedBlockOutputStream /// if you want to serialize elements of Nested data structure in different instances of MergedColumnOnlyOutputStream. MergedColumnOnlyOutputStream( const MergeTreeDataPartPtr & data_part, + const StorageMetadataPtr & metadata_snapshot_, const Block & header_, CompressionCodecPtr default_codec_, const std::vector & indices_to_recalc_, diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp index 1bbc56d940dc..8319b0e018dc 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp @@ -31,9 +31,19 @@ namespace ErrorCodes ReplicatedMergeTreeBlockOutputStream::ReplicatedMergeTreeBlockOutputStream( - StorageReplicatedMergeTree & storage_, size_t quorum_, size_t quorum_timeout_ms_, size_t max_parts_per_block_, bool deduplicate_) - : storage(storage_), quorum(quorum_), quorum_timeout_ms(quorum_timeout_ms_), max_parts_per_block(max_parts_per_block_), deduplicate(deduplicate_), - log(&Poco::Logger::get(storage.getLogName() + " (Replicated OutputStream)")) + StorageReplicatedMergeTree & storage_, + const StorageMetadataPtr & metadata_snapshot_, + size_t quorum_, + size_t quorum_timeout_ms_, + size_t max_parts_per_block_, + bool deduplicate_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , quorum(quorum_) + , quorum_timeout_ms(quorum_timeout_ms_) + , max_parts_per_block(max_parts_per_block_) + , deduplicate(deduplicate_) + , log(&Poco::Logger::get(storage.getLogName() + " (Replicated OutputStream)")) { /// The quorum value `1` has the same meaning as if it is disabled. if (quorum == 1) @@ -43,7 +53,7 @@ ReplicatedMergeTreeBlockOutputStream::ReplicatedMergeTreeBlockOutputStream( Block ReplicatedMergeTreeBlockOutputStream::getHeader() const { - return storage.getSampleBlock(); + return metadata_snapshot->getSampleBlock(); } @@ -128,7 +138,7 @@ void ReplicatedMergeTreeBlockOutputStream::write(const Block & block) /// Write part to the filesystem under temporary name. Calculate a checksum. - MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block); + MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, metadata_snapshot); String block_id; diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index b8650c25c7de..ac169d248c2b 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -22,8 +22,12 @@ class StorageReplicatedMergeTree; class ReplicatedMergeTreeBlockOutputStream : public IBlockOutputStream { public: - ReplicatedMergeTreeBlockOutputStream(StorageReplicatedMergeTree & storage_, - size_t quorum_, size_t quorum_timeout_ms_, size_t max_parts_per_block_, + ReplicatedMergeTreeBlockOutputStream( + StorageReplicatedMergeTree & storage_, + const StorageMetadataPtr & metadata_snapshot_, + size_t quorum_, + size_t quorum_timeout_ms_, + size_t max_parts_per_block_, bool deduplicate_); Block getHeader() const override; @@ -55,6 +59,7 @@ class ReplicatedMergeTreeBlockOutputStream : public IBlockOutputStream void commitPart(zkutil::ZooKeeperPtr & zookeeper, MergeTreeData::MutableDataPartPtr & part, const String & block_id); StorageReplicatedMergeTree & storage; + StorageMetadataPtr metadata_snapshot; size_t quorum; size_t quorum_timeout_ms; size_t max_parts_per_block; diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 42eab838f32b..b08e4e93bedb 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -167,10 +167,10 @@ Pipes StorageBuffer::read( auto destination_lock = destination->lockStructureForShare( false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [this, destination](const String& column_name) + const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [metadata_snapshot, destination](const String& column_name) { const auto & dest_columns = destination->getColumns(); - const auto & our_columns = getColumns(); + const auto & our_columns = metadata_snapshot->getColumns(); return dest_columns.hasPhysical(column_name) && dest_columns.get(column_name).type->equals(*our_columns.get(column_name).type); }); @@ -188,7 +188,7 @@ Pipes StorageBuffer::read( else { /// There is a struct mismatch and we need to convert read blocks from the destination table. - const Block header = getSampleBlock(); + const Block header = metadata_snapshot->getSampleBlock(); Names columns_intersection = column_names; Block header_after_adding_defaults = header; const auto & dest_columns = destination->getColumns(); @@ -326,9 +326,14 @@ static void appendBlock(const Block & from, Block & to) class BufferBlockOutputStream : public IBlockOutputStream { public: - explicit BufferBlockOutputStream(StorageBuffer & storage_) : storage(storage_) {} + explicit BufferBlockOutputStream( + StorageBuffer & storage_, + const StorageMetadataPtr & metadata_snapshot_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + {} - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override { @@ -404,6 +409,7 @@ class BufferBlockOutputStream : public IBlockOutputStream } private: StorageBuffer & storage; + StorageMetadataPtr metadata_snapshot; void insertIntoBuffer(const Block & block, StorageBuffer::Buffer & buffer) { @@ -434,9 +440,9 @@ class BufferBlockOutputStream : public IBlockOutputStream }; -BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { - return std::make_shared(*this); + return std::make_shared(*this, metadata_snapshot); } @@ -654,8 +660,8 @@ void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr tabl /** We will insert columns that are the intersection set of columns of the buffer table and the subordinate table. * This will support some of the cases (but not all) when the table structure does not match. */ - Block structure_of_destination_table - = allow_materialized ? table->getSampleBlock() : destination_metadata_snapshot->getSampleBlockNonMaterialized(); + Block structure_of_destination_table = allow_materialized ? destination_metadata_snapshot->getSampleBlock() + : destination_metadata_snapshot->getSampleBlockNonMaterialized(); Block block_to_write; for (size_t i : ext::range(0, structure_of_destination_table.columns())) { diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 66066ec3c186..6868f468f2e4 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -536,7 +536,7 @@ BlockOutputStreamPtr StorageDistributed::write(const ASTPtr &, const StorageMeta /// DistributedBlockOutputStream will not own cluster, but will own ConnectionPools of the cluster return std::make_shared( - context, *this, createInsertToRemoteTableQuery(remote_database, remote_table, metadata_snapshot->getSampleBlockNonMaterialized()), cluster, + context, *this, metadata_snapshot, createInsertToRemoteTableQuery(remote_database, remote_table, metadata_snapshot->getSampleBlockNonMaterialized()), cluster, insert_sync, timeout); } diff --git a/src/Storages/StorageFile.cpp b/src/Storages/StorageFile.cpp index 07df2b4ec8a5..65f36a481709 100644 --- a/src/Storages/StorageFile.cpp +++ b/src/Storages/StorageFile.cpp @@ -214,9 +214,9 @@ class StorageFileSource : public SourceWithProgress using FilesInfoPtr = std::shared_ptr; - static Block getHeader(StorageFile & storage, bool need_path_column, bool need_file_column) + static Block getHeader(const StorageMetadataPtr & metadata_snapshot, bool need_path_column, bool need_file_column) { - auto header = storage.getSampleBlock(); + auto header = metadata_snapshot->getSampleBlock(); /// Note: AddingDefaultsBlockInputStream doesn't change header. @@ -230,12 +230,14 @@ class StorageFileSource : public SourceWithProgress StorageFileSource( std::shared_ptr storage_, + const StorageMetadataPtr & metadata_snapshot_, const Context & context_, UInt64 max_block_size_, FilesInfoPtr files_info_, ColumnDefaults column_defaults_) - : SourceWithProgress(getHeader(*storage_, files_info_->need_path_column, files_info_->need_file_column)) + : SourceWithProgress(getHeader(metadata_snapshot_, files_info_->need_path_column, files_info_->need_file_column)) , storage(std::move(storage_)) + , metadata_snapshot(metadata_snapshot_) , files_info(std::move(files_info_)) , column_defaults(std::move(column_defaults_)) , context(context_) @@ -310,7 +312,7 @@ class StorageFileSource : public SourceWithProgress read_buf = wrapReadBufferWithCompressionMethod(std::move(nested_buffer), method); reader = FormatFactory::instance().getInput( - storage->format_name, *read_buf, storage->getSampleBlock(), context, max_block_size); + storage->format_name, *read_buf, metadata_snapshot->getSampleBlock(), context, max_block_size); if (!column_defaults.empty()) reader = std::make_shared(reader, column_defaults, context); @@ -357,6 +359,7 @@ class StorageFileSource : public SourceWithProgress private: std::shared_ptr storage; + StorageMetadataPtr metadata_snapshot; FilesInfoPtr files_info; String current_path; Block sample_block; @@ -377,7 +380,7 @@ class StorageFileSource : public SourceWithProgress Pipes StorageFile::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -414,7 +417,7 @@ Pipes StorageFile::read( for (size_t i = 0; i < num_streams; ++i) pipes.emplace_back(std::make_shared( - this_ptr, context, max_block_size, files_info, getColumns().getDefaults())); + this_ptr, metadata_snapshot, context, max_block_size, files_info, getColumns().getDefaults())); return pipes; } @@ -423,10 +426,14 @@ Pipes StorageFile::read( class StorageFileBlockOutputStream : public IBlockOutputStream { public: - explicit StorageFileBlockOutputStream(StorageFile & storage_, + explicit StorageFileBlockOutputStream( + StorageFile & storage_, + const StorageMetadataPtr & metadata_snapshot_, const CompressionMethod compression_method, const Context & context) - : storage(storage_), lock(storage.rwlock) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , lock(storage.rwlock) { if (storage.use_table_fd) { @@ -446,10 +453,10 @@ class StorageFileBlockOutputStream : public IBlockOutputStream compression_method, 3); } - writer = FormatFactory::instance().getOutput(storage.format_name, *write_buf, storage.getSampleBlock(), context); + writer = FormatFactory::instance().getOutput(storage.format_name, *write_buf, metadata_snapshot->getSampleBlock(), context); } - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override { @@ -473,6 +480,7 @@ class StorageFileBlockOutputStream : public IBlockOutputStream private: StorageFile & storage; + StorageMetadataPtr metadata_snapshot; std::unique_lock lock; std::unique_ptr write_buf; BlockOutputStreamPtr writer; @@ -480,13 +488,13 @@ class StorageFileBlockOutputStream : public IBlockOutputStream BlockOutputStreamPtr StorageFile::write( const ASTPtr & /*query*/, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const Context & context) { if (format_name == "Distributed") throw Exception("Method write is not implemented for Distributed format", ErrorCodes::NOT_IMPLEMENTED); - return std::make_shared(*this, + return std::make_shared(*this, metadata_snapshot, chooseCompressionMethod(paths[0], compression_method), context); } diff --git a/src/Storages/StorageHDFS.cpp b/src/Storages/StorageHDFS.cpp index 77afc4c47c83..ee5a426cedc7 100644 --- a/src/Storages/StorageHDFS.cpp +++ b/src/Storages/StorageHDFS.cpp @@ -264,7 +264,7 @@ Strings LSWithRegexpMatching(const String & path_for_ls, const HDFSFSPtr & fs, c Pipes StorageHDFS::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context_, QueryProcessingStage::Enum /*processed_stage*/, @@ -296,16 +296,16 @@ Pipes StorageHDFS::read( for (size_t i = 0; i < num_streams; ++i) pipes.emplace_back(std::make_shared( - sources_info, uri_without_path, format_name, compression_method, getSampleBlock(), context_, max_block_size)); + sources_info, uri_without_path, format_name, compression_method, metadata_snapshot->getSampleBlock(), context_, max_block_size)); return pipes; } -BlockOutputStreamPtr StorageHDFS::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageHDFS::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { return std::make_shared(uri, format_name, - getSampleBlock(), + metadata_snapshot->getSampleBlock(), context, chooseCompressionMethod(uri, compression_method)); } diff --git a/src/Storages/StorageInput.cpp b/src/Storages/StorageInput.cpp index 4117a6b3a370..4430fb11186d 100644 --- a/src/Storages/StorageInput.cpp +++ b/src/Storages/StorageInput.cpp @@ -60,7 +60,7 @@ void StorageInput::setInputStream(BlockInputStreamPtr input_stream_) Pipes StorageInput::read( const Names & /*column_names*/, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -74,7 +74,7 @@ Pipes StorageInput::read( { /// Send structure to the client. query_context.initializeInput(shared_from_this()); - pipes.emplace_back(std::make_shared(query_context, getSampleBlock())); + pipes.emplace_back(std::make_shared(query_context, metadata_snapshot->getSampleBlock())); return pipes; } diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 7d481395ef47..68b974c0dde7 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -53,29 +53,33 @@ StorageJoin::StorageJoin( , strictness(strictness_) , overwrite(overwrite_) { + auto metadata_snapshot = getInMemoryMetadataPtr(); for (const auto & key : key_names) - if (!getColumns().hasPhysical(key)) + if (!metadata_snapshot->getColumns().hasPhysical(key)) throw Exception{"Key column (" + key + ") does not exist in table declaration.", ErrorCodes::NO_SUCH_COLUMN_IN_TABLE}; table_join = std::make_shared(limits, use_nulls, kind, strictness, key_names); - join = std::make_shared(table_join, getSampleBlock().sortColumns(), overwrite); + join = std::make_shared(table_join, metadata_snapshot->getSampleBlock().sortColumns(), overwrite); restore(); } void StorageJoin::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) { + /// TODO(alesap) FIXME + auto metadata_snapshot = getInMemoryMetadataPtr(); Poco::File(path).remove(true); Poco::File(path).createDirectories(); Poco::File(path + "tmp/").createDirectories(); increment = 0; - join = std::make_shared(table_join, getSampleBlock().sortColumns(), overwrite); + join = std::make_shared(table_join, metadata_snapshot->getSampleBlock().sortColumns(), overwrite); } HashJoinPtr StorageJoin::getJoin(std::shared_ptr analyzed_join) const { + auto metadata_snapshot = getInMemoryMetadataPtr(); if (!analyzed_join->sameStrictnessAndKind(strictness, kind)) throw Exception("Table " + getStorageID().getNameForLogs() + " has incompatible type of JOIN.", ErrorCodes::INCOMPATIBLE_TYPE_OF_JOIN); @@ -89,7 +93,7 @@ HashJoinPtr StorageJoin::getJoin(std::shared_ptr analyzed_join) const /// Some HACK to remove wrong names qualifiers: table.column -> column. analyzed_join->setRightKeys(key_names); - HashJoinPtr join_clone = std::make_shared(analyzed_join, getSampleBlock().sortColumns()); + HashJoinPtr join_clone = std::make_shared(analyzed_join, metadata_snapshot->getSampleBlock().sortColumns()); join_clone->reuseJoinedData(*join); return join_clone; } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index a09a99b30e12..79cc3e5bf68a 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -114,10 +114,12 @@ class LogSource final : public SourceWithProgress class LogBlockOutputStream final : public IBlockOutputStream { public: - explicit LogBlockOutputStream(StorageLog & storage_) - : storage(storage_), - lock(storage.rwlock), - marks_stream(storage.disk->writeFile(storage.marks_file_path, 4096, WriteMode::Rewrite)) + explicit LogBlockOutputStream(StorageLog & storage_, const StorageMetadataPtr & metadata_snapshot_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , lock(storage.rwlock) + , marks_stream( + storage.disk->writeFile(storage.marks_file_path, 4096, WriteMode::Rewrite)) { } @@ -133,12 +135,13 @@ class LogBlockOutputStream final : public IBlockOutputStream } } - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override; void writeSuffix() override; private: StorageLog & storage; + StorageMetadataPtr metadata_snapshot; std::unique_lock lock; bool done = false; @@ -621,10 +624,10 @@ Pipes StorageLog::read( return pipes; } -BlockOutputStreamPtr StorageLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { loadMarks(); - return std::make_shared(*this); + return std::make_shared(*this, metadata_snapshot); } CheckResults StorageLog::checkData(const ASTPtr & /* query */, const Context & /* context */) diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index a387eadabe00..e96a48efc9e7 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -244,10 +244,11 @@ void StorageMaterializedView::checkAlterIsPossible(const AlterCommands & command } } -void StorageMaterializedView::alterPartition(const ASTPtr & query, const PartitionCommands &commands, const Context &context) +void StorageMaterializedView::alterPartition( + const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, const PartitionCommands & commands, const Context & context) { checkStatementCanBeForwarded(); - getTargetTable()->alterPartition(query, commands, context); + getTargetTable()->alterPartition(query, metadata_snapshot, commands, context); } void StorageMaterializedView::mutate(const MutationCommands & commands, const Context & context) diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index 42fe186a068a..672be800c8f6 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -43,7 +43,7 @@ class StorageMaterializedView final : public ext::shared_ptr_helper & modified_context, size_t streams_num, bool has_table_virtual_column, + const std::shared_ptr & modified_context, + size_t streams_num, + bool has_table_virtual_column, bool concat_streams) { const auto & [storage, struct_lock, table_name] = storage_with_lock; @@ -244,7 +254,6 @@ Pipes StorageMerge::createSources(const SelectQueryInfo & query_info, const Quer return pipes; } - auto metadata_snapshot = storage->getInMemoryMetadataPtr(); auto storage_stage = storage->getQueryProcessingStage(*modified_context, QueryProcessingStage::Complete, query_info.query); if (processed_stage <= storage_stage) { @@ -295,7 +304,7 @@ Pipes StorageMerge::createSources(const SelectQueryInfo & query_info, const Quer /// Subordinary tables could have different but convertible types, like numeric types of different width. /// We must return streams with structure equals to structure of Merge table. - convertingSourceStream(header, *modified_context, modified_query_info.query, pipe, processed_stage); + convertingSourceStream(header, metadata_snapshot, *modified_context, modified_query_info.query, pipe, processed_stage); pipe.addTableLock(struct_lock); pipe.addInterpreterContext(modified_context); @@ -430,8 +439,13 @@ Block StorageMerge::getQueryHeader( throw Exception("Logical Error: unknown processed stage.", ErrorCodes::LOGICAL_ERROR); } -void StorageMerge::convertingSourceStream(const Block & header, const Context & context, ASTPtr & query, - Pipe & pipe, QueryProcessingStage::Enum processed_stage) +void StorageMerge::convertingSourceStream( + const Block & header, + const StorageMetadataPtr & metadata_snapshot, + const Context & context, + ASTPtr & query, + Pipe & pipe, + QueryProcessingStage::Enum processed_stage) { Block before_block_header = pipe.getHeader(); pipe.addSimpleTransform(std::make_shared(before_block_header, header, ConvertingTransform::MatchColumnsMode::Name)); @@ -450,7 +464,7 @@ void StorageMerge::convertingSourceStream(const Block & header, const Context & /// So we need to throw exception. if (!header_column.type->equals(*before_column.type.get()) && processed_stage > QueryProcessingStage::FetchColumns) { - NamesAndTypesList source_columns = getSampleBlock().getNamesAndTypesList(); + NamesAndTypesList source_columns = metadata_snapshot->getSampleBlock().getNamesAndTypesList(); auto virtual_column = *getVirtuals().tryGetByName("_table"); source_columns.emplace_back(NameAndTypePair{virtual_column.name, virtual_column.type}); auto syntax_result = SyntaxAnalyzer(context).analyze(where_expression, source_columns); diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index 350f7a124fe4..14bf83f85348 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -82,14 +82,22 @@ class StorageMerge final : public ext::shared_ptr_helper, public I QueryProcessingStage::Enum processed_stage); Pipes createSources( - const SelectQueryInfo & query_info, const QueryProcessingStage::Enum & processed_stage, - const UInt64 max_block_size, const Block & header, const StorageWithLockAndName & storage_with_lock, + const StorageMetadataPtr & metadata_snapshot, + const SelectQueryInfo & query_info, + const QueryProcessingStage::Enum & processed_stage, + const UInt64 max_block_size, + const Block & header, + const StorageWithLockAndName & storage_with_lock, Names & real_column_names, - const std::shared_ptr & modified_context, size_t streams_num, bool has_table_virtual_column, + const std::shared_ptr & modified_context, + size_t streams_num, + bool has_table_virtual_column, bool concat_streams = false); - void convertingSourceStream(const Block & header, const Context & context, ASTPtr & query, - Pipe & pipe, QueryProcessingStage::Enum processed_stage); + void convertingSourceStream( + const Block & header, const StorageMetadataPtr & metadata_snapshot, + const Context & context, ASTPtr & query, + Pipe & pipe, QueryProcessingStage::Enum processed_stage); }; } diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 9c37cdd2b7c7..cfa5c34bece2 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -198,9 +198,9 @@ std::optional StorageMergeTree::totalBytes() const return getTotalActiveSizeInBytes(); } -BlockOutputStreamPtr StorageMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & context) { - return std::make_shared(*this, context.getSettingsRef().max_partitions_per_insert_block); + return std::make_shared(*this, metadata_snapshot, context.getSettingsRef().max_partitions_per_insert_block); } void StorageMergeTree::checkTableCanBeDropped() const @@ -1017,7 +1017,8 @@ bool StorageMergeTree::optimize( return true; } -void StorageMergeTree::alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) +void StorageMergeTree::alterPartition( + const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, const PartitionCommands & commands, const Context & context) { for (const PartitionCommand & command : commands) { @@ -1085,7 +1086,7 @@ void StorageMergeTree::alterPartition(const ASTPtr & query, const PartitionComma break; default: - IStorage::alterPartition(query, commands, context); // should throw an exception. + IStorage::alterPartition(query, metadata_snapshot, commands, context); // should throw an exception. } } } @@ -1126,7 +1127,8 @@ void StorageMergeTree::dropPartition(const ASTPtr & partition, bool detach, cons } -void StorageMergeTree::attachPartition(const ASTPtr & partition, bool attach_part, const Context & context) +void StorageMergeTree::attachPartition( + const ASTPtr & partition, bool attach_part, const Context & context) { // TODO: should get some locks to prevent race with 'alter … modify column' diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index 679726826d40..4b6da58572b8 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -55,7 +55,11 @@ class StorageMergeTree final : public ext::shared_ptr_helper, */ bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override; - void alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override; + void alterPartition( + const ASTPtr & query, + const StorageMetadataPtr & /* metadata_snapshot */, + const PartitionCommands & commands, + const Context & context) override; void mutate(const MutationCommands & commands, const Context & context) override; diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index dce9e0f38ecd..b1262771d21e 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -65,7 +65,7 @@ StorageMySQL::StorageMySQL( Pipes StorageMySQL::read( const Names & column_names_, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info_, const Context & context_, QueryProcessingStage::Enum /*processed_stage*/, @@ -74,12 +74,17 @@ Pipes StorageMySQL::read( { check(column_names_); String query = transformQueryForExternalDatabase( - query_info_, getColumns().getOrdinary(), IdentifierQuotingStyle::BackticksMySQL, remote_database_name, remote_table_name, context_); + query_info_, + metadata_snapshot->getColumns().getOrdinary(), + IdentifierQuotingStyle::BackticksMySQL, + remote_database_name, + remote_table_name, + context_); Block sample_block; for (const String & column_name : column_names_) { - auto column_data = getColumns().getPhysical(column_name); + auto column_data = metadata_snapshot->getColumns().getPhysical(column_name); sample_block.insert({ column_data.type, column_data.name }); } @@ -95,12 +100,15 @@ Pipes StorageMySQL::read( class StorageMySQLBlockOutputStream : public IBlockOutputStream { public: - explicit StorageMySQLBlockOutputStream(const StorageMySQL & storage_, + explicit StorageMySQLBlockOutputStream( + const StorageMySQL & storage_, + const StorageMetadataPtr & metadata_snapshot_, const std::string & remote_database_name_, const std::string & remote_table_name_, const mysqlxx::PoolWithFailover::Entry & entry_, const size_t & mysql_max_rows_to_insert) : storage{storage_} + , metadata_snapshot{metadata_snapshot_} , remote_database_name{remote_database_name_} , remote_table_name{remote_table_name_} , entry{entry_} @@ -108,7 +116,7 @@ class StorageMySQLBlockOutputStream : public IBlockOutputStream { } - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override { @@ -136,7 +144,7 @@ class StorageMySQLBlockOutputStream : public IBlockOutputStream sqlbuf << backQuoteMySQL(remote_database_name) << "." << backQuoteMySQL(remote_table_name); sqlbuf << " (" << dumpNamesWithBackQuote(block) << ") VALUES "; - auto writer = FormatFactory::instance().getOutput("Values", sqlbuf, storage.getSampleBlock(), storage.global_context); + auto writer = FormatFactory::instance().getOutput("Values", sqlbuf, metadata_snapshot->getSampleBlock(), storage.global_context); writer->write(block); if (!storage.on_duplicate_clause.empty()) @@ -192,6 +200,7 @@ class StorageMySQLBlockOutputStream : public IBlockOutputStream private: const StorageMySQL & storage; + StorageMetadataPtr metadata_snapshot; std::string remote_database_name; std::string remote_table_name; mysqlxx::PoolWithFailover::Entry entry; @@ -199,9 +208,9 @@ class StorageMySQLBlockOutputStream : public IBlockOutputStream }; -BlockOutputStreamPtr StorageMySQL::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageMySQL::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & context) { - return std::make_shared(*this, remote_database_name, remote_table_name, pool.get(), context.getSettingsRef().mysql_max_rows_to_insert); + return std::make_shared(*this, metadata_snapshot, remote_database_name, remote_table_name, pool.get(), context.getSettingsRef().mysql_max_rows_to_insert); } void registerStorageMySQL(StorageFactory & factory) diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index a6f84ffe4df7..8ae5a887013e 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3449,7 +3449,7 @@ void StorageReplicatedMergeTree::assertNotReadonly() const } -BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) +BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & context) { const auto storage_settings_ptr = getSettings(); assertNotReadonly(); @@ -3457,8 +3457,7 @@ BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, const Settings & query_settings = context.getSettingsRef(); bool deduplicate = storage_settings_ptr->replicated_deduplication_window != 0 && query_settings.insert_deduplicate; - return std::make_shared(*this, - query_settings.insert_quorum, query_settings.insert_quorum_timeout.totalMilliseconds(), query_settings.max_partitions_per_insert_block, deduplicate); + return std::make_shared(*this, metadata_snapshot, query_settings.insert_quorum, query_settings.insert_quorum_timeout.totalMilliseconds(), query_settings.max_partitions_per_insert_block, deduplicate); } @@ -3830,7 +3829,11 @@ void StorageReplicatedMergeTree::alter( } } -void StorageReplicatedMergeTree::alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & query_context) +void StorageReplicatedMergeTree::alterPartition( + const ASTPtr & query, + const StorageMetadataPtr & metadata_snapshot, + const PartitionCommands & commands, + const Context & query_context) { for (const PartitionCommand & command : commands) { @@ -3846,7 +3849,7 @@ void StorageReplicatedMergeTree::alterPartition(const ASTPtr & query, const Part break; case PartitionCommand::ATTACH_PARTITION: - attachPartition(command.partition, command.part, query_context); + attachPartition(command.partition, metadata_snapshot, command.part, query_context); break; case PartitionCommand::MOVE_PARTITION: { @@ -4014,7 +4017,7 @@ void StorageReplicatedMergeTree::truncate(const ASTPtr & query, const Context & } -void StorageReplicatedMergeTree::attachPartition(const ASTPtr & partition, bool attach_part, const Context & query_context) +void StorageReplicatedMergeTree::attachPartition(const ASTPtr & partition, const StorageMetadataPtr & metadata_snapshot, bool attach_part, const Context & query_context) { // TODO: should get some locks to prevent race with 'alter … modify column' @@ -4023,7 +4026,7 @@ void StorageReplicatedMergeTree::attachPartition(const ASTPtr & partition, bool PartsTemporaryRename renamed_parts(*this, "detached/"); MutableDataPartsVector loaded_parts = tryLoadPartsToAttach(partition, attach_part, query_context, renamed_parts); - ReplicatedMergeTreeBlockOutputStream output(*this, 0, 0, 0, false); /// TODO Allow to use quorum here. + ReplicatedMergeTreeBlockOutputStream output(*this, metadata_snapshot, 0, 0, 0, false); /// TODO Allow to use quorum here. for (size_t i = 0; i < loaded_parts.size(); ++i) { String old_name = loaded_parts[i]->name; diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index 5fcfd98e71d2..50530070d190 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -105,7 +105,11 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helpergetSampleBlock(); } void write(const Block & block) override; void writeSuffix() override; private: StorageSetOrJoinBase & table; + StorageMetadataPtr metadata_snapshot; String backup_path; String backup_tmp_path; String backup_file_name; @@ -50,14 +53,20 @@ class SetOrJoinBlockOutputStream : public IBlockOutputStream }; -SetOrJoinBlockOutputStream::SetOrJoinBlockOutputStream(StorageSetOrJoinBase & table_, - const String & backup_path_, const String & backup_tmp_path_, const String & backup_file_name_) - : table(table_), - backup_path(backup_path_), backup_tmp_path(backup_tmp_path_), - backup_file_name(backup_file_name_), - backup_buf(backup_tmp_path + backup_file_name), - compressed_backup_buf(backup_buf), - backup_stream(compressed_backup_buf, 0, table.getSampleBlock()) +SetOrJoinBlockOutputStream::SetOrJoinBlockOutputStream( + StorageSetOrJoinBase & table_, + const StorageMetadataPtr & metadata_snapshot_, + const String & backup_path_, + const String & backup_tmp_path_, + const String & backup_file_name_) + : table(table_) + , metadata_snapshot(metadata_snapshot_) + , backup_path(backup_path_) + , backup_tmp_path(backup_tmp_path_) + , backup_file_name(backup_file_name_) + , backup_buf(backup_tmp_path + backup_file_name) + , compressed_backup_buf(backup_buf) + , backup_stream(compressed_backup_buf, 0, metadata_snapshot->getSampleBlock()) { } @@ -81,10 +90,10 @@ void SetOrJoinBlockOutputStream::writeSuffix() } -BlockOutputStreamPtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { UInt64 id = ++increment; - return std::make_shared(*this, path, path + "tmp/", toString(id) + ".bin"); + return std::make_shared(*this, metadata_snapshot, path, path + "tmp/", toString(id) + ".bin"); } @@ -119,7 +128,8 @@ StorageSet::StorageSet( : StorageSetOrJoinBase{relative_path_, table_id_, columns_, constraints_, context_}, set(std::make_shared(SizeLimits(), false, true)) { - Block header = getSampleBlock(); + + Block header = getInMemoryMetadataPtr()->getSampleBlock(); header = header.sortColumns(); set->setHeader(header); @@ -134,11 +144,12 @@ size_t StorageSet::getSize() const { return set->getTotalRowCount(); } void StorageSet::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) { + auto metadata_snapshot = getInMemoryMetadataPtr(); Poco::File(path).remove(true); Poco::File(path).createDirectories(); Poco::File(path + "tmp/").createDirectories(); - Block header = getSampleBlock(); + Block header = metadata_snapshot->getSampleBlock(); header = header.sortColumns(); increment = 0; diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index b0c5bcfd669d..4d9f08a60b7f 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -155,15 +155,17 @@ class StripeLogSource final : public SourceWithProgress class StripeLogBlockOutputStream final : public IBlockOutputStream { public: - explicit StripeLogBlockOutputStream(StorageStripeLog & storage_) - : storage(storage_), lock(storage.rwlock), - data_out_file(storage.table_path + "data.bin"), - data_out_compressed(storage.disk->writeFile(data_out_file, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Append)), - data_out(*data_out_compressed, CompressionCodecFactory::instance().getDefaultCodec(), storage.max_compress_block_size), - index_out_file(storage.table_path + "index.mrk"), - index_out_compressed(storage.disk->writeFile(index_out_file, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Append)), - index_out(*index_out_compressed), - block_out(data_out, 0, storage.getSampleBlock(), false, &index_out, storage.disk->getFileSize(data_out_file)) + explicit StripeLogBlockOutputStream(StorageStripeLog & storage_, const StorageMetadataPtr & metadata_snapshot_) + : storage(storage_) + , metadata_snapshot(metadata_snapshot_) + , lock(storage.rwlock) + , data_out_file(storage.table_path + "data.bin") + , data_out_compressed(storage.disk->writeFile(data_out_file, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Append)) + , data_out(*data_out_compressed, CompressionCodecFactory::instance().getDefaultCodec(), storage.max_compress_block_size) + , index_out_file(storage.table_path + "index.mrk") + , index_out_compressed(storage.disk->writeFile(index_out_file, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Append)) + , index_out(*index_out_compressed) + , block_out(data_out, 0, metadata_snapshot->getSampleBlock(), false, &index_out, storage.disk->getFileSize(data_out_file)) { } @@ -179,7 +181,7 @@ class StripeLogBlockOutputStream final : public IBlockOutputStream } } - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override { @@ -205,6 +207,7 @@ class StripeLogBlockOutputStream final : public IBlockOutputStream private: StorageStripeLog & storage; + StorageMetadataPtr metadata_snapshot; std::unique_lock lock; String data_out_file; @@ -311,9 +314,9 @@ Pipes StorageStripeLog::read( } -BlockOutputStreamPtr StorageStripeLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageStripeLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { - return std::make_shared(*this); + return std::make_shared(*this, metadata_snapshot); } diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 42b70f716f42..ba524c7761e5 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -109,8 +109,8 @@ class TinyLogSource final : public SourceWithProgress class TinyLogBlockOutputStream final : public IBlockOutputStream { public: - explicit TinyLogBlockOutputStream(StorageTinyLog & storage_) - : storage(storage_), lock(storage_.rwlock) + explicit TinyLogBlockOutputStream(StorageTinyLog & storage_, const StorageMetadataPtr & metadata_snapshot_) + : storage(storage_), metadata_snapshot(metadata_snapshot_), lock(storage_.rwlock) { } @@ -126,13 +126,14 @@ class TinyLogBlockOutputStream final : public IBlockOutputStream } } - Block getHeader() const override { return storage.getSampleBlock(); } + Block getHeader() const override { return metadata_snapshot->getSampleBlock(); } void write(const Block & block) override; void writeSuffix() override; private: StorageTinyLog & storage; + StorageMetadataPtr metadata_snapshot; std::unique_lock lock; bool done = false; @@ -394,7 +395,7 @@ void StorageTinyLog::rename(const String & new_path_to_table_data, const Storage Pipes StorageTinyLog::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -408,15 +409,15 @@ Pipes StorageTinyLog::read( // When reading, we lock the entire storage, because we only have one file // per column and can't modify it concurrently. pipes.emplace_back(std::make_shared( - max_block_size, Nested::collect(getColumns().getAllPhysical().addTypes(column_names)), *this, context.getSettingsRef().max_read_buffer_size)); + max_block_size, Nested::collect(metadata_snapshot->getColumns().getAllPhysical().addTypes(column_names)), *this, context.getSettingsRef().max_read_buffer_size)); return pipes; } -BlockOutputStreamPtr StorageTinyLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr StorageTinyLog::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { - return std::make_shared(*this); + return std::make_shared(*this, metadata_snapshot); } diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index 6cea71150662..949d922b611b 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -186,10 +186,10 @@ Pipes IStorageURLBase::read( return pipes; } -BlockOutputStreamPtr IStorageURLBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & /*context*/) +BlockOutputStreamPtr IStorageURLBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, const Context & /*context*/) { return std::make_shared( - uri, format_name, getSampleBlock(), context_global, + uri, format_name, metadata_snapshot->getSampleBlock(), context_global, ConnectionTimeouts::getHTTPTimeouts(context_global), chooseCompressionMethod(uri.toString(), compression_method)); } diff --git a/src/Storages/System/IStorageSystemOneBlock.h b/src/Storages/System/IStorageSystemOneBlock.h index 7644f62b96d5..de7e1a0e9336 100644 --- a/src/Storages/System/IStorageSystemOneBlock.h +++ b/src/Storages/System/IStorageSystemOneBlock.h @@ -30,7 +30,7 @@ class IStorageSystemOneBlock : public IStorage Pipes read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -39,7 +39,7 @@ class IStorageSystemOneBlock : public IStorage { check(column_names); - Block sample_block = getSampleBlock(); + Block sample_block = metadata_snapshot->getSampleBlock(); MutableColumns res_columns = sample_block.cloneEmptyColumns(); fillData(res_columns, context, query_info); diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 646a5434b647..14a59da1bf95 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -242,7 +242,7 @@ class ColumnsSource : public SourceWithProgress Pipes StorageSystemColumns::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -255,7 +255,7 @@ Pipes StorageSystemColumns::read( NameSet names_set(column_names.begin(), column_names.end()); - Block sample_block = getSampleBlock(); + Block sample_block = metadata_snapshot->getSampleBlock(); Block header; std::vector columns_mask(sample_block.columns()); diff --git a/src/Storages/System/StorageSystemDetachedParts.cpp b/src/Storages/System/StorageSystemDetachedParts.cpp index 3d24d90bbef8..7228651d1402 100644 --- a/src/Storages/System/StorageSystemDetachedParts.cpp +++ b/src/Storages/System/StorageSystemDetachedParts.cpp @@ -47,7 +47,7 @@ class StorageSystemDetachedParts final : Pipes read( const Names & /* column_names */, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -57,7 +57,7 @@ class StorageSystemDetachedParts final : StoragesInfoStream stream(query_info, context); /// Create the result. - Block block = getSampleBlock(); + Block block = metadata_snapshot->getSampleBlock(); MutableColumns new_columns = block.cloneEmptyColumns(); while (StoragesInfo info = stream.next()) diff --git a/src/Storages/System/StorageSystemDisks.cpp b/src/Storages/System/StorageSystemDisks.cpp index 36fde616bd41..d13ea29804d2 100644 --- a/src/Storages/System/StorageSystemDisks.cpp +++ b/src/Storages/System/StorageSystemDisks.cpp @@ -28,7 +28,7 @@ StorageSystemDisks::StorageSystemDisks(const std::string & name_) Pipes StorageSystemDisks::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -63,7 +63,7 @@ Pipes StorageSystemDisks::read( Chunk chunk(std::move(res_columns), num_rows); Pipes pipes; - pipes.emplace_back(std::make_shared(getSampleBlock(), std::move(chunk))); + pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlock(), std::move(chunk))); return pipes; } diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index e599bbb19e3f..4f99e1e8c6a8 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -225,7 +225,7 @@ StoragesInfo StoragesInfoStream::next() Pipes StorageSystemPartsBase::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -238,7 +238,7 @@ Pipes StorageSystemPartsBase::read( /// Create the result. - MutableColumns res_columns = getSampleBlock().cloneEmptyColumns(); + MutableColumns res_columns = metadata_snapshot->getSampleBlock().cloneEmptyColumns(); if (has_state_column) res_columns.push_back(ColumnString::create()); @@ -247,7 +247,7 @@ Pipes StorageSystemPartsBase::read( processNextStorage(res_columns, info, has_state_column); } - Block header = getSampleBlock(); + Block header = metadata_snapshot->getSampleBlock(); if (has_state_column) header.insert(ColumnWithTypeAndName(std::make_shared(), "_state")); diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index 24861fcbd6a1..8fb6a89ddd16 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -59,7 +59,7 @@ StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) Pipes StorageSystemReplicas::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -146,7 +146,7 @@ Pipes StorageSystemReplicas::read( col_engine = filtered_block.getByName("engine").column; } - MutableColumns res_columns = getSampleBlock().cloneEmptyColumns(); + MutableColumns res_columns = metadata_snapshot->getSampleBlock().cloneEmptyColumns(); for (size_t i = 0, size = col_database->size(); i < size; ++i) { @@ -187,7 +187,7 @@ Pipes StorageSystemReplicas::read( res_columns[col_num++]->insert(status.zookeeper_exception); } - Block header = getSampleBlock(); + Block header = metadata_snapshot->getSampleBlock(); Columns fin_columns; fin_columns.reserve(res_columns.size()); @@ -203,7 +203,7 @@ Pipes StorageSystemReplicas::read( Chunk chunk(std::move(fin_columns), num_rows); Pipes pipes; - pipes.emplace_back(std::make_shared(getSampleBlock(), std::move(chunk))); + pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlock(), std::move(chunk))); return pipes; } diff --git a/src/Storages/System/StorageSystemStoragePolicies.cpp b/src/Storages/System/StorageSystemStoragePolicies.cpp index a80747c1fa1b..44252a788b98 100644 --- a/src/Storages/System/StorageSystemStoragePolicies.cpp +++ b/src/Storages/System/StorageSystemStoragePolicies.cpp @@ -32,7 +32,7 @@ StorageSystemStoragePolicies::StorageSystemStoragePolicies(const std::string & n Pipes StorageSystemStoragePolicies::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -78,7 +78,7 @@ Pipes StorageSystemStoragePolicies::read( Chunk chunk(std::move(res_columns), num_rows); Pipes pipes; - pipes.emplace_back(std::make_shared(getSampleBlock(), std::move(chunk))); + pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlock(), std::move(chunk))); return pipes; } diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index f04b3ea20c9b..b33886ce179b 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -448,7 +448,7 @@ class TablesBlockSource : public SourceWithProgress Pipes StorageSystemTables::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, @@ -461,7 +461,7 @@ Pipes StorageSystemTables::read( NameSet names_set(column_names.begin(), column_names.end()); - Block sample_block = getSampleBlock(); + Block sample_block = metadata_snapshot->getSampleBlock(); Block res_block; std::vector columns_mask(sample_block.columns()); From 603bcdde2d5fdeb9e7c0b3f29be9ac7454d289fb Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 19:13:07 +0300 Subject: [PATCH 0351/1102] Add DistinctStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 11 +++---- src/Processors/QueryPlan/DistinctStep.cpp | 31 +++++++++++++++++++ src/Processors/QueryPlan/DistinctStep.h | 27 ++++++++++++++++ .../QueryPlan/MergingSortedStep.cpp | 2 +- src/Processors/QueryPlan/MergingSortedStep.h | 2 +- src/Processors/ya.make | 1 + 6 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 src/Processors/QueryPlan/DistinctStep.cpp create mode 100644 src/Processors/QueryPlan/DistinctStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 6730fe693377..4b7b1cd506a0 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -82,6 +82,7 @@ #include #include #include +#include namespace DB @@ -1769,13 +1770,11 @@ void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool befo SizeLimits limits(settings.max_rows_in_distinct, settings.max_bytes_in_distinct, settings.distinct_overflow_mode); - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type == QueryPipeline::StreamType::Totals) - return nullptr; + DistinctStep distinct_step( + DataStream{.header = pipeline.getHeader()}, + limits, limit_for_distinct, columns); - return std::make_shared(header, limits, limit_for_distinct, columns); - }); + distinct_step.transformPipeline(pipeline); } } diff --git a/src/Processors/QueryPlan/DistinctStep.cpp b/src/Processors/QueryPlan/DistinctStep.cpp new file mode 100644 index 000000000000..7e339ba816bd --- /dev/null +++ b/src/Processors/QueryPlan/DistinctStep.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +namespace DB +{ + +DistinctStep::DistinctStep( + const DataStream & input_stream_, + const SizeLimits & set_size_limits_, + UInt64 limit_hint_, + const Names & columns_) + : ITransformingStep(input_stream_, input_stream_) + , set_size_limits(set_size_limits_) + , limit_hint(limit_hint_) + , columns(columns_) +{ +} + +void DistinctStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, set_size_limits, limit_hint, columns); + }); +} + +} diff --git a/src/Processors/QueryPlan/DistinctStep.h b/src/Processors/QueryPlan/DistinctStep.h new file mode 100644 index 000000000000..638bd3debf75 --- /dev/null +++ b/src/Processors/QueryPlan/DistinctStep.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +namespace DB +{ + +class DistinctStep : public ITransformingStep +{ +public: + explicit DistinctStep( + const DataStream & input_stream_, + const SizeLimits & set_size_limits_, + UInt64 limit_hint_, + const Names & columns_); + + String getName() const override { return "Distinct"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SizeLimits set_size_limits; + UInt64 limit_hint; + Names columns; +}; + +} diff --git a/src/Processors/QueryPlan/MergingSortedStep.cpp b/src/Processors/QueryPlan/MergingSortedStep.cpp index e43c5a598aed..5417941745ac 100644 --- a/src/Processors/QueryPlan/MergingSortedStep.cpp +++ b/src/Processors/QueryPlan/MergingSortedStep.cpp @@ -6,7 +6,7 @@ namespace DB { MergingSortedStep::MergingSortedStep( - DataStream input_stream, + const DataStream & input_stream, SortDescription sort_description_, size_t max_block_size_, UInt64 limit_) diff --git a/src/Processors/QueryPlan/MergingSortedStep.h b/src/Processors/QueryPlan/MergingSortedStep.h index 3b586e85a3eb..920073da8cb1 100644 --- a/src/Processors/QueryPlan/MergingSortedStep.h +++ b/src/Processors/QueryPlan/MergingSortedStep.h @@ -11,7 +11,7 @@ class MergingSortedStep : public ITransformingStep { public: explicit MergingSortedStep( - DataStream input_stream, + const DataStream & input_stream, SortDescription sort_description_, size_t max_block_size_, UInt64 limit_ = 0); diff --git a/src/Processors/ya.make b/src/Processors/ya.make index b1e203b75b70..52b0a7bbb8c1 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -137,6 +137,7 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp Transforms/AggregatingInOrderTransform.cpp + QueryPlan/DistinctTransform.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp QueryPlan/ISourceStep.cpp From 5bb1d5cc4353854df1e9e1c11ebbe42df3e4dfac Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 19:49:49 +0300 Subject: [PATCH 0352/1102] Add LimitByStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 10 +++---- src/Processors/QueryPlan/LimitByStep.cpp | 30 +++++++++++++++++++++ src/Processors/QueryPlan/LimitByStep.h | 26 ++++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/Processors/QueryPlan/LimitByStep.cpp create mode 100644 src/Processors/QueryPlan/LimitByStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 4b7b1cd506a0..fdf4fee89635 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -83,6 +83,7 @@ #include #include #include +#include namespace DB @@ -1813,13 +1814,8 @@ void InterpreterSelectQuery::executeLimitBy(QueryPipeline & pipeline) UInt64 length = getLimitUIntValue(query.limitByLength(), *context, "LIMIT"); UInt64 offset = (query.limitByOffset() ? getLimitUIntValue(query.limitByOffset(), *context, "OFFSET") : 0); - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type == QueryPipeline::StreamType::Totals) - return nullptr; - - return std::make_shared(header, length, offset, columns); - }); + LimitByStep limit_by(DataStream{.header = pipeline.getHeader()}, length, offset, columns); + limit_by.transformPipeline(pipeline); } diff --git a/src/Processors/QueryPlan/LimitByStep.cpp b/src/Processors/QueryPlan/LimitByStep.cpp new file mode 100644 index 000000000000..6b9dd9d3727b --- /dev/null +++ b/src/Processors/QueryPlan/LimitByStep.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +namespace DB +{ + +LimitByStep::LimitByStep( + const DataStream & input_stream_, + size_t group_length_, size_t group_offset_, const Names & columns_) + : ITransformingStep(input_stream_, input_stream_) + , group_length(group_length_) + , group_offset(group_offset_) + , columns(columns_) +{ +} + + +void LimitByStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, group_length, group_offset, columns); + }); +} + +} diff --git a/src/Processors/QueryPlan/LimitByStep.h b/src/Processors/QueryPlan/LimitByStep.h new file mode 100644 index 000000000000..744918cb8366 --- /dev/null +++ b/src/Processors/QueryPlan/LimitByStep.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace DB +{ + +class LimitByStep : public ITransformingStep +{ +public: + explicit LimitByStep( + const DataStream & input_stream_, + size_t group_length_, size_t group_offset_, const Names & columns_); + + String getName() const override { return "LimitBy"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + size_t group_length; + size_t group_offset; + Names columns; +}; + +} + + diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 52b0a7bbb8c1..fba6aea7fd96 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -143,6 +143,7 @@ SRCS( QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp + QueryPlan/LimitByStep.cpp QueryPlan/MergeSortingStep.cpp QueryPlan/MergingSortedStep.cpp QueryPlan/PartialSortingStep.cpp From ccc2bda66666f1ac548c04f28dcdd465b00d20d5 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 19:55:04 +0300 Subject: [PATCH 0353/1102] getConstraints() in StorageInMemoryMetadata (suspicious commit, but pretend to work) --- src/Interpreters/InterpreterCreateQuery.cpp | 7 +-- src/Interpreters/InterpreterInsertQuery.cpp | 4 +- src/Storages/IStorage.cpp | 5 -- src/Storages/IStorage.h | 3 +- .../ReplicatedMergeTreeTableMetadata.cpp | 8 +-- .../ReplicatedMergeTreeTableMetadata.h | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 1 + src/Storages/StorageReplicatedMergeTree.cpp | 51 ++++++++++--------- src/Storages/StorageReplicatedMergeTree.h | 6 +-- 9 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index 5d8c43aed0dc..bb82c94a7641 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -430,14 +430,15 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::setProperties(AS /// as_storage->getColumns() and setEngine(...) must be called under structure lock of other_table for CREATE ... AS other_table. as_storage_lock = as_storage->lockStructureForShare( false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - properties.columns = as_storage->getColumns(); + auto as_storage_metadata = as_storage->getInMemoryMetadataPtr(); + properties.columns = as_storage_metadata->getColumns(); /// Secondary indices make sense only for MergeTree family of storage engines. /// We should not copy them for other storages. if (create.storage && endsWith(create.storage->engine->name, "MergeTree")) - properties.indices = as_storage->getSecondaryIndices(); + properties.indices = as_storage_metadata->getSecondaryIndices(); - properties.constraints = as_storage->getConstraints(); + properties.constraints = as_storage_metadata->getConstraints(); } else if (create.select) { diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index 443e2714ec7a..e7fdf80e2978 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -237,9 +237,9 @@ BlockIO InterpreterInsertQuery::execute() /// Note that we wrap transforms one on top of another, so we write them in reverse of data processing order. /// Checking constraints. It must be done after calculation of all defaults, so we can check them on calculated columns. - if (const auto & constraints = table->getConstraints(); !constraints.empty()) + if (const auto & constraints = metadata_snapshot->getConstraints(); !constraints.empty()) out = std::make_shared( - query.table_id, out, out->getHeader(), table->getConstraints(), context); + query.table_id, out, out->getHeader(), metadata_snapshot->getConstraints(), context); /// Actually we don't know structure of input blocks from query/table, /// because some clients break insertion protocol (columns != header) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 43e9a5dd040e..1fb3e0952298 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -47,11 +47,6 @@ bool IStorage::hasSecondaryIndices() const return !metadata->secondary_indices.empty(); } -const ConstraintsDescription & IStorage::getConstraints() const -{ - return metadata->constraints; -} - namespace { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index df1e1685a2ed..e45d6a1128b0 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -130,7 +130,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo virtual bool hasEvenlyDistributedRead() const { return false; } /// Returns true if there is set table TTL, any column TTL or any move TTL. - virtual bool hasAnyTTL() const { return hasAnyColumnTTL() || hasAnyTableTTL(); } + bool hasAnyTTL() const { return hasAnyColumnTTL() || hasAnyTableTTL(); } /// Optional size information of each physical column. /// Currently it's only used by the MergeTree family for query optimizations. @@ -144,7 +144,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Has at least one non primary index bool hasSecondaryIndices() const; - const ConstraintsDescription & getConstraints() const; /// Storage settings ASTPtr getSettingsChanges() const; diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index 2444affdbff4..820f41326f18 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -23,7 +23,7 @@ static String formattedAST(const ASTPtr & ast) return ss.str(); } -ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTreeData & data) +ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTreeData & data, const StorageMetadataPtr & metadata_snapshot) { if (data.format_version < MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING) date_column = data.minmax_idx_columns[data.minmax_idx_date_column_pos]; @@ -53,15 +53,15 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr if (data.format_version >= MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING) partition_key = formattedAST(data.getPartitionKey().expression_list_ast); - ttl_table = formattedAST(data.getTableTTLs().definition_ast); + ttl_table = formattedAST(metadata_snapshot->getTableTTLs().definition_ast); - skip_indices = data.getSecondaryIndices().toString(); + skip_indices = metadata_snapshot->getSecondaryIndices().toString(); if (data.canUseAdaptiveGranularity()) index_granularity_bytes = data_settings->index_granularity_bytes; else index_granularity_bytes = 0; - constraints = data.getConstraints().toString(); + constraints = metadata_snapshot->getConstraints().toString(); } void ReplicatedMergeTreeTableMetadata::write(WriteBuffer & out) const diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.h b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.h index 280a8c8b4037..f7174140ee15 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.h +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.h @@ -32,7 +32,7 @@ struct ReplicatedMergeTreeTableMetadata UInt64 index_granularity_bytes; ReplicatedMergeTreeTableMetadata() = default; - explicit ReplicatedMergeTreeTableMetadata(const MergeTreeData & data); + explicit ReplicatedMergeTreeTableMetadata(const MergeTreeData & data, const StorageMetadataPtr & metadata_snapshot); void read(ReadBuffer & in); static ReplicatedMergeTreeTableMetadata parse(const String & s); diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index cce3911370d4..359d561cd1fd 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -290,4 +290,5 @@ Block StorageInMemoryMetadata::getSampleBlockForColumns(const Names & column_nam return res; } + } diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 8ae5a887013e..170a77e3508f 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -248,6 +248,8 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( return; } + auto metadata_snapshot = getInMemoryMetadataPtr(); + if (!attach) { if (!getDataParts().empty()) @@ -255,21 +257,21 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( try { - bool is_first_replica = createTableIfNotExists(); + bool is_first_replica = createTableIfNotExists(metadata_snapshot); /// We have to check granularity on other replicas. If it's fixed we /// must create our new replica with fixed granularity and store this /// information in /replica/metadata. other_replicas_fixed_granularity = checkFixedGranualrityInZookeeper(); - checkTableStructure(zookeeper_path); + checkTableStructure(zookeeper_path, metadata_snapshot); Coordination::Stat metadata_stat; current_zookeeper->get(zookeeper_path + "/metadata", &metadata_stat); metadata_version = metadata_stat.version; if (!is_first_replica) - createReplica(); + createReplica(metadata_snapshot); } catch (...) { @@ -288,11 +290,11 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( { /// We have to check shared node granularity before we create ours. other_replicas_fixed_granularity = checkFixedGranualrityInZookeeper(); - ReplicatedMergeTreeTableMetadata current_metadata(*this); + ReplicatedMergeTreeTableMetadata current_metadata(*this, metadata_snapshot); current_zookeeper->createOrUpdate(replica_path + "/metadata", current_metadata.toString(), zkutil::CreateMode::Persistent); } - checkTableStructure(replica_path); + checkTableStructure(replica_path, metadata_snapshot); checkParts(skip_sanity_checks); if (current_zookeeper->exists(replica_path + "/metadata_version")) @@ -418,7 +420,7 @@ void StorageReplicatedMergeTree::createNewZooKeeperNodes() } -bool StorageReplicatedMergeTree::createTableIfNotExists() +bool StorageReplicatedMergeTree::createTableIfNotExists(const StorageMetadataPtr & metadata_snapshot) { auto zookeeper = getZooKeeper(); zookeeper->createAncestors(zookeeper_path); @@ -483,7 +485,7 @@ bool StorageReplicatedMergeTree::createTableIfNotExists() LOG_DEBUG(log, "Creating table {}", zookeeper_path); /// We write metadata of table so that the replicas can check table parameters with them. - String metadata_str = ReplicatedMergeTreeTableMetadata(*this).toString(); + String metadata_str = ReplicatedMergeTreeTableMetadata(*this, metadata_snapshot).toString(); Coordination::Requests ops; ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", zkutil::CreateMode::Persistent)); @@ -552,7 +554,7 @@ bool StorageReplicatedMergeTree::createTableIfNotExists() throw Exception("Cannot create table, because it is created concurrently every time or because of logical error", ErrorCodes::LOGICAL_ERROR); } -void StorageReplicatedMergeTree::createReplica() +void StorageReplicatedMergeTree::createReplica(const StorageMetadataPtr & metadata_snapshot) { auto zookeeper = getZooKeeper(); @@ -588,7 +590,7 @@ void StorageReplicatedMergeTree::createReplica() zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/is_lost", is_lost_value, zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", ReplicatedMergeTreeTableMetadata(*this).toString(), + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", ReplicatedMergeTreeTableMetadata(*this, metadata_snapshot).toString(), zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), zkutil::CreateMode::Persistent)); @@ -728,11 +730,11 @@ void StorageReplicatedMergeTree::drop() /** Verify that list of columns and table storage_settings_ptr match those specified in ZK (/ metadata). * If not, throw an exception. */ -void StorageReplicatedMergeTree::checkTableStructure(const String & zookeeper_prefix) +void StorageReplicatedMergeTree::checkTableStructure(const String & zookeeper_prefix, const StorageMetadataPtr & metadata_snapshot) { auto zookeeper = getZooKeeper(); - ReplicatedMergeTreeTableMetadata old_metadata(*this); + ReplicatedMergeTreeTableMetadata old_metadata(*this, metadata_snapshot); Coordination::Stat metadata_stat; String metadata_str = zookeeper->get(zookeeper_prefix + "/metadata", &metadata_stat); @@ -3624,7 +3626,7 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); - auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this).checkAndFindDiff(metadata_from_entry); + auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); setTableStructure(std::move(columns_from_entry), metadata_diff); metadata_version = entry.alter_version; @@ -3683,24 +3685,24 @@ void StorageReplicatedMergeTree::alter( throw Exception("Can't ALTER readonly table", ErrorCodes::TABLE_IS_READ_ONLY); - StorageInMemoryMetadata current_metadata = getInMemoryMetadata(); + auto current_metadata = getInMemoryMetadataPtr(); - StorageInMemoryMetadata future_metadata = current_metadata; + StorageInMemoryMetadata future_metadata = *current_metadata; params.apply(future_metadata, query_context); - ReplicatedMergeTreeTableMetadata future_metadata_in_zk(*this); - if (ast_to_str(future_metadata.sorting_key.definition_ast) != ast_to_str(current_metadata.sorting_key.definition_ast)) + ReplicatedMergeTreeTableMetadata future_metadata_in_zk(*this, current_metadata); + if (ast_to_str(future_metadata.sorting_key.definition_ast) != ast_to_str(current_metadata->sorting_key.definition_ast)) future_metadata_in_zk.sorting_key = serializeAST(*future_metadata.sorting_key.expression_list_ast); - if (ast_to_str(future_metadata.table_ttl.definition_ast) != ast_to_str(current_metadata.table_ttl.definition_ast)) + if (ast_to_str(future_metadata.table_ttl.definition_ast) != ast_to_str(current_metadata->table_ttl.definition_ast)) future_metadata_in_zk.ttl_table = serializeAST(*future_metadata.table_ttl.definition_ast); String new_indices_str = future_metadata.secondary_indices.toString(); - if (new_indices_str != current_metadata.secondary_indices.toString()) + if (new_indices_str != current_metadata->secondary_indices.toString()) future_metadata_in_zk.skip_indices = new_indices_str; String new_constraints_str = future_metadata.constraints.toString(); - if (new_constraints_str != current_metadata.constraints.toString()) + if (new_constraints_str != current_metadata->constraints.toString()) future_metadata_in_zk.constraints = new_constraints_str; Coordination::Requests ops; @@ -3711,14 +3713,15 @@ void StorageReplicatedMergeTree::alter( String new_columns_str = future_metadata.columns.toString(); ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/columns", new_columns_str, -1)); - if (ast_to_str(current_metadata.settings_changes) != ast_to_str(future_metadata.settings_changes)) + if (ast_to_str(current_metadata->settings_changes) != ast_to_str(future_metadata.settings_changes)) { lockStructureExclusively( table_lock_holder, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); /// Just change settings - current_metadata.settings_changes = future_metadata.settings_changes; - changeSettings(current_metadata.settings_changes, table_lock_holder); - DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(query_context, table_id, current_metadata); + StorageInMemoryMetadata metadata_copy = *current_metadata; + metadata_copy.settings_changes = future_metadata.settings_changes; + changeSettings(metadata_copy.settings_changes, table_lock_holder); + DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(query_context, table_id, metadata_copy); } /// We can be sure, that in case of successfull commit in zookeeper our @@ -3733,7 +3736,7 @@ void StorageReplicatedMergeTree::alter( alter_entry->create_time = time(nullptr); auto maybe_mutation_commands = params.getMutationCommands( - current_metadata, query_context.getSettingsRef().materialize_ttl_after_modify, query_context); + *current_metadata, query_context.getSettingsRef().materialize_ttl_after_modify, query_context); alter_entry->have_mutation = !maybe_mutation_commands.empty(); ops.emplace_back(zkutil::makeCreateRequest( diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index 50530070d190..49dc09dbcf49 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -301,17 +301,17 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helper Date: Tue, 16 Jun 2020 21:23:01 +0300 Subject: [PATCH 0354/1102] Add LimitStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 20 +++++++------ src/Processors/QueryPlan/LimitStep.cpp | 30 +++++++++++++++++++ src/Processors/QueryPlan/LimitStep.h | 32 +++++++++++++++++++++ src/Processors/ya.make | 3 +- 4 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 src/Processors/QueryPlan/LimitStep.cpp create mode 100644 src/Processors/QueryPlan/LimitStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index fdf4fee89635..5b21f4b6322a 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -84,6 +84,7 @@ #include #include #include +#include namespace DB @@ -1795,8 +1796,9 @@ void InterpreterSelectQuery::executePreLimit(QueryPipeline & pipeline, bool do_n limit_offset = 0; } - auto limit = std::make_shared(pipeline.getHeader(), limit_length, limit_offset, pipeline.getNumStreams()); - pipeline.addPipe({std::move(limit)}); + LimitStep limit(DataStream{.header = pipeline.getHeader()}, limit_length, limit_offset); + limit.setStepDescription("preliminary LIMIT"); + limit.transformPipeline(pipeline); } } @@ -1903,14 +1905,14 @@ void InterpreterSelectQuery::executeLimit(QueryPipeline & pipeline) order_descr = getSortDescription(query, *context); } - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type != QueryPipeline::StreamType::Main) - return nullptr; + LimitStep limit( + DataStream{.header = pipeline.getHeader()}, + limit_length, limit_offset, always_read_till_end, query.limit_with_ties, order_descr); - return std::make_shared( - header, limit_length, limit_offset, 1, always_read_till_end, query.limit_with_ties, order_descr); - }); + if (query.limit_with_ties) + limit.setStepDescription("LIMIT WITH TIES"); + + limit.transformPipeline(pipeline); } } diff --git a/src/Processors/QueryPlan/LimitStep.cpp b/src/Processors/QueryPlan/LimitStep.cpp new file mode 100644 index 000000000000..daae8913ee5b --- /dev/null +++ b/src/Processors/QueryPlan/LimitStep.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +namespace DB +{ + +LimitStep::LimitStep( + const DataStream & input_stream_, + size_t limit_, size_t offset_, + bool always_read_till_end_, + bool with_ties_, + SortDescription description_) + : ITransformingStep(input_stream_, input_stream_) + , limit(limit_), offset(offset_) + , always_read_till_end(always_read_till_end_) + , with_ties(with_ties_), description(std::move(description_)) +{ + +} + +void LimitStep::transformPipeline(QueryPipeline & pipeline) +{ + auto transform = std::make_shared( + pipeline.getHeader(), limit, offset, pipeline.getNumStreams(), always_read_till_end, with_ties, description); + + pipeline.addPipe({std::move(transform)}); +} + +} diff --git a/src/Processors/QueryPlan/LimitStep.h b/src/Processors/QueryPlan/LimitStep.h new file mode 100644 index 000000000000..4a12e8f67051 --- /dev/null +++ b/src/Processors/QueryPlan/LimitStep.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +namespace DB +{ + +class LimitStep : public ITransformingStep +{ +public: + LimitStep( + const DataStream & input_stream_, + size_t limit_, size_t offset_, + bool always_read_till_end_ = false, + bool with_ties_ = false, + SortDescription description_ = {}); + + String getName() const override { return "Limit"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + size_t limit; + size_t offset; + bool always_read_till_end; + + bool with_ties; + const SortDescription description; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index fba6aea7fd96..69ca03e554a5 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -137,13 +137,14 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp Transforms/AggregatingInOrderTransform.cpp - QueryPlan/DistinctTransform.cpp + QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp QueryPlan/LimitByStep.cpp + QueryPlan/LimitStep.cpp QueryPlan/MergeSortingStep.cpp QueryPlan/MergingSortedStep.cpp QueryPlan/PartialSortingStep.cpp From fa60903620b46dccc310ecca309b0e67a53600a6 Mon Sep 17 00:00:00 2001 From: alesapin Date: Tue, 16 Jun 2020 21:41:11 +0300 Subject: [PATCH 0355/1102] Fix race condition --- src/Storages/StorageBuffer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index b08e4e93bedb..f8df14aa4820 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -160,13 +160,14 @@ Pipes StorageBuffer::read( { auto destination = DatabaseCatalog::instance().getTable(destination_id, context); - auto destination_metadata_snapshot = destination->getInMemoryMetadataPtr(); if (destination.get() == this) throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP); auto destination_lock = destination->lockStructureForShare( false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto destination_metadata_snapshot = destination->getInMemoryMetadataPtr(); + const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [metadata_snapshot, destination](const String& column_name) { const auto & dest_columns = destination->getColumns(); From fdacc9f90e90f58520fe62a102d51bea27d39a32 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Tue, 16 Jun 2020 21:57:21 +0300 Subject: [PATCH 0356/1102] Add MergingAggregatedStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 33 +++-------- .../QueryPlan/MergingAggregatedStep.cpp | 55 +++++++++++++++++++ .../QueryPlan/MergingAggregatedStep.h | 32 +++++++++++ src/Processors/ya.make | 1 + 4 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/Processors/QueryPlan/MergingAggregatedStep.cpp create mode 100644 src/Processors/QueryPlan/MergingAggregatedStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 5b21f4b6322a..ccdf53184721 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -85,6 +85,7 @@ #include #include #include +#include namespace DB @@ -1538,34 +1539,14 @@ void InterpreterSelectQuery::executeMergeAggregated(QueryPipeline & pipeline, bo auto transform_params = std::make_shared(params, final); - if (!settings.distributed_aggregation_memory_efficient) - { - /// We union several sources into one, parallelizing the work. - pipeline.resize(1); - - /// Now merge the aggregated blocks - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params, settings.max_threads); - }); - } - else - { - /// pipeline.resize(max_streams); - Seem we don't need it. - auto num_merge_threads = settings.aggregation_memory_efficient_merge_threads - ? static_cast(settings.aggregation_memory_efficient_merge_threads) - : static_cast(settings.max_threads); - - auto pipe = createMergingAggregatedMemoryEfficientPipe( - pipeline.getHeader(), + MergingAggregatedStep merging_aggregated( + DataStream{.header = pipeline.getHeader()}, transform_params, - pipeline.getNumStreams(), - num_merge_threads); + settings.distributed_aggregation_memory_efficient, + settings.max_threads, + settings.aggregation_memory_efficient_merge_threads); - pipeline.addPipe(std::move(pipe)); - } - - pipeline.enableQuotaForCurrentStreams(); + merging_aggregated.transformPipeline(pipeline); } diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.cpp b/src/Processors/QueryPlan/MergingAggregatedStep.cpp new file mode 100644 index 000000000000..ad67ed5de426 --- /dev/null +++ b/src/Processors/QueryPlan/MergingAggregatedStep.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include + +namespace DB +{ + +MergingAggregatedStep::MergingAggregatedStep( + const DataStream & input_stream_, + AggregatingTransformParamsPtr params_, + bool memory_efficient_aggregation_, + size_t max_threads_, + size_t memory_efficient_merge_threads_) + : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + , params(params_) + , memory_efficient_aggregation(memory_efficient_aggregation_) + , max_threads(max_threads_) + , memory_efficient_merge_threads(memory_efficient_merge_threads_) +{ +} + +void MergingAggregatedStep::transformPipeline(QueryPipeline & pipeline) +{ + if (!memory_efficient_aggregation) + { + /// We union several sources into one, parallelizing the work. + pipeline.resize(1); + + /// Now merge the aggregated blocks + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, params, max_threads); + }); + } + else + { + auto num_merge_threads = memory_efficient_merge_threads + ? static_cast(memory_efficient_merge_threads) + : static_cast(max_threads); + + auto pipe = createMergingAggregatedMemoryEfficientPipe( + pipeline.getHeader(), + params, + pipeline.getNumStreams(), + num_merge_threads); + + pipeline.addPipe(std::move(pipe)); + } + + pipeline.enableQuotaForCurrentStreams(); +} + +} diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.h b/src/Processors/QueryPlan/MergingAggregatedStep.h new file mode 100644 index 000000000000..51a907285df9 --- /dev/null +++ b/src/Processors/QueryPlan/MergingAggregatedStep.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +namespace DB +{ + +struct AggregatingTransformParams; +using AggregatingTransformParamsPtr = std::shared_ptr; + +class MergingAggregatedStep : public ITransformingStep +{ +public: + MergingAggregatedStep( + const DataStream & input_stream_, + AggregatingTransformParamsPtr params_, + bool memory_efficient_aggregation_, + size_t max_threads_, + size_t memory_efficient_merge_threads_); + + String getName() const override { return "MergingAggregated"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + AggregatingTransformParamsPtr params; + bool memory_efficient_aggregation; + size_t max_threads; + size_t memory_efficient_merge_threads; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 69ca03e554a5..18e5c77cc049 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -146,6 +146,7 @@ SRCS( QueryPlan/LimitByStep.cpp QueryPlan/LimitStep.cpp QueryPlan/MergeSortingStep.cpp + QueryPlan/MergingAggregatedStep.cpp QueryPlan/MergingSortedStep.cpp QueryPlan/PartialSortingStep.cpp QueryPlan/ReadFromStorageStep.cpp From 8b50e3450b93e436c5d205ad9e4905c735ad5291 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Tue, 16 Jun 2020 23:01:15 +0300 Subject: [PATCH 0357/1102] move the default endpoint to config --- base/daemon/SentryWriter.cpp | 2 +- programs/server/config.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index 0524285ea42b..d979c6712903 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -96,7 +96,7 @@ void SentryWriter::initialize(Poco::Util::LayeredConfiguration & config) { const std::filesystem::path & default_tmp_path = std::filesystem::path(config.getString("tmp_path", Poco::Path::temp())) / "sentry"; const std::string & endpoint - = config.getString("send_crash_reports.endpoint", "https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277"); + = config.getString("send_crash_reports.endpoint"); const std::string & temp_folder_path = config.getString("send_crash_reports.tmp_path", default_tmp_path); Poco::File(temp_folder_path).createDirectories(); diff --git a/programs/server/config.xml b/programs/server/config.xml index e5482a074a30..d1226e6f09c9 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -52,6 +52,9 @@ false false + + + https://6f33034cfe684dd7a3ab9875e57b1c8d@o388870.ingest.sentry.io/5226277 From 102628ff099b65c2bd764b2ea66ef8bcd1c606f5 Mon Sep 17 00:00:00 2001 From: Ivan Blinkov Date: Wed, 17 Jun 2020 10:54:06 +0300 Subject: [PATCH 0358/1102] remove extra line --- base/daemon/SentryWriter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp index d979c6712903..b59df1ba55ca 100644 --- a/base/daemon/SentryWriter.cpp +++ b/base/daemon/SentryWriter.cpp @@ -226,7 +226,6 @@ void SentryWriter::onFault(int sig, const siginfo_t & info, const ucontext_t & c sentry_value_t threads = sentry_value_new_object(); sentry_value_set_by_key(threads, "values", values); - sentry_value_set_by_key(event, "threads", threads); LOG_INFO(logger, "Sending crash report"); From 0829d1d16db2c4c5efd8d39f4feaca097fb77a67 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 12:16:01 +0300 Subject: [PATCH 0359/1102] Fix ya.make --- src/Processors/ya.make | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 18e5c77cc049..6e5128c2e85c 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -149,6 +149,7 @@ SRCS( QueryPlan/MergingAggregatedStep.cpp QueryPlan/MergingSortedStep.cpp QueryPlan/PartialSortingStep.cpp + QueryPlan/ReadFromPreparedSource.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp QueryPlan/QueryPlan.cpp From 62f2c17a668d85cfb88a6137a14997cda1e35d5b Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 12:38:47 +0300 Subject: [PATCH 0360/1102] Secondary indices in StorageInMemoryMetadata --- src/Interpreters/ExpressionAnalyzer.cpp | 2 +- src/Interpreters/ExpressionAnalyzer.h | 12 +++++++--- src/Interpreters/InterpreterSelectQuery.cpp | 4 ++-- src/Interpreters/MutationsInterpreter.cpp | 4 ++-- src/Storages/IStorage.cpp | 11 ---------- src/Storages/IStorage.h | 6 +---- src/Storages/MergeTree/MergeTreeData.cpp | 22 ++++++++++--------- src/Storages/MergeTree/MergeTreeData.h | 7 +++--- .../MergeTree/MergeTreeDataMergerMutator.cpp | 21 +++++++++--------- .../MergeTree/MergeTreeDataMergerMutator.h | 1 + .../MergeTree/MergeTreeDataSelectExecutor.cpp | 2 +- .../MergeTree/MergeTreeDataWriter.cpp | 6 ++--- .../MergeTree/MergedBlockOutputStream.cpp | 2 +- .../MergeTree/StorageFromMergeTreeDataPart.h | 5 +++-- src/Storages/StorageBuffer.cpp | 6 +++-- src/Storages/StorageBuffer.h | 2 +- src/Storages/StorageMaterializedView.h | 6 +++-- src/Storages/StorageMerge.cpp | 6 +++-- src/Storages/StorageMerge.h | 3 ++- 19 files changed, 66 insertions(+), 62 deletions(-) diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 24c71e276d6e..039001796ccd 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -356,7 +356,7 @@ void SelectQueryExpressionAnalyzer::makeSetsForIndex(const ASTPtr & node) const IAST & args = *func->arguments; const ASTPtr & left_in_operand = args.children.at(0); - if (storage()->mayBenefitFromIndexForIn(left_in_operand, context)) + if (storage()->mayBenefitFromIndexForIn(left_in_operand, context, metadata_snapshot)) { const ASTPtr & arg = args.children.at(1); if (arg->as() || arg->as()) diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index c69cb61162f1..1cc1b33bad1a 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -11,7 +11,6 @@ #include #include - namespace DB { @@ -32,6 +31,9 @@ class ASTExpressionList; class ASTSelectQuery; struct ASTTablesInSelectQueryElement; +struct StorageInMemoryMetadata; +using StorageMetadataPtr = std::shared_ptr; + /// Create columns in block or return false if not possible bool sanitizeBlock(Block & block); @@ -232,11 +234,14 @@ class SelectQueryExpressionAnalyzer : public ExpressionAnalyzer const ASTPtr & query_, const SyntaxAnalyzerResultPtr & syntax_analyzer_result_, const Context & context_, + const StorageMetadataPtr & metadata_snapshot_, const NameSet & required_result_columns_ = {}, bool do_global_ = false, const SelectQueryOptions & options_ = {}) - : ExpressionAnalyzer(query_, syntax_analyzer_result_, context_, options_.subquery_depth, do_global_) - , required_result_columns(required_result_columns_), query_options(options_) + : ExpressionAnalyzer(query_, syntax_analyzer_result_, context_, options_.subquery_depth, do_global_) + , metadata_snapshot(metadata_snapshot_) + , required_result_columns(required_result_columns_) + , query_options(options_) { } @@ -260,6 +265,7 @@ class SelectQueryExpressionAnalyzer : public ExpressionAnalyzer void appendProjectResult(ExpressionActionsChain & chain) const; private: + StorageMetadataPtr metadata_snapshot; /// If non-empty, ignore all expressions not from this list. NameSet required_result_columns; SelectQueryOptions query_options; diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index f73245179ce9..331093b9d536 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -130,7 +130,7 @@ String InterpreterSelectQuery::generateFilterActions( /// Using separate expression analyzer to prevent any possible alias injection auto syntax_result = SyntaxAnalyzer(*context).analyzeSelect(query_ast, SyntaxAnalyzerResult({}, storage)); - SelectQueryExpressionAnalyzer analyzer(query_ast, syntax_result, *context); + SelectQueryExpressionAnalyzer analyzer(query_ast, syntax_result, *context, metadata_snapshot); actions = analyzer.simpleSelectActions(); return expr_list->children.at(0)->getColumnName(); @@ -336,7 +336,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( context->getQueryContext().addScalar(it.first, it.second); query_analyzer = std::make_unique( - query_ptr, syntax_analyzer_result, *context, + query_ptr, syntax_analyzer_result, *context, metadata_snapshot, NameSet(required_result_column_names.begin(), required_result_column_names.end()), !options.only_analyze, options); diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index ce47ce6e4762..7bf54d20a619 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -294,8 +294,8 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) throw Exception("Empty mutation commands list", ErrorCodes::LOGICAL_ERROR); - const ColumnsDescription & columns_desc = storage->getColumns(); - const IndicesDescription & indices_desc = storage->getSecondaryIndices(); + const ColumnsDescription & columns_desc = metadata_snapshot->getColumns(); + const IndicesDescription & indices_desc = metadata_snapshot->getSecondaryIndices(); NamesAndTypesList all_columns = columns_desc.getAllPhysical(); NameSet updated_columns; diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 1fb3e0952298..7d50025faff4 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -37,17 +37,6 @@ const ColumnsDescription & IStorage::getColumns() const return metadata->columns; } -const IndicesDescription & IStorage::getSecondaryIndices() const -{ - return metadata->secondary_indices; -} - -bool IStorage::hasSecondaryIndices() const -{ - return !metadata->secondary_indices.empty(); -} - - namespace { #if !defined(ARCADIA_BUILD) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index e45d6a1128b0..78d9b7d2013e 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -140,10 +140,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired const ColumnsDescription & getColumns() const; /// returns combined set of columns - const IndicesDescription & getSecondaryIndices() const; - /// Has at least one non primary index - bool hasSecondaryIndices() const; - /// Storage settings ASTPtr getSettingsChanges() const; @@ -413,7 +409,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo virtual bool supportsIndexForIn() const { return false; } /// Provides a hint that the storage engine may evaluate the IN-condition by using an index. - virtual bool mayBenefitFromIndexForIn(const ASTPtr & /* left_in_operand */, const Context & /* query_context */) const { return false; } + virtual bool mayBenefitFromIndexForIn(const ASTPtr & /* left_in_operand */, const Context & /* query_context */, const StorageMetadataPtr & /* metadata_snapshot */) const { return false; } /// Checks validity of the data virtual CheckResults checkData(const ASTPtr & /* query */, const Context & /* context */) { throw Exception("Check query is not supported for " + getName() + " storage", ErrorCodes::NOT_IMPLEMENTED); } diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 8971b50a0fdc..143ce44da5eb 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -408,14 +408,14 @@ ExpressionActionsPtr getCombinedIndicesExpression( } -ExpressionActionsPtr MergeTreeData::getPrimaryKeyAndSkipIndicesExpression() const +ExpressionActionsPtr MergeTreeData::getPrimaryKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const { - return getCombinedIndicesExpression(getPrimaryKey(), getSecondaryIndices(), getColumns(), global_context); + return getCombinedIndicesExpression(getPrimaryKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); } -ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression() const +ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const { - return getCombinedIndicesExpression(getSortingKey(), getSecondaryIndices(), getColumns(), global_context); + return getCombinedIndicesExpression(getSortingKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); } @@ -1237,9 +1237,10 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S { /// Check that needed transformations can be applied to the list of columns without considering type conversions. StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + StorageInMemoryMetadata old_metadata = getInMemoryMetadata(); commands.apply(new_metadata, global_context); - if (getSecondaryIndices().empty() && !new_metadata.secondary_indices.empty() && - !settings.allow_experimental_data_skipping_indices) + if (old_metadata.getSecondaryIndices().empty() && !new_metadata.secondary_indices.empty() + && !settings.allow_experimental_data_skipping_indices) throw Exception("You must set the setting `allow_experimental_data_skipping_indices` to 1 " \ "before using data skipping indices.", ErrorCodes::BAD_ARGUMENTS); @@ -1259,7 +1260,7 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S columns_alter_type_forbidden.insert(col); } - for (const auto & index : getSecondaryIndices()) + for (const auto & index : old_metadata.getSecondaryIndices()) { for (const String & col : index.expression->getRequiredColumns()) columns_alter_type_forbidden.insert(col); @@ -2932,7 +2933,8 @@ bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const A return false; } -bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context &) const +bool MergeTreeData::mayBenefitFromIndexForIn( + const ASTPtr & left_in_operand, const Context &, const StorageMetadataPtr & metadata_snapshot) const { /// Make sure that the left side of the IN operator contain part of the key. /// If there is a tuple on the left side of the IN operator, at least one item of the tuple @@ -2945,7 +2947,7 @@ bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, con { if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item)) return true; - for (const auto & index : getSecondaryIndices()) + for (const auto & index : metadata_snapshot->getSecondaryIndices()) if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(item)) return true; } @@ -2954,7 +2956,7 @@ bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, con } else { - for (const auto & index : getSecondaryIndices()) + for (const auto & index : metadata_snapshot->getSecondaryIndices()) if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(left_in_operand)) return true; diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index 4be9f450535d..22d2a9da79cf 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -350,7 +350,8 @@ class MergeTreeData : public IStorage bool supportsSettings() const override { return true; } NamesAndTypesList getVirtuals() const override; - bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context &) const override; + bool + mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context &, const StorageMetadataPtr & metadata_snapshot) const override; /// Load the set of data parts from disk. Call once - immediately after the object is created. void loadDataParts(bool skip_sanity_checks); @@ -643,8 +644,8 @@ class MergeTreeData : public IStorage Int64 minmax_idx_date_column_pos = -1; /// In a common case minmax index includes a date column. Int64 minmax_idx_time_column_pos = -1; /// In other cases, minmax index often includes a dateTime column. - ExpressionActionsPtr getPrimaryKeyAndSkipIndicesExpression() const; - ExpressionActionsPtr getSortingKeyAndSkipIndicesExpression() const; + ExpressionActionsPtr getPrimaryKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const; + ExpressionActionsPtr getSortingKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const; std::optional selectTTLEntryForTTLInfos(const IMergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 3bff5c9f5059..afd1586ac6c4 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -612,7 +612,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor NamesAndTypesList merging_columns; Names gathering_column_names, merging_column_names; extractMergingAndGatheringColumns( - storage_columns, data.getSortingKey().expression, data.getSecondaryIndices(), + storage_columns, data.getSortingKey().expression, metadata_snapshot->getSecondaryIndices(), data.merging_params, gathering_columns, gathering_column_names, merging_columns, merging_column_names); auto single_disk_volume = std::make_shared("volume_" + future_part.name, disk); @@ -798,10 +798,10 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor merged_stream = std::make_shared(merged_stream, data, new_data_part, time_of_merge, force_ttl); - if (data.hasSecondaryIndices()) + if (metadata_snapshot->hasSecondaryIndices()) { - const auto & indices = data.getSecondaryIndices(); - merged_stream = std::make_shared(merged_stream, indices.getSingleExpressionForIndices(data.getColumns(), data.global_context)); + const auto & indices = metadata_snapshot->getSecondaryIndices(); + merged_stream = std::make_shared(merged_stream, indices.getSingleExpressionForIndices(metadata_snapshot->getColumns(), data.global_context)); merged_stream = std::make_shared(merged_stream); } @@ -810,7 +810,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor new_data_part, metadata_snapshot, merging_columns, - index_factory.getMany(data.getSecondaryIndices()), + index_factory.getMany(metadata_snapshot->getSecondaryIndices()), compression_codec, merged_column_to_size, data_settings->min_merge_bytes_to_use_direct_io, @@ -1084,7 +1084,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor /// All columns from part are changed and may be some more that were missing before in part if (isCompactPart(source_part) || source_part->getColumns().isSubsetOf(updated_header.getNamesAndTypesList())) { - auto part_indices = getIndicesForNewDataPart(data.getSecondaryIndices(), for_file_renames); + auto part_indices = getIndicesForNewDataPart(metadata_snapshot->getSecondaryIndices(), for_file_renames); mutateAllPartColumns( new_data_part, metadata_snapshot, @@ -1101,7 +1101,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor else /// TODO: check that we modify only non-key columns in this case. { /// We will modify only some of the columns. Other columns and key values can be copied as-is. - auto indices_to_recalc = getIndicesToRecalculate(in, updated_header.getNamesAndTypesList(), context); + auto indices_to_recalc = getIndicesToRecalculate(in, updated_header.getNamesAndTypesList(), metadata_snapshot, context); NameSet files_to_skip = collectFilesToSkip(updated_header, indices_to_recalc, mrk_extension); NameToNameVector files_to_rename = collectFilesForRenames(source_part, for_file_renames, mrk_extension); @@ -1524,6 +1524,7 @@ MergeTreeIndices MergeTreeDataMergerMutator::getIndicesForNewDataPart( std::set MergeTreeDataMergerMutator::getIndicesToRecalculate( BlockInputStreamPtr & input_stream, const NamesAndTypesList & updated_columns, + const StorageMetadataPtr & metadata_snapshot, const Context & context) const { /// Checks if columns used in skipping indexes modified. @@ -1532,7 +1533,7 @@ std::set MergeTreeDataMergerMutator::getIndicesToRecalculate( ASTPtr indices_recalc_expr_list = std::make_shared(); for (const auto & col : updated_columns.getNames()) { - const auto & indices = data.getSecondaryIndices(); + const auto & indices = metadata_snapshot->getSecondaryIndices(); for (size_t i = 0; i < indices.size(); ++i) { const auto & index = indices[i]; @@ -1597,9 +1598,9 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( if (mutating_stream == nullptr) throw Exception("Cannot mutate part columns with uninitialized mutations stream. It's a bug", ErrorCodes::LOGICAL_ERROR); - if (data.hasPrimaryKey() || data.hasSecondaryIndices()) + if (data.hasPrimaryKey() || metadata_snapshot->hasSecondaryIndices()) mutating_stream = std::make_shared( - std::make_shared(mutating_stream, data.getPrimaryKeyAndSkipIndicesExpression())); + std::make_shared(mutating_stream, data.getPrimaryKeyAndSkipIndicesExpression(metadata_snapshot))); if (need_remove_expired_values) mutating_stream = std::make_shared(mutating_stream, data, new_data_part, time_of_mutation, true); diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h index 3625c9bbe265..7828f79ea332 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h @@ -177,6 +177,7 @@ class MergeTreeDataMergerMutator std::set getIndicesToRecalculate( BlockInputStreamPtr & input_stream, const NamesAndTypesList & updated_columns, + const StorageMetadataPtr & metadata_snapshot, const Context & context) const; /// Override all columns of new part using mutating_stream diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index ac2f48511855..9fd020d0317a 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -550,7 +550,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::vector> useful_indices; - for (const auto & index : data.getSecondaryIndices()) + for (const auto & index : metadata_snapshot->getSecondaryIndices()) { auto index_helper = MergeTreeIndexFactory::instance().get(index); auto condition = index_helper->createIndexCondition(query_info, context); diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 71501a0e19ae..284ea02097bf 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -262,8 +262,8 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa new_data_part->volume->getDisk()->createDirectories(full_path); /// If we need to calculate some columns to sort. - if (data.hasSortingKey() || data.hasSecondaryIndices()) - data.getSortingKeyAndSkipIndicesExpression()->execute(block); + if (data.hasSortingKey() || metadata_snapshot->hasSecondaryIndices()) + data.getSortingKeyAndSkipIndicesExpression(metadata_snapshot)->execute(block); Names sort_columns = data.getSortingKeyColumns(); SortDescription sort_description; @@ -302,7 +302,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa auto compression_codec = data.global_context.chooseCompressionCodec(0, 0); const auto & index_factory = MergeTreeIndexFactory::instance(); - MergedBlockOutputStream out(new_data_part, metadata_snapshot, columns, index_factory.getMany(data.getSecondaryIndices()), compression_codec); + MergedBlockOutputStream out(new_data_part, metadata_snapshot, columns, index_factory.getMany(metadata_snapshot->getSecondaryIndices()), compression_codec); out.writePrefix(); out.writeWithPermutation(block, perm_ptr); diff --git a/src/Storages/MergeTree/MergedBlockOutputStream.cpp b/src/Storages/MergeTree/MergedBlockOutputStream.cpp index c768678c4540..9cbdc3383675 100644 --- a/src/Storages/MergeTree/MergedBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergedBlockOutputStream.cpp @@ -164,7 +164,7 @@ void MergedBlockOutputStream::writeImpl(const Block & block, const IColumn::Perm return; std::unordered_set skip_indexes_column_names_set; - for (const auto & index : storage.getSecondaryIndices()) + for (const auto & index : metadata_snapshot->getSecondaryIndices()) std::copy(index.column_names.cbegin(), index.column_names.cend(), std::inserter(skip_indexes_column_names_set, skip_indexes_column_names_set.end())); Names skip_indexes_column_names(skip_indexes_column_names_set.begin(), skip_indexes_column_names_set.end()); diff --git a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h index 45ee947b81fc..17891fde34af 100644 --- a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h +++ b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h @@ -35,9 +35,10 @@ class StorageFromMergeTreeDataPart final : public ext::shared_ptr_helperstorage.mayBenefitFromIndexForIn(left_in_operand, query_context); + return part->storage.mayBenefitFromIndexForIn(left_in_operand, query_context, metadata_snapshot); } NamesAndTypesList getVirtuals() const override diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index f8df14aa4820..4882b5fdc1c1 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -447,7 +447,8 @@ BlockOutputStreamPtr StorageBuffer::write(const ASTPtr & /*query*/, const Storag } -bool StorageBuffer::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const +bool StorageBuffer::mayBenefitFromIndexForIn( + const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & /*metadata_snapshot*/) const { if (!destination_id) return false; @@ -457,7 +458,8 @@ bool StorageBuffer::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, con if (destination.get() == this) throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP); - return destination->mayBenefitFromIndexForIn(left_in_operand, query_context); + /// TODO alesap (check destination metadata) + return destination->mayBenefitFromIndexForIn(left_in_operand, query_context, destination->getInMemoryMetadataPtr()); } diff --git a/src/Storages/StorageBuffer.h b/src/Storages/StorageBuffer.h index 7cd73dc556c0..403b6c53172c 100644 --- a/src/Storages/StorageBuffer.h +++ b/src/Storages/StorageBuffer.h @@ -84,7 +84,7 @@ friend class BufferBlockOutputStream; bool supportsFinal() const override { return true; } bool supportsIndexForIn() const override { return true; } - bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const override; + bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & metadata_snapshot) const override; void checkAlterIsPossible(const AlterCommands & commands, const Settings & /* settings */) const override; diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index 672be800c8f6..ef895ff01659 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -26,9 +26,11 @@ class StorageMaterializedView final : public ext::shared_ptr_helpersupportsFinal(); } bool supportsIndexForIn() const override { return getTargetTable()->supportsIndexForIn(); } bool supportsParallelInsert() const override { return getTargetTable()->supportsParallelInsert(); } - bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const override + bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & /* metadata_snapshot */) const override { - return getTargetTable()->mayBenefitFromIndexForIn(left_in_operand, query_context); + auto target_table = getTargetTable(); + auto metadata_snapshot = target_table->getInMemoryMetadataPtr(); + return target_table->mayBenefitFromIndexForIn(left_in_operand, query_context, metadata_snapshot); } BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index e24e5986994a..e47cde8de529 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -81,7 +81,7 @@ bool StorageMerge::isRemote() const } -bool StorageMerge::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const +bool StorageMerge::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & /*metadata_snapshot*/) const { /// It's beneficial if it is true for at least one table. StorageListWithLocks selected_tables = getSelectedTables( @@ -90,7 +90,9 @@ bool StorageMerge::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, cons size_t i = 0; for (const auto & table : selected_tables) { - if (std::get<0>(table)->mayBenefitFromIndexForIn(left_in_operand, query_context)) + auto storage_ptr = std::get<0>(table); + auto metadata_snapshot = storage_ptr->getInMemoryMetadataPtr(); + if (storage_ptr->mayBenefitFromIndexForIn(left_in_operand, query_context, metadata_snapshot)) return true; ++i; diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index 14bf83f85348..1ad22869e397 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -44,7 +44,8 @@ class StorageMerge final : public ext::shared_ptr_helper, public I /// the structure of sub-tables is not checked void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; - bool mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, const Context & query_context) const override; + bool mayBenefitFromIndexForIn( + const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & metadata_snapshot) const override; private: String source_database; From ab61abccc1eb3901dc3154010add03e58caf3958 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 13:34:23 +0300 Subject: [PATCH 0361/1102] Partition key in StorageInMemoryMetadata --- src/Interpreters/MutationsInterpreter.cpp | 15 +++--- src/Storages/IStorage.cpp | 22 --------- src/Storages/IStorage.h | 12 ----- src/Storages/MergeTree/IMergeTreeDataPart.cpp | 8 ++-- .../MergeTree/MergeTreeBlockOutputStream.cpp | 2 +- src/Storages/MergeTree/MergeTreeData.cpp | 46 ++++++++++--------- src/Storages/MergeTree/MergeTreeData.h | 12 ++--- .../MergeTree/MergeTreeDataWriter.cpp | 8 ++-- src/Storages/MergeTree/MergeTreeDataWriter.h | 2 +- src/Storages/MergeTree/MergeTreePartition.cpp | 11 +++-- .../ReplicatedMergeTreeBlockOutputStream.cpp | 2 +- .../ReplicatedMergeTreeTableMetadata.cpp | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 21 +++++++++ src/Storages/StorageInMemoryMetadata.h | 11 +++++ src/Storages/StorageMergeTree.cpp | 14 ++++-- src/Storages/StorageReplicatedMergeTree.cpp | 19 ++++++-- src/Storages/System/StorageSystemColumns.cpp | 6 +-- src/Storages/System/StorageSystemTables.cpp | 3 +- 18 files changed, 118 insertions(+), 98 deletions(-) diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 7bf54d20a619..50b68ba7ca30 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -214,7 +214,7 @@ MutationsInterpreter::MutationsInterpreter( select_interpreter = std::make_unique(mutation_ast, context, storage, limits); } -static NameSet getKeyColumns(const StoragePtr & storage) +static NameSet getKeyColumns(const StoragePtr & storage, const StorageMetadataPtr & metadata_snapshot) { const MergeTreeData * merge_tree_data = dynamic_cast(storage.get()); if (!merge_tree_data) @@ -222,7 +222,7 @@ static NameSet getKeyColumns(const StoragePtr & storage) NameSet key_columns; - for (const String & col : merge_tree_data->getColumnsRequiredForPartitionKey()) + for (const String & col : metadata_snapshot->getColumnsRequiredForPartitionKey()) key_columns.insert(col); for (const String & col : merge_tree_data->getColumnsRequiredForSortingKey()) @@ -239,15 +239,16 @@ static NameSet getKeyColumns(const StoragePtr & storage) } static void validateUpdateColumns( - const StoragePtr & storage, const NameSet & updated_columns, + const StoragePtr & storage, + const StorageMetadataPtr & metadata_snapshot, const NameSet & updated_columns, const std::unordered_map & column_to_affected_materialized) { - NameSet key_columns = getKeyColumns(storage); + NameSet key_columns = getKeyColumns(storage, metadata_snapshot); for (const String & column_name : updated_columns) { auto found = false; - for (const auto & col : storage->getColumns().getOrdinary()) + for (const auto & col : metadata_snapshot->getColumns().getOrdinary()) { if (col.name == column_name) { @@ -258,7 +259,7 @@ static void validateUpdateColumns( if (!found) { - for (const auto & col : storage->getColumns().getMaterialized()) + for (const auto & col : metadata_snapshot->getColumns().getMaterialized()) { if (col.name == column_name) throw Exception("Cannot UPDATE materialized column " + backQuote(column_name), ErrorCodes::CANNOT_UPDATE_COLUMN); @@ -326,7 +327,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } } - validateUpdateColumns(storage, updated_columns, column_to_affected_materialized); + validateUpdateColumns(storage, metadata_snapshot, updated_columns, column_to_affected_materialized); } /// Columns, that we need to read for calculation of skip indices or TTL expressions. diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 7d50025faff4..84afd2fcf1ce 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,28 +319,6 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -const KeyDescription & IStorage::getPartitionKey() const -{ - return metadata->partition_key; -} - -bool IStorage::isPartitionKeyDefined() const -{ - return metadata->partition_key.definition_ast != nullptr; -} - -bool IStorage::hasPartitionKey() const -{ - return !metadata->partition_key.column_names.empty(); -} - -Names IStorage::getColumnsRequiredForPartitionKey() const -{ - if (hasPartitionKey()) - return metadata->partition_key.expression->getRequiredColumns(); - return {}; -} - const KeyDescription & IStorage::getSortingKey() const { return metadata->sorting_key; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 78d9b7d2013e..5f08d48d4b04 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -427,18 +427,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns data paths if storage supports it, empty vector otherwise. virtual Strings getDataPaths() const { return {}; } - /// Returns structure with partition key. - const KeyDescription & getPartitionKey() const; - /// Returns ASTExpressionList of partition key expression for storage or nullptr if there is none. - ASTPtr getPartitionKeyAST() const { return metadata->partition_key.definition_ast; } - /// Storage has user-defined (in CREATE query) partition key. - bool isPartitionKeyDefined() const; - /// Storage has partition key. - bool hasPartitionKey() const; - /// Returns column names that need to be read to calculate partition key. - Names getColumnsRequiredForPartitionKey() const; - - /// Returns structure with sorting key. const KeyDescription & getSortingKey() const; /// Returns ASTExpressionList of sorting key expression for storage or nullptr if there is none. diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.cpp b/src/Storages/MergeTree/IMergeTreeDataPart.cpp index 287bf916c194..03b2dea23ba1 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPart.cpp @@ -496,7 +496,8 @@ void IMergeTreeDataPart::loadPartitionAndMinMaxIndex() minmax_idx.load(storage, volume->getDisk(), path); } - String calculated_partition_id = partition.getID(storage.getPartitionKey().sample_block); + auto metadata_snapshot = storage.getInMemoryMetadataPtr(); + String calculated_partition_id = partition.getID(metadata_snapshot->getPartitionKey().sample_block); if (calculated_partition_id != info.partition_id) throw Exception( "While loading part " + getFullPath() + ": calculated partition ID: " + calculated_partition_id @@ -840,6 +841,7 @@ void IMergeTreeDataPart::checkConsistencyBase() const { String path = getFullRelativePath(); + auto metadata_snapshot = storage.getInMemoryMetadataPtr(); const auto & pk = storage.getPrimaryKey(); if (!checksums.empty()) { @@ -851,7 +853,7 @@ void IMergeTreeDataPart::checkConsistencyBase() const if (!checksums.files.count("count.txt")) throw Exception("No checksum for count.txt", ErrorCodes::NO_FILE_IN_DATA_PART); - if (storage.hasPartitionKey() && !checksums.files.count("partition.dat")) + if (metadata_snapshot->hasPartitionKey() && !checksums.files.count("partition.dat")) throw Exception("No checksum for partition.dat", ErrorCodes::NO_FILE_IN_DATA_PART); if (!isEmpty()) @@ -884,7 +886,7 @@ void IMergeTreeDataPart::checkConsistencyBase() const { check_file_not_empty(volume->getDisk(), path + "count.txt"); - if (storage.hasPartitionKey()) + if (metadata_snapshot->hasPartitionKey()) check_file_not_empty(volume->getDisk(), path + "partition.dat"); for (const String & col_name : storage.minmax_idx_columns) diff --git a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp index 1ea6b049bf68..5f774a97bceb 100644 --- a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp @@ -16,7 +16,7 @@ void MergeTreeBlockOutputStream::write(const Block & block) { storage.delayInsertOrThrowIfNeeded(); - auto part_blocks = storage.writer.splitBlockIntoParts(block, max_parts_per_block); + auto part_blocks = storage.writer.splitBlockIntoParts(block, max_parts_per_block, metadata_snapshot); for (auto & current_block : part_blocks) { Stopwatch watch; diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 143ce44da5eb..b7e152fe6b4b 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -169,7 +169,7 @@ MergeTreeData::MergeTreeData( min_format_version = MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING; } - setProperties(metadata_, attach); + setProperties(metadata_, metadata_, attach); const auto settings = getSettings(); /// NOTE: using the same columns list as is read when performing actual merges. @@ -184,7 +184,7 @@ MergeTreeData::MergeTreeData( } - setTTLExpressions(metadata_); + setTTLExpressions(metadata_, metadata_); /// format_file always contained on any data path PathWithDisk version_file; @@ -274,7 +274,7 @@ static void checkKeyExpression(const ExpressionActions & expr, const Block & sam } } -void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata, bool attach) const +void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & /*old_metadata*/, bool attach) const { if (!new_metadata.sorting_key.definition_ast) throw Exception("ORDER BY cannot be empty", ErrorCodes::BAD_ARGUMENTS); @@ -381,9 +381,9 @@ void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata } -void MergeTreeData::setProperties(const StorageInMemoryMetadata & new_metadata, bool attach) +void MergeTreeData::setProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata, bool attach) { - checkProperties(new_metadata, attach); + checkProperties(new_metadata, old_metadata, attach); setInMemoryMetadata(new_metadata); } @@ -475,7 +475,7 @@ void MergeTreeData::initPartitionKey(const KeyDescription & new_partition_key) } -void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_metadata) const +void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) const { auto new_column_ttls = new_metadata.column_ttls_by_name; @@ -483,8 +483,8 @@ void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_meta { NameSet columns_ttl_forbidden; - if (hasPartitionKey()) - for (const auto & col : getColumnsRequiredForPartitionKey()) + if (old_metadata.hasPartitionKey()) + for (const auto & col : old_metadata.getColumnsRequiredForPartitionKey()) columns_ttl_forbidden.insert(col); if (hasSortingKey()) @@ -517,9 +517,9 @@ void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_meta } /// Todo replace columns with TTL for columns -void MergeTreeData::setTTLExpressions(const StorageInMemoryMetadata & new_metadata) +void MergeTreeData::setTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) { - checkTTLExpressions(new_metadata); + checkTTLExpressions(new_metadata, old_metadata); //setColumnTTLs(new_metadata.column_ttls_by_name); //setTableTTLs(new_metadata.table_ttl); } @@ -1251,12 +1251,12 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S /// (and not as a part of some expression) and if the ALTER only affects column metadata. NameSet columns_alter_type_metadata_only; - if (hasPartitionKey()) + if (old_metadata.hasPartitionKey()) { /// Forbid altering partition key columns because it can change partition ID format. /// TODO: in some cases (e.g. adding an Enum value) a partition key column can still be ALTERed. /// We should allow it. - for (const String & col : getColumnsRequiredForPartitionKey()) + for (const String & col : old_metadata.getColumnsRequiredForPartitionKey()) columns_alter_type_forbidden.insert(col); } @@ -1284,7 +1284,7 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S columns_alter_type_forbidden.insert(merging_params.sign_column); std::map old_types; - for (const auto & column : getColumns().getAllPhysical()) + for (const auto & column : old_metadata.getColumns().getAllPhysical()) old_types.emplace(column.name, column.type.get()); @@ -1329,9 +1329,9 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S } } - checkProperties(new_metadata); + checkProperties(new_metadata, old_metadata); - checkTTLExpressions(new_metadata); + checkTTLExpressions(new_metadata, old_metadata); if (hasSettingsChanges()) { @@ -2450,7 +2450,8 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, const Context /// Re-parse partition key fields using the information about expected field types. - size_t fields_count = getPartitionKey().sample_block.columns(); + auto metadata_snapshot = getInMemoryMetadataPtr(); + size_t fields_count = metadata_snapshot->getPartitionKey().sample_block.columns(); if (partition_ast.fields_count != fields_count) throw Exception( "Wrong number of fields in the partition expression: " + toString(partition_ast.fields_count) + @@ -2467,7 +2468,7 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, const Context ReadBufferFromMemory right_paren_buf(")", 1); ConcatReadBuffer buf({&left_paren_buf, &fields_buf, &right_paren_buf}); - auto input_stream = FormatFactory::instance().getInput("Values", buf, getPartitionKey().sample_block, context, context.getSettingsRef().max_block_size); + auto input_stream = FormatFactory::instance().getInput("Values", buf, metadata_snapshot->getPartitionKey().sample_block, context, context.getSettingsRef().max_block_size); auto block = input_stream->read(); if (!block || !block.rows()) @@ -2964,7 +2965,7 @@ bool MergeTreeData::mayBenefitFromIndexForIn( } } -MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & source_table) const +MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & source_table, const StorageMetadataPtr & src_snapshot, const StorageMetadataPtr & my_snapshot) const { MergeTreeData * src_data = dynamic_cast(&source_table); if (!src_data) @@ -2972,7 +2973,7 @@ MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & sour " supports attachPartitionFrom only for MergeTree family of table engines." " Got " + source_table.getName(), ErrorCodes::NOT_IMPLEMENTED); - if (getColumns().getAllPhysical().sizeOfDifference(src_data->getColumns().getAllPhysical())) + if (my_snapshot->getColumns().getAllPhysical().sizeOfDifference(src_snapshot->getColumns().getAllPhysical())) throw Exception("Tables have different structure", ErrorCodes::INCOMPATIBLE_COLUMNS); auto query_to_string = [] (const ASTPtr & ast) @@ -2983,7 +2984,7 @@ MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & sour if (query_to_string(getSortingKeyAST()) != query_to_string(src_data->getSortingKeyAST())) throw Exception("Tables have different ordering", ErrorCodes::BAD_ARGUMENTS); - if (query_to_string(getPartitionKeyAST()) != query_to_string(src_data->getPartitionKeyAST())) + if (query_to_string(my_snapshot->getPartitionKeyAST()) != query_to_string(src_snapshot->getPartitionKeyAST())) throw Exception("Tables have different partition key", ErrorCodes::BAD_ARGUMENTS); if (format_version != src_data->format_version) @@ -2992,9 +2993,10 @@ MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & sour return *src_data; } -MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(const StoragePtr & source_table) const +MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData( + const StoragePtr & source_table, const StorageMetadataPtr & src_snapshot, const StorageMetadataPtr & my_snapshot) const { - return checkStructureAndGetMergeTreeData(*source_table); + return checkStructureAndGetMergeTreeData(*source_table, src_snapshot, my_snapshot); } MergeTreeData::MutableDataPartPtr MergeTreeData::cloneAndLoadDataPartOnSameDisk(const MergeTreeData::DataPartPtr & src_part, diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index 22d2a9da79cf..863b5ba16445 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -556,8 +556,8 @@ class MergeTreeData : public IStorage /// Extracts MergeTreeData of other *MergeTree* storage /// and checks that their structure suitable for ALTER TABLE ATTACH PARTITION FROM /// Tables structure should be locked. - MergeTreeData & checkStructureAndGetMergeTreeData(const StoragePtr & source_table) const; - MergeTreeData & checkStructureAndGetMergeTreeData(IStorage & source_table) const; + MergeTreeData & checkStructureAndGetMergeTreeData(const StoragePtr & source_table, const StorageMetadataPtr & src_snapshot, const StorageMetadataPtr & my_snapshot) const; + MergeTreeData & checkStructureAndGetMergeTreeData(IStorage & source_table, const StorageMetadataPtr & src_snapshot, const StorageMetadataPtr & my_snapshot) const; MergeTreeData::MutableDataPartPtr cloneAndLoadDataPartOnSameDisk( const MergeTreeData::DataPartPtr & src_part, const String & tmp_part_prefix, const MergeTreePartInfo & dst_part_info); @@ -781,14 +781,14 @@ class MergeTreeData : public IStorage /// The same for clearOldTemporaryDirectories. std::mutex clear_old_temporary_directories_mutex; - void checkProperties(const StorageInMemoryMetadata & new_metadata, bool attach = false) const; + void checkProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata, bool attach = false) const; - void setProperties(const StorageInMemoryMetadata & new_metadata, bool attach = false); + void setProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata, bool attach = false); void initPartitionKey(const KeyDescription & new_partition_key); - void checkTTLExpressions(const StorageInMemoryMetadata & new_metadata) const; - void setTTLExpressions(const StorageInMemoryMetadata & new_metadata); + void checkTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) const; + void setTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata); void checkStoragePolicy(const StoragePolicyPtr & new_storage_policy) const; diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 284ea02097bf..f96c9b48c4d2 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -132,7 +132,7 @@ void updateTTL( } -BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block, size_t max_parts) +BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block, size_t max_parts, const StorageMetadataPtr & metadata_snapshot) { BlocksWithPartition result; if (!block || !block.rows()) @@ -140,14 +140,14 @@ BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block data.check(block, true); - if (!data.hasPartitionKey()) /// Table is not partitioned. + if (!metadata_snapshot->hasPartitionKey()) /// Table is not partitioned. { result.emplace_back(Block(block), Row()); return result; } Block block_copy = block; - const auto & partition_key = data.getPartitionKey(); + const auto & partition_key = metadata_snapshot->getPartitionKey(); partition_key.expression->execute(block_copy); ColumnRawPtrs partition_columns; @@ -206,7 +206,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa MergeTreePartition partition(std::move(block_with_partition.partition)); - MergeTreePartInfo new_part_info(partition.getID(data.getPartitionKey().sample_block), temp_index, temp_index, 0); + MergeTreePartInfo new_part_info(partition.getID(metadata_snapshot->getPartitionKey().sample_block), temp_index, temp_index, 0); String part_name; if (data.format_version < MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING) { diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.h b/src/Storages/MergeTree/MergeTreeDataWriter.h index dabdcbd21488..c04a09185c59 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.h +++ b/src/Storages/MergeTree/MergeTreeDataWriter.h @@ -40,7 +40,7 @@ class MergeTreeDataWriter * (split rows by partition) * Works deterministically: if same block was passed, function will return same result in same order. */ - BlocksWithPartition splitBlockIntoParts(const Block & block, size_t max_parts); + BlocksWithPartition splitBlockIntoParts(const Block & block, size_t max_parts, const StorageMetadataPtr & metadata_snapshot); /** All rows must correspond to same partition. * Returns part with unique name starting with 'tmp_', yet not added to MergeTreeData. diff --git a/src/Storages/MergeTree/MergeTreePartition.cpp b/src/Storages/MergeTree/MergeTreePartition.cpp index 54e213fafacc..cd73e9228fde 100644 --- a/src/Storages/MergeTree/MergeTreePartition.cpp +++ b/src/Storages/MergeTree/MergeTreePartition.cpp @@ -26,7 +26,7 @@ static std::unique_ptr openForReading(const DiskPtr & di String MergeTreePartition::getID(const MergeTreeData & storage) const { - return getID(storage.getPartitionKey().sample_block); + return getID(storage.getInMemoryMetadataPtr()->getPartitionKey().sample_block); } /// NOTE: This ID is used to create part names which are then persisted in ZK and as directory names on the file system. @@ -89,7 +89,7 @@ String MergeTreePartition::getID(const Block & partition_key_sample) const void MergeTreePartition::serializeText(const MergeTreeData & storage, WriteBuffer & out, const FormatSettings & format_settings) const { - const auto & partition_key_sample = storage.getPartitionKey().sample_block; + const auto & partition_key_sample = storage.getInMemoryMetadataPtr()->getPartitionKey().sample_block; size_t key_size = partition_key_sample.columns(); if (key_size == 0) @@ -124,10 +124,11 @@ void MergeTreePartition::serializeText(const MergeTreeData & storage, WriteBuffe void MergeTreePartition::load(const MergeTreeData & storage, const DiskPtr & disk, const String & part_path) { - if (!storage.hasPartitionKey()) + auto metadata_snapshot = storage.getInMemoryMetadataPtr(); + if (!metadata_snapshot->hasPartitionKey()) return; - const auto & partition_key_sample = storage.getPartitionKey().sample_block; + const auto & partition_key_sample = metadata_snapshot->getPartitionKey().sample_block; auto partition_file_path = part_path + "partition.dat"; auto file = openForReading(disk, partition_file_path); value.resize(partition_key_sample.columns()); @@ -137,7 +138,7 @@ void MergeTreePartition::load(const MergeTreeData & storage, const DiskPtr & dis void MergeTreePartition::store(const MergeTreeData & storage, const DiskPtr & disk, const String & part_path, MergeTreeDataPartChecksums & checksums) const { - store(storage.getPartitionKey().sample_block, disk, part_path, checksums); + store(storage.getInMemoryMetadataPtr()->getPartitionKey().sample_block, disk, part_path, checksums); } void MergeTreePartition::store(const Block & partition_key_sample, const DiskPtr & disk, const String & part_path, MergeTreeDataPartChecksums & checksums) const diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp index 8319b0e018dc..13df5ef23f18 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp @@ -130,7 +130,7 @@ void ReplicatedMergeTreeBlockOutputStream::write(const Block & block) if (quorum) checkQuorumPrecondition(zookeeper); - auto part_blocks = storage.writer.splitBlockIntoParts(block, max_parts_per_block); + auto part_blocks = storage.writer.splitBlockIntoParts(block, max_parts_per_block, metadata_snapshot); for (auto & current_block : part_blocks) { diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index 820f41326f18..1f62fba03a08 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -51,7 +51,7 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr data_format_version = data.format_version; if (data.format_version >= MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING) - partition_key = formattedAST(data.getPartitionKey().expression_list_ast); + partition_key = formattedAST(metadata_snapshot->getPartitionKey().expression_list_ast); ttl_table = formattedAST(metadata_snapshot->getTableTTLs().definition_ast); diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 359d561cd1fd..8fbe415ead65 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -291,4 +291,25 @@ Block StorageInMemoryMetadata::getSampleBlockForColumns(const Names & column_nam return res; } +const KeyDescription & StorageInMemoryMetadata::getPartitionKey() const +{ + return partition_key; +} + +bool StorageInMemoryMetadata::isPartitionKeyDefined() const +{ + return partition_key.definition_ast != nullptr; +} + +bool StorageInMemoryMetadata::hasPartitionKey() const +{ + return !partition_key.column_names.empty(); +} + +Names StorageInMemoryMetadata::getColumnsRequiredForPartitionKey() const +{ + if (hasPartitionKey()) + return partition_key.expression->getRequiredColumns(); + return {}; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 9f9154c48fb8..8996f9fc1b93 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -112,6 +112,17 @@ struct StorageInMemoryMetadata Block getSampleBlockWithVirtuals(const NamesAndTypesList & virtuals) const; /// ordinary + materialized + virtuals. Block getSampleBlockForColumns( const Names & column_names, const NamesAndTypesList & virtuals) const; /// ordinary + materialized + aliases + virtuals. + + /// Returns structure with partition key. + const KeyDescription & getPartitionKey() const; + /// Returns ASTExpressionList of partition key expression for storage or nullptr if there is none. + ASTPtr getPartitionKeyAST() const { return partition_key.definition_ast; } + /// Storage has user-defined (in CREATE query) partition key. + bool isPartitionKeyDefined() const; + /// Storage has partition key. + bool hasPartitionKey() const; + /// Returns column names that need to be read to calculate partition key. + Names getColumnsRequiredForPartitionKey() const; }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index cfa5c34bece2..c13070fa4a5e 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -259,6 +259,7 @@ void StorageMergeTree::alter( auto table_id = getStorageID(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + StorageInMemoryMetadata old_metadata = getInMemoryMetadata(); auto maybe_mutation_commands = commands.getMutationCommands(new_metadata, context.getSettingsRef().materialize_ttl_after_modify, context); String mutation_file_name; Int64 mutation_version = -1; @@ -282,8 +283,8 @@ void StorageMergeTree::alter( changeSettings(new_metadata.settings_changes, table_lock_holder); /// Reinitialize primary key because primary key column types might have changed. - setProperties(new_metadata); - setTTLExpressions(new_metadata); + setProperties(new_metadata, old_metadata); + setTTLExpressions(new_metadata, old_metadata); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); @@ -1151,9 +1152,11 @@ void StorageMergeTree::replacePartitionFrom(const StoragePtr & source_table, con { auto lock1 = lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto lock2 = source_table->lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto source_metadata_snapshot = source_table->getInMemoryMetadataPtr(); + auto my_metadata_snapshot = getInMemoryMetadataPtr(); Stopwatch watch; - MergeTreeData & src_data = checkStructureAndGetMergeTreeData(source_table); + MergeTreeData & src_data = checkStructureAndGetMergeTreeData(source_table, source_metadata_snapshot, my_metadata_snapshot); String partition_id = getPartitionIDFromQuery(partition, context); DataPartsVector src_parts = src_data.getDataPartsVectorInPartition(MergeTreeDataPartState::Committed, partition_id); @@ -1232,9 +1235,12 @@ void StorageMergeTree::movePartitionToTable(const StoragePtr & dest_table, const " should have the same storage policy of source table " + getStorageID().getNameForLogs() + ". " + getStorageID().getNameForLogs() + ": " + this->getStoragePolicy()->getName() + ", " + dest_table_storage->getStorageID().getNameForLogs() + ": " + dest_table_storage->getStoragePolicy()->getName(), ErrorCodes::LOGICAL_ERROR); + + auto dest_metadata_snapshot = dest_table->getInMemoryMetadataPtr(); + auto metadata_snapshot = getInMemoryMetadataPtr(); Stopwatch watch; - MergeTreeData & src_data = dest_table_storage->checkStructureAndGetMergeTreeData(*this); + MergeTreeData & src_data = dest_table_storage->checkStructureAndGetMergeTreeData(*this, metadata_snapshot, dest_metadata_snapshot); String partition_id = getPartitionIDFromQuery(partition, context); DataPartsVector src_parts = src_data.getDataPartsVectorInPartition(MergeTreeDataPartState::Committed, partition_id); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index a7ddf96d08c3..fc1bd8538e59 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -758,6 +758,7 @@ void StorageReplicatedMergeTree::checkTableStructure(const String & zookeeper_pr void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_columns, const ReplicatedMergeTreeTableMetadata::Diff & metadata_diff) { StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + StorageInMemoryMetadata old_metadata = getInMemoryMetadata(); if (new_columns != new_metadata.columns) { new_metadata.columns = new_columns; @@ -820,8 +821,8 @@ void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_column /// Even if the primary/sorting keys didn't change we must reinitialize it /// because primary key column types might have changed. - setProperties(new_metadata); - setTTLExpressions(new_metadata); + setProperties(new_metadata, old_metadata); + setTTLExpressions(new_metadata, old_metadata); } @@ -1794,6 +1795,7 @@ bool StorageReplicatedMergeTree::executeReplaceRange(const LogEntry & entry) auto table_lock_holder_dst_table = lockStructureForShare( false, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto dst_metadata_snapshot = getInMemoryMetadataPtr(); for (size_t i = 0; i < entry_replace.new_part_names.size(); ++i) { @@ -1843,10 +1845,11 @@ bool StorageReplicatedMergeTree::executeReplaceRange(const LogEntry & entry) return 0; } + auto src_metadata_snapshot = source_table->getInMemoryMetadataPtr(); MergeTreeData * src_data = nullptr; try { - src_data = &checkStructureAndGetMergeTreeData(source_table); + src_data = &checkStructureAndGetMergeTreeData(source_table, src_metadata_snapshot, dst_metadata_snapshot); } catch (Exception &) { @@ -5212,8 +5215,11 @@ void StorageReplicatedMergeTree::replacePartitionFrom(const StoragePtr & source_ auto lock1 = lockStructureForShare(true, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto lock2 = source_table->lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto source_metadata_snapshot = source_table->getInMemoryMetadataPtr(); + auto metadata_snapshot = getInMemoryMetadataPtr(); + Stopwatch watch; - MergeTreeData & src_data = checkStructureAndGetMergeTreeData(source_table); + MergeTreeData & src_data = checkStructureAndGetMergeTreeData(source_table, source_metadata_snapshot, metadata_snapshot); String partition_id = getPartitionIDFromQuery(partition, context); DataPartsVector src_all_parts = src_data.getDataPartsVectorInPartition(MergeTreeDataPartState::Committed, partition_id); @@ -5405,8 +5411,11 @@ void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_ta getStorageID().getNameForLogs() + ": " + this->getStoragePolicy()->getName() + ", " + getStorageID().getNameForLogs() + ": " + dest_table_storage->getStoragePolicy()->getName(), ErrorCodes::LOGICAL_ERROR); + auto dest_metadata_snapshot = dest_table->getInMemoryMetadataPtr(); + auto metadata_snapshot = getInMemoryMetadataPtr(); + Stopwatch watch; - MergeTreeData & src_data = dest_table_storage->checkStructureAndGetMergeTreeData(*this); + MergeTreeData & src_data = dest_table_storage->checkStructureAndGetMergeTreeData(*this, metadata_snapshot, dest_metadata_snapshot); auto src_data_id = src_data.getStorageID(); String partition_id = getPartitionIDFromQuery(partition, query_context); diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 14a59da1bf95..5860facc814c 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -122,13 +122,13 @@ class ColumnsSource : public SourceWithProgress throw; } - columns = storage->getColumns(); + auto metadadata_snapshot = storage->getInMemoryMetadataPtr(); + columns = metadadata_snapshot->getColumns(); - cols_required_for_partition_key = storage->getColumnsRequiredForPartitionKey(); + cols_required_for_partition_key = metadadata_snapshot->getColumnsRequiredForPartitionKey(); cols_required_for_sorting_key = storage->getColumnsRequiredForSortingKey(); cols_required_for_primary_key = storage->getColumnsRequiredForPrimaryKey(); cols_required_for_sampling = storage->getColumnsRequiredForSampling(); - column_sizes = storage->getColumnSizes(); } diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index b33886ce179b..78346d1968fd 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -267,6 +267,7 @@ class TablesBlockSource : public SourceWithProgress throw; } } + auto metadata_snapshot = table->getInMemoryMetadataPtr(); ++rows_count; @@ -365,7 +366,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { assert(table != nullptr); - if ((expression_ptr = table->getPartitionKeyAST())) + if ((expression_ptr = metadata_snapshot->getPartitionKeyAST())) res_columns[res_index++]->insert(queryToString(expression_ptr)); else res_columns[res_index++]->insertDefault(); From ba04d02f1e23c6830a8750530d95ad3b73545bb8 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 14:05:11 +0300 Subject: [PATCH 0362/1102] Compilable sorting key in metadata --- src/Interpreters/ExpressionAnalyzer.cpp | 3 +- src/Interpreters/ExpressionAnalyzer.h | 1 + src/Interpreters/InterpreterSelectQuery.cpp | 6 +++- src/Interpreters/MutationsInterpreter.cpp | 4 +-- src/Storages/IStorage.cpp | 29 ------------------ src/Storages/IStorage.h | 16 ---------- src/Storages/MergeTree/MergeTreeData.cpp | 20 ++++++------- .../MergeTree/MergeTreeDataMergerMutator.cpp | 16 ++++++---- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 10 +++---- .../MergeTree/MergeTreeDataWriter.cpp | 4 +-- .../ReplicatedMergeTreeTableMetadata.cpp | 4 +-- src/Storages/ReadInOrderOptimizer.cpp | 10 +++---- src/Storages/ReadInOrderOptimizer.h | 2 +- src/Storages/StorageBuffer.cpp | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 30 +++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 17 +++++++++++ src/Storages/StorageMaterializedView.cpp | 2 +- src/Storages/StorageMerge.cpp | 4 ++- src/Storages/StorageReplicatedMergeTree.cpp | 2 +- src/Storages/System/StorageSystemColumns.cpp | 8 ++--- src/Storages/System/StorageSystemTables.cpp | 2 +- 21 files changed, 104 insertions(+), 88 deletions(-) diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 039001796ccd..28aa42877d6e 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -1011,6 +1011,7 @@ ExpressionActionsPtr SelectQueryExpressionAnalyzer::simpleSelectActions() ExpressionAnalysisResult::ExpressionAnalysisResult( SelectQueryExpressionAnalyzer & query_analyzer, + const StorageMetadataPtr & metadata_snapshot, bool first_stage_, bool second_stage_, bool only_types, @@ -1068,7 +1069,7 @@ ExpressionAnalysisResult::ExpressionAnalysisResult( if (storage && query.final()) { - Names columns_for_final = storage->getColumnsRequiredForFinal(); + Names columns_for_final = metadata_snapshot->getColumnsRequiredForFinal(); additional_required_columns_after_prewhere.insert(additional_required_columns_after_prewhere.end(), columns_for_final.begin(), columns_for_final.end()); } diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index 1cc1b33bad1a..cd0b837b4ec5 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -204,6 +204,7 @@ struct ExpressionAnalysisResult ExpressionAnalysisResult( SelectQueryExpressionAnalyzer & query_analyzer, + const StorageMetadataPtr & metadata_snapshot, bool first_stage, bool second_stage, bool only_types, diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 331093b9d536..038448ef3531 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -492,8 +492,12 @@ Block InterpreterSelectQuery::getSampleBlockImpl() bool second_stage = from_stage <= QueryProcessingStage::WithMergeableState && options.to_stage > QueryProcessingStage::WithMergeableState; + Names columns_required_for_sampling; + Names columns_required_for_; + analysis_result = ExpressionAnalysisResult( *query_analyzer, + metadata_snapshot, first_stage, second_stage, options.only_analyze, @@ -1329,7 +1333,7 @@ void InterpreterSelectQuery::executeFetchColumns( getSortDescriptionFromGroupBy(query), query_info.syntax_analyzer_result); - query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); + query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage, metadata_snapshot); } Pipes pipes = storage->read(required_columns, metadata_snapshot, query_info, *context, processing_stage, max_block_size, max_streams); diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 50b68ba7ca30..0c0227d476fa 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -225,7 +225,7 @@ static NameSet getKeyColumns(const StoragePtr & storage, const StorageMetadataPt for (const String & col : metadata_snapshot->getColumnsRequiredForPartitionKey()) key_columns.insert(col); - for (const String & col : merge_tree_data->getColumnsRequiredForSortingKey()) + for (const String & col : metadata_snapshot->getColumnsRequiredForSortingKey()) key_columns.insert(col); /// We don't process sample_by_ast separately because it must be among the primary key columns. @@ -731,7 +731,7 @@ size_t MutationsInterpreter::evaluateCommandsSize() std::optional MutationsInterpreter::getStorageSortDescriptionIfPossible(const Block & header) const { - Names sort_columns = storage->getSortingKeyColumns(); + Names sort_columns = metadata_snapshot->getSortingKeyColumns(); SortDescription sort_description; size_t sort_columns_size = sort_columns.size(); sort_description.reserve(sort_columns_size); diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 84afd2fcf1ce..0c2ddc09da8a 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,35 +319,6 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -const KeyDescription & IStorage::getSortingKey() const -{ - return metadata->sorting_key; -} - -bool IStorage::isSortingKeyDefined() const -{ - return metadata->sorting_key.definition_ast != nullptr; -} - -bool IStorage::hasSortingKey() const -{ - return !metadata->sorting_key.column_names.empty(); -} - -Names IStorage::getColumnsRequiredForSortingKey() const -{ - if (hasSortingKey()) - return metadata->sorting_key.expression->getRequiredColumns(); - return {}; -} - -Names IStorage::getSortingKeyColumns() const -{ - if (hasSortingKey()) - return metadata->sorting_key.column_names; - return {}; -} - const KeyDescription & IStorage::getPrimaryKey() const { return metadata->primary_key; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 5f08d48d4b04..4e1ca81dd108 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -427,20 +427,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns data paths if storage supports it, empty vector otherwise. virtual Strings getDataPaths() const { return {}; } - /// Returns structure with sorting key. - const KeyDescription & getSortingKey() const; - /// Returns ASTExpressionList of sorting key expression for storage or nullptr if there is none. - ASTPtr getSortingKeyAST() const { return metadata->sorting_key.definition_ast; } - /// Storage has user-defined (in CREATE query) sorting key. - bool isSortingKeyDefined() const; - /// Storage has sorting key. It means, that it contains at least one column. - bool hasSortingKey() const; - /// Returns column names that need to be read to calculate sorting key. - Names getColumnsRequiredForSortingKey() const; - /// Returns columns names in sorting key specified by user in ORDER BY - /// expression. For example: 'a', 'x * y', 'toStartOfMonth(date)', etc. - Names getSortingKeyColumns() const; - /// Returns structure with primary key. const KeyDescription & getPrimaryKey() const; /// Returns ASTExpressionList of primary key expression for storage or nullptr if there is none. @@ -467,8 +453,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns column names that need to be read to calculate sampling key. Names getColumnsRequiredForSampling() const; - /// Returns column names that need to be read for FINAL to work. - Names getColumnsRequiredForFinal() const { return getColumnsRequiredForSortingKey(); } /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index b7e152fe6b4b..a1487b3a1fee 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -274,7 +274,7 @@ static void checkKeyExpression(const ExpressionActions & expr, const Block & sam } } -void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & /*old_metadata*/, bool attach) const +void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata, bool attach) const { if (!new_metadata.sorting_key.definition_ast) throw Exception("ORDER BY cannot be empty", ErrorCodes::BAD_ARGUMENTS); @@ -312,7 +312,7 @@ void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata auto all_columns = new_metadata.columns.getAllPhysical(); /// Order by check AST - if (hasSortingKey()) + if (old_metadata.hasSortingKey()) { /// This is ALTER, not CREATE/ATTACH TABLE. Let us check that all new columns used in the sorting key /// expression have just been added (so that the sorting order is guaranteed to be valid with the new key). @@ -321,7 +321,7 @@ void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata Names new_sorting_key_columns = new_sorting_key.column_names; ASTPtr added_key_column_expr_list = std::make_shared(); - const auto & old_sorting_key_columns = getSortingKeyColumns(); + const auto & old_sorting_key_columns = old_metadata.getSortingKeyColumns(); for (size_t new_i = 0, old_i = 0; new_i < sorting_key_size; ++new_i) { if (old_i < old_sorting_key_columns.size()) @@ -342,7 +342,7 @@ void MergeTreeData::checkProperties(const StorageInMemoryMetadata & new_metadata NamesAndTypesList deleted_columns; NamesAndTypesList added_columns; - getColumns().getAllPhysical().getDifference(all_columns, deleted_columns, added_columns); + old_metadata.getColumns().getAllPhysical().getDifference(all_columns, deleted_columns, added_columns); for (const String & col : used_columns) { @@ -415,7 +415,7 @@ ExpressionActionsPtr MergeTreeData::getPrimaryKeyAndSkipIndicesExpression(const ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const { - return getCombinedIndicesExpression(getSortingKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); + return getCombinedIndicesExpression(metadata_snapshot->getSortingKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); } @@ -487,8 +487,8 @@ void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_meta for (const auto & col : old_metadata.getColumnsRequiredForPartitionKey()) columns_ttl_forbidden.insert(col); - if (hasSortingKey()) - for (const auto & col : getColumnsRequiredForSortingKey()) + if (old_metadata.hasSortingKey()) + for (const auto & col : old_metadata.getColumnsRequiredForSortingKey()) columns_ttl_forbidden.insert(col); for (const auto & [name, ttl_description] : new_column_ttls) @@ -1266,9 +1266,9 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S columns_alter_type_forbidden.insert(col); } - if (hasSortingKey()) + if (old_metadata.hasSortingKey()) { - auto sorting_key_expr = getSortingKey().expression; + auto sorting_key_expr = old_metadata.getSortingKey().expression; for (const ExpressionAction & action : sorting_key_expr->getActions()) { auto action_columns = action.getNeededColumns(); @@ -2981,7 +2981,7 @@ MergeTreeData & MergeTreeData::checkStructureAndGetMergeTreeData(IStorage & sour return ast ? queryToString(ast) : ""; }; - if (query_to_string(getSortingKeyAST()) != query_to_string(src_data->getSortingKeyAST())) + if (query_to_string(my_snapshot->getSortingKeyAST()) != query_to_string(src_snapshot->getSortingKeyAST())) throw Exception("Tables have different ordering", ErrorCodes::BAD_ARGUMENTS); if (query_to_string(my_snapshot->getPartitionKeyAST()) != query_to_string(src_snapshot->getPartitionKeyAST())) diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index afd1586ac6c4..fb08f379ee33 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -612,8 +612,14 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor NamesAndTypesList merging_columns; Names gathering_column_names, merging_column_names; extractMergingAndGatheringColumns( - storage_columns, data.getSortingKey().expression, metadata_snapshot->getSecondaryIndices(), - data.merging_params, gathering_columns, gathering_column_names, merging_columns, merging_column_names); + storage_columns, + metadata_snapshot->getSortingKey().expression, + metadata_snapshot->getSecondaryIndices(), + data.merging_params, + gathering_columns, + gathering_column_names, + merging_columns, + merging_column_names); auto single_disk_volume = std::make_shared("volume_" + future_part.name, disk); MergeTreeData::MutableDataPartPtr new_data_part = data.createPart( @@ -719,16 +725,16 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor Pipe pipe(std::move(input)); - if (data.hasSortingKey()) + if (metadata_snapshot->hasSortingKey()) { - auto expr = std::make_shared(pipe.getHeader(), data.getSortingKey().expression); + auto expr = std::make_shared(pipe.getHeader(), metadata_snapshot->getSortingKey().expression); pipe.addSimpleTransform(std::move(expr)); } pipes.emplace_back(std::move(pipe)); } - Names sort_columns = data.getSortingKeyColumns(); + Names sort_columns = metadata_snapshot->getSortingKeyColumns(); SortDescription sort_description; size_t sort_columns_size = sort_columns.size(); sort_description.reserve(sort_columns_size); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 9fd020d0317a..9d198c7ed659 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -617,7 +617,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( if (select.final()) { /// Add columns needed to calculate the sorting expression and the sign. - std::vector add_columns = data.getColumnsRequiredForSortingKey(); + std::vector add_columns = metadata_snapshot->getColumnsRequiredForSortingKey(); column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); if (!data.merging_params.sign_column.empty()) @@ -644,7 +644,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order) && query_info.input_order_info) { size_t prefix_size = query_info.input_order_info->order_key_prefix_descr.size(); - auto order_key_prefix_ast = data.getSortingKey().expression_list_ast->clone(); + auto order_key_prefix_ast = metadata_snapshot->getSortingKey().expression_list_ast->clone(); order_key_prefix_ast->children.resize(prefix_size); auto syntax_result = SyntaxAnalyzer(context).analyze(order_key_prefix_ast, data.getColumns().getAllPhysical()); @@ -1064,7 +1064,7 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( { SortDescription sort_description; for (size_t j = 0; j < input_order_info->order_key_prefix_descr.size(); ++j) - sort_description.emplace_back(data.getSortingKey().column_names[j], + sort_description.emplace_back(metadata_snapshot->getSortingKey().column_names[j], input_order_info->direction, 1); /// Drop temporary columns, added by 'sorting_key_prefix_expr' @@ -1138,11 +1138,11 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsFinal( if (!out_projection) out_projection = createProjection(pipe, data); - pipe.addSimpleTransform(std::make_shared(pipe.getHeader(), data.getSortingKey().expression)); + pipe.addSimpleTransform(std::make_shared(pipe.getHeader(), metadata_snapshot->getSortingKey().expression)); pipes.emplace_back(std::move(pipe)); } - Names sort_columns = data.getSortingKeyColumns(); + Names sort_columns = metadata_snapshot->getSortingKeyColumns(); SortDescription sort_description; size_t sort_columns_size = sort_columns.size(); sort_description.reserve(sort_columns_size); diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index f96c9b48c4d2..c31cfd3da6f7 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -262,10 +262,10 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa new_data_part->volume->getDisk()->createDirectories(full_path); /// If we need to calculate some columns to sort. - if (data.hasSortingKey() || metadata_snapshot->hasSecondaryIndices()) + if (metadata_snapshot->hasSortingKey() || metadata_snapshot->hasSecondaryIndices()) data.getSortingKeyAndSkipIndicesExpression(metadata_snapshot)->execute(block); - Names sort_columns = data.getSortingKeyColumns(); + Names sort_columns = metadata_snapshot->getSortingKeyColumns(); SortDescription sort_description; size_t sort_columns_size = sort_columns.size(); sort_description.reserve(sort_columns_size); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index 1f62fba03a08..81366db5b2ab 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -41,11 +41,11 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr /// - When we have only ORDER BY, than store it in "primary key:" row of /metadata /// - When we have both, than store PRIMARY KEY in "primary key:" row and ORDER BY in "sorting key:" row of /metadata if (!data.isPrimaryKeyDefined()) - primary_key = formattedAST(data.getSortingKey().expression_list_ast); + primary_key = formattedAST(metadata_snapshot->getSortingKey().expression_list_ast); else { primary_key = formattedAST(data.getPrimaryKey().expression_list_ast); - sorting_key = formattedAST(data.getSortingKey().expression_list_ast); + sorting_key = formattedAST(metadata_snapshot->getSortingKey().expression_list_ast); } data_format_version = data.format_version; diff --git a/src/Storages/ReadInOrderOptimizer.cpp b/src/Storages/ReadInOrderOptimizer.cpp index bfdbd7ef5578..a6cc6211788b 100644 --- a/src/Storages/ReadInOrderOptimizer.cpp +++ b/src/Storages/ReadInOrderOptimizer.cpp @@ -30,20 +30,20 @@ ReadInOrderOptimizer::ReadInOrderOptimizer( forbidden_columns.insert(elem.first); } -InputOrderInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & storage) const +InputOrderInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & storage, const StorageMetadataPtr & metadata_snapshot) const { Names sorting_key_columns; if (const auto * merge_tree = dynamic_cast(storage.get())) { - if (!merge_tree->hasSortingKey()) + if (!metadata_snapshot->hasSortingKey()) return {}; - sorting_key_columns = merge_tree->getSortingKeyColumns(); + sorting_key_columns = metadata_snapshot->getSortingKeyColumns(); } else if (const auto * part = dynamic_cast(storage.get())) { - if (!part->hasSortingKey()) + if (!metadata_snapshot->hasSortingKey()) return {}; - sorting_key_columns = part->getSortingKeyColumns(); + sorting_key_columns = metadata_snapshot->getSortingKeyColumns(); } else /// Inapplicable storage type { diff --git a/src/Storages/ReadInOrderOptimizer.h b/src/Storages/ReadInOrderOptimizer.h index de858e8fd92c..f2a3e448f50f 100644 --- a/src/Storages/ReadInOrderOptimizer.h +++ b/src/Storages/ReadInOrderOptimizer.h @@ -20,7 +20,7 @@ class ReadInOrderOptimizer const SortDescription & required_sort_description, const SyntaxAnalyzerResultPtr & syntax_result); - InputOrderInfoPtr getInputOrder(const StoragePtr & storage) const; + InputOrderInfoPtr getInputOrder(const StoragePtr & storage, const StorageMetadataPtr & metadata_snapshot) const; private: /// Actions for every element of order expression to analyze functions for monotonicity diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 4882b5fdc1c1..5eaaf98d397f 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -179,7 +179,7 @@ Pipes StorageBuffer::read( if (dst_has_same_structure) { if (query_info.order_optimizer) - query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination); + query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination, metadata_snapshot); /// The destination table has the same structure of the requested columns and we can simply read blocks from there. pipes_from_dst = destination->read( diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 8fbe415ead65..3c7f474c4828 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -312,4 +312,34 @@ Names StorageInMemoryMetadata::getColumnsRequiredForPartitionKey() const return partition_key.expression->getRequiredColumns(); return {}; } + + +const KeyDescription & StorageInMemoryMetadata::getSortingKey() const +{ + return sorting_key; +} + +bool StorageInMemoryMetadata::isSortingKeyDefined() const +{ + return sorting_key.definition_ast != nullptr; +} + +bool StorageInMemoryMetadata::hasSortingKey() const +{ + return !sorting_key.column_names.empty(); +} + +Names StorageInMemoryMetadata::getColumnsRequiredForSortingKey() const +{ + if (hasSortingKey()) + return sorting_key.expression->getRequiredColumns(); + return {}; +} + +Names StorageInMemoryMetadata::getSortingKeyColumns() const +{ + if (hasSortingKey()) + return sorting_key.column_names; + return {}; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 8996f9fc1b93..f73700574105 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -123,6 +123,23 @@ struct StorageInMemoryMetadata bool hasPartitionKey() const; /// Returns column names that need to be read to calculate partition key. Names getColumnsRequiredForPartitionKey() const; + + /// Returns structure with sorting key. + const KeyDescription & getSortingKey() const; + /// Returns ASTExpressionList of sorting key expression for storage or nullptr if there is none. + ASTPtr getSortingKeyAST() const { return sorting_key.definition_ast; } + /// Storage has user-defined (in CREATE query) sorting key. + bool isSortingKeyDefined() const; + /// Storage has sorting key. It means, that it contains at least one column. + bool hasSortingKey() const; + /// Returns column names that need to be read to calculate sorting key. + Names getColumnsRequiredForSortingKey() const; + /// Returns columns names in sorting key specified by user in ORDER BY + /// expression. For example: 'a', 'x * y', 'toStartOfMonth(date)', etc. + Names getSortingKeyColumns() const; + + /// Returns column names that need to be read for FINAL to work. + Names getColumnsRequiredForFinal() const { return getColumnsRequiredForSortingKey(); } }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index e96a48efc9e7..4eba4d6a165b 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -121,7 +121,7 @@ Pipes StorageMaterializedView::read( auto metadata_snapshot = storage->getInMemoryMetadataPtr(); if (query_info.order_optimizer) - query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); + query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage, metadata_snapshot); Pipes pipes = storage->read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index e47cde8de529..9765db35fc37 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -184,7 +184,9 @@ Pipes StorageMerge::read( { for (auto it = selected_tables.begin(); it != selected_tables.end(); ++it) { - auto current_info = query_info.order_optimizer->getInputOrder(std::get<0>(*it)); + auto storage_ptr = std::get<0>(*it); + auto storage_metadata_snapshot = storage_ptr->getInMemoryMetadataPtr(); + auto current_info = query_info.order_optimizer->getInputOrder(storage_ptr, storage_metadata_snapshot); if (it == selected_tables.begin()) input_sorting_info = current_info; else if (!current_info || (input_sorting_info && *current_info != *input_sorting_info)) diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index fc1bd8538e59..061be8b2821b 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -796,7 +796,7 @@ void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_column { /// Primary and sorting key become independent after this ALTER so we have to /// save the old ORDER BY expression as the new primary key. - auto old_sorting_key_ast = getSortingKey().definition_ast; + auto old_sorting_key_ast = old_metadata.getSortingKey().definition_ast; primary_key = KeyDescription::getKeyFromAST( old_sorting_key_ast, new_metadata.columns, global_context); } diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 5860facc814c..016d52ffdcbb 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -122,11 +122,11 @@ class ColumnsSource : public SourceWithProgress throw; } - auto metadadata_snapshot = storage->getInMemoryMetadataPtr(); - columns = metadadata_snapshot->getColumns(); + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); + columns = metadata_snapshot->getColumns(); - cols_required_for_partition_key = metadadata_snapshot->getColumnsRequiredForPartitionKey(); - cols_required_for_sorting_key = storage->getColumnsRequiredForSortingKey(); + cols_required_for_partition_key = metadata_snapshot->getColumnsRequiredForPartitionKey(); + cols_required_for_sorting_key = metadata_snapshot->getColumnsRequiredForSortingKey(); cols_required_for_primary_key = storage->getColumnsRequiredForPrimaryKey(); cols_required_for_sampling = storage->getColumnsRequiredForSampling(); column_sizes = storage->getColumnSizes(); diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index 78346d1968fd..50982e3c4b72 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -375,7 +375,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { assert(table != nullptr); - if ((expression_ptr = table->getSortingKey().expression_list_ast)) + if ((expression_ptr = metadata_snapshot->getSortingKey().expression_list_ast)) res_columns[res_index++]->insert(queryToString(expression_ptr)); else res_columns[res_index++]->insertDefault(); From eca6e9087ebae69b79d667fc5e74f1190338ab0f Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 14:52:19 +0300 Subject: [PATCH 0363/1102] Fix race condition --- src/Interpreters/InterpreterAlterQuery.cpp | 2 +- src/Interpreters/InterpreterSelectQuery.cpp | 10 +++++++--- src/Interpreters/InterpreterSelectQuery.h | 6 ++++-- src/Interpreters/MutationsInterpreter.cpp | 10 ++++++---- src/Interpreters/MutationsInterpreter.h | 10 ++++++++-- src/Storages/LiveView/StorageLiveView.cpp | 4 ++-- src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp | 4 ++-- src/Storages/StorageMergeTree.cpp | 3 ++- 8 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index 869c3ae98d38..3736b482ddf0 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -86,7 +86,7 @@ BlockIO InterpreterAlterQuery::execute() auto table_lock_holder = table->lockStructureForShare( false /* because mutation is executed asyncronously */, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - MutationsInterpreter(table, mutation_commands, context, false).validate(table_lock_holder); + MutationsInterpreter(table, metadata_snapshot, mutation_commands, context, false).validate(table_lock_holder); table->mutate(mutation_commands, context); } diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 038448ef3531..42ce69f34c73 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -165,8 +165,9 @@ InterpreterSelectQuery::InterpreterSelectQuery( const ASTPtr & query_ptr_, const Context & context_, const StoragePtr & storage_, + const StorageMetadataPtr & metadata_snapshot_, const SelectQueryOptions & options_) - : InterpreterSelectQuery(query_ptr_, context_, nullptr, std::nullopt, storage_, options_.copy().noSubquery()) + : InterpreterSelectQuery(query_ptr_, context_, nullptr, std::nullopt, storage_, options_.copy().noSubquery(), {}, metadata_snapshot_) {} InterpreterSelectQuery::~InterpreterSelectQuery() = default; @@ -214,7 +215,8 @@ InterpreterSelectQuery::InterpreterSelectQuery( std::optional input_pipe_, const StoragePtr & storage_, const SelectQueryOptions & options_, - const Names & required_result_column_names) + const Names & required_result_column_names, + const StorageMetadataPtr & metadata_snapshot_) : options(options_) /// NOTE: the query almost always should be cloned because it will be modified during analysis. , query_ptr(options.modify_inplace ? query_ptr_ : query_ptr_->clone()) @@ -223,6 +225,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( , input(input_) , input_pipe(std::move(input_pipe_)) , log(&Poco::Logger::get("InterpreterSelectQuery")) + , metadata_snapshot(metadata_snapshot_) { checkStackSize(); @@ -255,7 +258,8 @@ InterpreterSelectQuery::InterpreterSelectQuery( table_lock = storage->lockStructureForShare( false, context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout); table_id = storage->getStorageID(); - metadata_snapshot = storage->getInMemoryMetadataPtr(); + if (metadata_snapshot == nullptr) + metadata_snapshot = storage->getInMemoryMetadataPtr(); } if (has_input || !joined_tables.resolveTables()) diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 8f7237ffd7ee..2f0faa2ba72c 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -70,6 +70,7 @@ class InterpreterSelectQuery : public IInterpreter const ASTPtr & query_ptr_, const Context & context_, const StoragePtr & storage_, + const StorageMetadataPtr & metadata_snapshot_ = nullptr, const SelectQueryOptions & = {}); ~InterpreterSelectQuery() override; @@ -98,7 +99,8 @@ class InterpreterSelectQuery : public IInterpreter std::optional input_pipe, const StoragePtr & storage_, const SelectQueryOptions &, - const Names & required_result_column_names = {}); + const Names & required_result_column_names = {}, + const StorageMetadataPtr & metadata_snapshot_= nullptr); ASTSelectQuery & getSelectQuery() { return query_ptr->as(); } @@ -184,13 +186,13 @@ class InterpreterSelectQuery : public IInterpreter StoragePtr storage; StorageID table_id = StorageID::createEmpty(); /// Will be initialized if storage is not nullptr TableStructureReadLockHolder table_lock; - StorageMetadataPtr metadata_snapshot; /// Used when we read from prepared input, not table or subquery. BlockInputStreamPtr input; std::optional input_pipe; Poco::Logger * log; + StorageMetadataPtr metadata_snapshot; }; } diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 0c0227d476fa..1a38fcf40f39 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -163,6 +163,7 @@ ColumnDependencies getAllColumnDependencies(const StorageMetadataPtr & metadata_ bool isStorageTouchedByMutations( StoragePtr storage, + const StorageMetadataPtr & metadata_snapshot, const std::vector & commands, Context context_copy) { @@ -183,7 +184,7 @@ bool isStorageTouchedByMutations( /// Interpreter must be alive, when we use result of execute() method. /// For some reason it may copy context and and give it into ExpressionBlockInputStream /// after that we will use context from destroyed stack frame in our stream. - InterpreterSelectQuery interpreter(select_query, context_copy, storage, SelectQueryOptions().ignoreLimits()); + InterpreterSelectQuery interpreter(select_query, context_copy, storage, metadata_snapshot, SelectQueryOptions().ignoreLimits()); BlockInputStreamPtr in = interpreter.execute().getInputStream(); Block block = in->read(); @@ -200,18 +201,19 @@ bool isStorageTouchedByMutations( MutationsInterpreter::MutationsInterpreter( StoragePtr storage_, + const StorageMetadataPtr & metadata_snapshot_, MutationCommands commands_, const Context & context_, bool can_execute_) : storage(std::move(storage_)) - , metadata_snapshot(storage->getInMemoryMetadataPtr()) + , metadata_snapshot(metadata_snapshot_) , commands(std::move(commands_)) , context(context_) , can_execute(can_execute_) { mutation_ast = prepare(!can_execute); SelectQueryOptions limits = SelectQueryOptions().analyze(!can_execute).ignoreLimits(); - select_interpreter = std::make_unique(mutation_ast, context, storage, limits); + select_interpreter = std::make_unique(mutation_ast, context, storage, metadata_snapshot_, limits); } static NameSet getKeyColumns(const StoragePtr & storage, const StorageMetadataPtr & metadata_snapshot) @@ -504,7 +506,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } const ASTPtr select_query = prepareInterpreterSelectQuery(stages_copy, /* dry_run = */ true); - InterpreterSelectQuery interpreter{select_query, context, storage, SelectQueryOptions().analyze(/* dry_run = */ false).ignoreLimits()}; + InterpreterSelectQuery interpreter{select_query, context, storage, metadata_snapshot, SelectQueryOptions().analyze(/* dry_run = */ false).ignoreLimits()}; auto first_stage_header = interpreter.getSampleBlock(); auto in = std::make_shared(first_stage_header); diff --git a/src/Interpreters/MutationsInterpreter.h b/src/Interpreters/MutationsInterpreter.h index 158ed8d55af8..3c3175c18562 100644 --- a/src/Interpreters/MutationsInterpreter.h +++ b/src/Interpreters/MutationsInterpreter.h @@ -15,7 +15,8 @@ namespace DB class Context; /// Return false if the data isn't going to be changed by mutations. -bool isStorageTouchedByMutations(StoragePtr storage, const std::vector & commands, Context context_copy); +bool isStorageTouchedByMutations( + StoragePtr storage, const StorageMetadataPtr & metadata_snapshot, const std::vector & commands, Context context_copy); /// Create an input stream that will read data from storage and apply mutation commands (UPDATEs, DELETEs, MATERIALIZEs) /// to this data. @@ -24,7 +25,12 @@ class MutationsInterpreter public: /// Storage to mutate, array of mutations commands and context. If you really want to execute mutation /// use can_execute = true, in other cases (validation, amount of commands) it can be false - MutationsInterpreter(StoragePtr storage_, MutationCommands commands_, const Context & context_, bool can_execute_); + MutationsInterpreter( + StoragePtr storage_, + const StorageMetadataPtr & metadata_snapshot_, + MutationCommands commands_, + const Context & context_, + bool can_execute_); void validate(TableStructureReadLockHolder & table_lock_holder); diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index cb4964f3c55f..f1b9459b3d3f 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -147,7 +147,7 @@ BlockInputStreamPtr StorageLiveView::completeQuery(Pipes pipes) }; block_context->addExternalTable(getBlocksTableName(), TemporaryTableHolder(global_context, creator)); - InterpreterSelectQuery select(getInnerBlocksQuery(), *block_context, StoragePtr(), SelectQueryOptions(QueryProcessingStage::Complete)); + InterpreterSelectQuery select(getInnerBlocksQuery(), *block_context, StoragePtr(), nullptr, SelectQueryOptions(QueryProcessingStage::Complete)); BlockInputStreamPtr data = std::make_shared(select.execute().getInputStream()); /// Squashing is needed here because the view query can generate a lot of blocks @@ -214,7 +214,7 @@ void StorageLiveView::writeIntoLiveView( }; TemporaryTableHolder blocks_storage(context, creator); - InterpreterSelectQuery select_block(mergeable_query, context, blocks_storage.getTable(), + InterpreterSelectQuery select_block(mergeable_query, context, blocks_storage.getTable(), blocks_storage.getTable()->getInMemoryMetadataPtr(), QueryProcessingStage::WithMergeableState); auto data_mergeable_stream = std::make_shared( diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index fb08f379ee33..02279fe32989 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -1019,7 +1019,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor commands_for_part.emplace_back(command); } - if (!isStorageTouchedByMutations(storage_from_source_part, commands_for_part, context_for_reading)) + if (!isStorageTouchedByMutations(storage_from_source_part, metadata_snapshot, commands_for_part, context_for_reading)) { LOG_TRACE(log, "Part {} doesn't change up to mutation version {}", source_part->name, future_part.part_info.mutation); return data.cloneAndLoadDataPartOnSameDisk(source_part, "tmp_clone_", future_part.part_info); @@ -1045,7 +1045,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor if (!for_interpreter.empty()) { - interpreter.emplace(storage_from_source_part, for_interpreter, context_for_reading, true); + interpreter.emplace(storage_from_source_part, metadata_snapshot, for_interpreter, context_for_reading, true); in = interpreter->execute(table_lock_holder); updated_header = interpreter->getUpdatedHeader(); in->setProgressCallback(MergeProgressCallback(merge_entry, watch_prev_elapsed, stage_progress)); diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index c13070fa4a5e..f259d74b9ea7 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -789,7 +789,8 @@ bool StorageMergeTree::tryMutatePart() if (!commands_for_size_validation.empty()) { - MutationsInterpreter interpreter(shared_from_this(), commands_for_size_validation, global_context, false); + MutationsInterpreter interpreter( + shared_from_this(), metadata_snapshot, commands_for_size_validation, global_context, false); commands_size += interpreter.evaluateCommandsSize(); } From 5abbaeecf5e7185b8bd7da57ed5fd6d2850b4943 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Wed, 17 Jun 2020 13:02:04 +0300 Subject: [PATCH 0364/1102] distinct combinator for single numeric arguments --- .../AggregateFunctionDistinct.cpp | 29 ++- .../AggregateFunctionDistinct.h | 195 ++++++++++++------ .../AggregateFunctionGroupUniqArray.h | 38 +--- src/AggregateFunctions/KeyHolderHelpers.h | 34 +++ .../01259_combinator_distinct.reference | 1 + .../0_stateless/01259_combinator_distinct.sql | 11 +- 6 files changed, 201 insertions(+), 107 deletions(-) create mode 100644 src/AggregateFunctions/KeyHolderHelpers.h diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index 820c2f0f72c6..1661277d525b 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "registerAggregateFunctions.h" @@ -9,6 +10,7 @@ namespace DB namespace ErrorCodes { extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int ILLEGAL_TYPE_OF_ARGUMENT; } class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombinator @@ -22,19 +24,30 @@ class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombi throw Exception("Incorrect number of arguments for aggregate function with " + getName() + " suffix", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - DataTypes nested_arguments; - for (const auto & type : arguments) - { - nested_arguments.push_back(type); - } - - return nested_arguments; + return arguments; } AggregateFunctionPtr transformAggregateFunction( const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array &) const override { - return std::make_shared(nested_function, arguments); + AggregateFunctionPtr res; + if (arguments.size() == 1) + { + res = AggregateFunctionPtr(createWithNumericType(*arguments[0], nested_function, arguments)); + if (res) + return res; + + if (arguments[0]->isValueUnambiguouslyRepresentedInContiguousMemoryRegion()) + return std::make_shared>(nested_function, arguments); + else + return std::make_shared>(nested_function, arguments); + } + + if (!res) + throw Exception("Illegal type " /* + argument_type->getName() + */ + " of argument for aggregate function " + nested_function->getName() + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return res; } }; diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 32f5df6d8f05..72099a33cfd3 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -16,34 +17,22 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } -struct AggregateFunctionDistinctData +template +struct AggregateFunctionDistinctSingleNumericData { - using Key = UInt128; - - HashSet< - Key, - UInt128TrivialHash, - HashTableGrower<3>, - HashTableAllocatorWithStackMemory - > set; - std::mutex mutex; - - bool ALWAYS_INLINE tryToInsert(const Key& key) - { - return set.insert(key).second; - } + /// When creating, the hash table must be small. + using Set = HashSetWithStackMemory, 4>; + Set value; }; -/** Adaptor for aggregate functions. - * Adding -Distinct suffix to aggregate function -**/ - -class AggregateFunctionDistinct final : public IAggregateFunctionDataHelper +template +class AggregateFunctionDistinctBase : public IAggregateFunctionDataHelper { -private: +protected: + static constexpr size_t prefix_size = sizeof(Data); AggregateFunctionPtr nested_func; size_t num_arguments; - size_t prefix_size; + AggregateDataPtr getNestedPlace(AggregateDataPtr place) const noexcept { @@ -56,14 +45,22 @@ class AggregateFunctionDistinct final : public IAggregateFunctionDataHelper(arguments, {}) - , nested_func(nested), num_arguments(arguments.size()) + + size_t sizeOfData() const override { - prefix_size = sizeof(AggregateFunctionDistinctData); + return prefix_size + nested_func->sizeOfData(); + } - if (arguments.empty()) - throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + void create(AggregateDataPtr place) const override + { + new (place) Data; + nested_func->create(getNestedPlace(place)); + } + + void destroy(AggregateDataPtr place) const noexcept override + { + this->data(place).~Data(); + nested_func->destroy(getNestedPlace(place)); } String getName() const override @@ -76,71 +73,151 @@ class AggregateFunctionDistinct final : public IAggregateFunctionDataHelpergetReturnType(); } - void create(AggregateDataPtr place) const override + bool allocatesMemoryInArena() const override { - new (place) AggregateFunctionDistinctData; - nested_func->create(getNestedPlace(place)); + return true; } - void destroy(AggregateDataPtr place) const noexcept override + AggregateFunctionDistinctBase(AggregateFunctionPtr nested, const DataTypes & arguments) + : IAggregateFunctionDataHelper(arguments, {}) + , nested_func(nested), num_arguments(arguments.size()) { - data(place).~AggregateFunctionDistinctData(); - nested_func->destroy(getNestedPlace(place)); + if (arguments.empty()) + throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); } +}; - size_t sizeOfData() const override + +/** Adaptor for aggregate functions. + * Adding -Distinct suffix to aggregate function +**/ +template +class AggregateFunctionDistinctSingleNumericImpl final + : public AggregateFunctionDistinctBase, + AggregateFunctionDistinctSingleNumericImpl> +{ +public: + + AggregateFunctionDistinctSingleNumericImpl(AggregateFunctionPtr nested, const DataTypes & arguments) + : AggregateFunctionDistinctBase< + AggregateFunctionDistinctSingleNumericData, + AggregateFunctionDistinctSingleNumericImpl>(nested, arguments) {} + + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override { - return prefix_size + nested_func->sizeOfData(); + const auto & vec = assert_cast &>(*columns[0]).getData(); + if (this->data(place).value.insert(vec[row_num]).second) + this->nested_func->add(this->getNestedPlace(place), columns, row_num, arena); } - size_t alignOfData() const override + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { - return nested_func->alignOfData(); + auto & cur_set = this->data(place).value; + auto & rhs_set = this->data(rhs).value; + + auto arguments = this->argument_types[0]->createColumn(); + for (auto & elem : rhs_set) + if (cur_set.insert(elem.getValue()).second) + arguments->insert(elem.getValue()); + + const auto * arguments_ptr = arguments.get(); + if (!arguments->empty()) + this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); } - bool hasTrivialDestructor() const override + void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { - return nested_func->hasTrivialDestructor(); + this->data(place).value.write(buf); + this->nested_func->serialize(this->getNestedPlace(place), buf); } - void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { - SipHash hash; - for (size_t i = 0; i < num_arguments; ++i) - { - columns[i]->updateHashWithValue(row_num, hash); - } + this->data(place).value.read(buf); + this->nested_func->deserialize(this->getNestedPlace(place), buf, arena); + } + + void insertResultInto(AggregateDataPtr place, IColumn & to) const override + { + this->nested_func->insertResultInto(this->getNestedPlace(place), to); + } +}; - UInt128 key; - hash.get128(key.low, key.high); +struct AggregateFunctionDistinctSingleGenericData +{ + using Set = HashSetWithSavedHashWithStackMemory; + Set value; +}; + +template +class AggregateFunctionDistinctSingleGenericImpl final + : public AggregateFunctionDistinctBase> +{ +public: + using Data = AggregateFunctionDistinctSingleGenericData; - if (this->data(place).tryToInsert(key)) - nested_func->add(getNestedPlace(place), columns, row_num, arena); + AggregateFunctionDistinctSingleGenericImpl(AggregateFunctionPtr nested, const DataTypes & arguments) + : AggregateFunctionDistinctBase< + AggregateFunctionDistinctSingleGenericData, + AggregateFunctionDistinctSingleGenericImpl>(nested, arguments) {} + + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + { + auto & set = this->data(place).value; + + Data::Set::LookupResult it; + bool inserted; + auto key_holder = getKeyHolder(*columns[0], row_num, *arena); + set.emplace(key_holder, it, inserted); + if (inserted) + this->nested_func->add(this->getNestedPlace(place), columns, row_num, arena); } void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { - nested_func->merge(getNestedPlace(place), rhs, arena); + auto & cur_set = this->data(place).value; + auto & rhs_set = this->data(rhs).value; + + Data::Set::LookupResult it; + bool inserted; + auto arguments = this->argument_types[0]->createColumn(); + for (auto & elem : rhs_set) + { + cur_set.emplace(ArenaKeyHolder{elem.getValue(), *arena}, it, inserted); + if (inserted) + deserializeAndInsert(elem.getValue(), *arguments); + } + + const auto * arguments_ptr = arguments.get(); + if (!arguments->empty()) + this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); } void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { - nested_func->serialize(getNestedPlace(place), buf); + auto & set = this->data(place).value; + writeVarUInt(set.size(), buf); + for (const auto & elem : set) + writeStringBinary(elem.getValue(), buf); + + this->nested_func->serialize(this->getNestedPlace(place), buf); } void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { - nested_func->deserialize(getNestedPlace(place), buf, arena); - } + auto & set = this->data(place).value; + size_t size; + readVarUInt(size, buf); + for (size_t i = 0; i < size; ++i) + set.insert(readStringBinaryInto(*arena, buf)); - void insertResultInto(AggregateDataPtr place, IColumn & to) const override - { - nested_func->insertResultInto(getNestedPlace(place), to); + this->nested_func->deserialize(this->getNestedPlace(place), buf, arena); } - bool allocatesMemoryInArena() const override + void insertResultInto(AggregateDataPtr place, IColumn & to) const override { - return nested_func->allocatesMemoryInArena(); + this->nested_func->insertResultInto(this->getNestedPlace(place), to); } }; diff --git a/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h b/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h index 88b1c87f5267..b6683567404c 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h +++ b/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h @@ -16,6 +16,7 @@ #include #include +#include #define AGGREGATE_FUNCTION_GROUP_ARRAY_UNIQ_MAX_SIZE 0xFFFFFF @@ -147,26 +148,6 @@ class AggregateFunctionGroupUniqArrayGeneric using State = AggregateFunctionGroupUniqArrayGenericData; - static auto getKeyHolder(const IColumn & column, size_t row_num, Arena & arena) - { - if constexpr (is_plain_column) - { - return ArenaKeyHolder{column.getDataAt(row_num), arena}; - } - else - { - const char * begin = nullptr; - StringRef serialized = column.serializeValueIntoArena(row_num, arena, begin); - assert(serialized.data != nullptr); - return SerializedKeyHolder{serialized, arena}; - } - } - - static void deserializeAndInsert(StringRef str, IColumn & data_to) - { - return deserializeAndInsertImpl(str, data_to); - } - public: AggregateFunctionGroupUniqArrayGeneric(const DataTypePtr & input_data_type_, UInt64 max_elems_ = std::numeric_limits::max()) : IAggregateFunctionDataHelper>({input_data_type_}, {}) @@ -215,7 +196,7 @@ class AggregateFunctionGroupUniqArrayGeneric bool inserted; State::Set::LookupResult it; - auto key_holder = getKeyHolder(*columns[0], row_num, *arena); + auto key_holder = getKeyHolder(*columns[0], row_num, *arena); set.emplace(key_holder, it, inserted); } @@ -247,22 +228,9 @@ class AggregateFunctionGroupUniqArrayGeneric offsets_to.push_back(offsets_to.back() + set.size()); for (auto & elem : set) - deserializeAndInsert(elem.getValue(), data_to); + deserializeAndInsert(elem.getValue(), data_to); } }; - -template <> -inline void deserializeAndInsertImpl(StringRef str, IColumn & data_to) -{ - data_to.deserializeAndInsertFromArena(str.data); -} - -template <> -inline void deserializeAndInsertImpl(StringRef str, IColumn & data_to) -{ - data_to.insertData(str.data, str.size); -} - #undef AGGREGATE_FUNCTION_GROUP_ARRAY_UNIQ_MAX_SIZE } diff --git a/src/AggregateFunctions/KeyHolderHelpers.h b/src/AggregateFunctions/KeyHolderHelpers.h new file mode 100644 index 000000000000..01b3cf2a3698 --- /dev/null +++ b/src/AggregateFunctions/KeyHolderHelpers.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace DB +{ + +template +static auto getKeyHolder(const IColumn & column, size_t row_num, Arena & arena) +{ + if constexpr (is_plain_column) + { + return ArenaKeyHolder{column.getDataAt(row_num), arena}; + } + else + { + const char * begin = nullptr; + StringRef serialized = column.serializeValueIntoArena(row_num, arena, begin); + assert(serialized.data != nullptr); + return SerializedKeyHolder{serialized, arena}; + } +} + +template +static void deserializeAndInsert(StringRef str, IColumn & data_to) +{ + if constexpr (is_plain_column) + data_to.insertData(str.data, str.size); + else + data_to.deserializeAndInsertFromArena(str.data); +} + +} diff --git a/tests/queries/0_stateless/01259_combinator_distinct.reference b/tests/queries/0_stateless/01259_combinator_distinct.reference index 739d225ad676..83756ffdaa4a 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.reference +++ b/tests/queries/0_stateless/01259_combinator_distinct.reference @@ -2,4 +2,5 @@ 78 [0,1,2,3,4,5,6,7,8,9,10,11,12] [0,1,2,3,4,5,6,7,8,9,10,11,12] +20 5.669227916063075e-17 diff --git a/tests/queries/0_stateless/01259_combinator_distinct.sql b/tests/queries/0_stateless/01259_combinator_distinct.sql index 3f07dc443ddf..adfddeb34e42 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.sql +++ b/tests/queries/0_stateless/01259_combinator_distinct.sql @@ -1,5 +1,6 @@ -SELECT sum(DISTINCT x) FROM (SELECT number AS x FROM system.numbers LIMIT 1000); -SELECT sum(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); -SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers LIMIT 1000); -SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 1000); -SELECT corrStableDistinct(DISTINCT x, y) FROM (SELECT number % 11 AS x, number % 13 AS y FROM system.numbers LIMIT 1000); \ No newline at end of file +SELECT sum(DISTINCT x) FROM (SELECT number AS x FROM system.numbers_mt LIMIT 100000); +SELECT sum(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); +SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); +SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); +SELECT finalizeAggregation(countState(DISTINCT toString(number % 20))) FROM numbers_mt (100000); +-- SELECT corrStableDistinct(DISTINCT x, y) FROM (SELECT number % 11 AS x, number % 13 AS y FROM system.numbers LIMIT 1000); From 1da393b2180bedc1827bdd7b8c6356e06db7a993 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 15:07:09 +0300 Subject: [PATCH 0365/1102] Sampling key in StorageInMemoryMetadata --- src/Interpreters/ExpressionAnalyzer.cpp | 2 +- src/Storages/IStorage.cpp | 23 ------------------- src/Storages/IStorage.h | 14 +---------- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 4 ++-- .../ReplicatedMergeTreeTableMetadata.cpp | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 23 +++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 11 +++++++++ src/Storages/System/StorageSystemColumns.cpp | 2 +- src/Storages/System/StorageSystemTables.cpp | 2 +- 9 files changed, 41 insertions(+), 42 deletions(-) diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 28aa42877d6e..9d9a403ab596 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -1062,7 +1062,7 @@ ExpressionAnalysisResult::ExpressionAnalysisResult( if (storage && (query.sampleSize() || settings.parallel_replicas_count > 1)) { - Names columns_for_sampling = storage->getColumnsRequiredForSampling(); + Names columns_for_sampling = metadata_snapshot->getColumnsRequiredForSampling(); additional_required_columns_after_prewhere.insert(additional_required_columns_after_prewhere.end(), columns_for_sampling.begin(), columns_for_sampling.end()); } diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 0c2ddc09da8a..0a50cf955593 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -348,35 +348,12 @@ Names IStorage::getPrimaryKeyColumns() const return {}; } -const KeyDescription & IStorage::getSamplingKey() const -{ - return metadata->sampling_key; -} - -bool IStorage::isSamplingKeyDefined() const -{ - return metadata->sampling_key.definition_ast != nullptr; -} - -bool IStorage::hasSamplingKey() const -{ - return !metadata->sampling_key.column_names.empty(); -} - -Names IStorage::getColumnsRequiredForSampling() const -{ - if (hasSamplingKey()) - return metadata->sampling_key.expression->getRequiredColumns(); - return {}; -} - TTLTableDescription IStorage::getTableTTLs() const { std::lock_guard lock(ttl_mutex); return metadata->table_ttl; } - bool IStorage::hasAnyTableTTL() const { return hasAnyMoveTTL() || hasRowsTTL(); diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 4e1ca81dd108..5aa595b13754 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -101,7 +101,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo virtual bool isView() const { return false; } /// Returns true if the storage supports queries with the SAMPLE section. - virtual bool supportsSampling() const { return hasSamplingKey(); } + virtual bool supportsSampling() const { return getInMemoryMetadataPtr()->hasSamplingKey(); } /// Returns true if the storage supports queries with the FINAL section. virtual bool supportsFinal() const { return false; } @@ -442,18 +442,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// * y', 'toStartOfMonth(date)', etc. Names getPrimaryKeyColumns() const; - /// Returns structure with sampling key. - const KeyDescription & getSamplingKey() const; - /// Returns sampling expression AST for storage or nullptr if there is none. - ASTPtr getSamplingKeyAST() const { return metadata->sampling_key.definition_ast; } - /// Storage has user-defined (in CREATE query) sampling key. - bool isSamplingKeyDefined() const; - /// Storage has sampling key. - bool hasSamplingKey() const; - /// Returns column names that need to be read to calculate sampling key. - Names getColumnsRequiredForSampling() const; - - /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 9d198c7ed659..58214bae5cae 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -390,7 +390,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( used_sample_factor = 1.0 / boost::rational_cast(relative_sample_size); RelativeSize size_of_universum = 0; - const auto & sampling_key = data.getSamplingKey(); + const auto & sampling_key = metadata_snapshot->getSamplingKey(); DataTypePtr sampling_column_type = sampling_key.data_types[0]; if (typeid_cast(sampling_column_type.get())) @@ -459,7 +459,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( /// If sample and final are used together no need to calculate sampling expression twice. /// The first time it was calculated for final, because sample key is a part of the PK. /// So, assume that we already have calculated column. - ASTPtr sampling_key_ast = data.getSamplingKeyAST(); + ASTPtr sampling_key_ast = metadata_snapshot->getSamplingKeyAST(); if (select.final()) { diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index 81366db5b2ab..c3d91f4a5a99 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -29,7 +29,7 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr date_column = data.minmax_idx_columns[data.minmax_idx_date_column_pos]; const auto data_settings = data.getSettings(); - sampling_expression = formattedAST(data.getSamplingKeyAST()); + sampling_expression = formattedAST(metadata_snapshot->getSamplingKeyAST()); index_granularity = data_settings->index_granularity; merging_params_mode = static_cast(data.merging_params.mode); sign_column = data.merging_params.sign_column; diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 3c7f474c4828..6c5429fc556e 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -342,4 +342,27 @@ Names StorageInMemoryMetadata::getSortingKeyColumns() const return sorting_key.column_names; return {}; } + +const KeyDescription & StorageInMemoryMetadata::getSamplingKey() const +{ + return sampling_key; +} + +bool StorageInMemoryMetadata::isSamplingKeyDefined() const +{ + return sampling_key.definition_ast != nullptr; +} + +bool StorageInMemoryMetadata::hasSamplingKey() const +{ + return !sampling_key.column_names.empty(); +} + +Names StorageInMemoryMetadata::getColumnsRequiredForSampling() const +{ + if (hasSamplingKey()) + return sampling_key.expression->getRequiredColumns(); + return {}; +} + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index f73700574105..1abea7d250c6 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -140,6 +140,17 @@ struct StorageInMemoryMetadata /// Returns column names that need to be read for FINAL to work. Names getColumnsRequiredForFinal() const { return getColumnsRequiredForSortingKey(); } + + /// Returns structure with sampling key. + const KeyDescription & getSamplingKey() const; + /// Returns sampling expression AST for storage or nullptr if there is none. + ASTPtr getSamplingKeyAST() const { return sampling_key.definition_ast; } + /// Storage has user-defined (in CREATE query) sampling key. + bool isSamplingKeyDefined() const; + /// Storage has sampling key. + bool hasSamplingKey() const; + /// Returns column names that need to be read to calculate sampling key. + Names getColumnsRequiredForSampling() const; }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 016d52ffdcbb..f998dc27cabd 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -128,7 +128,7 @@ class ColumnsSource : public SourceWithProgress cols_required_for_partition_key = metadata_snapshot->getColumnsRequiredForPartitionKey(); cols_required_for_sorting_key = metadata_snapshot->getColumnsRequiredForSortingKey(); cols_required_for_primary_key = storage->getColumnsRequiredForPrimaryKey(); - cols_required_for_sampling = storage->getColumnsRequiredForSampling(); + cols_required_for_sampling = metadata_snapshot->getColumnsRequiredForSampling(); column_sizes = storage->getColumnSizes(); } diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index 50982e3c4b72..0852a96e8ba9 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -393,7 +393,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { assert(table != nullptr); - if ((expression_ptr = table->getSamplingKeyAST())) + if ((expression_ptr = metadata_snapshot->getSamplingKeyAST())) res_columns[res_index++]->insert(queryToString(expression_ptr)); else res_columns[res_index++]->insertDefault(); From 1afdebeebdb8dcb0559ff51eacd259ea6094a409 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 15:39:20 +0300 Subject: [PATCH 0366/1102] Primary key in storage metadata --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- src/Storages/IStorage.cpp | 29 ------------------- src/Storages/IStorage.h | 15 ---------- src/Storages/MergeTree/IMergeTreeDataPart.cpp | 5 ++-- src/Storages/MergeTree/IMergeTreeDataPart.h | 1 + .../MergeTree/IMergeTreeDataPartWriter.cpp | 6 ++-- .../MergeTree/IMergeTreeDataPartWriter.h | 2 ++ src/Storages/MergeTree/MergeTreeData.cpp | 19 ++++++------ src/Storages/MergeTree/MergeTreeData.h | 2 +- .../MergeTree/MergeTreeDataMergerMutator.cpp | 2 +- .../MergeTree/MergeTreeDataPartCompact.cpp | 5 ++-- .../MergeTree/MergeTreeDataPartCompact.h | 1 + .../MergeTree/MergeTreeDataPartWide.cpp | 3 +- .../MergeTree/MergeTreeDataPartWide.h | 1 + .../MergeTreeDataPartWriterCompact.cpp | 8 ++--- .../MergeTreeDataPartWriterCompact.h | 1 + .../MergeTree/MergeTreeDataPartWriterWide.cpp | 8 ++--- .../MergeTree/MergeTreeDataPartWriterWide.h | 1 + .../MergeTree/MergeTreeDataSelectExecutor.cpp | 22 +++++++++----- .../MergeTree/MergeTreeDataSelectExecutor.h | 2 ++ .../MergeTree/MergeTreeWhereOptimizer.cpp | 13 +++++---- .../MergeTree/MergeTreeWhereOptimizer.h | 3 ++ .../MergeTree/MergedBlockOutputStream.cpp | 4 +-- .../MergedColumnOnlyOutputStream.cpp | 1 + .../ReplicatedMergeTreeTableMetadata.cpp | 4 +-- src/Storages/StorageInMemoryMetadata.cpp | 28 ++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 15 ++++++++++ src/Storages/System/StorageSystemTables.cpp | 2 +- 28 files changed, 114 insertions(+), 91 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 42ce69f34c73..41b2abc33c6e 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -330,7 +330,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( current_info.query = query_ptr; current_info.syntax_analyzer_result = syntax_analyzer_result; - MergeTreeWhereOptimizer{current_info, *context, *merge_tree, syntax_analyzer_result->requiredSourceColumns(), log}; + MergeTreeWhereOptimizer{current_info, *context, *merge_tree, metadata_snapshot, syntax_analyzer_result->requiredSourceColumns(), log}; } } diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 0a50cf955593..f9b7f41f1392 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,35 +319,6 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -const KeyDescription & IStorage::getPrimaryKey() const -{ - return metadata->primary_key; -} - -bool IStorage::isPrimaryKeyDefined() const -{ - return metadata->primary_key.definition_ast != nullptr; -} - -bool IStorage::hasPrimaryKey() const -{ - return !metadata->primary_key.column_names.empty(); -} - -Names IStorage::getColumnsRequiredForPrimaryKey() const -{ - if (hasPrimaryKey()) - return metadata->primary_key.expression->getRequiredColumns(); - return {}; -} - -Names IStorage::getPrimaryKeyColumns() const -{ - if (!metadata->primary_key.column_names.empty()) - return metadata->primary_key.column_names; - return {}; -} - TTLTableDescription IStorage::getTableTTLs() const { std::lock_guard lock(ttl_mutex); diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 5aa595b13754..787b96c91970 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -427,21 +427,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns data paths if storage supports it, empty vector otherwise. virtual Strings getDataPaths() const { return {}; } - /// Returns structure with primary key. - const KeyDescription & getPrimaryKey() const; - /// Returns ASTExpressionList of primary key expression for storage or nullptr if there is none. - ASTPtr getPrimaryKeyAST() const { return metadata->primary_key.definition_ast; } - /// Storage has user-defined (in CREATE query) sorting key. - bool isPrimaryKeyDefined() const; - /// Storage has primary key (maybe part of some other key). It means, that - /// it contains at least one column. - bool hasPrimaryKey() const; - /// Returns column names that need to be read to calculate primary key. - Names getColumnsRequiredForPrimaryKey() const; - /// Returns columns names in sorting key specified by. For example: 'a', 'x - /// * y', 'toStartOfMonth(date)', etc. - Names getPrimaryKeyColumns() const; - /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.cpp b/src/Storages/MergeTree/IMergeTreeDataPart.cpp index 03b2dea23ba1..17ff2259436a 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPart.cpp @@ -437,7 +437,8 @@ void IMergeTreeDataPart::loadIndex() if (!index_granularity.isInitialized()) throw Exception("Index granularity is not loaded before index loading", ErrorCodes::LOGICAL_ERROR); - const auto & primary_key = storage.getPrimaryKey(); + auto metadata_snapshot = storage.getInMemoryMetadataPtr(); + const auto & primary_key = metadata_snapshot->getPrimaryKey(); size_t key_size = primary_key.column_names.size(); if (key_size) @@ -842,7 +843,7 @@ void IMergeTreeDataPart::checkConsistencyBase() const String path = getFullRelativePath(); auto metadata_snapshot = storage.getInMemoryMetadataPtr(); - const auto & pk = storage.getPrimaryKey(); + const auto & pk = metadata_snapshot->getPrimaryKey(); if (!checksums.empty()) { if (!pk.column_names.empty() && !checksums.files.count("primary.idx")) diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.h b/src/Storages/MergeTree/IMergeTreeDataPart.h index f5ca0fee070b..04babece83e0 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.h +++ b/src/Storages/MergeTree/IMergeTreeDataPart.h @@ -86,6 +86,7 @@ class IMergeTreeDataPart : public std::enable_shared_from_this & indices_to_recalc, const CompressionCodecPtr & default_codec_, const MergeTreeWriterSettings & writer_settings, diff --git a/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp b/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp index cfda613d31d3..73ac7fc00645 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp @@ -65,6 +65,7 @@ void IMergeTreeDataPartWriter::Stream::addToChecksums(MergeTreeData::DataPart::C IMergeTreeDataPartWriter::IMergeTreeDataPartWriter( const MergeTreeData::DataPartPtr & data_part_, const NamesAndTypesList & columns_list_, + const StorageMetadataPtr & metadata_snapshot_, const std::vector & indices_to_recalc_, const String & marks_file_extension_, const CompressionCodecPtr & default_codec_, @@ -73,6 +74,7 @@ IMergeTreeDataPartWriter::IMergeTreeDataPartWriter( : data_part(data_part_) , part_path(data_part_->getFullRelativePath()) , storage(data_part_->storage) + , metadata_snapshot(metadata_snapshot_) , columns_list(columns_list_) , marks_file_extension(marks_file_extension_) , index_granularity(index_granularity_) @@ -162,7 +164,7 @@ void IMergeTreeDataPartWriter::fillIndexGranularity(size_t index_granularity_for void IMergeTreeDataPartWriter::initPrimaryIndex() { - if (storage.hasPrimaryKey()) + if (metadata_snapshot->hasPrimaryKey()) { index_file_stream = data_part->volume->getDisk()->writeFile(part_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Rewrite); index_stream = std::make_unique(*index_file_stream); @@ -221,7 +223,7 @@ void IMergeTreeDataPartWriter::calculateAndSerializePrimaryIndex(const Block & p while (index_mark < total_marks && current_row < rows) { - if (storage.hasPrimaryKey()) + if (metadata_snapshot->hasPrimaryKey()) { for (size_t j = 0; j < primary_columns_num; ++j) { diff --git a/src/Storages/MergeTree/IMergeTreeDataPartWriter.h b/src/Storages/MergeTree/IMergeTreeDataPartWriter.h index 149aeaa2f0d1..2f849e7c8952 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPartWriter.h +++ b/src/Storages/MergeTree/IMergeTreeDataPartWriter.h @@ -63,6 +63,7 @@ class IMergeTreeDataPartWriter : private boost::noncopyable IMergeTreeDataPartWriter( const MergeTreeData::DataPartPtr & data_part, const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot_, const std::vector & indices_to_recalc, const String & marks_file_extension, const CompressionCodecPtr & default_codec, @@ -119,6 +120,7 @@ class IMergeTreeDataPartWriter : private boost::noncopyable MergeTreeData::DataPartPtr data_part; String part_path; const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; NamesAndTypesList columns_list; const String marks_file_extension; diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index a1487b3a1fee..ff38a21a15f9 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -173,11 +173,11 @@ MergeTreeData::MergeTreeData( const auto settings = getSettings(); /// NOTE: using the same columns list as is read when performing actual merges. - merging_params.check(getColumns().getAllPhysical()); + merging_params.check(metadata_.getColumns().getAllPhysical()); if (metadata_.sampling_key.definition_ast != nullptr) { - const auto & pk_sample_block = getPrimaryKey().sample_block; + const auto & pk_sample_block = metadata_.getPrimaryKey().sample_block; if (!pk_sample_block.has(metadata_.sampling_key.column_names[0]) && !attach && !settings->compatibility_allow_sampling_expression_not_in_primary_key) /// This is for backward compatibility. throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); @@ -410,7 +410,7 @@ ExpressionActionsPtr getCombinedIndicesExpression( ExpressionActionsPtr MergeTreeData::getPrimaryKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const { - return getCombinedIndicesExpression(getPrimaryKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); + return getCombinedIndicesExpression(metadata_snapshot->getPrimaryKey(), metadata_snapshot->getSecondaryIndices(), metadata_snapshot->getColumns(), global_context); } ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression(const StorageMetadataPtr & metadata_snapshot) const @@ -2915,11 +2915,12 @@ MergeTreeData::DataPartsVector MergeTreeData::Transaction::commit(MergeTreeData: return total_covered_parts; } -bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const +bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions( + const ASTPtr & node, const StorageMetadataPtr & metadata_snapshot) const { const String column_name = node->getColumnName(); - for (const auto & name : getPrimaryKeyColumns()) + for (const auto & name : metadata_snapshot->getPrimaryKeyColumns()) if (column_name == name) return true; @@ -2929,7 +2930,7 @@ bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const A if (const auto * func = node->as()) if (func->arguments->children.size() == 1) - return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(func->arguments->children.front()); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(func->arguments->children.front(), metadata_snapshot); return false; } @@ -2946,14 +2947,14 @@ bool MergeTreeData::mayBenefitFromIndexForIn( { for (const auto & item : left_in_operand_tuple->arguments->children) { - if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item)) + if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item, metadata_snapshot)) return true; for (const auto & index : metadata_snapshot->getSecondaryIndices()) if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(item)) return true; } /// The tuple itself may be part of the primary key, so check that as a last resort. - return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand, metadata_snapshot); } else { @@ -2961,7 +2962,7 @@ bool MergeTreeData::mayBenefitFromIndexForIn( if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(left_in_operand)) return true; - return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand); + return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand, metadata_snapshot); } } diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index 863b5ba16445..fdbe36d10ec1 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -812,7 +812,7 @@ class MergeTreeData : public IStorage DataPartsLock & data_parts_lock) const; /// Checks whether the column is in the primary key, possibly wrapped in a chain of functions with single argument. - bool isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node) const; + bool isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(const ASTPtr & node, const StorageMetadataPtr & metadata_snapshot) const; /// Common part for |freezePartition()| and |freezeAll()|. using MatcherFn = std::function; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 02279fe32989..fad65e492c05 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -1604,7 +1604,7 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( if (mutating_stream == nullptr) throw Exception("Cannot mutate part columns with uninitialized mutations stream. It's a bug", ErrorCodes::LOGICAL_ERROR); - if (data.hasPrimaryKey() || metadata_snapshot->hasSecondaryIndices()) + if (metadata_snapshot->hasPrimaryKey() || metadata_snapshot->hasSecondaryIndices()) mutating_stream = std::make_shared( std::make_shared(mutating_stream, data.getPrimaryKeyAndSkipIndicesExpression(metadata_snapshot))); diff --git a/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp b/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp index 32acc266e424..d45aa882b2a9 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp @@ -54,6 +54,7 @@ IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartCompact::getReader( IMergeTreeDataPart::MergeTreeWriterPtr MergeTreeDataPartCompact::getWriter( const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot, const std::vector & indices_to_recalc, const CompressionCodecPtr & default_codec, const MergeTreeWriterSettings & writer_settings, @@ -68,8 +69,8 @@ IMergeTreeDataPart::MergeTreeWriterPtr MergeTreeDataPartCompact::getWriter( { return *getColumnPosition(lhs.name) < *getColumnPosition(rhs.name); }); return std::make_unique( - shared_from_this(), ordered_columns_list, indices_to_recalc, - index_granularity_info.marks_file_extension, + shared_from_this(), ordered_columns_list, metadata_snapshot, + indices_to_recalc, index_granularity_info.marks_file_extension, default_codec, writer_settings, computed_index_granularity); } diff --git a/src/Storages/MergeTree/MergeTreeDataPartCompact.h b/src/Storages/MergeTree/MergeTreeDataPartCompact.h index b4a2b5fa7975..0b27dd533395 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartCompact.h +++ b/src/Storages/MergeTree/MergeTreeDataPartCompact.h @@ -46,6 +46,7 @@ class MergeTreeDataPartCompact : public IMergeTreeDataPart MergeTreeWriterPtr getWriter( const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot, const std::vector & indices_to_recalc, const CompressionCodecPtr & default_codec_, const MergeTreeWriterSettings & writer_settings, diff --git a/src/Storages/MergeTree/MergeTreeDataPartWide.cpp b/src/Storages/MergeTree/MergeTreeDataPartWide.cpp index d96b61b4bea3..e4901b1f74d3 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWide.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartWide.cpp @@ -53,13 +53,14 @@ IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartWide::getReader( IMergeTreeDataPart::MergeTreeWriterPtr MergeTreeDataPartWide::getWriter( const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot, const std::vector & indices_to_recalc, const CompressionCodecPtr & default_codec, const MergeTreeWriterSettings & writer_settings, const MergeTreeIndexGranularity & computed_index_granularity) const { return std::make_unique( - shared_from_this(), columns_list, indices_to_recalc, + shared_from_this(), columns_list, metadata_snapshot, indices_to_recalc, index_granularity_info.marks_file_extension, default_codec, writer_settings, computed_index_granularity); } diff --git a/src/Storages/MergeTree/MergeTreeDataPartWide.h b/src/Storages/MergeTree/MergeTreeDataPartWide.h index ba9e07655101..144dfa86cfb8 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWide.h +++ b/src/Storages/MergeTree/MergeTreeDataPartWide.h @@ -39,6 +39,7 @@ class MergeTreeDataPartWide : public IMergeTreeDataPart MergeTreeWriterPtr getWriter( const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot, const std::vector & indices_to_recalc, const CompressionCodecPtr & default_codec_, const MergeTreeWriterSettings & writer_settings, diff --git a/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.cpp b/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.cpp index 1a7a757c1498..f7a3ad75cf58 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.cpp @@ -3,19 +3,17 @@ namespace DB { - - MergeTreeDataPartWriterCompact::MergeTreeDataPartWriterCompact( const MergeTreeData::DataPartPtr & data_part_, const NamesAndTypesList & columns_list_, + const StorageMetadataPtr & metadata_snapshot_, const std::vector & indices_to_recalc_, const String & marks_file_extension_, const CompressionCodecPtr & default_codec_, const MergeTreeWriterSettings & settings_, const MergeTreeIndexGranularity & index_granularity_) - : IMergeTreeDataPartWriter(data_part_, columns_list_, - indices_to_recalc_, marks_file_extension_, - default_codec_, settings_, index_granularity_) + : IMergeTreeDataPartWriter( + data_part_, columns_list_, metadata_snapshot_, indices_to_recalc_, marks_file_extension_, default_codec_, settings_, index_granularity_) { using DataPart = MergeTreeDataPartCompact; String data_file_name = DataPart::DATA_FILE_NAME; diff --git a/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.h b/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.h index 07caba947125..8183c038c4c7 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.h +++ b/src/Storages/MergeTree/MergeTreeDataPartWriterCompact.h @@ -10,6 +10,7 @@ class MergeTreeDataPartWriterCompact : public IMergeTreeDataPartWriter MergeTreeDataPartWriterCompact( const MergeTreeData::DataPartPtr & data_part, const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot_, const std::vector & indices_to_recalc, const String & marks_file_extension, const CompressionCodecPtr & default_codec, diff --git a/src/Storages/MergeTree/MergeTreeDataPartWriterWide.cpp b/src/Storages/MergeTree/MergeTreeDataPartWriterWide.cpp index 1ab10b554096..e71ea4d4b944 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWriterWide.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartWriterWide.cpp @@ -16,16 +16,16 @@ namespace MergeTreeDataPartWriterWide::MergeTreeDataPartWriterWide( const MergeTreeData::DataPartPtr & data_part_, const NamesAndTypesList & columns_list_, + const StorageMetadataPtr & metadata_snapshot_, const std::vector & indices_to_recalc_, const String & marks_file_extension_, const CompressionCodecPtr & default_codec_, const MergeTreeWriterSettings & settings_, const MergeTreeIndexGranularity & index_granularity_) - : IMergeTreeDataPartWriter(data_part_, columns_list_, - indices_to_recalc_, marks_file_extension_, - default_codec_, settings_, index_granularity_) + : IMergeTreeDataPartWriter( + data_part_, columns_list_, metadata_snapshot_, indices_to_recalc_, marks_file_extension_, default_codec_, settings_, index_granularity_) { - const auto & columns = storage.getColumns(); + const auto & columns = metadata_snapshot->getColumns(); for (const auto & it : columns_list) addStreams(it.name, *it.type, columns.getCodecOrDefault(it.name, default_codec), settings.estimated_size); } diff --git a/src/Storages/MergeTree/MergeTreeDataPartWriterWide.h b/src/Storages/MergeTree/MergeTreeDataPartWriterWide.h index acd7f749d00d..f5a9d17f63c6 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWriterWide.h +++ b/src/Storages/MergeTree/MergeTreeDataPartWriterWide.h @@ -13,6 +13,7 @@ class MergeTreeDataPartWriterWide : public IMergeTreeDataPartWriter MergeTreeDataPartWriterWide( const MergeTreeData::DataPartPtr & data_part, const NamesAndTypesList & columns_list, + const StorageMetadataPtr & metadata_snapshot, const std::vector & indices_to_recalc, const String & marks_file_extension, const CompressionCodecPtr & default_codec, diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 58214bae5cae..2cc5fc7dd5b6 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -100,7 +100,10 @@ static Block getBlockWithPartColumn(const MergeTreeData::DataPartsVector & parts size_t MergeTreeDataSelectExecutor::getApproximateTotalRowsToRead( - const MergeTreeData::DataPartsVector & parts, const KeyCondition & key_condition, const Settings & settings) const + const MergeTreeData::DataPartsVector & parts, + const StorageMetadataPtr & metadata_snapshot, + const KeyCondition & key_condition, + const Settings & settings) const { size_t rows_count = 0; @@ -109,7 +112,7 @@ size_t MergeTreeDataSelectExecutor::getApproximateTotalRowsToRead( for (const auto & part : parts) { - MarkRanges ranges = markRangesFromPKRange(part, key_condition, settings); + MarkRanges ranges = markRangesFromPKRange(part, metadata_snapshot, key_condition, settings); /** In order to get a lower bound on the number of rows that match the condition on PK, * consider only guaranteed full marks. @@ -224,7 +227,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( data.check(real_column_names); const Settings & settings = context.getSettingsRef(); - const auto & primary_key = data.getPrimaryKey(); + const auto & primary_key = metadata_snapshot->getPrimaryKey(); Names primary_key_columns = primary_key.column_names; KeyCondition key_condition(query_info, context, primary_key_columns, primary_key.expression); @@ -326,7 +329,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( /// Convert absolute value of the sampling (in form `SAMPLE 1000000` - how many rows to read) into the relative `SAMPLE 0.1` (how much data to read). size_t approx_total_rows = 0; if (relative_sample_size > 1 || relative_sample_offset > 1) - approx_total_rows = getApproximateTotalRowsToRead(parts, key_condition, settings); + approx_total_rows = getApproximateTotalRowsToRead(parts, metadata_snapshot, key_condition, settings); if (relative_sample_size > 1) { @@ -565,8 +568,8 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( { RangesInDataPart ranges(part, part_index++); - if (data.hasPrimaryKey()) - ranges.ranges = markRangesFromPKRange(part, key_condition, settings); + if (metadata_snapshot->hasPrimaryKey()) + ranges.ranges = markRangesFromPKRange(part, metadata_snapshot, key_condition, settings); else { size_t total_marks_count = part->getMarksCount(); @@ -1297,7 +1300,10 @@ void MergeTreeDataSelectExecutor::createPositiveSignCondition( /// Calculates a set of mark ranges, that could possibly contain keys, required by condition. /// In other words, it removes subranges from whole range, that definitely could not contain required keys. MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( - const MergeTreeData::DataPartPtr & part, const KeyCondition & key_condition, const Settings & settings) const + const MergeTreeData::DataPartPtr & part, + const StorageMetadataPtr & metadata_snapshot, + const KeyCondition & key_condition, + const Settings & settings) const { MarkRanges res; @@ -1335,7 +1341,7 @@ MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( std::function create_field_ref; /// If there are no monotonic functions, there is no need to save block reference. /// Passing explicit field to FieldRef allows to optimize ranges and shows better performance. - const auto & primary_key = data.getPrimaryKey(); + const auto & primary_key = metadata_snapshot->getPrimaryKey(); if (key_condition.hasMonotonicFunctionsChain()) { auto index_block = std::make_shared(); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h index 7811eb53b713..ba0613a832d6 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -91,6 +91,7 @@ class MergeTreeDataSelectExecutor /// Get the approximate value (bottom estimate - only by full marks) of the number of rows falling under the index. size_t getApproximateTotalRowsToRead( const MergeTreeData::DataPartsVector & parts, + const StorageMetadataPtr & metadata_snapshot, const KeyCondition & key_condition, const Settings & settings) const; @@ -102,6 +103,7 @@ class MergeTreeDataSelectExecutor MarkRanges markRangesFromPKRange( const MergeTreeData::DataPartPtr & part, + const StorageMetadataPtr & metadata_snapshot, const KeyCondition & key_condition, const Settings & settings) const; diff --git a/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp b/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp index 61f99ac6d882..7e51bcff27de 100644 --- a/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp +++ b/src/Storages/MergeTree/MergeTreeWhereOptimizer.cpp @@ -31,15 +31,16 @@ MergeTreeWhereOptimizer::MergeTreeWhereOptimizer( SelectQueryInfo & query_info, const Context & context, const MergeTreeData & data, + const StorageMetadataPtr & metadata_snapshot, const Names & queried_columns_, Poco::Logger * log_) - : table_columns{ext::map(data.getColumns().getAllPhysical(), - [] (const NameAndTypePair & col) { return col.name; })}, - queried_columns{queried_columns_}, - block_with_constants{KeyCondition::getBlockWithConstants(query_info.query, query_info.syntax_analyzer_result, context)}, - log{log_} + : table_columns{ext::map( + metadata_snapshot->getColumns().getAllPhysical(), [](const NameAndTypePair & col) { return col.name; })} + , queried_columns{queried_columns_} + , block_with_constants{KeyCondition::getBlockWithConstants(query_info.query, query_info.syntax_analyzer_result, context)} + , log{log_} { - const auto & primary_key = data.getPrimaryKey(); + const auto & primary_key = metadata_snapshot->getPrimaryKey(); if (!primary_key.column_names.empty()) first_primary_key_column = primary_key.column_names[0]; diff --git a/src/Storages/MergeTree/MergeTreeWhereOptimizer.h b/src/Storages/MergeTree/MergeTreeWhereOptimizer.h index f9fdc01812b0..866d0a8754e5 100644 --- a/src/Storages/MergeTree/MergeTreeWhereOptimizer.h +++ b/src/Storages/MergeTree/MergeTreeWhereOptimizer.h @@ -16,6 +16,8 @@ namespace DB class ASTSelectQuery; class ASTFunction; class MergeTreeData; +struct StorageInMemoryMetadata; +using StorageMetadataPtr = std::shared_ptr; /** Identifies WHERE expressions that can be placed in PREWHERE by calculating respective * sizes of columns used in particular expression and identifying "good" conditions of @@ -31,6 +33,7 @@ class MergeTreeWhereOptimizer : private boost::noncopyable SelectQueryInfo & query_info, const Context & context, const MergeTreeData & data, + const StorageMetadataPtr & metadata_snapshot, const Names & queried_columns_, Poco::Logger * log_); diff --git a/src/Storages/MergeTree/MergedBlockOutputStream.cpp b/src/Storages/MergeTree/MergedBlockOutputStream.cpp index 9cbdc3383675..e776a35f21f0 100644 --- a/src/Storages/MergeTree/MergedBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergedBlockOutputStream.cpp @@ -59,7 +59,7 @@ MergedBlockOutputStream::MergedBlockOutputStream( volume->getDisk()->createDirectories(part_path); - writer = data_part->getWriter(columns_list, skip_indices, default_codec, writer_settings); + writer = data_part->getWriter(columns_list, metadata_snapshot, skip_indices, default_codec, writer_settings); writer->initPrimaryIndex(); writer->initSkipIndices(); } @@ -169,7 +169,7 @@ void MergedBlockOutputStream::writeImpl(const Block & block, const IColumn::Perm std::inserter(skip_indexes_column_names_set, skip_indexes_column_names_set.end())); Names skip_indexes_column_names(skip_indexes_column_names_set.begin(), skip_indexes_column_names_set.end()); - Block primary_key_block = getBlockAndPermute(block, storage.getPrimaryKeyColumns(), permutation); + Block primary_key_block = getBlockAndPermute(block, metadata_snapshot->getPrimaryKeyColumns(), permutation); Block skip_indexes_block = getBlockAndPermute(block, skip_indexes_column_names, permutation); writer->write(block, permutation, primary_key_block, skip_indexes_block); diff --git a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp index b74a8243437c..1faadd0d7203 100644 --- a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp +++ b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp @@ -28,6 +28,7 @@ MergedColumnOnlyOutputStream::MergedColumnOnlyOutputStream( writer = data_part->getWriter( header.getNamesAndTypesList(), + metadata_snapshot_, indices_to_recalc, default_codec, std::move(writer_settings), diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index c3d91f4a5a99..cbb0a665b885 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -40,11 +40,11 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr /// So rules in zookeeper metadata is following: /// - When we have only ORDER BY, than store it in "primary key:" row of /metadata /// - When we have both, than store PRIMARY KEY in "primary key:" row and ORDER BY in "sorting key:" row of /metadata - if (!data.isPrimaryKeyDefined()) + if (!metadata_snapshot->isPrimaryKeyDefined()) primary_key = formattedAST(metadata_snapshot->getSortingKey().expression_list_ast); else { - primary_key = formattedAST(data.getPrimaryKey().expression_list_ast); + primary_key = formattedAST(metadata_snapshot->getPrimaryKey().expression_list_ast); sorting_key = formattedAST(metadata_snapshot->getSortingKey().expression_list_ast); } diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 6c5429fc556e..404baa6677fb 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -365,4 +365,32 @@ Names StorageInMemoryMetadata::getColumnsRequiredForSampling() const return {}; } +const KeyDescription & StorageInMemoryMetadata::getPrimaryKey() const +{ + return primary_key; +} + +bool StorageInMemoryMetadata::isPrimaryKeyDefined() const +{ + return primary_key.definition_ast != nullptr; +} + +bool StorageInMemoryMetadata::hasPrimaryKey() const +{ + return !primary_key.column_names.empty(); +} + +Names StorageInMemoryMetadata::getColumnsRequiredForPrimaryKey() const +{ + if (hasPrimaryKey()) + return primary_key.expression->getRequiredColumns(); + return {}; +} + +Names StorageInMemoryMetadata::getPrimaryKeyColumns() const +{ + if (!primary_key.column_names.empty()) + return primary_key.column_names; + return {}; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 1abea7d250c6..51036403e1f8 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -151,6 +151,21 @@ struct StorageInMemoryMetadata bool hasSamplingKey() const; /// Returns column names that need to be read to calculate sampling key. Names getColumnsRequiredForSampling() const; + + /// Returns structure with primary key. + const KeyDescription & getPrimaryKey() const; + /// Returns ASTExpressionList of primary key expression for storage or nullptr if there is none. + ASTPtr getPrimaryKeyAST() const { return primary_key.definition_ast; } + /// Storage has user-defined (in CREATE query) sorting key. + bool isPrimaryKeyDefined() const; + /// Storage has primary key (maybe part of some other key). It means, that + /// it contains at least one column. + bool hasPrimaryKey() const; + /// Returns column names that need to be read to calculate primary key. + Names getColumnsRequiredForPrimaryKey() const; + /// Returns columns names in sorting key specified by. For example: 'a', 'x + /// * y', 'toStartOfMonth(date)', etc. + Names getPrimaryKeyColumns() const; }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index 0852a96e8ba9..f8f400269407 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -384,7 +384,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { assert(table != nullptr); - if ((expression_ptr = table->getPrimaryKey().expression_list_ast)) + if ((expression_ptr = metadata_snapshot->getPrimaryKey().expression_list_ast)) res_columns[res_index++]->insert(queryToString(expression_ptr)); else res_columns[res_index++]->insertDefault(); From 7064a366e2638588aa8352ebe105c76b86bd9e92 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 15:40:27 +0300 Subject: [PATCH 0367/1102] Missed change for primary key --- src/Storages/System/StorageSystemColumns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index f998dc27cabd..83178870ba9c 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -127,7 +127,7 @@ class ColumnsSource : public SourceWithProgress cols_required_for_partition_key = metadata_snapshot->getColumnsRequiredForPartitionKey(); cols_required_for_sorting_key = metadata_snapshot->getColumnsRequiredForSortingKey(); - cols_required_for_primary_key = storage->getColumnsRequiredForPrimaryKey(); + cols_required_for_primary_key = metadata_snapshot->getColumnsRequiredForPrimaryKey(); cols_required_for_sampling = metadata_snapshot->getColumnsRequiredForSampling(); column_sizes = storage->getColumnSizes(); } From 582ea24469cb3425e55416cfc155c6dacfeaac76 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 16:38:17 +0300 Subject: [PATCH 0368/1102] Added AddingDelayedStreamStep --- src/Interpreters/InterpreterSelectQuery.cpp | 7 ++++- .../QueryPlan/AddingDelayedStreamStep.cpp | 20 ++++++++++++++ .../QueryPlan/AddingDelayedStreamStep.h | 26 +++++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/Processors/QueryPlan/AddingDelayedStreamStep.cpp create mode 100644 src/Processors/QueryPlan/AddingDelayedStreamStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index ccdf53184721..6a597b3cbf70 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -86,6 +86,7 @@ #include #include #include +#include namespace DB @@ -883,7 +884,11 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (auto stream = join->createStreamWithNonJoinedRows(join_result_sample, settings.max_block_size)) { auto source = std::make_shared(std::move(stream)); - pipeline.addDelayedStream(source); + AddingDelayedStreamStep add_non_joined_rows_step( + DataStream{.header = pipeline.getHeader()}, std::move(source)); + + add_non_joined_rows_step.setStepDescription("Add non-joined rows after JOIN"); + add_non_joined_rows_step.transformPipeline(pipeline); } } } diff --git a/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp new file mode 100644 index 000000000000..ea33bb359ae7 --- /dev/null +++ b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace DB +{ + +AddingDelayedStreamStep::AddingDelayedStreamStep( + const DataStream & input_stream_, + ProcessorPtr source_) + : ITransformingStep(input_stream_, input_stream_) + , source(std::move(source_)) +{ +} + +void AddingDelayedStreamStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addDelayedStream(source); +} + +} diff --git a/src/Processors/QueryPlan/AddingDelayedStreamStep.h b/src/Processors/QueryPlan/AddingDelayedStreamStep.h new file mode 100644 index 000000000000..457eecc0121e --- /dev/null +++ b/src/Processors/QueryPlan/AddingDelayedStreamStep.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +namespace DB +{ + +class IProcessor; +using ProcessorPtr = std::shared_ptr; + +class AddingDelayedStreamStep : public ITransformingStep +{ +public: + explicit AddingDelayedStreamStep( + const DataStream & input_stream_, + ProcessorPtr source_); + + String getName() const override { return "AddingDelayedStream"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + ProcessorPtr source; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 6e5128c2e85c..e00bcbfaf98a 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -137,6 +137,7 @@ SRCS( Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp Transforms/AggregatingInOrderTransform.cpp + QueryPlan/AddingDelayedStreamStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp From ed8f3b2fc42f73a82ca133240ff0642c990ab129 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 16:39:26 +0300 Subject: [PATCH 0369/1102] TTL in storage in memory metadata --- src/DataStreams/TTLBlockInputStream.cpp | 32 +++++++------ src/DataStreams/TTLBlockInputStream.h | 2 + src/Interpreters/InterpreterAlterQuery.cpp | 2 +- src/Interpreters/InterpreterOptimizeQuery.cpp | 3 +- src/Interpreters/MutationsInterpreter.cpp | 2 +- src/Storages/IStorage.cpp | 47 ------------------- src/Storages/IStorage.h | 27 +++-------- src/Storages/MergeTree/MergeTreeData.cpp | 6 ++- src/Storages/MergeTree/MergeTreeData.h | 20 ++++---- .../MergeTree/MergeTreeDataMergerMutator.cpp | 8 ++-- .../MergeTree/MergeTreeDataWriter.cpp | 10 ++-- src/Storages/StorageBuffer.cpp | 15 ++++-- src/Storages/StorageBuffer.h | 2 +- src/Storages/StorageInMemoryMetadata.h | 3 ++ src/Storages/StorageMaterializedView.cpp | 12 ++++- src/Storages/StorageMaterializedView.h | 8 +++- src/Storages/StorageMergeTree.cpp | 9 +++- src/Storages/StorageMergeTree.h | 8 +++- src/Storages/StorageReplicatedMergeTree.cpp | 9 +++- src/Storages/StorageReplicatedMergeTree.h | 2 +- 20 files changed, 109 insertions(+), 118 deletions(-) diff --git a/src/DataStreams/TTLBlockInputStream.cpp b/src/DataStreams/TTLBlockInputStream.cpp index c79abff98cd4..65e01a73f950 100644 --- a/src/DataStreams/TTLBlockInputStream.cpp +++ b/src/DataStreams/TTLBlockInputStream.cpp @@ -20,10 +20,12 @@ namespace ErrorCodes TTLBlockInputStream::TTLBlockInputStream( const BlockInputStreamPtr & input_, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const MergeTreeData::MutableDataPartPtr & data_part_, time_t current_time_, bool force_) : storage(storage_) + , metadata_snapshot(metadata_snapshot_) , data_part(data_part_) , current_time(current_time_) , force(force_) @@ -34,11 +36,11 @@ TTLBlockInputStream::TTLBlockInputStream( children.push_back(input_); header = children.at(0)->getHeader(); - const auto & storage_columns = storage.getColumns(); + const auto & storage_columns = metadata_snapshot->getColumns(); const auto & column_defaults = storage_columns.getDefaults(); ASTPtr default_expr_list = std::make_shared(); - for (const auto & [name, _] : storage.getColumnTTLs()) + for (const auto & [name, _] : metadata_snapshot->getColumnTTLs()) { auto it = column_defaults.find(name); if (it != column_defaults.end()) @@ -65,13 +67,12 @@ TTLBlockInputStream::TTLBlockInputStream( if (!default_expr_list->children.empty()) { - auto syntax_result = SyntaxAnalyzer(storage.global_context).analyze( - default_expr_list, storage.getColumns().getAllPhysical()); + auto syntax_result = SyntaxAnalyzer(storage.global_context).analyze(default_expr_list, metadata_snapshot->getColumns().getAllPhysical()); defaults_expression = ExpressionAnalyzer{default_expr_list, syntax_result, storage.global_context}.getActions(true); } - auto storage_rows_ttl = storage.getRowsTTL(); - if (storage.hasRowsTTL() && storage_rows_ttl.mode == TTLMode::GROUP_BY) + auto storage_rows_ttl = metadata_snapshot->getRowsTTL(); + if (metadata_snapshot->hasRowsTTL() && storage_rows_ttl.mode == TTLMode::GROUP_BY) { current_key_value.resize(storage_rows_ttl.group_by_keys.size()); @@ -106,14 +107,15 @@ bool TTLBlockInputStream::isTTLExpired(time_t ttl) const Block TTLBlockInputStream::readImpl() { /// Skip all data if table ttl is expired for part - auto storage_rows_ttl = storage.getRowsTTL(); - if (storage.hasRowsTTL() && !storage_rows_ttl.where_expression && - storage_rows_ttl.mode != TTLMode::GROUP_BY && isTTLExpired(old_ttl_infos.table_ttl.max)) + auto storage_rows_ttl = metadata_snapshot->getRowsTTL(); + if (metadata_snapshot->hasRowsTTL() && !storage_rows_ttl.where_expression && storage_rows_ttl.mode != TTLMode::GROUP_BY + && isTTLExpired(old_ttl_infos.table_ttl.max)) { rows_removed = data_part->rows_count; return {}; } + Block block = children.at(0)->read(); if (!block) { @@ -127,7 +129,7 @@ Block TTLBlockInputStream::readImpl() return block; } - if (storage.hasRowsTTL() && (force || isTTLExpired(old_ttl_infos.table_ttl.min))) + if (metadata_snapshot->hasRowsTTL() && (force || isTTLExpired(old_ttl_infos.table_ttl.min))) removeRowsWithExpiredTableTTL(block); removeValuesWithExpiredColumnTTL(block); @@ -153,7 +155,7 @@ void TTLBlockInputStream::readSuffixImpl() void TTLBlockInputStream::removeRowsWithExpiredTableTTL(Block & block) { - auto rows_ttl = storage.getRowsTTL(); + auto rows_ttl = metadata_snapshot->getRowsTTL(); rows_ttl.expression->execute(block); if (rows_ttl.where_expression) @@ -201,7 +203,7 @@ void TTLBlockInputStream::removeRowsWithExpiredTableTTL(Block & block) size_t rows_aggregated = 0; size_t current_key_start = 0; size_t rows_with_current_key = 0; - auto storage_rows_ttl = storage.getRowsTTL(); + auto storage_rows_ttl = metadata_snapshot->getRowsTTL(); for (size_t i = 0; i < block.rows(); ++i) { UInt32 cur_ttl = getTimestampByIndex(ttl_column, i); @@ -278,7 +280,7 @@ void TTLBlockInputStream::finalizeAggregates(MutableColumns & result_columns) if (!agg_result.empty()) { auto aggregated_res = aggregator->convertToBlocks(agg_result, true, 1); - auto storage_rows_ttl = storage.getRowsTTL(); + auto storage_rows_ttl = metadata_snapshot->getRowsTTL(); for (auto & agg_block : aggregated_res) { for (const auto & it : storage_rows_ttl.set_parts) @@ -310,7 +312,7 @@ void TTLBlockInputStream::removeValuesWithExpiredColumnTTL(Block & block) } std::vector columns_to_remove; - for (const auto & [name, ttl_entry] : storage.getColumnTTLs()) + for (const auto & [name, ttl_entry] : metadata_snapshot->getColumnTTLs()) { /// If we read not all table columns. E.g. while mutation. if (!block.has(name)) @@ -371,7 +373,7 @@ void TTLBlockInputStream::removeValuesWithExpiredColumnTTL(Block & block) void TTLBlockInputStream::updateMovesTTL(Block & block) { std::vector columns_to_remove; - for (const auto & ttl_entry : storage.getMoveTTLs()) + for (const auto & ttl_entry : metadata_snapshot->getMoveTTLs()) { auto & new_ttl_info = new_ttl_infos.moves_ttl[ttl_entry.result_column]; diff --git a/src/DataStreams/TTLBlockInputStream.h b/src/DataStreams/TTLBlockInputStream.h index 060306f7d2d3..3f37f35426c2 100644 --- a/src/DataStreams/TTLBlockInputStream.h +++ b/src/DataStreams/TTLBlockInputStream.h @@ -16,6 +16,7 @@ class TTLBlockInputStream : public IBlockInputStream TTLBlockInputStream( const BlockInputStreamPtr & input_, const MergeTreeData & storage_, + const StorageMetadataPtr & metadata_snapshot_, const MergeTreeData::MutableDataPartPtr & data_part_, time_t current_time, bool force_ @@ -33,6 +34,7 @@ class TTLBlockInputStream : public IBlockInputStream private: const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; /// ttl_infos and empty_columns are updating while reading const MergeTreeData::MutableDataPartPtr & data_part; diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index 3736b482ddf0..958291d5882f 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -69,7 +69,7 @@ BlockIO InterpreterAlterQuery::execute() } else if (auto mut_command = MutationCommand::parse(command_ast)) { - if (mut_command->type == MutationCommand::MATERIALIZE_TTL && !table->hasAnyTTL()) + if (mut_command->type == MutationCommand::MATERIALIZE_TTL && !metadata_snapshot->hasAnyTTL()) throw Exception("Cannot MATERIALIZE TTL as there is no TTL set for table " + table->getStorageID().getNameForLogs(), ErrorCodes::INCORRECT_QUERY); diff --git a/src/Interpreters/InterpreterOptimizeQuery.cpp b/src/Interpreters/InterpreterOptimizeQuery.cpp index c47fe1160cfd..680dd9b803b7 100644 --- a/src/Interpreters/InterpreterOptimizeQuery.cpp +++ b/src/Interpreters/InterpreterOptimizeQuery.cpp @@ -26,7 +26,8 @@ BlockIO InterpreterOptimizeQuery::execute() auto table_id = context.resolveStorageID(ast, Context::ResolveOrdinary); StoragePtr table = DatabaseCatalog::instance().getTable(table_id, context); - table->optimize(query_ptr, ast.partition, ast.final, ast.deduplicate, context); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); + table->optimize(query_ptr, metadata_snapshot, ast.partition, ast.final, ast.deduplicate, context); return {}; } diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 1a38fcf40f39..694e114af7a7 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -411,7 +411,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) } else if (command.type == MutationCommand::MATERIALIZE_TTL) { - if (storage->hasRowsTTL()) + if (metadata_snapshot->hasRowsTTL()) { for (const auto & column : all_columns) dependencies.emplace(column.name, ColumnDependency::TTL_TARGET); diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index f9b7f41f1392..3741a06fc4a0 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,53 +319,6 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -TTLTableDescription IStorage::getTableTTLs() const -{ - std::lock_guard lock(ttl_mutex); - return metadata->table_ttl; -} - -bool IStorage::hasAnyTableTTL() const -{ - return hasAnyMoveTTL() || hasRowsTTL(); -} - -TTLColumnsDescription IStorage::getColumnTTLs() const -{ - std::lock_guard lock(ttl_mutex); - return metadata->column_ttls_by_name; -} - -bool IStorage::hasAnyColumnTTL() const -{ - std::lock_guard lock(ttl_mutex); - return !metadata->column_ttls_by_name.empty(); -} - -TTLDescription IStorage::getRowsTTL() const -{ - std::lock_guard lock(ttl_mutex); - return metadata->table_ttl.rows_ttl; -} - -bool IStorage::hasRowsTTL() const -{ - std::lock_guard lock(ttl_mutex); - return metadata->table_ttl.rows_ttl.expression != nullptr; -} - -TTLDescriptions IStorage::getMoveTTLs() const -{ - std::lock_guard lock(ttl_mutex); - return metadata->table_ttl.move_ttl; -} - -bool IStorage::hasAnyMoveTTL() const -{ - std::lock_guard lock(ttl_mutex); - return !metadata->table_ttl.move_ttl.empty(); -} - ASTPtr IStorage::getSettingsChanges() const { if (metadata->settings_changes) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 787b96c91970..4eea343db5db 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -129,8 +129,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Example is StorageSystemNumbers. virtual bool hasEvenlyDistributedRead() const { return false; } - /// Returns true if there is set table TTL, any column TTL or any move TTL. - bool hasAnyTTL() const { return hasAnyColumnTTL() || hasAnyTableTTL(); } /// Optional size information of each physical column. /// Currently it's only used by the MergeTree family for query optimizations. @@ -362,7 +360,13 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /** Perform any background work. For example, combining parts in a MergeTree type table. * Returns whether any work has been done. */ - virtual bool optimize(const ASTPtr & /*query*/, const ASTPtr & /*partition*/, bool /*final*/, bool /*deduplicate*/, const Context & /*context*/) + virtual bool optimize( + const ASTPtr & /*query*/, + const StorageMetadataPtr & /*metadata_snapshot*/, + const ASTPtr & /*partition*/, + bool /*final*/, + bool /*deduplicate*/, + const Context & /*context*/) { throw Exception("Method optimize is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } @@ -430,23 +434,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } - /// Common tables TTLs (for rows and moves). - TTLTableDescription getTableTTLs() const; - bool hasAnyTableTTL() const; - - /// Separate TTLs for columns. - TTLColumnsDescription getColumnTTLs() const; - bool hasAnyColumnTTL() const; - - /// Just wrapper for table TTLs, return rows part of table TTLs. - TTLDescription getRowsTTL() const; - bool hasRowsTTL() const; - - /// Just wrapper for table TTLs, return moves (to disks or volumes) parts of - /// table TTL. - TTLDescriptions getMoveTTLs() const; - bool hasAnyMoveTTL() const; - /// If it is possible to quickly determine exact number of rows in the table at this moment of time, then return it. /// Used for: /// - Simple count() opimization diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index ff38a21a15f9..8c94325cd4b2 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -2801,8 +2801,9 @@ MergeTreeData::selectTTLEntryForTTLInfos(const IMergeTreeDataPart::TTLInfos & tt { time_t max_max_ttl = 0; TTLDescriptions::const_iterator best_entry_it; + auto metadata_snapshot = getInMemoryMetadataPtr(); - const auto & move_ttl_entries = getMoveTTLs(); + const auto & move_ttl_entries = metadata_snapshot->getMoveTTLs(); for (auto ttl_entry_it = move_ttl_entries.begin(); ttl_entry_it != move_ttl_entries.end(); ++ttl_entry_it) { auto ttl_info_it = ttl_infos.moves_ttl.find(ttl_entry_it->result_column); @@ -3235,11 +3236,12 @@ bool MergeTreeData::selectPartsAndMove() bool MergeTreeData::areBackgroundMovesNeeded() const { auto policy = getStoragePolicy(); + auto metadata_snapshot = getInMemoryMetadataPtr(); if (policy->getVolumes().size() > 1) return true; - return policy->getVolumes().size() == 1 && policy->getVolumes()[0]->getDisks().size() > 1 && hasAnyMoveTTL(); + return policy->getVolumes().size() == 1 && policy->getVolumes()[0]->getDisks().size() > 1 && metadata_snapshot->hasAnyMoveTTL(); } bool MergeTreeData::movePartsToSpace(const DataPartsVector & parts, SpacePtr space) diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index fdbe36d10ec1..af6bee4936cf 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -607,14 +607,18 @@ class MergeTreeData : public IStorage static ReservationPtr tryReserveSpace(UInt64 expected_size, SpacePtr space); /// Reserves space at least 1MB preferring best destination according to `ttl_infos`. - ReservationPtr reserveSpacePreferringTTLRules(UInt64 expected_size, - const IMergeTreeDataPart::TTLInfos & ttl_infos, - time_t time_of_move, - size_t min_volume_index = 0) const; - ReservationPtr tryReserveSpacePreferringTTLRules(UInt64 expected_size, - const IMergeTreeDataPart::TTLInfos & ttl_infos, - time_t time_of_move, - size_t min_volume_index = 0) const; + ReservationPtr reserveSpacePreferringTTLRules( + UInt64 expected_size, + const IMergeTreeDataPart::TTLInfos & ttl_infos, + time_t time_of_move, + size_t min_volume_index = 0) const; + + ReservationPtr tryReserveSpacePreferringTTLRules( + UInt64 expected_size, + const IMergeTreeDataPart::TTLInfos & ttl_infos, + time_t time_of_move, + size_t min_volume_index = 0) const; + /// Choose disk with max available free space /// Reserves 0 bytes ReservationPtr makeEmptyReservationOnLargestDisk() { return getStoragePolicy()->makeEmptyReservationOnLargestDisk(); } diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index fad65e492c05..3024adafb273 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -801,7 +801,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor merged_stream = std::make_shared(merged_stream, SizeLimits(), 0 /*limit_hint*/, Names()); if (need_remove_expired_values) - merged_stream = std::make_shared(merged_stream, data, new_data_part, time_of_merge, force_ttl); + merged_stream = std::make_shared(merged_stream, data, metadata_snapshot, new_data_part, time_of_merge, force_ttl); if (metadata_snapshot->hasSecondaryIndices()) @@ -1576,7 +1576,7 @@ std::set MergeTreeDataMergerMutator::getIndicesToRecalculate( bool MergeTreeDataMergerMutator::shouldExecuteTTL(const StorageMetadataPtr & metadata_snapshot, const Names & columns, const MutationCommands & commands) const { - if (!data.hasAnyTTL()) + if (!metadata_snapshot->hasAnyTTL()) return false; for (const auto & command : commands) @@ -1609,7 +1609,7 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( std::make_shared(mutating_stream, data.getPrimaryKeyAndSkipIndicesExpression(metadata_snapshot))); if (need_remove_expired_values) - mutating_stream = std::make_shared(mutating_stream, data, new_data_part, time_of_mutation, true); + mutating_stream = std::make_shared(mutating_stream, data, metadata_snapshot, new_data_part, time_of_mutation, true); IMergeTreeDataPart::MinMaxIndex minmax_idx; @@ -1656,7 +1656,7 @@ void MergeTreeDataMergerMutator::mutateSomePartColumns( throw Exception("Cannot mutate part columns with uninitialized mutations stream. It's a bug", ErrorCodes::LOGICAL_ERROR); if (need_remove_expired_values) - mutating_stream = std::make_shared(mutating_stream, data, new_data_part, time_of_mutation, true); + mutating_stream = std::make_shared(mutating_stream, data, metadata_snapshot, new_data_part, time_of_mutation, true); IMergedBlockOutputStream::WrittenOffsetColumns unused_written_offsets; MergedColumnOnlyOutputStream out( diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index c31cfd3da6f7..5974f366b66b 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -230,11 +230,11 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa size_t expected_size = block.bytes(); DB::IMergeTreeDataPart::TTLInfos move_ttl_infos; - const auto & move_ttl_entries = data.getMoveTTLs(); + const auto & move_ttl_entries = metadata_snapshot->getMoveTTLs(); for (const auto & ttl_entry : move_ttl_entries) updateTTL(ttl_entry, move_ttl_infos, move_ttl_infos.moves_ttl[ttl_entry.result_column], block, false); - NamesAndTypesList columns = data.getColumns().getAllPhysical().filter(block.getNames()); + NamesAndTypesList columns = metadata_snapshot->getColumns().getAllPhysical().filter(block.getNames()); ReservationPtr reservation = data.reserveSpacePreferringTTLRules(expected_size, move_ttl_infos, time(nullptr)); VolumePtr volume = data.getStoragePolicy()->getVolume(0); @@ -289,10 +289,10 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa ProfileEvents::increment(ProfileEvents::MergeTreeDataWriterBlocksAlreadySorted); } - if (data.hasRowsTTL()) - updateTTL(data.getRowsTTL(), new_data_part->ttl_infos, new_data_part->ttl_infos.table_ttl, block, true); + if (metadata_snapshot->hasRowsTTL()) + updateTTL(metadata_snapshot->getRowsTTL(), new_data_part->ttl_infos, new_data_part->ttl_infos.table_ttl, block, true); - for (const auto & [name, ttl_entry] : data.getColumnTTLs()) + for (const auto & [name, ttl_entry] : metadata_snapshot->getColumnTTLs()) updateTTL(ttl_entry, new_data_part->ttl_infos, new_data_part->ttl_infos.columns_ttl[name], block, true); new_data_part->ttl_infos.update(move_ttl_infos); diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 5eaaf98d397f..13b37980c569 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -484,7 +484,7 @@ void StorageBuffer::shutdown() try { - optimize(nullptr /*query*/, {} /*partition*/, false /*final*/, false /*deduplicate*/, global_context); + optimize(nullptr /*query*/, getInMemoryMetadataPtr(), {} /*partition*/, false /*final*/, false /*deduplicate*/, global_context); } catch (...) { @@ -503,7 +503,13 @@ void StorageBuffer::shutdown() * * This kind of race condition make very hard to implement proper tests. */ -bool StorageBuffer::optimize(const ASTPtr & /*query*/, const ASTPtr & partition, bool final, bool deduplicate, const Context & /*context*/) +bool StorageBuffer::optimize( + const ASTPtr & /*query*/, + const StorageMetadataPtr & /*metadata_snapshot*/, + const ASTPtr & partition, + bool final, + bool deduplicate, + const Context & /*context*/) { if (partition) throw Exception("Partition cannot be specified when optimizing table of type Buffer", ErrorCodes::NOT_IMPLEMENTED); @@ -793,11 +799,12 @@ void StorageBuffer::alter(const AlterCommands & params, const Context & context, auto table_id = getStorageID(); checkAlterIsPossible(params, context.getSettingsRef()); + auto metadata_snapshot = getInMemoryMetadataPtr(); /// So that no blocks of the old structure remain. - optimize({} /*query*/, {} /*partition_id*/, false /*final*/, false /*deduplicate*/, context); + optimize({} /*query*/, metadata_snapshot, {} /*partition_id*/, false /*final*/, false /*deduplicate*/, context); - StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + StorageInMemoryMetadata new_metadata = *metadata_snapshot; params.apply(new_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); setInMemoryMetadata(new_metadata); diff --git a/src/Storages/StorageBuffer.h b/src/Storages/StorageBuffer.h index 403b6c53172c..ceedbd25a0ca 100644 --- a/src/Storages/StorageBuffer.h +++ b/src/Storages/StorageBuffer.h @@ -69,7 +69,7 @@ friend class BufferBlockOutputStream; void startup() override; /// Flush all buffers into the subordinate table and stop background thread. void shutdown() override; - bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override; + bool optimize(const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override; bool supportsSampling() const override { return true; } bool supportsPrewhere() const override diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 51036403e1f8..cc6fcfbe0831 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -86,6 +86,9 @@ struct StorageInMemoryMetadata const ConstraintsDescription & getConstraints() const; + /// Returns true if there is set table TTL, any column TTL or any move TTL. + bool hasAnyTTL() const { return hasAnyColumnTTL() || hasAnyTableTTL(); } + /// Common tables TTLs (for rows and moves). TTLTableDescription getTableTTLs() const; bool hasAnyTableTTL() const; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 4eba4d6a165b..319e1631ae6f 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -187,10 +187,18 @@ void StorageMaterializedView::checkStatementCanBeForwarded() const + "Execute the statement directly on it.", ErrorCodes::INCORRECT_QUERY); } -bool StorageMaterializedView::optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) +bool StorageMaterializedView::optimize( + const ASTPtr & query, + const StorageMetadataPtr & /*metadata_snapshot*/, + const ASTPtr & partition, + bool final, + bool deduplicate, + const Context & context) { checkStatementCanBeForwarded(); - return getTargetTable()->optimize(query, partition, final, deduplicate, context); + auto storage_ptr = getTargetTable(); + auto metadata_snapshot = storage_ptr->getInMemoryMetadataPtr(); + return getTargetTable()->optimize(query, metadata_snapshot, partition, final, deduplicate, context); } void StorageMaterializedView::alter( diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index ef895ff01659..6f462c2ccccf 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -39,7 +39,13 @@ class StorageMaterializedView final : public ext::shared_ptr_helperhasAnyTTL()); new_part = merger_mutator.mergePartsToTemporaryPart( future_part, metadata_snapshot, *merge_entry, table_lock_holder, time(nullptr), @@ -965,7 +965,12 @@ void StorageMergeTree::clearOldMutations(bool truncate) } bool StorageMergeTree::optimize( - const ASTPtr & /*query*/, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) + const ASTPtr & /*query*/, + const StorageMetadataPtr & /*metadata_snapshot*/, + const ASTPtr & partition, + bool final, + bool deduplicate, + const Context & context) { String disable_reason; if (!partition && final) diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index 4b6da58572b8..69ee6714164f 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -53,7 +53,13 @@ class StorageMergeTree final : public ext::shared_ptr_helper, /** Perform the next step in combining the parts. */ - bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override; + bool optimize( + const ASTPtr & query, + const StorageMetadataPtr & /*metadata_snapshot*/, + const ASTPtr & partition, + bool final, + bool deduplicate, + const Context & context) override; void alterPartition( const ASTPtr & query, diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 061be8b2821b..b0a7e5502339 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3475,7 +3475,12 @@ BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, bool StorageReplicatedMergeTree::optimize( - const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & query_context) + const ASTPtr & query, + const StorageMetadataPtr & metadata_snapshot, + const ASTPtr & partition, + bool final, + bool deduplicate, + const Context & query_context) { assertNotReadonly(); @@ -3498,7 +3503,7 @@ bool StorageReplicatedMergeTree::optimize( return false; }; - bool force_ttl = (final && hasAnyTTL()); + bool force_ttl = (final && metadata_snapshot->hasAnyTTL()); const auto storage_settings_ptr = getSettings(); if (!partition && final) diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index fe1b052b7170..c98fcb0ae3d8 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -101,7 +101,7 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helper Date: Wed, 17 Jun 2020 16:46:01 +0300 Subject: [PATCH 0370/1102] Settings changes in StorageInMemoryMetadata --- src/Storages/IStorage.cpp | 7 ------- src/Storages/IStorage.h | 5 ----- src/Storages/MergeTree/MergeTreeData.cpp | 4 ++-- src/Storages/StorageInMemoryMetadata.cpp | 8 ++++++++ src/Storages/StorageInMemoryMetadata.h | 4 ++++ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 3741a06fc4a0..6b3aafa17845 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,13 +319,6 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -ASTPtr IStorage::getSettingsChanges() const -{ - if (metadata->settings_changes) - return metadata->settings_changes->clone(); - return nullptr; -} - const SelectQueryDescription & IStorage::getSelectQuery() const { return metadata->select; diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 4eea343db5db..d9e9aa247c22 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -138,11 +138,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired const ColumnsDescription & getColumns() const; /// returns combined set of columns - - /// Storage settings - ASTPtr getSettingsChanges() const; - bool hasSettingsChanges() const { return metadata->settings_changes != nullptr; } - /// Select query for *View storages. const SelectQueryDescription & getSelectQuery() const; bool hasSelectQuery() const; diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 8c94325cd4b2..115e0b78bf09 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -1333,10 +1333,10 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S checkTTLExpressions(new_metadata, old_metadata); - if (hasSettingsChanges()) + if (old_metadata.hasSettingsChanges()) { - const auto current_changes = getSettingsChanges()->as().changes; + const auto current_changes = old_metadata.getSettingsChanges()->as().changes; const auto & new_changes = new_metadata.settings_changes->as().changes; for (const auto & changed_setting : new_changes) { diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 404baa6677fb..5f8c83d6e14c 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -393,4 +393,12 @@ Names StorageInMemoryMetadata::getPrimaryKeyColumns() const return primary_key.column_names; return {}; } + +ASTPtr StorageInMemoryMetadata::getSettingsChanges() const +{ + if (settings_changes) + return settings_changes->clone(); + return nullptr; +} + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index cc6fcfbe0831..1de17d768ae4 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -169,6 +169,10 @@ struct StorageInMemoryMetadata /// Returns columns names in sorting key specified by. For example: 'a', 'x /// * y', 'toStartOfMonth(date)', etc. Names getPrimaryKeyColumns() const; + + /// Storage settings + ASTPtr getSettingsChanges() const; + bool hasSettingsChanges() const { return settings_changes != nullptr; } }; using StorageMetadataPtr = std::shared_ptr; From 31abbe5dbd3b43c2968bb558c747598a6904f326 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 17:06:22 +0300 Subject: [PATCH 0371/1102] Select query in metadata --- src/DataStreams/PushingToViewsBlockOutputStream.cpp | 2 +- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- src/Storages/IStorage.cpp | 10 ---------- src/Storages/IStorage.h | 3 --- src/Storages/StorageInMemoryMetadata.cpp | 9 +++++++++ src/Storages/StorageInMemoryMetadata.h | 4 ++++ src/Storages/StorageMaterializedView.cpp | 11 +++++++---- src/Storages/StorageView.cpp | 2 +- src/Storages/StorageView.h | 4 ++-- 9 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index 2e02c26d38cf..e6e368f78e9b 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -79,7 +79,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( StoragePtr inner_table = materialized_view->getTargetTable(); auto inner_table_id = inner_table->getStorageID(); - query = materialized_view->getSelectQuery().inner_query; + query = dependent_metadata_snapshot->getSelectQuery().inner_query; std::unique_ptr insert = std::make_unique(); insert->table_id = inner_table_id; diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 41b2abc33c6e..509825e75e44 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -308,7 +308,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( /// Allow push down and other optimizations for VIEW: replace with subquery and rewrite it. ASTPtr view_table; if (view) - view->replaceWithSubquery(getSelectQuery(), view_table); + view->replaceWithSubquery(getSelectQuery(), view_table, metadata_snapshot); syntax_analyzer_result = SyntaxAnalyzer(*context).analyzeSelect( query_ptr, SyntaxAnalyzerResult(source_header.getNamesAndTypesList(), storage), diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 6b3aafa17845..a67229d6231e 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -319,14 +319,4 @@ NamesAndTypesList IStorage::getVirtuals() const return {}; } -const SelectQueryDescription & IStorage::getSelectQuery() const -{ - return metadata->select; -} - -bool IStorage::hasSelectQuery() const -{ - return metadata->select.select_query != nullptr; -} - } diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index d9e9aa247c22..4b712853b537 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -138,9 +138,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired const ColumnsDescription & getColumns() const; /// returns combined set of columns - /// Select query for *View storages. - const SelectQueryDescription & getSelectQuery() const; - bool hasSelectQuery() const; StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 5f8c83d6e14c..3b72dd970891 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -400,5 +400,14 @@ ASTPtr StorageInMemoryMetadata::getSettingsChanges() const return settings_changes->clone(); return nullptr; } +const SelectQueryDescription & StorageInMemoryMetadata::getSelectQuery() const +{ + return select; +} + +bool StorageInMemoryMetadata::hasSelectQuery() const +{ + return select.select_query != nullptr; +} } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 1de17d768ae4..efda4377dfc9 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -173,6 +173,10 @@ struct StorageInMemoryMetadata /// Storage settings ASTPtr getSettingsChanges() const; bool hasSettingsChanges() const { return settings_changes != nullptr; } + + /// Select query for *View storages. + const SelectQueryDescription & getSelectQuery() const; + bool hasSelectQuery() const; }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 319e1631ae6f..3d3137fe1a68 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -165,7 +165,7 @@ static void executeDropQuery(ASTDropQuery::Kind kind, Context & global_context, void StorageMaterializedView::drop() { auto table_id = getStorageID(); - const auto & select_query = getSelectQuery(); + const auto & select_query = getInMemoryMetadataPtr()->getSelectQuery(); if (!select_query.select_table_id.empty()) DatabaseCatalog::instance().removeDependency(select_query.select_table_id, table_id); @@ -209,13 +209,14 @@ void StorageMaterializedView::alter( lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); + StorageInMemoryMetadata old_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); /// start modify query if (context.getSettingsRef().allow_experimental_alter_materialized_view_structure) { const auto & new_select = new_metadata.select; - const auto & old_select = getSelectQuery(); + const auto & old_select = old_metadata.getSelectQuery(); DatabaseCatalog::instance().updateDependency(old_select.select_table_id, table_id, new_select.select_table_id, table_id); @@ -268,6 +269,7 @@ void StorageMaterializedView::mutate(const MutationCommands & commands, const Co void StorageMaterializedView::renameInMemory(const StorageID & new_table_id) { auto old_table_id = getStorageID(); + auto metadata_snapshot = getInMemoryMetadataPtr(); bool from_atomic_to_atomic_database = old_table_id.hasUUID() && new_table_id.hasUUID(); if (has_inner_table && tryGetTargetTable() && !from_atomic_to_atomic_database) @@ -293,14 +295,15 @@ void StorageMaterializedView::renameInMemory(const StorageID & new_table_id) } IStorage::renameInMemory(new_table_id); - const auto & select_query = getSelectQuery(); + const auto & select_query = metadata_snapshot->getSelectQuery(); // TODO Actually we don't need to update dependency if MV has UUID, but then db and table name will be outdated DatabaseCatalog::instance().updateDependency(select_query.select_table_id, old_table_id, select_query.select_table_id, getStorageID()); } void StorageMaterializedView::shutdown() { - const auto & select_query = getSelectQuery(); + auto metadata_snapshot = getInMemoryMetadataPtr(); + const auto & select_query = metadata_snapshot->getSelectQuery(); /// Make sure the dependency is removed after DETACH TABLE if (!select_query.select_table_id.empty()) DatabaseCatalog::instance().removeDependency(select_query.select_table_id, getStorageID()); diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 7e49580d6c2a..006b1b3caec1 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -63,7 +63,7 @@ Pipes StorageView::read( { Pipes pipes; - ASTPtr current_inner_query = getSelectQuery().inner_query; + ASTPtr current_inner_query = metadata_snapshot->getSelectQuery().inner_query; if (query_info.view_query) { diff --git a/src/Storages/StorageView.h b/src/Storages/StorageView.h index 143ed3c06c41..9de1f3f0bd80 100644 --- a/src/Storages/StorageView.h +++ b/src/Storages/StorageView.h @@ -30,9 +30,9 @@ class StorageView final : public ext::shared_ptr_helper, public ISt size_t max_block_size, unsigned num_streams) override; - void replaceWithSubquery(ASTSelectQuery & select_query, ASTPtr & view_name) const + void replaceWithSubquery(ASTSelectQuery & select_query, ASTPtr & view_name, const StorageMetadataPtr & metadata_snapshot) const { - replaceWithSubquery(select_query, getSelectQuery().inner_query->clone(), view_name); + replaceWithSubquery(select_query, metadata_snapshot->getSelectQuery().inner_query->clone(), view_name); } static void replaceWithSubquery(ASTSelectQuery & outer_query, ASTPtr view_query, ASTPtr & view_name); From f279df60152c9f0d28b26246e7eff017c69f2ba6 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 17:21:48 +0300 Subject: [PATCH 0372/1102] Added AggregatingStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 106 ++++------------ .../QueryPlan/AddingDelayedStreamStep.h | 2 +- src/Processors/QueryPlan/AggregatingStep.cpp | 119 ++++++++++++++++++ src/Processors/QueryPlan/AggregatingStep.h | 42 +++++++ src/Processors/ya.make | 1 + 5 files changed, 184 insertions(+), 86 deletions(-) create mode 100644 src/Processors/QueryPlan/AggregatingStep.cpp create mode 100644 src/Processors/QueryPlan/AggregatingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 6a597b3cbf70..e64595dd757e 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -87,6 +87,7 @@ #include #include #include +#include namespace DB @@ -1422,96 +1423,31 @@ void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const auto transform_params = std::make_shared(params, final); - /// Forget about current totals and extremes. They will be calculated again after aggregation if needed. - pipeline.dropTotalsAndExtremes(); + SortDescription group_by_sort_description; if (group_by_info && settings.optimize_aggregation_in_order) - { - auto & query = getSelectQuery(); - SortDescription group_by_descr = getSortDescriptionFromGroupBy(query); - bool need_finish_sorting = (group_by_info->order_key_prefix_descr.size() < group_by_descr.size()); - - if (need_finish_sorting) - { - /// TOO SLOW - } - else - { - if (pipeline.getNumStreams() > 1) - { - auto many_data = std::make_shared(pipeline.getNumStreams()); - size_t counter = 0; - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params, group_by_descr, settings.max_block_size, many_data, counter++); - }); - - for (auto & column_description : group_by_descr) - { - if (!column_description.column_name.empty()) - { - column_description.column_number = pipeline.getHeader().getPositionByName(column_description.column_name); - column_description.column_name.clear(); - } - } - - auto transform = std::make_shared( - pipeline.getHeader(), - pipeline.getNumStreams(), - group_by_descr, - settings.max_block_size); - - pipeline.addPipe({ std::move(transform) }); - } - else - { - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params, group_by_descr, settings.max_block_size); - }); - } - - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params); - }); - - pipeline.enableQuotaForCurrentStreams(); - return; - } - } - - /// If there are several sources, then we perform parallel aggregation - if (pipeline.getNumStreams() > 1) - { - /// Add resize transform to uniformly distribute data between aggregating streams. - if (!(storage && storage->hasEvenlyDistributedRead())) - pipeline.resize(pipeline.getNumStreams(), true, true); - - auto many_data = std::make_shared(pipeline.getNumStreams()); - auto merge_threads = settings.aggregation_memory_efficient_merge_threads - ? static_cast(settings.aggregation_memory_efficient_merge_threads) - : static_cast(settings.max_threads); - - size_t counter = 0; - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params, many_data, counter++, max_streams, merge_threads); - }); - - pipeline.resize(1); - } + group_by_sort_description = getSortDescriptionFromGroupBy(getSelectQuery()); else - { - pipeline.resize(1); + group_by_info = nullptr; - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, transform_params); - }); - } + auto merge_threads = max_streams; + auto temporary_data_merge_threads = settings.aggregation_memory_efficient_merge_threads + ? static_cast(settings.aggregation_memory_efficient_merge_threads) + : static_cast(settings.max_threads); - pipeline.enableQuotaForCurrentStreams(); + bool storage_has_evenly_distributed_read = storage && storage->hasEvenlyDistributedRead(); + + AggregatingStep aggregating_step( + DataStream{.header = pipeline.getHeader()}, + std::move(transform_params), + settings.max_block_size, + merge_threads, + temporary_data_merge_threads, + storage_has_evenly_distributed_read, + std::move(group_by_info), + std::move(group_by_sort_description)); + + aggregating_step.transformPipeline(pipeline); } diff --git a/src/Processors/QueryPlan/AddingDelayedStreamStep.h b/src/Processors/QueryPlan/AddingDelayedStreamStep.h index 457eecc0121e..4bf2c62c9053 100644 --- a/src/Processors/QueryPlan/AddingDelayedStreamStep.h +++ b/src/Processors/QueryPlan/AddingDelayedStreamStep.h @@ -11,7 +11,7 @@ using ProcessorPtr = std::shared_ptr; class AddingDelayedStreamStep : public ITransformingStep { public: - explicit AddingDelayedStreamStep( + AddingDelayedStreamStep( const DataStream & input_stream_, ProcessorPtr source_); diff --git a/src/Processors/QueryPlan/AggregatingStep.cpp b/src/Processors/QueryPlan/AggregatingStep.cpp new file mode 100644 index 000000000000..7649d0085c34 --- /dev/null +++ b/src/Processors/QueryPlan/AggregatingStep.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +namespace DB +{ + +AggregatingStep::AggregatingStep( + const DataStream & input_stream_, + AggregatingTransformParamsPtr transform_params_, + size_t max_block_size_, + size_t merge_threads_, + size_t temporary_data_merge_threads_, + bool storage_has_evenly_distributed_read_, + InputOrderInfoPtr group_by_info_, + SortDescription group_by_sort_description_) + : ITransformingStep(input_stream_, input_stream_) + , transform_params(std::move(transform_params_)) + , max_block_size(max_block_size_) + , merge_threads(merge_threads_) + , temporary_data_merge_threads(temporary_data_merge_threads_) + , storage_has_evenly_distributed_read(storage_has_evenly_distributed_read_) + , group_by_info(std::move(group_by_info_)) + , group_by_sort_description(std::move(group_by_sort_description_)) +{ +} + +void AggregatingStep::transformPipeline(QueryPipeline & pipeline) +{ + /// Forget about current totals and extremes. They will be calculated again after aggregation if needed. + pipeline.dropTotalsAndExtremes(); + + if (group_by_info) + { + bool need_finish_sorting = (group_by_info->order_key_prefix_descr.size() < group_by_sort_description.size()); + + if (need_finish_sorting) + { + /// TOO SLOW + } + else + { + if (pipeline.getNumStreams() > 1) + { + auto many_data = std::make_shared(pipeline.getNumStreams()); + size_t counter = 0; + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params, group_by_sort_description, max_block_size, many_data, counter++); + }); + + for (auto & column_description : group_by_sort_description) + { + if (!column_description.column_name.empty()) + { + column_description.column_number = pipeline.getHeader().getPositionByName(column_description.column_name); + column_description.column_name.clear(); + } + } + + auto transform = std::make_shared( + pipeline.getHeader(), + pipeline.getNumStreams(), + group_by_sort_description, + max_block_size); + + pipeline.addPipe({ std::move(transform) }); + } + else + { + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params, group_by_sort_description, max_block_size); + }); + } + + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params); + }); + + pipeline.enableQuotaForCurrentStreams(); + return; + } + } + + /// If there are several sources, then we perform parallel aggregation + if (pipeline.getNumStreams() > 1) + { + /// Add resize transform to uniformly distribute data between aggregating streams. + if (!storage_has_evenly_distributed_read) + pipeline.resize(pipeline.getNumStreams(), true, true); + + auto many_data = std::make_shared(pipeline.getNumStreams()); + + size_t counter = 0; + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params, many_data, counter++, merge_threads, temporary_data_merge_threads); + }); + + pipeline.resize(1); + } + else + { + pipeline.resize(1); + + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params); + }); + } + + pipeline.enableQuotaForCurrentStreams(); +} + +} diff --git a/src/Processors/QueryPlan/AggregatingStep.h b/src/Processors/QueryPlan/AggregatingStep.h new file mode 100644 index 000000000000..6a42929b785b --- /dev/null +++ b/src/Processors/QueryPlan/AggregatingStep.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +namespace DB +{ + +struct AggregatingTransformParams; +using AggregatingTransformParamsPtr = std::shared_ptr; + +class AggregatingStep : public ITransformingStep +{ +public: + AggregatingStep( + const DataStream & input_stream_, + AggregatingTransformParamsPtr transform_params_, + size_t max_block_size_, + size_t merge_threads_, + size_t temporary_data_merge_threads_, + bool storage_has_evenly_distributed_read_, + InputOrderInfoPtr group_by_info_, + SortDescription group_by_sort_description_); + + String getName() const override { return "Aggregating"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + AggregatingTransformParamsPtr transform_params; + size_t max_block_size; + size_t merge_threads; + size_t temporary_data_merge_threads; + + bool storage_has_evenly_distributed_read; + + InputOrderInfoPtr group_by_info; + SortDescription group_by_sort_description; +}; + +} + diff --git a/src/Processors/ya.make b/src/Processors/ya.make index e00bcbfaf98a..8d373fbf05d3 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -138,6 +138,7 @@ SRCS( Transforms/TotalsHavingTransform.cpp Transforms/AggregatingInOrderTransform.cpp QueryPlan/AddingDelayedStreamStep.cpp + QueryPlan/AggregatingStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp From 33c27de54d535fdc3f0445db55638025644cfbad Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 17:32:25 +0300 Subject: [PATCH 0373/1102] Check methods in metadata --- src/Storages/IStorage.cpp | 170 ----------------- src/Storages/IStorage.h | 15 -- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 2 +- .../MergeTree/MergeTreeDataWriter.cpp | 2 +- .../ReplicatedMergeTreeBlockOutputStream.cpp | 2 +- src/Storages/StorageBuffer.cpp | 2 +- src/Storages/StorageGenerateRandom.cpp | 4 +- src/Storages/StorageInMemoryMetadata.cpp | 172 ++++++++++++++++++ src/Storages/StorageInMemoryMetadata.h | 15 ++ src/Storages/StorageJoin.cpp | 2 +- src/Storages/StorageLog.cpp | 6 +- src/Storages/StorageMemory.cpp | 4 +- src/Storages/StorageMySQL.cpp | 2 +- src/Storages/StorageStripeLog.cpp | 2 +- src/Storages/StorageTinyLog.cpp | 4 +- src/Storages/StorageValues.cpp | 4 +- src/Storages/StorageXDBC.cpp | 2 +- src/Storages/System/IStorageSystemOneBlock.h | 2 +- src/Storages/System/StorageSystemColumns.cpp | 2 +- src/Storages/System/StorageSystemDisks.cpp | 2 +- src/Storages/System/StorageSystemNumbers.cpp | 4 +- src/Storages/System/StorageSystemOne.cpp | 4 +- .../System/StorageSystemPartsBase.cpp | 6 +- src/Storages/System/StorageSystemPartsBase.h | 4 +- src/Storages/System/StorageSystemReplicas.cpp | 2 +- .../System/StorageSystemStoragePolicies.cpp | 2 +- src/Storages/System/StorageSystemTables.cpp | 2 +- src/Storages/System/StorageSystemZeros.cpp | 4 +- 28 files changed, 223 insertions(+), 221 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index a67229d6231e..38fdaa832bd5 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -37,176 +37,6 @@ const ColumnsDescription & IStorage::getColumns() const return metadata->columns; } -namespace -{ -#if !defined(ARCADIA_BUILD) - using NamesAndTypesMap = google::dense_hash_map; - using UniqueStrings = google::dense_hash_set; -#else - using NamesAndTypesMap = google::sparsehash::dense_hash_map; - using UniqueStrings = google::sparsehash::dense_hash_set; -#endif - - String listOfColumns(const NamesAndTypesList & available_columns) - { - std::stringstream ss; - for (auto it = available_columns.begin(); it != available_columns.end(); ++it) - { - if (it != available_columns.begin()) - ss << ", "; - ss << it->name; - } - return ss.str(); - } - - NamesAndTypesMap getColumnsMap(const NamesAndTypesList & columns) - { - NamesAndTypesMap res; - res.set_empty_key(StringRef()); - - for (const auto & column : columns) - res.insert({column.name, column.type.get()}); - - return res; - } - - UniqueStrings initUniqueStrings() - { - UniqueStrings strings; - strings.set_empty_key(StringRef()); - return strings; - } -} - -void IStorage::check(const Names & column_names, bool include_virtuals) const -{ - NamesAndTypesList available_columns = getColumns().getAllPhysical(); - if (include_virtuals) - { - auto virtuals = getVirtuals(); - available_columns.insert(available_columns.end(), virtuals.begin(), virtuals.end()); - } - - const String list_of_columns = listOfColumns(available_columns); - - if (column_names.empty()) - throw Exception("Empty list of columns queried. There are columns: " + list_of_columns, ErrorCodes::EMPTY_LIST_OF_COLUMNS_QUERIED); - - const auto columns_map = getColumnsMap(available_columns); - - auto unique_names = initUniqueStrings(); - for (const auto & name : column_names) - { - if (columns_map.end() == columns_map.find(name)) - throw Exception( - "There is no column with name " + backQuote(name) + " in table " + getStorageID().getNameForLogs() + ". There are columns: " + list_of_columns, - ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); - - if (unique_names.end() != unique_names.find(name)) - throw Exception("Column " + name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); - unique_names.insert(name); - } -} - -void IStorage::check(const NamesAndTypesList & provided_columns) const -{ - const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); - const auto columns_map = getColumnsMap(available_columns); - - auto unique_names = initUniqueStrings(); - for (const NameAndTypePair & column : provided_columns) - { - auto it = columns_map.find(column.name); - if (columns_map.end() == it) - throw Exception( - "There is no column with name " + column.name + ". There are columns: " + listOfColumns(available_columns), - ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); - - if (!column.type->equals(*it->second)) - throw Exception( - "Type mismatch for column " + column.name + ". Column has type " + it->second->getName() + ", got type " - + column.type->getName(), - ErrorCodes::TYPE_MISMATCH); - - if (unique_names.end() != unique_names.find(column.name)) - throw Exception("Column " + column.name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); - unique_names.insert(column.name); - } -} - -void IStorage::check(const NamesAndTypesList & provided_columns, const Names & column_names) const -{ - const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); - const auto available_columns_map = getColumnsMap(available_columns); - const auto & provided_columns_map = getColumnsMap(provided_columns); - - if (column_names.empty()) - throw Exception( - "Empty list of columns queried. There are columns: " + listOfColumns(available_columns), - ErrorCodes::EMPTY_LIST_OF_COLUMNS_QUERIED); - - auto unique_names = initUniqueStrings(); - for (const String & name : column_names) - { - auto it = provided_columns_map.find(name); - if (provided_columns_map.end() == it) - continue; - - auto jt = available_columns_map.find(name); - if (available_columns_map.end() == jt) - throw Exception( - "There is no column with name " + name + ". There are columns: " + listOfColumns(available_columns), - ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); - - if (!it->second->equals(*jt->second)) - throw Exception( - "Type mismatch for column " + name + ". Column has type " + jt->second->getName() + ", got type " + it->second->getName(), - ErrorCodes::TYPE_MISMATCH); - - if (unique_names.end() != unique_names.find(name)) - throw Exception("Column " + name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); - unique_names.insert(name); - } -} - -void IStorage::check(const Block & block, bool need_all) const -{ - const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); - const auto columns_map = getColumnsMap(available_columns); - - NameSet names_in_block; - - block.checkNumberOfRows(); - - for (const auto & column : block) - { - if (names_in_block.count(column.name)) - throw Exception("Duplicate column " + column.name + " in block", ErrorCodes::DUPLICATE_COLUMN); - - names_in_block.insert(column.name); - - auto it = columns_map.find(column.name); - if (columns_map.end() == it) - throw Exception( - "There is no column with name " + column.name + ". There are columns: " + listOfColumns(available_columns), - ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); - - if (!column.type->equals(*it->second)) - throw Exception( - "Type mismatch for column " + column.name + ". Column has type " + it->second->getName() + ", got type " - + column.type->getName(), - ErrorCodes::TYPE_MISMATCH); - } - - if (need_all && names_in_block.size() < columns_map.size()) - { - for (const auto & available_column : available_columns) - { - if (!names_in_block.count(available_column.name)) - throw Exception("Expected column " + available_column.name, ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK); - } - } -} bool IStorage::isVirtualColumn(const String & column_name) const { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 4b712853b537..bb4bf2ed09b6 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -138,25 +138,10 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired const ColumnsDescription & getColumns() const; /// returns combined set of columns - StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } - /// Verify that all the requested names are in the table and are set correctly: - /// list of names is not empty and the names do not repeat. - void check(const Names & column_names, bool include_virtuals = false) const; - - /// Check that all the requested names are in the table and have the correct types. - void check(const NamesAndTypesList & columns) const; - - /// Check that all names from the intersection of `names` and `columns` are in the table and have the same types. - void check(const NamesAndTypesList & columns, const Names & column_names) const; - - /// Check that the data block contains all the columns of the table with the correct types, - /// contains only the columns of the table, and all the columns are different. - /// If |need_all| is set, then checks that all the columns of the table are in the block. - void check(const Block & block, bool need_all = false) const; /// Return list of virtual columns (like _part, _table, etc). In the vast /// majority of cases virtual columns are static constant part of Storage diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 2cc5fc7dd5b6..b72c46afca30 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -224,7 +224,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::multiset part_values = VirtualColumnUtils::extractSingleValueFromBlock(virtual_columns_block, "_part"); - data.check(real_column_names); + metadata_snapshot->check(real_column_names, data.getVirtuals()); const Settings & settings = context.getSettingsRef(); const auto & primary_key = metadata_snapshot->getPrimaryKey(); diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 5974f366b66b..099480aca2f5 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -138,7 +138,7 @@ BlocksWithPartition MergeTreeDataWriter::splitBlockIntoParts(const Block & block if (!block || !block.rows()) return result; - data.check(block, true); + metadata_snapshot->check(block, true); if (!metadata_snapshot->hasPartitionKey()) /// Table is not partitioned. { diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp index 13df5ef23f18..bdefc5f1b146 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp @@ -211,7 +211,7 @@ void ReplicatedMergeTreeBlockOutputStream::writeExistingPart(MergeTreeData::Muta void ReplicatedMergeTreeBlockOutputStream::commitPart( zkutil::ZooKeeperPtr & zookeeper, MergeTreeData::MutableDataPartPtr & part, const String & block_id) { - storage.check(part->getColumns()); + metadata_snapshot->check(part->getColumns()); assertSessionIsNotExpired(zookeeper); /// Obtain incremental block number and lock it. The lock holds our intention to add the block to the filesystem. diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 13b37980c569..2ce258a2d25d 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -342,7 +342,7 @@ class BufferBlockOutputStream : public IBlockOutputStream return; // Check table structure. - storage.check(block, true); + metadata_snapshot->check(block, true); size_t rows = block.rows(); if (!rows) diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index f1d97a4e5c4f..bcebeec09dd9 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -429,14 +429,14 @@ void registerStorageGenerateRandom(StorageFactory & factory) Pipes StorageGenerateRandom::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, size_t max_block_size, unsigned num_streams) { - check(column_names, true); + metadata_snapshot->check(column_names, getVirtuals()); Pipes pipes; pipes.reserve(num_streams); diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 3b72dd970891..a394e196eacc 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -1,6 +1,10 @@ #include +#include +#include #include +#include + namespace DB { @@ -410,4 +414,172 @@ bool StorageInMemoryMetadata::hasSelectQuery() const return select.select_query != nullptr; } +namespace +{ +#if !defined(ARCADIA_BUILD) + using NamesAndTypesMap = google::dense_hash_map; + using UniqueStrings = google::dense_hash_set; +#else + using NamesAndTypesMap = google::sparsehash::dense_hash_map; + using UniqueStrings = google::sparsehash::dense_hash_set; +#endif + + String listOfColumns(const NamesAndTypesList & available_columns) + { + std::stringstream ss; + for (auto it = available_columns.begin(); it != available_columns.end(); ++it) + { + if (it != available_columns.begin()) + ss << ", "; + ss << it->name; + } + return ss.str(); + } + + NamesAndTypesMap getColumnsMap(const NamesAndTypesList & columns) + { + NamesAndTypesMap res; + res.set_empty_key(StringRef()); + + for (const auto & column : columns) + res.insert({column.name, column.type.get()}); + + return res; + } + + UniqueStrings initUniqueStrings() + { + UniqueStrings strings; + strings.set_empty_key(StringRef()); + return strings; + } +} + +void StorageInMemoryMetadata::check(const Names & column_names, const NamesAndTypesList & virtuals) const +{ + NamesAndTypesList available_columns = getColumns().getAllPhysical(); + available_columns.insert(available_columns.end(), virtuals.begin(), virtuals.end()); + + const String list_of_columns = listOfColumns(available_columns); + + if (column_names.empty()) + throw Exception("Empty list of columns queried. There are columns: " + list_of_columns, ErrorCodes::EMPTY_LIST_OF_COLUMNS_QUERIED); + + const auto columns_map = getColumnsMap(available_columns); + + auto unique_names = initUniqueStrings(); + for (const auto & name : column_names) + { + if (columns_map.end() == columns_map.find(name)) + throw Exception( + "There is no column with name " + backQuote(name) + " in table " + /* TODO alesap getStorageID().getNameForLogs() +*/ ". There are columns: " + list_of_columns, + ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); + + if (unique_names.end() != unique_names.find(name)) + throw Exception("Column " + name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); + unique_names.insert(name); + } +} + +void StorageInMemoryMetadata::check(const NamesAndTypesList & provided_columns) const +{ + const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); + const auto columns_map = getColumnsMap(available_columns); + + auto unique_names = initUniqueStrings(); + for (const NameAndTypePair & column : provided_columns) + { + auto it = columns_map.find(column.name); + if (columns_map.end() == it) + throw Exception( + "There is no column with name " + column.name + ". There are columns: " + listOfColumns(available_columns), + ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); + + if (!column.type->equals(*it->second)) + throw Exception( + "Type mismatch for column " + column.name + ". Column has type " + it->second->getName() + ", got type " + + column.type->getName(), + ErrorCodes::TYPE_MISMATCH); + + if (unique_names.end() != unique_names.find(column.name)) + throw Exception("Column " + column.name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); + unique_names.insert(column.name); + } +} + +void StorageInMemoryMetadata::check(const NamesAndTypesList & provided_columns, const Names & column_names) const +{ + const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); + const auto available_columns_map = getColumnsMap(available_columns); + const auto & provided_columns_map = getColumnsMap(provided_columns); + + if (column_names.empty()) + throw Exception( + "Empty list of columns queried. There are columns: " + listOfColumns(available_columns), + ErrorCodes::EMPTY_LIST_OF_COLUMNS_QUERIED); + + auto unique_names = initUniqueStrings(); + for (const String & name : column_names) + { + auto it = provided_columns_map.find(name); + if (provided_columns_map.end() == it) + continue; + + auto jt = available_columns_map.find(name); + if (available_columns_map.end() == jt) + throw Exception( + "There is no column with name " + name + ". There are columns: " + listOfColumns(available_columns), + ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); + + if (!it->second->equals(*jt->second)) + throw Exception( + "Type mismatch for column " + name + ". Column has type " + jt->second->getName() + ", got type " + it->second->getName(), + ErrorCodes::TYPE_MISMATCH); + + if (unique_names.end() != unique_names.find(name)) + throw Exception("Column " + name + " queried more than once", ErrorCodes::COLUMN_QUERIED_MORE_THAN_ONCE); + unique_names.insert(name); + } +} + +void StorageInMemoryMetadata::check(const Block & block, bool need_all) const +{ + const NamesAndTypesList & available_columns = getColumns().getAllPhysical(); + const auto columns_map = getColumnsMap(available_columns); + + NameSet names_in_block; + + block.checkNumberOfRows(); + + for (const auto & column : block) + { + if (names_in_block.count(column.name)) + throw Exception("Duplicate column " + column.name + " in block", ErrorCodes::DUPLICATE_COLUMN); + + names_in_block.insert(column.name); + + auto it = columns_map.find(column.name); + if (columns_map.end() == it) + throw Exception( + "There is no column with name " + column.name + ". There are columns: " + listOfColumns(available_columns), + ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); + + if (!column.type->equals(*it->second)) + throw Exception( + "Type mismatch for column " + column.name + ". Column has type " + it->second->getName() + ", got type " + + column.type->getName(), + ErrorCodes::TYPE_MISMATCH); + } + + if (need_all && names_in_block.size() < columns_map.size()) + { + for (const auto & available_column : available_columns) + { + if (!names_in_block.count(available_column.name)) + throw Exception("Expected column " + available_column.name, ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK); + } + } +} + + } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index efda4377dfc9..e4755bb0464a 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -177,6 +177,21 @@ struct StorageInMemoryMetadata /// Select query for *View storages. const SelectQueryDescription & getSelectQuery() const; bool hasSelectQuery() const; + + /// Verify that all the requested names are in the table and are set correctly: + /// list of names is not empty and the names do not repeat. + void check(const Names & column_names, const NamesAndTypesList & virtuals) const; + + /// Check that all the requested names are in the table and have the correct types. + void check(const NamesAndTypesList & columns) const; + + /// Check that all names from the intersection of `names` and `columns` are in the table and have the same types. + void check(const NamesAndTypesList & columns, const Names & column_names) const; + + /// Check that the data block contains all the columns of the table with the correct types, + /// contains only the columns of the table, and all the columns are different. + /// If |need_all| is set, then checks that all the columns of the table are in the block. + void check(const Block & block, bool need_all = false) const; }; using StorageMetadataPtr = std::shared_ptr; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 68b974c0dde7..300ab400a46d 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -446,7 +446,7 @@ Pipes StorageJoin::read( size_t max_block_size, unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); Pipes pipes; pipes.emplace_back(std::make_shared(*join, max_block_size, metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index 79cc3e5bf68a..fcae9c9aa821 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -276,7 +276,7 @@ void LogSource::readData(const String & name, const IDataType & type, IColumn & void LogBlockOutputStream::write(const Block & block) { - storage.check(block, true); + metadata_snapshot->check(block, true); /// The set of written offset columns so that you do not write shared offsets of columns for nested structures multiple times WrittenStreams written_streams; @@ -580,14 +580,14 @@ const StorageLog::Marks & StorageLog::getMarksWithRealRowCount() const Pipes StorageLog::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & context, QueryProcessingStage::Enum /*processed_stage*/, size_t max_block_size, unsigned num_streams) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); loadMarks(); NamesAndTypesList all_columns = Nested::collect(getColumns().getAllPhysical().addTypes(column_names)); diff --git a/src/Storages/StorageMemory.cpp b/src/Storages/StorageMemory.cpp index 442c5a3d67bf..f9ef3cfcc989 100644 --- a/src/Storages/StorageMemory.cpp +++ b/src/Storages/StorageMemory.cpp @@ -81,7 +81,7 @@ class MemoryBlockOutputStream : public IBlockOutputStream void write(const Block & block) override { - storage.check(block, true); + metadata_snapshot->check(block, true); std::lock_guard lock(storage.mutex); storage.data.push_back(block); } @@ -110,7 +110,7 @@ Pipes StorageMemory::read( size_t /*max_block_size*/, unsigned num_streams) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); std::lock_guard lock(mutex); diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index b1262771d21e..3e9b48e976b3 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -72,7 +72,7 @@ Pipes StorageMySQL::read( size_t max_block_size_, unsigned) { - check(column_names_); + metadata_snapshot->check(column_names_, getVirtuals()); String query = transformQueryForExternalDatabase( query_info_, metadata_snapshot->getColumns().getOrdinary(), diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index 4d9f08a60b7f..407c9b164ffc 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -276,7 +276,7 @@ Pipes StorageStripeLog::read( { std::shared_lock lock(rwlock); - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); NameSet column_names_set(column_names.begin(), column_names.end()); diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index ba524c7761e5..4015d8ca5740 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -309,7 +309,7 @@ void TinyLogBlockOutputStream::writeSuffix() void TinyLogBlockOutputStream::write(const Block & block) { - storage.check(block, true); + metadata_snapshot->check(block, true); /// The set of written offset columns so that you do not write shared columns for nested structures multiple times WrittenStreams written_streams; @@ -402,7 +402,7 @@ Pipes StorageTinyLog::read( const size_t max_block_size, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); Pipes pipes; diff --git a/src/Storages/StorageValues.cpp b/src/Storages/StorageValues.cpp index bb29b4a0932c..063cd3d52243 100644 --- a/src/Storages/StorageValues.cpp +++ b/src/Storages/StorageValues.cpp @@ -23,14 +23,14 @@ StorageValues::StorageValues( Pipes StorageValues::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, size_t /*max_block_size*/, unsigned /*num_streams*/) { - check(column_names, true); + metadata_snapshot->check(column_names, getVirtuals()); Pipes pipes; diff --git a/src/Storages/StorageXDBC.cpp b/src/Storages/StorageXDBC.cpp index c3c62ea1f0a0..ab8b37db7db0 100644 --- a/src/Storages/StorageXDBC.cpp +++ b/src/Storages/StorageXDBC.cpp @@ -90,7 +90,7 @@ Pipes StorageXDBC::read( size_t max_block_size, unsigned num_streams) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); bridge_helper->startBridgeSync(); return IStorageURLBase::read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); diff --git a/src/Storages/System/IStorageSystemOneBlock.h b/src/Storages/System/IStorageSystemOneBlock.h index de7e1a0e9336..b3a2a6fe53b4 100644 --- a/src/Storages/System/IStorageSystemOneBlock.h +++ b/src/Storages/System/IStorageSystemOneBlock.h @@ -37,7 +37,7 @@ class IStorageSystemOneBlock : public IStorage size_t /*max_block_size*/, unsigned /*num_streams*/) override { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); Block sample_block = metadata_snapshot->getSampleBlock(); MutableColumns res_columns = sample_block.cloneEmptyColumns(); diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 83178870ba9c..319ef257d6da 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -249,7 +249,7 @@ Pipes StorageSystemColumns::read( const size_t max_block_size, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); /// Create a mask of what columns are needed in the result. diff --git a/src/Storages/System/StorageSystemDisks.cpp b/src/Storages/System/StorageSystemDisks.cpp index d13ea29804d2..fbcdd78988a2 100644 --- a/src/Storages/System/StorageSystemDisks.cpp +++ b/src/Storages/System/StorageSystemDisks.cpp @@ -35,7 +35,7 @@ Pipes StorageSystemDisks::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); MutableColumnPtr col_name = ColumnString::create(); MutableColumnPtr col_path = ColumnString::create(); diff --git a/src/Storages/System/StorageSystemNumbers.cpp b/src/Storages/System/StorageSystemNumbers.cpp index fd7e04cfb1fb..50921c53fb64 100644 --- a/src/Storages/System/StorageSystemNumbers.cpp +++ b/src/Storages/System/StorageSystemNumbers.cpp @@ -125,14 +125,14 @@ StorageSystemNumbers::StorageSystemNumbers(const StorageID & table_id, bool mult Pipes StorageSystemNumbers::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo &, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, size_t max_block_size, unsigned num_streams) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); if (limit && *limit < max_block_size) { diff --git a/src/Storages/System/StorageSystemOne.cpp b/src/Storages/System/StorageSystemOne.cpp index af736c215b5b..20d61d5da1b1 100644 --- a/src/Storages/System/StorageSystemOne.cpp +++ b/src/Storages/System/StorageSystemOne.cpp @@ -22,14 +22,14 @@ StorageSystemOne::StorageSystemOne(const std::string & name_) Pipes StorageSystemOne::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo &, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); Block header{ColumnWithTypeAndName( DataTypeUInt8().createColumn(), diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index 4f99e1e8c6a8..b998b60c02da 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -26,7 +26,7 @@ namespace ErrorCodes extern const int TABLE_IS_DROPPED; } -bool StorageSystemPartsBase::hasStateColumn(const Names & column_names) const +bool StorageSystemPartsBase::hasStateColumn(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const { bool has_state_column = false; Names real_column_names; @@ -41,7 +41,7 @@ bool StorageSystemPartsBase::hasStateColumn(const Names & column_names) const /// Do not check if only _state column is requested if (!(has_state_column && real_column_names.empty())) - check(real_column_names); + metadata_snapshot->check(real_column_names, {}); return has_state_column; } @@ -232,7 +232,7 @@ Pipes StorageSystemPartsBase::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - bool has_state_column = hasStateColumn(column_names); + bool has_state_column = hasStateColumn(column_names, metadata_snapshot); StoragesInfoStream stream(query_info, context); diff --git a/src/Storages/System/StorageSystemPartsBase.h b/src/Storages/System/StorageSystemPartsBase.h index a46cecec9dd1..8af1f46d8a7a 100644 --- a/src/Storages/System/StorageSystemPartsBase.h +++ b/src/Storages/System/StorageSystemPartsBase.h @@ -57,7 +57,7 @@ class StorageSystemPartsBase : public IStorage public: Pipes read( const Names & column_names, - const StorageMetadataPtr & metadata_, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -67,7 +67,7 @@ class StorageSystemPartsBase : public IStorage NamesAndTypesList getVirtuals() const override; private: - bool hasStateColumn(const Names & column_names) const; + bool hasStateColumn(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const; protected: const FormatSettings format_settings; diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index 8fb6a89ddd16..f79e9138500d 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -66,7 +66,7 @@ Pipes StorageSystemReplicas::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); const auto access = context.getAccess(); const bool check_access_for_databases = !access->isGranted(AccessType::SHOW_TABLES); diff --git a/src/Storages/System/StorageSystemStoragePolicies.cpp b/src/Storages/System/StorageSystemStoragePolicies.cpp index 44252a788b98..a6092a28a47e 100644 --- a/src/Storages/System/StorageSystemStoragePolicies.cpp +++ b/src/Storages/System/StorageSystemStoragePolicies.cpp @@ -39,7 +39,7 @@ Pipes StorageSystemStoragePolicies::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); MutableColumnPtr col_policy_name = ColumnString::create(); MutableColumnPtr col_volume_name = ColumnString::create(); diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index f8f400269407..84635acb887a 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -456,7 +456,7 @@ Pipes StorageSystemTables::read( const size_t max_block_size, const unsigned /*num_streams*/) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); /// Create a mask of what columns are needed in the result. diff --git a/src/Storages/System/StorageSystemZeros.cpp b/src/Storages/System/StorageSystemZeros.cpp index 2bc53b5093e7..d325840091e6 100644 --- a/src/Storages/System/StorageSystemZeros.cpp +++ b/src/Storages/System/StorageSystemZeros.cpp @@ -92,14 +92,14 @@ StorageSystemZeros::StorageSystemZeros(const StorageID & table_id_, bool multith Pipes StorageSystemZeros::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo &, const Context & /*context*/, QueryProcessingStage::Enum /*processed_stage*/, size_t max_block_size, unsigned num_streams) { - check(column_names); + metadata_snapshot->check(column_names, getVirtuals()); bool use_multiple_streams = multithreaded; From ef8781cce77dddff57f44aaa1005b0f88e30dcdf Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 17:37:21 +0300 Subject: [PATCH 0374/1102] Better getVirtuals method --- src/Storages/IStorage.cpp | 4 ++-- src/Storages/IStorage.h | 2 +- src/Storages/StorageDistributed.cpp | 4 ++-- src/Storages/StorageMerge.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 38fdaa832bd5..884982c93b92 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -38,10 +38,10 @@ const ColumnsDescription & IStorage::getColumns() const } -bool IStorage::isVirtualColumn(const String & column_name) const +bool IStorage::isVirtualColumn(const String & column_name, const StorageMetadataPtr & metadata_snapshot) const { /// Virtual column maybe overriden by real column - return !getColumns().has(column_name) && getVirtuals().contains(column_name); + return !metadata_snapshot->getColumns().has(column_name) && getVirtuals().contains(column_name); } RWLockImpl::LockHolder IStorage::tryLockTimed( diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index bb4bf2ed09b6..a125c6f83104 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -159,7 +159,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /// Returns whether the column is virtual - by default all columns are real. /// Initially reserved virtual column name may be shadowed by real column. - bool isVirtualColumn(const String & column_name) const; + bool isVirtualColumn(const String & column_name, const StorageMetadataPtr & metadata_snapshot) const; private: diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 6868f468f2e4..ce4fcbb35139 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -464,7 +464,7 @@ QueryProcessingStage::Enum StorageDistributed::getQueryProcessingStage(const Con Pipes StorageDistributed::read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -497,7 +497,7 @@ Pipes StorageDistributed::read( const Scalars & scalars = context.hasQueryContext() ? context.getQueryContext().getScalars() : Scalars{}; bool has_virtual_shard_num_column = std::find(column_names.begin(), column_names.end(), "_shard_num") != column_names.end(); - if (has_virtual_shard_num_column && !isVirtualColumn("_shard_num")) + if (has_virtual_shard_num_column && !isVirtualColumn("_shard_num", metadata_snapshot)) has_virtual_shard_num_column = false; ClusterProxy::SelectStreamFactory select_stream_factory = remote_table_function_ptr diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 9765db35fc37..92e965c420e9 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -146,7 +146,7 @@ Pipes StorageMerge::read( for (const auto & column_name : column_names) { - if (column_name == "_table" && isVirtualColumn(column_name)) + if (column_name == "_table" && isVirtualColumn(column_name, metadata_snapshot)) has_table_virtual_column = true; else real_column_names.push_back(column_name); From 1ad4f2c0fd4ad17c5081d86551daf02174eefb55 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 18:50:21 +0300 Subject: [PATCH 0375/1102] Add CreatingSets step. --- src/Interpreters/InterpreterSelectQuery.cpp | 9 ++++-- src/Processors/QueryPlan/CreatingSetsStep.cpp | 30 +++++++++++++++++++ src/Processors/QueryPlan/CreatingSetsStep.h | 28 +++++++++++++++++ .../Transforms/CreatingSetsTransform.cpp | 8 ++--- .../Transforms/CreatingSetsTransform.h | 4 +-- src/Processors/ya.make | 1 + 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 src/Processors/QueryPlan/CreatingSetsStep.cpp create mode 100644 src/Processors/QueryPlan/CreatingSetsStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index e64595dd757e..11c3a6b1211b 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -88,6 +88,7 @@ #include #include #include +#include namespace DB @@ -1873,12 +1874,14 @@ void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPipeline & pip const Settings & settings = context->getSettingsRef(); - auto creating_sets = std::make_shared( - pipeline.getHeader(), subqueries_for_sets, + CreatingSetsStep creating_sets( + DataStream{.header = pipeline.getHeader()}, + subqueries_for_sets, SizeLimits(settings.max_rows_to_transfer, settings.max_bytes_to_transfer, settings.transfer_overflow_mode), *context); - pipeline.addCreatingSetsTransform(std::move(creating_sets)); + creating_sets.setStepDescription("Create sets for subqueries and joins"); + creating_sets.transformPipeline(pipeline); } diff --git a/src/Processors/QueryPlan/CreatingSetsStep.cpp b/src/Processors/QueryPlan/CreatingSetsStep.cpp new file mode 100644 index 000000000000..fa2eb9b34b39 --- /dev/null +++ b/src/Processors/QueryPlan/CreatingSetsStep.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +namespace DB +{ + +CreatingSetsStep::CreatingSetsStep( + const DataStream & input_stream_, + SubqueriesForSets subqueries_for_sets_, + SizeLimits network_transfer_limits_, + const Context & context_) + : ITransformingStep(input_stream_, input_stream_) + , subqueries_for_sets(std::move(subqueries_for_sets_)) + , network_transfer_limits(std::move(network_transfer_limits_)) + , context(context_) +{ +} + +void CreatingSetsStep::transformPipeline(QueryPipeline & pipeline) +{ + auto creating_sets = std::make_shared( + pipeline.getHeader(), subqueries_for_sets, + network_transfer_limits, + context); + + pipeline.addCreatingSetsTransform(std::move(creating_sets)); +} + +} diff --git a/src/Processors/QueryPlan/CreatingSetsStep.h b/src/Processors/QueryPlan/CreatingSetsStep.h new file mode 100644 index 000000000000..d3c4db30502c --- /dev/null +++ b/src/Processors/QueryPlan/CreatingSetsStep.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +namespace DB +{ + +class CreatingSetsStep : public ITransformingStep +{ +public: + CreatingSetsStep( + const DataStream & input_stream_, + SubqueriesForSets subqueries_for_sets_, + SizeLimits network_transfer_limits_, + const Context & context_); + + String getName() const override { return "CreatingSets"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SubqueriesForSets subqueries_for_sets; + SizeLimits network_transfer_limits; + const Context & context; +}; + +} diff --git a/src/Processors/Transforms/CreatingSetsTransform.cpp b/src/Processors/Transforms/CreatingSetsTransform.cpp index af8fa4097df7..321419c092cb 100644 --- a/src/Processors/Transforms/CreatingSetsTransform.cpp +++ b/src/Processors/Transforms/CreatingSetsTransform.cpp @@ -23,13 +23,13 @@ namespace ErrorCodes CreatingSetsTransform::CreatingSetsTransform( Block out_header_, - const SubqueriesForSets & subqueries_for_sets_, - const SizeLimits & network_transfer_limits_, + SubqueriesForSets subqueries_for_sets_, + SizeLimits network_transfer_limits_, const Context & context_) : IProcessor({}, {std::move(out_header_)}) - , subqueries_for_sets(subqueries_for_sets_) + , subqueries_for_sets(std::move(subqueries_for_sets_)) , cur_subquery(subqueries_for_sets.begin()) - , network_transfer_limits(network_transfer_limits_) + , network_transfer_limits(std::move(network_transfer_limits_)) , context(context_) { } diff --git a/src/Processors/Transforms/CreatingSetsTransform.h b/src/Processors/Transforms/CreatingSetsTransform.h index f6df60429597..ac9ac7130f35 100644 --- a/src/Processors/Transforms/CreatingSetsTransform.h +++ b/src/Processors/Transforms/CreatingSetsTransform.h @@ -21,8 +21,8 @@ class CreatingSetsTransform : public IProcessor public: CreatingSetsTransform( Block out_header_, - const SubqueriesForSets & subqueries_for_sets_, - const SizeLimits & network_transfer_limits_, + SubqueriesForSets subqueries_for_sets_, + SizeLimits network_transfer_limits_, const Context & context_); String getName() const override { return "CreatingSetsTransform"; } diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 8d373fbf05d3..537d81e9750a 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -139,6 +139,7 @@ SRCS( Transforms/AggregatingInOrderTransform.cpp QueryPlan/AddingDelayedStreamStep.cpp QueryPlan/AggregatingStep.cpp + QueryPlan/CreatingSetsStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp From dffdece3501fce6ef74b1ae7c970f3f477024bb6 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 19:39:58 +0300 Subject: [PATCH 0376/1102] getColumns in StorageInMemoryMetadta (only compilable) --- src/Core/iostream_debug_helpers.cpp | 2 +- .../InputStreamFromASTInsertQuery.cpp | 9 +++++-- .../InputStreamFromASTInsertQuery.h | 5 +++- .../PushingToViewsBlockOutputStream.cpp | 5 ++-- src/DataStreams/RemoteQueryExecutor.cpp | 2 +- src/Databases/DatabaseMySQL.cpp | 3 ++- src/Functions/hasColumnInTable.cpp | 3 ++- src/Interpreters/InterpreterDescribeQuery.cpp | 3 ++- src/Interpreters/InterpreterInsertQuery.cpp | 4 +-- src/Interpreters/InterpreterSelectQuery.cpp | 13 +++++----- src/Interpreters/InterpreterWatchQuery.cpp | 2 +- src/Interpreters/JoinedTables.cpp | 4 +-- src/Interpreters/JoinedTables.h | 4 ++- src/Interpreters/MutationsInterpreter.cpp | 4 +-- src/Interpreters/SyntaxAnalyzer.cpp | 11 +++++--- src/Interpreters/SyntaxAnalyzer.h | 17 ++++++++++-- src/Interpreters/getTableExpressions.cpp | 6 +++-- src/Interpreters/interpretSubquery.cpp | 4 +-- src/Server/MySQLHandler.cpp | 3 ++- src/Server/TCPHandler.cpp | 10 ++++--- src/Storages/IStorage.cpp | 6 ----- src/Storages/IStorage.h | 1 - src/Storages/LiveView/StorageLiveView.cpp | 12 ++++++--- src/Storages/MergeTree/IMergeTreeDataPart.cpp | 7 ++--- src/Storages/MergeTree/IMergeTreeDataPart.h | 3 ++- src/Storages/MergeTree/IMergeTreeReader.cpp | 26 +++++++++++++------ src/Storages/MergeTree/IMergeTreeReader.h | 5 +++- .../MergeTree/MergeTreeBlockReadUtils.cpp | 21 +++++++++------ .../MergeTree/MergeTreeBlockReadUtils.h | 11 +++++--- .../MergeTree/MergeTreeDataMergerMutator.cpp | 6 ++--- .../MergeTree/MergeTreeDataPartCompact.cpp | 3 ++- .../MergeTree/MergeTreeDataPartCompact.h | 1 + .../MergeTree/MergeTreeDataPartWide.cpp | 3 ++- .../MergeTree/MergeTreeDataPartWide.h | 1 + .../MergeTree/MergeTreeDataSelectExecutor.cpp | 25 +----------------- .../MergeTree/MergeTreeDataSelectExecutor.h | 6 ----- src/Storages/MergeTree/MergeTreeReadPool.cpp | 2 +- .../MergeTree/MergeTreeReaderCompact.cpp | 25 ++++++++++++------ .../MergeTree/MergeTreeReaderCompact.h | 1 + .../MergeTree/MergeTreeReaderWide.cpp | 11 ++++++-- src/Storages/MergeTree/MergeTreeReaderWide.h | 1 + .../MergeTreeReverseSelectProcessor.cpp | 9 ++++--- .../MergeTree/MergeTreeSelectProcessor.cpp | 8 +++--- .../MergeTree/MergeTreeSequentialSource.cpp | 4 +-- ...rgeTreeThreadSelectBlockInputProcessor.cpp | 8 +++--- src/Storages/StorageBuffer.cpp | 10 +++---- src/Storages/StorageDistributed.cpp | 19 +++++++++----- src/Storages/StorageDistributed.h | 4 +-- src/Storages/StorageFile.cpp | 2 +- src/Storages/StorageGenerateRandom.cpp | 2 +- src/Storages/StorageLog.cpp | 17 ++++++------ src/Storages/StorageLog.h | 4 +-- src/Storages/StorageMerge.cpp | 10 ++++--- src/Storages/StorageReplicatedMergeTree.cpp | 8 +++--- src/Storages/StorageTinyLog.cpp | 21 ++++++++------- src/Storages/StorageURL.cpp | 14 +++++++--- src/Storages/StorageURL.h | 15 ++++++----- src/Storages/StorageXDBC.cpp | 12 ++++++--- src/Storages/StorageXDBC.h | 5 +++- .../System/StorageSystemPartsColumns.cpp | 2 +- src/Storages/TTLDescription.cpp | 4 +-- src/Storages/getStructureOfRemoteTable.cpp | 8 ++++-- src/Storages/tests/gtest_storage_log.cpp | 2 +- src/TableFunctions/TableFunctionMerge.cpp | 2 +- 64 files changed, 285 insertions(+), 196 deletions(-) diff --git a/src/Core/iostream_debug_helpers.cpp b/src/Core/iostream_debug_helpers.cpp index 3a77b1f42be2..8683bb14db66 100644 --- a/src/Core/iostream_debug_helpers.cpp +++ b/src/Core/iostream_debug_helpers.cpp @@ -49,7 +49,7 @@ std::ostream & operator<<(std::ostream & stream, const IStorage & what) { auto table_id = what.getStorageID(); stream << "IStorage(name = " << what.getName() << ", tableName = " << table_id.table_name << ") {" - << what.getColumns().getAllPhysical().toString() << "}"; + << what.getInMemoryMetadataPtr()->getColumns().getAllPhysical().toString() << "}"; return stream; } diff --git a/src/DataStreams/InputStreamFromASTInsertQuery.cpp b/src/DataStreams/InputStreamFromASTInsertQuery.cpp index 47b61294da32..19c6fe41ecaf 100644 --- a/src/DataStreams/InputStreamFromASTInsertQuery.cpp +++ b/src/DataStreams/InputStreamFromASTInsertQuery.cpp @@ -21,7 +21,11 @@ namespace ErrorCodes InputStreamFromASTInsertQuery::InputStreamFromASTInsertQuery( - const ASTPtr & ast, ReadBuffer * input_buffer_tail_part, const Block & header, const Context & context, const ASTPtr & input_function) + const ASTPtr & ast, + ReadBuffer * input_buffer_tail_part, + const Block & header, + const Context & context, + const ASTPtr & input_function) { const auto * ast_insert_query = ast->as(); @@ -59,7 +63,8 @@ InputStreamFromASTInsertQuery::InputStreamFromASTInsertQuery( if (context.getSettingsRef().input_format_defaults_for_omitted_fields && ast_insert_query->table_id && !input_function) { StoragePtr storage = DatabaseCatalog::instance().getTable(ast_insert_query->table_id, context); - auto column_defaults = storage->getColumns().getDefaults(); + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); + auto column_defaults = metadata_snapshot->getColumns().getDefaults(); if (!column_defaults.empty()) res_stream = std::make_shared(res_stream, column_defaults, context); } diff --git a/src/DataStreams/InputStreamFromASTInsertQuery.h b/src/DataStreams/InputStreamFromASTInsertQuery.h index a57e9199603a..0604f011e283 100644 --- a/src/DataStreams/InputStreamFromASTInsertQuery.h +++ b/src/DataStreams/InputStreamFromASTInsertQuery.h @@ -11,6 +11,8 @@ namespace DB struct BlockIO; class Context; +struct StorageInMemoryMetadata; +using StorageMetadataPtr = std::shared_ptr; /** Prepares an input stream which produce data containing in INSERT query * Head of inserting data could be stored in INSERT ast directly @@ -19,7 +21,8 @@ class Context; class InputStreamFromASTInsertQuery : public IBlockInputStream { public: - InputStreamFromASTInsertQuery(const ASTPtr & ast, + InputStreamFromASTInsertQuery( + const ASTPtr & ast, ReadBuffer * input_buffer_tail_part, const Block & header, const Context & context, diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index e6e368f78e9b..72de6b889f17 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -79,6 +79,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( StoragePtr inner_table = materialized_view->getTargetTable(); auto inner_table_id = inner_table->getStorageID(); + auto inner_metadata_snapshot = inner_table->getInMemoryMetadataPtr(); query = dependent_metadata_snapshot->getSelectQuery().inner_query; std::unique_ptr insert = std::make_unique(); @@ -90,7 +91,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( /// Insert only columns returned by select. auto list = std::make_shared(); - const auto & inner_table_columns = inner_table->getColumns(); + const auto & inner_table_columns = inner_metadata_snapshot->getColumns(); for (auto & column : header) /// But skip columns which storage doesn't have. if (inner_table_columns.hasPhysical(column.name)) @@ -323,7 +324,7 @@ void PushingToViewsBlockOutputStream::process(const Block & block, size_t view_n Context local_context = *select_context; local_context.addViewSource( StorageValues::create( - storage->getStorageID(), storage->getColumns(), block, storage->getVirtuals())); + storage->getStorageID(), metadata_snapshot->getColumns(), block, storage->getVirtuals())); select.emplace(view.query, local_context, SelectQueryOptions()); in = std::make_shared(select->execute().getInputStream()); diff --git a/src/DataStreams/RemoteQueryExecutor.cpp b/src/DataStreams/RemoteQueryExecutor.cpp index 45ddd7c08932..be09cd948143 100644 --- a/src/DataStreams/RemoteQueryExecutor.cpp +++ b/src/DataStreams/RemoteQueryExecutor.cpp @@ -325,7 +325,7 @@ void RemoteQueryExecutor::sendExternalTables() Pipes pipes; pipes = cur->read( - cur->getColumns().getNamesOfPhysical(), + metadata_snapshot->getColumns().getNamesOfPhysical(), metadata_snapshot, {}, context, read_from_table_stage, DEFAULT_BLOCK_SIZE, 1); diff --git a/src/Databases/DatabaseMySQL.cpp b/src/Databases/DatabaseMySQL.cpp index 5d4b81014f9f..a73fbafb7f51 100644 --- a/src/Databases/DatabaseMySQL.cpp +++ b/src/Databases/DatabaseMySQL.cpp @@ -139,7 +139,8 @@ static ASTPtr getCreateQueryFromStorage(const StoragePtr & storage, const ASTPtr create_table_query->table = table_id.table_name; create_table_query->database = table_id.database_name; - for (const auto & column_type_and_name : storage->getColumns().getOrdinary()) + auto metadata_snapshot = storage->getInMemoryMetadataPtr(); + for (const auto & column_type_and_name : metadata_snapshot->getColumns().getOrdinary()) { const auto & column_declaration = std::make_shared(); column_declaration->name = column_type_and_name.name; diff --git a/src/Functions/hasColumnInTable.cpp b/src/Functions/hasColumnInTable.cpp index b9ec2b84837e..ef447070e7a9 100644 --- a/src/Functions/hasColumnInTable.cpp +++ b/src/Functions/hasColumnInTable.cpp @@ -114,7 +114,8 @@ void FunctionHasColumnInTable::executeImpl(Block & block, const ColumnNumbers & if (host_name.empty()) { const StoragePtr & table = DatabaseCatalog::instance().getTable({database_name, table_name}, global_context); - has_column = table->getColumns().hasPhysical(column_name); + auto table_metadata = table->getInMemoryMetadataPtr(); + has_column = table_metadata->getColumns().hasPhysical(column_name); } else { diff --git a/src/Interpreters/InterpreterDescribeQuery.cpp b/src/Interpreters/InterpreterDescribeQuery.cpp index d457fefed6a6..535a4280b45e 100644 --- a/src/Interpreters/InterpreterDescribeQuery.cpp +++ b/src/Interpreters/InterpreterDescribeQuery.cpp @@ -91,7 +91,8 @@ BlockInputStreamPtr InterpreterDescribeQuery::executeImpl() auto table_lock = table->lockStructureForShare( false, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); - columns = table->getColumns(); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); + columns = metadata_snapshot->getColumns(); } Block sample_block = getSampleBlock(); diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index e7fdf80e2978..a39e89619701 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -244,7 +244,7 @@ BlockIO InterpreterInsertQuery::execute() /// Actually we don't know structure of input blocks from query/table, /// because some clients break insertion protocol (columns != header) out = std::make_shared( - out, query_sample_block, out->getHeader(), table->getColumns().getDefaults(), context); + out, query_sample_block, out->getHeader(), metadata_snapshot->getColumns().getDefaults(), context); /// It's important to squash blocks as early as possible (before other transforms), /// because other transforms may work inefficient if block size is small. @@ -295,7 +295,7 @@ BlockIO InterpreterInsertQuery::execute() if (!allow_materialized) { - for (const auto & column : table->getColumns()) + for (const auto & column : metadata_snapshot->getColumns()) if (column.default_desc.kind == ColumnDefaultKind::Materialized && header.has(column.name)) throw Exception("Cannot insert column " + column.name + ", because it is MATERIALIZED column.", ErrorCodes::ILLEGAL_COLUMN); } diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 509825e75e44..f601ca74112a 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -129,7 +129,7 @@ String InterpreterSelectQuery::generateFilterActions( table_expr->children.push_back(table_expr->database_and_table_name); /// Using separate expression analyzer to prevent any possible alias injection - auto syntax_result = SyntaxAnalyzer(*context).analyzeSelect(query_ast, SyntaxAnalyzerResult({}, storage)); + auto syntax_result = SyntaxAnalyzer(*context).analyzeSelect(query_ast, SyntaxAnalyzerResult({}, storage, metadata_snapshot)); SelectQueryExpressionAnalyzer analyzer(query_ast, syntax_result, *context, metadata_snapshot); actions = analyzer.simpleSelectActions(); @@ -263,7 +263,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( } if (has_input || !joined_tables.resolveTables()) - joined_tables.makeFakeTable(storage, source_header); + joined_tables.makeFakeTable(storage, metadata_snapshot, source_header); /// Rewrite JOINs if (!has_input && joined_tables.tablesCount() > 1) @@ -311,8 +311,9 @@ InterpreterSelectQuery::InterpreterSelectQuery( view->replaceWithSubquery(getSelectQuery(), view_table, metadata_snapshot); syntax_analyzer_result = SyntaxAnalyzer(*context).analyzeSelect( - query_ptr, SyntaxAnalyzerResult(source_header.getNamesAndTypesList(), storage), - options, joined_tables.tablesWithColumns(), required_result_column_names, table_join); + query_ptr, + SyntaxAnalyzerResult(source_header.getNamesAndTypesList(), storage, metadata_snapshot), + options, joined_tables.tablesWithColumns(), required_result_column_names, table_join); if (view) { @@ -1087,7 +1088,7 @@ void InterpreterSelectQuery::executeFetchColumns( /// Detect, if ALIAS columns are required for query execution auto alias_columns_required = false; - const ColumnsDescription & storage_columns = storage->getColumns(); + const ColumnsDescription & storage_columns = metadata_snapshot->getColumns(); for (const auto & column_name : required_columns) { auto column_default = storage_columns.getDefault(column_name); @@ -1210,7 +1211,7 @@ void InterpreterSelectQuery::executeFetchColumns( prewhere_info->prewhere_actions = std::move(new_actions); auto analyzed_result - = SyntaxAnalyzer(*context).analyze(required_columns_from_prewhere_expr, storage->getColumns().getAllPhysical()); + = SyntaxAnalyzer(*context).analyze(required_columns_from_prewhere_expr, metadata_snapshot->getColumns().getAllPhysical()); prewhere_info->alias_actions = ExpressionAnalyzer(required_columns_from_prewhere_expr, analyzed_result, *context).getActions(true, false); diff --git a/src/Interpreters/InterpreterWatchQuery.cpp b/src/Interpreters/InterpreterWatchQuery.cpp index 489be488b4d5..71ec1609046c 100644 --- a/src/Interpreters/InterpreterWatchQuery.cpp +++ b/src/Interpreters/InterpreterWatchQuery.cpp @@ -47,7 +47,7 @@ BlockIO InterpreterWatchQuery::execute() ErrorCodes::UNKNOWN_TABLE); /// List of columns to read to execute the query. - Names required_columns = storage->getColumns().getNamesOfPhysical(); + Names required_columns = storage->getInMemoryMetadataPtr()->getColumns().getNamesOfPhysical(); context.checkAccess(AccessType::SELECT, table_id, required_columns); /// Get context settings for this query diff --git a/src/Interpreters/JoinedTables.cpp b/src/Interpreters/JoinedTables.cpp index 7450890952a2..127df9b5eacb 100644 --- a/src/Interpreters/JoinedTables.cpp +++ b/src/Interpreters/JoinedTables.cpp @@ -207,11 +207,11 @@ bool JoinedTables::resolveTables() return !tables_with_columns.empty(); } -void JoinedTables::makeFakeTable(StoragePtr storage, const Block & source_header) +void JoinedTables::makeFakeTable(StoragePtr storage, const StorageMetadataPtr & metadata_snapshot, const Block & source_header) { if (storage) { - const ColumnsDescription & storage_columns = storage->getColumns(); + const ColumnsDescription & storage_columns = metadata_snapshot->getColumns(); tables_with_columns.emplace_back(DatabaseAndTableWithAlias{}, storage_columns.getOrdinary()); auto & table = tables_with_columns.back(); diff --git a/src/Interpreters/JoinedTables.h b/src/Interpreters/JoinedTables.h index 2591b49527b7..cff86c5a535f 100644 --- a/src/Interpreters/JoinedTables.h +++ b/src/Interpreters/JoinedTables.h @@ -13,6 +13,8 @@ namespace DB class ASTSelectQuery; class TableJoin; struct SelectQueryOptions; +struct StorageInMemoryMetadata; +using StorageMetadataPtr = std::shared_ptr; /// Joined tables' columns resolver. /// We want to get each table structure at most once per table occurance. Or even better once per table. @@ -31,7 +33,7 @@ class JoinedTables bool resolveTables(); /// Make fake tables_with_columns[0] in case we have predefined input in InterpreterSelectQuery - void makeFakeTable(StoragePtr storage, const Block & source_header); + void makeFakeTable(StoragePtr storage, const StorageMetadataPtr & metadata_snapshot, const Block & source_header); std::shared_ptr makeTableJoin(const ASTSelectQuery & select_query); const TablesWithColumns & tablesWithColumns() const { return tables_with_columns; } diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 694e114af7a7..3ad813a15b77 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -419,7 +419,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) else { NameSet new_updated_columns; - auto column_ttls = storage->getColumns().getColumnTTLs(); + auto column_ttls = metadata_snapshot->getColumns().getColumnTTLs(); for (const auto & elem : column_ttls) { dependencies.emplace(elem.first, ColumnDependency::TTL_TARGET); @@ -528,7 +528,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) ASTPtr MutationsInterpreter::prepareInterpreterSelectQuery(std::vector & prepared_stages, bool dry_run) { - NamesAndTypesList all_columns = storage->getColumns().getAllPhysical(); + NamesAndTypesList all_columns = metadata_snapshot->getColumns().getAllPhysical(); /// Next, for each stage calculate columns changed by this and previous stages. diff --git a/src/Interpreters/SyntaxAnalyzer.cpp b/src/Interpreters/SyntaxAnalyzer.cpp index 2dc2943d36d3..9e927e1eb5a5 100644 --- a/src/Interpreters/SyntaxAnalyzer.cpp +++ b/src/Interpreters/SyntaxAnalyzer.cpp @@ -681,7 +681,7 @@ void SyntaxAnalyzerResult::collectSourceColumns(bool add_special) { if (storage) { - const ColumnsDescription & columns = storage->getColumns(); + const ColumnsDescription & columns = metadata_snapshot->getColumns(); auto columns_from_storage = add_special ? columns.getAll() : columns.getAllPhysical(); if (source_columns.empty()) @@ -962,14 +962,19 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( return std::make_shared(result); } -SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze(ASTPtr & query, const NamesAndTypesList & source_columns, ConstStoragePtr storage, bool allow_aggregations) const +SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze( + ASTPtr & query, + const NamesAndTypesList & source_columns, + ConstStoragePtr storage, + const StorageMetadataPtr & metadata_snapshot, + bool allow_aggregations) const { if (query->as()) throw Exception("Not select analyze for select asts.", ErrorCodes::LOGICAL_ERROR); const auto & settings = context.getSettingsRef(); - SyntaxAnalyzerResult result(source_columns, storage, false); + SyntaxAnalyzerResult result(source_columns, storage, metadata_snapshot, false); normalize(query, result.aliases, settings); diff --git a/src/Interpreters/SyntaxAnalyzer.h b/src/Interpreters/SyntaxAnalyzer.h index 175c2db295a3..4308b70c45a8 100644 --- a/src/Interpreters/SyntaxAnalyzer.h +++ b/src/Interpreters/SyntaxAnalyzer.h @@ -16,10 +16,13 @@ class Context; struct Settings; struct SelectQueryOptions; using Scalars = std::map; +struct StorageInMemoryMetadata; +using StorageMetadataPtr = std::shared_ptr; struct SyntaxAnalyzerResult { ConstStoragePtr storage; + StorageMetadataPtr metadata_snapshot; std::shared_ptr analyzed_join; NamesAndTypesList source_columns; @@ -51,8 +54,13 @@ struct SyntaxAnalyzerResult /// Results of scalar sub queries Scalars scalars; - SyntaxAnalyzerResult(const NamesAndTypesList & source_columns_, ConstStoragePtr storage_ = {}, bool add_special = true) + SyntaxAnalyzerResult( + const NamesAndTypesList & source_columns_, + ConstStoragePtr storage_ = {}, + const StorageMetadataPtr & metadata_snapshot_ = {}, + bool add_special = true) : storage(storage_) + , metadata_snapshot(metadata_snapshot_) , source_columns(source_columns_) { collectSourceColumns(add_special); @@ -86,7 +94,12 @@ class SyntaxAnalyzer {} /// Analyze and rewrite not select query - SyntaxAnalyzerResultPtr analyze(ASTPtr & query, const NamesAndTypesList & source_columns_, ConstStoragePtr storage = {}, bool allow_aggregations = false) const; + SyntaxAnalyzerResultPtr analyze( + ASTPtr & query, + const NamesAndTypesList & source_columns_, + ConstStoragePtr storage = {}, + const StorageMetadataPtr & metadata_snapshot = {}, + bool allow_aggregations = false) const; /// Analyze and rewrite select query SyntaxAnalyzerResultPtr analyzeSelect( diff --git a/src/Interpreters/getTableExpressions.cpp b/src/Interpreters/getTableExpressions.cpp index 6e3fd516e1c5..56ca614dc2df 100644 --- a/src/Interpreters/getTableExpressions.cpp +++ b/src/Interpreters/getTableExpressions.cpp @@ -87,7 +87,8 @@ static NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression const auto table_function = table_expression.table_function; auto * query_context = const_cast(&context.getQueryContext()); const auto & function_storage = query_context->executeTableFunction(table_function); - const auto & columns = function_storage->getColumns(); + auto function_metadata_snapshot = function_storage->getInMemoryMetadataPtr(); + const auto & columns = function_metadata_snapshot->getColumns(); names_and_type_list = columns.getOrdinary(); materialized = columns.getMaterialized(); aliases = columns.getAliases(); @@ -97,7 +98,8 @@ static NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression { auto table_id = context.resolveStorageID(table_expression.database_and_table_name); const auto & table = DatabaseCatalog::instance().getTable(table_id, context); - const auto & columns = table->getColumns(); + auto table_metadata_snapshot = table->getInMemoryMetadataPtr(); + const auto & columns = table_metadata_snapshot->getColumns(); names_and_type_list = columns.getOrdinary(); materialized = columns.getMaterialized(); aliases = columns.getAliases(); diff --git a/src/Interpreters/interpretSubquery.cpp b/src/Interpreters/interpretSubquery.cpp index c94759897f57..cf343a4fda26 100644 --- a/src/Interpreters/interpretSubquery.cpp +++ b/src/Interpreters/interpretSubquery.cpp @@ -90,14 +90,14 @@ std::shared_ptr interpretSubquery( { auto * query_context = const_cast(&context.getQueryContext()); const auto & storage = query_context->executeTableFunction(table_expression); - columns = storage->getColumns().getOrdinary(); + columns = storage->getInMemoryMetadataPtr()->getColumns().getOrdinary(); select_query->addTableFunction(*const_cast(&table_expression)); // XXX: const_cast should be avoided! } else { auto table_id = context.resolveStorageID(table_expression); const auto & storage = DatabaseCatalog::instance().getTable(table_id, context); - columns = storage->getColumns().getOrdinary(); + columns = storage->getInMemoryMetadataPtr()->getColumns().getOrdinary(); select_query->replaceDatabaseAndTable(table_id); } diff --git a/src/Server/MySQLHandler.cpp b/src/Server/MySQLHandler.cpp index 51b3d7eaef52..68f1bb8efff7 100644 --- a/src/Server/MySQLHandler.cpp +++ b/src/Server/MySQLHandler.cpp @@ -254,7 +254,8 @@ void MySQLHandler::comFieldList(ReadBuffer & payload) packet.readPayload(payload); String database = connection_context.getCurrentDatabase(); StoragePtr table_ptr = DatabaseCatalog::instance().getTable({database, packet.table}, connection_context); - for (const NameAndTypePair & column: table_ptr->getColumns().getAll()) + auto metadata_snapshot = table_ptr->getInMemoryMetadataPtr(); + for (const NameAndTypePair & column : metadata_snapshot->getColumns().getAll()) { ColumnDefinition column_definition( database, packet.table, packet.table, column.name, column.name, CharacterSet::binary, 100, ColumnType::MYSQL_TYPE_STRING, 0, 0 diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 009f7ad80f01..056234af45d3 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -213,17 +213,18 @@ void TCPHandler::runImpl() if (&context != &query_context.value()) throw Exception("Unexpected context in Input initializer", ErrorCodes::LOGICAL_ERROR); + auto metadata_snapshot = input_storage->getInMemoryMetadataPtr(); state.need_receive_data_for_input = true; /// Send ColumnsDescription for input storage. if (client_revision >= DBMS_MIN_REVISION_WITH_COLUMN_DEFAULTS_METADATA && query_context->getSettingsRef().input_format_defaults_for_omitted_fields) { - sendTableColumns(input_storage->getColumns()); + sendTableColumns(metadata_snapshot->getColumns()); } /// Send block to the client - input storage structure. - state.input_header = input_storage->getInMemoryMetadataPtr()->getSampleBlock(); + state.input_header = metadata_snapshot->getSampleBlock(); sendData(state.input_header); }); @@ -474,7 +475,10 @@ void TCPHandler::processInsertQuery(const Settings & connection_settings) if (query_context->getSettingsRef().input_format_defaults_for_omitted_fields) { if (!table_id.empty()) - sendTableColumns(DatabaseCatalog::instance().getTable(table_id, *query_context)->getColumns()); + { + auto storage_ptr = DatabaseCatalog::instance().getTable(table_id, *query_context); + sendTableColumns(storage_ptr->getInMemoryMetadataPtr()->getColumns()); + } } } diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 884982c93b92..8ee9561466a4 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -32,12 +32,6 @@ namespace ErrorCodes extern const int DEADLOCK_AVOIDED; } -const ColumnsDescription & IStorage::getColumns() const -{ - return metadata->columns; -} - - bool IStorage::isVirtualColumn(const String & column_name, const StorageMetadataPtr & metadata_snapshot) const { /// Virtual column maybe overriden by real column diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index a125c6f83104..ba1945d5c79e 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -137,7 +137,6 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired - const ColumnsDescription & getColumns() const; /// returns combined set of columns StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index f1b9459b3d3f..ac6bd48f5347 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -142,8 +142,10 @@ BlockInputStreamPtr StorageLiveView::completeQuery(Pipes pipes) auto creator = [&](const StorageID & blocks_id_global) { - return StorageBlocks::createStorage(blocks_id_global, getParentStorage()->getColumns(), - std::move(pipes), QueryProcessingStage::WithMergeableState); + auto parent_table_metadata = getParentStorage()->getInMemoryMetadataPtr(); + return StorageBlocks::createStorage( + blocks_id_global, parent_table_metadata->getColumns(), + std::move(pipes), QueryProcessingStage::WithMergeableState); }; block_context->addExternalTable(getBlocksTableName(), TemporaryTableHolder(global_context, creator)); @@ -209,8 +211,10 @@ void StorageLiveView::writeIntoLiveView( auto creator = [&](const StorageID & blocks_id_global) { - return StorageBlocks::createStorage(blocks_id_global, live_view.getParentStorage()->getColumns(), - std::move(pipes), QueryProcessingStage::FetchColumns); + auto parent_metadata = live_view.getParentStorage()->getInMemoryMetadataPtr(); + return StorageBlocks::createStorage( + blocks_id_global, parent_metadata->getColumns(), + std::move(pipes), QueryProcessingStage::FetchColumns); }; TemporaryTableHolder blocks_storage(context, creator); diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.cpp b/src/Storages/MergeTree/IMergeTreeDataPart.cpp index 17ff2259436a..61dfeed6b7cc 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPart.cpp @@ -352,9 +352,9 @@ size_t IMergeTreeDataPart::getFileSizeOrZero(const String & file_name) const return checksum->second.file_size; } -String IMergeTreeDataPart::getColumnNameWithMinumumCompressedSize() const +String IMergeTreeDataPart::getColumnNameWithMinumumCompressedSize(const StorageMetadataPtr & metadata_snapshot) const { - const auto & storage_columns = storage.getColumns().getAllPhysical(); + const auto & storage_columns = metadata_snapshot->getColumns().getAllPhysical(); auto alter_conversions = storage.getAlterConversionsForPart(shared_from_this()); std::optional minimum_size_column; @@ -613,6 +613,7 @@ void IMergeTreeDataPart::loadTTLInfos() void IMergeTreeDataPart::loadColumns(bool require) { String path = getFullRelativePath() + "columns.txt"; + auto metadata_snapshot = storage.getInMemoryMetadataPtr(); if (!volume->getDisk()->exists(path)) { /// We can get list of columns only from columns.txt in compact parts. @@ -620,7 +621,7 @@ void IMergeTreeDataPart::loadColumns(bool require) throw Exception("No columns.txt in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART); /// If there is no file with a list of columns, write it down. - for (const NameAndTypePair & column : storage.getColumns().getAllPhysical()) + for (const NameAndTypePair & column : metadata_snapshot->getColumns().getAllPhysical()) if (volume->getDisk()->exists(getFullRelativePath() + getFileNameForColumn(column) + ".bin")) columns.push_back(column); diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.h b/src/Storages/MergeTree/IMergeTreeDataPart.h index 04babece83e0..0e73b1370c52 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.h +++ b/src/Storages/MergeTree/IMergeTreeDataPart.h @@ -77,6 +77,7 @@ class IMergeTreeDataPart : public std::enable_shared_from_thisstorage) + : data_part(data_part_) + , avg_value_size_hints(avg_value_size_hints_) + , columns(columns_) + , uncompressed_cache(uncompressed_cache_) + , mark_cache(mark_cache_) + , settings(settings_) + , storage(data_part_->storage) + , metadata_snapshot(metadata_snapshot_) , all_mark_ranges(all_mark_ranges_) , alter_conversions(storage.getAlterConversionsForPart(data_part)) { @@ -112,7 +122,7 @@ void IMergeTreeReader::fillMissingColumns(Columns & res_columns, bool & should_e if (res_columns[i] == nullptr) { - if (storage.getColumns().hasDefault(name)) + if (metadata_snapshot->getColumns().hasDefault(name)) { should_evaluate_missing_defaults = true; continue; @@ -170,7 +180,7 @@ void IMergeTreeReader::evaluateMissingDefaults(Block additional_columns, Columns additional_columns.insert({res_columns[pos], name_and_type->type, name_and_type->name}); } - DB::evaluateMissingDefaults(additional_columns, columns, storage.getColumns().getDefaults(), storage.global_context); + DB::evaluateMissingDefaults(additional_columns, columns, metadata_snapshot->getColumns().getDefaults(), storage.global_context); /// Move columns from block. name_and_type = columns.begin(); diff --git a/src/Storages/MergeTree/IMergeTreeReader.h b/src/Storages/MergeTree/IMergeTreeReader.h index 02d8f67f9d06..6e9922b29ed1 100644 --- a/src/Storages/MergeTree/IMergeTreeReader.h +++ b/src/Storages/MergeTree/IMergeTreeReader.h @@ -18,8 +18,10 @@ class IMergeTreeReader : private boost::noncopyable using ValueSizeMap = std::map; using DeserializeBinaryBulkStateMap = std::map; - IMergeTreeReader(const MergeTreeData::DataPartPtr & data_part_, + IMergeTreeReader( + const MergeTreeData::DataPartPtr & data_part_, const NamesAndTypesList & columns_, + const StorageMetadataPtr & metadata_snapshot_, UncompressedCache * uncompressed_cache_, MarkCache * mark_cache_, const MarkRanges & all_mark_ranges_, @@ -75,6 +77,7 @@ class IMergeTreeReader : private boost::noncopyable MergeTreeReaderSettings settings; const MergeTreeData & storage; + StorageMetadataPtr metadata_snapshot; MarkRanges all_mark_ranges; friend class MergeTreeRangeReader::DelayedStream; diff --git a/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp b/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp index 310c566fb19b..03235742a683 100644 --- a/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp +++ b/src/Storages/MergeTree/MergeTreeBlockReadUtils.cpp @@ -13,14 +13,14 @@ namespace ErrorCodes } -NameSet injectRequiredColumns(const MergeTreeData & storage, const MergeTreeData::DataPartPtr & part, Names & columns) +NameSet injectRequiredColumns(const MergeTreeData & storage, const StorageMetadataPtr & metadata_snapshot, const MergeTreeData::DataPartPtr & part, Names & columns) { NameSet required_columns{std::begin(columns), std::end(columns)}; NameSet injected_columns; auto all_column_files_missing = true; - const auto & storage_columns = storage.getColumns(); + const auto & storage_columns = metadata_snapshot->getColumns(); auto alter_conversions = storage.getAlterConversionsForPart(part); for (size_t i = 0; i < columns.size(); ++i) { @@ -66,7 +66,7 @@ NameSet injectRequiredColumns(const MergeTreeData & storage, const MergeTreeData */ if (all_column_files_missing) { - const auto minimum_size_column_name = part->getColumnNameWithMinumumCompressedSize(); + const auto minimum_size_column_name = part->getColumnNameWithMinumumCompressedSize(metadata_snapshot); columns.push_back(minimum_size_column_name); /// correctly report added column injected_columns.insert(columns.back()); @@ -214,14 +214,19 @@ void MergeTreeBlockSizePredictor::update(const Block & sample_block, const Colum } -MergeTreeReadTaskColumns getReadTaskColumns(const MergeTreeData & storage, const MergeTreeData::DataPartPtr & data_part, - const Names & required_columns, const PrewhereInfoPtr & prewhere_info, bool check_columns) +MergeTreeReadTaskColumns getReadTaskColumns( + const MergeTreeData & storage, + const StorageMetadataPtr & metadata_snapshot, + const MergeTreeData::DataPartPtr & data_part, + const Names & required_columns, + const PrewhereInfoPtr & prewhere_info, + bool check_columns) { Names column_names = required_columns; Names pre_column_names; /// inject columns required for defaults evaluation - bool should_reorder = !injectRequiredColumns(storage, data_part, column_names).empty(); + bool should_reorder = !injectRequiredColumns(storage, metadata_snapshot, data_part, column_names).empty(); if (prewhere_info) { @@ -233,7 +238,7 @@ MergeTreeReadTaskColumns getReadTaskColumns(const MergeTreeData & storage, const if (pre_column_names.empty()) pre_column_names.push_back(column_names[0]); - const auto injected_pre_columns = injectRequiredColumns(storage, data_part, pre_column_names); + const auto injected_pre_columns = injectRequiredColumns(storage, metadata_snapshot, data_part, pre_column_names); if (!injected_pre_columns.empty()) should_reorder = true; @@ -251,7 +256,7 @@ MergeTreeReadTaskColumns getReadTaskColumns(const MergeTreeData & storage, const if (check_columns) { - const NamesAndTypesList & physical_columns = storage.getColumns().getAllPhysical(); + const NamesAndTypesList & physical_columns = metadata_snapshot->getColumns().getAllPhysical(); result.pre_columns = physical_columns.addTypes(pre_column_names); result.columns = physical_columns.addTypes(column_names); } diff --git a/src/Storages/MergeTree/MergeTreeBlockReadUtils.h b/src/Storages/MergeTree/MergeTreeBlockReadUtils.h index 108742e11012..31d609e42426 100644 --- a/src/Storages/MergeTree/MergeTreeBlockReadUtils.h +++ b/src/Storages/MergeTree/MergeTreeBlockReadUtils.h @@ -22,7 +22,7 @@ using MergeTreeBlockSizePredictorPtr = std::unique_ptrgetColumns().getNamesOfPhysical(); + NamesAndTypesList storage_columns = metadata_snapshot->getColumns().getAllPhysical(); const auto data_settings = data.getSettings(); NamesAndTypesList gathering_columns; @@ -1041,7 +1041,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor UInt64 watch_prev_elapsed = 0; MergeStageProgress stage_progress(1.0); - NamesAndTypesList storage_columns = data.getColumns().getAllPhysical(); + NamesAndTypesList storage_columns = metadata_snapshot->getColumns().getAllPhysical(); if (!for_interpreter.empty()) { diff --git a/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp b/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp index d45aa882b2a9..65577eb4ca1d 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartCompact.cpp @@ -38,6 +38,7 @@ MergeTreeDataPartCompact::MergeTreeDataPartCompact( IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartCompact::getReader( const NamesAndTypesList & columns_to_read, + const StorageMetadataPtr & metadata_snapshot, const MarkRanges & mark_ranges, UncompressedCache * uncompressed_cache, MarkCache * mark_cache, @@ -47,7 +48,7 @@ IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartCompact::getReader( { auto ptr = std::static_pointer_cast(shared_from_this()); return std::make_unique( - ptr, columns_to_read, uncompressed_cache, + ptr, columns_to_read, metadata_snapshot, uncompressed_cache, mark_cache, mark_ranges, reader_settings, avg_value_size_hints, profile_callback); } diff --git a/src/Storages/MergeTree/MergeTreeDataPartCompact.h b/src/Storages/MergeTree/MergeTreeDataPartCompact.h index 0b27dd533395..7d5c8628570b 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartCompact.h +++ b/src/Storages/MergeTree/MergeTreeDataPartCompact.h @@ -37,6 +37,7 @@ class MergeTreeDataPartCompact : public IMergeTreeDataPart MergeTreeReaderPtr getReader( const NamesAndTypesList & columns, + const StorageMetadataPtr & metadata_snapshot, const MarkRanges & mark_ranges, UncompressedCache * uncompressed_cache, MarkCache * mark_cache, diff --git a/src/Storages/MergeTree/MergeTreeDataPartWide.cpp b/src/Storages/MergeTree/MergeTreeDataPartWide.cpp index e4901b1f74d3..e9383bc917d6 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWide.cpp +++ b/src/Storages/MergeTree/MergeTreeDataPartWide.cpp @@ -37,6 +37,7 @@ MergeTreeDataPartWide::MergeTreeDataPartWide( IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartWide::getReader( const NamesAndTypesList & columns_to_read, + const StorageMetadataPtr & metadata_snapshot, const MarkRanges & mark_ranges, UncompressedCache * uncompressed_cache, MarkCache * mark_cache, @@ -46,7 +47,7 @@ IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartWide::getReader( { auto ptr = std::static_pointer_cast(shared_from_this()); return std::make_unique( - ptr, columns_to_read, uncompressed_cache, + ptr, columns_to_read, metadata_snapshot, uncompressed_cache, mark_cache, mark_ranges, reader_settings, avg_value_size_hints, profile_callback); } diff --git a/src/Storages/MergeTree/MergeTreeDataPartWide.h b/src/Storages/MergeTree/MergeTreeDataPartWide.h index 144dfa86cfb8..8d8b6fa678bf 100644 --- a/src/Storages/MergeTree/MergeTreeDataPartWide.h +++ b/src/Storages/MergeTree/MergeTreeDataPartWide.h @@ -30,6 +30,7 @@ class MergeTreeDataPartWide : public IMergeTreeDataPart MergeTreeReaderPtr getReader( const NamesAndTypesList & columns, + const StorageMetadataPtr & metadata_snapshot, const MarkRanges & mark_ranges, UncompressedCache * uncompressed_cache, MarkCache * mark_cache, diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index b72c46afca30..7f7fd203297d 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -650,7 +650,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( auto order_key_prefix_ast = metadata_snapshot->getSortingKey().expression_list_ast->clone(); order_key_prefix_ast->children.resize(prefix_size); - auto syntax_result = SyntaxAnalyzer(context).analyze(order_key_prefix_ast, data.getColumns().getAllPhysical()); + auto syntax_result = SyntaxAnalyzer(context).analyze(order_key_prefix_ast, metadata_snapshot->getColumns().getAllPhysical()); auto sorting_key_prefix_expr = ExpressionAnalyzer(order_key_prefix_ast, syntax_result, context).getActions(false); res = spreadMarkRangesAmongStreamsWithOrder( @@ -1274,29 +1274,6 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsFinal( return pipes; } - -void MergeTreeDataSelectExecutor::createPositiveSignCondition( - ExpressionActionsPtr & out_expression, String & out_column, const Context & context) const -{ - auto function = std::make_shared(); - auto arguments = std::make_shared(); - auto sign = std::make_shared(data.merging_params.sign_column); - auto one = std::make_shared(1); - - function->name = "equals"; - function->arguments = arguments; - function->children.push_back(arguments); - - arguments->children.push_back(sign); - arguments->children.push_back(one); - - ASTPtr query = function; - auto syntax_result = SyntaxAnalyzer(context).analyze(query, data.getColumns().getAllPhysical()); - out_expression = ExpressionAnalyzer(query, syntax_result, context).getActions(false); - out_column = function->getColumnName(); -} - - /// Calculates a set of mark ranges, that could possibly contain keys, required by condition. /// In other words, it removes subranges from whole range, that definitely could not contain required keys. MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h index ba0613a832d6..5669e8708b62 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -95,12 +95,6 @@ class MergeTreeDataSelectExecutor const KeyCondition & key_condition, const Settings & settings) const; - /// Create the expression "Sign == 1". - void createPositiveSignCondition( - ExpressionActionsPtr & out_expression, - String & out_column, - const Context & context) const; - MarkRanges markRangesFromPKRange( const MergeTreeData::DataPartPtr & part, const StorageMetadataPtr & metadata_snapshot, diff --git a/src/Storages/MergeTree/MergeTreeReadPool.cpp b/src/Storages/MergeTree/MergeTreeReadPool.cpp index 8c73dc39dfbe..fdf3908d21e8 100644 --- a/src/Storages/MergeTree/MergeTreeReadPool.cpp +++ b/src/Storages/MergeTree/MergeTreeReadPool.cpp @@ -211,7 +211,7 @@ std::vector MergeTreeReadPool::fillPerPartInfo( per_part_sum_marks.push_back(sum_marks); auto [required_columns, required_pre_columns, should_reorder] = - getReadTaskColumns(data, part.data_part, column_names, prewhere_info, check_columns); + getReadTaskColumns(data, metadata_snapshot, part.data_part, column_names, prewhere_info, check_columns); /// will be used to distinguish between PREWHERE and WHERE columns when applying filter const auto & required_column_names = required_columns.getNames(); diff --git a/src/Storages/MergeTree/MergeTreeReaderCompact.cpp b/src/Storages/MergeTree/MergeTreeReaderCompact.cpp index c4a05a8bfac8..64e9deec7447 100644 --- a/src/Storages/MergeTree/MergeTreeReaderCompact.cpp +++ b/src/Storages/MergeTree/MergeTreeReaderCompact.cpp @@ -17,6 +17,7 @@ namespace ErrorCodes MergeTreeReaderCompact::MergeTreeReaderCompact( DataPartCompactPtr data_part_, NamesAndTypesList columns_, + const StorageMetadataPtr & metadata_snapshot_, UncompressedCache * uncompressed_cache_, MarkCache * mark_cache_, MarkRanges mark_ranges_, @@ -24,15 +25,23 @@ MergeTreeReaderCompact::MergeTreeReaderCompact( ValueSizeMap avg_value_size_hints_, const ReadBufferFromFileBase::ProfileCallback & profile_callback_, clockid_t clock_type_) - : IMergeTreeReader(std::move(data_part_), std::move(columns_), - uncompressed_cache_, mark_cache_, std::move(mark_ranges_), - std::move(settings_), std::move(avg_value_size_hints_)) + : IMergeTreeReader( + std::move(data_part_), + std::move(columns_), + metadata_snapshot_, + uncompressed_cache_, + mark_cache_, + std::move(mark_ranges_), + std::move(settings_), + std::move(avg_value_size_hints_)) , marks_loader( - data_part->volume->getDisk(), - mark_cache, - data_part->index_granularity_info.getMarksFilePath(data_part->getFullRelativePath() + MergeTreeDataPartCompact::DATA_FILE_NAME), - data_part->getMarksCount(), data_part->index_granularity_info, - settings.save_marks_in_cache, data_part->getColumns().size()) + data_part->volume->getDisk(), + mark_cache, + data_part->index_granularity_info.getMarksFilePath(data_part->getFullRelativePath() + MergeTreeDataPartCompact::DATA_FILE_NAME), + data_part->getMarksCount(), + data_part->index_granularity_info, + settings.save_marks_in_cache, + data_part->getColumns().size()) { size_t buffer_size = settings.max_read_buffer_size; const String full_data_path = data_part->getFullRelativePath() + MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION; diff --git a/src/Storages/MergeTree/MergeTreeReaderCompact.h b/src/Storages/MergeTree/MergeTreeReaderCompact.h index 827306cd9838..584d8ed2ff0f 100644 --- a/src/Storages/MergeTree/MergeTreeReaderCompact.h +++ b/src/Storages/MergeTree/MergeTreeReaderCompact.h @@ -17,6 +17,7 @@ class MergeTreeReaderCompact : public IMergeTreeReader MergeTreeReaderCompact( DataPartCompactPtr data_part_, NamesAndTypesList columns_, + const StorageMetadataPtr & metadata_snapshot_, UncompressedCache * uncompressed_cache_, MarkCache * mark_cache_, MarkRanges mark_ranges_, diff --git a/src/Storages/MergeTree/MergeTreeReaderWide.cpp b/src/Storages/MergeTree/MergeTreeReaderWide.cpp index 34bf095e57e9..2326c012fee0 100644 --- a/src/Storages/MergeTree/MergeTreeReaderWide.cpp +++ b/src/Storages/MergeTree/MergeTreeReaderWide.cpp @@ -28,6 +28,7 @@ namespace ErrorCodes MergeTreeReaderWide::MergeTreeReaderWide( DataPartWidePtr data_part_, NamesAndTypesList columns_, + const StorageMetadataPtr & metadata_snapshot_, UncompressedCache * uncompressed_cache_, MarkCache * mark_cache_, MarkRanges mark_ranges_, @@ -36,8 +37,14 @@ MergeTreeReaderWide::MergeTreeReaderWide( const ReadBufferFromFileBase::ProfileCallback & profile_callback_, clockid_t clock_type_) : IMergeTreeReader( - std::move(data_part_), std::move(columns_), uncompressed_cache_, std::move(mark_cache_), - std::move(mark_ranges_), std::move(settings_), std::move(avg_value_size_hints_)) + std::move(data_part_), + std::move(columns_), + metadata_snapshot_, + uncompressed_cache_, + std::move(mark_cache_), + std::move(mark_ranges_), + std::move(settings_), + std::move(avg_value_size_hints_)) { try { diff --git a/src/Storages/MergeTree/MergeTreeReaderWide.h b/src/Storages/MergeTree/MergeTreeReaderWide.h index 7684d69f0a5d..69652d1e954c 100644 --- a/src/Storages/MergeTree/MergeTreeReaderWide.h +++ b/src/Storages/MergeTree/MergeTreeReaderWide.h @@ -17,6 +17,7 @@ class MergeTreeReaderWide : public IMergeTreeReader MergeTreeReaderWide( DataPartWidePtr data_part_, NamesAndTypesList columns_, + const StorageMetadataPtr & metadata_snapshot_, UncompressedCache * uncompressed_cache_, MarkCache * mark_cache_, MarkRanges mark_ranges_, diff --git a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp index c47dd7fb669a..b71c343614be 100644 --- a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp @@ -76,7 +76,7 @@ MergeTreeReverseSelectProcessor::MergeTreeReverseSelectProcessor( ordered_names = header_without_virtual_columns.getNames(); - task_columns = getReadTaskColumns(storage, data_part, required_columns, prewhere_info, check_columns); + task_columns = getReadTaskColumns(storage, metadata_snapshot, data_part, required_columns, prewhere_info, check_columns); /// will be used to distinguish between PREWHERE and WHERE columns when applying filter const auto & column_names = task_columns.columns.getNames(); @@ -87,11 +87,12 @@ MergeTreeReverseSelectProcessor::MergeTreeReverseSelectProcessor( owned_mark_cache = storage.global_context.getMarkCache(); - reader = data_part->getReader(task_columns.columns, all_mark_ranges, - owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings); + reader = data_part->getReader(task_columns.columns, metadata_snapshot, + all_mark_ranges, owned_uncompressed_cache.get(), + owned_mark_cache.get(), reader_settings); if (prewhere_info) - pre_reader = data_part->getReader(task_columns.pre_columns, all_mark_ranges, + pre_reader = data_part->getReader(task_columns.pre_columns, metadata_snapshot, all_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings); } diff --git a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp index 84c0f44c109e..b46b414bfe88 100644 --- a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp @@ -68,7 +68,9 @@ try } is_first_task = false; - task_columns = getReadTaskColumns(storage, data_part, required_columns, prewhere_info, check_columns); + task_columns = getReadTaskColumns( + storage, metadata_snapshot, data_part, + required_columns, prewhere_info, check_columns); auto size_predictor = (preferred_block_size_bytes == 0) ? nullptr @@ -90,11 +92,11 @@ try owned_mark_cache = storage.global_context.getMarkCache(); - reader = data_part->getReader(task_columns.columns, all_mark_ranges, + reader = data_part->getReader(task_columns.columns, metadata_snapshot, all_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings); if (prewhere_info) - pre_reader = data_part->getReader(task_columns.pre_columns, all_mark_ranges, + pre_reader = data_part->getReader(task_columns.pre_columns, metadata_snapshot, all_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings); } diff --git a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp index dfd60bd50ef6..f8e31db2b5ab 100644 --- a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp +++ b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp @@ -39,7 +39,7 @@ MergeTreeSequentialSource::MergeTreeSequentialSource( addTotalRowsApprox(data_part->rows_count); /// Add columns because we don't want to read empty blocks - injectRequiredColumns(storage, data_part, columns_to_read); + injectRequiredColumns(storage, metadata_snapshot, data_part, columns_to_read); NamesAndTypesList columns_for_reader; if (take_column_types_from_storage) { @@ -60,7 +60,7 @@ MergeTreeSequentialSource::MergeTreeSequentialSource( .save_marks_in_cache = false }; - reader = data_part->getReader(columns_for_reader, + reader = data_part->getReader(columns_for_reader, metadata_snapshot, MarkRanges{MarkRange(0, data_part->getMarksCount())}, /* uncompressed_cache = */ nullptr, mark_cache.get(), reader_settings); } diff --git a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp index 784c842d7d60..c332685799cd 100644 --- a/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeThreadSelectBlockInputProcessor.cpp @@ -74,12 +74,12 @@ bool MergeTreeThreadSelectBlockInputProcessor::getNewTask() owned_uncompressed_cache = storage.global_context.getUncompressedCache(); owned_mark_cache = storage.global_context.getMarkCache(); - reader = task->data_part->getReader(task->columns, rest_mark_ranges, + reader = task->data_part->getReader(task->columns, metadata_snapshot, rest_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings, IMergeTreeReader::ValueSizeMap{}, profile_callback); if (prewhere_info) - pre_reader = task->data_part->getReader(task->pre_columns, rest_mark_ranges, + pre_reader = task->data_part->getReader(task->pre_columns, metadata_snapshot, rest_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings, IMergeTreeReader::ValueSizeMap{}, profile_callback); } @@ -90,12 +90,12 @@ bool MergeTreeThreadSelectBlockInputProcessor::getNewTask() { auto rest_mark_ranges = pool->getRestMarks(*task->data_part, task->mark_ranges[0]); /// retain avg_value_size_hints - reader = task->data_part->getReader(task->columns, rest_mark_ranges, + reader = task->data_part->getReader(task->columns, metadata_snapshot, rest_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings, reader->getAvgValueSizeHints(), profile_callback); if (prewhere_info) - pre_reader = task->data_part->getReader(task->pre_columns, rest_mark_ranges, + pre_reader = task->data_part->getReader(task->pre_columns, metadata_snapshot, rest_mark_ranges, owned_uncompressed_cache.get(), owned_mark_cache.get(), reader_settings, reader->getAvgValueSizeHints(), profile_callback); } diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 2ce258a2d25d..4e659a8e7b11 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -168,9 +168,9 @@ Pipes StorageBuffer::read( auto destination_metadata_snapshot = destination->getInMemoryMetadataPtr(); - const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [metadata_snapshot, destination](const String& column_name) + const bool dst_has_same_structure = std::all_of(column_names.begin(), column_names.end(), [metadata_snapshot, destination_metadata_snapshot](const String& column_name) { - const auto & dest_columns = destination->getColumns(); + const auto & dest_columns = destination_metadata_snapshot->getColumns(); const auto & our_columns = metadata_snapshot->getColumns(); return dest_columns.hasPhysical(column_name) && dest_columns.get(column_name).type->equals(*our_columns.get(column_name).type); @@ -192,8 +192,8 @@ Pipes StorageBuffer::read( const Block header = metadata_snapshot->getSampleBlock(); Names columns_intersection = column_names; Block header_after_adding_defaults = header; - const auto & dest_columns = destination->getColumns(); - const auto & our_columns = getColumns(); + const auto & dest_columns = destination_metadata_snapshot->getColumns(); + const auto & our_columns = metadata_snapshot->getColumns(); for (const String & column_name : column_names) { if (!dest_columns.hasPhysical(column_name)) @@ -224,7 +224,7 @@ Pipes StorageBuffer::read( for (auto & pipe : pipes_from_dst) { pipe.addSimpleTransform(std::make_shared( - pipe.getHeader(), header_after_adding_defaults, getColumns().getDefaults(), context)); + pipe.getHeader(), header_after_adding_defaults, metadata_snapshot->getColumns().getDefaults(), context)); pipe.addSimpleTransform(std::make_shared( pipe.getHeader(), header, ConvertingTransform::MatchColumnsMode::Name)); diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index ce4fcbb35139..238623c1576e 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -290,7 +290,7 @@ StorageDistributed::StorageDistributed( if (sharding_key_) { - sharding_key_expr = buildShardingKeyExpression(sharding_key_, *global_context, getColumns().getAllPhysical(), false); + sharding_key_expr = buildShardingKeyExpression(sharding_key_, *global_context, metadata_.getColumns().getAllPhysical(), false); sharding_key_column_name = sharding_key_->getColumnName(); } @@ -447,6 +447,7 @@ bool StorageDistributed::canForceGroupByNoMerge(const Context &context, QueryPro QueryProcessingStage::Enum StorageDistributed::getQueryProcessingStage(const Context &context, QueryProcessingStage::Enum to_stage, const ASTPtr & query_ptr) const { const auto & settings = context.getSettingsRef(); + auto metadata_snapshot = getInMemoryMetadataPtr(); if (canForceGroupByNoMerge(context, to_stage, query_ptr)) return QueryProcessingStage::Complete; @@ -454,7 +455,7 @@ QueryProcessingStage::Enum StorageDistributed::getQueryProcessingStage(const Con ClusterPtr cluster = getCluster(); if (settings.optimize_skip_unused_shards) { - ClusterPtr optimized_cluster = getOptimizedCluster(context, query_ptr); + ClusterPtr optimized_cluster = getOptimizedCluster(context, metadata_snapshot, query_ptr); if (optimized_cluster) cluster = optimized_cluster; } @@ -476,7 +477,7 @@ Pipes StorageDistributed::read( ClusterPtr cluster = getCluster(); if (settings.optimize_skip_unused_shards) { - ClusterPtr optimized_cluster = getOptimizedCluster(context, query_info.query); + ClusterPtr optimized_cluster = getOptimizedCluster(context, metadata_snapshot, query_info.query); if (optimized_cluster) { LOG_DEBUG(log, "Skipping irrelevant shards - the query will be sent to the following shards of the cluster (shard numbers): {}", makeFormattedListOfShards(optimized_cluster)); @@ -683,14 +684,14 @@ ClusterPtr StorageDistributed::getCluster() const return owned_cluster ? owned_cluster : global_context->getCluster(cluster_name); } -ClusterPtr StorageDistributed::getOptimizedCluster(const Context & context, const ASTPtr & query_ptr) const +ClusterPtr StorageDistributed::getOptimizedCluster(const Context & context, const StorageMetadataPtr & metadata_snapshot, const ASTPtr & query_ptr) const { ClusterPtr cluster = getCluster(); const Settings & settings = context.getSettingsRef(); if (has_sharding_key) { - ClusterPtr optimized = skipUnusedShards(cluster, query_ptr, context); + ClusterPtr optimized = skipUnusedShards(cluster, query_ptr, metadata_snapshot, context); if (optimized) return optimized; } @@ -751,7 +752,11 @@ IColumn::Selector StorageDistributed::createSelector(const ClusterPtr cluster, c /// Returns a new cluster with fewer shards if constant folding for `sharding_key_expr` is possible /// using constraints from "PREWHERE" and "WHERE" conditions, otherwise returns `nullptr` -ClusterPtr StorageDistributed::skipUnusedShards(ClusterPtr cluster, const ASTPtr & query_ptr, const Context & context) const +ClusterPtr StorageDistributed::skipUnusedShards( + ClusterPtr cluster, + const ASTPtr & query_ptr, + const StorageMetadataPtr & metadata_snapshot, + const Context & context) const { const auto & select = query_ptr->as(); @@ -770,7 +775,7 @@ ClusterPtr StorageDistributed::skipUnusedShards(ClusterPtr cluster, const ASTPtr condition_ast = select.prewhere() ? select.prewhere()->clone() : select.where()->clone(); } - replaceConstantExpressions(condition_ast, context, getColumns().getAll(), shared_from_this()); + replaceConstantExpressions(condition_ast, context, metadata_snapshot->getColumns().getAll(), shared_from_this()); const auto blocks = evaluateExpressionOverConstantCondition(condition_ast, sharding_key_expr); // Can't get definite answer if we can skip any shards diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index 3f148cfff018..af508a80646b 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -120,8 +120,8 @@ class StorageDistributed final : public ext::shared_ptr_helper( - this_ptr, metadata_snapshot, context, max_block_size, files_info, getColumns().getDefaults())); + this_ptr, metadata_snapshot, context, max_block_size, files_info, metadata_snapshot->getColumns().getDefaults())); return pipes; } diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index bcebeec09dd9..dad323f7b725 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -441,7 +441,7 @@ Pipes StorageGenerateRandom::read( Pipes pipes; pipes.reserve(num_streams); - const ColumnsDescription & our_columns = getColumns(); + const ColumnsDescription & our_columns = metadata_snapshot->getColumns(); Block block_header; for (const auto & name : column_names) { diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index fcae9c9aa821..e0953283a17d 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -358,7 +358,7 @@ void LogBlockOutputStream::writeData(const String & name, const IDataType & type if (written_streams.count(stream_name)) return; - const auto & columns = storage.getColumns(); + const auto & columns = metadata_snapshot->getColumns(); streams.try_emplace( stream_name, storage.disk, @@ -445,7 +445,7 @@ StorageLog::StorageLog( /// create directories if they do not exist disk->createDirectories(table_path); - for (const auto & column : getColumns().getAllPhysical()) + for (const auto & column : metadata_.getColumns().getAllPhysical()) addFiles(column.name, *column.type); marks_file_path = table_path + DBMS_STORAGE_LOG_MARKS_FILE_NAME; @@ -539,13 +539,14 @@ void StorageLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLo { std::shared_lock lock(rwlock); + auto metadata_snapshot = getInMemoryMetadataPtr(); files.clear(); file_count = 0; loaded_marks = false; disk->clearDirectory(table_path); - for (const auto & column : getColumns().getAllPhysical()) + for (const auto & column : metadata_snapshot->getColumns().getAllPhysical()) addFiles(column.name, *column.type); file_checker = FileChecker{disk, table_path + "sizes.json"}; @@ -553,11 +554,11 @@ void StorageLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLo } -const StorageLog::Marks & StorageLog::getMarksWithRealRowCount() const +const StorageLog::Marks & StorageLog::getMarksWithRealRowCount(const StorageMetadataPtr & metadata_snapshot) const { /// There should be at least one physical column - const String column_name = getColumns().getAllPhysical().begin()->name; - const auto column_type = getColumns().getAllPhysical().begin()->type; + const String column_name = metadata_snapshot->getColumns().getAllPhysical().begin()->name; + const auto column_type = metadata_snapshot->getColumns().getAllPhysical().begin()->type; String filename; /** We take marks from first column. @@ -590,13 +591,13 @@ Pipes StorageLog::read( metadata_snapshot->check(column_names, getVirtuals()); loadMarks(); - NamesAndTypesList all_columns = Nested::collect(getColumns().getAllPhysical().addTypes(column_names)); + NamesAndTypesList all_columns = Nested::collect(metadata_snapshot->getColumns().getAllPhysical().addTypes(column_names)); std::shared_lock lock(rwlock); Pipes pipes; - const Marks & marks = getMarksWithRealRowCount(); + const Marks & marks = getMarksWithRealRowCount(metadata_snapshot); size_t marks_size = marks.size(); if (num_streams > marks_size) diff --git a/src/Storages/StorageLog.h b/src/Storages/StorageLog.h index 60f885ce45c3..90d0799e1a8d 100644 --- a/src/Storages/StorageLog.h +++ b/src/Storages/StorageLog.h @@ -26,7 +26,7 @@ class StorageLog final : public ext::shared_ptr_helper, public IStor Pipes read( const Names & column_names, - const StorageMetadataPtr & /*metadata_snapshot*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum processed_stage, @@ -112,7 +112,7 @@ class StorageLog final : public ext::shared_ptr_helper, public IStor * * Return the first group of marks that contain the number of rows, but not the internals of the arrays. */ - const Marks & getMarksWithRealRowCount() const; + const Marks & getMarksWithRealRowCount(const StorageMetadataPtr & metadata_snapshot) const; }; } diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 92e965c420e9..228cec993571 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -250,9 +250,11 @@ Pipes StorageMerge::createSources( if (!storage) { - auto pipe = InterpreterSelectQuery(modified_query_info.query, *modified_context, - std::make_shared(header), - SelectQueryOptions(processed_stage).analyze()).execute().pipeline.getPipe(); + auto pipe = InterpreterSelectQuery( + modified_query_info.query, *modified_context, + std::make_shared(header), + SelectQueryOptions(processed_stage).analyze()).execute().pipeline.getPipe(); + pipe.addInterpreterContext(modified_context); pipes.emplace_back(std::move(pipe)); return pipes; @@ -263,7 +265,7 @@ Pipes StorageMerge::createSources( { /// If there are only virtual columns in query, you must request at least one other column. if (real_column_names.empty()) - real_column_names.push_back(ExpressionActions::getSmallestColumn(storage->getColumns().getAllPhysical())); + real_column_names.push_back(ExpressionActions::getSmallestColumn(metadata_snapshot->getColumns().getAllPhysical())); pipes = storage->read(real_column_names, metadata_snapshot, modified_query_info, *modified_context, processed_stage, max_block_size, UInt32(streams_num)); } diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index b0a7e5502339..e45d54a8c648 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -500,7 +500,7 @@ bool StorageReplicatedMergeTree::createTableIfNotExists(const StorageMetadataPtr ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/metadata", metadata_str, zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/columns", getColumns().toString(), + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/columns", metadata_snapshot->getColumns().toString(), zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log", "", zkutil::CreateMode::Persistent)); @@ -535,7 +535,7 @@ bool StorageReplicatedMergeTree::createTableIfNotExists(const StorageMetadataPtr zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", metadata_str, zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", metadata_snapshot->getColumns().toString(), zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata_version", std::to_string(metadata_version), zkutil::CreateMode::Persistent)); @@ -596,7 +596,7 @@ void StorageReplicatedMergeTree::createReplica(const StorageMetadataPtr & metada zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", ReplicatedMergeTreeTableMetadata(*this, metadata_snapshot).toString(), zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", metadata_snapshot->getColumns().toString(), zkutil::CreateMode::Persistent)); ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata_version", std::to_string(metadata_version), zkutil::CreateMode::Persistent)); @@ -748,7 +748,7 @@ void StorageReplicatedMergeTree::checkTableStructure(const String & zookeeper_pr Coordination::Stat columns_stat; auto columns_from_zk = ColumnsDescription::parse(zookeeper->get(zookeeper_prefix + "/columns", &columns_stat)); - const ColumnsDescription & old_columns = getColumns(); + const ColumnsDescription & old_columns = metadata_snapshot->getColumns(); if (columns_from_zk != old_columns) { throw Exception("Table columns structure in ZooKeeper is different from local table structure", ErrorCodes::INCOMPATIBLE_COLUMNS); diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 4015d8ca5740..4578a82f6503 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -237,8 +237,9 @@ void TinyLogSource::readData(const String & name, const IDataType & type, IColum } -IDataType::OutputStreamGetter TinyLogBlockOutputStream::createStreamGetter(const String & name, - WrittenStreams & written_streams) +IDataType::OutputStreamGetter TinyLogBlockOutputStream::createStreamGetter( + const String & name, + WrittenStreams & written_streams) { return [&] (const IDataType::SubstreamPath & path) -> WriteBuffer * { @@ -247,12 +248,13 @@ IDataType::OutputStreamGetter TinyLogBlockOutputStream::createStreamGetter(const if (!written_streams.insert(stream_name).second) return nullptr; - const auto & columns = storage.getColumns(); + const auto & columns = metadata_snapshot->getColumns(); if (!streams.count(stream_name)) - streams[stream_name] = std::make_unique(storage.disk, - storage.files[stream_name].data_file_path, - columns.getCodecOrDefault(name), - storage.max_compress_block_size); + streams[stream_name] = std::make_unique( + storage.disk, + storage.files[stream_name].data_file_path, + columns.getCodecOrDefault(name), + storage.max_compress_block_size); return &streams[stream_name]->compressed; }; @@ -351,7 +353,7 @@ StorageTinyLog::StorageTinyLog( disk->createDirectories(table_path); } - for (const auto & col : getColumns().getAllPhysical()) + for (const auto & col : metadata_.getColumns().getAllPhysical()) addFiles(col.name, *col.type); } @@ -430,13 +432,14 @@ CheckResults StorageTinyLog::checkData(const ASTPtr & /* query */, const Context void StorageTinyLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) { std::unique_lock lock(rwlock); + auto metadata_snapshot = getInMemoryMetadataPtr(); disk->clearDirectory(table_path); files.clear(); file_checker = FileChecker{disk, table_path + "sizes.json"}; - for (const auto &column : getColumns().getAllPhysical()) + for (const auto & column : metadata_snapshot->getColumns().getAllPhysical()) addFiles(column.name, *column.type); } diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index 949d922b611b..802ad0571a8e 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -136,7 +136,9 @@ std::string IStorageURLBase::getReadMethod() const return Poco::Net::HTTPRequest::HTTP_GET; } -std::vector> IStorageURLBase::getReadURIParams(const Names & /*column_names*/, +std::vector> IStorageURLBase::getReadURIParams( + const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum & /*processed_stage*/, @@ -145,7 +147,9 @@ std::vector> IStorageURLBase::getReadURIPara return {}; } -std::function IStorageURLBase::getReadPOSTDataCallback(const Names & /*column_names*/, +std::function IStorageURLBase::getReadPOSTDataCallback( + const Names & /*column_names*/, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum & /*processed_stage*/, @@ -165,7 +169,7 @@ Pipes IStorageURLBase::read( unsigned /*num_streams*/) { auto request_uri = uri; - auto params = getReadURIParams(column_names, query_info, context, processed_stage, max_block_size); + auto params = getReadURIParams(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size); for (const auto & [param, value] : params) request_uri.addQueryParameter(param, value); @@ -173,7 +177,9 @@ Pipes IStorageURLBase::read( pipes.emplace_back(std::make_shared( request_uri, getReadMethod(), - getReadPOSTDataCallback(column_names, query_info, context, processed_stage, max_block_size), + getReadPOSTDataCallback( + column_names, metadata_snapshot, query_info, + context, processed_stage, max_block_size), format_name, getName(), getHeaderBlock(column_names, metadata_snapshot), diff --git a/src/Storages/StorageURL.h b/src/Storages/StorageURL.h index 04cbb278c379..67ad95d2f91c 100644 --- a/src/Storages/StorageURL.h +++ b/src/Storages/StorageURL.h @@ -50,6 +50,7 @@ class IStorageURLBase : public IStorage virtual std::vector> getReadURIParams( const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum & processed_stage, @@ -57,6 +58,7 @@ class IStorageURLBase : public IStorage virtual std::function getReadPOSTDataCallback( const Names & column_names, + const StorageMetadataPtr & /*metadata_snapshot*/, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum & processed_stage, @@ -68,12 +70,13 @@ class IStorageURLBase : public IStorage class StorageURLBlockOutputStream : public IBlockOutputStream { public: - StorageURLBlockOutputStream(const Poco::URI & uri, - const String & format, - const Block & sample_block_, - const Context & context, - const ConnectionTimeouts & timeouts, - const CompressionMethod compression_method); + StorageURLBlockOutputStream( + const Poco::URI & uri, + const String & format, + const Block & sample_block_, + const Context & context, + const ConnectionTimeouts & timeouts, + const CompressionMethod compression_method); Block getHeader() const override { diff --git a/src/Storages/StorageXDBC.cpp b/src/Storages/StorageXDBC.cpp index ab8b37db7db0..05cf4ed5abff 100644 --- a/src/Storages/StorageXDBC.cpp +++ b/src/Storages/StorageXDBC.cpp @@ -50,7 +50,9 @@ std::string StorageXDBC::getReadMethod() const return Poco::Net::HTTPRequest::HTTP_POST; } -std::vector> StorageXDBC::getReadURIParams(const Names & column_names, +std::vector> StorageXDBC::getReadURIParams( + const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & /*query_info*/, const Context & /*context*/, QueryProcessingStage::Enum & /*processed_stage*/, @@ -59,20 +61,22 @@ std::vector> StorageXDBC::getReadURIParams(c NamesAndTypesList cols; for (const String & name : column_names) { - auto column_data = getColumns().getPhysical(name); + auto column_data = metadata_snapshot->getColumns().getPhysical(name); cols.emplace_back(column_data.name, column_data.type); } return bridge_helper->getURLParams(cols.toString(), max_block_size); } -std::function StorageXDBC::getReadPOSTDataCallback(const Names & /*column_names*/, +std::function StorageXDBC::getReadPOSTDataCallback( + const Names & /*column_names*/, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum & /*processed_stage*/, size_t /*max_block_size*/) const { String query = transformQueryForExternalDatabase(query_info, - getColumns().getOrdinary(), + metadata_snapshot->getColumns().getOrdinary(), bridge_helper->getIdentifierQuotingStyle(), remote_database_name, remote_table_name, diff --git a/src/Storages/StorageXDBC.h b/src/Storages/StorageXDBC.h index 0e227d7d4323..44931af4643f 100644 --- a/src/Storages/StorageXDBC.h +++ b/src/Storages/StorageXDBC.h @@ -29,7 +29,8 @@ class StorageXDBC : public IStorageURLBase const std::string & remote_database_name, const std::string & remote_table_name, const ColumnsDescription & columns_, - const Context & context_, BridgeHelperPtr bridge_helper_); + const Context & context_, + BridgeHelperPtr bridge_helper_); BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; @@ -45,6 +46,7 @@ class StorageXDBC : public IStorageURLBase std::vector> getReadURIParams( const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum & processed_stage, @@ -52,6 +54,7 @@ class StorageXDBC : public IStorageURLBase std::function getReadPOSTDataCallback( const Names & column_names, + const StorageMetadataPtr & metadata_snapshot, const SelectQueryInfo & query_info, const Context & context, QueryProcessingStage::Enum & processed_stage, diff --git a/src/Storages/System/StorageSystemPartsColumns.cpp b/src/Storages/System/StorageSystemPartsColumns.cpp index 479621fd47f6..4631bb9c4c52 100644 --- a/src/Storages/System/StorageSystemPartsColumns.cpp +++ b/src/Storages/System/StorageSystemPartsColumns.cpp @@ -71,7 +71,7 @@ void StorageSystemPartsColumns::processNextStorage(MutableColumns & columns_, co }; std::unordered_map columns_info; - for (const auto & column : info.storage->getColumns()) + for (const auto & column : info.storage->getInMemoryMetadataPtr()->getColumns()) { ColumnInfo column_info; if (column.default_desc.expression) diff --git a/src/Storages/TTLDescription.cpp b/src/Storages/TTLDescription.cpp index ea6b3e64aff7..6e0d323e8a05 100644 --- a/src/Storages/TTLDescription.cpp +++ b/src/Storages/TTLDescription.cpp @@ -220,7 +220,7 @@ TTLDescription TTLDescription::getTTLFromAST( if (value->as()) { - auto syntax_result = SyntaxAnalyzer(context).analyze(value, columns.getAllPhysical(), {}, true); + auto syntax_result = SyntaxAnalyzer(context).analyze(value, columns.getAllPhysical(), {}, {}, true); auto expr_actions = ExpressionAnalyzer(value, syntax_result, context).getActions(false); for (const auto & column : expr_actions->getRequiredColumns()) { @@ -249,7 +249,7 @@ TTLDescription TTLDescription::getTTLFromAST( for (auto [name, value] : aggregations) { - auto syntax_result = SyntaxAnalyzer(context).analyze(value, columns.getAllPhysical(), {}, true); + auto syntax_result = SyntaxAnalyzer(context).analyze(value, columns.getAllPhysical(), {}, {}, true); auto expr_analyzer = ExpressionAnalyzer(value, syntax_result, context); TTLAggregateDescription set_part; diff --git a/src/Storages/getStructureOfRemoteTable.cpp b/src/Storages/getStructureOfRemoteTable.cpp index 19d1172f1ffc..aca5456d85ec 100644 --- a/src/Storages/getStructureOfRemoteTable.cpp +++ b/src/Storages/getStructureOfRemoteTable.cpp @@ -75,7 +75,8 @@ ColumnsDescription getStructureOfRemoteTableInShard( { const auto * table_function = table_func_ptr->as(); TableFunctionPtr table_function_ptr = TableFunctionFactory::instance().get(table_function->name, context); - return table_function_ptr->execute(table_func_ptr, context, table_function_ptr->getName())->getColumns(); + auto storage_ptr = table_function_ptr->execute(table_func_ptr, context, table_function_ptr->getName()); + return storage_ptr->getInMemoryMetadataPtr()->getColumns(); } auto table_func_name = queryToString(table_func_ptr); @@ -84,7 +85,10 @@ ColumnsDescription getStructureOfRemoteTableInShard( else { if (shard_info.isLocal()) - return DatabaseCatalog::instance().getTable(table_id, context)->getColumns(); + { + auto storage_ptr = DatabaseCatalog::instance().getTable(table_id, context); + return storage_ptr->getInMemoryMetadataPtr()->getColumns(); + } /// Request for a table description query = "DESC TABLE " + table_id.getFullTableName(); diff --git a/src/Storages/tests/gtest_storage_log.cpp b/src/Storages/tests/gtest_storage_log.cpp index 618d524987b3..c97adaf118d7 100644 --- a/src/Storages/tests/gtest_storage_log.cpp +++ b/src/Storages/tests/gtest_storage_log.cpp @@ -78,7 +78,7 @@ std::string writeData(int rows, DB::StoragePtr & table, const DB::Context & cont Block block; { - const auto & storage_columns = table->getColumns(); + const auto & storage_columns = metadata_snapshot->getColumns(); ColumnWithTypeAndName column; column.name = "a"; column.type = storage_columns.getPhysical("a").type; diff --git a/src/TableFunctions/TableFunctionMerge.cpp b/src/TableFunctions/TableFunctionMerge.cpp index ee447a13174a..7c0c1fb233ff 100644 --- a/src/TableFunctions/TableFunctionMerge.cpp +++ b/src/TableFunctions/TableFunctionMerge.cpp @@ -42,7 +42,7 @@ static NamesAndTypesList chooseColumns(const String & source_database, const Str throw Exception("Error while executing table function merge. In database " + source_database + " no one matches regular expression: " + table_name_regexp_, ErrorCodes::UNKNOWN_TABLE); - return any_table->getColumns().getAllPhysical(); + return any_table->getInMemoryMetadataPtr()->getColumns().getAllPhysical(); } From c5d9379df0ecffe56f186141c1da3a11ee511277 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 19:54:51 +0300 Subject: [PATCH 0377/1102] Add TotalsHavingStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 8 ++-- src/Processors/QueryPlan/TotalsHavingStep.cpp | 38 ++++++++++++++++++ src/Processors/QueryPlan/TotalsHavingStep.h | 39 +++++++++++++++++++ .../Transforms/TotalsHavingTransform.cpp | 4 +- .../Transforms/TotalsHavingTransform.h | 2 + src/Processors/ya.make | 1 + 6 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 src/Processors/QueryPlan/TotalsHavingStep.cpp create mode 100644 src/Processors/QueryPlan/TotalsHavingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 11c3a6b1211b..9452fbeaa43c 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -89,6 +89,7 @@ #include #include #include +#include namespace DB @@ -1508,12 +1509,13 @@ void InterpreterSelectQuery::executeTotalsAndHaving(QueryPipeline & pipeline, bo { const Settings & settings = context->getSettingsRef(); - auto totals_having = std::make_shared( - pipeline.getHeader(), overflow_row, expression, + TotalsHavingStep totals_having_step( + DataStream{.header = pipeline.getHeader()}, + overflow_row, expression, has_having ? getSelectQuery().having()->getColumnName() : "", settings.totals_mode, settings.totals_auto_threshold, final); - pipeline.addTotalsHavingTransform(std::move(totals_having)); + totals_having_step.transformPipeline(pipeline); } diff --git a/src/Processors/QueryPlan/TotalsHavingStep.cpp b/src/Processors/QueryPlan/TotalsHavingStep.cpp new file mode 100644 index 000000000000..0db1acd74f58 --- /dev/null +++ b/src/Processors/QueryPlan/TotalsHavingStep.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +namespace DB +{ + +TotalsHavingStep::TotalsHavingStep( + const DataStream & input_stream_, + bool overflow_row_, + const ExpressionActionsPtr & expression_, + const std::string & filter_column_, + TotalsMode totals_mode_, + double auto_include_threshold_, + bool final_) + : ITransformingStep( + input_stream_, + DataStream{.header = TotalsHavingTransform::transformHeader(input_stream_.header, expression_, final_)}) + , overflow_row(overflow_row_) + , expression(expression_) + , filter_column_name(filter_column_) + , totals_mode(totals_mode_) + , auto_include_threshold(auto_include_threshold_) + , final(final_) +{ +} + +void TotalsHavingStep::transformPipeline(QueryPipeline & pipeline) +{ + auto totals_having = std::make_shared( + pipeline.getHeader(), overflow_row, expression, + filter_column_name, totals_mode, auto_include_threshold, final); + + pipeline.addTotalsHavingTransform(std::move(totals_having)); +} + +} diff --git a/src/Processors/QueryPlan/TotalsHavingStep.h b/src/Processors/QueryPlan/TotalsHavingStep.h new file mode 100644 index 000000000000..ddb0ccd25728 --- /dev/null +++ b/src/Processors/QueryPlan/TotalsHavingStep.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +namespace DB +{ + +class ExpressionActions; +using ExpressionActionsPtr = std::shared_ptr; + +enum class TotalsMode; + +class TotalsHavingStep : public ITransformingStep +{ +public: + TotalsHavingStep( + const DataStream & input_stream_, + bool overflow_row_, + const ExpressionActionsPtr & expression_, + const std::string & filter_column_, + TotalsMode totals_mode_, + double auto_include_threshold_, + bool final_); + + String getName() const override { return "TotalsHaving"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + bool overflow_row; + ExpressionActionsPtr expression; + String filter_column_name; + TotalsMode totals_mode; + double auto_include_threshold; + bool final; +}; + +} + diff --git a/src/Processors/Transforms/TotalsHavingTransform.cpp b/src/Processors/Transforms/TotalsHavingTransform.cpp index 083066a72d95..eb1cbf6e9d81 100644 --- a/src/Processors/Transforms/TotalsHavingTransform.cpp +++ b/src/Processors/Transforms/TotalsHavingTransform.cpp @@ -32,7 +32,7 @@ void finalizeChunk(Chunk & chunk) chunk.setColumns(std::move(columns), num_rows); } -static Block createOutputHeader(Block block, const ExpressionActionsPtr & expression, bool final) +Block TotalsHavingTransform::transformHeader(Block block, const ExpressionActionsPtr & expression, bool final) { if (final) finalizeBlock(block); @@ -51,7 +51,7 @@ TotalsHavingTransform::TotalsHavingTransform( TotalsMode totals_mode_, double auto_include_threshold_, bool final_) - : ISimpleTransform(header, createOutputHeader(header, expression_, final_), true) + : ISimpleTransform(header, transformHeader(header, expression_, final_), true) , overflow_row(overflow_row_) , expression(expression_) , filter_column_name(filter_column_) diff --git a/src/Processors/Transforms/TotalsHavingTransform.h b/src/Processors/Transforms/TotalsHavingTransform.h index f16b333ffd4e..8191ec763b09 100644 --- a/src/Processors/Transforms/TotalsHavingTransform.h +++ b/src/Processors/Transforms/TotalsHavingTransform.h @@ -37,6 +37,8 @@ class TotalsHavingTransform : public ISimpleTransform Status prepare() override; void work() override; + static Block transformHeader(Block block, const ExpressionActionsPtr & expression, bool final); + protected: void transform(Chunk & chunk) override; diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 537d81e9750a..b5be02082417 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -155,6 +155,7 @@ SRCS( QueryPlan/ReadFromPreparedSource.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp + QueryPlan/TotalsHavingStep QueryPlan/QueryPlan.cpp ) From 51a47560e0a980344fb34b0d06d99885a74948f9 Mon Sep 17 00:00:00 2001 From: alesapin Date: Wed, 17 Jun 2020 19:59:26 +0300 Subject: [PATCH 0378/1102] Fix segmentation fault --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index f601ca74112a..d832bcb7dc06 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1180,7 +1180,7 @@ void InterpreterSelectQuery::executeFetchColumns( = ext::map(required_columns_after_prewhere, [](const auto & it) { return it.name; }); } - auto syntax_result = SyntaxAnalyzer(*context).analyze(required_columns_all_expr, required_columns_after_prewhere, storage); + auto syntax_result = SyntaxAnalyzer(*context).analyze(required_columns_all_expr, required_columns_after_prewhere, storage, metadata_snapshot); alias_actions = ExpressionAnalyzer(required_columns_all_expr, syntax_result, *context).getActions(true); /// The set of required columns could be added as a result of adding an action to calculate ALIAS. From d41db3622fdf0c3abf64e89dd865f470e4c6c6c2 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 20:15:24 +0300 Subject: [PATCH 0379/1102] Add RollupStep and CubeStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 20 ++++++++------- src/Processors/QueryPlan/CubeStep.cpp | 27 +++++++++++++++++++++ src/Processors/QueryPlan/CubeStep.h | 24 ++++++++++++++++++ src/Processors/QueryPlan/DistinctStep.h | 2 +- src/Processors/QueryPlan/RollupStep.cpp | 27 +++++++++++++++++++++ src/Processors/QueryPlan/RollupStep.h | 24 ++++++++++++++++++ src/Processors/ya.make | 2 ++ 7 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 src/Processors/QueryPlan/CubeStep.cpp create mode 100644 src/Processors/QueryPlan/CubeStep.h create mode 100644 src/Processors/QueryPlan/RollupStep.cpp create mode 100644 src/Processors/QueryPlan/RollupStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 9452fbeaa43c..bec7d6fec185 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -90,6 +90,8 @@ #include #include #include +#include +#include namespace DB @@ -1540,16 +1542,16 @@ void InterpreterSelectQuery::executeRollupOrCube(QueryPipeline & pipeline, Modif auto transform_params = std::make_shared(params, true); - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + if (modificator == Modificator::ROLLUP) { - if (stream_type == QueryPipeline::StreamType::Totals) - return nullptr; - - if (modificator == Modificator::ROLLUP) - return std::make_shared(header, std::move(transform_params)); - else - return std::make_shared(header, std::move(transform_params)); - }); + RollupStep rollup_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); + rollup_step.transformPipeline(pipeline); + } + else + { + CubeStep rollup_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); + cube_step.transformPipeline(pipeline); + } } diff --git a/src/Processors/QueryPlan/CubeStep.cpp b/src/Processors/QueryPlan/CubeStep.cpp new file mode 100644 index 000000000000..3c8b2087fe0a --- /dev/null +++ b/src/Processors/QueryPlan/CubeStep.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace DB +{ + +CubeStep::CubeStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_) + : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + , params(std::move(params_)) +{ +} + +void CubeStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.resize(1); + + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type == QueryPipeline::StreamType::Totals) + return nullptr; + + return std::make_shared(header, std::move(params)); + }); +} + +} diff --git a/src/Processors/QueryPlan/CubeStep.h b/src/Processors/QueryPlan/CubeStep.h new file mode 100644 index 000000000000..c04f6a4f8544 --- /dev/null +++ b/src/Processors/QueryPlan/CubeStep.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +namespace DB +{ + +struct AggregatingTransformParams; +using AggregatingTransformParamsPtr = std::shared_ptr; + +class CubeStep : public ITransformingStep +{ +public: + CubeStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_); + + String getName() const override { return "Cube"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + AggregatingTransformParamsPtr params; +}; + +} diff --git a/src/Processors/QueryPlan/DistinctStep.h b/src/Processors/QueryPlan/DistinctStep.h index 638bd3debf75..a70c66b2a4f9 100644 --- a/src/Processors/QueryPlan/DistinctStep.h +++ b/src/Processors/QueryPlan/DistinctStep.h @@ -8,7 +8,7 @@ namespace DB class DistinctStep : public ITransformingStep { public: - explicit DistinctStep( + DistinctStep( const DataStream & input_stream_, const SizeLimits & set_size_limits_, UInt64 limit_hint_, diff --git a/src/Processors/QueryPlan/RollupStep.cpp b/src/Processors/QueryPlan/RollupStep.cpp new file mode 100644 index 000000000000..5045cb71cb20 --- /dev/null +++ b/src/Processors/QueryPlan/RollupStep.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace DB +{ + +RollupStep::RollupStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_) + : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + , params(std::move(params_)) +{ +} + +void RollupStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.resize(1); + + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type == QueryPipeline::StreamType::Totals) + return nullptr; + + return std::make_shared(header, std::move(params)); + }); +} + +} diff --git a/src/Processors/QueryPlan/RollupStep.h b/src/Processors/QueryPlan/RollupStep.h new file mode 100644 index 000000000000..56e8d81e37b9 --- /dev/null +++ b/src/Processors/QueryPlan/RollupStep.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include + +namespace DB +{ + +struct AggregatingTransformParams; +using AggregatingTransformParamsPtr = std::shared_ptr; + +class RollupStep : public ITransformingStep +{ +public: + RollupStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_); + + String getName() const override { return "Rollup"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + AggregatingTransformParamsPtr params; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index b5be02082417..8cf8e4954c2d 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -140,6 +140,7 @@ SRCS( QueryPlan/AddingDelayedStreamStep.cpp QueryPlan/AggregatingStep.cpp QueryPlan/CreatingSetsStep.cpp + QueryPlan/CubeStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp QueryPlan/FilterStep.cpp @@ -155,6 +156,7 @@ SRCS( QueryPlan/ReadFromPreparedSource.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp + QueryPlan/RollupStep.cpp QueryPlan/TotalsHavingStep QueryPlan/QueryPlan.cpp ) From 2cffa56914759d7429783775363c7a7e28ebb233 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 20:20:09 +0300 Subject: [PATCH 0380/1102] Add RollupStep and CubeStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index bec7d6fec185..30d36f54abb7 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1549,7 +1549,7 @@ void InterpreterSelectQuery::executeRollupOrCube(QueryPipeline & pipeline, Modif } else { - CubeStep rollup_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); + CubeStep cube_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); cube_step.transformPipeline(pipeline); } } From 84fd7fe51c929ffabb01a8185bc9838d6ebde978 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:02:46 +0300 Subject: [PATCH 0381/1102] Update having. --- src/Interpreters/InterpreterSelectQuery.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 30d36f54abb7..50d56124ed37 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1497,13 +1497,12 @@ void InterpreterSelectQuery::executeMergeAggregated(QueryPipeline & pipeline, bo void InterpreterSelectQuery::executeHaving(QueryPipeline & pipeline, const ExpressionActionsPtr & expression) { - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - bool on_totals = stream_type == QueryPipeline::StreamType::Totals; + FilterStep having_step( + DataStream{.header = pipeline.getHeader()}, + expression, getSelectQuery().having()->getColumnName(), false); - /// TODO: do we need to save filter there? - return std::make_shared(header, expression, getSelectQuery().having()->getColumnName(), false, on_totals); - }); + having_step.setStepDescription("HAVING"); + having_step.transformPipeline(pipeline); } From 7fcea660deb0a41a4edc3a81c102b2edf3978be3 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:08:30 +0300 Subject: [PATCH 0382/1102] Fix ya.make/ --- src/Processors/ya.make | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 8cf8e4954c2d..f1a45214da48 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -157,7 +157,7 @@ SRCS( QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp QueryPlan/RollupStep.cpp - QueryPlan/TotalsHavingStep + QueryPlan/TotalsHavingStep.cpp QueryPlan/QueryPlan.cpp ) From d795b2b03775d25caa54ea65707ed3529ad5e754 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:20:30 +0300 Subject: [PATCH 0383/1102] Add FillingStep --- src/Interpreters/InterpreterSelectQuery.cpp | 7 +++---- src/Processors/QueryPlan/FillingStep.cpp | 22 +++++++++++++++++++++ src/Processors/QueryPlan/FillingStep.h | 21 ++++++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/Processors/QueryPlan/FillingStep.cpp create mode 100644 src/Processors/QueryPlan/FillingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 50d56124ed37..1c49f6a61ab6 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -92,6 +92,7 @@ #include #include #include +#include namespace DB @@ -1788,10 +1789,8 @@ void InterpreterSelectQuery::executeWithFill(QueryPipeline & pipeline) if (fill_descr.empty()) return; - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, fill_descr); - }); + FillingStep filling_step(DataStream{.header = pipeline.getHeader()}, std::move(fill_descr)); + filling_step.transformPipeline(pipeline); } } diff --git a/src/Processors/QueryPlan/FillingStep.cpp b/src/Processors/QueryPlan/FillingStep.cpp new file mode 100644 index 000000000000..fec63b3322d1 --- /dev/null +++ b/src/Processors/QueryPlan/FillingStep.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +namespace DB +{ + +FillingStep::FillingStep(const DataStream & input_stream_, SortDescription sort_description_) + : ITransformingStep(input_stream_, input_stream_) + , sort_description(std::move(sort_description_)) +{ +} + +void FillingStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, sort_description); + }); +} + +} diff --git a/src/Processors/QueryPlan/FillingStep.h b/src/Processors/QueryPlan/FillingStep.h new file mode 100644 index 000000000000..e7ec7ab17d72 --- /dev/null +++ b/src/Processors/QueryPlan/FillingStep.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace DB +{ + +class FillingStep : public ITransformingStep +{ +public: + FillingStep(const DataStream & input_stream_, SortDescription sort_description_); + + String getName() const override { return "Filling"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SortDescription sort_description; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index f1a45214da48..8ce100f6bdc3 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -143,6 +143,7 @@ SRCS( QueryPlan/CubeStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp + QueryPlan/FillingStep.cpp QueryPlan/FilterStep.cpp QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp From 69ff3a8f7dbc459e6989695cb850b6c94b4853b1 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:30:36 +0300 Subject: [PATCH 0384/1102] Add ExtremesStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 11 ++++++----- src/Processors/QueryPlan/ExtremesStep.cpp | 15 +++++++++++++++ src/Processors/QueryPlan/ExtremesStep.h | 16 ++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/Processors/QueryPlan/ExtremesStep.cpp create mode 100644 src/Processors/QueryPlan/ExtremesStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 1c49f6a61ab6..f2e3dc443f09 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -93,6 +93,7 @@ #include #include #include +#include namespace DB @@ -1676,10 +1677,9 @@ void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const void InterpreterSelectQuery::executeProjection(QueryPipeline & pipeline, const ExpressionActionsPtr & expression) { - pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr - { - return std::make_shared(header, expression); - }); + ExpressionStep projection_step(DataStream{.header = pipeline.getHeader()}, expression); + projection_step.setStepDescription("Projection"); + projection_step.transformPipeline(pipeline); } @@ -1866,7 +1866,8 @@ void InterpreterSelectQuery::executeExtremes(QueryPipeline & pipeline) if (!context->getSettingsRef().extremes) return; - pipeline.addExtremesTransform(); + ExtremesStep extremes_step(DataStream{.header = pipeline.getHeader()}); + extremes_step.transformPipeline(pipeline); } void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const SubqueriesForSets & subqueries_for_sets) diff --git a/src/Processors/QueryPlan/ExtremesStep.cpp b/src/Processors/QueryPlan/ExtremesStep.cpp new file mode 100644 index 000000000000..91cd0bddc7c3 --- /dev/null +++ b/src/Processors/QueryPlan/ExtremesStep.cpp @@ -0,0 +1,15 @@ +#include +#include + +namespace DB +{ + +ExtremesStep::ExtremesStep(const DataStream & input_stream_) : ITransformingStep(input_stream_, input_stream_) {} + + +void ExtremesStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addExtremesTransform(); +} + +} diff --git a/src/Processors/QueryPlan/ExtremesStep.h b/src/Processors/QueryPlan/ExtremesStep.h new file mode 100644 index 000000000000..1a6910261961 --- /dev/null +++ b/src/Processors/QueryPlan/ExtremesStep.h @@ -0,0 +1,16 @@ +#pragma once +#include +namespace DB +{ + +class ExtremesStep : public ITransformingStep +{ +public: + ExtremesStep(const DataStream & input_stream_); + + String getName() const override { return "Extremes"; } + + void transformPipeline(QueryPipeline & pipeline) override; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 8ce100f6bdc3..82b74890b02f 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -143,6 +143,7 @@ SRCS( QueryPlan/CubeStep.cpp QueryPlan/DistinctStep.cpp QueryPlan/ExpressionStep.cpp + QueryPlan/ExtremesStep.cpp QueryPlan/FillingStep.cpp QueryPlan/FilterStep.cpp QueryPlan/ISourceStep.cpp From 7780a74bd8e095c1ff9bc09eff27bba5d3fa0680 Mon Sep 17 00:00:00 2001 From: Avogar Date: Wed, 17 Jun 2020 22:32:11 +0300 Subject: [PATCH 0385/1102] Update ORCBlockOutputFormat and add orc include directory in include path --- src/CMakeLists.txt | 6 + .../Formats/Impl/ORCBlockOutputFormat.cpp | 167 ++++++++++-------- .../Formats/Impl/ORCBlockOutputFormat.h | 31 ++-- .../01307_orc_output_format.reference | 12 +- .../0_stateless/01307_orc_output_format.sh | 4 +- 5 files changed, 117 insertions(+), 103 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fe223373cf38..aa779f011b7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -361,6 +361,12 @@ target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${DOUBLE_C target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${MSGPACK_INCLUDE_DIR}) +target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${ORC_INCLUDE_DIR}) +configure_file ( + "${ORC_INCLUDE_DIR}/orc/orc-config.hh.in" + "${ORC_INCLUDE_DIR}/orc/orc-config.hh" +) + if (ENABLE_TESTS AND USE_GTEST) macro (grep_gtest_sources BASE_DIR DST_VAR) # Cold match files that are not in tests/ directories diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp index 3745ee229a86..4bd9dd230fcd 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -136,14 +136,13 @@ template void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( orc::ColumnVectorBatch * orc_column, const IColumn & column, - const PaddedPODArray * null_bytemap, - size_t rows_num) + const PaddedPODArray * null_bytemap) { NumberVectorBatch * number_orc_column = dynamic_cast(orc_column); const auto & number_column = assert_cast &>(column); - number_orc_column->resize(rows_num); + number_orc_column->resize(number_column.size()); - for (size_t i = 0; i != rows_num; ++i) + for (size_t i = 0; i != number_column.size(); ++i) { if (null_bytemap && (*null_bytemap)[i]) { @@ -152,7 +151,7 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( } number_orc_column->data[i] = number_column.getElement(i); } - number_orc_column->numElements = rows_num; + number_orc_column->numElements = number_column.size(); } template @@ -161,7 +160,6 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeDecimals( const IColumn & column, DataTypePtr & type, const PaddedPODArray * null_bytemap, - size_t rows_num, ConvertFunc convert) { DecimalVectorBatch *decimal_orc_column = dynamic_cast(orc_column); @@ -169,8 +167,8 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeDecimals( const auto * decimal_type = typeid_cast *>(type.get()); decimal_orc_column->precision = decimal_type->getPrecision(); decimal_orc_column->scale = decimal_type->getScale(); - decimal_orc_column->resize(rows_num); - for (size_t i = 0; i != rows_num; ++i) + decimal_orc_column->resize(decimal_column.size()); + for (size_t i = 0; i != decimal_column.size(); ++i) { if (null_bytemap && (*null_bytemap)[i]) { @@ -179,134 +177,150 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeDecimals( } decimal_orc_column->values[i] = convert(decimal_column.getElement(i).value); } - decimal_orc_column->numElements = rows_num; + decimal_orc_column->numElements = decimal_column.size(); +} + +template +void ORCBlockOutputFormat::ORCBlockOutputFormat::writeStrings( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + const PaddedPODArray * null_bytemap) +{ + orc::StringVectorBatch * string_orc_column = dynamic_cast(orc_column); + const auto & string_column = assert_cast(column); + string_orc_column->resize(string_column.size()); + + for (size_t i = 0; i != string_column.size(); ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + string_orc_column->notNull[i] = 0; + continue; + } + const StringRef & string = string_column.getDataAt(i); + string_orc_column->data[i] = const_cast(string.data); + string_orc_column->length[i] = string.size; + } + string_orc_column->numElements = string_column.size(); +} + +template +void ORCBlockOutputFormat::ORCBlockOutputFormat::writeDateTimes( + orc::ColumnVectorBatch * orc_column, + const IColumn & column, + const PaddedPODArray * null_bytemap, + GetSecondsFunc get_seconds, + GetNanosecondsFunc get_nanoseconds) +{ + orc::TimestampVectorBatch * timestamp_orc_column = dynamic_cast(orc_column); + const auto & timestamp_column = assert_cast(column); + timestamp_orc_column->resize(timestamp_column.size()); + + for (size_t i = 0; i != timestamp_column.size(); ++i) + { + if (null_bytemap && (*null_bytemap)[i]) + { + timestamp_orc_column->notNull[i] = 0; + continue; + } + timestamp_orc_column->data[i] = get_seconds(timestamp_column.getElement(i)); + timestamp_orc_column->nanoseconds[i] = get_nanoseconds(timestamp_column.getElement(i)); + } + timestamp_orc_column->numElements = timestamp_column.size(); } void ORCBlockOutputFormat::writeColumn( orc::ColumnVectorBatch * orc_column, const IColumn & column, DataTypePtr & type, - const PaddedPODArray * null_bytemap, - size_t rows_num) + const PaddedPODArray * null_bytemap) { if (null_bytemap) { orc_column->hasNulls = true; + orc_column->notNull.resize(column.size()); } switch (type->getTypeId()) { case TypeIndex::Int8: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::UInt8: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Int16: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Date: [[fallthrough]]; case TypeIndex::UInt16: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Int32: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::UInt32: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Int64: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::UInt64: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Float32: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); break; } case TypeIndex::Float64: { - writeNumbers(orc_column, column, null_bytemap, rows_num); + writeNumbers(orc_column, column, null_bytemap); + break; + } + case TypeIndex::FixedString: + { + writeStrings(orc_column, column, null_bytemap); break; } - case TypeIndex::FixedString: [[fallthrough]]; case TypeIndex::String: { - orc::StringVectorBatch * string_orc_column = dynamic_cast(orc_column); - const auto & string_column = assert_cast(column); - string_orc_column->resize(rows_num); - - for (size_t i = 0; i != rows_num; ++i) - { - if (null_bytemap && (*null_bytemap)[i]) - { - string_orc_column->notNull[i] = 0; - continue; - } - const StringRef & string = string_column.getDataAt(i); - string_orc_column->data[i] = const_cast(string.data); - string_orc_column->length[i] = string.size; - } - string_orc_column->numElements = rows_num; + writeStrings(orc_column, column, null_bytemap); break; } case TypeIndex::DateTime: { - orc::TimestampVectorBatch * timestamp_orc_column = dynamic_cast(orc_column); - const auto & timestamp_column = assert_cast(column); - timestamp_orc_column->resize(rows_num); - - for (size_t i = 0; i != rows_num; ++i) - { - if (null_bytemap && (*null_bytemap)[i]) - { - timestamp_orc_column->notNull[i] = 0; - continue; - } - timestamp_orc_column->data[i] = timestamp_column.getElement(i); - timestamp_orc_column->nanoseconds[i] = 0; - } - timestamp_orc_column->numElements = rows_num; + writeDateTimes( + orc_column, + column, null_bytemap, + [](UInt32 value){ return value; }, + [](UInt32){ return 0; }); break; } case TypeIndex::DateTime64: { - orc::TimestampVectorBatch * timestamp_orc_column = dynamic_cast(orc_column); - const auto & timestamp_column = assert_cast(column); const auto * timestamp_type = assert_cast(type.get()); - UInt32 scale = timestamp_type->getScale(); - timestamp_orc_column->resize(rows_num); - - for (size_t i = 0; i != rows_num; ++i) - { - if (null_bytemap && (*null_bytemap)[i]) - { - timestamp_orc_column->notNull[i] = 0; - continue; - } - UInt64 value = timestamp_column.getElement(i); - timestamp_orc_column->data[i] = value / std::pow(10, scale); - timestamp_orc_column->nanoseconds[i] = (value % UInt64(std::pow(10, scale))) * std::pow(10, 9 - scale); - } - timestamp_orc_column->numElements = rows_num; + writeDateTimes( + orc_column, + column, null_bytemap, + [scale](UInt64 value){ return value / std::pow(10, scale); }, + [scale](UInt64 value){ return (value % UInt64(std::pow(10, scale))) * std::pow(10, 9 - scale); }); break; } case TypeIndex::Decimal32:; @@ -316,7 +330,6 @@ void ORCBlockOutputFormat::writeColumn( column, type, null_bytemap, - rows_num, [](Int32 value){ return value; }); break; } @@ -327,7 +340,6 @@ void ORCBlockOutputFormat::writeColumn( column, type, null_bytemap, - rows_num, [](Int64 value){ return value; }); break; } @@ -338,7 +350,6 @@ void ORCBlockOutputFormat::writeColumn( column, type, null_bytemap, - rows_num, [](Int128 value){ return orc::Int128(value >> 64, (value << 64) >> 64); }); break; } @@ -347,7 +358,7 @@ void ORCBlockOutputFormat::writeColumn( const auto & nullable_column = assert_cast(column); const PaddedPODArray & new_null_bytemap = assert_cast &>(*nullable_column.getNullMapColumnPtr()).getData(); auto nested_type = removeNullable(type); - writeColumn(orc_column, nullable_column.getNestedColumn(), nested_type, &new_null_bytemap, rows_num); + writeColumn(orc_column, nullable_column.getNestedColumn(), nested_type, &new_null_bytemap); break; } /* Doesn't work @@ -357,16 +368,16 @@ void ORCBlockOutputFormat::writeColumn( const auto & list_column = assert_cast(column); auto nested_type = assert_cast(*type).getNestedType(); const ColumnArray::Offsets & offsets = list_column.getOffsets(); - list_orc_column->resize(rows_num); + list_orc_column->resize(list_column.size()); list_orc_column->offsets[0] = 0; - for (size_t i = 0; i != rows_num; ++i) + for (size_t i = 0; i != list_column.size(); ++i) { list_orc_column->offsets[i + 1] = offsets[i]; } const IColumn & nested_column = list_column.getData(); orc::ColumnVectorBatch * nested_orc_column = list_orc_column->elements.get(); writeColumn(nested_orc_column, nested_column, nested_type, null_bytemap, nested_column.size()); - list_orc_column->numElements = rows_num; + list_orc_column->numElements = list_column.size(); break; } */ @@ -383,7 +394,7 @@ void ORCBlockOutputFormat::consume(Chunk chunk) orc::StructVectorBatch *root = dynamic_cast(batch.get()); for (size_t i = 0; i != columns_num; ++i) { - writeColumn(root->fields[i], *chunk.getColumns()[i], data_types[i], nullptr, rows_num); + writeColumn(root->fields[i], *chunk.getColumns()[i], data_types[i], nullptr); } root->numElements = rows_num; writer->add(*batch); diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h index e075169b66f9..18261a90acff 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h @@ -39,25 +39,22 @@ class ORCBlockOutputFormat : public IOutputFormat private: ORC_UNIQUE_PTR getORCType(const DataTypePtr & type); + template - void writeDecimals( - orc::ColumnVectorBatch * orc_column, - const IColumn & column, - DataTypePtr & type, - const PaddedPODArray * null_bytemap, - size_t rows_num, - ConvertFunc convert); + void writeDecimals(orc::ColumnVectorBatch * orc_column, const IColumn & column, DataTypePtr & type, + const PaddedPODArray * null_bytemap, ConvertFunc convert); + template - void writeNumbers( - orc::ColumnVectorBatch * orc_column, - const IColumn & column, - const PaddedPODArray * null_bytemap, - size_t rows_num); - void writeColumn( - orc::ColumnVectorBatch * orc_column, - const IColumn & column, DataTypePtr & type, - const PaddedPODArray * null_bytemap, - size_t rows_num); + void writeNumbers(orc::ColumnVectorBatch * orc_column, const IColumn & column, const PaddedPODArray * null_bytemap); + + template + void writeStrings(orc::ColumnVectorBatch * orc_column, const IColumn & column, const PaddedPODArray * null_bytemap); + + template + void writeDateTimes(orc::ColumnVectorBatch * orc_column, const IColumn & column, const PaddedPODArray * null_bytemap, + GetSecondsFunc get_seconds, GetNanosecondsFunc get_nanoseconds); + + void writeColumn(orc::ColumnVectorBatch * orc_column, const IColumn & column, DataTypePtr & type, const PaddedPODArray * null_bytemap); const FormatSettings format_settings; ORCOutputStream output_stream; diff --git a/tests/queries/0_stateless/01307_orc_output_format.reference b/tests/queries/0_stateless/01307_orc_output_format.reference index bd62476c2dfd..da719072eb26 100644 --- a/tests/queries/0_stateless/01307_orc_output_format.reference +++ b/tests/queries/0_stateless/01307_orc_output_format.reference @@ -1,6 +1,6 @@ -255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2021-12-19 2021-12-19 03:00:00 2021-12-19 03:00:00.000 1.0001 1.0000000100 100000.00000000000001000000 1 -4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2024-10-04 2028-04-21 01:20:00 2021-12-19 03:14:51.000 34.1234 123123.1231231230 123123123.12312312312312300000 \N -42 42 42 42 42 42 42 42 42.42 42.42 42 1970-02-12 1970-01-01 03:00:42 0000-00-00 00:00:00.000 42.4200 42.4242424200 424242.42424242424242000000 42 -255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2021-12-19 2021-12-19 03:00:00 2021-12-19 03:00:00.000 1.0001 1.0000000100 100000.00000000000001000000 1 -4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2024-10-04 2028-04-21 01:20:00 2021-12-19 03:14:51.123 34.1234 123123.1231231230 123123123.12312312312312300000 \N -42 42 42 42 42 42 42 42 42.42 42.42 42 1970-02-12 1970-01-01 03:00:42 1970-01-01 03:00:00.042 42.4200 42.4242424200 424242.42424242424242000000 42 +255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2020 2021-12-19 2021-12-19 03:00:00 1.0001 1.0000000100 100000.00000000000001000000 1 +4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2000 2024-10-04 2028-04-21 01:20:00 34.1234 123123.1231231230 123123123.12312312312312300000 \N +42 42 42 42 42 42 42 42 42.42 42.42 42 4242 1970-02-12 1970-01-01 03:00:42 42.4200 42.4242424200 424242.42424242424242000000 42 +255 65535 4294967295 100000000000 -128 -32768 -2147483648 -100000000000 2.02 10000.0000001 String 2020 2021-12-19 2021-12-19 03:00:00 1.0001 1.0000000100 100000.00000000000001000000 1 +4 1234 3244467295 500000000000 -1 -256 -14741221 -7000000000 100.1 14321.032141201 Another string 2000 2024-10-04 2028-04-21 01:20:00 34.1234 123123.1231231230 123123123.12312312312312300000 \N +42 42 42 42 42 42 42 42 42.42 42.42 42 4242 1970-02-12 1970-01-01 03:00:42 42.4200 42.4242424200 424242.42424242424242000000 42 diff --git a/tests/queries/0_stateless/01307_orc_output_format.sh b/tests/queries/0_stateless/01307_orc_output_format.sh index 8d7e85a03ded..c46131dcff64 100755 --- a/tests/queries/0_stateless/01307_orc_output_format.sh +++ b/tests/queries/0_stateless/01307_orc_output_format.sh @@ -5,9 +5,9 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) $CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS orc"; -$CLICKHOUSE_CLIENT --query="CREATE TABLE orc (uint8 UInt8, uint16 UInt16, uint32 UInt32, uint64 UInt64, int8 Int8, int16 Int16, int32 Int32, int64 Int64, float Float32, double Float64, string String, date Date, datetime DateTime, datetime64 DateTime64, decimal32 Decimal32(4), decimal64 Decimal64(10), decimal128 Decimal128(20), nullable Nullable(Int32)) ENGINE = Memory"; +$CLICKHOUSE_CLIENT --query="CREATE TABLE orc (uint8 UInt8, uint16 UInt16, uint32 UInt32, uint64 UInt64, int8 Int8, int16 Int16, int32 Int32, int64 Int64, float Float32, double Float64, string String, fixed FixedString(4), date Date, datetime DateTime, decimal32 Decimal32(4), decimal64 Decimal64(10), decimal128 Decimal128(20), nullable Nullable(Int32)) ENGINE = Memory"; -$CLICKHOUSE_CLIENT --query="INSERT INTO orc VALUES (255, 65535, 4294967295, 100000000000, -128, -32768, -2147483648, -100000000000, 2.02, 10000.0000001, 'String', 18980, 1639872000, 1639872000000, 1.0001, 1.00000001, 100000.00000000000001, 1), (4, 1234, 3244467295, 500000000000, -1, -256, -14741221, -7000000000, 100.1, 14321.032141201, 'Another string', 20000, 1839882000, 1639872891123, 34.1234, 123123.123123123, 123123123.123123123123123, NULL), (42, 42, 42, 42, 42, 42, 42, 42, 42.42, 42.42, '42', 42, 42, 42, 42.42, 42.42424242, 424242.42424242424242, 42)"; +$CLICKHOUSE_CLIENT --query="INSERT INTO orc VALUES (255, 65535, 4294967295, 100000000000, -128, -32768, -2147483648, -100000000000, 2.02, 10000.0000001, 'String', '2020', 18980, 1639872000, 1.0001, 1.00000001, 100000.00000000000001, 1), (4, 1234, 3244467295, 500000000000, -1, -256, -14741221, -7000000000, 100.1, 14321.032141201, 'Another string', '2000', 20000, 1839882000, 34.1234, 123123.123123123, 123123123.123123123123123, NULL), (42, 42, 42, 42, 42, 42, 42, 42, 42.42, 42.42, '42', '4242', 42, 42, 42.42, 42.42424242, 424242.42424242424242, 42)"; $CLICKHOUSE_CLIENT --query="SELECT * FROM orc FORMAT ORC" > $CURDIR/tmp_orc_test_all_types.orc; From 88b325dcdc373dd8c34d0479c7cc482b618da6fe Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Wed, 17 Jun 2020 22:36:27 +0300 Subject: [PATCH 0386/1102] rework distinct combinator --- .../AggregateFunctionAggThrow.cpp | 2 +- .../AggregateFunctionArgMinMax.h | 2 +- .../AggregateFunctionArray.h | 4 +- src/AggregateFunctions/AggregateFunctionAvg.h | 2 +- .../AggregateFunctionBitwise.h | 2 +- .../AggregateFunctionBoundingRatio.h | 2 +- ...egateFunctionCategoricalInformationValue.h | 4 +- .../AggregateFunctionCount.h | 4 +- .../AggregateFunctionDistinct.h | 67 +++++++------------ .../AggregateFunctionEntropy.h | 2 +- .../AggregateFunctionForEach.h | 4 +- .../AggregateFunctionGroupArray.h | 6 +- .../AggregateFunctionGroupArrayInsertAt.h | 2 +- .../AggregateFunctionGroupArrayMoving.h | 2 +- .../AggregateFunctionGroupBitmap.h | 4 +- .../AggregateFunctionGroupUniqArray.h | 5 +- .../AggregateFunctionHistogram.h | 2 +- src/AggregateFunctions/AggregateFunctionIf.h | 4 +- .../AggregateFunctionMLMethod.h | 2 +- .../AggregateFunctionMaxIntersections.h | 2 +- .../AggregateFunctionMerge.h | 4 +- .../AggregateFunctionMinMaxAny.h | 2 +- .../AggregateFunctionNothing.h | 2 +- .../AggregateFunctionNull.h | 6 +- .../AggregateFunctionOrFill.h | 9 +-- .../AggregateFunctionQuantile.h | 2 +- .../AggregateFunctionResample.h | 5 +- .../AggregateFunctionRetention.h | 2 +- .../AggregateFunctionSequenceMatch.h | 4 +- .../AggregateFunctionSimpleLinearRegression.h | 4 +- .../AggregateFunctionState.h | 2 +- .../AggregateFunctionStatistics.h | 4 +- .../AggregateFunctionStatisticsSimple.h | 2 +- src/AggregateFunctions/AggregateFunctionSum.h | 2 +- .../AggregateFunctionSumMap.h | 2 +- .../AggregateFunctionTimeSeriesGroupSum.h | 2 +- .../AggregateFunctionTopK.h | 4 +- .../AggregateFunctionUniq.h | 4 +- .../AggregateFunctionUniqCombined.h | 4 +- .../AggregateFunctionUniqUpTo.h | 4 +- .../AggregateFunctionWindowFunnel.h | 2 +- src/AggregateFunctions/IAggregateFunction.h | 2 +- src/Columns/ColumnAggregateFunction.cpp | 2 +- src/Functions/array/arrayReduce.cpp | 2 +- src/Functions/array/arrayReduceInRanges.cpp | 2 +- src/Functions/runningAccumulate.cpp | 2 +- src/Interpreters/Aggregator.cpp | 28 +++++--- src/Interpreters/Aggregator.h | 7 +- .../Algorithms/AggregatingSortedAlgorithm.cpp | 2 +- .../GraphiteRollupSortedAlgorithm.cpp | 2 +- .../Algorithms/SummingSortedAlgorithm.cpp | 2 +- .../01259_combinator_distinct.reference | 4 +- .../0_stateless/01259_combinator_distinct.sql | 9 ++- 53 files changed, 128 insertions(+), 132 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionAggThrow.cpp b/src/AggregateFunctions/AggregateFunctionAggThrow.cpp index ea3eb9b1a20c..fada039e20a0 100644 --- a/src/AggregateFunctions/AggregateFunctionAggThrow.cpp +++ b/src/AggregateFunctions/AggregateFunctionAggThrow.cpp @@ -93,7 +93,7 @@ class AggregateFunctionThrow final : public IAggregateFunctionDataHelperdata(place).result.insertResultInto(to); } diff --git a/src/AggregateFunctions/AggregateFunctionArray.h b/src/AggregateFunctions/AggregateFunctionArray.h index 4fe5e459ae19..24b070107075 100644 --- a/src/AggregateFunctions/AggregateFunctionArray.h +++ b/src/AggregateFunctions/AggregateFunctionArray.h @@ -119,9 +119,9 @@ class AggregateFunctionArray final : public IAggregateFunctionHelperdeserialize(place, buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - nested_func->insertResultInto(place, to); + nested_func->insertResultInto(place, to, arena); } bool allocatesMemoryInArena() const override diff --git a/src/AggregateFunctions/AggregateFunctionAvg.h b/src/AggregateFunctions/AggregateFunctionAvg.h index d9ef8647b821..1f3426160cb2 100644 --- a/src/AggregateFunctions/AggregateFunctionAvg.h +++ b/src/AggregateFunctions/AggregateFunctionAvg.h @@ -80,7 +80,7 @@ class AggregateFunctionAvgBase : public IAggregateFunctionDataHelperdata(place).denominator, buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & column = static_cast(to); column.getData().push_back(this->data(place).template result()); diff --git a/src/AggregateFunctions/AggregateFunctionBitwise.h b/src/AggregateFunctions/AggregateFunctionBitwise.h index a4e5f7ddafa4..6d9eb3c36e16 100644 --- a/src/AggregateFunctions/AggregateFunctionBitwise.h +++ b/src/AggregateFunctions/AggregateFunctionBitwise.h @@ -74,7 +74,7 @@ class AggregateFunctionBitwise final : public IAggregateFunctionDataHelperdata(place).value, buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast &>(to).getData().push_back(this->data(place).value); } diff --git a/src/AggregateFunctions/AggregateFunctionBoundingRatio.h b/src/AggregateFunctions/AggregateFunctionBoundingRatio.h index 81846db4bac0..9ceb7976f4a3 100644 --- a/src/AggregateFunctions/AggregateFunctionBoundingRatio.h +++ b/src/AggregateFunctions/AggregateFunctionBoundingRatio.h @@ -150,7 +150,7 @@ class AggregateFunctionBoundingRatio final : public IAggregateFunctionDataHelper data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(getBoundingRatio(data(place))); } diff --git a/src/AggregateFunctions/AggregateFunctionCategoricalInformationValue.h b/src/AggregateFunctions/AggregateFunctionCategoricalInformationValue.h index 1c397c26631f..aa205a71c978 100644 --- a/src/AggregateFunctions/AggregateFunctionCategoricalInformationValue.h +++ b/src/AggregateFunctions/AggregateFunctionCategoricalInformationValue.h @@ -119,8 +119,8 @@ class AggregateFunctionCategoricalIV final : public IAggregateFunctionHelper(to); auto & data_col = static_cast(col.getData()); diff --git a/src/AggregateFunctions/AggregateFunctionCount.h b/src/AggregateFunctions/AggregateFunctionCount.h index e54f014f7a46..51040bdcfad4 100644 --- a/src/AggregateFunctions/AggregateFunctionCount.h +++ b/src/AggregateFunctions/AggregateFunctionCount.h @@ -57,7 +57,7 @@ class AggregateFunctionCount final : public IAggregateFunctionDataHelper(to).getData().push_back(data(place).count); } @@ -114,7 +114,7 @@ class AggregateFunctionCountNotNullUnary final : public IAggregateFunctionDataHe readVarUInt(data(place).count, buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(data(place).count); } diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 72099a33cfd3..5c663bb64411 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -32,7 +32,6 @@ class AggregateFunctionDistinctBase : public IAggregateFunctionDataHelper, AggregateFunctionDistinctSingleNumericImpl>(nested, arguments) {} - void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena *) const override { const auto & vec = assert_cast &>(*columns[0]).getData(); - if (this->data(place).value.insert(vec[row_num]).second) - this->nested_func->add(this->getNestedPlace(place), columns, row_num, arena); + this->data(place).value.insert(vec[row_num]); } - void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena *) const override { - auto & cur_set = this->data(place).value; - auto & rhs_set = this->data(rhs).value; - - auto arguments = this->argument_types[0]->createColumn(); - for (auto & elem : rhs_set) - if (cur_set.insert(elem.getValue()).second) - arguments->insert(elem.getValue()); - - const auto * arguments_ptr = arguments.get(); - if (!arguments->empty()) - this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); + this->data(place).value.merge(this->data(rhs).value); } void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { this->data(place).value.write(buf); - this->nested_func->serialize(this->getNestedPlace(place), buf); } - void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override + void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena *) const override { this->data(place).value.read(buf); - this->nested_func->deserialize(this->getNestedPlace(place), buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - this->nested_func->insertResultInto(this->getNestedPlace(place), to); + const auto & set = this->data(place).value; + auto arguments = this->argument_types[0]->createColumn(); + for (const auto & elem : set) + arguments->insert(elem.getValue()); + + const auto * arguments_ptr = arguments.get(); + this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); + this->nested_func->insertResultInto(this->getNestedPlace(place), to, arena); } }; @@ -170,38 +163,25 @@ class AggregateFunctionDistinctSingleGenericImpl final bool inserted; auto key_holder = getKeyHolder(*columns[0], row_num, *arena); set.emplace(key_holder, it, inserted); - if (inserted) - this->nested_func->add(this->getNestedPlace(place), columns, row_num, arena); } void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override { auto & cur_set = this->data(place).value; - auto & rhs_set = this->data(rhs).value; + const auto & rhs_set = this->data(rhs).value; Data::Set::LookupResult it; bool inserted; - auto arguments = this->argument_types[0]->createColumn(); - for (auto & elem : rhs_set) - { + for (const auto & elem : rhs_set) cur_set.emplace(ArenaKeyHolder{elem.getValue(), *arena}, it, inserted); - if (inserted) - deserializeAndInsert(elem.getValue(), *arguments); - } - - const auto * arguments_ptr = arguments.get(); - if (!arguments->empty()) - this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); } void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { - auto & set = this->data(place).value; + const auto & set = this->data(place).value; writeVarUInt(set.size(), buf); for (const auto & elem : set) writeStringBinary(elem.getValue(), buf); - - this->nested_func->serialize(this->getNestedPlace(place), buf); } void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override @@ -211,13 +191,18 @@ class AggregateFunctionDistinctSingleGenericImpl final readVarUInt(size, buf); for (size_t i = 0; i < size; ++i) set.insert(readStringBinaryInto(*arena, buf)); - - this->nested_func->deserialize(this->getNestedPlace(place), buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - this->nested_func->insertResultInto(this->getNestedPlace(place), to); + const auto & set = this->data(place).value; + auto arguments = this->argument_types[0]->createColumn(); + for (const auto & elem : set) + deserializeAndInsert(elem.getValue(), *arguments); + + const auto * arguments_ptr = arguments.get(); + this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); + this->nested_func->insertResultInto(this->getNestedPlace(place), to, arena); } }; diff --git a/src/AggregateFunctions/AggregateFunctionEntropy.h b/src/AggregateFunctions/AggregateFunctionEntropy.h index ff233a5ac93d..656aca43f606 100644 --- a/src/AggregateFunctions/AggregateFunctionEntropy.h +++ b/src/AggregateFunctions/AggregateFunctionEntropy.h @@ -132,7 +132,7 @@ class AggregateFunctionEntropy final : public IAggregateFunctionDataHelperdata(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & column = assert_cast &>(to); column.getData().push_back(this->data(place).get()); diff --git a/src/AggregateFunctions/AggregateFunctionForEach.h b/src/AggregateFunctions/AggregateFunctionForEach.h index 23a3487de476..19f2994d3f1b 100644 --- a/src/AggregateFunctions/AggregateFunctionForEach.h +++ b/src/AggregateFunctions/AggregateFunctionForEach.h @@ -225,7 +225,7 @@ class AggregateFunctionForEach final : public IAggregateFunctionDataHelperinsertResultInto(nested_state, elems_to); + nested_func->insertResultInto(nested_state, elems_to, arena); nested_state += nested_size_of_data; } diff --git a/src/AggregateFunctions/AggregateFunctionGroupArray.h b/src/AggregateFunctions/AggregateFunctionGroupArray.h index b76efd9f6c20..f3d31eb599bf 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArray.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArray.h @@ -282,7 +282,7 @@ class GroupArrayNumericImpl final // if constexpr (Trait::sampler == Sampler::DETERMINATOR) } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { const auto & value = this->data(place).value; size_t size = value.size(); @@ -600,7 +600,7 @@ class GroupArrayGeneralImpl final // if constexpr (Trait::sampler == Sampler::DETERMINATOR) } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & column_array = assert_cast(to); @@ -815,7 +815,7 @@ class GroupArrayGeneralListImpl final data(place).last = prev; } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & column_array = assert_cast(to); diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h b/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h index 0eec38c51a7e..d84c99aec57a 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayInsertAt.h @@ -179,7 +179,7 @@ class AggregateFunctionGroupArrayInsertAtGeneric final } } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { ColumnArray & to_array = assert_cast(to); IColumn & to_data = to_array.getData(); diff --git a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h index 8f93a7eb25a7..19562b37a12d 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h +++ b/src/AggregateFunctions/AggregateFunctionGroupArrayMoving.h @@ -158,7 +158,7 @@ class MovingImpl final this->data(place).sum = value.back(); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { const auto & data = this->data(place); size_t size = data.value.size(); diff --git a/src/AggregateFunctions/AggregateFunctionGroupBitmap.h b/src/AggregateFunctions/AggregateFunctionGroupBitmap.h index 766479cc08d2..a6470aa69433 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupBitmap.h +++ b/src/AggregateFunctions/AggregateFunctionGroupBitmap.h @@ -48,7 +48,7 @@ class AggregateFunctionBitmap final : public IAggregateFunctionDataHelperdata(place).rbs.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast &>(to).getData().push_back(this->data(place).rbs.size()); } @@ -113,7 +113,7 @@ class AggregateFunctionBitmapL2 final : public IAggregateFunctionDataHelperdata(place).rbs.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast &>(to).getData().push_back(this->data(place).rbs.size()); } diff --git a/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h b/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h index b6683567404c..2ee9d0f6e1ca 100644 --- a/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h +++ b/src/AggregateFunctions/AggregateFunctionGroupUniqArray.h @@ -98,7 +98,7 @@ class AggregateFunctionGroupUniqArray this->data(place).value.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { ColumnArray & arr_to = assert_cast(to); ColumnArray::Offsets & offsets_to = arr_to.getOffsets(); @@ -218,7 +218,7 @@ class AggregateFunctionGroupUniqArrayGeneric } } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { ColumnArray & arr_to = assert_cast(to); ColumnArray::Offsets & offsets_to = arr_to.getOffsets(); @@ -231,6 +231,7 @@ class AggregateFunctionGroupUniqArrayGeneric deserializeAndInsert(elem.getValue(), data_to); } }; + #undef AGGREGATE_FUNCTION_GROUP_ARRAY_UNIQ_MAX_SIZE } diff --git a/src/AggregateFunctions/AggregateFunctionHistogram.h b/src/AggregateFunctions/AggregateFunctionHistogram.h index 8eaa42fdac43..bc9c95ecf2aa 100644 --- a/src/AggregateFunctions/AggregateFunctionHistogram.h +++ b/src/AggregateFunctions/AggregateFunctionHistogram.h @@ -353,7 +353,7 @@ class AggregateFunctionHistogram final: public IAggregateFunctionDataHelperdata(place).read(buf, max_bins); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & data = this->data(place); diff --git a/src/AggregateFunctions/AggregateFunctionIf.h b/src/AggregateFunctions/AggregateFunctionIf.h index bf4f0b24de33..f04450c91427 100644 --- a/src/AggregateFunctions/AggregateFunctionIf.h +++ b/src/AggregateFunctions/AggregateFunctionIf.h @@ -95,9 +95,9 @@ class AggregateFunctionIf final : public IAggregateFunctionHelperdeserialize(place, buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - nested_func->insertResultInto(place, to); + nested_func->insertResultInto(place, to, arena); } bool allocatesMemoryInArena() const override diff --git a/src/AggregateFunctions/AggregateFunctionMLMethod.h b/src/AggregateFunctions/AggregateFunctionMLMethod.h index a11ca9032a57..8a93b66ab3be 100644 --- a/src/AggregateFunctions/AggregateFunctionMLMethod.h +++ b/src/AggregateFunctions/AggregateFunctionMLMethod.h @@ -388,7 +388,7 @@ class AggregateFunctionMLMethod final : public IAggregateFunctionDataHelperdata(place).returnWeights(to); } diff --git a/src/AggregateFunctions/AggregateFunctionMaxIntersections.h b/src/AggregateFunctions/AggregateFunctionMaxIntersections.h index 050c5fd78ea2..b8a4dd63eeae 100644 --- a/src/AggregateFunctions/AggregateFunctionMaxIntersections.h +++ b/src/AggregateFunctions/AggregateFunctionMaxIntersections.h @@ -129,7 +129,7 @@ class AggregateFunctionIntersectionsMax final buf.read(reinterpret_cast(value.data()), size * sizeof(value[0])); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { Int64 current_intersections = 0; Int64 max_intersections = 0; diff --git a/src/AggregateFunctions/AggregateFunctionMerge.h b/src/AggregateFunctions/AggregateFunctionMerge.h index 51a3c11118f5..066f7a762f8a 100644 --- a/src/AggregateFunctions/AggregateFunctionMerge.h +++ b/src/AggregateFunctions/AggregateFunctionMerge.h @@ -93,9 +93,9 @@ class AggregateFunctionMerge final : public IAggregateFunctionHelperdeserialize(place, buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - nested_func->insertResultInto(place, to); + nested_func->insertResultInto(place, to, arena); } bool allocatesMemoryInArena() const override diff --git a/src/AggregateFunctions/AggregateFunctionMinMaxAny.h b/src/AggregateFunctions/AggregateFunctionMinMaxAny.h index 69504f7b2493..a21a64af9a48 100644 --- a/src/AggregateFunctions/AggregateFunctionMinMaxAny.h +++ b/src/AggregateFunctions/AggregateFunctionMinMaxAny.h @@ -746,7 +746,7 @@ class AggregateFunctionsSingleValue final : public IAggregateFunctionDataHelper< return Data::allocatesMemoryInArena(); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { this->data(place).insertResultInto(to); } diff --git a/src/AggregateFunctions/AggregateFunctionNothing.h b/src/AggregateFunctions/AggregateFunctionNothing.h index 511dbbecd385..af90dfb5179f 100644 --- a/src/AggregateFunctions/AggregateFunctionNothing.h +++ b/src/AggregateFunctions/AggregateFunctionNothing.h @@ -67,7 +67,7 @@ class AggregateFunctionNothing final : public IAggregateFunctionHelper } } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { if constexpr (result_is_nullable) { ColumnNullable & to_concrete = assert_cast(to); if (getFlag(place)) { - nested_function->insertResultInto(nestedPlace(place), to_concrete.getNestedColumn()); + nested_function->insertResultInto(nestedPlace(place), to_concrete.getNestedColumn(), arena); to_concrete.getNullMapData().push_back(0); } else @@ -167,7 +167,7 @@ class AggregateFunctionNullBase : public IAggregateFunctionHelper } else { - nested_function->insertResultInto(nestedPlace(place), to); + nested_function->insertResultInto(nestedPlace(place), to, arena); } } diff --git a/src/AggregateFunctions/AggregateFunctionOrFill.h b/src/AggregateFunctions/AggregateFunctionOrFill.h index 1bbf2ea3135c..333f07d5e33f 100644 --- a/src/AggregateFunctions/AggregateFunctionOrFill.h +++ b/src/AggregateFunctions/AggregateFunctionOrFill.h @@ -148,7 +148,8 @@ class AggregateFunctionOrFill final : public IAggregateFunctionHelperinsertResultInto(place, to); + nested_function->insertResultInto(place, to, arena); else { ColumnNullable & col = typeid_cast(to); col.getNullMapColumn().insertDefault(); - nested_function->insertResultInto(place, col.getNestedColumn()); + nested_function->insertResultInto(place, col.getNestedColumn(), arena); } } else { // -OrDefault - nested_function->insertResultInto(place, to); + nested_function->insertResultInto(place, to, arena); } } else diff --git a/src/AggregateFunctions/AggregateFunctionQuantile.h b/src/AggregateFunctions/AggregateFunctionQuantile.h index 7bdfc13295cd..536d9d5683f2 100644 --- a/src/AggregateFunctions/AggregateFunctionQuantile.h +++ b/src/AggregateFunctions/AggregateFunctionQuantile.h @@ -138,7 +138,7 @@ class AggregateFunctionQuantile final : public IAggregateFunctionDataHelperdata(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { /// const_cast is required because some data structures apply finalizaton (like sorting) for obtain a result. auto & data = this->data(place); diff --git a/src/AggregateFunctions/AggregateFunctionResample.h b/src/AggregateFunctions/AggregateFunctionResample.h index 49cc312287ee..043e094a688e 100644 --- a/src/AggregateFunctions/AggregateFunctionResample.h +++ b/src/AggregateFunctions/AggregateFunctionResample.h @@ -174,13 +174,14 @@ class AggregateFunctionResample final : public IAggregateFunctionHelper(to); auto & col_offsets = assert_cast(col.getOffsetsColumn()); for (size_t i = 0; i < total; ++i) - nested_function->insertResultInto(place + i * size_of_data, col.getData()); + nested_function->insertResultInto(place + i * size_of_data, col.getData(), arena); col_offsets.getData().push_back(col.getData().size()); } diff --git a/src/AggregateFunctions/AggregateFunctionRetention.h b/src/AggregateFunctions/AggregateFunctionRetention.h index 3a76ba9f0554..b742dcdf77fd 100644 --- a/src/AggregateFunctions/AggregateFunctionRetention.h +++ b/src/AggregateFunctions/AggregateFunctionRetention.h @@ -123,7 +123,7 @@ class AggregateFunctionRetention final this->data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & data_to = assert_cast(assert_cast(to).getData()).getData(); auto & offsets_to = assert_cast(to).getOffsets(); diff --git a/src/AggregateFunctions/AggregateFunctionSequenceMatch.h b/src/AggregateFunctions/AggregateFunctionSequenceMatch.h index 416786f8fcbc..79463e890e4b 100644 --- a/src/AggregateFunctions/AggregateFunctionSequenceMatch.h +++ b/src/AggregateFunctions/AggregateFunctionSequenceMatch.h @@ -560,7 +560,7 @@ class AggregateFunctionSequenceMatch final : public AggregateFunctionSequenceBas DataTypePtr getReturnType() const override { return std::make_shared(); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { this->data(place).sort(); @@ -588,7 +588,7 @@ class AggregateFunctionSequenceCount final : public AggregateFunctionSequenceBas DataTypePtr getReturnType() const override { return std::make_shared(); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { const_cast(this->data(place)).sort(); assert_cast(to).getData().push_back(count(place)); diff --git a/src/AggregateFunctions/AggregateFunctionSimpleLinearRegression.h b/src/AggregateFunctions/AggregateFunctionSimpleLinearRegression.h index d1405172e270..8c029855a265 100644 --- a/src/AggregateFunctions/AggregateFunctionSimpleLinearRegression.h +++ b/src/AggregateFunctions/AggregateFunctionSimpleLinearRegression.h @@ -170,8 +170,8 @@ class AggregateFunctionSimpleLinearRegression final : public IAggregateFunctionD void insertResultInto( AggregateDataPtr place, - IColumn & to - ) const override + IColumn & to, + Arena *) const override { Ret k = this->data(place).getK(); Ret b = this->data(place).getB(k); diff --git a/src/AggregateFunctions/AggregateFunctionState.h b/src/AggregateFunctions/AggregateFunctionState.h index 126d63573afc..51a316777235 100644 --- a/src/AggregateFunctions/AggregateFunctionState.h +++ b/src/AggregateFunctions/AggregateFunctionState.h @@ -80,7 +80,7 @@ class AggregateFunctionState final : public IAggregateFunctionHelperdeserialize(place, buf, arena); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(place); } diff --git a/src/AggregateFunctions/AggregateFunctionStatistics.h b/src/AggregateFunctions/AggregateFunctionStatistics.h index 7f6de43f5e14..b0ff57665da1 100644 --- a/src/AggregateFunctions/AggregateFunctionStatistics.h +++ b/src/AggregateFunctions/AggregateFunctionStatistics.h @@ -143,7 +143,7 @@ class AggregateFunctionVariance final this->data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { this->data(place).publish(to); } @@ -395,7 +395,7 @@ class AggregateFunctionCovariance final this->data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { this->data(place).publish(to); } diff --git a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h index 96c07cc3d41b..7962453cb352 100644 --- a/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h +++ b/src/AggregateFunctions/AggregateFunctionStatisticsSimple.h @@ -455,7 +455,7 @@ class AggregateFunctionVarianceSimple final this->data(place).read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { const auto & data = this->data(place); auto & dst = static_cast(to).getData(); diff --git a/src/AggregateFunctions/AggregateFunctionSum.h b/src/AggregateFunctions/AggregateFunctionSum.h index 9d3d559ecee8..6f921dbb78ba 100644 --- a/src/AggregateFunctions/AggregateFunctionSum.h +++ b/src/AggregateFunctions/AggregateFunctionSum.h @@ -305,7 +305,7 @@ class AggregateFunctionSum final : public IAggregateFunctionDataHelperdata(place).read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { auto & column = static_cast(to); column.getData().push_back(this->data(place).get()); diff --git a/src/AggregateFunctions/AggregateFunctionSumMap.h b/src/AggregateFunctions/AggregateFunctionSumMap.h index e2aef611955a..8209170791e0 100644 --- a/src/AggregateFunctions/AggregateFunctionSumMap.h +++ b/src/AggregateFunctions/AggregateFunctionSumMap.h @@ -242,7 +242,7 @@ class AggregateFunctionSumMapBase : public IAggregateFunctionDataHelper< } } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { // Final step does compaction of keys that have zero values, this mutates the state auto & merged_maps = this->data(place).merged_maps; diff --git a/src/AggregateFunctions/AggregateFunctionTimeSeriesGroupSum.h b/src/AggregateFunctions/AggregateFunctionTimeSeriesGroupSum.h index ad83324e4832..3ec40455cf3d 100644 --- a/src/AggregateFunctions/AggregateFunctionTimeSeriesGroupSum.h +++ b/src/AggregateFunctions/AggregateFunctionTimeSeriesGroupSum.h @@ -253,7 +253,7 @@ class AggregateFunctionTimeSeriesGroupSum final void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena *) const override { this->data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { const auto & value = this->data(place).result; size_t size = value.size(); diff --git a/src/AggregateFunctions/AggregateFunctionTopK.h b/src/AggregateFunctions/AggregateFunctionTopK.h index 23eb0e7ff09c..68317d0bdf0f 100644 --- a/src/AggregateFunctions/AggregateFunctionTopK.h +++ b/src/AggregateFunctions/AggregateFunctionTopK.h @@ -79,7 +79,7 @@ class AggregateFunctionTopK set.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { ColumnArray & arr_to = assert_cast(to); ColumnArray::Offsets & offsets_to = arr_to.getOffsets(); @@ -200,7 +200,7 @@ class AggregateFunctionTopKGeneric : public IAggregateFunctionDataHelperdata(place).value.merge(this->data(rhs).value); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { ColumnArray & arr_to = assert_cast(to); ColumnArray::Offsets & offsets_to = arr_to.getOffsets(); diff --git a/src/AggregateFunctions/AggregateFunctionUniq.h b/src/AggregateFunctions/AggregateFunctionUniq.h index 1588611b8a24..fe0e96f036b4 100644 --- a/src/AggregateFunctions/AggregateFunctionUniq.h +++ b/src/AggregateFunctions/AggregateFunctionUniq.h @@ -240,7 +240,7 @@ class AggregateFunctionUniq final : public IAggregateFunctionDataHelperdata(place).set.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).set.size()); } @@ -300,7 +300,7 @@ class AggregateFunctionUniqVariadic final : public IAggregateFunctionDataHelper< this->data(place).set.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).set.size()); } diff --git a/src/AggregateFunctions/AggregateFunctionUniqCombined.h b/src/AggregateFunctions/AggregateFunctionUniqCombined.h index a92caa4a551b..e34cc602ccd3 100644 --- a/src/AggregateFunctions/AggregateFunctionUniqCombined.h +++ b/src/AggregateFunctions/AggregateFunctionUniqCombined.h @@ -167,7 +167,7 @@ class AggregateFunctionUniqCombined final this->data(place).set.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).set.size()); } @@ -229,7 +229,7 @@ class AggregateFunctionUniqCombinedVariadic final : public IAggregateFunctionDat this->data(place).set.read(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).set.size()); } diff --git a/src/AggregateFunctions/AggregateFunctionUniqUpTo.h b/src/AggregateFunctions/AggregateFunctionUniqUpTo.h index 4c71215141c7..2a48e0fb182f 100644 --- a/src/AggregateFunctions/AggregateFunctionUniqUpTo.h +++ b/src/AggregateFunctions/AggregateFunctionUniqUpTo.h @@ -180,7 +180,7 @@ class AggregateFunctionUniqUpTo final : public IAggregateFunctionDataHelperdata(place).read(buf, threshold); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).size()); } @@ -242,7 +242,7 @@ class AggregateFunctionUniqUpToVariadic final this->data(place).read(buf, threshold); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(this->data(place).size()); } diff --git a/src/AggregateFunctions/AggregateFunctionWindowFunnel.h b/src/AggregateFunctions/AggregateFunctionWindowFunnel.h index b5704203aded..3f41046c20e4 100644 --- a/src/AggregateFunctions/AggregateFunctionWindowFunnel.h +++ b/src/AggregateFunctions/AggregateFunctionWindowFunnel.h @@ -280,7 +280,7 @@ class AggregateFunctionWindowFunnel final this->data(place).deserialize(buf); } - void insertResultInto(AggregateDataPtr place, IColumn & to) const override + void insertResultInto(AggregateDataPtr place, IColumn & to, Arena *) const override { assert_cast(to).getData().push_back(getEventLevel(this->data(place))); } diff --git a/src/AggregateFunctions/IAggregateFunction.h b/src/AggregateFunctions/IAggregateFunction.h index 439a5e07c2e1..32dcce908c63 100644 --- a/src/AggregateFunctions/IAggregateFunction.h +++ b/src/AggregateFunctions/IAggregateFunction.h @@ -106,7 +106,7 @@ class IAggregateFunction /// Inserts results into a column. /// This method must be called once, from single thread. /// After this method was called for state, you can't do anything with state but destroy. - virtual void insertResultInto(AggregateDataPtr place, IColumn & to) const = 0; + virtual void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const = 0; /// Used for machine learning methods. Predict result from trained model. /// Will insert result into `to` column for rows in range [offset, offset + limit). diff --git a/src/Columns/ColumnAggregateFunction.cpp b/src/Columns/ColumnAggregateFunction.cpp index d4021b45f0e9..3374d171059f 100644 --- a/src/Columns/ColumnAggregateFunction.cpp +++ b/src/Columns/ColumnAggregateFunction.cpp @@ -135,7 +135,7 @@ MutableColumnPtr ColumnAggregateFunction::convertToValues(MutableColumnPtr colum res->reserve(data.size()); for (auto * val : data) - func->insertResultInto(val, *res); + func->insertResultInto(val, *res, &column_aggregate_func.createOrGetArena()); return res; } diff --git a/src/Functions/array/arrayReduce.cpp b/src/Functions/array/arrayReduce.cpp index 8d44acc82f5d..5c9c9472e98d 100644 --- a/src/Functions/array/arrayReduce.cpp +++ b/src/Functions/array/arrayReduce.cpp @@ -187,7 +187,7 @@ void FunctionArrayReduce::executeImpl(Block & block, const ColumnNumbers & argum for (size_t i = 0; i < input_rows_count; ++i) if (!res_col_aggregate_function) - agg_func.insertResultInto(places[i], res_col); + agg_func.insertResultInto(places[i], res_col, arena.get()); else res_col_aggregate_function->insertFrom(places[i]); block.getByPosition(result).column = std::move(result_holder); diff --git a/src/Functions/array/arrayReduceInRanges.cpp b/src/Functions/array/arrayReduceInRanges.cpp index 2dd0cd563432..5b594fdb6218 100644 --- a/src/Functions/array/arrayReduceInRanges.cpp +++ b/src/Functions/array/arrayReduceInRanges.cpp @@ -376,7 +376,7 @@ void FunctionArrayReduceInRanges::executeImpl(Block & block, const ColumnNumbers } if (!res_col_aggregate_function) - agg_func.insertResultInto(place, result_data); + agg_func.insertResultInto(place, result_data, arena.get()); else res_col_aggregate_function->insertFrom(place); } diff --git a/src/Functions/runningAccumulate.cpp b/src/Functions/runningAccumulate.cpp index 275259e12095..bf109654bc2d 100644 --- a/src/Functions/runningAccumulate.cpp +++ b/src/Functions/runningAccumulate.cpp @@ -124,7 +124,7 @@ class FunctionRunningAccumulate : public IFunction } agg_func.merge(place.data(), state_to_add, arena.get()); - agg_func.insertResultInto(place.data(), result_column); + agg_func.insertResultInto(place.data(), result_column, arena.get()); ++row_number; } diff --git a/src/Interpreters/Aggregator.cpp b/src/Interpreters/Aggregator.cpp index 538a24fa9976..5bd427b42cd8 100644 --- a/src/Interpreters/Aggregator.cpp +++ b/src/Interpreters/Aggregator.cpp @@ -822,10 +822,11 @@ Block Aggregator::convertOneBucketToBlock( MutableColumns & key_columns, AggregateColumnsData & aggregate_columns, MutableColumns & final_aggregate_columns, + Arena * arena, bool final_) { convertToBlockImpl(method, method.data.impls[bucket], - key_columns, aggregate_columns, final_aggregate_columns, final_); + key_columns, aggregate_columns, final_aggregate_columns, arena, final_); }); block.info.bucket_num = bucket; @@ -983,6 +984,7 @@ void Aggregator::convertToBlockImpl( MutableColumns & key_columns, AggregateColumnsData & aggregate_columns, MutableColumns & final_aggregate_columns, + Arena * arena, bool final) const { if (data.empty()) @@ -992,7 +994,7 @@ void Aggregator::convertToBlockImpl( throw Exception{"Aggregate. Unexpected key columns size.", ErrorCodes::LOGICAL_ERROR}; if (final) - convertToBlockImplFinal(method, data, key_columns, final_aggregate_columns); + convertToBlockImplFinal(method, data, key_columns, final_aggregate_columns, arena); else convertToBlockImplNotFinal(method, data, key_columns, aggregate_columns); /// In order to release memory early. @@ -1003,7 +1005,8 @@ void Aggregator::convertToBlockImpl( template inline void Aggregator::insertAggregatesIntoColumns( Mapped & mapped, - MutableColumns & final_aggregate_columns) const + MutableColumns & final_aggregate_columns, + Arena * arena) const { /** Final values of aggregate functions are inserted to columns. * Then states of aggregate functions, that are not longer needed, are destroyed. @@ -1034,7 +1037,8 @@ inline void Aggregator::insertAggregatesIntoColumns( for (; insert_i < params.aggregates_size; ++insert_i) aggregate_functions[insert_i]->insertResultInto( mapped + offsets_of_aggregate_states[insert_i], - *final_aggregate_columns[insert_i]); + *final_aggregate_columns[insert_i], + arena); } catch (...) { @@ -1071,21 +1075,22 @@ void NO_INLINE Aggregator::convertToBlockImplFinal( Method & method, Table & data, MutableColumns & key_columns, - MutableColumns & final_aggregate_columns) const + MutableColumns & final_aggregate_columns, + Arena * arena) const { if constexpr (Method::low_cardinality_optimization) { if (data.hasNullKeyData()) { key_columns[0]->insertDefault(); - insertAggregatesIntoColumns(data.getNullKeyData(), final_aggregate_columns); + insertAggregatesIntoColumns(data.getNullKeyData(), final_aggregate_columns, arena); } } data.forEachValue([&](const auto & key, auto & mapped) { method.insertKeyIntoColumns(key, key_columns, key_sizes); - insertAggregatesIntoColumns(mapped, final_aggregate_columns); + insertAggregatesIntoColumns(mapped, final_aggregate_columns, arena); }); } @@ -1174,7 +1179,7 @@ Block Aggregator::prepareBlockAndFill( } } - filler(key_columns, aggregate_columns_data, final_aggregate_columns, final); + filler(key_columns, aggregate_columns_data, final_aggregate_columns, data_variants.aggregates_pool, final); Block res = header.cloneEmpty(); @@ -1198,6 +1203,7 @@ Block Aggregator::prepareBlockAndFill( return res; } + void Aggregator::fillAggregateColumnsWithSingleKey( AggregatedDataVariants & data_variants, MutableColumns & final_aggregate_columns) @@ -1240,6 +1246,7 @@ Block Aggregator::prepareBlockAndFillWithoutKey(AggregatedDataVariants & data_va MutableColumns & key_columns, AggregateColumnsData & aggregate_columns, MutableColumns & final_aggregate_columns, + Arena * arena, bool final_) { if (data_variants.type == AggregatedDataVariants::Type::without_key || params.overflow_row) @@ -1254,7 +1261,7 @@ Block Aggregator::prepareBlockAndFillWithoutKey(AggregatedDataVariants & data_va } else { - insertAggregatesIntoColumns(data, final_aggregate_columns); + insertAggregatesIntoColumns(data, final_aggregate_columns, arena); } if (params.overflow_row) @@ -1282,12 +1289,13 @@ Block Aggregator::prepareBlockAndFillSingleLevel(AggregatedDataVariants & data_v MutableColumns & key_columns, AggregateColumnsData & aggregate_columns, MutableColumns & final_aggregate_columns, + Arena * arena, bool final_) { #define M(NAME) \ else if (data_variants.type == AggregatedDataVariants::Type::NAME) \ convertToBlockImpl(*data_variants.NAME, data_variants.NAME->data, \ - key_columns, aggregate_columns, final_aggregate_columns, final_); + key_columns, aggregate_columns, final_aggregate_columns, arena, final_); if (false) {} // NOLINT APPLY_FOR_VARIANTS_SINGLE_LEVEL(M) diff --git a/src/Interpreters/Aggregator.h b/src/Interpreters/Aggregator.h index 6d0eeee90143..6c55cb88781c 100644 --- a/src/Interpreters/Aggregator.h +++ b/src/Interpreters/Aggregator.h @@ -1164,19 +1164,22 @@ class Aggregator MutableColumns & key_columns, AggregateColumnsData & aggregate_columns, MutableColumns & final_aggregate_columns, + Arena * arena, bool final) const; template void insertAggregatesIntoColumns( Mapped & mapped, - MutableColumns & final_aggregate_columns) const; + MutableColumns & final_aggregate_columns, + Arena * arena) const; template void convertToBlockImplFinal( Method & method, Table & data, MutableColumns & key_columns, - MutableColumns & final_aggregate_columns) const; + MutableColumns & final_aggregate_columns, + Arena * arena) const; template void convertToBlockImplNotFinal( diff --git a/src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp b/src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp index be9bf3e354ce..3214ca0b4cc3 100644 --- a/src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp +++ b/src/Processors/Merges/Algorithms/AggregatingSortedAlgorithm.cpp @@ -223,7 +223,7 @@ void AggregatingSortedAlgorithm::AggregatingMergedData::finishGroup() /// Write the simple aggregation result for the current group. for (auto & desc : def.columns_to_simple_aggregate) { - desc.function->insertResultInto(desc.state.data(), *desc.column); + desc.function->insertResultInto(desc.state.data(), *desc.column, arena.get()); desc.destroyState(); } diff --git a/src/Processors/Merges/Algorithms/GraphiteRollupSortedAlgorithm.cpp b/src/Processors/Merges/Algorithms/GraphiteRollupSortedAlgorithm.cpp index f26fe96876fe..02e0746f09d9 100644 --- a/src/Processors/Merges/Algorithms/GraphiteRollupSortedAlgorithm.cpp +++ b/src/Processors/Merges/Algorithms/GraphiteRollupSortedAlgorithm.cpp @@ -301,7 +301,7 @@ void GraphiteRollupSortedAlgorithm::GraphiteRollupMergedData::insertRow( const Graphite::AggregationPattern * aggregation_pattern = std::get<1>(current_rule); if (aggregate_state_created) { - aggregation_pattern->function->insertResultInto(place_for_aggregate_state.data(), *value_column); + aggregation_pattern->function->insertResultInto(place_for_aggregate_state.data(), *value_column, nullptr); aggregation_pattern->function->destroy(place_for_aggregate_state.data()); aggregate_state_created = false; } diff --git a/src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp b/src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp index 89154044ae52..7d58c22702e6 100644 --- a/src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp +++ b/src/Processors/Merges/Algorithms/SummingSortedAlgorithm.cpp @@ -497,7 +497,7 @@ void SummingSortedAlgorithm::SummingMergedData::finishGroup() { try { - desc.function->insertResultInto(desc.state.data(), *desc.merged_column); + desc.function->insertResultInto(desc.state.data(), *desc.merged_column, nullptr); /// Update zero status of current row if (desc.column_numbers.size() == 1) diff --git a/tests/queries/0_stateless/01259_combinator_distinct.reference b/tests/queries/0_stateless/01259_combinator_distinct.reference index 83756ffdaa4a..281250dedb69 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.reference +++ b/tests/queries/0_stateless/01259_combinator_distinct.reference @@ -1,6 +1,4 @@ -499500 +4999950000 78 [0,1,2,3,4,5,6,7,8,9,10,11,12] -[0,1,2,3,4,5,6,7,8,9,10,11,12] 20 -5.669227916063075e-17 diff --git a/tests/queries/0_stateless/01259_combinator_distinct.sql b/tests/queries/0_stateless/01259_combinator_distinct.sql index adfddeb34e42..1fef2f170086 100644 --- a/tests/queries/0_stateless/01259_combinator_distinct.sql +++ b/tests/queries/0_stateless/01259_combinator_distinct.sql @@ -1,6 +1,5 @@ -SELECT sum(DISTINCT x) FROM (SELECT number AS x FROM system.numbers_mt LIMIT 100000); -SELECT sum(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); -SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); -SELECT groupArray(DISTINCT x) FROM (SELECT number % 13 AS x FROM system.numbers_mt LIMIT 100000); -SELECT finalizeAggregation(countState(DISTINCT toString(number % 20))) FROM numbers_mt (100000); +SELECT sum(DISTINCT number) FROM numbers_mt(100000); +SELECT sum(DISTINCT number % 13) FROM numbers_mt(100000); +SELECT arraySort(groupArray(DISTINCT number % 13)) FROM numbers_mt(100000); +SELECT finalizeAggregation(countState(DISTINCT toString(number % 20))) FROM numbers_mt(100000); -- SELECT corrStableDistinct(DISTINCT x, y) FROM (SELECT number % 11 AS x, number % 13 AS y FROM system.numbers LIMIT 1000); From e8049d34c5198bedbe9bbc0d205d0df0cddf3d87 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:38:45 +0300 Subject: [PATCH 0387/1102] Add OffsetsStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 9 +++----- src/Processors/QueryPlan/OffsetsStep.cpp | 25 +++++++++++++++++++++ src/Processors/QueryPlan/OffsetsStep.h | 21 +++++++++++++++++ src/Processors/ya.make | 1 + 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 src/Processors/QueryPlan/OffsetsStep.cpp create mode 100644 src/Processors/QueryPlan/OffsetsStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index f2e3dc443f09..035dba3f46ac 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -94,6 +94,7 @@ #include #include #include +#include namespace DB @@ -1852,12 +1853,8 @@ void InterpreterSelectQuery::executeOffset(QueryPipeline & pipeline) UInt64 limit_offset; std::tie(limit_length, limit_offset) = getLimitLengthAndOffset(query, *context); - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type != QueryPipeline::StreamType::Main) - return nullptr; - return std::make_shared(header, limit_offset, 1); - }); + OffsetsStep offsets_step(DataStream{.header = pipeline.getHeader()}, limit_offset); + offsets_step.transformPipeline(pipeline); } } diff --git a/src/Processors/QueryPlan/OffsetsStep.cpp b/src/Processors/QueryPlan/OffsetsStep.cpp new file mode 100644 index 000000000000..04a23953b5f7 --- /dev/null +++ b/src/Processors/QueryPlan/OffsetsStep.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +namespace DB +{ + +OffsetsStep::OffsetsStep(const DataStream & input_stream_, size_t offsets_) + : ITransformingStep(input_stream_, input_stream_) + , offset(offsets_) +{ +} + +void OffsetsStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, offset, 1); + }); +} + +} diff --git a/src/Processors/QueryPlan/OffsetsStep.h b/src/Processors/QueryPlan/OffsetsStep.h new file mode 100644 index 000000000000..83f0c43dd7dc --- /dev/null +++ b/src/Processors/QueryPlan/OffsetsStep.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +namespace DB +{ + +class OffsetsStep : public ITransformingStep +{ +public: + OffsetsStep(const DataStream & input_stream_, size_t offset_); + + String getName() const override { return "Offsets"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + size_t offset; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 82b74890b02f..711c1738df1f 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -154,6 +154,7 @@ SRCS( QueryPlan/MergeSortingStep.cpp QueryPlan/MergingAggregatedStep.cpp QueryPlan/MergingSortedStep.cpp + QueryPlan/OffsetsStep.cpp QueryPlan/PartialSortingStep.cpp QueryPlan/ReadFromPreparedSource.cpp QueryPlan/ReadFromStorageStep.cpp From 169ad5e8055c62cc35f6f138f2d8b61487ab8eb5 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 22:57:13 +0300 Subject: [PATCH 0388/1102] Add FinishSortingStep. --- src/Interpreters/InterpreterSelectQuery.cpp | 41 +++---------- .../QueryPlan/FinishSortingStep.cpp | 61 +++++++++++++++++++ src/Processors/QueryPlan/FinishSortingStep.h | 29 +++++++++ src/Processors/ya.make | 1 + 4 files changed, 99 insertions(+), 33 deletions(-) create mode 100644 src/Processors/QueryPlan/FinishSortingStep.cpp create mode 100644 src/Processors/QueryPlan/FinishSortingStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 035dba3f46ac..870c454f530d 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -95,6 +95,7 @@ #include #include #include +#include namespace DB @@ -1572,40 +1573,14 @@ void InterpreterSelectQuery::executeOrderOptimized(QueryPipeline & pipeline, Inp { const Settings & settings = context->getSettingsRef(); - bool need_finish_sorting = (input_sorting_info->order_key_prefix_descr.size() < output_order_descr.size()); - if (pipeline.getNumStreams() > 1) - { - UInt64 limit_for_merging = (need_finish_sorting ? 0 : limit); - auto transform = std::make_shared( - pipeline.getHeader(), - pipeline.getNumStreams(), - input_sorting_info->order_key_prefix_descr, - settings.max_block_size, limit_for_merging); - - pipeline.addPipe({ std::move(transform) }); - } - - pipeline.enableQuotaForCurrentStreams(); - - if (need_finish_sorting) - { - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type != QueryPipeline::StreamType::Main) - return nullptr; - - return std::make_shared(header, output_order_descr, limit); - }); - - /// NOTE limits are not applied to the size of temporary sets in FinishSortingTransform + FinishSortingStep finish_sorting_step( + DataStream{.header = pipeline.getHeader()}, + input_sorting_info->order_key_prefix_descr, + output_order_descr, + settings.max_block_size, + limit); - pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr - { - return std::make_shared( - header, input_sorting_info->order_key_prefix_descr, - output_order_descr, settings.max_block_size, limit); - }); - } + finish_sorting_step.transformPipeline(pipeline); } void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr input_sorting_info) diff --git a/src/Processors/QueryPlan/FinishSortingStep.cpp b/src/Processors/QueryPlan/FinishSortingStep.cpp new file mode 100644 index 000000000000..87a28d910402 --- /dev/null +++ b/src/Processors/QueryPlan/FinishSortingStep.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +FinishSortingStep::FinishSortingStep( + const DataStream & input_stream_, + SortDescription prefix_description_, + SortDescription result_description_, + size_t max_block_size_, + UInt64 limit_) + : ITransformingStep(input_stream_, input_stream_) + , prefix_description(std::move(prefix_description_)) + , result_description(std::move(result_description_)) + , max_block_size(max_block_size_) + , limit(limit_) +{ +} + +void FinishSortingStep::transformPipeline(QueryPipeline & pipeline) +{ + bool need_finish_sorting = (prefix_description.size() < result_description.size()); + if (pipeline.getNumStreams() > 1) + { + UInt64 limit_for_merging = (need_finish_sorting ? 0 : limit); + auto transform = std::make_shared( + pipeline.getHeader(), + pipeline.getNumStreams(), + prefix_description, + max_block_size, limit_for_merging); + + pipeline.addPipe({ std::move(transform) }); + } + + pipeline.enableQuotaForCurrentStreams(); + + if (need_finish_sorting) + { + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, result_description, limit); + }); + + /// NOTE limits are not applied to the size of temporary sets in FinishSortingTransform + pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr + { + return std::make_shared( + header, prefix_description, result_description, max_block_size, limit); + }); + } +} + +} diff --git a/src/Processors/QueryPlan/FinishSortingStep.h b/src/Processors/QueryPlan/FinishSortingStep.h new file mode 100644 index 000000000000..43bdf261e970 --- /dev/null +++ b/src/Processors/QueryPlan/FinishSortingStep.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace DB +{ + +class FinishSortingStep : public ITransformingStep +{ +public: + FinishSortingStep( + const DataStream & input_stream_, + SortDescription prefix_description_, + SortDescription result_description_, + size_t max_block_size, + UInt64 limit); + + String getName() const override { return "FinishSorting"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + SortDescription prefix_description; + SortDescription result_description; + size_t max_block_size; + UInt64 limit; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 711c1738df1f..07328af754b5 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -146,6 +146,7 @@ SRCS( QueryPlan/ExtremesStep.cpp QueryPlan/FillingStep.cpp QueryPlan/FilterStep.cpp + QueryPlan/FinishSortingStep.cpp QueryPlan/ISourceStep.cpp QueryPlan/ITransformingStep.cpp QueryPlan/IQueryPlanStep.cpp From 9ad00187e3286fa3c0c04f720981181b842dbbfd Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Wed, 17 Jun 2020 23:19:55 +0300 Subject: [PATCH 0389/1102] Update InterpreterSelectQuery.. --- src/Interpreters/InterpreterSelectQuery.cpp | 75 +++++++------------- src/Processors/QueryPlan/AggregatingStep.cpp | 2 +- 2 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 870c454f530d..99113907a20a 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1,6 +1,9 @@ -#include #include #include +#include + +#include + #include #include #include @@ -27,53 +30,11 @@ #include #include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include #include -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include #include #include @@ -97,6 +58,24 @@ #include #include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + namespace DB { @@ -1398,10 +1377,8 @@ void InterpreterSelectQuery::executeWhere(QueryPipeline & pipeline, const Expres void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info) { - pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, expression); - }); + ExpressionStep expression_before_aggregation(DataStream{.header = pipeline.getHeader()}, expression); + expression_before_aggregation.transformPipeline(pipeline); Block header_before_aggregation = pipeline.getHeader(); ColumnNumbers keys; @@ -1490,7 +1467,7 @@ void InterpreterSelectQuery::executeMergeAggregated(QueryPipeline & pipeline, bo MergingAggregatedStep merging_aggregated( DataStream{.header = pipeline.getHeader()}, - transform_params, + std::move(transform_params), settings.distributed_aggregation_memory_efficient, settings.max_threads, settings.aggregation_memory_efficient_merge_threads); diff --git a/src/Processors/QueryPlan/AggregatingStep.cpp b/src/Processors/QueryPlan/AggregatingStep.cpp index 7649d0085c34..11fb5a143643 100644 --- a/src/Processors/QueryPlan/AggregatingStep.cpp +++ b/src/Processors/QueryPlan/AggregatingStep.cpp @@ -16,7 +16,7 @@ AggregatingStep::AggregatingStep( bool storage_has_evenly_distributed_read_, InputOrderInfoPtr group_by_info_, SortDescription group_by_sort_description_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, DataStream{.header = transform_params_->getHeader()}) , transform_params(std::move(transform_params_)) , max_block_size(max_block_size_) , merge_threads(merge_threads_) From fb7f4c6369f52673378bd56cd3690ae256f03ea7 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Thu, 18 Jun 2020 02:42:40 +0300 Subject: [PATCH 0390/1102] fix build --- src/AggregateFunctions/AggregateFunctionDistinct.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index 1661277d525b..c77e977b0faf 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -28,7 +28,10 @@ class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombi } AggregateFunctionPtr transformAggregateFunction( - const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array &) const override + const AggregateFunctionPtr & nested_function, + const AggregateFunctionProperties &, + const DataTypes & arguments, + const Array &) const override { AggregateFunctionPtr res; if (arguments.size() == 1) From 4fd3bcd8232758ade3d2c353aedb184324b14387 Mon Sep 17 00:00:00 2001 From: Avogar Date: Thu, 18 Jun 2020 03:17:08 +0300 Subject: [PATCH 0391/1102] Fix build errors --- src/Processors/Formats/Impl/ORCBlockOutputFormat.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h index 18261a90acff..0cd50959ad7d 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h @@ -19,11 +19,12 @@ class ORCOutputStream : public orc::OutputStream uint64_t getNaturalWriteSize() const override; void write(const void* buf, size_t length) override; - void close() override {}; - const std::string& getName() const override { return "ORCOutputStream"; }; + void close() override {} + const std::string& getName() const override { return name; } private: WriteBuffer & out; + std::string name = "ORCOutputStream"; }; class ORCBlockOutputFormat : public IOutputFormat @@ -35,8 +36,6 @@ class ORCBlockOutputFormat : public IOutputFormat void consume(Chunk chunk) override; void finalize() override; - String getContentType() const override { return "application/octet-stream"; } - private: ORC_UNIQUE_PTR getORCType(const DataTypePtr & type); From f4037b8f54840ca0b449e86e0bb575de9c01e1be Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 12:00:43 +0300 Subject: [PATCH 0392/1102] Fix build --- src/Storages/StorageReplicatedMergeTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 9f8859385d31..0e9a2bf7ec4b 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -743,7 +743,7 @@ void StorageReplicatedMergeTree::checkTableStructure(const String & zookeeper_pr Coordination::Stat metadata_stat; String metadata_str = zookeeper->get(zookeeper_prefix + "/metadata", &metadata_stat); auto metadata_from_zk = ReplicatedMergeTreeTableMetadata::parse(metadata_str); - old_metadata.checkEquals(metadata_from_zk, getColumns(), global_context); + old_metadata.checkEquals(metadata_from_zk, metadata_snapshot->getColumns(), global_context); Coordination::Stat columns_stat; auto columns_from_zk = ColumnsDescription::parse(zookeeper->get(zookeeper_prefix + "/columns", &columns_stat)); From 760e9a8488f0a5ee24a4ccce141593195c3ffcef Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 12:08:24 +0300 Subject: [PATCH 0393/1102] Fix crash --- src/Storages/StorageDistributed.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index f853a6a86735..53342c754ed7 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -215,9 +215,14 @@ class ReplacingConstantExpressionsMatcher } }; -void replaceConstantExpressions(ASTPtr & node, const Context & context, const NamesAndTypesList & columns, ConstStoragePtr storage) +void replaceConstantExpressions( + ASTPtr & node, + const Context & context, + const NamesAndTypesList & columns, + ConstStoragePtr storage, + const StorageMetadataPtr & metadata_snapshot) { - auto syntax_result = SyntaxAnalyzer(context).analyze(node, columns, storage); + auto syntax_result = SyntaxAnalyzer(context).analyze(node, columns, storage, metadata_snapshot); Block block_with_constants = KeyCondition::getBlockWithConstants(node, syntax_result, context); InDepthNodeVisitor visitor(block_with_constants); @@ -777,7 +782,7 @@ ClusterPtr StorageDistributed::skipUnusedShards( condition_ast = select.prewhere() ? select.prewhere()->clone() : select.where()->clone(); } - replaceConstantExpressions(condition_ast, context, metadata_snapshot->getColumns().getAll(), shared_from_this()); + replaceConstantExpressions(condition_ast, context, metadata_snapshot->getColumns().getAll(), shared_from_this(), metadata_snapshot); const auto blocks = evaluateExpressionOverConstantCondition(condition_ast, sharding_key_expr); // Can't get definite answer if we can skip any shards From 21d9c7fcc894bda6b0af549586c9452e47b82b0f Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 12:22:29 +0300 Subject: [PATCH 0394/1102] Fix build. --- src/Processors/QueryPlan/OffsetsStep.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Processors/QueryPlan/OffsetsStep.cpp b/src/Processors/QueryPlan/OffsetsStep.cpp index 04a23953b5f7..b43bc9f51d06 100644 --- a/src/Processors/QueryPlan/OffsetsStep.cpp +++ b/src/Processors/QueryPlan/OffsetsStep.cpp @@ -5,9 +5,9 @@ namespace DB { -OffsetsStep::OffsetsStep(const DataStream & input_stream_, size_t offsets_) +OffsetsStep::OffsetsStep(const DataStream & input_stream_, size_t offset_) : ITransformingStep(input_stream_, input_stream_) - , offset(offsets_) + , offset(offset_) { } From 35ce47951d7e7f2d9490b97af16899933af4285e Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 12:22:54 +0300 Subject: [PATCH 0395/1102] Fix storage merge --- src/Storages/StorageMerge.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 228cec993571..235f78505e06 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -170,9 +170,7 @@ Pipes StorageMerge::read( if (selected_tables.empty()) /// FIXME: do we support sampling in this case? return createSources( - metadata_snapshot, query_info, processed_stage, - max_block_size, header, {}, real_column_names, - modified_context, 0, has_table_virtual_column); + {}, query_info, processed_stage, max_block_size, header, {}, real_column_names, modified_context, 0, has_table_virtual_column); size_t tables_count = selected_tables.size(); Float64 num_streams_multiplier = std::min(unsigned(tables_count), std::max(1U, unsigned(context.getSettingsRef().max_streams_multiplier_for_merge_tables))); @@ -212,8 +210,10 @@ Pipes StorageMerge::read( if (query_info.query->as()->sampleSize() && !storage->supportsSampling()) throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED); + auto storage_metadata_snapshot = storage->getInMemoryMetadataPtr(); + auto source_pipes = createSources( - metadata_snapshot, query_info, processed_stage, + storage_metadata_snapshot, query_info, processed_stage, max_block_size, header, table, real_column_names, modified_context, current_streams, has_table_virtual_column); @@ -267,6 +267,7 @@ Pipes StorageMerge::createSources( if (real_column_names.empty()) real_column_names.push_back(ExpressionActions::getSmallestColumn(metadata_snapshot->getColumns().getAllPhysical())); + pipes = storage->read(real_column_names, metadata_snapshot, modified_query_info, *modified_context, processed_stage, max_block_size, UInt32(streams_num)); } else if (processed_stage > storage_stage) From aab4ce6394c9bd72622f29f749e5bb5f56a6851a Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 13:29:13 +0300 Subject: [PATCH 0396/1102] Truncate with metadata --- src/Interpreters/InterpreterDropQuery.cpp | 6 ++++-- src/Storages/IStorage.h | 6 +++++- src/Storages/StorageDistributed.cpp | 2 +- src/Storages/StorageDistributed.h | 2 +- src/Storages/StorageFile.cpp | 6 +++++- src/Storages/StorageFile.h | 6 +++++- src/Storages/StorageJoin.cpp | 5 ++--- src/Storages/StorageJoin.h | 2 +- src/Storages/StorageLog.cpp | 3 +-- src/Storages/StorageLog.h | 2 +- src/Storages/StorageMaterializedView.cpp | 2 +- src/Storages/StorageMaterializedView.h | 2 +- src/Storages/StorageMemory.cpp | 3 ++- src/Storages/StorageMemory.h | 4 ++-- src/Storages/StorageMergeTree.cpp | 2 +- src/Storages/StorageMergeTree.h | 2 +- src/Storages/StorageReplicatedMergeTree.cpp | 3 ++- src/Storages/StorageReplicatedMergeTree.h | 2 +- src/Storages/StorageSet.cpp | 3 +-- src/Storages/StorageSet.h | 2 +- src/Storages/StorageStripeLog.cpp | 2 +- src/Storages/StorageStripeLog.h | 2 +- src/Storages/StorageTinyLog.cpp | 4 ++-- src/Storages/StorageTinyLog.h | 2 +- 24 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/Interpreters/InterpreterDropQuery.cpp b/src/Interpreters/InterpreterDropQuery.cpp index 5ffce2fc3ec2..15f19b585dec 100644 --- a/src/Interpreters/InterpreterDropQuery.cpp +++ b/src/Interpreters/InterpreterDropQuery.cpp @@ -105,8 +105,9 @@ BlockIO InterpreterDropQuery::executeToTable( table->checkTableCanBeDropped(); auto table_lock = table->lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); /// Drop table data, don't touch metadata - table->truncate(query_ptr, context, table_lock); + table->truncate(query_ptr, metadata_snapshot, context, table_lock); } else if (query.kind == ASTDropQuery::Kind::Drop) { @@ -187,7 +188,8 @@ BlockIO InterpreterDropQuery::executeToTemporaryTable(const String & table_name, { auto table_lock = table->lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); /// Drop table data, don't touch metadata - table->truncate(query_ptr, context, table_lock); + auto metadata_snapshot = table->getInMemoryMetadataPtr(); + table->truncate(query_ptr, metadata_snapshot, context, table_lock); } else if (kind == ASTDropQuery::Kind::Drop) { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index ba1945d5c79e..ec13e26ff430 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -292,7 +292,11 @@ class IStorage : public std::enable_shared_from_this, public TypePromo /** Clear the table data and leave it empty. * Must be called under lockForAlter. */ - virtual void truncate(const ASTPtr & /*query*/, const Context & /* context */, TableStructureWriteLockHolder &) + virtual void truncate( + const ASTPtr & /*query*/, + const StorageMetadataPtr & /* metadata_snapshot */, + const Context & /* context */, + TableStructureWriteLockHolder &) { throw Exception("Truncate is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 53342c754ed7..2e07a393b046 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -619,7 +619,7 @@ Strings StorageDistributed::getDataPaths() const return paths; } -void StorageDistributed::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageDistributed::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) { std::lock_guard lock(cluster_nodes_mutex); diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index aeb5f1875e95..c952ccde8acd 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -82,7 +82,7 @@ class StorageDistributed final : public ext::shared_ptr_helper, public ISt const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; - void truncate(const ASTPtr & /*query*/, const Context & /* context */, TableStructureWriteLockHolder &) override; + void truncate( + const ASTPtr & /*query*/, + const StorageMetadataPtr & /* metadata_snapshot */, + const Context & /* context */, + TableStructureWriteLockHolder &) override; void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 300ab400a46d..5000dcd8b186 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -64,10 +64,9 @@ StorageJoin::StorageJoin( } -void StorageJoin::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageJoin::truncate( + const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) { - /// TODO(alesap) FIXME - auto metadata_snapshot = getInMemoryMetadataPtr(); Poco::File(path).remove(true); Poco::File(path).createDirectories(); Poco::File(path + "tmp/").createDirectories(); diff --git a/src/Storages/StorageJoin.h b/src/Storages/StorageJoin.h index 40dbf1b44dd9..4d4d1a81da24 100644 --- a/src/Storages/StorageJoin.h +++ b/src/Storages/StorageJoin.h @@ -27,7 +27,7 @@ class StorageJoin final : public ext::shared_ptr_helper, public Sto public: String getName() const override { return "Join"; } - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; /// Access the innards. HashJoinPtr & getJoin() { return join; } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index e0953283a17d..45d55938db38 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -535,11 +535,10 @@ void StorageLog::rename(const String & new_path_to_table_data, const StorageID & renameInMemory(new_table_id); } -void StorageLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageLog::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) { std::shared_lock lock(rwlock); - auto metadata_snapshot = getInMemoryMetadataPtr(); files.clear(); file_count = 0; loaded_marks = false; diff --git a/src/Storages/StorageLog.h b/src/Storages/StorageLog.h index 90d0799e1a8d..670e2777d44c 100644 --- a/src/Storages/StorageLog.h +++ b/src/Storages/StorageLog.h @@ -39,7 +39,7 @@ class StorageLog final : public ext::shared_ptr_helper, public IStor CheckResults checkData(const ASTPtr & /* query */, const Context & /* context */) override; - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 3d3137fe1a68..2c0d5727b310 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -173,7 +173,7 @@ void StorageMaterializedView::drop() executeDropQuery(ASTDropQuery::Kind::Drop, global_context, target_table_id); } -void StorageMaterializedView::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageMaterializedView::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) { if (has_inner_table) executeDropQuery(ASTDropQuery::Kind::Truncate, global_context, target_table_id); diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index 6f462c2ccccf..e2111a15f5c4 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -37,7 +37,7 @@ class StorageMaterializedView final : public ext::shared_ptr_helper; size_t max_block_size, unsigned num_streams) override; - BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) override; + BlockOutputStreamPtr write(const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, const Context & context) override; void drop() override; - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; std::optional totalRows() const override; std::optional totalBytes() const override; diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 40b3aeffb8a0..45f8ecf0ef92 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -231,7 +231,7 @@ void StorageMergeTree::drop() dropAllData(); } -void StorageMergeTree::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageMergeTree::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) { { /// Asks to complete merges and does not allow them to start. diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index 69ee6714164f..cf3eccc0c0b4 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -75,7 +75,7 @@ class StorageMergeTree final : public ext::shared_ptr_helper, CancellationCode killMutation(const String & mutation_id) override; void drop() override; - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; void alter(const AlterCommands & commands, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 0e9a2bf7ec4b..2e64c54112f6 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -4011,7 +4011,8 @@ void StorageReplicatedMergeTree::dropPartition(const ASTPtr & query, const ASTPt } -void StorageReplicatedMergeTree::truncate(const ASTPtr & query, const Context & query_context, TableStructureWriteLockHolder & table_lock) +void StorageReplicatedMergeTree::truncate( + const ASTPtr & query, const StorageMetadataPtr &, const Context & query_context, TableStructureWriteLockHolder & table_lock) { table_lock.release(); /// Truncate is done asynchronously. diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index c98fcb0ae3d8..c1ba737d849d 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -120,7 +120,7 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helperfinishInsert(); } size_t StorageSet::getSize() const { return set->getTotalRowCount(); } -void StorageSet::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageSet::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) { - auto metadata_snapshot = getInMemoryMetadataPtr(); Poco::File(path).remove(true); Poco::File(path).createDirectories(); Poco::File(path + "tmp/").createDirectories(); diff --git a/src/Storages/StorageSet.h b/src/Storages/StorageSet.h index b7785aadc6a2..2685fa26ba66 100644 --- a/src/Storages/StorageSet.h +++ b/src/Storages/StorageSet.h @@ -67,7 +67,7 @@ friend struct ext::shared_ptr_helper; /// Access the insides. SetPtr & getSet() { return set; } - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; private: SetPtr set; diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index 407c9b164ffc..4b95a389f2c2 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -326,7 +326,7 @@ CheckResults StorageStripeLog::checkData(const ASTPtr & /* query */, const Conte return file_checker.check(); } -void StorageStripeLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageStripeLog::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) { std::shared_lock lock(rwlock); diff --git a/src/Storages/StorageStripeLog.h b/src/Storages/StorageStripeLog.h index d06758a60e82..381be7762df3 100644 --- a/src/Storages/StorageStripeLog.h +++ b/src/Storages/StorageStripeLog.h @@ -42,7 +42,7 @@ class StorageStripeLog final : public ext::shared_ptr_helper, Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; protected: StorageStripeLog( diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 4578a82f6503..4beb44405d79 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -429,10 +429,10 @@ CheckResults StorageTinyLog::checkData(const ASTPtr & /* query */, const Context return file_checker.check(); } -void StorageTinyLog::truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageTinyLog::truncate( + const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) { std::unique_lock lock(rwlock); - auto metadata_snapshot = getInMemoryMetadataPtr(); disk->clearDirectory(table_path); diff --git a/src/Storages/StorageTinyLog.h b/src/Storages/StorageTinyLog.h index a55bf6d0dcf9..ae124e5e9580 100644 --- a/src/Storages/StorageTinyLog.h +++ b/src/Storages/StorageTinyLog.h @@ -41,7 +41,7 @@ class StorageTinyLog final : public ext::shared_ptr_helper, publ Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } - void truncate(const ASTPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; void drop() override; From 4de5331b0df17163e05fe11893ea4c2da35ad985 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 13:39:33 +0300 Subject: [PATCH 0397/1102] Fix SystemTables --- src/Storages/System/StorageSystemTables.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index 84635acb887a..df8df75ad6d9 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -267,7 +267,6 @@ class TablesBlockSource : public SourceWithProgress throw; } } - auto metadata_snapshot = table->getInMemoryMetadataPtr(); ++rows_count; @@ -362,10 +361,14 @@ class TablesBlockSource : public SourceWithProgress else src_index += 2; + StorageMetadataPtr metadata_snapshot; + if (table != nullptr) + metadata_snapshot = table->getInMemoryMetadataPtr(); + ASTPtr expression_ptr; if (columns_mask[src_index++]) { - assert(table != nullptr); + assert(metadata_snapshot != nullptr); if ((expression_ptr = metadata_snapshot->getPartitionKeyAST())) res_columns[res_index++]->insert(queryToString(expression_ptr)); else @@ -374,7 +377,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { - assert(table != nullptr); + assert(metadata_snapshot != nullptr); if ((expression_ptr = metadata_snapshot->getSortingKey().expression_list_ast)) res_columns[res_index++]->insert(queryToString(expression_ptr)); else @@ -383,7 +386,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { - assert(table != nullptr); + assert(metadata_snapshot != nullptr); if ((expression_ptr = metadata_snapshot->getPrimaryKey().expression_list_ast)) res_columns[res_index++]->insert(queryToString(expression_ptr)); else @@ -392,7 +395,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { - assert(table != nullptr); + assert(metadata_snapshot != nullptr); if ((expression_ptr = metadata_snapshot->getSamplingKeyAST())) res_columns[res_index++]->insert(queryToString(expression_ptr)); else @@ -401,7 +404,7 @@ class TablesBlockSource : public SourceWithProgress if (columns_mask[src_index++]) { - assert(table != nullptr); + assert(metadata_snapshot != nullptr); auto policy = table->getStoragePolicy(); if (policy) res_columns[res_index++]->insert(policy->getName()); From d4c49816ab140f1ca0d73c173d1cd1a62b8003fd Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 14:02:31 +0300 Subject: [PATCH 0398/1102] Really atomic metadata --- src/DataStreams/InputStreamFromASTInsertQuery.h | 2 +- src/Interpreters/ExpressionAnalyzer.h | 2 +- src/Interpreters/JoinedTables.h | 2 +- src/Interpreters/SyntaxAnalyzer.h | 2 +- src/Storages/IStorage.h | 15 ++++++++------- src/Storages/MergeTree/MergeTreeWhereOptimizer.h | 2 +- src/Storages/StorageInMemoryMetadata.h | 2 +- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/DataStreams/InputStreamFromASTInsertQuery.h b/src/DataStreams/InputStreamFromASTInsertQuery.h index 0604f011e283..d4c6443c77da 100644 --- a/src/DataStreams/InputStreamFromASTInsertQuery.h +++ b/src/DataStreams/InputStreamFromASTInsertQuery.h @@ -12,7 +12,7 @@ namespace DB struct BlockIO; class Context; struct StorageInMemoryMetadata; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; /** Prepares an input stream which produce data containing in INSERT query * Head of inserting data could be stored in INSERT ast directly diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index cd0b837b4ec5..31939f5016bd 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -32,7 +32,7 @@ class ASTSelectQuery; struct ASTTablesInSelectQueryElement; struct StorageInMemoryMetadata; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; /// Create columns in block or return false if not possible bool sanitizeBlock(Block & block); diff --git a/src/Interpreters/JoinedTables.h b/src/Interpreters/JoinedTables.h index cff86c5a535f..f150de83a940 100644 --- a/src/Interpreters/JoinedTables.h +++ b/src/Interpreters/JoinedTables.h @@ -14,7 +14,7 @@ class ASTSelectQuery; class TableJoin; struct SelectQueryOptions; struct StorageInMemoryMetadata; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; /// Joined tables' columns resolver. /// We want to get each table structure at most once per table occurance. Or even better once per table. diff --git a/src/Interpreters/SyntaxAnalyzer.h b/src/Interpreters/SyntaxAnalyzer.h index 4308b70c45a8..dd3c49a0f1a1 100644 --- a/src/Interpreters/SyntaxAnalyzer.h +++ b/src/Interpreters/SyntaxAnalyzer.h @@ -17,7 +17,7 @@ struct Settings; struct SelectQueryOptions; using Scalars = std::map; struct StorageInMemoryMetadata; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; struct SyntaxAnalyzerResult { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index ec13e26ff430..375ab90aee48 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -82,7 +82,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo IStorage() = delete; /// Storage fields should be initialized in separate methods like setColumns /// or setTableTTLs. - explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)), metadata(std::make_shared()) {} //-V730 + explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)), metadata(std::make_unique()) {} //-V730 virtual ~IStorage() = default; IStorage(const IStorage &) = delete; @@ -137,9 +137,12 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: /// thread-unsafe part. lockStructure must be acquired - StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata; } - StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata; } - void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata = std::make_shared(metadata_); } + StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata.get(); } + StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata.get(); } + void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) + { + metadata.set(std::make_unique(metadata_)); + } /// Return list of virtual columns (like _part, _table, etc). In the vast @@ -165,9 +168,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo StorageID storage_id; mutable std::mutex id_mutex; - /// TODO (alesap) just use multiversion for atomic metadata - mutable std::mutex ttl_mutex; - StorageMetadataPtr metadata; + MultiVersionStorageMetadataPtr metadata; private: RWLockImpl::LockHolder tryLockTimed( const RWLock & rwlock, RWLockImpl::Type type, const String & query_id, const SettingSeconds & acquire_timeout) const; diff --git a/src/Storages/MergeTree/MergeTreeWhereOptimizer.h b/src/Storages/MergeTree/MergeTreeWhereOptimizer.h index 866d0a8754e5..cb2f8939cb53 100644 --- a/src/Storages/MergeTree/MergeTreeWhereOptimizer.h +++ b/src/Storages/MergeTree/MergeTreeWhereOptimizer.h @@ -17,7 +17,7 @@ class ASTSelectQuery; class ASTFunction; class MergeTreeData; struct StorageInMemoryMetadata; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; /** Identifies WHERE expressions that can be placed in PREWHERE by calculating respective * sizes of columns used in particular expression and identifying "good" conditions of diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index e4755bb0464a..bda48bc19cb0 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -194,7 +194,7 @@ struct StorageInMemoryMetadata void check(const Block & block, bool need_all = false) const; }; -using StorageMetadataPtr = std::shared_ptr; +using StorageMetadataPtr = std::shared_ptr; using MultiVersionStorageMetadataPtr = MultiVersion; } From 1a69c3234ae26dd158d1102adf7223f2dd8ff0d3 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 14:09:55 +0300 Subject: [PATCH 0399/1102] Fix style --- src/Storages/IStorage.cpp | 7 ------- src/Storages/StorageInMemoryMetadata.cpp | 5 ----- 2 files changed, 12 deletions(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 8ee9561466a4..3a4559f94dcd 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -20,13 +20,6 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; - extern const int COLUMN_QUERIED_MORE_THAN_ONCE; - extern const int DUPLICATE_COLUMN; - extern const int EMPTY_LIST_OF_COLUMNS_PASSED; - extern const int EMPTY_LIST_OF_COLUMNS_QUERIED; - extern const int NO_SUCH_COLUMN_IN_TABLE; - extern const int NOT_FOUND_COLUMN_IN_BLOCK; - extern const int TYPE_MISMATCH; extern const int TABLE_IS_DROPPED; extern const int NOT_IMPLEMENTED; extern const int DEADLOCK_AVOIDED; diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index a394e196eacc..81d1f3874245 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -10,17 +10,12 @@ namespace DB { namespace ErrorCodes { - extern const int LOGICAL_ERROR; extern const int COLUMN_QUERIED_MORE_THAN_ONCE; extern const int DUPLICATE_COLUMN; - extern const int EMPTY_LIST_OF_COLUMNS_PASSED; extern const int EMPTY_LIST_OF_COLUMNS_QUERIED; extern const int NO_SUCH_COLUMN_IN_TABLE; extern const int NOT_FOUND_COLUMN_IN_BLOCK; extern const int TYPE_MISMATCH; - extern const int TABLE_IS_DROPPED; - extern const int NOT_IMPLEMENTED; - extern const int DEADLOCK_AVOIDED; } From c8a58299ac20beb96f17dabb340524bb40dfb789 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 14:42:48 +0300 Subject: [PATCH 0400/1102] Fix storage buffer metadata --- src/Storages/StorageBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 065dfaa27bff..88619f5bc42b 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -179,7 +179,7 @@ Pipes StorageBuffer::read( if (dst_has_same_structure) { if (query_info.order_optimizer) - query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination, metadata_snapshot); + query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination, destination_metadata_snapshot); /// The destination table has the same structure of the requested columns and we can simply read blocks from there. pipes_from_dst = destination->read( From 75a66fbba3af436cd3e6791d72e9b6eacd16b680 Mon Sep 17 00:00:00 2001 From: Avogar Date: Thu, 18 Jun 2020 15:03:48 +0300 Subject: [PATCH 0401/1102] Fix errors 2 --- .../Formats/Impl/ORCBlockOutputFormat.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp index 4bd9dd230fcd..f34ca21a1b36 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -42,16 +42,7 @@ void ORCOutputStream::write(const void* buf, size_t length) } ORCBlockOutputFormat::ORCBlockOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & format_settings_) - : IOutputFormat(header_, out_), format_settings{format_settings_}, output_stream(out_), data_types(header_.getDataTypes()) -{ - schema = orc::createStructType(); - size_t columns_count = header_.columns(); - for (size_t i = 0; i != columns_count; ++i) - { - schema->addStructField(header_.safeGetByPosition(i).name, getORCType(data_types[i])); - } - writer = orc::createWriter(*schema, &output_stream, options); -} + : IOutputFormat(header_, out_), format_settings{format_settings_}, output_stream(out_), data_types(header_.getDataTypes()) {} ORC_UNIQUE_PTR ORCBlockOutputFormat::getORCType(const DataTypePtr & type) { @@ -149,7 +140,10 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( number_orc_column->notNull[i] = 0; continue; } - number_orc_column->data[i] = number_column.getElement(i); + if constexpr (std::is_same_v) + number_orc_column->data[i] = static_cast(number_column.getElement(i)); + else + number_orc_column->data[i] = number_column.getElement(i); } number_orc_column->numElements = number_column.size(); } @@ -390,6 +384,16 @@ void ORCBlockOutputFormat::consume(Chunk chunk) { size_t columns_num = chunk.getNumColumns(); size_t rows_num = chunk.getNumRows(); + if (!writer) + { + const Block & header = getPort(PortKind::Main).getHeader(); + schema = orc::createStructType(); + for (size_t i = 0; i != columns_num; ++i) + { + schema->addStructField(header.safeGetByPosition(i).name, getORCType(data_types[i])); + } + writer = orc::createWriter(*schema, &output_stream, options); + } ORC_UNIQUE_PTR batch = writer->createRowBatch(rows_num); orc::StructVectorBatch *root = dynamic_cast(batch.get()); for (size_t i = 0; i != columns_num; ++i) From 5bb2ddc6f96d9a8d6dc46c8e21f5eeed6604267b Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 16:00:16 +0300 Subject: [PATCH 0402/1102] Add DISTINCT trait for QueryPlanStep. --- src/Interpreters/ExpressionActions.cpp | 9 +++++ src/Interpreters/ExpressionActions.h | 2 + src/Interpreters/InterpreterSelectQuery.cpp | 20 +++++----- src/Interpreters/InterpreterSelectQuery.h | 2 +- .../QueryPlan/AddingDelayedStreamStep.cpp | 9 ++++- src/Processors/QueryPlan/AggregatingStep.cpp | 9 ++++- src/Processors/QueryPlan/CreatingSetsStep.cpp | 9 ++++- src/Processors/QueryPlan/CubeStep.cpp | 12 +++++- src/Processors/QueryPlan/DistinctStep.cpp | 40 ++++++++++++++++++- src/Processors/QueryPlan/DistinctStep.h | 4 +- src/Processors/QueryPlan/ExpressionStep.cpp | 28 ++++++++++++- src/Processors/QueryPlan/ExtremesStep.cpp | 11 ++++- src/Processors/QueryPlan/FillingStep.cpp | 9 ++++- src/Processors/QueryPlan/FilterStep.cpp | 29 +++++++++++++- .../QueryPlan/FinishSortingStep.cpp | 9 ++++- src/Processors/QueryPlan/IQueryPlanStep.h | 5 ++- .../QueryPlan/ITransformingStep.cpp | 10 ++++- src/Processors/QueryPlan/ITransformingStep.h | 7 +++- src/Processors/QueryPlan/LimitByStep.cpp | 11 ++++- src/Processors/QueryPlan/LimitStep.cpp | 9 ++++- src/Processors/QueryPlan/MergeSortingStep.cpp | 9 ++++- .../QueryPlan/MergingAggregatedStep.cpp | 12 +++++- .../QueryPlan/MergingSortedStep.cpp | 12 +++++- src/Processors/QueryPlan/OffsetsStep.cpp | 9 ++++- .../QueryPlan/PartialSortingStep.cpp | 9 ++++- src/Processors/QueryPlan/RollupStep.cpp | 12 +++++- src/Processors/QueryPlan/TotalsHavingStep.cpp | 10 ++++- 27 files changed, 278 insertions(+), 39 deletions(-) diff --git a/src/Interpreters/ExpressionActions.cpp b/src/Interpreters/ExpressionActions.cpp index c86239b71ae8..0a99fcd6f210 100644 --- a/src/Interpreters/ExpressionActions.cpp +++ b/src/Interpreters/ExpressionActions.cpp @@ -691,6 +691,15 @@ void ExpressionActions::execute(Block & block, ExtraBlockPtr & not_processed, si } } +bool ExpressionActions::hasJoinOrArrayJoin() const +{ + for (const auto & action : actions) + if (action.type == ExpressionAction::JOIN || action.type == ExpressionAction::ARRAY_JOIN) + return true; + + return false; +} + bool ExpressionActions::hasTotalsInJoin() const { for (const auto & action : actions) diff --git a/src/Interpreters/ExpressionActions.h b/src/Interpreters/ExpressionActions.h index 080e8f8a10f2..3d27ffb5b5cf 100644 --- a/src/Interpreters/ExpressionActions.h +++ b/src/Interpreters/ExpressionActions.h @@ -212,6 +212,8 @@ class ExpressionActions /// Execute the expression on the block with continuation. void execute(Block & block, ExtraBlockPtr & not_processed, size_t & start_action) const; + bool hasJoinOrArrayJoin() const; + /// Check if joined subquery has totals. bool hasTotalsInJoin() const; diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 99113907a20a..11a325ae25f2 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -786,7 +786,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn executeOrder(pipeline, query_info.input_order_info); if (expressions.has_order_by && query.limitLength()) - executeDistinct(pipeline, false, expressions.selected_columns); + executeDistinct(pipeline, false, expressions.selected_columns, true); if (expressions.hasLimitBy()) { @@ -893,7 +893,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn else { executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); - executeDistinct(pipeline, true, expressions.selected_columns); + executeDistinct(pipeline, true, expressions.selected_columns, true); } preliminary_sort(); @@ -937,7 +937,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn executeHaving(pipeline, expressions.before_having); executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); - executeDistinct(pipeline, true, expressions.selected_columns); + executeDistinct(pipeline, true, expressions.selected_columns, true); } else if (query.group_by_with_totals || query.group_by_with_rollup || query.group_by_with_cube) @@ -969,16 +969,11 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn has_prelimit = true; } - bool need_merge_streams = need_second_distinct_pass || query.limitBy(); - - if (need_merge_streams) - pipeline.resize(1); - /** If there was more than one stream, * then DISTINCT needs to be performed once again after merging all streams. */ if (need_second_distinct_pass) - executeDistinct(pipeline, false, expressions.selected_columns); + executeDistinct(pipeline, false, expressions.selected_columns, false); if (expressions.hasLimitBy()) { @@ -1636,7 +1631,7 @@ void InterpreterSelectQuery::executeProjection(QueryPipeline & pipeline, const E } -void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns) +void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns, bool pre_distinct) { auto & query = getSelectQuery(); if (query.distinct) @@ -1654,7 +1649,10 @@ void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool befo DistinctStep distinct_step( DataStream{.header = pipeline.getHeader()}, - limits, limit_for_distinct, columns); + limits, limit_for_distinct, columns, pre_distinct); + + if (pre_distinct) + distinct_step.setStepDescription("Preliminary DISTINCT"); distinct_step.transformPipeline(pipeline); } diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index c47b0131ed06..6b0169a4b682 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -129,7 +129,7 @@ class InterpreterSelectQuery : public IInterpreter void executeLimit(QueryPipeline & pipeline); void executeOffset(QueryPipeline & pipeline); static void executeProjection(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); - void executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns); + void executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns, bool pre_distinct); void executeExtremes(QueryPipeline & pipeline); void executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const std::unordered_map & subqueries_for_sets); void executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit, const std::string & description); diff --git a/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp index ea33bb359ae7..a1cb88d604d9 100644 --- a/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp +++ b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp @@ -4,10 +4,17 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false + }; +} + AddingDelayedStreamStep::AddingDelayedStreamStep( const DataStream & input_stream_, ProcessorPtr source_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , source(std::move(source_)) { } diff --git a/src/Processors/QueryPlan/AggregatingStep.cpp b/src/Processors/QueryPlan/AggregatingStep.cpp index 11fb5a143643..24e687ba73bf 100644 --- a/src/Processors/QueryPlan/AggregatingStep.cpp +++ b/src/Processors/QueryPlan/AggregatingStep.cpp @@ -7,6 +7,13 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false /// Actually, we may check that distinct names are in aggregation keys + }; +} + AggregatingStep::AggregatingStep( const DataStream & input_stream_, AggregatingTransformParamsPtr transform_params_, @@ -16,7 +23,7 @@ AggregatingStep::AggregatingStep( bool storage_has_evenly_distributed_read_, InputOrderInfoPtr group_by_info_, SortDescription group_by_sort_description_) - : ITransformingStep(input_stream_, DataStream{.header = transform_params_->getHeader()}) + : ITransformingStep(input_stream_, transform_params_->getHeader(), getTraits()) , transform_params(std::move(transform_params_)) , max_block_size(max_block_size_) , merge_threads(merge_threads_) diff --git a/src/Processors/QueryPlan/CreatingSetsStep.cpp b/src/Processors/QueryPlan/CreatingSetsStep.cpp index fa2eb9b34b39..ebf94180a7b4 100644 --- a/src/Processors/QueryPlan/CreatingSetsStep.cpp +++ b/src/Processors/QueryPlan/CreatingSetsStep.cpp @@ -5,12 +5,19 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + CreatingSetsStep::CreatingSetsStep( const DataStream & input_stream_, SubqueriesForSets subqueries_for_sets_, SizeLimits network_transfer_limits_, const Context & context_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , subqueries_for_sets(std::move(subqueries_for_sets_)) , network_transfer_limits(std::move(network_transfer_limits_)) , context(context_) diff --git a/src/Processors/QueryPlan/CubeStep.cpp b/src/Processors/QueryPlan/CubeStep.cpp index 3c8b2087fe0a..124ef78b279b 100644 --- a/src/Processors/QueryPlan/CubeStep.cpp +++ b/src/Processors/QueryPlan/CubeStep.cpp @@ -5,10 +5,20 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false + }; +} + CubeStep::CubeStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_) - : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + : ITransformingStep(input_stream_, params_->getHeader(), getTraits()) , params(std::move(params_)) { + /// Aggregation keys are distinct + for (auto key : params->params.keys) + output_stream->distinct_columns.insert(params->params.src_header.getByPosition(key).name); } void CubeStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/DistinctStep.cpp b/src/Processors/QueryPlan/DistinctStep.cpp index 7e339ba816bd..5fd26ce73b20 100644 --- a/src/Processors/QueryPlan/DistinctStep.cpp +++ b/src/Processors/QueryPlan/DistinctStep.cpp @@ -5,20 +5,56 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + + DistinctStep::DistinctStep( const DataStream & input_stream_, const SizeLimits & set_size_limits_, UInt64 limit_hint_, - const Names & columns_) - : ITransformingStep(input_stream_, input_stream_) + const Names & columns_, + bool pre_distinct_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , set_size_limits(set_size_limits_) , limit_hint(limit_hint_) , columns(columns_) + , pre_distinct(pre_distinct_) +{ + auto & distinct_columns = pre_distinct ? output_stream->local_distinct_columns + : output_stream->distinct_columns; + + /// Add more distinct columns. + for (const auto & name : columns) + distinct_columns.insert(name); +} + +static bool checkColumnsAlreadyDistinct(const Names & columns, const NameSet & distinct_names) { + bool columns_already_distinct = true; + for (const auto & name : columns) + if (distinct_names.count(name) == 0) + columns_already_distinct = false; + + return columns_already_distinct; } void DistinctStep::transformPipeline(QueryPipeline & pipeline) { + if (checkColumnsAlreadyDistinct(columns, input_streams.front().distinct_columns)) + return; + + if ((pre_distinct || pipeline.getNumStreams() <= 1) + && checkColumnsAlreadyDistinct(columns, input_streams.front().local_distinct_columns)) + return; + + if (!pre_distinct) + pipeline.resize(1); + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr { if (stream_type != QueryPipeline::StreamType::Main) diff --git a/src/Processors/QueryPlan/DistinctStep.h b/src/Processors/QueryPlan/DistinctStep.h index a70c66b2a4f9..5ec6e683bba7 100644 --- a/src/Processors/QueryPlan/DistinctStep.h +++ b/src/Processors/QueryPlan/DistinctStep.h @@ -12,7 +12,8 @@ class DistinctStep : public ITransformingStep const DataStream & input_stream_, const SizeLimits & set_size_limits_, UInt64 limit_hint_, - const Names & columns_); + const Names & columns_, + bool pre_distinct_); /// If is enabled, execute distinct for separate streams. Otherwise, merge streams. String getName() const override { return "Distinct"; } @@ -22,6 +23,7 @@ class DistinctStep : public ITransformingStep SizeLimits set_size_limits; UInt64 limit_hint; Names columns; + bool pre_distinct; }; } diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index 8265a9884b96..c3f7177eea04 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -2,17 +2,43 @@ #include #include #include +#include namespace DB { +static ITransformingStep::DataStreamTraits getTraits(const ExpressionActionsPtr & expression) +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = !expression->hasJoinOrArrayJoin() + }; +} + +static void filterDistinctColumns(const Block & res_header, NameSet & distinct_columns) +{ + if (distinct_columns.empty()) + return; + + NameSet new_distinct_columns; + for (const auto & column : res_header) + if (distinct_columns.count(column.name)) + new_distinct_columns.insert(column.name); + + distinct_columns.swap(new_distinct_columns); +} + ExpressionStep::ExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_) : ITransformingStep( input_stream_, - DataStream{.header = ExpressionTransform::transformHeader(input_stream_.header, expression_)}) + ExpressionTransform::transformHeader(input_stream_.header, expression_), + getTraits(expression_)) , expression(std::move(expression_)) , default_totals(default_totals_) { + /// Some columns may be removed by expression. + /// TODO: also check aliases, functions and some types of join + filterDistinctColumns(output_stream->header, output_stream->distinct_columns); + filterDistinctColumns(output_stream->header, output_stream->local_distinct_columns); } void ExpressionStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/ExtremesStep.cpp b/src/Processors/QueryPlan/ExtremesStep.cpp index 91cd0bddc7c3..33b08c0da7a2 100644 --- a/src/Processors/QueryPlan/ExtremesStep.cpp +++ b/src/Processors/QueryPlan/ExtremesStep.cpp @@ -4,8 +4,17 @@ namespace DB { -ExtremesStep::ExtremesStep(const DataStream & input_stream_) : ITransformingStep(input_stream_, input_stream_) {} +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} +ExtremesStep::ExtremesStep(const DataStream & input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) +{ +} void ExtremesStep::transformPipeline(QueryPipeline & pipeline) { diff --git a/src/Processors/QueryPlan/FillingStep.cpp b/src/Processors/QueryPlan/FillingStep.cpp index fec63b3322d1..e5562378234b 100644 --- a/src/Processors/QueryPlan/FillingStep.cpp +++ b/src/Processors/QueryPlan/FillingStep.cpp @@ -5,8 +5,15 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false /// TODO: it seem to actually be true. Check it later. + }; +} + FillingStep::FillingStep(const DataStream & input_stream_, SortDescription sort_description_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , sort_description(std::move(sort_description_)) { } diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index 7fc39723684d..926c907c53ba 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -1,10 +1,32 @@ #include #include #include +#include namespace DB { +static ITransformingStep::DataStreamTraits getTraits(const ExpressionActionsPtr & expression) +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = !expression->hasJoinOrArrayJoin() /// I suppose it actually never happens + }; +} + +static void filterDistinctColumns(const Block & res_header, NameSet & distinct_columns) +{ + if (distinct_columns.empty()) + return; + + NameSet new_distinct_columns; + for (const auto & column : res_header) + if (distinct_columns.count(column.name)) + new_distinct_columns.insert(column.name); + + distinct_columns.swap(new_distinct_columns); +} + + FilterStep::FilterStep( const DataStream & input_stream_, ExpressionActionsPtr expression_, @@ -12,12 +34,15 @@ FilterStep::FilterStep( bool remove_filter_column_) : ITransformingStep( input_stream_, - DataStream{.header = FilterTransform::transformHeader( - input_stream_.header, expression_, filter_column_name_, remove_filter_column_)}) + FilterTransform::transformHeader(input_stream_.header, expression_, filter_column_name_, remove_filter_column_), + getTraits(expression_)) , expression(std::move(expression_)) , filter_column_name(std::move(filter_column_name_)) , remove_filter_column(remove_filter_column_) { + /// TODO: it would be easier to remove all expressions from filter step. It should only filter by column name. + filterDistinctColumns(output_stream->header, output_stream->distinct_columns); + filterDistinctColumns(output_stream->header, output_stream->local_distinct_columns); } void FilterStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/FinishSortingStep.cpp b/src/Processors/QueryPlan/FinishSortingStep.cpp index 87a28d910402..194f315b728f 100644 --- a/src/Processors/QueryPlan/FinishSortingStep.cpp +++ b/src/Processors/QueryPlan/FinishSortingStep.cpp @@ -8,13 +8,20 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + FinishSortingStep::FinishSortingStep( const DataStream & input_stream_, SortDescription prefix_description_, SortDescription result_description_, size_t max_block_size_, UInt64 limit_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , prefix_description(std::move(prefix_description_)) , result_description(std::move(result_description_)) , max_block_size(max_block_size_) diff --git a/src/Processors/QueryPlan/IQueryPlanStep.h b/src/Processors/QueryPlan/IQueryPlanStep.h index a33dcca4bef9..938ddda81b1e 100644 --- a/src/Processors/QueryPlan/IQueryPlanStep.h +++ b/src/Processors/QueryPlan/IQueryPlanStep.h @@ -14,10 +14,11 @@ class DataStream public: Block header; - /// Only header for now. + NameSet distinct_columns; + NameSet local_distinct_columns; /// Those columns are distinct in separate thread, but not in general. + /// Things which may be added: /// * sort description - /// * distinct columns /// * limit /// * estimated rows number /// * memory allocation context diff --git a/src/Processors/QueryPlan/ITransformingStep.cpp b/src/Processors/QueryPlan/ITransformingStep.cpp index a7c756dddb27..bf4afdfccd40 100644 --- a/src/Processors/QueryPlan/ITransformingStep.cpp +++ b/src/Processors/QueryPlan/ITransformingStep.cpp @@ -4,10 +4,16 @@ namespace DB { -ITransformingStep::ITransformingStep(DataStream input_stream, DataStream output_stream_) +ITransformingStep::ITransformingStep(DataStream input_stream, Block output_header, DataStreamTraits traits) { input_streams.emplace_back(std::move(input_stream)); - output_stream = std::move(output_stream_); + output_stream = DataStream{.header = std::move(output_header)}; + + if (traits.preserves_distinct_columns) + { + output_stream->distinct_columns = input_streams.front().distinct_columns; + output_stream->local_distinct_columns = input_streams.front().local_distinct_columns; + } } QueryPipelinePtr ITransformingStep::updatePipeline(QueryPipelines pipelines) diff --git a/src/Processors/QueryPlan/ITransformingStep.h b/src/Processors/QueryPlan/ITransformingStep.h index 3e722cf764cc..f18b6ba3c8dd 100644 --- a/src/Processors/QueryPlan/ITransformingStep.h +++ b/src/Processors/QueryPlan/ITransformingStep.h @@ -7,7 +7,12 @@ namespace DB class ITransformingStep : public IQueryPlanStep { public: - ITransformingStep(DataStream input_stream, DataStream output_stream); + struct DataStreamTraits + { + bool preserves_distinct_columns; + }; + + ITransformingStep(DataStream input_stream, Block output_header, DataStreamTraits traits); QueryPipelinePtr updatePipeline(QueryPipelines pipelines) override; diff --git a/src/Processors/QueryPlan/LimitByStep.cpp b/src/Processors/QueryPlan/LimitByStep.cpp index 6b9dd9d3727b..1ce2c8964fb9 100644 --- a/src/Processors/QueryPlan/LimitByStep.cpp +++ b/src/Processors/QueryPlan/LimitByStep.cpp @@ -5,10 +5,17 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + LimitByStep::LimitByStep( const DataStream & input_stream_, size_t group_length_, size_t group_offset_, const Names & columns_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , group_length(group_length_) , group_offset(group_offset_) , columns(columns_) @@ -18,6 +25,8 @@ LimitByStep::LimitByStep( void LimitByStep::transformPipeline(QueryPipeline & pipeline) { + pipeline.resize(1); + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr { if (stream_type != QueryPipeline::StreamType::Main) diff --git a/src/Processors/QueryPlan/LimitStep.cpp b/src/Processors/QueryPlan/LimitStep.cpp index daae8913ee5b..aef7f89c7a5e 100644 --- a/src/Processors/QueryPlan/LimitStep.cpp +++ b/src/Processors/QueryPlan/LimitStep.cpp @@ -5,13 +5,20 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + LimitStep::LimitStep( const DataStream & input_stream_, size_t limit_, size_t offset_, bool always_read_till_end_, bool with_ties_, SortDescription description_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , limit(limit_), offset(offset_) , always_read_till_end(always_read_till_end_) , with_ties(with_ties_), description(std::move(description_)) diff --git a/src/Processors/QueryPlan/MergeSortingStep.cpp b/src/Processors/QueryPlan/MergeSortingStep.cpp index d2a3f01b0609..bc48ae1a98ae 100644 --- a/src/Processors/QueryPlan/MergeSortingStep.cpp +++ b/src/Processors/QueryPlan/MergeSortingStep.cpp @@ -5,6 +5,13 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + MergeSortingStep::MergeSortingStep( const DataStream & input_stream, const SortDescription & description_, @@ -14,7 +21,7 @@ MergeSortingStep::MergeSortingStep( size_t max_bytes_before_external_sort_, VolumePtr tmp_volume_, size_t min_free_disk_space_) - : ITransformingStep(input_stream, input_stream) + : ITransformingStep(input_stream, input_stream.header, getTraits()) , description(description_) , max_merged_block_size(max_merged_block_size_) , limit(limit_) diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.cpp b/src/Processors/QueryPlan/MergingAggregatedStep.cpp index ad67ed5de426..6cf1c7608586 100644 --- a/src/Processors/QueryPlan/MergingAggregatedStep.cpp +++ b/src/Processors/QueryPlan/MergingAggregatedStep.cpp @@ -7,18 +7,28 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false + }; +} + MergingAggregatedStep::MergingAggregatedStep( const DataStream & input_stream_, AggregatingTransformParamsPtr params_, bool memory_efficient_aggregation_, size_t max_threads_, size_t memory_efficient_merge_threads_) - : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + : ITransformingStep(input_stream_, params_->getHeader(), getTraits()) , params(params_) , memory_efficient_aggregation(memory_efficient_aggregation_) , max_threads(max_threads_) , memory_efficient_merge_threads(memory_efficient_merge_threads_) { + /// Aggregation keys are distinct + for (auto key : params->params.keys) + output_stream->distinct_columns.insert(params->params.src_header.getByPosition(key).name); } void MergingAggregatedStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/MergingSortedStep.cpp b/src/Processors/QueryPlan/MergingSortedStep.cpp index 5417941745ac..4157972149f6 100644 --- a/src/Processors/QueryPlan/MergingSortedStep.cpp +++ b/src/Processors/QueryPlan/MergingSortedStep.cpp @@ -5,16 +5,26 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + MergingSortedStep::MergingSortedStep( const DataStream & input_stream, SortDescription sort_description_, size_t max_block_size_, UInt64 limit_) - : ITransformingStep(input_stream, input_stream) + : ITransformingStep(input_stream, input_stream.header, getTraits()) , sort_description(std::move(sort_description_)) , max_block_size(max_block_size_) , limit(limit_) { + /// Streams are merged together, only global distinct keys remain distinct. + /// Note: we can not clear it if know that there will be only one stream in pipeline. Should we add info about it? + output_stream->local_distinct_columns.clear(); } void MergingSortedStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/OffsetsStep.cpp b/src/Processors/QueryPlan/OffsetsStep.cpp index b43bc9f51d06..a98b996b8e9e 100644 --- a/src/Processors/QueryPlan/OffsetsStep.cpp +++ b/src/Processors/QueryPlan/OffsetsStep.cpp @@ -5,8 +5,15 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + OffsetsStep::OffsetsStep(const DataStream & input_stream_, size_t offset_) - : ITransformingStep(input_stream_, input_stream_) + : ITransformingStep(input_stream_, input_stream_.header, getTraits()) , offset(offset_) { } diff --git a/src/Processors/QueryPlan/PartialSortingStep.cpp b/src/Processors/QueryPlan/PartialSortingStep.cpp index 984d34e8e8e3..ee68415d3fc2 100644 --- a/src/Processors/QueryPlan/PartialSortingStep.cpp +++ b/src/Processors/QueryPlan/PartialSortingStep.cpp @@ -6,12 +6,19 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + PartialSortingStep::PartialSortingStep( const DataStream & input_stream, SortDescription sort_description_, UInt64 limit_, SizeLimits size_limits_) - : ITransformingStep(input_stream, input_stream) + : ITransformingStep(input_stream, input_stream.header, getTraits()) , sort_description(std::move(sort_description_)) , limit(limit_) , size_limits(size_limits_) diff --git a/src/Processors/QueryPlan/RollupStep.cpp b/src/Processors/QueryPlan/RollupStep.cpp index 5045cb71cb20..0bca6b42ff63 100644 --- a/src/Processors/QueryPlan/RollupStep.cpp +++ b/src/Processors/QueryPlan/RollupStep.cpp @@ -5,10 +5,20 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = false + }; +} + RollupStep::RollupStep(const DataStream & input_stream_, AggregatingTransformParamsPtr params_) - : ITransformingStep(input_stream_, DataStream{.header = params_->getHeader()}) + : ITransformingStep(input_stream_, params_->getHeader(), getTraits()) , params(std::move(params_)) { + /// Aggregation keys are distinct + for (auto key : params->params.keys) + output_stream->distinct_columns.insert(params->params.src_header.getByPosition(key).name); } void RollupStep::transformPipeline(QueryPipeline & pipeline) diff --git a/src/Processors/QueryPlan/TotalsHavingStep.cpp b/src/Processors/QueryPlan/TotalsHavingStep.cpp index 0db1acd74f58..d1b6fda3133a 100644 --- a/src/Processors/QueryPlan/TotalsHavingStep.cpp +++ b/src/Processors/QueryPlan/TotalsHavingStep.cpp @@ -6,6 +6,13 @@ namespace DB { +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + TotalsHavingStep::TotalsHavingStep( const DataStream & input_stream_, bool overflow_row_, @@ -16,7 +23,8 @@ TotalsHavingStep::TotalsHavingStep( bool final_) : ITransformingStep( input_stream_, - DataStream{.header = TotalsHavingTransform::transformHeader(input_stream_.header, expression_, final_)}) + TotalsHavingTransform::transformHeader(input_stream_.header, expression_, final_), + getTraits()) , overflow_row(overflow_row_) , expression(expression_) , filter_column_name(filter_column_) From e0841360dbc40bc3cf452820e9959e7d3bee1a76 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 16:38:45 +0300 Subject: [PATCH 0403/1102] Use QueryPlan in InterpreterSelectQuery [part 1]. --- src/Interpreters/InterpreterSelectQuery.cpp | 157 +++++++++---------- src/Interpreters/InterpreterSelectQuery.h | 46 +++--- src/Processors/QueryPlan/ExpressionStep.cpp | 25 ++- src/Processors/QueryPlan/ReadNothingStep.cpp | 4 +- src/Processors/QueryPlan/ReadNothingStep.h | 2 +- 5 files changed, 124 insertions(+), 110 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 11a325ae25f2..a1f42018bbeb 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include @@ -464,7 +466,10 @@ Block InterpreterSelectQuery::getSampleBlock() BlockIO InterpreterSelectQuery::execute() { BlockIO res; - executeImpl(res.pipeline, input, std::move(input_pipe)); + QueryPlan query_plan; + executeImpl(query_plan, input, std::move(input_pipe)); + + res.pipeline = std::move(*query_plan.buildQueryPipeline()); res.pipeline.addInterpreterContext(context); res.pipeline.addStorageHolder(storage); @@ -683,7 +688,7 @@ static UInt64 getLimitForSorting(const ASTSelectQuery & query, const Context & c return 0; } -void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockInputStreamPtr & prepared_input, std::optional prepared_pipe) +void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInputStreamPtr & prepared_input, std::optional prepared_pipe) { /** Streams of data. When the query is executed in parallel, we have several data streams. * If there is no GROUP BY, then perform all operations before ORDER BY and LIMIT in parallel, then @@ -704,30 +709,30 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (options.only_analyze) { - ReadNothingStep read_nothing(DataStream{.header = source_header}); - read_nothing.initializePipeline(pipeline); + auto read_nothing = std::make_unique(source_header); + query_plan.addStep(std::move(read_nothing)); if (expressions.prewhere_info) { - FilterStep prewhere_step( - DataStream{.header = pipeline.getHeader()}, + auto prewhere_step = std::make_unique( + query_plan.getCurrentDataStream(), expressions.prewhere_info->prewhere_actions, expressions.prewhere_info->prewhere_column_name, expressions.prewhere_info->remove_prewhere_column); - prewhere_step.setStepDescription("PREWHERE"); - prewhere_step.transformPipeline(pipeline); + prewhere_step->setStepDescription("PREWHERE"); + query_plan.addStep(std::move(prewhere_step)); // To remove additional columns in dry run // For example, sample column which can be removed in this stage if (expressions.prewhere_info->remove_columns_actions) { - ExpressionStep remove_columns( - DataStream{.header = pipeline.getHeader()}, + auto remove_columns = std::make_unique( + query_plan.getCurrentDataStream(), expressions.prewhere_info->remove_columns_actions); - remove_columns.setStepDescription("Remove unnecessary columns after PREWHERE"); - remove_columns.transformPipeline(pipeline); + remove_columns->setStepDescription("Remove unnecessary columns after PREWHERE"); + query_plan.addStep(std::move(remove_columns)); } } } @@ -735,13 +740,14 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn { if (prepared_input) { - ReadFromPreparedSource prepared_source_step(Pipe(std::make_shared(prepared_input))); - prepared_source_step.initializePipeline(pipeline); + auto prepared_source_step = std::make_unique( + Pipe(std::make_shared(prepared_input))); + query_plan.addStep(std::move(prepared_source_step)); } else if (prepared_pipe) { - ReadFromPreparedSource prepared_source_step(std::move(*prepared_pipe)); - prepared_source_step.initializePipeline(pipeline); + auto prepared_source_step = std::make_unique(std::move(*prepared_pipe)); + query_plan.addStep(std::move(prepared_source_step)); } if (from_stage == QueryProcessingStage::WithMergeableState && @@ -752,7 +758,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn throw Exception("PREWHERE is not supported if the table is filtered by row-level security expression", ErrorCodes::ILLEGAL_PREWHERE); /** Read the data from Storage. from_stage - to what stage the request was completed in Storage. */ - executeFetchColumns(from_stage, pipeline, expressions.prewhere_info, expressions.columns_to_remove_after_prewhere); + executeFetchColumns(from_stage, query_plan, expressions.prewhere_info, expressions.columns_to_remove_after_prewhere); LOG_TRACE(log, "{} -> {}", QueryProcessingStage::toString(from_stage), QueryProcessingStage::toString(options.to_stage)); } @@ -783,19 +789,19 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (!expressions.second_stage && !expressions.need_aggregate && !expressions.hasHaving()) { if (expressions.has_order_by) - executeOrder(pipeline, query_info.input_order_info); + executeOrder(query_plan, query_info.input_order_info); if (expressions.has_order_by && query.limitLength()) - executeDistinct(pipeline, false, expressions.selected_columns, true); + executeDistinct(query_plan, false, expressions.selected_columns, true); if (expressions.hasLimitBy()) { - executeExpression(pipeline, expressions.before_limit_by, "Before LIMIT BY"); - executeLimitBy(pipeline); + executeExpression(query_plan, expressions.before_limit_by, "Before LIMIT BY"); + executeLimitBy(query_plan); } if (query.limitLength()) - executePreLimit(pipeline, true); + executePreLimit(query_plan, true); } }; @@ -806,21 +812,21 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn preliminary_sort(); if (expressions.need_aggregate) - executeMergeAggregated(pipeline, aggregate_overflow_row, aggregate_final); + executeMergeAggregated(query_plan, aggregate_overflow_row, aggregate_final); } if (expressions.first_stage) { if (expressions.hasFilter()) { - FilterStep row_level_security_step( - DataStream{.header = pipeline.getHeader()}, + auto row_level_security_step = std::make_unique( + query_plan.getCurrentDataStream(), expressions.filter_info->actions, expressions.filter_info->column_name, expressions.filter_info->do_remove_column); - row_level_security_step.setStepDescription("Row-level security filter"); - row_level_security_step.transformPipeline(pipeline); + row_level_security_step->setStepDescription("Row-level security filter"); + query_plan.addStep(std::move(row_level_security_step)); } if (expressions.hasJoin()) @@ -828,15 +834,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn Block join_result_sample; JoinPtr join = expressions.before_join->getTableJoinAlgo(); - join_result_sample = ExpressionTransform::transformHeader(pipeline.getHeader(), expressions.before_join); - - /// In case joined subquery has totals, and we don't, add default chunk to totals. - bool default_totals = false; - if (!pipeline.hasTotals()) - { - pipeline.addDefaultTotals(); - default_totals = true; - } + join_result_sample = ExpressionTransform::transformHeader(query_plan.getCurrentDataStream().header, expressions.before_join); bool inflating_join = false; if (join) @@ -846,61 +844,60 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn inflating_join = isCross(hash_join->getKind()); } + QueryPlanStepPtr before_join_step; if (inflating_join) { - InflatingExpressionStep before_join_step( - DataStream{.header = pipeline.getHeader()}, + before_join_step = std::make_unique( + query_plan.getCurrentDataStream(), expressions.before_join, - default_totals); + true); - before_join_step.setStepDescription("JOIN"); - before_join_step.transformPipeline(pipeline); } else { - ExpressionStep before_join_step( - DataStream{.header = pipeline.getHeader()}, + before_join_step = std::make_unique( + query_plan.getCurrentDataStream(), expressions.before_join, - default_totals); - - before_join_step.setStepDescription("JOIN"); - before_join_step.transformPipeline(pipeline); + true); } + before_join_step->setStepDescription("JOIN"); + query_plan.addStep(std::move(before_join_step)); + if (join) { if (auto stream = join->createStreamWithNonJoinedRows(join_result_sample, settings.max_block_size)) { auto source = std::make_shared(std::move(stream)); - AddingDelayedStreamStep add_non_joined_rows_step( - DataStream{.header = pipeline.getHeader()}, std::move(source)); + auto add_non_joined_rows_step = std::make_unique( + query_plan.getCurrentDataStream(), std::move(source)); - add_non_joined_rows_step.setStepDescription("Add non-joined rows after JOIN"); - add_non_joined_rows_step.transformPipeline(pipeline); + add_non_joined_rows_step->setStepDescription("Add non-joined rows after JOIN"); + query_plan.addStep(std::move(add_non_joined_rows_step)); } } } if (expressions.hasWhere()) - executeWhere(pipeline, expressions.before_where, expressions.remove_where_filter); + executeWhere(query_plan, expressions.before_where, expressions.remove_where_filter); if (expressions.need_aggregate) { - executeAggregation(pipeline, expressions.before_aggregation, aggregate_overflow_row, aggregate_final, query_info.input_order_info); + executeAggregation(query_plan, expressions.before_aggregation, aggregate_overflow_row, aggregate_final, query_info.input_order_info); /// We need to reset input order info, so that executeOrder can't use it query_info.input_order_info.reset(); } else { - executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); - executeDistinct(pipeline, true, expressions.selected_columns, true); + executeExpression(query_plan, expressions.before_order_and_select, "Before ORDER BY and SELECT"); + executeDistinct(query_plan, true, expressions.selected_columns, true); } preliminary_sort(); // If there is no global subqueries, we can run subqueries only when receive them on server. if (!query_analyzer->hasGlobalSubqueries() && !subqueries_for_sets.empty()) - executeSubqueriesInSetsAndJoins(pipeline, subqueries_for_sets); + executeSubqueriesInSetsAndJoins(query_plan, subqueries_for_sets); } if (expressions.second_stage) @@ -911,40 +908,38 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn { /// If you need to combine aggregated results from multiple servers if (!expressions.first_stage) - executeMergeAggregated(pipeline, aggregate_overflow_row, aggregate_final); + executeMergeAggregated(query_plan, aggregate_overflow_row, aggregate_final); if (!aggregate_final) { if (query.group_by_with_totals) { bool final = !query.group_by_with_rollup && !query.group_by_with_cube; - executeTotalsAndHaving(pipeline, expressions.hasHaving(), expressions.before_having, aggregate_overflow_row, final); + executeTotalsAndHaving(query_plan, expressions.hasHaving(), expressions.before_having, aggregate_overflow_row, final); } if (query.group_by_with_rollup) - executeRollupOrCube(pipeline, Modificator::ROLLUP); + executeRollupOrCube(query_plan, Modificator::ROLLUP); else if (query.group_by_with_cube) - executeRollupOrCube(pipeline, Modificator::CUBE); + executeRollupOrCube(query_plan, Modificator::CUBE); if ((query.group_by_with_rollup || query.group_by_with_cube) && expressions.hasHaving()) { if (query.group_by_with_totals) throw Exception("WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of HAVING", ErrorCodes::NOT_IMPLEMENTED); - executeHaving(pipeline, expressions.before_having); + executeHaving(query_plan, expressions.before_having); } } else if (expressions.hasHaving()) - executeHaving(pipeline, expressions.before_having); + executeHaving(query_plan, expressions.before_having); - executeExpression(pipeline, expressions.before_order_and_select, "Before ORDER BY and SELECT"); - executeDistinct(pipeline, true, expressions.selected_columns, true); + executeExpression(query_plan, expressions.before_order_and_select, "Before ORDER BY and SELECT"); + executeDistinct(query_plan, true, expressions.selected_columns, true); } else if (query.group_by_with_totals || query.group_by_with_rollup || query.group_by_with_cube) throw Exception("WITH TOTALS, ROLLUP or CUBE are not supported without aggregation", ErrorCodes::NOT_IMPLEMENTED); - need_second_distinct_pass = query.distinct && pipeline.hasMixedStreams(); - if (expressions.has_order_by) { /** If there is an ORDER BY for distributed query processing, @@ -953,57 +948,57 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn */ if (!expressions.first_stage && !expressions.need_aggregate && !(query.group_by_with_totals && !aggregate_final)) - executeMergeSorted(pipeline, "before ORDER BY"); + executeMergeSorted(query_plan, "before ORDER BY"); else /// Otherwise, just sort. - executeOrder(pipeline, query_info.input_order_info); + executeOrder(query_plan, query_info.input_order_info); } /** Optimization - if there are several sources and there is LIMIT, then first apply the preliminary LIMIT, * limiting the number of rows in each up to `offset + limit`. */ bool has_prelimit = false; - if (query.limitLength() && !query.limit_with_ties && pipeline.hasMoreThanOneStream() && + if (query.limitLength() && !query.limit_with_ties && !query.distinct && !expressions.hasLimitBy() && !settings.extremes) { - executePreLimit(pipeline, false); + executePreLimit(query_plan, false); has_prelimit = true; } /** If there was more than one stream, * then DISTINCT needs to be performed once again after merging all streams. */ - if (need_second_distinct_pass) - executeDistinct(pipeline, false, expressions.selected_columns, false); + if (query.distinct) + executeDistinct(query_plan, false, expressions.selected_columns, false); if (expressions.hasLimitBy()) { - executeExpression(pipeline, expressions.before_limit_by, "Before LIMIT BY"); - executeLimitBy(pipeline); + executeExpression(query_plan, expressions.before_limit_by, "Before LIMIT BY"); + executeLimitBy(query_plan); } - executeWithFill(pipeline); + executeWithFill(query_plan); /** We must do projection after DISTINCT because projection may remove some columns. */ - executeProjection(pipeline, expressions.final_projection); + executeProjection(query_plan, expressions.final_projection); /** Extremes are calculated before LIMIT, but after LIMIT BY. This is Ok. */ - executeExtremes(pipeline); + executeExtremes(query_plan); if (!has_prelimit) /// Limit is no longer needed if there is prelimit. - executeLimit(pipeline); + executeLimit(query_plan); - executeOffset(pipeline); + executeOffset(query_plan); } } if (query_analyzer->hasGlobalSubqueries() && !subqueries_for_sets.empty()) - executeSubqueriesInSetsAndJoins(pipeline, subqueries_for_sets); + executeSubqueriesInSetsAndJoins(query_plan, subqueries_for_sets); } void InterpreterSelectQuery::executeFetchColumns( - QueryProcessingStage::Enum processing_stage, QueryPipeline & pipeline, + QueryProcessingStage::Enum processing_stage, QueryPlan & query_plan, const PrewhereInfoPtr & prewhere_info, const Names & columns_to_remove_after_prewhere) { auto & query = getSelectQuery(); diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 6b0169a4b682..7ab266b52eb1 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -14,7 +14,6 @@ #include #include -#include #include namespace Poco { class Logger; } @@ -25,6 +24,7 @@ namespace DB struct SubqueryForSet; class InterpreterSelectWithUnionQuery; class Context; +class QueryPlan; struct SyntaxAnalyzerResult; using SyntaxAnalyzerResultPtr = std::shared_ptr; @@ -104,35 +104,35 @@ class InterpreterSelectQuery : public IInterpreter Block getSampleBlockImpl(); - void executeImpl(QueryPipeline & pipeline, const BlockInputStreamPtr & prepared_input, std::optional prepared_pipe); + void executeImpl(QueryPlan & query_plan, const BlockInputStreamPtr & prepared_input, std::optional prepared_pipe); /// Different stages of query execution. void executeFetchColumns( QueryProcessingStage::Enum processing_stage, - QueryPipeline & pipeline, + QueryPlan & query_plan, const PrewhereInfoPtr & prewhere_info, const Names & columns_to_remove_after_prewhere); - void executeWhere(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool remove_filter); - void executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info); - void executeMergeAggregated(QueryPipeline & pipeline, bool overflow_row, bool final); - void executeTotalsAndHaving(QueryPipeline & pipeline, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final); - void executeHaving(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); - static void executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, const std::string & description); - void executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info); - void executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info, UInt64 limit, SortDescription & output_order_descr); - void executeWithFill(QueryPipeline & pipeline); - void executeMergeSorted(QueryPipeline & pipeline, const std::string & description); - void executePreLimit(QueryPipeline & pipeline, bool do_not_skip_offset); - void executeLimitBy(QueryPipeline & pipeline); - void executeLimit(QueryPipeline & pipeline); - void executeOffset(QueryPipeline & pipeline); - static void executeProjection(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); - void executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns, bool pre_distinct); - void executeExtremes(QueryPipeline & pipeline); - void executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const std::unordered_map & subqueries_for_sets); - void executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit, const std::string & description); + void executeWhere(QueryPlan & query_plan, const ExpressionActionsPtr & expression, bool remove_filter); + void executeAggregation(QueryPlan & query_plan, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info); + void executeMergeAggregated(QueryPlan & query_plan, bool overflow_row, bool final); + void executeTotalsAndHaving(QueryPlan & query_plan, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final); + void executeHaving(QueryPlan & query_plan, const ExpressionActionsPtr & expression); + static void executeExpression(QueryPlan & query_plan, const ExpressionActionsPtr & expression, const std::string & description); + void executeOrder(QueryPlan & query_plan, InputOrderInfoPtr sorting_info); + void executeOrderOptimized(QueryPlan & query_plan, InputOrderInfoPtr sorting_info, UInt64 limit, SortDescription & output_order_descr); + void executeWithFill(QueryPlan & query_plan); + void executeMergeSorted(QueryPlan & query_plan, const std::string & description); + void executePreLimit(QueryPlan & query_plan, bool do_not_skip_offset); + void executeLimitBy(QueryPlan & query_plan); + void executeLimit(QueryPlan & query_plan); + void executeOffset(QueryPlan & query_plan); + static void executeProjection(QueryPlan & query_plan, const ExpressionActionsPtr & expression); + void executeDistinct(QueryPlan & query_plan, bool before_order, Names columns, bool pre_distinct); + void executeExtremes(QueryPlan & query_plan); + void executeSubqueriesInSetsAndJoins(QueryPlan & query_plan, const std::unordered_map & subqueries_for_sets); + void executeMergeSorted(QueryPlan & query_plan, const SortDescription & sort_description, UInt64 limit, const std::string & description); String generateFilterActions( ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns = {}) const; @@ -143,7 +143,7 @@ class InterpreterSelectQuery : public IInterpreter CUBE = 1 }; - void executeRollupOrCube(QueryPipeline & pipeline, Modificator modificator); + void executeRollupOrCube(QueryPlan & query_plan, Modificator modificator); /** If there is a SETTINGS section in the SELECT query, then apply settings from it. * diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index c3f7177eea04..c4823f62ea3c 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -43,28 +43,47 @@ ExpressionStep::ExpressionStep(const DataStream & input_stream_, ExpressionActio void ExpressionStep::transformPipeline(QueryPipeline & pipeline) { + /// In case joined subquery has totals, and we don't, add default chunk to totals. + bool add_default_totals = false; + if (default_totals && !pipeline.hasTotals()) + { + pipeline.addDefaultTotals(); + add_default_totals = true; + } + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) { bool on_totals = stream_type == QueryPipeline::StreamType::Totals; - return std::make_shared(header, expression, on_totals, default_totals); + return std::make_shared(header, expression, on_totals, add_default_totals); }); } InflatingExpressionStep::InflatingExpressionStep(const DataStream & input_stream_, ExpressionActionsPtr expression_, bool default_totals_) : ITransformingStep( input_stream_, - DataStream{.header = ExpressionTransform::transformHeader(input_stream_.header, expression_)}) + ExpressionTransform::transformHeader(input_stream_.header, expression_), + getTraits(expression_)) , expression(std::move(expression_)) , default_totals(default_totals_) { + filterDistinctColumns(output_stream->header, output_stream->distinct_columns); + filterDistinctColumns(output_stream->header, output_stream->local_distinct_columns); } void InflatingExpressionStep::transformPipeline(QueryPipeline & pipeline) { + /// In case joined subquery has totals, and we don't, add default chunk to totals. + bool add_default_totals = false; + if (default_totals && !pipeline.hasTotals()) + { + pipeline.addDefaultTotals(); + add_default_totals = true; + } + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) { bool on_totals = stream_type == QueryPipeline::StreamType::Totals; - return std::make_shared(header, expression, on_totals, default_totals); + return std::make_shared(header, expression, on_totals, add_default_totals); }); } diff --git a/src/Processors/QueryPlan/ReadNothingStep.cpp b/src/Processors/QueryPlan/ReadNothingStep.cpp index 153fe60301fb..cdf1c248e578 100644 --- a/src/Processors/QueryPlan/ReadNothingStep.cpp +++ b/src/Processors/QueryPlan/ReadNothingStep.cpp @@ -5,8 +5,8 @@ namespace DB { -ReadNothingStep::ReadNothingStep(DataStream output_stream_) - : ISourceStep(std::move(output_stream_)) +ReadNothingStep::ReadNothingStep(Block output_header) + : ISourceStep(DataStream{.header = std::move(output_header)}) { } diff --git a/src/Processors/QueryPlan/ReadNothingStep.h b/src/Processors/QueryPlan/ReadNothingStep.h index b881e1fad29e..8580331f6b09 100644 --- a/src/Processors/QueryPlan/ReadNothingStep.h +++ b/src/Processors/QueryPlan/ReadNothingStep.h @@ -7,7 +7,7 @@ namespace DB class ReadNothingStep : public ISourceStep { public: - explicit ReadNothingStep(DataStream output_stream_); + explicit ReadNothingStep(Block output_header); String getName() const override { return "ReadNothing"; } From d79982f4973ccf8f0a9cc6198f23ec1f150494d6 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 19:10:47 +0300 Subject: [PATCH 0404/1102] Better locks in Storages --- src/Core/iostream_debug_helpers.cpp | 2 +- src/Core/iostream_debug_helpers.h | 3 - src/DataStreams/IBlockInputStream.h | 4 +- src/DataStreams/IBlockOutputStream.h | 4 +- .../PushingToViewsBlockOutputStream.cpp | 5 +- src/Databases/DatabaseMySQL.cpp | 2 +- src/Databases/DatabaseOnDisk.cpp | 2 +- src/Databases/IDatabase.h | 1 - src/Functions/FunctionJoinGet.cpp | 3 +- src/Functions/FunctionJoinGet.h | 4 +- src/Interpreters/InterpreterAlterQuery.cpp | 10 +-- src/Interpreters/InterpreterCreateQuery.cpp | 5 +- src/Interpreters/InterpreterDescribeQuery.cpp | 3 +- src/Interpreters/InterpreterDropQuery.cpp | 4 +- src/Interpreters/InterpreterInsertQuery.cpp | 3 +- src/Interpreters/InterpreterSelectQuery.cpp | 5 +- src/Interpreters/InterpreterSelectQuery.h | 2 +- src/Interpreters/MutationsInterpreter.cpp | 4 +- src/Interpreters/MutationsInterpreter.h | 4 +- src/Processors/Pipe.h | 6 +- src/Processors/QueryPipeline.h | 9 ++- src/Storages/IStorage.cpp | 37 +++------- src/Storages/IStorage.h | 41 +++-------- src/Storages/LiveView/StorageLiveView.cpp | 2 +- src/Storages/MergeTree/MergeTreeData.cpp | 6 +- src/Storages/MergeTree/MergeTreeData.h | 10 +-- .../MergeTree/MergeTreeDataMergerMutator.cpp | 6 +- .../MergeTree/MergeTreeDataMergerMutator.h | 4 +- .../ReplicatedMergeTreeCleanupThread.cpp | 4 +- .../ReplicatedMergeTreePartCheckThread.cpp | 3 +- src/Storages/ReadInOrderOptimizer.cpp | 4 +- src/Storages/StorageBuffer.cpp | 7 +- src/Storages/StorageBuffer.h | 2 +- src/Storages/StorageDistributed.cpp | 5 +- src/Storages/StorageDistributed.h | 4 +- src/Storages/StorageFile.cpp | 2 +- src/Storages/StorageFile.h | 2 +- src/Storages/StorageJoin.cpp | 2 +- src/Storages/StorageJoin.h | 2 +- src/Storages/StorageLog.cpp | 2 +- src/Storages/StorageLog.h | 2 +- src/Storages/StorageMaterializedView.cpp | 11 ++- src/Storages/StorageMaterializedView.h | 4 +- src/Storages/StorageMemory.cpp | 2 +- src/Storages/StorageMemory.h | 2 +- src/Storages/StorageMerge.cpp | 7 +- src/Storages/StorageMerge.h | 4 +- src/Storages/StorageMergeTree.cpp | 42 ++++------- src/Storages/StorageMergeTree.h | 4 +- src/Storages/StorageNull.cpp | 4 +- src/Storages/StorageNull.h | 2 +- src/Storages/StorageReplicatedMergeTree.cpp | 69 ++++++++----------- src/Storages/StorageReplicatedMergeTree.h | 6 +- src/Storages/StorageSet.cpp | 2 +- src/Storages/StorageSet.h | 2 +- src/Storages/StorageStripeLog.cpp | 2 +- src/Storages/StorageStripeLog.h | 2 +- src/Storages/StorageTinyLog.cpp | 2 +- src/Storages/StorageTinyLog.h | 2 +- src/Storages/System/StorageSystemColumns.cpp | 4 +- .../System/StorageSystemPartsBase.cpp | 2 +- src/Storages/System/StorageSystemPartsBase.h | 2 +- src/Storages/System/StorageSystemTables.cpp | 5 +- src/Storages/TableStructureLockHolder.h | 37 ++-------- 64 files changed, 163 insertions(+), 288 deletions(-) diff --git a/src/Core/iostream_debug_helpers.cpp b/src/Core/iostream_debug_helpers.cpp index 8683bb14db66..08477770c53a 100644 --- a/src/Core/iostream_debug_helpers.cpp +++ b/src/Core/iostream_debug_helpers.cpp @@ -53,7 +53,7 @@ std::ostream & operator<<(std::ostream & stream, const IStorage & what) return stream; } -std::ostream & operator<<(std::ostream & stream, const TableStructureReadLock &) +std::ostream & operator<<(std::ostream & stream, const TableLockHolder &) { stream << "TableStructureReadLock()"; return stream; diff --git a/src/Core/iostream_debug_helpers.h b/src/Core/iostream_debug_helpers.h index b9e5efa5d95d..8abffd4fe582 100644 --- a/src/Core/iostream_debug_helpers.h +++ b/src/Core/iostream_debug_helpers.h @@ -22,9 +22,6 @@ std::ostream & operator<<(std::ostream & stream, const IDataType & what); class IStorage; std::ostream & operator<<(std::ostream & stream, const IStorage & what); -class TableStructureReadLock; -std::ostream & operator<<(std::ostream & stream, const TableStructureReadLock & what); - class IFunctionOverloadResolver; std::ostream & operator<<(std::ostream & stream, const IFunctionOverloadResolver & what); diff --git a/src/DataStreams/IBlockInputStream.h b/src/DataStreams/IBlockInputStream.h index 66f3e68d6012..68850a822e80 100644 --- a/src/DataStreams/IBlockInputStream.h +++ b/src/DataStreams/IBlockInputStream.h @@ -109,7 +109,7 @@ class IBlockInputStream : public TypePromotion size_t checkDepth(size_t max_depth) const { return checkDepthImpl(max_depth, max_depth); } /// Do not allow to change the table while the blocks stream and its children are alive. - void addTableLock(const TableStructureReadLockHolder & lock) { table_locks.push_back(lock); } + void addTableLock(const TableLockHolder & lock) { table_locks.push_back(lock); } /// Get information about execution speed. const BlockStreamProfileInfo & getProfileInfo() const { return info; } @@ -229,7 +229,7 @@ class IBlockInputStream : public TypePromotion protected: /// Order is important: `table_locks` must be destroyed after `children` so that tables from /// which child streams read are protected by the locks during the lifetime of the child streams. - std::vector table_locks; + std::vector table_locks; BlockInputStreams children; std::shared_mutex children_mutex; diff --git a/src/DataStreams/IBlockOutputStream.h b/src/DataStreams/IBlockOutputStream.h index 060438ba4572..bb62d0183f98 100644 --- a/src/DataStreams/IBlockOutputStream.h +++ b/src/DataStreams/IBlockOutputStream.h @@ -61,10 +61,10 @@ class IBlockOutputStream : private boost::noncopyable /** Don't let to alter table while instance of stream is alive. */ - void addTableLock(const TableStructureReadLockHolder & lock) { table_locks.push_back(lock); } + void addTableLock(const TableLockHolder & lock) { table_locks.push_back(lock); } private: - std::vector table_locks; + std::vector table_locks; }; } diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index 72de6b889f17..2d2d678bff6f 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -33,7 +33,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( * but it's clear that here is not the best place for this functionality. */ addTableLock( - storage->lockStructureForShare(true, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout)); + storage->lockForShare(context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout)); /// If the "root" table deduplactes blocks, there are no need to make deduplication for children /// Moreover, deduplication for AggregatingMergeTree children could produce false positives due to low size of inserting blocks @@ -74,8 +74,7 @@ PushingToViewsBlockOutputStream::PushingToViewsBlockOutputStream( if (auto * materialized_view = dynamic_cast(dependent_table.get())) { addTableLock( - materialized_view->lockStructureForShare( - true, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout)); + materialized_view->lockForShare(context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout)); StoragePtr inner_table = materialized_view->getTargetTable(); auto inner_table_id = inner_table->getStorageID(); diff --git a/src/Databases/DatabaseMySQL.cpp b/src/Databases/DatabaseMySQL.cpp index a73fbafb7f51..6e5837257f09 100644 --- a/src/Databases/DatabaseMySQL.cpp +++ b/src/Databases/DatabaseMySQL.cpp @@ -362,7 +362,7 @@ void DatabaseMySQL::cleanOutdatedTables() ++iterator; else { - const auto table_lock = (*iterator)->lockAlterIntention(RWLockImpl::NO_QUERY, lock_acquire_timeout); + const auto table_lock = (*iterator)->lockExclusively(RWLockImpl::NO_QUERY, lock_acquire_timeout); (*iterator)->shutdown(); (*iterator)->is_dropped = true; diff --git a/src/Databases/DatabaseOnDisk.cpp b/src/Databases/DatabaseOnDisk.cpp index 364c9d50c485..0a16b6eacff9 100644 --- a/src/Databases/DatabaseOnDisk.cpp +++ b/src/Databases/DatabaseOnDisk.cpp @@ -266,7 +266,7 @@ void DatabaseOnDisk::renameTable( } auto table_data_relative_path = getTableDataPath(table_name); - TableStructureWriteLockHolder table_lock; + TableExclusiveLockHolder table_lock; String table_metadata_path; ASTPtr attach_query; /// DatabaseLazy::detachTable may return nullptr even if table exists, so we need tryGetTable for this case. diff --git a/src/Databases/IDatabase.h b/src/Databases/IDatabase.h index b9a7a907f734..3d8d5c74ceb2 100644 --- a/src/Databases/IDatabase.h +++ b/src/Databases/IDatabase.h @@ -22,7 +22,6 @@ class Context; struct Settings; struct ConstraintsDescription; struct IndicesDescription; -struct TableStructureWriteLockHolder; class ASTCreateQuery; using Dictionaries = std::vector; diff --git a/src/Functions/FunctionJoinGet.cpp b/src/Functions/FunctionJoinGet.cpp index a4569684e7f6..a33b70684a51 100644 --- a/src/Functions/FunctionJoinGet.cpp +++ b/src/Functions/FunctionJoinGet.cpp @@ -67,8 +67,7 @@ FunctionBaseImplPtr JoinGetOverloadResolver::build(const ColumnsWithTyp auto join = storage_join->getJoin(); DataTypes data_types(arguments.size()); - auto table_lock = storage_join->lockStructureForShare( - false, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto table_lock = storage_join->lockForShare(context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); for (size_t i = 0; i < arguments.size(); ++i) data_types[i] = arguments[i].type; diff --git a/src/Functions/FunctionJoinGet.h b/src/Functions/FunctionJoinGet.h index f233ccd8a4f2..af95686c2077 100644 --- a/src/Functions/FunctionJoinGet.h +++ b/src/Functions/FunctionJoinGet.h @@ -37,7 +37,7 @@ class FunctionJoinGet final : public IFunctionBaseImpl public: static constexpr auto name = or_null ? "joinGetOrNull" : "joinGet"; - FunctionJoinGet(TableStructureReadLockHolder table_lock_, StoragePtr storage_join_, + FunctionJoinGet(TableLockHolder table_lock_, StoragePtr storage_join_, HashJoinPtr join_, String attr_name_, DataTypes argument_types_, DataTypePtr return_type_) : table_lock(std::move(table_lock_)) @@ -57,7 +57,7 @@ class FunctionJoinGet final : public IFunctionBaseImpl ExecutableFunctionImplPtr prepare(const Block & sample_block, const ColumnNumbers & arguments, size_t result) const override; private: - TableStructureReadLockHolder table_lock; + TableLockHolder table_lock; StoragePtr storage_join; HashJoinPtr join; const String attr_name; diff --git a/src/Interpreters/InterpreterAlterQuery.cpp b/src/Interpreters/InterpreterAlterQuery.cpp index 958291d5882f..61277b8160ce 100644 --- a/src/Interpreters/InterpreterAlterQuery.cpp +++ b/src/Interpreters/InterpreterAlterQuery.cpp @@ -43,6 +43,7 @@ BlockIO InterpreterAlterQuery::execute() context.checkAccess(getRequiredAccess()); auto table_id = context.resolveStorageID(alter, Context::ResolveOrdinary); StoragePtr table = DatabaseCatalog::instance().getTable(table_id, context); + auto alter_lock = table->lockForAlter(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = table->getInMemoryMetadataPtr(); /// Add default database to table identifiers that we can encounter in e.g. default expressions, @@ -83,10 +84,7 @@ BlockIO InterpreterAlterQuery::execute() if (!mutation_commands.empty()) { - auto table_lock_holder = table->lockStructureForShare( - false /* because mutation is executed asyncronously */, - context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - MutationsInterpreter(table, metadata_snapshot, mutation_commands, context, false).validate(table_lock_holder); + MutationsInterpreter(table, metadata_snapshot, mutation_commands, context, false).validate(); table->mutate(mutation_commands, context); } @@ -112,13 +110,11 @@ BlockIO InterpreterAlterQuery::execute() if (!alter_commands.empty()) { - auto table_lock_holder = table->lockAlterIntention( - context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); StorageInMemoryMetadata metadata = table->getInMemoryMetadata(); alter_commands.validate(metadata, context); alter_commands.prepare(metadata); table->checkAlterIsPossible(alter_commands, context.getSettingsRef()); - table->alter(alter_commands, context, table_lock_holder); + table->alter(alter_commands, context, alter_lock); } return {}; diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index bb82c94a7641..3e09d728c4cc 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -405,7 +405,7 @@ ConstraintsDescription InterpreterCreateQuery::getConstraintsDescription(const A InterpreterCreateQuery::TableProperties InterpreterCreateQuery::setProperties(ASTCreateQuery & create) const { TableProperties properties; - TableStructureReadLockHolder as_storage_lock; + TableLockHolder as_storage_lock; if (create.columns_list) { @@ -428,8 +428,7 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::setProperties(AS StoragePtr as_storage = DatabaseCatalog::instance().getTable({as_database_name, create.as_table}, context); /// as_storage->getColumns() and setEngine(...) must be called under structure lock of other_table for CREATE ... AS other_table. - as_storage_lock = as_storage->lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + as_storage_lock = as_storage->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto as_storage_metadata = as_storage->getInMemoryMetadataPtr(); properties.columns = as_storage_metadata->getColumns(); diff --git a/src/Interpreters/InterpreterDescribeQuery.cpp b/src/Interpreters/InterpreterDescribeQuery.cpp index 535a4280b45e..94fa748ea156 100644 --- a/src/Interpreters/InterpreterDescribeQuery.cpp +++ b/src/Interpreters/InterpreterDescribeQuery.cpp @@ -89,8 +89,7 @@ BlockInputStreamPtr InterpreterDescribeQuery::executeImpl() table = DatabaseCatalog::instance().getTable(table_id, context); } - auto table_lock = table->lockStructureForShare( - false, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto table_lock = table->lockForShare(context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = table->getInMemoryMetadataPtr(); columns = metadata_snapshot->getColumns(); } diff --git a/src/Interpreters/InterpreterDropQuery.cpp b/src/Interpreters/InterpreterDropQuery.cpp index 15f19b585dec..e6853a8af4c7 100644 --- a/src/Interpreters/InterpreterDropQuery.cpp +++ b/src/Interpreters/InterpreterDropQuery.cpp @@ -93,7 +93,7 @@ BlockIO InterpreterDropQuery::executeToTable( { context.checkAccess(table->isView() ? AccessType::DROP_VIEW : AccessType::DROP_TABLE, table_id); table->shutdown(); - TableStructureWriteLockHolder table_lock; + TableExclusiveLockHolder table_lock; if (database->getEngineName() != "Atomic") table_lock = table->lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); /// Drop table from memory, don't touch data and metadata @@ -116,7 +116,7 @@ BlockIO InterpreterDropQuery::executeToTable( table->shutdown(); - TableStructureWriteLockHolder table_lock; + TableExclusiveLockHolder table_lock; if (database->getEngineName() != "Atomic") table_lock = table->lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index a39e89619701..554907d37d31 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -118,8 +118,7 @@ BlockIO InterpreterInsertQuery::execute() BlockIO res; StoragePtr table = getTable(query); - auto table_lock = table->lockStructureForShare( - true, context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto table_lock = table->lockForShare(context.getInitialQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = table->getInMemoryMetadataPtr(); auto query_sample_block = getSampleBlock(query, table, metadata_snapshot); diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index d832bcb7dc06..e0d5adf92b8e 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -255,8 +255,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( if (storage) { - table_lock = storage->lockStructureForShare( - false, context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout); + table_lock = storage->lockForShare(context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout); table_id = storage->getStorageID(); if (metadata_snapshot == nullptr) metadata_snapshot = storage->getInMemoryMetadataPtr(); @@ -277,7 +276,7 @@ InterpreterSelectQuery::InterpreterSelectQuery( { /// Rewritten with subquery. Free storage locks here. storage = {}; - table_lock.release(); + table_lock.reset(); table_id = StorageID::createEmpty(); } } diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 2f0faa2ba72c..e274175eb309 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -185,7 +185,7 @@ class InterpreterSelectQuery : public IInterpreter /// Table from where to read data, if not subquery. StoragePtr storage; StorageID table_id = StorageID::createEmpty(); /// Will be initialized if storage is not nullptr - TableStructureReadLockHolder table_lock; + TableLockHolder table_lock; /// Used when we read from prepared input, not table or subquery. BlockInputStreamPtr input; diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 3ad813a15b77..51b0cf924840 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -671,7 +671,7 @@ BlockInputStreamPtr MutationsInterpreter::addStreamsForLaterStages(const std::ve return in; } -void MutationsInterpreter::validate(TableStructureReadLockHolder &) +void MutationsInterpreter::validate() { const Settings & settings = context.getSettingsRef(); @@ -696,7 +696,7 @@ void MutationsInterpreter::validate(TableStructureReadLockHolder &) addStreamsForLaterStages(stages, in)->getHeader(); } -BlockInputStreamPtr MutationsInterpreter::execute(TableStructureReadLockHolder &) +BlockInputStreamPtr MutationsInterpreter::execute() { if (!can_execute) throw Exception("Cannot execute mutations interpreter because can_execute flag set to false", ErrorCodes::LOGICAL_ERROR); diff --git a/src/Interpreters/MutationsInterpreter.h b/src/Interpreters/MutationsInterpreter.h index 3c3175c18562..894d135a0995 100644 --- a/src/Interpreters/MutationsInterpreter.h +++ b/src/Interpreters/MutationsInterpreter.h @@ -32,12 +32,12 @@ class MutationsInterpreter const Context & context_, bool can_execute_); - void validate(TableStructureReadLockHolder & table_lock_holder); + void validate(); size_t evaluateCommandsSize(); /// The resulting stream will return blocks containing only changed columns and columns, that we need to recalculate indices. - BlockInputStreamPtr execute(TableStructureReadLockHolder & table_lock_holder); + BlockInputStreamPtr execute(); /// Only changed columns. const Block & getUpdatedHeader() const; diff --git a/src/Processors/Pipe.h b/src/Processors/Pipe.h index ec5514915a73..085016c3588d 100644 --- a/src/Processors/Pipe.h +++ b/src/Processors/Pipe.h @@ -62,12 +62,12 @@ class Pipe /// Do not allow to change the table while the processors of pipe are alive. /// TODO: move it to pipeline. - void addTableLock(const TableStructureReadLockHolder & lock) { table_locks.push_back(lock); } + void addTableLock(const TableLockHolder & lock) { table_locks.push_back(lock); } /// This methods are from QueryPipeline. Needed to make conversion from pipeline to pipe possible. void addInterpreterContext(std::shared_ptr context) { interpreter_context.emplace_back(std::move(context)); } void addStorageHolder(StoragePtr storage) { storage_holders.emplace_back(std::move(storage)); } - const std::vector & getTableLocks() const { return table_locks; } + const std::vector & getTableLocks() const { return table_locks; } const std::vector> & getContexts() const { return interpreter_context; } const std::vector & getStorageHolders() const { return storage_holders; } @@ -80,7 +80,7 @@ class Pipe /// It is the max number of processors which can be executed in parallel for each step. See QueryPipeline::Streams. size_t max_parallel_streams = 0; - std::vector table_locks; + std::vector table_locks; /// Some processors may implicitly use Context or temporary Storage created by Interpreter. /// But lifetime of Streams is not nested in lifetime of Interpreters, so we have to store it here, diff --git a/src/Processors/QueryPipeline.h b/src/Processors/QueryPipeline.h index 129b7f5ae3c9..9f632a7180a6 100644 --- a/src/Processors/QueryPipeline.h +++ b/src/Processors/QueryPipeline.h @@ -7,14 +7,13 @@ #include #include +#include namespace DB { -class TableStructureReadLock; -using TableStructureReadLockPtr = std::shared_ptr; -using TableStructureReadLocks = std::vector; +using TableLockHolders = std::vector; class Context; class IOutputFormat; @@ -146,7 +145,7 @@ class QueryPipeline const Block & getHeader() const { return current_header; } - void addTableLock(const TableStructureReadLockHolder & lock) { table_locks.push_back(lock); } + void addTableLock(const TableLockHolder & lock) { table_locks.push_back(lock); } void addInterpreterContext(std::shared_ptr context) { interpreter_context.emplace_back(std::move(context)); } void addStorageHolder(StoragePtr storage) { storage_holders.emplace_back(std::move(storage)); } @@ -180,7 +179,7 @@ class QueryPipeline /// because QueryPipeline is alive until query is finished. std::vector> interpreter_context; std::vector storage_holders; - TableStructureReadLocks table_locks; + TableLockHolders table_locks; /// Common header for each stream. Block current_header; diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 3a4559f94dcd..42224ec01aca 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -47,58 +47,43 @@ RWLockImpl::LockHolder IStorage::tryLockTimed( return lock_holder; } -TableStructureReadLockHolder IStorage::lockStructureForShare(bool will_add_new_data, const String & query_id, const SettingSeconds & acquire_timeout) +TableLockHolder IStorage::lockForShare(const String & query_id, const SettingSeconds & acquire_timeout) { - TableStructureReadLockHolder result; - if (will_add_new_data) - result.new_data_structure_lock = tryLockTimed(new_data_structure_lock, RWLockImpl::Read, query_id, acquire_timeout); - result.structure_lock = tryLockTimed(structure_lock, RWLockImpl::Read, query_id, acquire_timeout); + TableLockHolder result = tryLockTimed(drop_lock, RWLockImpl::Read, query_id, acquire_timeout); if (is_dropped) throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return result; } -TableStructureWriteLockHolder IStorage::lockAlterIntention(const String & query_id, const SettingSeconds & acquire_timeout) +TableLockHolder IStorage::lockForAlter(const String & query_id, const SettingSeconds & acquire_timeout) { - TableStructureWriteLockHolder result; - result.alter_intention_lock = tryLockTimed(alter_intention_lock, RWLockImpl::Write, query_id, acquire_timeout); + TableLockHolder result = tryLockTimed(alter_lock, RWLockImpl::Write, query_id, acquire_timeout); if (is_dropped) throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return result; } -void IStorage::lockStructureExclusively(TableStructureWriteLockHolder & lock_holder, const String & query_id, const SettingSeconds & acquire_timeout) -{ - if (!lock_holder.alter_intention_lock) - throw Exception("Alter intention lock for table " + getStorageID().getNameForLogs() + " was not taken. This is a bug.", ErrorCodes::LOGICAL_ERROR); - - if (!lock_holder.new_data_structure_lock) - lock_holder.new_data_structure_lock = tryLockTimed(new_data_structure_lock, RWLockImpl::Write, query_id, acquire_timeout); - lock_holder.structure_lock = tryLockTimed(structure_lock, RWLockImpl::Write, query_id, acquire_timeout); -} -TableStructureWriteLockHolder IStorage::lockExclusively(const String & query_id, const SettingSeconds & acquire_timeout) +TableExclusiveLockHolder IStorage::lockExclusively(const String & query_id, const SettingSeconds & acquire_timeout) { - TableStructureWriteLockHolder result; - result.alter_intention_lock = tryLockTimed(alter_intention_lock, RWLockImpl::Write, query_id, acquire_timeout); + TableExclusiveLockHolder result; + result.alter_lock = tryLockTimed(alter_lock, RWLockImpl::Write, query_id, acquire_timeout); if (is_dropped) throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); - result.new_data_structure_lock = tryLockTimed(new_data_structure_lock, RWLockImpl::Write, query_id, acquire_timeout); - result.structure_lock = tryLockTimed(structure_lock, RWLockImpl::Write, query_id, acquire_timeout); + result.drop_lock = tryLockTimed(drop_lock, RWLockImpl::Write, query_id, acquire_timeout); return result; } void IStorage::alter( - const AlterCommands & params, - const Context & context, - TableStructureWriteLockHolder & table_lock_holder) + const AlterCommands & params, const Context & context, TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); params.apply(new_metadata, context); diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 375ab90aee48..1309b727a74f 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -135,7 +135,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo using ColumnSizeByName = std::unordered_map; virtual ColumnSizeByName getColumnSizes() const { return {}; } -public: /// thread-unsafe part. lockStructure must be acquired +public: StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata.get(); } StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata.get(); } @@ -174,21 +174,11 @@ class IStorage : public std::enable_shared_from_this, public TypePromo const RWLock & rwlock, RWLockImpl::Type type, const String & query_id, const SettingSeconds & acquire_timeout) const; public: - /// Acquire this lock if you need the table structure to remain constant during the execution of - /// the query. If will_add_new_data is true, this means that the query will add new data to the table - /// (INSERT or a parts merge). - TableStructureReadLockHolder lockStructureForShare(bool will_add_new_data, const String & query_id, const SettingSeconds & acquire_timeout); - - /// Acquire this lock at the start of ALTER to lock out other ALTERs and make sure that only you - /// can modify the table structure. It can later be upgraded to the exclusive lock. - TableStructureWriteLockHolder lockAlterIntention(const String & query_id, const SettingSeconds & acquire_timeout); + TableLockHolder lockForShare(const String & query_id, const SettingSeconds & acquire_timeout); - /// Upgrade alter intention lock to the full exclusive structure lock. This is done by ALTER queries - /// to ensure that no other query uses the table structure and it can be safely changed. - void lockStructureExclusively(TableStructureWriteLockHolder & lock_holder, const String & query_id, const SettingSeconds & acquire_timeout); + TableLockHolder lockForAlter(const String & query_id, const SettingSeconds & acquire_timeout); - /// Acquire the full exclusive lock immediately. No other queries can run concurrently. - TableStructureWriteLockHolder lockExclusively(const String & query_id, const SettingSeconds & acquire_timeout); + TableExclusiveLockHolder lockExclusively(const String & query_id, const SettingSeconds & acquire_timeout); /** Returns stage to which query is going to be processed in read() function. * (Normally, the function only reads the columns from the list, but in other cases, @@ -297,7 +287,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo const ASTPtr & /*query*/, const StorageMetadataPtr & /* metadata_snapshot */, const Context & /* context */, - TableStructureWriteLockHolder &) + TableExclusiveLockHolder &) { throw Exception("Truncate is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } @@ -323,7 +313,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo * This method must fully execute the ALTER query, taking care of the locks itself. * To update the table metadata on disk, this method should call InterpreterAlterQuery::updateMetadata-> */ - virtual void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder); + virtual void alter(const AlterCommands & params, const Context & context, TableLockHolder & alter_lock_holder); /** Checks that alter commands can be applied to storage. For example, columns can be modified, * or primary key can be changes, etc. @@ -441,22 +431,9 @@ class IStorage : public std::enable_shared_from_this, public TypePromo } private: - /// You always need to take the next three locks in this order. - - /// If you hold this lock exclusively, you can be sure that no other structure modifying queries - /// (e.g. ALTER, DROP) are concurrently executing. But queries that only read table structure - /// (e.g. SELECT, INSERT) can continue to execute. - mutable RWLock alter_intention_lock = RWLockImpl::create(); - - /// It is taken for share for the entire INSERT query and the entire merge of the parts (for MergeTree). - /// ALTER COLUMN queries acquire an exclusive lock to ensure that no new parts with the old structure - /// are added to the table and thus the set of parts to modify doesn't change. - mutable RWLock new_data_structure_lock = RWLockImpl::create(); - - /// Lock for the table column structure (names, types, etc.) and data path. - /// It is taken in exclusive mode by queries that modify them (e.g. RENAME, ALTER and DROP) - /// and in share mode by other queries. - mutable RWLock structure_lock = RWLockImpl::create(); + mutable RWLock alter_lock = RWLockImpl::create(); + + mutable RWLock drop_lock = RWLockImpl::create(); }; } diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index ac6bd48f5347..0abb01d7dc70 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -514,7 +514,7 @@ void StorageLiveView::drop() void StorageLiveView::refresh(const Context & context) { - auto alter_lock = lockAlterIntention(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto alter_lock = lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); { std::lock_guard lock(mutex); if (getNewBlocks()) diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 115e0b78bf09..0ed4e98e864b 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -1436,7 +1436,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeData::createPart( void MergeTreeData::changeSettings( const ASTPtr & new_settings, - TableStructureWriteLockHolder & /* table_lock_holder */) + TableLockHolder & /* table_lock_holder */) { if (new_settings) { @@ -1481,7 +1481,7 @@ void MergeTreeData::changeSettings( } } -void MergeTreeData::freezeAll(const String & with_name, const Context & context, TableStructureReadLockHolder &) +void MergeTreeData::freezeAll(const String & with_name, const Context & context, TableLockHolder &) { freezePartitionsByMatcher([] (const DataPartPtr &){ return true; }, with_name, context); } @@ -2289,7 +2289,7 @@ void MergeTreeData::removePartContributionToColumnSizes(const DataPartPtr & part } -void MergeTreeData::freezePartition(const ASTPtr & partition_ast, const String & with_name, const Context & context, TableStructureReadLockHolder &) +void MergeTreeData::freezePartition(const ASTPtr & partition_ast, const String & with_name, const Context & context, TableLockHolder &) { std::optional prefix; String partition_id; diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index af6bee4936cf..ca6928cbb01e 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -477,7 +477,7 @@ class MergeTreeData : public IStorage /// Delete all directories which names begin with "tmp" /// Set non-negative parameter value to override MergeTreeSettings temporary_directories_lifetime - /// Must be called with locked lockStructureForShare(). + /// Must be called with locked lockForShare(). void clearOldTemporaryDirectories(ssize_t custom_directories_lifetime_seconds = -1); /// After the call to dropAllData() no method can be called. @@ -489,7 +489,7 @@ class MergeTreeData : public IStorage /// Moves the entire data directory. /// Flushes the uncompressed blocks cache and the marks cache. - /// Must be called with locked lockStructureForAlter(). + /// Must be called with locked lockForShare(). void rename(const String & new_table_path, const StorageID & new_table_id) override; /// Check if the ALTER can be performed: @@ -502,10 +502,10 @@ class MergeTreeData : public IStorage /// Change MergeTreeSettings void changeSettings( const ASTPtr & new_settings, - TableStructureWriteLockHolder & table_lock_holder); + TableLockHolder & table_lock_holder); /// Freezes all parts. - void freezeAll(const String & with_name, const Context & context, TableStructureReadLockHolder & table_lock_holder); + void freezeAll(const String & with_name, const Context & context, TableLockHolder & table_lock_holder); /// Should be called if part data is suspected to be corrupted. void reportBrokenPart(const String & name) const @@ -527,7 +527,7 @@ class MergeTreeData : public IStorage * Backup is created in directory clickhouse_dir/shadow/i/, where i - incremental number, * or if 'with_name' is specified - backup is created in directory with specified name. */ - void freezePartition(const ASTPtr & partition, const String & with_name, const Context & context, TableStructureReadLockHolder & table_lock_holder); + void freezePartition(const ASTPtr & partition, const String & with_name, const Context & context, TableLockHolder & table_lock_holder); public: diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 732c97c6dcc5..d52154002feb 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -581,7 +581,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor const FutureMergedMutatedPart & future_part, const StorageMetadataPtr & metadata_snapshot, MergeList::Entry & merge_entry, - TableStructureReadLockHolder &, + TableLockHolder &, time_t time_of_merge, const ReservationPtr & space_reservation, bool deduplicate, @@ -995,7 +995,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor time_t time_of_mutation, const Context & context, const ReservationPtr & space_reservation, - TableStructureReadLockHolder & table_lock_holder) + TableLockHolder &) { checkOperationIsNotCanceled(merge_entry); @@ -1046,7 +1046,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor if (!for_interpreter.empty()) { interpreter.emplace(storage_from_source_part, metadata_snapshot, for_interpreter, context_for_reading, true); - in = interpreter->execute(table_lock_holder); + in = interpreter->execute(); updated_header = interpreter->getUpdatedHeader(); in->setProgressCallback(MergeProgressCallback(merge_entry, watch_prev_elapsed, stage_progress)); } diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h index 7828f79ea332..d62587bef5f8 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h @@ -107,7 +107,7 @@ class MergeTreeDataMergerMutator const FutureMergedMutatedPart & future_part, const StorageMetadataPtr & metadata_snapshot, MergeListEntry & merge_entry, - TableStructureReadLockHolder & table_lock_holder, + TableLockHolder & table_lock_holder, time_t time_of_merge, const ReservationPtr & space_reservation, bool deduplicate, @@ -122,7 +122,7 @@ class MergeTreeDataMergerMutator time_t time_of_mutation, const Context & context, const ReservationPtr & space_reservation, - TableStructureReadLockHolder & table_lock_holder); + TableLockHolder & table_lock_holder); MergeTreeData::DataPartPtr renameMergedTemporaryPart( MergeTreeData::MutableDataPartPtr & new_data_part, diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp index 0870c0fdf724..f7fa957e9979 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp @@ -58,9 +58,7 @@ void ReplicatedMergeTreeCleanupThread::iterate() storage.clearOldPartsAndRemoveFromZK(); { - /// TODO: Implement tryLockStructureForShare. - auto lock = storage.lockStructureForShare( - false, RWLockImpl::NO_QUERY, storage.getSettings()->lock_acquire_timeout_for_background_operations); + auto lock = storage.lockForShare(RWLockImpl::NO_QUERY, storage.getSettings()->lock_acquire_timeout_for_background_operations); storage.clearOldTemporaryDirectories(); } diff --git a/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp b/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp index 0d824fa2dd81..75a3c4630618 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp @@ -201,8 +201,7 @@ CheckResult ReplicatedMergeTreePartCheckThread::checkPart(const String & part_na { auto zookeeper = storage.getZooKeeper(); - auto table_lock = storage.lockStructureForShare( - false, RWLockImpl::NO_QUERY, storage.getSettings()->lock_acquire_timeout_for_background_operations); + auto table_lock = storage.lockForShare(RWLockImpl::NO_QUERY, storage.getSettings()->lock_acquire_timeout_for_background_operations); auto local_part_header = ReplicatedMergeTreePartHeader::fromColumnsAndChecksums( part->getColumns(), part->checksums); diff --git a/src/Storages/ReadInOrderOptimizer.cpp b/src/Storages/ReadInOrderOptimizer.cpp index a6cc6211788b..bc220bc33ce7 100644 --- a/src/Storages/ReadInOrderOptimizer.cpp +++ b/src/Storages/ReadInOrderOptimizer.cpp @@ -33,13 +33,13 @@ ReadInOrderOptimizer::ReadInOrderOptimizer( InputOrderInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & storage, const StorageMetadataPtr & metadata_snapshot) const { Names sorting_key_columns; - if (const auto * merge_tree = dynamic_cast(storage.get())) + if (dynamic_cast(storage.get())) { if (!metadata_snapshot->hasSortingKey()) return {}; sorting_key_columns = metadata_snapshot->getSortingKeyColumns(); } - else if (const auto * part = dynamic_cast(storage.get())) + else if (dynamic_cast(storage.get())) { if (!metadata_snapshot->hasSortingKey()) return {}; diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 88619f5bc42b..b4d6b66ebe79 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -163,8 +163,7 @@ Pipes StorageBuffer::read( if (destination.get() == this) throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP); - auto destination_lock = destination->lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto destination_lock = destination->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto destination_metadata_snapshot = destination->getInMemoryMetadataPtr(); @@ -804,10 +803,8 @@ std::optional StorageBuffer::totalBytes() const return bytes; } -void StorageBuffer::alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) +void StorageBuffer::alter(const AlterCommands & params, const Context & context, TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - auto table_id = getStorageID(); checkAlterIsPossible(params, context.getSettingsRef()); auto metadata_snapshot = getInMemoryMetadataPtr(); diff --git a/src/Storages/StorageBuffer.h b/src/Storages/StorageBuffer.h index ceedbd25a0ca..e168f79293ec 100644 --- a/src/Storages/StorageBuffer.h +++ b/src/Storages/StorageBuffer.h @@ -89,7 +89,7 @@ friend class BufferBlockOutputStream; void checkAlterIsPossible(const AlterCommands & commands, const Settings & /* settings */) const override; /// The structure of the subordinate table is not checked and does not change. - void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; + void alter(const AlterCommands & params, const Context & context, TableLockHolder & table_lock_holder) override; std::optional totalRows() const override; std::optional totalBytes() const override; diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 2e07a393b046..9c20e3f8e118 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -564,9 +564,8 @@ void StorageDistributed::checkAlterIsPossible(const AlterCommands & commands, co } } -void StorageDistributed::alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) +void StorageDistributed::alter(const AlterCommands & params, const Context & context, TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); checkAlterIsPossible(params, context.getSettingsRef()); @@ -619,7 +618,7 @@ Strings StorageDistributed::getDataPaths() const return paths; } -void StorageDistributed::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageDistributed::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) { std::lock_guard lock(cluster_nodes_mutex); diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index c952ccde8acd..006f2bb580a0 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -82,7 +82,7 @@ class StorageDistributed final : public ext::shared_ptr_helper, public ISt const ASTPtr & /*query*/, const StorageMetadataPtr & /* metadata_snapshot */, const Context & /* context */, - TableStructureWriteLockHolder &) override; + TableExclusiveLockHolder &) override; void rename(const String & new_path_to_table_data, const StorageID & new_table_id) override; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 5000dcd8b186..21e4370c28ba 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -65,7 +65,7 @@ StorageJoin::StorageJoin( void StorageJoin::truncate( - const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) + const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder&) { Poco::File(path).remove(true); Poco::File(path).createDirectories(); diff --git a/src/Storages/StorageJoin.h b/src/Storages/StorageJoin.h index 4d4d1a81da24..fb8ffc1c3535 100644 --- a/src/Storages/StorageJoin.h +++ b/src/Storages/StorageJoin.h @@ -27,7 +27,7 @@ class StorageJoin final : public ext::shared_ptr_helper, public Sto public: String getName() const override { return "Join"; } - void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) override; /// Access the innards. HashJoinPtr & getJoin() { return join; } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index 45d55938db38..542fb507d83a 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -535,7 +535,7 @@ void StorageLog::rename(const String & new_path_to_table_data, const StorageID & renameInMemory(new_table_id); } -void StorageLog::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) +void StorageLog::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) { std::shared_lock lock(rwlock); diff --git a/src/Storages/StorageLog.h b/src/Storages/StorageLog.h index 670e2777d44c..d020f9066090 100644 --- a/src/Storages/StorageLog.h +++ b/src/Storages/StorageLog.h @@ -39,7 +39,7 @@ class StorageLog final : public ext::shared_ptr_helper, public IStor CheckResults checkData(const ASTPtr & /* query */, const Context & /* context */) override; - void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) override; Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 2c0d5727b310..976b3c80decd 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -116,8 +116,7 @@ Pipes StorageMaterializedView::read( const unsigned num_streams) { auto storage = getTargetTable(); - auto lock = storage->lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock = storage->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = storage->getInMemoryMetadataPtr(); if (query_info.order_optimizer) @@ -134,8 +133,7 @@ Pipes StorageMaterializedView::read( BlockOutputStreamPtr StorageMaterializedView::write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, const Context & context) { auto storage = getTargetTable(); - auto lock = storage->lockStructureForShare( - true, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock = storage->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto metadata_snapshot = storage->getInMemoryMetadataPtr(); auto stream = storage->write(query, metadata_snapshot, context); @@ -173,7 +171,7 @@ void StorageMaterializedView::drop() executeDropQuery(ASTDropQuery::Kind::Drop, global_context, target_table_id); } -void StorageMaterializedView::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageMaterializedView::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) { if (has_inner_table) executeDropQuery(ASTDropQuery::Kind::Truncate, global_context, target_table_id); @@ -204,9 +202,8 @@ bool StorageMaterializedView::optimize( void StorageMaterializedView::alter( const AlterCommands & params, const Context & context, - TableStructureWriteLockHolder & table_lock_holder) + TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); StorageInMemoryMetadata old_metadata = getInMemoryMetadata(); diff --git a/src/Storages/StorageMaterializedView.h b/src/Storages/StorageMaterializedView.h index e2111a15f5c4..f45d9203badf 100644 --- a/src/Storages/StorageMaterializedView.h +++ b/src/Storages/StorageMaterializedView.h @@ -37,7 +37,7 @@ class StorageMaterializedView final : public ext::shared_ptr_helper; void drop() override; - void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) override; std::optional totalRows() const override; std::optional totalBytes() const override; diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 235f78505e06..f4030ed573fe 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -333,7 +333,7 @@ StorageMerge::StorageListWithLocks StorageMerge::getSelectedTables(const String const auto & table = iterator->table(); if (table && table.get() != this) selected_tables.emplace_back( - table, table->lockStructureForShare(false, query_id, settings.lock_acquire_timeout), iterator->name()); + table, table->lockForShare(query_id, settings.lock_acquire_timeout), iterator->name()); iterator->next(); } @@ -362,7 +362,7 @@ StorageMerge::StorageListWithLocks StorageMerge::getSelectedTables( if (storage.get() != this) { selected_tables.emplace_back( - storage, storage->lockStructureForShare(false, query_id, settings.lock_acquire_timeout), iterator->name()); + storage, storage->lockForShare(query_id, settings.lock_acquire_timeout), iterator->name()); virtual_column->insert(iterator->name()); } @@ -405,9 +405,8 @@ void StorageMerge::checkAlterIsPossible(const AlterCommands & commands, const Se } void StorageMerge::alter( - const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) + const AlterCommands & params, const Context & context, TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata storage_metadata = getInMemoryMetadata(); diff --git a/src/Storages/StorageMerge.h b/src/Storages/StorageMerge.h index 1ad22869e397..f2af25d3f3d9 100644 --- a/src/Storages/StorageMerge.h +++ b/src/Storages/StorageMerge.h @@ -42,7 +42,7 @@ class StorageMerge final : public ext::shared_ptr_helper, public I /// you need to add and remove columns in the sub-tables manually /// the structure of sub-tables is not checked - void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; + void alter(const AlterCommands & params, const Context & context, TableLockHolder & table_lock_holder) override; bool mayBenefitFromIndexForIn( const ASTPtr & left_in_operand, const Context & query_context, const StorageMetadataPtr & metadata_snapshot) const override; @@ -52,7 +52,7 @@ class StorageMerge final : public ext::shared_ptr_helper, public I OptimizedRegularExpression table_name_regexp; Context global_context; - using StorageWithLockAndName = std::tuple; + using StorageWithLockAndName = std::tuple; using StorageListWithLocks = std::list; StorageListWithLocks getSelectedTables(const String & query_id, const Settings & settings) const; diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 45f8ecf0ef92..9042afe1b2d5 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -231,7 +231,7 @@ void StorageMergeTree::drop() dropAllData(); } -void StorageMergeTree::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageMergeTree::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) { { /// Asks to complete merges and does not allow them to start. @@ -254,7 +254,7 @@ void StorageMergeTree::truncate(const ASTPtr &, const StorageMetadataPtr &, cons void StorageMergeTree::alter( const AlterCommands & commands, const Context & context, - TableStructureWriteLockHolder & table_lock_holder) + TableLockHolder & table_lock_holder) { auto table_id = getStorageID(); @@ -268,8 +268,6 @@ void StorageMergeTree::alter( /// This alter can be performed at new_metadata level only if (commands.isSettingsAlter()) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - changeSettings(new_metadata.settings_changes, table_lock_holder); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); @@ -277,10 +275,6 @@ void StorageMergeTree::alter( else { { - /// TODO (relax this lock and remove this action lock) - auto merges_block = getActionLock(ActionLocks::PartsMerge); - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - changeSettings(new_metadata.settings_changes, table_lock_holder); /// Reinitialize primary key because primary key column types might have changed. setProperties(new_metadata, old_metadata); @@ -290,9 +284,6 @@ void StorageMergeTree::alter( if (!maybe_mutation_commands.empty()) mutation_version = startMutation(maybe_mutation_commands, mutation_file_name); - /// We release all locks except alter_intention_lock which allows - /// to execute alter queries sequentially - table_lock_holder.releaseAllExceptAlterIntention(); } /// Always execute required mutations synchronously, because alters @@ -591,8 +582,7 @@ bool StorageMergeTree::merge( bool deduplicate, String * out_disable_reason) { - auto table_lock_holder = lockStructureForShare( - true, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto table_lock_holder = lockForShare(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); auto metadata_snapshot = getInMemoryMetadataPtr(); FutureMergedMutatedPart future_part; @@ -740,8 +730,7 @@ BackgroundProcessingPoolTaskResult StorageMergeTree::movePartsTask() bool StorageMergeTree::tryMutatePart() { - auto table_lock_holder = lockStructureForShare( - true, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto table_lock_holder = lockForShare(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); size_t max_ast_elements = global_context.getSettingsRef().max_expanded_ast_elements; @@ -876,13 +865,8 @@ BackgroundProcessingPoolTaskResult StorageMergeTree::mergeMutateTask() /// Clear old parts. It is unnecessary to do it more than once a second. if (auto lock = time_after_previous_cleanup.compareAndRestartDeferred(1)) { - { - /// TODO: Implement tryLockStructureForShare. - auto lock_structure = lockStructureForShare( - false, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); - clearOldPartsFromFilesystem(); - clearOldTemporaryDirectories(); - } + clearOldPartsFromFilesystem(); + clearOldTemporaryDirectories(); clearOldMutations(); } @@ -1078,16 +1062,14 @@ void StorageMergeTree::alterPartition( case PartitionCommand::FREEZE_PARTITION: { - auto lock = lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock = lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); freezePartition(command.partition, command.with_name, context, lock); } break; case PartitionCommand::FREEZE_ALL_PARTITIONS: { - auto lock = lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock = lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); freezeAll(command.with_name, context, lock); } break; @@ -1156,8 +1138,8 @@ void StorageMergeTree::attachPartition( void StorageMergeTree::replacePartitionFrom(const StoragePtr & source_table, const ASTPtr & partition, bool replace, const Context & context) { - auto lock1 = lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - auto lock2 = source_table->lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock1 = lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock2 = source_table->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto source_metadata_snapshot = source_table->getInMemoryMetadataPtr(); auto my_metadata_snapshot = getInMemoryMetadataPtr(); @@ -1229,8 +1211,8 @@ void StorageMergeTree::replacePartitionFrom(const StoragePtr & source_table, con void StorageMergeTree::movePartitionToTable(const StoragePtr & dest_table, const ASTPtr & partition, const Context & context) { - auto lock1 = lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - auto lock2 = dest_table->lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock1 = lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock2 = dest_table->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto dest_table_storage = std::dynamic_pointer_cast(dest_table); if (!dest_table_storage) diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index cf3eccc0c0b4..9a45fd285dcc 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -75,9 +75,9 @@ class StorageMergeTree final : public ext::shared_ptr_helper, CancellationCode killMutation(const String & mutation_id) override; void drop() override; - void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) override; - void alter(const AlterCommands & commands, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; + void alter(const AlterCommands & commands, const Context & context, TableLockHolder & table_lock_holder) override; void checkTableCanBeDropped() const override; diff --git a/src/Storages/StorageNull.cpp b/src/Storages/StorageNull.cpp index 7589c4b44dcc..499f7329cd9b 100644 --- a/src/Storages/StorageNull.cpp +++ b/src/Storages/StorageNull.cpp @@ -45,10 +45,8 @@ void StorageNull::checkAlterIsPossible(const AlterCommands & commands, const Set } -void StorageNull::alter( - const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) +void StorageNull::alter(const AlterCommands & params, const Context & context, TableLockHolder &) { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata new_metadata = getInMemoryMetadata(); diff --git a/src/Storages/StorageNull.h b/src/Storages/StorageNull.h index 6bd102bdcdac..e79174c25654 100644 --- a/src/Storages/StorageNull.h +++ b/src/Storages/StorageNull.h @@ -44,7 +44,7 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt void checkAlterIsPossible(const AlterCommands & commands, const Settings & /* settings */) const override; - void alter(const AlterCommands & params, const Context & context, TableStructureWriteLockHolder & table_lock_holder) override; + void alter(const AlterCommands & params, const Context & context, TableLockHolder & table_lock_holder) override; std::optional totalRows() const override { diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 2e64c54112f6..b62f6bbd198e 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -1309,8 +1309,7 @@ bool StorageReplicatedMergeTree::tryExecuteMerge(const LogEntry & entry) ReservationPtr reserved_space = reserveSpacePreferringTTLRules(estimated_space_for_merge, ttl_infos, time(nullptr), max_volume_index); - auto table_lock = lockStructureForShare( - false, RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); + auto table_lock = lockForShare(RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); FutureMergedMutatedPart future_merged_part(parts, entry.new_part_type); @@ -1436,8 +1435,8 @@ bool StorageReplicatedMergeTree::tryExecutePartMutation(const StorageReplicatedM /// Can throw an exception. ReservationPtr reserved_space = reserveSpace(estimated_space_for_result, source_part->volume); - auto table_lock = lockStructureForShare( - false, RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); + auto table_lock = lockForShare( + RWLockImpl::NO_QUERY, storage_settings_ptr->lock_acquire_timeout_for_background_operations); StorageMetadataPtr metadata_snapshot = getInMemoryMetadataPtr(); MutableDataPartPtr new_part; @@ -1793,8 +1792,8 @@ bool StorageReplicatedMergeTree::executeReplaceRange(const LogEntry & entry) PartDescriptions parts_to_add; DataPartsVector parts_to_remove; - auto table_lock_holder_dst_table = lockStructureForShare( - false, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto table_lock_holder_dst_table = lockForShare( + RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); auto dst_metadata_snapshot = getInMemoryMetadataPtr(); for (size_t i = 0; i < entry_replace.new_part_names.size(); ++i) @@ -1833,7 +1832,7 @@ bool StorageReplicatedMergeTree::executeReplaceRange(const LogEntry & entry) } StoragePtr source_table; - TableStructureReadLockHolder table_lock_holder_src_table; + TableLockHolder table_lock_holder_src_table; StorageID source_table_id{entry_replace.from_database, entry_replace.from_table}; auto clone_data_parts_from_source_table = [&] () -> size_t @@ -1857,11 +1856,11 @@ bool StorageReplicatedMergeTree::executeReplaceRange(const LogEntry & entry) return 0; } - table_lock_holder_src_table = source_table->lockStructureForShare( - false, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + table_lock_holder_src_table = source_table->lockForShare( + RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); - DataPartStates valid_states{MergeTreeDataPartState::PreCommitted, MergeTreeDataPartState::Committed, - MergeTreeDataPartState::Outdated}; + DataPartStates valid_states{ + MergeTreeDataPartState::PreCommitted, MergeTreeDataPartState::Committed, MergeTreeDataPartState::Outdated}; size_t num_clonable_parts = 0; for (PartDescriptionPtr & part_desc : parts_to_add) @@ -3092,10 +3091,9 @@ bool StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin LOG_DEBUG(log, "Fetching part {} from {}", part_name, source_replica_path); - TableStructureReadLockHolder table_lock_holder; + TableLockHolder table_lock_holder; if (!to_detached) - table_lock_holder = lockStructureForShare( - true, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + table_lock_holder = lockForShare(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); /// Logging Stopwatch stopwatch; @@ -3636,10 +3634,7 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer zookeeper->multi(requests); { - /// TODO (relax this lock and remove this action locks) - auto merges_block = getActionLock(ActionLocks::PartsMerge); - auto fetchers_block = getActionLock(ActionLocks::PartsFetch); - auto table_lock = lockExclusively(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto alter_lock = lockForAlter(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); @@ -3658,7 +3653,7 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer void StorageReplicatedMergeTree::alter( - const AlterCommands & params, const Context & query_context, TableStructureWriteLockHolder & table_lock_holder) + const AlterCommands & params, const Context & query_context, TableLockHolder & table_lock_holder) { assertNotReadonly(); @@ -3666,8 +3661,6 @@ void StorageReplicatedMergeTree::alter( if (params.isSettingsAlter()) { - lockStructureExclusively( - table_lock_holder, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); /// We don't replicate storage_settings_ptr ALTER. It's local operation. /// Also we don't upgrade alter lock to table structure lock. StorageInMemoryMetadata future_metadata = getInMemoryMetadata(); @@ -3732,8 +3725,6 @@ void StorageReplicatedMergeTree::alter( if (ast_to_str(current_metadata->settings_changes) != ast_to_str(future_metadata.settings_changes)) { - lockStructureExclusively( - table_lock_holder, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); /// Just change settings StorageInMemoryMetadata metadata_copy = *current_metadata; metadata_copy.settings_changes = future_metadata.settings_changes; @@ -3824,7 +3815,7 @@ void StorageReplicatedMergeTree::alter( } - table_lock_holder.release(); + table_lock_holder.reset(); std::vector unwaited; if (query_context.getSettingsRef().replication_alter_partitions_sync == 2) @@ -3908,16 +3899,14 @@ void StorageReplicatedMergeTree::alterPartition( case PartitionCommand::FREEZE_PARTITION: { - auto lock = lockStructureForShare( - false, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); + auto lock = lockForShare(query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); freezePartition(command.partition, command.with_name, query_context, lock); } break; case PartitionCommand::FREEZE_ALL_PARTITIONS: { - auto lock = lockStructureForShare( - false, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); + auto lock = lockForShare(query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); freezeAll(command.with_name, query_context, lock); } break; @@ -4012,7 +4001,7 @@ void StorageReplicatedMergeTree::dropPartition(const ASTPtr & query, const ASTPt void StorageReplicatedMergeTree::truncate( - const ASTPtr & query, const StorageMetadataPtr &, const Context & query_context, TableStructureWriteLockHolder & table_lock) + const ASTPtr & query, const StorageMetadataPtr &, const Context & query_context, TableExclusiveLockHolder & table_lock) { table_lock.release(); /// Truncate is done asynchronously. @@ -4925,10 +4914,8 @@ CancellationCode StorageReplicatedMergeTree::killMutation(const String & mutatio void StorageReplicatedMergeTree::clearOldPartsAndRemoveFromZK() { - /// Critical section is not required (since grabOldParts() returns unique part set on each call) - - auto table_lock = lockStructureForShare( - false, RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto table_lock = lockForShare( + RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); auto zookeeper = getZooKeeper(); DataPartsVector parts = grabOldParts(); @@ -5219,8 +5206,8 @@ void StorageReplicatedMergeTree::replacePartitionFrom(const StoragePtr & source_ const Context & context) { /// First argument is true, because we possibly will add new data to current table. - auto lock1 = lockStructureForShare(true, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - auto lock2 = source_table->lockStructureForShare(false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock1 = lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto lock2 = source_table->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto source_metadata_snapshot = source_table->getInMemoryMetadataPtr(); auto metadata_snapshot = getInMemoryMetadataPtr(); @@ -5397,16 +5384,16 @@ void StorageReplicatedMergeTree::replacePartitionFrom(const StoragePtr & source_ /// If necessary, wait until the operation is performed on all replicas. if (context.getSettingsRef().replication_alter_partitions_sync > 1) { - lock2.release(); - lock1.release(); + lock2.reset(); + lock1.reset(); waitForAllReplicasToProcessLogEntry(entry); } } void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_table, const ASTPtr & partition, const Context & query_context) { - auto lock1 = lockStructureForShare(false, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); - auto lock2 = dest_table->lockStructureForShare(false, query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); + auto lock1 = lockForShare(query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); + auto lock2 = dest_table->lockForShare(query_context.getCurrentQueryId(), query_context.getSettingsRef().lock_acquire_timeout); auto dest_table_storage = std::dynamic_pointer_cast(dest_table); if (!dest_table_storage) @@ -5583,7 +5570,7 @@ void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_ta if (query_context.getSettingsRef().replication_alter_partitions_sync > 1) { - lock2.release(); + lock2.reset(); dest_table_storage->waitForAllReplicasToProcessLogEntry(entry); } @@ -5600,7 +5587,7 @@ void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_ta if (query_context.getSettingsRef().replication_alter_partitions_sync > 1) { - lock1.release(); + lock1.reset(); waitForAllReplicasToProcessLogEntry(entry_delete); } diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index c1ba737d849d..e340de88749e 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -103,7 +103,7 @@ class StorageReplicatedMergeTree final : public ext::shared_ptr_helperfinishInsert(); } size_t StorageSet::getSize() const { return set->getTotalRowCount(); } -void StorageSet::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) +void StorageSet::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) { Poco::File(path).remove(true); Poco::File(path).createDirectories(); diff --git a/src/Storages/StorageSet.h b/src/Storages/StorageSet.h index 2685fa26ba66..de7c65bbc3e9 100644 --- a/src/Storages/StorageSet.h +++ b/src/Storages/StorageSet.h @@ -67,7 +67,7 @@ friend struct ext::shared_ptr_helper; /// Access the insides. SetPtr & getSet() { return set; } - void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) override; private: SetPtr set; diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index 4b95a389f2c2..3086e9711216 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -326,7 +326,7 @@ CheckResults StorageStripeLog::checkData(const ASTPtr & /* query */, const Conte return file_checker.check(); } -void StorageStripeLog::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) +void StorageStripeLog::truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder &) { std::shared_lock lock(rwlock); diff --git a/src/Storages/StorageStripeLog.h b/src/Storages/StorageStripeLog.h index 381be7762df3..dfdf4c381b68 100644 --- a/src/Storages/StorageStripeLog.h +++ b/src/Storages/StorageStripeLog.h @@ -42,7 +42,7 @@ class StorageStripeLog final : public ext::shared_ptr_helper, Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } - void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr &, const Context &, TableExclusiveLockHolder&) override; protected: StorageStripeLog( diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 4beb44405d79..7a399f35c9c6 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -430,7 +430,7 @@ CheckResults StorageTinyLog::checkData(const ASTPtr & /* query */, const Context } void StorageTinyLog::truncate( - const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) + const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) { std::unique_lock lock(rwlock); diff --git a/src/Storages/StorageTinyLog.h b/src/Storages/StorageTinyLog.h index ae124e5e9580..60dacf6e1626 100644 --- a/src/Storages/StorageTinyLog.h +++ b/src/Storages/StorageTinyLog.h @@ -41,7 +41,7 @@ class StorageTinyLog final : public ext::shared_ptr_helper, publ Strings getDataPaths() const override { return {DB::fullPath(disk, table_path)}; } - void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableStructureWriteLockHolder &) override; + void truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, const Context &, TableExclusiveLockHolder &) override; void drop() override; diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 319ef257d6da..85d0f6797082 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -103,11 +103,11 @@ class ColumnsSource : public SourceWithProgress { StoragePtr storage = storages.at(std::make_pair(database_name, table_name)); - TableStructureReadLockHolder table_lock; + TableLockHolder table_lock; try { - table_lock = storage->lockStructureForShare(false, query_id, lock_acquire_timeout); + table_lock = storage->lockForShare(query_id, lock_acquire_timeout); } catch (const Exception & e) { diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index b998b60c02da..b48f8a3cb6b6 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -196,7 +196,7 @@ StoragesInfo StoragesInfoStream::next() try { /// For table not to be dropped and set of columns to remain constant. - info.table_lock = info.storage->lockStructureForShare(false, query_id, settings.lock_acquire_timeout); + info.table_lock = info.storage->lockForShare(query_id, settings.lock_acquire_timeout); } catch (const Exception & e) { diff --git a/src/Storages/System/StorageSystemPartsBase.h b/src/Storages/System/StorageSystemPartsBase.h index 8af1f46d8a7a..56c9a8fb0d00 100644 --- a/src/Storages/System/StorageSystemPartsBase.h +++ b/src/Storages/System/StorageSystemPartsBase.h @@ -14,7 +14,7 @@ class Context; struct StoragesInfo { StoragePtr storage = nullptr; - TableStructureReadLockHolder table_lock; + TableLockHolder table_lock; String database; String table; diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index df8df75ad6d9..b7f029945d8d 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -245,7 +245,7 @@ class TablesBlockSource : public SourceWithProgress continue; StoragePtr table = nullptr; - TableStructureReadLockHolder lock; + TableLockHolder lock; if (need_lock_structure) { @@ -257,8 +257,7 @@ class TablesBlockSource : public SourceWithProgress } try { - lock = table->lockStructureForShare( - false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + lock = table->lockForShare(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); } catch (const Exception & e) { diff --git a/src/Storages/TableStructureLockHolder.h b/src/Storages/TableStructureLockHolder.h index b5fc0c620add..946f9ee545c1 100644 --- a/src/Storages/TableStructureLockHolder.h +++ b/src/Storages/TableStructureLockHolder.h @@ -5,44 +5,17 @@ namespace DB { -/// Structs that hold table structure (columns, their types, default values etc.) locks when executing queries. -/// See IStorage::lock* methods for comments. - -struct TableStructureWriteLockHolder -{ - void release() - { - *this = TableStructureWriteLockHolder(); - } - - void releaseAllExceptAlterIntention() - { - new_data_structure_lock.reset(); - structure_lock.reset(); - } - -private: - friend class IStorage; - - /// Order is important. - RWLockImpl::LockHolder alter_intention_lock; - RWLockImpl::LockHolder new_data_structure_lock; - RWLockImpl::LockHolder structure_lock; -}; - -struct TableStructureReadLockHolder +struct TableExclusiveLockHolder { - void release() - { - *this = TableStructureReadLockHolder(); - } + void release() { *this = TableExclusiveLockHolder(); } private: friend class IStorage; /// Order is important. - RWLockImpl::LockHolder new_data_structure_lock; - RWLockImpl::LockHolder structure_lock; + RWLockImpl::LockHolder alter_lock; + RWLockImpl::LockHolder drop_lock; }; +using TableLockHolder = RWLockImpl::LockHolder; } From 54e5fe7dbc6336da462812549ae2cad1911cbf66 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 19:19:40 +0300 Subject: [PATCH 0405/1102] Less locks --- src/Storages/StorageMergeTree.cpp | 2 -- src/Storages/StorageReplicatedMergeTree.cpp | 14 +++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 9042afe1b2d5..16e921f52948 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -1086,8 +1086,6 @@ void StorageMergeTree::dropPartition(const ASTPtr & partition, bool detach, cons /// Asks to complete merges and does not allow them to start. /// This protects against "revival" of data for a removed partition after completion of merge. auto merge_blocker = merger_mutator.merges_blocker.cancel(); - /// Waits for completion of merge and does not start new ones. - auto lock = lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); String partition_id = getPartitionIDFromQuery(partition, context); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index b62f6bbd198e..c1eb8183a32f 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3633,17 +3633,13 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer zookeeper->multi(requests); - { - auto alter_lock = lockForAlter(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); - - LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); + LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); - auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); - setTableStructure(std::move(columns_from_entry), metadata_diff); - metadata_version = entry.alter_version; + auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); + setTableStructure(std::move(columns_from_entry), metadata_diff); + metadata_version = entry.alter_version; - LOG_INFO(log, "Applied changes to the metadata of the table. Current metadata version: {}", metadata_version); - } + LOG_INFO(log, "Applied changes to the metadata of the table. Current metadata version: {}", metadata_version); /// This transaction may not happen, but it's OK, because on the next retry we will eventually create/update this node zookeeper->createOrUpdate(replica_path + "/metadata_version", std::to_string(metadata_version), zkutil::CreateMode::Persistent); From b3ee8967dce4f9b6e9e99e447ff3b63a4b2d7e63 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 19:28:20 +0300 Subject: [PATCH 0406/1102] Fix style --- src/Storages/IStorage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 42224ec01aca..919464a6a5de 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -19,7 +19,6 @@ namespace DB namespace ErrorCodes { - extern const int LOGICAL_ERROR; extern const int TABLE_IS_DROPPED; extern const int NOT_IMPLEMENTED; extern const int DEADLOCK_AVOIDED; From e888dafdc25f9a498feefdc3eec2776c93731892 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 20:03:42 +0300 Subject: [PATCH 0407/1102] Remove unused method --- src/Storages/MergeTree/MergeTreeData.cpp | 11 ----------- src/Storages/MergeTree/MergeTreeData.h | 1 - src/Storages/StorageMergeTree.cpp | 1 - src/Storages/StorageReplicatedMergeTree.cpp | 7 +++---- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 0ed4e98e864b..779a6a7ebea4 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -183,9 +183,6 @@ MergeTreeData::MergeTreeData( throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); } - - setTTLExpressions(metadata_, metadata_); - /// format_file always contained on any data path PathWithDisk version_file; /// Creating directories, if not exist. @@ -516,14 +513,6 @@ void MergeTreeData::checkTTLExpressions(const StorageInMemoryMetadata & new_meta } } -/// Todo replace columns with TTL for columns -void MergeTreeData::setTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) -{ - checkTTLExpressions(new_metadata, old_metadata); - //setColumnTTLs(new_metadata.column_ttls_by_name); - //setTableTTLs(new_metadata.table_ttl); -} - void MergeTreeData::checkStoragePolicy(const StoragePolicyPtr & new_storage_policy) const { diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index ca6928cbb01e..2e6c0bfc9031 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -792,7 +792,6 @@ class MergeTreeData : public IStorage void initPartitionKey(const KeyDescription & new_partition_key); void checkTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) const; - void setTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata); void checkStoragePolicy(const StoragePolicyPtr & new_storage_policy) const; diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 16e921f52948..324c61ae419f 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -278,7 +278,6 @@ void StorageMergeTree::alter( changeSettings(new_metadata.settings_changes, table_lock_holder); /// Reinitialize primary key because primary key column types might have changed. setProperties(new_metadata, old_metadata); - setTTLExpressions(new_metadata, old_metadata); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index c1eb8183a32f..352e0cbe8020 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -816,13 +816,12 @@ void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_column } } - auto table_id = getStorageID(); - DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(global_context, table_id, new_metadata); - /// Even if the primary/sorting keys didn't change we must reinitialize it /// because primary key column types might have changed. setProperties(new_metadata, old_metadata); - setTTLExpressions(new_metadata, old_metadata); + + auto table_id = getStorageID(); + DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(global_context, table_id, new_metadata); } From c666763cc05048477fea6099c0b20280bc3946cd Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 20:09:06 +0300 Subject: [PATCH 0408/1102] Remove unused method better --- src/Storages/MergeTree/MergeTreeData.cpp | 3 +++ src/Storages/StorageMergeTree.cpp | 1 + src/Storages/StorageReplicatedMergeTree.cpp | 1 + 3 files changed, 5 insertions(+) diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 779a6a7ebea4..72937bd71027 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -183,6 +183,9 @@ MergeTreeData::MergeTreeData( throw Exception("Sampling expression must be present in the primary key", ErrorCodes::BAD_ARGUMENTS); } + + checkTTLExpressions(metadata_, metadata_); + /// format_file always contained on any data path PathWithDisk version_file; /// Creating directories, if not exist. diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index 324c61ae419f..1c497a6b62cc 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -277,6 +277,7 @@ void StorageMergeTree::alter( { changeSettings(new_metadata.settings_changes, table_lock_holder); /// Reinitialize primary key because primary key column types might have changed. + checkTTLExpressions(new_metadata, old_metadata); setProperties(new_metadata, old_metadata); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, new_metadata); diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 352e0cbe8020..30b2644fd04b 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -818,6 +818,7 @@ void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_column /// Even if the primary/sorting keys didn't change we must reinitialize it /// because primary key column types might have changed. + checkTTLExpressions(new_metadata, old_metadata); setProperties(new_metadata, old_metadata); auto table_id = getStorageID(); From 8b131e2079c07b8d5c46cc758055e46d8e029d61 Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 20:19:11 +0300 Subject: [PATCH 0409/1102] Remove int contention --- src/Storages/StorageReplicatedMergeTree.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 30b2644fd04b..c1534d9eed65 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3633,13 +3633,16 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer zookeeper->multi(requests); - LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); + { + auto alter_lock = lockExclusively(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); - auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); - setTableStructure(std::move(columns_from_entry), metadata_diff); - metadata_version = entry.alter_version; + auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); + setTableStructure(std::move(columns_from_entry), metadata_diff); + metadata_version = entry.alter_version; - LOG_INFO(log, "Applied changes to the metadata of the table. Current metadata version: {}", metadata_version); + LOG_INFO(log, "Applied changes to the metadata of the table. Current metadata version: {}", metadata_version); + } /// This transaction may not happen, but it's OK, because on the next retry we will eventually create/update this node zookeeper->createOrUpdate(replica_path + "/metadata_version", std::to_string(metadata_version), zkutil::CreateMode::Persistent); From 45adacf0bc0ff5db21e5b711a99202a87837027e Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 20:45:00 +0300 Subject: [PATCH 0410/1102] Use QueryPlan in InterpreterSelectQuery [part 2]. --- src/Interpreters/InterpreterSelectQuery.cpp | 251 +++++++++--------- src/Interpreters/InterpreterSelectQuery.h | 3 + .../InterpreterSelectWithUnionQuery.cpp | 61 ++--- .../InterpreterSelectWithUnionQuery.h | 7 +- src/Processors/QueryPipeline.cpp | 5 +- src/Processors/QueryPipeline.h | 2 +- src/Processors/QueryPlan/ConvertingStep.cpp | 48 ++++ src/Processors/QueryPlan/ConvertingStep.h | 20 ++ src/Processors/QueryPlan/MergeSortingStep.cpp | 2 +- src/Processors/QueryPlan/QueryPlan.cpp | 43 +++ src/Processors/QueryPlan/QueryPlan.h | 7 + src/Processors/QueryPlan/TotalsHavingStep.h | 1 - src/Processors/QueryPlan/UnionStep.cpp | 39 +++ src/Processors/QueryPlan/UnionStep.h | 22 ++ src/Processors/ya.make | 2 + 15 files changed, 341 insertions(+), 172 deletions(-) create mode 100644 src/Processors/QueryPlan/ConvertingStep.cpp create mode 100644 src/Processors/QueryPlan/ConvertingStep.h create mode 100644 src/Processors/QueryPlan/UnionStep.cpp create mode 100644 src/Processors/QueryPlan/UnionStep.h diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index a1f42018bbeb..7911372e5b7b 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -77,6 +77,7 @@ #include #include #include +#include namespace DB @@ -462,26 +463,29 @@ Block InterpreterSelectQuery::getSampleBlock() return result_header; } +void InterpreterSelectQuery::buildQueryPlan(QueryPlan & query_plan) +{ + executeImpl(query_plan, input, std::move(input_pipe)); + + /// We must guarantee that result structure is the same as in getSampleBlock() + if (!blocksHaveEqualStructure(query_plan.getCurrentDataStream().header, result_header)) + { + auto converting = std::make_unique(query_plan.getCurrentDataStream(), result_header); + query_plan.addStep(std::move(converting)); + } +} BlockIO InterpreterSelectQuery::execute() { BlockIO res; QueryPlan query_plan; - executeImpl(query_plan, input, std::move(input_pipe)); + + buildQueryPlan(query_plan); res.pipeline = std::move(*query_plan.buildQueryPipeline()); res.pipeline.addInterpreterContext(context); res.pipeline.addStorageHolder(storage); - /// We must guarantee that result structure is the same as in getSampleBlock() - if (!blocksHaveEqualStructure(res.pipeline.getHeader(), result_header)) - { - res.pipeline.addSimpleTransform([&](const Block & header) - { - return std::make_shared(header, result_header, ConvertingTransform::MatchColumnsMode::Name); - }); - } - return res; } @@ -1045,9 +1049,9 @@ void InterpreterSelectQuery::executeFetchColumns( {std::move(column), std::make_shared(func, argument_types, desc.parameters), desc.column_name}}; auto istream = std::make_shared(block_with_count); - ReadFromPreparedSource prepared_count(Pipe(std::make_shared(istream))); - prepared_count.setStepDescription("Optimized trivial count"); - prepared_count.initializePipeline(pipeline); + auto prepared_count = std::make_unique(Pipe(std::make_shared(istream))); + prepared_count->setStepDescription("Optimized trivial count"); + query_plan.addStep(std::move(prepared_count)); from_stage = QueryProcessingStage::WithMergeableState; analysis_result.first_stage = false; return; @@ -1241,7 +1245,7 @@ void InterpreterSelectQuery::executeFetchColumns( { is_remote = true; max_streams = settings.max_distributed_connections; - pipeline.setMaxThreads(max_streams); + query_plan.setMaxThreads(max_streams); } UInt64 max_block_size = settings.max_block_size; @@ -1266,14 +1270,14 @@ void InterpreterSelectQuery::executeFetchColumns( { max_block_size = std::max(UInt64(1), limit_length + limit_offset); max_streams = 1; - pipeline.setMaxThreads(max_streams); + query_plan.setMaxThreads(max_streams); } if (!max_block_size) throw Exception("Setting 'max_block_size' cannot be zero", ErrorCodes::PARAMETER_OUT_OF_BOUND); /// Initialize the initial data streams to which the query transforms are superimposed. Table or subquery or prepared input? - if (pipeline.initialized()) + if (query_plan.isInitialized()) { /// Prepared input. } @@ -1295,7 +1299,7 @@ void InterpreterSelectQuery::executeFetchColumns( interpreter_subquery->ignoreWithTotals(); } - pipeline = interpreter_subquery->execute().pipeline; + interpreter_subquery->buildQueryPlan(query_plan); } else if (storage) { @@ -1331,13 +1335,12 @@ void InterpreterSelectQuery::executeFetchColumns( query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); } - ReadFromStorageStep read_step( + auto read_step = std::make_unique( table_lock, options, storage, required_columns, query_info, *context, processing_stage, max_block_size, max_streams); - read_step.setStepDescription("Read from " + storage->getName()); - - pipeline = std::move(*read_step.updatePipeline({})); + read_step->setStepDescription("Read from " + storage->getName()); + query_plan.addStep(std::move(read_step)); } else throw Exception("Logical error in InterpreterSelectQuery: nowhere to read", ErrorCodes::LOGICAL_ERROR); @@ -1345,32 +1348,33 @@ void InterpreterSelectQuery::executeFetchColumns( /// Aliases in table declaration. if (processing_stage == QueryProcessingStage::FetchColumns && alias_actions) { - ExpressionStep table_aliases(DataStream{.header = pipeline.getHeader()}, alias_actions); - table_aliases.setStepDescription("Add table aliases"); - table_aliases.transformPipeline(pipeline); + auto table_aliases = std::make_unique(query_plan.getCurrentDataStream(), alias_actions); + table_aliases->setStepDescription("Add table aliases"); + query_plan.addStep(std::move(table_aliases)); } } -void InterpreterSelectQuery::executeWhere(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool remove_filter) +void InterpreterSelectQuery::executeWhere(QueryPlan & query_plan, const ExpressionActionsPtr & expression, bool remove_filter) { - FilterStep where_step( - DataStream{.header = pipeline.getHeader()}, + auto where_step = std::make_unique( + query_plan.getCurrentDataStream(), expression, getSelectQuery().where()->getColumnName(), remove_filter); - where_step.setStepDescription("WHERE"); - where_step.transformPipeline(pipeline); + where_step->setStepDescription("WHERE"); + query_plan.addStep(std::move(where_step)); } -void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info) +void InterpreterSelectQuery::executeAggregation(QueryPlan & query_plan, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info) { - ExpressionStep expression_before_aggregation(DataStream{.header = pipeline.getHeader()}, expression); - expression_before_aggregation.transformPipeline(pipeline); + auto expression_before_aggregation = std::make_unique(query_plan.getCurrentDataStream(), expression); + expression_before_aggregation->setStepDescription("Before GROUP BY"); + query_plan.addStep(std::move(expression_before_aggregation)); - Block header_before_aggregation = pipeline.getHeader(); + const auto & header_before_aggregation = query_plan.getCurrentDataStream().header; ColumnNumbers keys; for (const auto & key : query_analyzer->aggregationKeys()) keys.push_back(header_before_aggregation.getPositionByName(key.name)); @@ -1412,8 +1416,8 @@ void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const bool storage_has_evenly_distributed_read = storage && storage->hasEvenlyDistributedRead(); - AggregatingStep aggregating_step( - DataStream{.header = pipeline.getHeader()}, + auto aggregating_step = std::make_unique( + query_plan.getCurrentDataStream(), std::move(transform_params), settings.max_block_size, merge_threads, @@ -1422,13 +1426,13 @@ void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const std::move(group_by_info), std::move(group_by_sort_description)); - aggregating_step.transformPipeline(pipeline); + query_plan.addStep(std::move(aggregating_step)); } -void InterpreterSelectQuery::executeMergeAggregated(QueryPipeline & pipeline, bool overflow_row, bool final) +void InterpreterSelectQuery::executeMergeAggregated(QueryPlan & query_plan, bool overflow_row, bool final) { - Block header_before_merge = pipeline.getHeader(); + const auto & header_before_merge = query_plan.getCurrentDataStream().header; ColumnNumbers keys; for (const auto & key : query_analyzer->aggregationKeys()) @@ -1455,47 +1459,45 @@ void InterpreterSelectQuery::executeMergeAggregated(QueryPipeline & pipeline, bo auto transform_params = std::make_shared(params, final); - MergingAggregatedStep merging_aggregated( - DataStream{.header = pipeline.getHeader()}, + auto merging_aggregated = std::make_unique( + query_plan.getCurrentDataStream(), std::move(transform_params), settings.distributed_aggregation_memory_efficient, settings.max_threads, settings.aggregation_memory_efficient_merge_threads); - merging_aggregated.transformPipeline(pipeline); + query_plan.addStep(std::move(merging_aggregated)); } -void InterpreterSelectQuery::executeHaving(QueryPipeline & pipeline, const ExpressionActionsPtr & expression) +void InterpreterSelectQuery::executeHaving(QueryPlan & query_plan, const ExpressionActionsPtr & expression) { - FilterStep having_step( - DataStream{.header = pipeline.getHeader()}, + auto having_step = std::make_unique( + query_plan.getCurrentDataStream(), expression, getSelectQuery().having()->getColumnName(), false); - having_step.setStepDescription("HAVING"); - having_step.transformPipeline(pipeline); + having_step->setStepDescription("HAVING"); + query_plan.addStep(std::move(having_step)); } -void InterpreterSelectQuery::executeTotalsAndHaving(QueryPipeline & pipeline, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final) +void InterpreterSelectQuery::executeTotalsAndHaving(QueryPlan & query_plan, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final) { const Settings & settings = context->getSettingsRef(); - TotalsHavingStep totals_having_step( - DataStream{.header = pipeline.getHeader()}, + auto totals_having_step = std::make_unique( + query_plan.getCurrentDataStream(), overflow_row, expression, has_having ? getSelectQuery().having()->getColumnName() : "", settings.totals_mode, settings.totals_auto_threshold, final); - totals_having_step.transformPipeline(pipeline); + query_plan.addStep(std::move(totals_having_step)); } -void InterpreterSelectQuery::executeRollupOrCube(QueryPipeline & pipeline, Modificator modificator) +void InterpreterSelectQuery::executeRollupOrCube(QueryPlan & query_plan, Modificator modificator) { - pipeline.resize(1); - - Block header_before_transform = pipeline.getHeader(); + const auto & header_before_transform = query_plan.getCurrentDataStream().header; ColumnNumbers keys; @@ -1512,45 +1514,40 @@ void InterpreterSelectQuery::executeRollupOrCube(QueryPipeline & pipeline, Modif auto transform_params = std::make_shared(params, true); + QueryPlanStepPtr step; if (modificator == Modificator::ROLLUP) - { - RollupStep rollup_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); - rollup_step.transformPipeline(pipeline); - } + step = std::make_unique(query_plan.getCurrentDataStream(), std::move(transform_params)); else - { - CubeStep cube_step(DataStream{.header = pipeline.getHeader()}, std::move(transform_params)); - cube_step.transformPipeline(pipeline); - } + step = std::make_unique(query_plan.getCurrentDataStream(), std::move(transform_params)); + + query_plan.addStep(std::move(step)); } -void InterpreterSelectQuery::executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, const std::string & description) +void InterpreterSelectQuery::executeExpression(QueryPlan & query_plan, const ExpressionActionsPtr & expression, const std::string & description) { - ExpressionStep expression_step( - DataStream{.header = pipeline.getHeader()}, - expression); + auto expression_step = std::make_unique(query_plan.getCurrentDataStream(), expression); - expression_step.setStepDescription(description); - expression_step.transformPipeline(pipeline); + expression_step->setStepDescription(description); + query_plan.addStep(std::move(expression_step)); } -void InterpreterSelectQuery::executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr input_sorting_info, UInt64 limit, SortDescription & output_order_descr) +void InterpreterSelectQuery::executeOrderOptimized(QueryPlan & query_plan, InputOrderInfoPtr input_sorting_info, UInt64 limit, SortDescription & output_order_descr) { const Settings & settings = context->getSettingsRef(); - FinishSortingStep finish_sorting_step( - DataStream{.header = pipeline.getHeader()}, + auto finish_sorting_step = std::make_unique( + query_plan.getCurrentDataStream(), input_sorting_info->order_key_prefix_descr, output_order_descr, settings.max_block_size, limit); - finish_sorting_step.transformPipeline(pipeline); + query_plan.addStep(std::move(finish_sorting_step)); } -void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr input_sorting_info) +void InterpreterSelectQuery::executeOrder(QueryPlan & query_plan, InputOrderInfoPtr input_sorting_info) { auto & query = getSelectQuery(); SortDescription output_order_descr = getSortDescription(query, *context); @@ -1564,69 +1561,69 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderIn * and then merge them into one sorted stream. * At this stage we merge per-thread streams into one. */ - executeOrderOptimized(pipeline, input_sorting_info, limit, output_order_descr); + executeOrderOptimized(query_plan, input_sorting_info, limit, output_order_descr); return; } const Settings & settings = context->getSettingsRef(); - PartialSortingStep partial_sorting( - DataStream{.header = pipeline.getHeader()}, + auto partial_sorting = std::make_unique( + query_plan.getCurrentDataStream(), output_order_descr, limit, SizeLimits(settings.max_rows_to_sort, settings.max_bytes_to_sort, settings.sort_overflow_mode)); - partial_sorting.setStepDescription("Sort each block before ORDER BY"); - partial_sorting.transformPipeline(pipeline); + partial_sorting->setStepDescription("Sort each block before ORDER BY"); + query_plan.addStep(std::move(partial_sorting)); /// Merge the sorted blocks. - MergeSortingStep merge_sorting_step( - DataStream{.header = pipeline.getHeader()}, + auto merge_sorting_step = std::make_unique( + query_plan.getCurrentDataStream(), output_order_descr, settings.max_block_size, limit, - settings.max_bytes_before_remerge_sort / pipeline.getNumStreams(), + settings.max_bytes_before_remerge_sort, settings.max_bytes_before_external_sort, context->getTemporaryVolume(), settings.min_free_disk_space_for_temporary_data); - merge_sorting_step.setStepDescription("Merge sorted blocks before ORDER BY"); - merge_sorting_step.transformPipeline(pipeline); + merge_sorting_step->setStepDescription("Merge sorted blocks before ORDER BY"); + query_plan.addStep(std::move(merge_sorting_step)); /// If there are several streams, we merge them into one - executeMergeSorted(pipeline, output_order_descr, limit, "before ORDER BY"); + executeMergeSorted(query_plan, output_order_descr, limit, "before ORDER BY"); } -void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const std::string & description) +void InterpreterSelectQuery::executeMergeSorted(QueryPlan & query_plan, const std::string & description) { auto & query = getSelectQuery(); SortDescription order_descr = getSortDescription(query, *context); UInt64 limit = getLimitForSorting(query, *context); - executeMergeSorted(pipeline, order_descr, limit, description); + executeMergeSorted(query_plan, order_descr, limit, description); } -void InterpreterSelectQuery::executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit, const std::string & description) +void InterpreterSelectQuery::executeMergeSorted(QueryPlan & query_plan, const SortDescription & sort_description, UInt64 limit, const std::string & description) { const Settings & settings = context->getSettingsRef(); - MergingSortedStep merging_sorted( - DataStream{.header = pipeline.getHeader()}, + auto merging_sorted = std::make_unique( + query_plan.getCurrentDataStream(), sort_description, settings.max_block_size, limit); - merging_sorted.setStepDescription("Merge sorted streams " + description); - merging_sorted.transformPipeline(pipeline); + merging_sorted->setStepDescription("Merge sorted streams " + description); + query_plan.addStep(std::move(merging_sorted)); } -void InterpreterSelectQuery::executeProjection(QueryPipeline & pipeline, const ExpressionActionsPtr & expression) +void InterpreterSelectQuery::executeProjection(QueryPlan & query_plan, const ExpressionActionsPtr & expression) { - ExpressionStep projection_step(DataStream{.header = pipeline.getHeader()}, expression); - projection_step.setStepDescription("Projection"); - projection_step.transformPipeline(pipeline); + auto projection_step = std::make_unique(query_plan.getCurrentDataStream(), expression); + projection_step->setStepDescription("Projection"); + query_plan.addStep(std::move(projection_step)); } -void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool before_order, Names columns, bool pre_distinct) +void InterpreterSelectQuery::executeDistinct(QueryPlan & query_plan, bool before_order, Names columns, bool pre_distinct) { auto & query = getSelectQuery(); if (query.distinct) @@ -1642,20 +1639,20 @@ void InterpreterSelectQuery::executeDistinct(QueryPipeline & pipeline, bool befo SizeLimits limits(settings.max_rows_in_distinct, settings.max_bytes_in_distinct, settings.distinct_overflow_mode); - DistinctStep distinct_step( - DataStream{.header = pipeline.getHeader()}, + auto distinct_step = std::make_unique( + query_plan.getCurrentDataStream(), limits, limit_for_distinct, columns, pre_distinct); if (pre_distinct) - distinct_step.setStepDescription("Preliminary DISTINCT"); + distinct_step->setStepDescription("Preliminary DISTINCT"); - distinct_step.transformPipeline(pipeline); + query_plan.addStep(std::move(distinct_step)); } } /// Preliminary LIMIT - is used in every source, if there are several sources, before they are combined. -void InterpreterSelectQuery::executePreLimit(QueryPipeline & pipeline, bool do_not_skip_offset) +void InterpreterSelectQuery::executePreLimit(QueryPlan & query_plan, bool do_not_skip_offset) { auto & query = getSelectQuery(); /// If there is LIMIT @@ -1669,14 +1666,14 @@ void InterpreterSelectQuery::executePreLimit(QueryPipeline & pipeline, bool do_n limit_offset = 0; } - LimitStep limit(DataStream{.header = pipeline.getHeader()}, limit_length, limit_offset); - limit.setStepDescription("preliminary LIMIT"); - limit.transformPipeline(pipeline); + auto limit = std::make_unique(query_plan.getCurrentDataStream(), limit_length, limit_offset); + limit->setStepDescription("preliminary LIMIT"); + query_plan.addStep(std::move(limit)); } } -void InterpreterSelectQuery::executeLimitBy(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeLimitBy(QueryPlan & query_plan) { auto & query = getSelectQuery(); if (!query.limitByLength() || !query.limitBy()) @@ -1689,8 +1686,8 @@ void InterpreterSelectQuery::executeLimitBy(QueryPipeline & pipeline) UInt64 length = getLimitUIntValue(query.limitByLength(), *context, "LIMIT"); UInt64 offset = (query.limitByOffset() ? getLimitUIntValue(query.limitByOffset(), *context, "OFFSET") : 0); - LimitByStep limit_by(DataStream{.header = pipeline.getHeader()}, length, offset, columns); - limit_by.transformPipeline(pipeline); + auto limit_by = std::make_unique(query_plan.getCurrentDataStream(), length, offset, columns); + query_plan.addStep(std::move(limit_by)); } @@ -1719,7 +1716,7 @@ namespace } } -void InterpreterSelectQuery::executeWithFill(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeWithFill(QueryPlan & query_plan) { auto & query = getSelectQuery(); if (query.orderBy()) @@ -1735,13 +1732,13 @@ void InterpreterSelectQuery::executeWithFill(QueryPipeline & pipeline) if (fill_descr.empty()) return; - FillingStep filling_step(DataStream{.header = pipeline.getHeader()}, std::move(fill_descr)); - filling_step.transformPipeline(pipeline); + auto filling_step = std::make_unique(query_plan.getCurrentDataStream(), std::move(fill_descr)); + query_plan.addStep(std::move(filling_step)); } } -void InterpreterSelectQuery::executeLimit(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeLimit(QueryPlan & query_plan) { auto & query = getSelectQuery(); /// If there is LIMIT @@ -1776,19 +1773,19 @@ void InterpreterSelectQuery::executeLimit(QueryPipeline & pipeline) order_descr = getSortDescription(query, *context); } - LimitStep limit( - DataStream{.header = pipeline.getHeader()}, + auto limit = std::make_unique( + query_plan.getCurrentDataStream(), limit_length, limit_offset, always_read_till_end, query.limit_with_ties, order_descr); if (query.limit_with_ties) - limit.setStepDescription("LIMIT WITH TIES"); + limit->setStepDescription("LIMIT WITH TIES"); - limit.transformPipeline(pipeline); + query_plan.addStep(std::move(limit)); } } -void InterpreterSelectQuery::executeOffset(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeOffset(QueryPlan & query_plan) { auto & query = getSelectQuery(); /// If there is not a LIMIT but an offset @@ -1798,35 +1795,35 @@ void InterpreterSelectQuery::executeOffset(QueryPipeline & pipeline) UInt64 limit_offset; std::tie(limit_length, limit_offset) = getLimitLengthAndOffset(query, *context); - OffsetsStep offsets_step(DataStream{.header = pipeline.getHeader()}, limit_offset); - offsets_step.transformPipeline(pipeline); + auto offsets_step = std::make_unique(query_plan.getCurrentDataStream(), limit_offset); + query_plan.addStep(std::move(offsets_step)); } } -void InterpreterSelectQuery::executeExtremes(QueryPipeline & pipeline) +void InterpreterSelectQuery::executeExtremes(QueryPlan & query_plan) { if (!context->getSettingsRef().extremes) return; - ExtremesStep extremes_step(DataStream{.header = pipeline.getHeader()}); - extremes_step.transformPipeline(pipeline); + auto extremes_step = std::make_unique(query_plan.getCurrentDataStream()); + query_plan.addStep(std::move(extremes_step)); } -void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const SubqueriesForSets & subqueries_for_sets) +void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPlan & query_plan, const SubqueriesForSets & subqueries_for_sets) { if (query_info.input_order_info) - executeMergeSorted(pipeline, query_info.input_order_info->order_key_prefix_descr, 0, "before creating sets for subqueries and joins"); + executeMergeSorted(query_plan, query_info.input_order_info->order_key_prefix_descr, 0, "before creating sets for subqueries and joins"); const Settings & settings = context->getSettingsRef(); - CreatingSetsStep creating_sets( - DataStream{.header = pipeline.getHeader()}, + auto creating_sets = std::make_unique( + query_plan.getCurrentDataStream(), subqueries_for_sets, SizeLimits(settings.max_rows_to_transfer, settings.max_bytes_to_transfer, settings.transfer_overflow_mode), *context); - creating_sets.setStepDescription("Create sets for subqueries and joins"); - creating_sets.transformPipeline(pipeline); + creating_sets->setStepDescription("Create sets for subqueries and joins"); + query_plan.addStep(std::move(creating_sets)); } diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index 7ab266b52eb1..fdf6176cb243 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -77,6 +77,9 @@ class InterpreterSelectQuery : public IInterpreter /// Execute a query. Get the stream of blocks to read. BlockIO execute() override; + /// Builds QueryPlan for current query. + void buildQueryPlan(QueryPlan & query_plan); + bool ignoreLimits() const override { return options.ignore_limits; } bool ignoreQuota() const override { return options.ignore_quota; } diff --git a/src/Interpreters/InterpreterSelectWithUnionQuery.cpp b/src/Interpreters/InterpreterSelectWithUnionQuery.cpp index 7b86616555a3..6a922bebc6e4 100644 --- a/src/Interpreters/InterpreterSelectWithUnionQuery.cpp +++ b/src/Interpreters/InterpreterSelectWithUnionQuery.cpp @@ -6,10 +6,9 @@ #include #include #include - -#include -#include -#include +#include +#include +#include namespace DB @@ -173,47 +172,35 @@ Block InterpreterSelectWithUnionQuery::getSampleBlock( return cache[key] = InterpreterSelectWithUnionQuery(query_ptr, context, SelectQueryOptions().analyze()).getSampleBlock(); } - -BlockIO InterpreterSelectWithUnionQuery::execute() +void InterpreterSelectWithUnionQuery::buildQueryPlan(QueryPlan & query_plan) { - BlockIO res; - QueryPipeline & main_pipeline = res.pipeline; - std::vector pipelines; - bool has_main_pipeline = false; - - Blocks headers; - headers.reserve(nested_interpreters.size()); + size_t num_plans = nested_interpreters.size(); + std::vector plans(num_plans); + DataStreams data_streams(num_plans); - for (auto & interpreter : nested_interpreters) + for (size_t i = 0; i < num_plans; ++i) { - if (!has_main_pipeline) - { - has_main_pipeline = true; - main_pipeline = interpreter->execute().pipeline; - headers.emplace_back(main_pipeline.getHeader()); - } - else - { - pipelines.emplace_back(interpreter->execute().pipeline); - headers.emplace_back(pipelines.back().getHeader()); - } + nested_interpreters[i]->buildQueryPlan(plans[i]); + data_streams[i] = plans[i].getCurrentDataStream(); } - if (!has_main_pipeline) - main_pipeline.init(Pipe(std::make_shared(getSampleBlock()))); + auto max_threads = context->getSettingsRef().max_threads; + auto union_step = std::make_unique(std::move(data_streams), result_header, max_threads); - if (!pipelines.empty()) - { - auto common_header = getCommonHeaderForUnion(headers); - main_pipeline.unitePipelines(std::move(pipelines), common_header); + query_plan.unitePlans(std::move(union_step), std::move(plans)); +} - // nested queries can force 1 thread (due to simplicity) - // but in case of union this cannot be done. - UInt64 max_threads = context->getSettingsRef().max_threads; - main_pipeline.setMaxThreads(std::min(nested_interpreters.size(), max_threads)); - } +BlockIO InterpreterSelectWithUnionQuery::execute() +{ + BlockIO res; + + QueryPlan query_plan; + buildQueryPlan(query_plan); + + auto pipeline = query_plan.buildQueryPipeline(); - main_pipeline.addInterpreterContext(context); + res.pipeline = std::move(*pipeline); + res.pipeline.addInterpreterContext(context); return res; } diff --git a/src/Interpreters/InterpreterSelectWithUnionQuery.h b/src/Interpreters/InterpreterSelectWithUnionQuery.h index 3b5fe533a84d..5590066a4db4 100644 --- a/src/Interpreters/InterpreterSelectWithUnionQuery.h +++ b/src/Interpreters/InterpreterSelectWithUnionQuery.h @@ -5,14 +5,12 @@ #include #include -#include - namespace DB { class Context; class InterpreterSelectQuery; - +class QueryPlan; /** Interprets one or multiple SELECT queries inside UNION ALL chain. */ @@ -27,6 +25,9 @@ class InterpreterSelectWithUnionQuery : public IInterpreter ~InterpreterSelectWithUnionQuery() override; + /// Builds QueryPlan for current query. + void buildQueryPlan(QueryPlan & query_plan); + BlockIO execute() override; bool ignoreLimits() const override { return options.ignore_limits; } diff --git a/src/Processors/QueryPipeline.cpp b/src/Processors/QueryPipeline.cpp index 5b6109440d51..a4e4ab2595e3 100644 --- a/src/Processors/QueryPipeline.cpp +++ b/src/Processors/QueryPipeline.cpp @@ -563,7 +563,7 @@ void QueryPipeline::setOutputFormat(ProcessorPtr output) } void QueryPipeline::unitePipelines( - std::vector && pipelines, const Block & common_header) + std::vector> pipelines, const Block & common_header) { if (initialized()) { @@ -583,8 +583,9 @@ void QueryPipeline::unitePipelines( if (totals_having_port) totals.push_back(totals_having_port); - for (auto & pipeline : pipelines) + for (auto & pipeline_ptr : pipelines) { + auto & pipeline = *pipeline_ptr; pipeline.checkInitialized(); if (!pipeline.isCompleted()) diff --git a/src/Processors/QueryPipeline.h b/src/Processors/QueryPipeline.h index 129b7f5ae3c9..6d9409ffc47a 100644 --- a/src/Processors/QueryPipeline.h +++ b/src/Processors/QueryPipeline.h @@ -135,7 +135,7 @@ class QueryPipeline void enableQuotaForCurrentStreams(); - void unitePipelines(std::vector && pipelines, const Block & common_header); + void unitePipelines(std::vector> pipelines, const Block & common_header); PipelineExecutorPtr execute(); diff --git a/src/Processors/QueryPlan/ConvertingStep.cpp b/src/Processors/QueryPlan/ConvertingStep.cpp new file mode 100644 index 000000000000..312210fd1493 --- /dev/null +++ b/src/Processors/QueryPlan/ConvertingStep.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +namespace DB +{ + +static ITransformingStep::DataStreamTraits getTraits() +{ + return ITransformingStep::DataStreamTraits{ + .preserves_distinct_columns = true + }; +} + +static void filterDistinctColumns(const Block & res_header, NameSet & distinct_columns) +{ + if (distinct_columns.empty()) + return; + + NameSet new_distinct_columns; + for (const auto & column : res_header) + if (distinct_columns.count(column.name)) + new_distinct_columns.insert(column.name); + + distinct_columns.swap(new_distinct_columns); +} + +ConvertingStep::ConvertingStep(const DataStream & input_stream_, Block result_header_) + : ITransformingStep( + input_stream_, + result_header_, + getTraits()) + , result_header(std::move(result_header_)) +{ + /// Some columns may be removed + filterDistinctColumns(output_stream->header, output_stream->distinct_columns); + filterDistinctColumns(output_stream->header, output_stream->local_distinct_columns); +} + +void ConvertingStep::transformPipeline(QueryPipeline & pipeline) +{ + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, result_header, ConvertingTransform::MatchColumnsMode::Name); + }); +} + +} diff --git a/src/Processors/QueryPlan/ConvertingStep.h b/src/Processors/QueryPlan/ConvertingStep.h new file mode 100644 index 000000000000..540deece246d --- /dev/null +++ b/src/Processors/QueryPlan/ConvertingStep.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace DB +{ + +class ConvertingStep : public ITransformingStep +{ +public: + ConvertingStep(const DataStream & input_stream_, Block result_header_); + + String getName() const override { return "Converting"; } + + void transformPipeline(QueryPipeline & pipeline) override; + +private: + Block result_header; +}; + +} diff --git a/src/Processors/QueryPlan/MergeSortingStep.cpp b/src/Processors/QueryPlan/MergeSortingStep.cpp index bc48ae1a98ae..d34fac4d3ca6 100644 --- a/src/Processors/QueryPlan/MergeSortingStep.cpp +++ b/src/Processors/QueryPlan/MergeSortingStep.cpp @@ -40,7 +40,7 @@ void MergeSortingStep::transformPipeline(QueryPipeline & pipeline) return std::make_shared( header, description, max_merged_block_size, limit, - max_bytes_before_remerge, + max_bytes_before_remerge / pipeline.getNumStreams(), max_bytes_before_external_sort, tmp_volume, min_free_disk_space); diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index d29b66876a51..cbbd5bf5efb5 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -35,6 +35,43 @@ const DataStream & QueryPlan::getCurrentDataStream() const return root->step->getOutputStream(); } +void QueryPlan::unitePlans(QueryPlanStepPtr step, std::vector plans) +{ + if (isInitialized()) + throw Exception("Cannot unite plans because current QueryPlan is already initialized", + ErrorCodes::LOGICAL_ERROR); + + const auto & inputs = step->getInputStreams(); + size_t num_inputs = step->getInputStreams().size(); + if (num_inputs != plans.size()) + { + throw Exception("Cannot unite QueryPlans using " + step->getName() + + " because step has different number of inputs. " + "Has " + std::to_string(plans.size()) + " plans " + "and " + std::to_string(num_inputs) + " inputs", ErrorCodes::LOGICAL_ERROR); + } + + for (size_t i = 0; i < num_inputs; ++i) + { + const auto & step_header = inputs[i].header; + const auto & plan_header = plans[i].getCurrentDataStream().header; + if (!blocksHaveEqualStructure(step_header, plan_header)) + throw Exception("Cannot unite QueryPlans using " + step->getName() + " because " + "it has incompatible header with plan " + root->step->getName() + " " + "plan header: " + plan_header.dumpStructure() + + "step header: " + step_header.dumpStructure(), ErrorCodes::LOGICAL_ERROR); + } + + for (auto & plan : plans) + nodes.insert(nodes.end(), plan.nodes.begin(), plan.nodes.end()); + + nodes.emplace_back(Node{.step = std::move(step)}); + root = &nodes.back(); + + for (auto & plan : plans) + root->children.emplace_back(plan.root); +} + void QueryPlan::addStep(QueryPlanStepPtr step) { checkNotCompleted(); @@ -48,6 +85,7 @@ void QueryPlan::addStep(QueryPlanStepPtr step) "step has no inputs, but QueryPlan is already initialised", ErrorCodes::LOGICAL_ERROR); nodes.emplace_back(Node{.step = std::move(step)}); + root = &nodes.back(); return; } @@ -100,7 +138,12 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() size_t next_child = frame.pipelines.size(); if (next_child == frame.node->children.size()) { + bool limit_max_threads = frame.pipelines.empty(); last_pipeline = frame.node->step->updatePipeline(std::move(frame.pipelines)); + + if (limit_max_threads) + last_pipeline->setMaxThreads(max_threads); + stack.pop(); } else diff --git a/src/Processors/QueryPlan/QueryPlan.h b/src/Processors/QueryPlan/QueryPlan.h index 168cfc3665a5..d47c5052a4f6 100644 --- a/src/Processors/QueryPlan/QueryPlan.h +++ b/src/Processors/QueryPlan/QueryPlan.h @@ -18,6 +18,7 @@ using QueryPipelinePtr = std::unique_ptr; class QueryPlan { public: + void unitePlans(QueryPlanStepPtr step, std::vector plans); void addStep(QueryPlanStepPtr step); bool isInitialized() const { return root != nullptr; } /// Tree is not empty @@ -26,6 +27,10 @@ class QueryPlan QueryPipelinePtr buildQueryPipeline(); + /// Set upper limit for the recommend number of threads. Will be applied to the newly-created pipelines. + /// TODO: make it in a better way. + void setMaxThreads(size_t max_threads_) { max_threads = max_threads_; } + private: struct Node { @@ -40,6 +45,8 @@ class QueryPlan void checkInitialized() const; void checkNotCompleted() const; + + size_t max_threads = 0; }; } diff --git a/src/Processors/QueryPlan/TotalsHavingStep.h b/src/Processors/QueryPlan/TotalsHavingStep.h index ddb0ccd25728..52cc936f6229 100644 --- a/src/Processors/QueryPlan/TotalsHavingStep.h +++ b/src/Processors/QueryPlan/TotalsHavingStep.h @@ -1,6 +1,5 @@ #pragma once #include -#include namespace DB { diff --git a/src/Processors/QueryPlan/UnionStep.cpp b/src/Processors/QueryPlan/UnionStep.cpp new file mode 100644 index 000000000000..0da7806e58f2 --- /dev/null +++ b/src/Processors/QueryPlan/UnionStep.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +namespace DB +{ + +UnionStep::UnionStep(DataStreams input_streams_, Block result_header, size_t max_threads_) + : header(std::move(result_header)) + , max_threads(max_threads_) +{ + input_streams = std::move(input_streams_); + + /// TODO: update traits + output_stream = DataStream{.header = header}; +} + +QueryPipelinePtr UnionStep::updatePipeline(QueryPipelines pipelines) +{ + auto pipeline = std::make_unique(); + if (pipelines.empty()) + { + pipeline->init(Pipe(std::make_shared(output_stream->header))); + return pipeline; + } + + size_t num_pipelines = pipelines.size(); + pipeline->unitePipelines(std::move(pipelines), output_stream->header); + + if (num_pipelines > 1) + { + // nested queries can force 1 thread (due to simplicity) + // but in case of union this cannot be done. + pipeline->setMaxThreads(std::min(num_pipelines, max_threads)); + } +} + +} diff --git a/src/Processors/QueryPlan/UnionStep.h b/src/Processors/QueryPlan/UnionStep.h new file mode 100644 index 000000000000..2c3d17b2e82e --- /dev/null +++ b/src/Processors/QueryPlan/UnionStep.h @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace DB +{ + +class UnionStep : public IQueryPlanStep +{ +public: + /// max_threads is used to limit the number of threads for result pipeline. + UnionStep(DataStreams input_streams_, Block result_header, size_t max_threads_); + + String getName() const override { return "Union"; } + + QueryPipelinePtr updatePipeline(QueryPipelines pipelines) override; + +private: + Block header; + size_t max_threads; +}; + +} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 07328af754b5..6e4d2e4aa544 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -139,6 +139,7 @@ SRCS( Transforms/AggregatingInOrderTransform.cpp QueryPlan/AddingDelayedStreamStep.cpp QueryPlan/AggregatingStep.cpp + QueryPlan/ConvertingStep.cpp QueryPlan/CreatingSetsStep.cpp QueryPlan/CubeStep.cpp QueryPlan/DistinctStep.cpp @@ -157,6 +158,7 @@ SRCS( QueryPlan/MergingSortedStep.cpp QueryPlan/OffsetsStep.cpp QueryPlan/PartialSortingStep.cpp + QueryPlan/UnionStep.cpp QueryPlan/ReadFromPreparedSource.cpp QueryPlan/ReadFromStorageStep.cpp QueryPlan/ReadNothingStep.cpp From 71b5d267ce1fcae6f47704bb91d8f95c63b688f2 Mon Sep 17 00:00:00 2001 From: Avogar Date: Thu, 18 Jun 2020 21:02:13 +0300 Subject: [PATCH 0411/1102] Set compression to None --- .../Formats/Impl/ORCBlockOutputFormat.cpp | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp index f34ca21a1b36..d57a5b665ca7 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -42,7 +42,17 @@ void ORCOutputStream::write(const void* buf, size_t length) } ORCBlockOutputFormat::ORCBlockOutputFormat(WriteBuffer & out_, const Block & header_, const FormatSettings & format_settings_) - : IOutputFormat(header_, out_), format_settings{format_settings_}, output_stream(out_), data_types(header_.getDataTypes()) {} + : IOutputFormat(header_, out_), format_settings{format_settings_}, output_stream(out_), data_types(header_.getDataTypes()) +{ + schema = orc::createStructType(); + options.setCompression(orc::CompressionKind::CompressionKind_NONE); + size_t columns_count = header_.columns(); + for (size_t i = 0; i != columns_count; ++i) + { + schema->addStructField(header_.safeGetByPosition(i).name, getORCType(data_types[i])); + } + writer = orc::createWriter(*schema, &output_stream, options); +} ORC_UNIQUE_PTR ORCBlockOutputFormat::getORCType(const DataTypePtr & type) { @@ -140,10 +150,7 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( number_orc_column->notNull[i] = 0; continue; } - if constexpr (std::is_same_v) - number_orc_column->data[i] = static_cast(number_column.getElement(i)); - else - number_orc_column->data[i] = number_column.getElement(i); + number_orc_column->data[i] = number_column.getElement(i); } number_orc_column->numElements = number_column.size(); } @@ -355,7 +362,7 @@ void ORCBlockOutputFormat::writeColumn( writeColumn(orc_column, nullable_column.getNestedColumn(), nested_type, &new_null_bytemap); break; } - /* Doesn't work + /* Doesn't work for unknown reason case TypeIndex::Array: { orc::ListVectorBatch * list_orc_column = dynamic_cast(orc_column); @@ -384,16 +391,6 @@ void ORCBlockOutputFormat::consume(Chunk chunk) { size_t columns_num = chunk.getNumColumns(); size_t rows_num = chunk.getNumRows(); - if (!writer) - { - const Block & header = getPort(PortKind::Main).getHeader(); - schema = orc::createStructType(); - for (size_t i = 0; i != columns_num; ++i) - { - schema->addStructField(header.safeGetByPosition(i).name, getORCType(data_types[i])); - } - writer = orc::createWriter(*schema, &output_stream, options); - } ORC_UNIQUE_PTR batch = writer->createRowBatch(rows_num); orc::StructVectorBatch *root = dynamic_cast(batch.get()); for (size_t i = 0; i != columns_num; ++i) From b7bed87a7fa8124849b6982bc277d28755f81719 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 21:29:33 +0300 Subject: [PATCH 0412/1102] Use QueryPlan in InterpreterSelectQuery [part 3]. --- src/Interpreters/Aggregator.cpp | 35 ++++++++++++-------- src/Interpreters/Aggregator.h | 16 +++++++-- src/Interpreters/InterpreterSelectQuery.cpp | 21 +++++------- src/Processors/QueryPlan/AggregatingStep.cpp | 21 ++++++++++-- src/Processors/QueryPlan/AggregatingStep.h | 7 ++-- 5 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/Interpreters/Aggregator.cpp b/src/Interpreters/Aggregator.cpp index 538a24fa9976..c8165632896b 100644 --- a/src/Interpreters/Aggregator.cpp +++ b/src/Interpreters/Aggregator.cpp @@ -96,39 +96,48 @@ void AggregatedDataVariants::convertToTwoLevel() } } - Block Aggregator::getHeader(bool final) const +{ + return params.getHeader(final); +} + +Block Aggregator::Params::getHeader( + const Block & src_header, + const Block & intermediate_header, + const ColumnNumbers & keys, + const AggregateDescriptions & aggregates, + bool final) { Block res; - if (params.src_header) + if (src_header) { - for (size_t i = 0; i < params.keys_size; ++i) - res.insert(params.src_header.safeGetByPosition(params.keys[i]).cloneEmpty()); + for (const auto & key : keys) + res.insert(src_header.safeGetByPosition(key).cloneEmpty()); - for (size_t i = 0; i < params.aggregates_size; ++i) + for (const auto & aggregate : aggregates) { - size_t arguments_size = params.aggregates[i].arguments.size(); + size_t arguments_size = aggregate.arguments.size(); DataTypes argument_types(arguments_size); for (size_t j = 0; j < arguments_size; ++j) - argument_types[j] = params.src_header.safeGetByPosition(params.aggregates[i].arguments[j]).type; + argument_types[j] = src_header.safeGetByPosition(aggregate.arguments[j]).type; DataTypePtr type; if (final) - type = params.aggregates[i].function->getReturnType(); + type = aggregate.function->getReturnType(); else - type = std::make_shared(params.aggregates[i].function, argument_types, params.aggregates[i].parameters); + type = std::make_shared(aggregate.function, argument_types, aggregate.parameters); - res.insert({ type, params.aggregates[i].column_name }); + res.insert({ type, aggregate.column_name }); } } - else if (params.intermediate_header) + else if (intermediate_header) { - res = params.intermediate_header.cloneEmpty(); + res = intermediate_header.cloneEmpty(); if (final) { - for (const auto & aggregate : params.aggregates) + for (const auto & aggregate : aggregates) { auto & elem = res.getByName(aggregate.column_name); diff --git a/src/Interpreters/Aggregator.h b/src/Interpreters/Aggregator.h index 6d0eeee90143..0546271873b6 100644 --- a/src/Interpreters/Aggregator.h +++ b/src/Interpreters/Aggregator.h @@ -869,8 +869,8 @@ class Aggregator * two-level aggregation begins to be used. Enough to reach of at least one of the thresholds. * 0 - the corresponding threshold is not specified. */ - const size_t group_by_two_level_threshold; - const size_t group_by_two_level_threshold_bytes; + size_t group_by_two_level_threshold; + size_t group_by_two_level_threshold_bytes; /// Settings to flush temporary data to the filesystem (external aggregation). const size_t max_bytes_before_external_group_by; /// 0 - do not use external aggregation. @@ -911,6 +911,18 @@ class Aggregator { intermediate_header = intermediate_header_; } + + static Block getHeader( + const Block & src_header, + const Block & intermediate_header, + const ColumnNumbers & keys, + const AggregateDescriptions & aggregates, + bool final); + + Block getHeader(bool final) const + { + return getHeader(src_header, intermediate_header, keys, aggregates, final); + } }; Aggregator(const Params & params_); diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 7911372e5b7b..613824b63f02 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1387,20 +1387,15 @@ void InterpreterSelectQuery::executeAggregation(QueryPlan & query_plan, const Ex const Settings & settings = context->getSettingsRef(); - /** Two-level aggregation is useful in two cases: - * 1. Parallel aggregation is done, and the results should be merged in parallel. - * 2. An aggregation is done with store of temporary data on the disk, and they need to be merged in a memory efficient way. - */ - bool allow_to_use_two_level_group_by = pipeline.getNumStreams() > 1 || settings.max_bytes_before_external_group_by != 0; - Aggregator::Params params(header_before_aggregation, keys, aggregates, overflow_row, settings.max_rows_to_group_by, settings.group_by_overflow_mode, - allow_to_use_two_level_group_by ? settings.group_by_two_level_threshold : SettingUInt64(0), - allow_to_use_two_level_group_by ? settings.group_by_two_level_threshold_bytes : SettingUInt64(0), - settings.max_bytes_before_external_group_by, settings.empty_result_for_aggregation_by_empty_set, - context->getTemporaryVolume(), settings.max_threads, settings.min_free_disk_space_for_temporary_data); - - auto transform_params = std::make_shared(params, final); + settings.group_by_two_level_threshold, + settings.group_by_two_level_threshold_bytes, + settings.max_bytes_before_external_group_by, + settings.empty_result_for_aggregation_by_empty_set, + context->getTemporaryVolume(), + settings.max_threads, + settings.min_free_disk_space_for_temporary_data); SortDescription group_by_sort_description; @@ -1418,7 +1413,7 @@ void InterpreterSelectQuery::executeAggregation(QueryPlan & query_plan, const Ex auto aggregating_step = std::make_unique( query_plan.getCurrentDataStream(), - std::move(transform_params), + params, final, settings.max_block_size, merge_threads, temporary_data_merge_threads, diff --git a/src/Processors/QueryPlan/AggregatingStep.cpp b/src/Processors/QueryPlan/AggregatingStep.cpp index 24e687ba73bf..11a6b8e6cbc9 100644 --- a/src/Processors/QueryPlan/AggregatingStep.cpp +++ b/src/Processors/QueryPlan/AggregatingStep.cpp @@ -16,15 +16,17 @@ static ITransformingStep::DataStreamTraits getTraits() AggregatingStep::AggregatingStep( const DataStream & input_stream_, - AggregatingTransformParamsPtr transform_params_, + Aggregator::Params params_, + bool final_, size_t max_block_size_, size_t merge_threads_, size_t temporary_data_merge_threads_, bool storage_has_evenly_distributed_read_, InputOrderInfoPtr group_by_info_, SortDescription group_by_sort_description_) - : ITransformingStep(input_stream_, transform_params_->getHeader(), getTraits()) - , transform_params(std::move(transform_params_)) + : ITransformingStep(input_stream_, params_.getHeader(final_), getTraits()) + , params(std::move(params_)) + , final(std::move(final_)) , max_block_size(max_block_size_) , merge_threads(merge_threads_) , temporary_data_merge_threads(temporary_data_merge_threads_) @@ -39,6 +41,19 @@ void AggregatingStep::transformPipeline(QueryPipeline & pipeline) /// Forget about current totals and extremes. They will be calculated again after aggregation if needed. pipeline.dropTotalsAndExtremes(); + bool allow_to_use_two_level_group_by = pipeline.getNumStreams() > 1 || params.max_bytes_before_external_group_by != 0; + if (!allow_to_use_two_level_group_by) + { + params.group_by_two_level_threshold = 0; + params.group_by_two_level_threshold_bytes = 0; + } + + /** Two-level aggregation is useful in two cases: + * 1. Parallel aggregation is done, and the results should be merged in parallel. + * 2. An aggregation is done with store of temporary data on the disk, and they need to be merged in a memory efficient way. + */ + auto transform_params = std::make_shared(std::move(params), final); + if (group_by_info) { bool need_finish_sorting = (group_by_info->order_key_prefix_descr.size() < group_by_sort_description.size()); diff --git a/src/Processors/QueryPlan/AggregatingStep.h b/src/Processors/QueryPlan/AggregatingStep.h index 6a42929b785b..df75eee1b3fb 100644 --- a/src/Processors/QueryPlan/AggregatingStep.h +++ b/src/Processors/QueryPlan/AggregatingStep.h @@ -2,6 +2,7 @@ #include #include #include +#include namespace DB { @@ -14,7 +15,8 @@ class AggregatingStep : public ITransformingStep public: AggregatingStep( const DataStream & input_stream_, - AggregatingTransformParamsPtr transform_params_, + Aggregator::Params params_, + bool final_, size_t max_block_size_, size_t merge_threads_, size_t temporary_data_merge_threads_, @@ -27,7 +29,8 @@ class AggregatingStep : public ITransformingStep void transformPipeline(QueryPipeline & pipeline) override; private: - AggregatingTransformParamsPtr transform_params; + Aggregator::Params params; + bool final; size_t max_block_size; size_t merge_threads; size_t temporary_data_merge_threads; From a99d9f771204d35d777189d81e654819f3602ae1 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 21:36:37 +0300 Subject: [PATCH 0413/1102] Fix build. --- src/Interpreters/InterpreterSelectQuery.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 613824b63f02..94021c372117 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -34,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -58,7 +56,6 @@ #include #include #include -#include #include #include @@ -906,8 +903,6 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu if (expressions.second_stage) { - bool need_second_distinct_pass = false; - if (expressions.need_aggregate) { /// If you need to combine aggregated results from multiple servers From 7bfb5c9a47a5058bcda068c825ec75c0aae25c16 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 21:40:02 +0300 Subject: [PATCH 0414/1102] Fix build. --- src/Interpreters/InterpreterInsertQuery.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index 7deed262eda6..e84186e91f72 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -3,18 +3,15 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -24,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +28,6 @@ #include #include #include -#include namespace DB @@ -162,7 +157,7 @@ BlockIO InterpreterInsertQuery::execute() const auto & cluster = storage_src->getCluster(); const auto & shards_info = cluster->getShardsInfo(); - std::vector pipelines; + std::vector> pipelines; String new_query_str = queryToString(new_query); for (size_t shard_index : ext::range(0, shards_info.size())) @@ -171,7 +166,7 @@ BlockIO InterpreterInsertQuery::execute() if (shard_info.isLocal()) { InterpreterInsertQuery interpreter(new_query, context); - pipelines.emplace_back(interpreter.execute().pipeline); + pipelines.emplace_back(std::make_unique(interpreter.execute().pipeline)); } else { @@ -184,8 +179,8 @@ BlockIO InterpreterInsertQuery::execute() /// INSERT SELECT query returns empty block auto in_stream = std::make_shared(std::move(connections), new_query_str, Block{}, context); pipelines.emplace_back(); - pipelines.back().init(Pipe(std::make_shared(std::move(in_stream)))); - pipelines.back().setSinks([](const Block & header, QueryPipeline::StreamType) -> ProcessorPtr + pipelines.back()->init(Pipe(std::make_shared(std::move(in_stream)))); + pipelines.back()->setSinks([](const Block & header, QueryPipeline::StreamType) -> ProcessorPtr { return std::make_shared(header); }); From 0ba7ff85e1838a39db41f52006a9c2581e8dcffd Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 21:45:27 +0300 Subject: [PATCH 0415/1102] Fix build. --- src/Processors/QueryPlan/UnionStep.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Processors/QueryPlan/UnionStep.cpp b/src/Processors/QueryPlan/UnionStep.cpp index 0da7806e58f2..c39a2fcafda6 100644 --- a/src/Processors/QueryPlan/UnionStep.cpp +++ b/src/Processors/QueryPlan/UnionStep.cpp @@ -34,6 +34,8 @@ QueryPipelinePtr UnionStep::updatePipeline(QueryPipelines pipelines) // but in case of union this cannot be done. pipeline->setMaxThreads(std::min(num_pipelines, max_threads)); } + + return pipeline; } } From 041533eae204a2bfc478ed551e3554032d940ef4 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Tue, 16 Jun 2020 21:49:04 +0300 Subject: [PATCH 0416/1102] Disable optimize_skip_unused_shards if sharding_key has non-deterministic func Example of such functions is rand() And this patch disables only optimize_skip_unused_shards, i.e. INSERT code path does not changed, so it will work as before. --- src/Storages/StorageDistributed.cpp | 18 +++++++++++++++++- src/Storages/StorageDistributed.h | 1 + ...01071_force_optimize_skip_unused_shards.sql | 6 ++++++ ...nused_shards_no_non_deterministic.reference | 0 ...skip_unused_shards_no_non_deterministic.sql | 10 ++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/queries/0_stateless/01320_optimize_skip_unused_shards_no_non_deterministic.reference create mode 100644 tests/queries/0_stateless/01320_optimize_skip_unused_shards_no_non_deterministic.sql diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 77ed0470d4a4..d434aa4b0b91 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -188,6 +189,18 @@ ExpressionActionsPtr buildShardingKeyExpression(const ASTPtr & sharding_key, con return ExpressionAnalyzer(query, syntax_result, context).getActions(project); } +bool isExpressionActionsDeterministics(const ExpressionActionsPtr & actions) +{ + for (const auto & action : actions->getActions()) + { + if (action.type != ExpressionAction::APPLY_FUNCTION) + continue; + if (!action.function_base->isDeterministic()) + return false; + } + return true; +} + class ReplacingConstantExpressionsMatcher { public: @@ -292,6 +305,7 @@ StorageDistributed::StorageDistributed( { sharding_key_expr = buildShardingKeyExpression(sharding_key_, *global_context, getColumns().getAllPhysical(), false); sharding_key_column_name = sharding_key_->getColumnName(); + sharding_key_is_deterministic = isExpressionActionsDeterministics(sharding_key_expr); } if (!relative_data_path.empty()) @@ -687,7 +701,7 @@ ClusterPtr StorageDistributed::getOptimizedCluster(const Context & context, cons ClusterPtr cluster = getCluster(); const Settings & settings = context.getSettingsRef(); - if (has_sharding_key) + if (has_sharding_key && sharding_key_is_deterministic) { ClusterPtr optimized = skipUnusedShards(cluster, query_ptr, context); if (optimized) @@ -700,6 +714,8 @@ ClusterPtr StorageDistributed::getOptimizedCluster(const Context & context, cons std::stringstream exception_message; if (!has_sharding_key) exception_message << "No sharding key"; + else if (sharding_key_is_deterministic) + exception_message << "Sharding key is not deterministic"; else exception_message << "Sharding key " << sharding_key_column_name << " is not used"; diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index ecd2b17b48e4..02da81a11727 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -142,6 +142,7 @@ class StorageDistributed final : public ext::shared_ptr_helper Date: Tue, 16 Jun 2020 22:02:06 +0300 Subject: [PATCH 0417/1102] optimize_skip_unused_shards=2 will disable it for nested distributed queries P.S. Looks like settings can be converted between SettingUInt64 and SettingBool without breaking binary protocol. FWIW maybe it is a good idea to change the semantics of the settings as follow (but I guess that changing semantic is not a good idea, better to add new settings and deprecate old ones): - optimize_skip_unused_shards -- accept nesting level on which the optimization will work - force_skip_optimize_shards_nesting -- accept nesting level on which the optimization will work --- docs/en/operations/settings/settings.md | 3 ++- docs/ru/operations/settings/settings.md | 3 ++- src/Core/Settings.h | 2 +- src/Interpreters/ClusterProxy/executeQuery.cpp | 8 ++++++++ ...optimize_skip_unused_shards_no_nested.reference | 0 ...01319_optimize_skip_unused_shards_no_nested.sql | 14 ++++++++++++++ 6 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.reference create mode 100644 tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 237058f1b833..89a3e60b6e7a 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1121,7 +1121,8 @@ Enables or disables skipping of unused shards for [SELECT](../../sql-reference/s Possible values: - 0 — Disabled. -- 1 — Enabled. +- 1 — Enabled, including nested `Distributed()` tables. +- 2 — Enabled, excluding nested `Distributed()` tables. Default value: 0 diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index 5e34affcaac3..05492700ee7f 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -1032,7 +1032,8 @@ ClickHouse генерирует исключение Возможные значения: - 0 — Выключена. -- 1 — Включена. +- 1 — Включена, включая вложенные `Distributed` таблицы. +- 2 — Включена, исключая вложенные `Distributed` таблицы. Значение по умолчанию: 0 diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 1f3a8f424007..daef73a002f5 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -121,7 +121,7 @@ struct Settings : public SettingsCollection \ M(SettingBool, distributed_group_by_no_merge, false, "Do not merge aggregation states from different servers for distributed query processing - in case it is for certain that there are different keys on different shards.", 0) \ M(SettingBool, parallel_distributed_insert_select, false, "If true, distributed insert select query in the same cluster will be processed on local tables on every shard", 0) \ - M(SettingBool, optimize_skip_unused_shards, false, "Assumes that data is distributed by sharding_key. Optimization to skip unused shards if SELECT query filters by sharding_key.", 0) \ + M(SettingUInt64, optimize_skip_unused_shards, 0, "Assumes that data is distributed by sharding_key. Optimization to skip unused shards if SELECT query filters by sharding_key (if 1 - includes nested Distributed, 2 - disable for nested Distributed).", 0) \ M(SettingBool, optimize_distributed_group_by_sharding_key, false, "Optimize GROUP BY sharding_key queries (by avodiing costly aggregation on the initiator server).", 0) \ M(SettingUInt64, force_optimize_skip_unused_shards, 0, "Throw an exception if unused shards cannot be skipped (1 - throw only if the table has the sharding key, 2 - always throw.", 0) \ M(SettingBool, force_optimize_skip_unused_shards_no_nested, false, "Do not apply force_optimize_skip_unused_shards for nested Distributed tables.", 0) \ diff --git a/src/Interpreters/ClusterProxy/executeQuery.cpp b/src/Interpreters/ClusterProxy/executeQuery.cpp index fa977249eaa8..64aae1755986 100644 --- a/src/Interpreters/ClusterProxy/executeQuery.cpp +++ b/src/Interpreters/ClusterProxy/executeQuery.cpp @@ -17,6 +17,8 @@ namespace ClusterProxy Context removeUserRestrictionsFromSettings(const Context & context, const Settings & settings) { + static const UInt64 OPTIMIZE_SKIP_UNUSED_SHARDS_NO_NESTED = 2; + Settings new_settings = settings; new_settings.queue_max_wait_ms = Cluster::saturate(new_settings.queue_max_wait_ms, settings.max_execution_time); @@ -34,6 +36,12 @@ Context removeUserRestrictionsFromSettings(const Context & context, const Settin new_settings.force_optimize_skip_unused_shards.changed = false; } + if (settings.optimize_skip_unused_shards == OPTIMIZE_SKIP_UNUSED_SHARDS_NO_NESTED) + { + new_settings.optimize_skip_unused_shards = 0; + new_settings.optimize_skip_unused_shards.changed = false; + } + Context new_context(context); new_context.setSettings(new_settings); diff --git a/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.reference b/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.reference new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql b/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql new file mode 100644 index 000000000000..293ab42dcf44 --- /dev/null +++ b/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql @@ -0,0 +1,14 @@ +drop table if exists data_01319; +drop table if exists dist_01319; +drop table if exists dist_layer_01319; + +create table data_01319 (key Int, sub_key Int) Engine=Null(); + +set force_optimize_skip_unused_shards=2; +set optimize_skip_unused_shards=1; + +create table dist_layer_01319 as data_01319 Engine=Distributed(test_cluster_two_shards, currentDatabase(), data_01319, sub_key%2); +create table dist_01319 as data_01319 Engine=Distributed(test_cluster_two_shards, currentDatabase(), dist_layer_01319, key%2); +select * from dist_01319 where key = 1; -- { serverError 507 } +set optimize_skip_unused_shards=2; -- no nested +select * from dist_01319 where key = 1; From d34e6217bcf325f5f2273c079d4b1b9d3ac87c0f Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 18 Jun 2020 21:45:39 +0300 Subject: [PATCH 0418/1102] Add logging of adjusting conditional settings for distributed queries --- src/Interpreters/ClusterProxy/executeQuery.cpp | 14 +++++++++++--- src/Interpreters/ClusterProxy/executeQuery.h | 4 ++-- src/Storages/StorageDistributed.cpp | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Interpreters/ClusterProxy/executeQuery.cpp b/src/Interpreters/ClusterProxy/executeQuery.cpp index 64aae1755986..38ad60f30bf0 100644 --- a/src/Interpreters/ClusterProxy/executeQuery.cpp +++ b/src/Interpreters/ClusterProxy/executeQuery.cpp @@ -15,7 +15,7 @@ namespace DB namespace ClusterProxy { -Context removeUserRestrictionsFromSettings(const Context & context, const Settings & settings) +Context removeUserRestrictionsFromSettings(const Context & context, const Settings & settings, Poco::Logger * log) { static const UInt64 OPTIMIZE_SKIP_UNUSED_SHARDS_NO_NESTED = 2; @@ -34,12 +34,18 @@ Context removeUserRestrictionsFromSettings(const Context & context, const Settin { new_settings.force_optimize_skip_unused_shards = 0; new_settings.force_optimize_skip_unused_shards.changed = false; + + if (log) + LOG_TRACE(log, "Disabling force_optimize_skip_unused_shards (due to force_optimize_skip_unused_shards_no_nested)"); } if (settings.optimize_skip_unused_shards == OPTIMIZE_SKIP_UNUSED_SHARDS_NO_NESTED) { new_settings.optimize_skip_unused_shards = 0; new_settings.optimize_skip_unused_shards.changed = false; + + if (log) + LOG_TRACE(log, "Disabling optimize_skip_unused_shards (due to optimize_skip_unused_shards=2)"); } Context new_context(context); @@ -49,14 +55,16 @@ Context removeUserRestrictionsFromSettings(const Context & context, const Settin } Pipes executeQuery( - IStreamFactory & stream_factory, const ClusterPtr & cluster, + IStreamFactory & stream_factory, const ClusterPtr & cluster, Poco::Logger * log, const ASTPtr & query_ast, const Context & context, const Settings & settings, const SelectQueryInfo & query_info) { + assert(log); + Pipes res; const std::string query = queryToString(query_ast); - Context new_context = removeUserRestrictionsFromSettings(context, settings); + Context new_context = removeUserRestrictionsFromSettings(context, settings, log); ThrottlerPtr user_level_throttler; if (auto * process_list_element = context.getProcessListElement()) diff --git a/src/Interpreters/ClusterProxy/executeQuery.h b/src/Interpreters/ClusterProxy/executeQuery.h index fed8b83db033..dcbbe0c7e950 100644 --- a/src/Interpreters/ClusterProxy/executeQuery.h +++ b/src/Interpreters/ClusterProxy/executeQuery.h @@ -21,13 +21,13 @@ class IStreamFactory; /// removes different restrictions (like max_concurrent_queries_for_user, max_memory_usage_for_user, etc.) /// from settings and creates new context with them -Context removeUserRestrictionsFromSettings(const Context & context, const Settings & settings); +Context removeUserRestrictionsFromSettings(const Context & context, const Settings & settings, Poco::Logger * log = nullptr); /// Execute a distributed query, creating a vector of BlockInputStreams, from which the result can be read. /// `stream_factory` object encapsulates the logic of creating streams for a different type of query /// (currently SELECT, DESCRIBE). Pipes executeQuery( - IStreamFactory & stream_factory, const ClusterPtr & cluster, + IStreamFactory & stream_factory, const ClusterPtr & cluster, Poco::Logger * log, const ASTPtr & query_ast, const Context & context, const Settings & settings, const SelectQueryInfo & query_info); } diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index d434aa4b0b91..201aeb7273b8 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -519,8 +519,8 @@ Pipes StorageDistributed::read( : ClusterProxy::SelectStreamFactory( header, processed_stage, StorageID{remote_database, remote_table}, scalars, has_virtual_shard_num_column, context.getExternalTables()); - return ClusterProxy::executeQuery( - select_stream_factory, cluster, modified_query_ast, context, context.getSettingsRef(), query_info); + return ClusterProxy::executeQuery(select_stream_factory, cluster, log, + modified_query_ast, context, context.getSettingsRef(), query_info); } From 724c09a22c75bded4f043ac6d7e2616d70b54307 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 18 Jun 2020 21:45:39 +0300 Subject: [PATCH 0419/1102] Add missing DROP TABLE in 01319_mv_constants_bug --- tests/queries/0_stateless/01319_mv_constants_bug.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/queries/0_stateless/01319_mv_constants_bug.sql b/tests/queries/0_stateless/01319_mv_constants_bug.sql index 975a33d7b71c..191183ab286c 100644 --- a/tests/queries/0_stateless/01319_mv_constants_bug.sql +++ b/tests/queries/0_stateless/01319_mv_constants_bug.sql @@ -3,6 +3,7 @@ DROP TABLE IF EXISTS distributed_table_1; DROP TABLE IF EXISTS distributed_table_2; DROP TABLE IF EXISTS local_table_1; DROP TABLE IF EXISTS local_table_2; +DROP TABLE IF EXISTS local_table_merged; CREATE TABLE local_table_1 (id String) ENGINE = MergeTree ORDER BY (id); CREATE TABLE local_table_2(id String) ENGINE = MergeTree ORDER BY (id); From 0e218b0f15a502ed5c95499e13fa89f8d52fa006 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 18 Jun 2020 21:45:40 +0300 Subject: [PATCH 0420/1102] Improve 01319_optimize_skip_unused_shards_no_nested Before there is no check that optimize_skip_unused_shards was working for the first level, use cluster with unavalable shard to guarantee this. --- .../01319_optimize_skip_unused_shards_no_nested.sql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql b/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql index 293ab42dcf44..6bf8e17a56c0 100644 --- a/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql +++ b/tests/queries/0_stateless/01319_optimize_skip_unused_shards_no_nested.sql @@ -7,8 +7,10 @@ create table data_01319 (key Int, sub_key Int) Engine=Null(); set force_optimize_skip_unused_shards=2; set optimize_skip_unused_shards=1; -create table dist_layer_01319 as data_01319 Engine=Distributed(test_cluster_two_shards, currentDatabase(), data_01319, sub_key%2); -create table dist_01319 as data_01319 Engine=Distributed(test_cluster_two_shards, currentDatabase(), dist_layer_01319, key%2); +create table dist_layer_01319 as data_01319 Engine=Distributed(test_cluster_two_shards, currentDatabase(), data_01319, sub_key); +-- test_unavailable_shard here to check that optimize_skip_unused_shards always +-- remove some nodes from the cluster for the first nesting level +create table dist_01319 as data_01319 Engine=Distributed(test_unavailable_shard, currentDatabase(), dist_layer_01319, key+1); select * from dist_01319 where key = 1; -- { serverError 507 } set optimize_skip_unused_shards=2; -- no nested select * from dist_01319 where key = 1; From 6cbdb6da00babaf74997c21a4235da02fdafef8e Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 22:03:21 +0300 Subject: [PATCH 0421/1102] Fix build. --- src/Processors/QueryPlan/QueryPlan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index cbbd5bf5efb5..66e22d1704e2 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -63,7 +63,7 @@ void QueryPlan::unitePlans(QueryPlanStepPtr step, std::vector plans) } for (auto & plan : plans) - nodes.insert(nodes.end(), plan.nodes.begin(), plan.nodes.end()); + nodes.splice(nodes.end(), std::move(plan.nodes)); nodes.emplace_back(Node{.step = std::move(step)}); root = &nodes.back(); From d0cfa35ab0eb9b79bb16b0897c593ca64de9f5f9 Mon Sep 17 00:00:00 2001 From: Evgenia Sudarikova <56156889+otrazhenia@users.noreply.github.com> Date: Thu, 18 Jun 2020 22:11:47 +0300 Subject: [PATCH 0422/1102] DOCSUP-1316 (#122) * add EN version * change links in EN version * changes after review * add RU description * minor changes --- docs/en/operations/settings/settings.md | 30 +++++++++++++++++++++++++ docs/ru/operations/settings/settings.md | 30 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index f29866d49804..8ae7a5573ecf 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1446,4 +1446,34 @@ Possible values: Default value: 0. +## min_insert_block_size_rows_for_materialized_views {#min-insert-block-size-rows-for-materialized-views} + +Sets minimum number of rows in block which can be inserted into a table by an `INSERT` query. Smaller-sized blocks are squashed into bigger ones. This setting is applied only for blocks inserted into [materialized view](../../sql-reference/statements/create.md#create-view). By adjusting this setting, you control blocks squashing while pushing to materialized view and avoid excessive memory usage. + +Possible values: + +- Any positive integer. +- 0 — Squashing disabled. + +Default value: 1048576. + +**See Also** + +- [min_insert_block_size_rows](#min-insert-block-size-rows) + +## min_insert_block_size_bytes_for_materialized_views {#min-insert-block-size-bytes-for-materialized-views} + +Sets minimum number of bytes in block which can be inserted into a table by an `INSERT` query. Smaller-sized blocks are squashed into bigger ones. This setting is applied only for blocks inserted into [materialized view](../../sql-reference/statements/create.md#create-view). By adjusting this setting, you control blocks squashing while pushing to materialized view and avoid excessive memory usage. + +Possible values: + +- Any positive integer. +- 0 — Squashing disabled. + +Default value: 268435456. + +**See also** + +- [min_insert_block_size_bytes](#min-insert-block-size-bytes) + [Original article](https://clickhouse.tech/docs/en/operations/settings/settings/) diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index 5e34affcaac3..26ad9b3d9ea7 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -1221,4 +1221,34 @@ Default value: 0. Значение по умолчанию: 16. +## min_insert_block_size_rows_for_materialized_views {#min-insert-block-size-rows-for-materialized-views} + +Устанавливает минимальное количество строк в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет контролировать склеивание блоков и использовать меньше памяти. + +Допустимые значения: + +- Положительное целое число. +- 0 — Склейка блоков выключена. + +Значение по умолчанию: 1048576. + +**См. также:** + +- [min_insert_block_size_rows](#min-insert-block-size-rows) + +## min_insert_block_size_bytes_for_materialized_views {#min-insert-block-size-bytes-for-materialized-views} + +Устанавливает минимальное количество байтов в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет контролировать склеивание блоков и использовать меньше памяти. + +Допустимые значения: + +- Положительное целое число. +- 0 — Склейка блоков выключена. + +Значение по умолчанию: 268435456. + +**См. также:** + +- [min_insert_block_size_bytes](#min-insert-block-size-bytes) + [Оригинальная статья](https://clickhouse.tech/docs/ru/operations/settings/settings/) From 26161aac8a061129bcab7b1f0781dbf536e5fec5 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 22:17:21 +0300 Subject: [PATCH 0423/1102] Fix ReadFromStorageStep --- src/Processors/QueryPlan/ReadFromStorageStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.cpp b/src/Processors/QueryPlan/ReadFromStorageStep.cpp index 14c95cbe2c23..8d8a9bf717b7 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.cpp +++ b/src/Processors/QueryPlan/ReadFromStorageStep.cpp @@ -119,7 +119,7 @@ ReadFromStorageStep::ReadFromStorageStep( pipeline->init(std::move(pipes)); - input_streams.emplace_back(DataStream{.header = pipeline->getHeader()}); + output_stream = DataStream{.header = pipeline->getHeader()}; } ReadFromStorageStep::~ReadFromStorageStep() = default; From 5ce12d8d2b0b1c1d5907965123a9df88c10abe72 Mon Sep 17 00:00:00 2001 From: Sergei Shtykov Date: Thu, 18 Jun 2020 22:21:30 +0300 Subject: [PATCH 0424/1102] CLICKHOUSEDOCS-658: Minor fix. --- docs/ru/operations/settings/settings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index 26ad9b3d9ea7..3cc0cd0f6d8c 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -1223,7 +1223,7 @@ Default value: 0. ## min_insert_block_size_rows_for_materialized_views {#min-insert-block-size-rows-for-materialized-views} -Устанавливает минимальное количество строк в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет контролировать склеивание блоков и использовать меньше памяти. +Устанавливает минимальное количество строк в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет избежать избыточного потребления памяти. Допустимые значения: @@ -1238,7 +1238,7 @@ Default value: 0. ## min_insert_block_size_bytes_for_materialized_views {#min-insert-block-size-bytes-for-materialized-views} -Устанавливает минимальное количество байтов в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет контролировать склеивание блоков и использовать меньше памяти. +Устанавливает минимальное количество байтов в блоке, который может быть вставлен в таблицу запросом `INSERT`. Блоки меньшего размера склеиваются в блоки большего размера. Настройка применяется только для блоков, вставляемых в [материализованное представление](../../sql-reference/statements/create.md#create-view). Настройка позволяет избежать избыточного потребления памяти. Допустимые значения: From de6ef17d2782c2535b6ec40fe34d0bab1c414f8e Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 22:42:28 +0300 Subject: [PATCH 0425/1102] Fix QueryPipeline::unitePipelines --- src/Processors/QueryPipeline.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Processors/QueryPipeline.cpp b/src/Processors/QueryPipeline.cpp index a4e4ab2595e3..7ad7bddb1048 100644 --- a/src/Processors/QueryPipeline.cpp +++ b/src/Processors/QueryPipeline.cpp @@ -643,6 +643,8 @@ void QueryPipeline::unitePipelines( else totals_having_port = uniteTotals(totals, current_header, processors); } + + current_header = common_header; } void QueryPipeline::setProgressCallback(const ProgressCallback & callback) From 009977c20c1651ccad992240428aa8388850358c Mon Sep 17 00:00:00 2001 From: alesapin Date: Thu, 18 Jun 2020 22:45:37 +0300 Subject: [PATCH 0426/1102] Fix locks --- src/Storages/LiveView/StorageLiveView.cpp | 2 +- src/Storages/StorageReplicatedMergeTree.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index 0abb01d7dc70..f4f3c6b8642b 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -514,7 +514,7 @@ void StorageLiveView::drop() void StorageLiveView::refresh(const Context & context) { - auto alter_lock = lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + auto table_lock = lockExclusively(context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); { std::lock_guard lock(mutex); if (getNewBlocks()) diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index c1534d9eed65..c256a79dfe3f 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -3634,7 +3634,7 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer zookeeper->multi(requests); { - auto alter_lock = lockExclusively(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); + auto lock = lockForAlter(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); auto metadata_diff = ReplicatedMergeTreeTableMetadata(*this, getInMemoryMetadataPtr()).checkAndFindDiff(metadata_from_entry); From 1f013208dbb7b2f0cf51d47404e0014af5db6036 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Thu, 18 Jun 2020 23:11:42 +0300 Subject: [PATCH 0427/1102] Fix style. --- src/Processors/QueryPlan/AddingDelayedStreamStep.cpp | 3 ++- src/Processors/QueryPlan/AggregatingStep.cpp | 3 ++- src/Processors/QueryPlan/ConvertingStep.cpp | 3 ++- src/Processors/QueryPlan/CreatingSetsStep.cpp | 3 ++- src/Processors/QueryPlan/CubeStep.cpp | 3 ++- src/Processors/QueryPlan/DistinctStep.cpp | 3 ++- src/Processors/QueryPlan/ExpressionStep.cpp | 3 ++- src/Processors/QueryPlan/ExtremesStep.cpp | 3 ++- src/Processors/QueryPlan/FillingStep.cpp | 3 ++- src/Processors/QueryPlan/FilterStep.cpp | 3 ++- src/Processors/QueryPlan/FinishSortingStep.cpp | 3 ++- src/Processors/QueryPlan/LimitByStep.cpp | 3 ++- src/Processors/QueryPlan/LimitStep.cpp | 3 ++- src/Processors/QueryPlan/MergeSortingStep.cpp | 3 ++- src/Processors/QueryPlan/MergingAggregatedStep.cpp | 3 ++- src/Processors/QueryPlan/MergingSortedStep.cpp | 3 ++- src/Processors/QueryPlan/OffsetsStep.cpp | 3 ++- src/Processors/QueryPlan/PartialSortingStep.cpp | 3 ++- src/Processors/QueryPlan/RollupStep.cpp | 3 ++- src/Processors/QueryPlan/TotalsHavingStep.cpp | 3 ++- 20 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp index a1cb88d604d9..522094cb790a 100644 --- a/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp +++ b/src/Processors/QueryPlan/AddingDelayedStreamStep.cpp @@ -6,7 +6,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false }; } diff --git a/src/Processors/QueryPlan/AggregatingStep.cpp b/src/Processors/QueryPlan/AggregatingStep.cpp index 11a6b8e6cbc9..47cb444654cc 100644 --- a/src/Processors/QueryPlan/AggregatingStep.cpp +++ b/src/Processors/QueryPlan/AggregatingStep.cpp @@ -9,7 +9,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false /// Actually, we may check that distinct names are in aggregation keys }; } diff --git a/src/Processors/QueryPlan/ConvertingStep.cpp b/src/Processors/QueryPlan/ConvertingStep.cpp index 312210fd1493..99287c50b90e 100644 --- a/src/Processors/QueryPlan/ConvertingStep.cpp +++ b/src/Processors/QueryPlan/ConvertingStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/CreatingSetsStep.cpp b/src/Processors/QueryPlan/CreatingSetsStep.cpp index ebf94180a7b4..4480fd53f323 100644 --- a/src/Processors/QueryPlan/CreatingSetsStep.cpp +++ b/src/Processors/QueryPlan/CreatingSetsStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/CubeStep.cpp b/src/Processors/QueryPlan/CubeStep.cpp index 124ef78b279b..1e95a6086558 100644 --- a/src/Processors/QueryPlan/CubeStep.cpp +++ b/src/Processors/QueryPlan/CubeStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false }; } diff --git a/src/Processors/QueryPlan/DistinctStep.cpp b/src/Processors/QueryPlan/DistinctStep.cpp index 5fd26ce73b20..0e1cab637fa6 100644 --- a/src/Processors/QueryPlan/DistinctStep.cpp +++ b/src/Processors/QueryPlan/DistinctStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/ExpressionStep.cpp b/src/Processors/QueryPlan/ExpressionStep.cpp index c4823f62ea3c..75c07554318f 100644 --- a/src/Processors/QueryPlan/ExpressionStep.cpp +++ b/src/Processors/QueryPlan/ExpressionStep.cpp @@ -9,7 +9,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits(const ExpressionActionsPtr & expression) { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = !expression->hasJoinOrArrayJoin() }; } diff --git a/src/Processors/QueryPlan/ExtremesStep.cpp b/src/Processors/QueryPlan/ExtremesStep.cpp index 33b08c0da7a2..61777f4241a0 100644 --- a/src/Processors/QueryPlan/ExtremesStep.cpp +++ b/src/Processors/QueryPlan/ExtremesStep.cpp @@ -6,7 +6,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/FillingStep.cpp b/src/Processors/QueryPlan/FillingStep.cpp index e5562378234b..80dba7949436 100644 --- a/src/Processors/QueryPlan/FillingStep.cpp +++ b/src/Processors/QueryPlan/FillingStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false /// TODO: it seem to actually be true. Check it later. }; } diff --git a/src/Processors/QueryPlan/FilterStep.cpp b/src/Processors/QueryPlan/FilterStep.cpp index 926c907c53ba..38ab4471e531 100644 --- a/src/Processors/QueryPlan/FilterStep.cpp +++ b/src/Processors/QueryPlan/FilterStep.cpp @@ -8,7 +8,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits(const ExpressionActionsPtr & expression) { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = !expression->hasJoinOrArrayJoin() /// I suppose it actually never happens }; } diff --git a/src/Processors/QueryPlan/FinishSortingStep.cpp b/src/Processors/QueryPlan/FinishSortingStep.cpp index 194f315b728f..400d45125d5b 100644 --- a/src/Processors/QueryPlan/FinishSortingStep.cpp +++ b/src/Processors/QueryPlan/FinishSortingStep.cpp @@ -10,7 +10,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/LimitByStep.cpp b/src/Processors/QueryPlan/LimitByStep.cpp index 1ce2c8964fb9..e18df84258ec 100644 --- a/src/Processors/QueryPlan/LimitByStep.cpp +++ b/src/Processors/QueryPlan/LimitByStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/LimitStep.cpp b/src/Processors/QueryPlan/LimitStep.cpp index aef7f89c7a5e..4b7928bfc5a0 100644 --- a/src/Processors/QueryPlan/LimitStep.cpp +++ b/src/Processors/QueryPlan/LimitStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/MergeSortingStep.cpp b/src/Processors/QueryPlan/MergeSortingStep.cpp index d34fac4d3ca6..4c8f265c2ca1 100644 --- a/src/Processors/QueryPlan/MergeSortingStep.cpp +++ b/src/Processors/QueryPlan/MergeSortingStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.cpp b/src/Processors/QueryPlan/MergingAggregatedStep.cpp index 6cf1c7608586..0d169073e7e0 100644 --- a/src/Processors/QueryPlan/MergingAggregatedStep.cpp +++ b/src/Processors/QueryPlan/MergingAggregatedStep.cpp @@ -9,7 +9,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false }; } diff --git a/src/Processors/QueryPlan/MergingSortedStep.cpp b/src/Processors/QueryPlan/MergingSortedStep.cpp index 4157972149f6..9c4bf874510c 100644 --- a/src/Processors/QueryPlan/MergingSortedStep.cpp +++ b/src/Processors/QueryPlan/MergingSortedStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/OffsetsStep.cpp b/src/Processors/QueryPlan/OffsetsStep.cpp index a98b996b8e9e..e09a169f4bd2 100644 --- a/src/Processors/QueryPlan/OffsetsStep.cpp +++ b/src/Processors/QueryPlan/OffsetsStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/PartialSortingStep.cpp b/src/Processors/QueryPlan/PartialSortingStep.cpp index ee68415d3fc2..c8be58eb3245 100644 --- a/src/Processors/QueryPlan/PartialSortingStep.cpp +++ b/src/Processors/QueryPlan/PartialSortingStep.cpp @@ -8,7 +8,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } diff --git a/src/Processors/QueryPlan/RollupStep.cpp b/src/Processors/QueryPlan/RollupStep.cpp index 0bca6b42ff63..5e940068ef8b 100644 --- a/src/Processors/QueryPlan/RollupStep.cpp +++ b/src/Processors/QueryPlan/RollupStep.cpp @@ -7,7 +7,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = false }; } diff --git a/src/Processors/QueryPlan/TotalsHavingStep.cpp b/src/Processors/QueryPlan/TotalsHavingStep.cpp index d1b6fda3133a..ec3788bc7d37 100644 --- a/src/Processors/QueryPlan/TotalsHavingStep.cpp +++ b/src/Processors/QueryPlan/TotalsHavingStep.cpp @@ -8,7 +8,8 @@ namespace DB static ITransformingStep::DataStreamTraits getTraits() { - return ITransformingStep::DataStreamTraits{ + return ITransformingStep::DataStreamTraits + { .preserves_distinct_columns = true }; } From 6708ae75f35972eaa894921ad5e1024b35423b97 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 11:30:21 +0300 Subject: [PATCH 0428/1102] Fix build. --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index b24f234a9f50..c11cbbe8c4d5 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -981,7 +981,7 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu /// because in that case columns from 'ORDER BY' are used. if (query.limit_with_ties) { - executeLimit(pipeline); + executeLimit(query_plan); has_prelimit = true; } From 9609bd9dee23450494cc03621283ea8cf391fa9b Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Thu, 11 Jun 2020 02:51:27 +0200 Subject: [PATCH 0429/1102] Kafka better states, formats based on PeekableReadBuffer, and other minor fixes. Add formats tests, fixes for JSONCompactEachRowWithNamesAndTypes, TSVWithNamesAndTypes. Some CR fixes Add sanitizing for kafka_max_block_size and kafka_poll_max_batch_size --- src/IO/PeekableReadBuffer.cpp | 13 + src/IO/PeekableReadBuffer.h | 12 +- .../Impl/JSONAsStringRowInputFormat.cpp | 6 + .../Formats/Impl/JSONAsStringRowInputFormat.h | 1 + .../Impl/JSONCompactEachRowRowInputFormat.cpp | 12 +- .../Impl/JSONCompactEachRowRowInputFormat.h | 2 +- .../Formats/Impl/MsgPackRowInputFormat.cpp | 12 + .../Formats/Impl/MsgPackRowInputFormat.h | 6 +- .../Formats/Impl/RegexpRowInputFormat.cpp | 13 +- .../Formats/Impl/RegexpRowInputFormat.h | 5 +- .../Impl/TabSeparatedRowInputFormat.cpp | 3 +- .../Formats/Impl/TemplateRowInputFormat.cpp | 1 + .../Formats/Impl/TemplateRowInputFormat.h | 10 +- .../Formats/Impl/ValuesBlockInputFormat.cpp | 1 + .../Formats/Impl/ValuesBlockInputFormat.h | 6 +- src/Storages/Kafka/KafkaBlockInputStream.cpp | 110 ++-- src/Storages/Kafka/KafkaBlockInputStream.h | 7 +- .../Kafka/ReadBufferFromKafkaConsumer.cpp | 194 ++++--- .../Kafka/ReadBufferFromKafkaConsumer.h | 42 +- src/Storages/Kafka/StorageKafka.cpp | 19 +- .../format_schemas/template_row.format | 1 + .../clickhouse_path/format_schemas/test.avsc | 11 + .../clickhouse_path/format_schemas/test.capnp | 10 + .../clickhouse_path/format_schemas/test.proto | 9 + tests/integration/test_storage_kafka/test.py | 499 +++++++++++++++++- 25 files changed, 827 insertions(+), 178 deletions(-) create mode 100644 tests/integration/test_storage_kafka/clickhouse_path/format_schemas/template_row.format create mode 100644 tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.avsc create mode 100644 tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.capnp create mode 100644 tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.proto diff --git a/src/IO/PeekableReadBuffer.cpp b/src/IO/PeekableReadBuffer.cpp index 9180ab94fb74..dd969d075494 100644 --- a/src/IO/PeekableReadBuffer.cpp +++ b/src/IO/PeekableReadBuffer.cpp @@ -20,6 +20,19 @@ PeekableReadBuffer::PeekableReadBuffer(ReadBuffer & sub_buf_, size_t start_size_ checkStateCorrect(); } +void PeekableReadBuffer::reset() +{ + peeked_size = 0; + checkpoint = nullptr; + checkpoint_in_own_memory = false; + + if (!currentlyReadFromOwnMemory()) + sub_buf.position() = pos; + + Buffer & sub_working = sub_buf.buffer(); + BufferBase::set(sub_working.begin(), sub_working.size(), sub_buf.offset()); +} + bool PeekableReadBuffer::peekNext() { checkStateCorrect(); diff --git a/src/IO/PeekableReadBuffer.h b/src/IO/PeekableReadBuffer.h index 49c59c934862..62b6f08f6218 100644 --- a/src/IO/PeekableReadBuffer.h +++ b/src/IO/PeekableReadBuffer.h @@ -12,7 +12,8 @@ namespace ErrorCodes /// Also allows to set checkpoint at some position in stream and come back to this position later. /// When next() is called, saves data between checkpoint and current position to own memory and loads next data to sub-buffer -/// Sub-buffer should not be accessed directly during the lifetime of peekable buffer. +/// Sub-buffer should not be accessed directly during the lifetime of peekable buffer (unless +/// you reset() the state of peekable buffer after each change of underlying buffer) /// If position() of peekable buffer is explicitly set to some position before checkpoint /// (e.g. by istr.position() = prev_pos), behavior is undefined. class PeekableReadBuffer : public BufferWithOwnMemory @@ -38,6 +39,11 @@ class PeekableReadBuffer : public BufferWithOwnMemory peeked_size = 0; } checkpoint = pos; + + // FIXME: we are checking checkpoint existence in few places (rollbackToCheckpoint/dropCheckpoint) + // by simple if(checkpoint) but checkpoint can be nullptr after + // setCheckpoint called on empty (non initialized/eof) buffer + // and we can't just use simple if(checkpoint) } /// Forget checkpoint and all data between checkpoint and position @@ -68,6 +74,10 @@ class PeekableReadBuffer : public BufferWithOwnMemory /// This data will be lost after destruction of peekable buffer. bool hasUnreadData() const; + // for streaming reading (like in Kafka) we need to restore initial state of the buffer + // w/o recreating the buffer. + void reset(); + private: bool nextImpl() override; diff --git a/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.cpp b/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.cpp index 46fcb9a2f776..42fa764f011c 100644 --- a/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.cpp @@ -21,6 +21,12 @@ JSONAsStringRowInputFormat::JSONAsStringRowInputFormat(const Block & header_, Re } } +void JSONAsStringRowInputFormat::resetParser() +{ + IRowInputFormat::resetParser(); + buf.reset(); +} + void JSONAsStringRowInputFormat::readJSONObject(IColumn & column) { PeekableReadBufferCheckpoint checkpoint{buf}; diff --git a/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.h b/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.h index c28e7624443a..c15a769343cd 100644 --- a/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.h +++ b/src/Processors/Formats/Impl/JSONAsStringRowInputFormat.h @@ -20,6 +20,7 @@ class JSONAsStringRowInputFormat : public IRowInputFormat bool readRow(MutableColumns & columns, RowReadExtension & ext) override; String getName() const override { return "JSONAsStringRowInputFormat"; } + void resetParser() override; private: void readJSONObject(IColumn & column); diff --git a/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.cpp b/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.cpp index 2895abc29f12..82e3cb795bf4 100644 --- a/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.cpp @@ -22,8 +22,6 @@ JSONCompactEachRowRowInputFormat::JSONCompactEachRowRowInputFormat(ReadBuffer & bool with_names_) : IRowInputFormat(header_, in_, std::move(params_)), format_settings(format_settings_), with_names(with_names_) { - /// In this format, BOM at beginning of stream cannot be confused with value, so it is safe to skip it. - skipBOMIfExists(in); const auto & sample = getPort().getHeader(); size_t num_columns = sample.columns(); @@ -39,8 +37,18 @@ JSONCompactEachRowRowInputFormat::JSONCompactEachRowRowInputFormat(ReadBuffer & } } +void JSONCompactEachRowRowInputFormat::resetParser() +{ + IRowInputFormat::resetParser(); + column_indexes_for_input_fields.clear(); + not_seen_columns.clear(); +} + void JSONCompactEachRowRowInputFormat::readPrefix() { + /// In this format, BOM at beginning of stream cannot be confused with value, so it is safe to skip it. + skipBOMIfExists(in); + if (with_names) { size_t num_columns = getPort().getHeader().columns(); diff --git a/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.h b/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.h index e633475d0f4b..5c864ebc7513 100644 --- a/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.h +++ b/src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.h @@ -26,7 +26,7 @@ class JSONCompactEachRowRowInputFormat : public IRowInputFormat bool readRow(MutableColumns & columns, RowReadExtension & ext) override; bool allowSyncAfterError() const override { return true; } void syncAfterError() override; - + void resetParser() override; private: void addInputColumn(const String & column_name); diff --git a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp index 3e112fb1ce65..4581a82f06e2 100644 --- a/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/MsgPackRowInputFormat.cpp @@ -26,6 +26,13 @@ namespace ErrorCodes MsgPackRowInputFormat::MsgPackRowInputFormat(const Block & header_, ReadBuffer & in_, Params params_) : IRowInputFormat(header_, in_, std::move(params_)), buf(in), parser(visitor), data_types(header_.getDataTypes()) {} +void MsgPackRowInputFormat::resetParser() +{ + IRowInputFormat::resetParser(); + buf.reset(); + visitor.reset(); +} + void MsgPackVisitor::set_info(IColumn & column, DataTypePtr type) // NOLINT { while (!info_stack.empty()) @@ -35,6 +42,11 @@ void MsgPackVisitor::set_info(IColumn & column, DataTypePtr type) // NOLINT info_stack.push(Info{column, type}); } +void MsgPackVisitor::reset() +{ + info_stack = {}; +} + void MsgPackVisitor::insert_integer(UInt64 value) // NOLINT { Info & info = info_stack.top(); diff --git a/src/Processors/Formats/Impl/MsgPackRowInputFormat.h b/src/Processors/Formats/Impl/MsgPackRowInputFormat.h index 92e4f5d0bd70..454d42fae3d8 100644 --- a/src/Processors/Formats/Impl/MsgPackRowInputFormat.h +++ b/src/Processors/Formats/Impl/MsgPackRowInputFormat.h @@ -37,6 +37,8 @@ class MsgPackVisitor : public msgpack::null_visitor void insert_integer(UInt64 value); + void reset(); + private: /// Stack is needed to process nested arrays std::stack info_stack; @@ -49,13 +51,15 @@ class MsgPackRowInputFormat : public IRowInputFormat bool readRow(MutableColumns & columns, RowReadExtension & ext) override; String getName() const override { return "MagPackRowInputFormat"; } + void resetParser() override; + private: bool readObject(); PeekableReadBuffer buf; MsgPackVisitor visitor; msgpack::detail::parse_helper parser; - DataTypes data_types; + const DataTypes data_types; }; } diff --git a/src/Processors/Formats/Impl/RegexpRowInputFormat.cpp b/src/Processors/Formats/Impl/RegexpRowInputFormat.cpp index 899630410a4b..7763de1642da 100644 --- a/src/Processors/Formats/Impl/RegexpRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/RegexpRowInputFormat.cpp @@ -15,7 +15,11 @@ namespace ErrorCodes RegexpRowInputFormat::RegexpRowInputFormat( ReadBuffer & in_, const Block & header_, Params params_, const FormatSettings & format_settings_) - : IRowInputFormat(header_, in_, std::move(params_)), buf(in_), format_settings(format_settings_), regexp(format_settings_.regexp.regexp) + : IRowInputFormat(header_, in_, std::move(params_)) + , buf(in_) + , format_settings(format_settings_) + , field_format(stringToFormat(format_settings_.regexp.escaping_rule)) + , regexp(format_settings_.regexp.regexp) { size_t fields_count = regexp.NumberOfCapturingGroups(); matched_fields.resize(fields_count); @@ -28,8 +32,13 @@ RegexpRowInputFormat::RegexpRowInputFormat( // Save pointer to argument. re2_arguments_ptrs[i] = &re2_arguments[i]; } +} + - field_format = stringToFormat(format_settings_.regexp.escaping_rule); +void RegexpRowInputFormat::resetParser() +{ + IRowInputFormat::resetParser(); + buf.reset(); } RegexpRowInputFormat::ColumnFormat RegexpRowInputFormat::stringToFormat(const String & format) diff --git a/src/Processors/Formats/Impl/RegexpRowInputFormat.h b/src/Processors/Formats/Impl/RegexpRowInputFormat.h index 8f9ecdc1349d..0cd8778e4994 100644 --- a/src/Processors/Formats/Impl/RegexpRowInputFormat.h +++ b/src/Processors/Formats/Impl/RegexpRowInputFormat.h @@ -32,6 +32,7 @@ class RegexpRowInputFormat : public IRowInputFormat String getName() const override { return "RegexpRowInputFormat"; } bool readRow(MutableColumns & columns, RowReadExtension & ext) override; + void resetParser() override; private: bool readField(size_t index, MutableColumns & columns); @@ -40,9 +41,9 @@ class RegexpRowInputFormat : public IRowInputFormat PeekableReadBuffer buf; const FormatSettings format_settings; - ColumnFormat field_format; + const ColumnFormat field_format; - RE2 regexp; + const RE2 regexp; // The vector of fields extracted from line using regexp. std::vector matched_fields; // These two vectors are needed to use RE2::FullMatchN (function for extracting fields). diff --git a/src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp b/src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp index dbbd1e28aa0b..96378119aeb5 100644 --- a/src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp @@ -340,8 +340,9 @@ void TabSeparatedRowInputFormat::syncAfterError() void TabSeparatedRowInputFormat::resetParser() { RowInputFormatWithDiagnosticInfo::resetParser(); + const auto & sample = getPort().getHeader(); + read_columns.assign(sample.columns(), false); column_indexes_for_input_fields.clear(); - read_columns.clear(); columns_to_fill_with_default_values.clear(); } diff --git a/src/Processors/Formats/Impl/TemplateRowInputFormat.cpp b/src/Processors/Formats/Impl/TemplateRowInputFormat.cpp index 271b9ea75c5a..ecb529a99af7 100644 --- a/src/Processors/Formats/Impl/TemplateRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/TemplateRowInputFormat.cpp @@ -498,6 +498,7 @@ void TemplateRowInputFormat::resetParser() { RowInputFormatWithDiagnosticInfo::resetParser(); end_of_stream = false; + buf.reset(); } void registerInputFormatProcessorTemplate(FormatFactory & factory) diff --git a/src/Processors/Formats/Impl/TemplateRowInputFormat.h b/src/Processors/Formats/Impl/TemplateRowInputFormat.h index efdf6eb5e6d9..6adfe0a34b44 100644 --- a/src/Processors/Formats/Impl/TemplateRowInputFormat.h +++ b/src/Processors/Formats/Impl/TemplateRowInputFormat.h @@ -50,19 +50,19 @@ class TemplateRowInputFormat : public RowInputFormatWithDiagnosticInfo private: PeekableReadBuffer buf; - DataTypes data_types; + const DataTypes data_types; FormatSettings settings; const bool ignore_spaces; - ParsedTemplateFormatString format; - ParsedTemplateFormatString row_format; + const ParsedTemplateFormatString format; + const ParsedTemplateFormatString row_format; size_t format_data_idx; bool end_of_stream = false; std::vector always_default_columns; - char default_csv_delimiter; + const char default_csv_delimiter; - std::string row_between_delimiter; + const std::string row_between_delimiter; }; } diff --git a/src/Processors/Formats/Impl/ValuesBlockInputFormat.cpp b/src/Processors/Formats/Impl/ValuesBlockInputFormat.cpp index 2a4d451d45a5..2e2c98c63d27 100644 --- a/src/Processors/Formats/Impl/ValuesBlockInputFormat.cpp +++ b/src/Processors/Formats/Impl/ValuesBlockInputFormat.cpp @@ -416,6 +416,7 @@ void ValuesBlockInputFormat::resetParser() IInputFormat::resetParser(); // I'm not resetting parser modes here. // There is a good chance that all messages have the same format. + buf.reset(); total_rows = 0; } diff --git a/src/Processors/Formats/Impl/ValuesBlockInputFormat.h b/src/Processors/Formats/Impl/ValuesBlockInputFormat.h index 059a15e1e864..f485870fc69d 100644 --- a/src/Processors/Formats/Impl/ValuesBlockInputFormat.h +++ b/src/Processors/Formats/Impl/ValuesBlockInputFormat.h @@ -70,12 +70,12 @@ class ValuesBlockInputFormat final : public IInputFormat private: PeekableReadBuffer buf; - RowInputFormatParams params; + const RowInputFormatParams params; std::unique_ptr context; /// pimpl const FormatSettings format_settings; - size_t num_columns; + const size_t num_columns; size_t total_rows = 0; std::vector parser_type_for_column; @@ -87,7 +87,7 @@ class ValuesBlockInputFormat final : public IInputFormat ConstantExpressionTemplates templates; ConstantExpressionTemplate::Cache templates_cache; - DataTypes types; + const DataTypes types; BlockMissingValues block_missing_values; }; diff --git a/src/Storages/Kafka/KafkaBlockInputStream.cpp b/src/Storages/Kafka/KafkaBlockInputStream.cpp index 3edfcc7b9d2e..711a14efb37a 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockInputStream.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace DB { @@ -12,11 +13,17 @@ namespace ErrorCodes { extern const int LOGICAL_ERROR; } + +// with default poll timeout (500ms) it will give about 5 sec delay for doing 10 retries +// when selecting from empty topic +const auto MAX_FAILED_POLL_ATTEMPTS = 10; + KafkaBlockInputStream::KafkaBlockInputStream( - StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix_) + StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, Poco::Logger * log_, size_t max_block_size_, bool commit_in_suffix_) : storage(storage_) , context(context_) , column_names(columns) + , log(log_) , max_block_size(max_block_size_) , commit_in_suffix(commit_in_suffix_) , non_virtual_header(storage.getSampleBlockNonMaterialized()) @@ -117,68 +124,75 @@ Block KafkaBlockInputStream::readImpl() }; size_t total_rows = 0; + size_t failed_poll_attempts = 0; while (true) { - // some formats (like RowBinaryWithNamesAndTypes / CSVWithNames) - // throw an exception from readPrefix when buffer in empty - if (buffer->eof()) - break; - - auto new_rows = read_kafka_message(); + auto new_rows = buffer->poll() ? read_kafka_message() : 0; - buffer->storeLastReadMessageOffset(); + if (new_rows) + { + buffer->storeLastReadMessageOffset(); - auto topic = buffer->currentTopic(); - auto key = buffer->currentKey(); - auto offset = buffer->currentOffset(); - auto partition = buffer->currentPartition(); - auto timestamp_raw = buffer->currentTimestamp(); - auto header_list = buffer->currentHeaderList(); + auto topic = buffer->currentTopic(); + auto key = buffer->currentKey(); + auto offset = buffer->currentOffset(); + auto partition = buffer->currentPartition(); + auto timestamp_raw = buffer->currentTimestamp(); + auto header_list = buffer->currentHeaderList(); - Array headers_names; - Array headers_values; + Array headers_names; + Array headers_values; - if (!header_list.empty()) - { - headers_names.reserve(header_list.size()); - headers_values.reserve(header_list.size()); - for (const auto & header : header_list) + if (!header_list.empty()) { - headers_names.emplace_back(header.get_name()); - headers_values.emplace_back(static_cast(header.get_value())); + headers_names.reserve(header_list.size()); + headers_values.reserve(header_list.size()); + for (const auto & header : header_list) + { + headers_names.emplace_back(header.get_name()); + headers_values.emplace_back(static_cast(header.get_value())); + } } - } - for (size_t i = 0; i < new_rows; ++i) - { - virtual_columns[0]->insert(topic); - virtual_columns[1]->insert(key); - virtual_columns[2]->insert(offset); - virtual_columns[3]->insert(partition); - if (timestamp_raw) + for (size_t i = 0; i < new_rows; ++i) { - auto ts = timestamp_raw->get_timestamp(); - virtual_columns[4]->insert(std::chrono::duration_cast(ts).count()); - virtual_columns[5]->insert(DecimalField(std::chrono::duration_cast(ts).count(),3)); - } - else - { - virtual_columns[4]->insertDefault(); - virtual_columns[5]->insertDefault(); + virtual_columns[0]->insert(topic); + virtual_columns[1]->insert(key); + virtual_columns[2]->insert(offset); + virtual_columns[3]->insert(partition); + if (timestamp_raw) + { + auto ts = timestamp_raw->get_timestamp(); + virtual_columns[4]->insert(std::chrono::duration_cast(ts).count()); + virtual_columns[5]->insert(DecimalField(std::chrono::duration_cast(ts).count(),3)); + } + else + { + virtual_columns[4]->insertDefault(); + virtual_columns[5]->insertDefault(); + } + virtual_columns[6]->insert(headers_names); + virtual_columns[7]->insert(headers_values); } - virtual_columns[6]->insert(headers_names); - virtual_columns[7]->insert(headers_values); - } - - total_rows = total_rows + new_rows; - buffer->allowNext(); - if (buffer->hasMorePolledMessages()) + total_rows = total_rows + new_rows; + } + else if (buffer->isStalled()) { - continue; + failed_poll_attempts++; } - if (total_rows >= max_block_size || !checkTimeLimit()) + else if (buffer->polledDataUnusable()) + { + break; + } + else + { + LOG_WARNING(log, "Parsing of message (topic: {}, partition: {}, offset: {}) return no rows.", buffer->currentTopic(), buffer->currentPartition(), buffer->currentOffset()); + } + + if (!buffer->hasMorePolledMessages() + && (total_rows >= max_block_size || !checkTimeLimit() || failed_poll_attempts >= MAX_FAILED_POLL_ATTEMPTS)) { break; } diff --git a/src/Storages/Kafka/KafkaBlockInputStream.h b/src/Storages/Kafka/KafkaBlockInputStream.h index 387f5088721d..012d1f60f430 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.h +++ b/src/Storages/Kafka/KafkaBlockInputStream.h @@ -7,6 +7,10 @@ #include +namespace Poco +{ + class Logger; +} namespace DB { @@ -14,7 +18,7 @@ class KafkaBlockInputStream : public IBlockInputStream { public: KafkaBlockInputStream( - StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix = true); + StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, Poco::Logger * log_, size_t max_block_size_, bool commit_in_suffix = true); ~KafkaBlockInputStream() override; String getName() const override { return storage.getName(); } @@ -31,6 +35,7 @@ class KafkaBlockInputStream : public IBlockInputStream StorageKafka & storage; const std::shared_ptr context; Names column_names; + Poco::Logger * log; UInt64 max_block_size; ConsumerBufferPtr buffer; diff --git a/src/Storages/Kafka/ReadBufferFromKafkaConsumer.cpp b/src/Storages/Kafka/ReadBufferFromKafkaConsumer.cpp index 31a9f55350da..3fd28cde5e5d 100644 --- a/src/Storages/Kafka/ReadBufferFromKafkaConsumer.cpp +++ b/src/Storages/Kafka/ReadBufferFromKafkaConsumer.cpp @@ -15,6 +15,7 @@ namespace ErrorCodes using namespace std::chrono_literals; const auto MAX_TIME_TO_WAIT_FOR_ASSIGNMENT_MS = 15000; +const std::size_t POLL_TIMEOUT_WO_ASSIGNMENT_MS = 50; const auto DRAIN_TIMEOUT_MS = 5000ms; @@ -57,12 +58,11 @@ ReadBufferFromKafkaConsumer::ReadBufferFromKafkaConsumer( // * clean buffered non-commited messages // * set flag / flush - messages.clear(); - current = messages.begin(); - BufferBase::set(nullptr, 0, 0); + cleanUnprocessed(); - rebalance_happened = true; + stalled_status = REBALANCE_HAPPENED; assignment.clear(); + waited_for_assignment = 0; // for now we use slower (but reliable) sync commit in main loop, so no need to repeat // try @@ -225,7 +225,6 @@ void ReadBufferFromKafkaConsumer::commit() } offsets_stored = 0; - stalled = false; } void ReadBufferFromKafkaConsumer::subscribe() @@ -252,18 +251,26 @@ void ReadBufferFromKafkaConsumer::subscribe() } } - stalled = false; - rebalance_happened = false; - offsets_stored = 0; + cleanUnprocessed(); + allowed = false; + + // we can reset any flags (except of CONSUMER_STOPPED) before attempt of reading new block of data + if (stalled_status != CONSUMER_STOPPED) + stalled_status = NO_MESSAGES_RETURNED; } -void ReadBufferFromKafkaConsumer::unsubscribe() +void ReadBufferFromKafkaConsumer::cleanUnprocessed() { - LOG_TRACE(log, "Re-joining claimed consumer after failure"); - messages.clear(); current = messages.begin(); BufferBase::set(nullptr, 0, 0); + offsets_stored = 0; +} + +void ReadBufferFromKafkaConsumer::unsubscribe() +{ + LOG_TRACE(log, "Re-joining claimed consumer after failure"); + cleanUnprocessed(); // it should not raise exception as used in destructor try @@ -284,12 +291,6 @@ void ReadBufferFromKafkaConsumer::unsubscribe() } -bool ReadBufferFromKafkaConsumer::hasMorePolledMessages() const -{ - return (!stalled) && (current != messages.end()); -} - - void ReadBufferFromKafkaConsumer::resetToLastCommitted(const char * msg) { if (assignment.empty()) @@ -302,102 +303,131 @@ void ReadBufferFromKafkaConsumer::resetToLastCommitted(const char * msg) LOG_TRACE(log, "{} Returned to committed position: {}", msg, committed_offset); } -/// Do commit messages implicitly after we processed the previous batch. -bool ReadBufferFromKafkaConsumer::nextImpl() +// it do the poll when needed +bool ReadBufferFromKafkaConsumer::poll() { + resetIfStopped(); - /// NOTE: ReadBuffer was implemented with an immutable underlying contents in mind. - /// If we failed to poll any message once - don't try again. - /// Otherwise, the |poll_timeout| expectations get flawn. + if (polledDataUnusable()) + return false; - // we can react on stop only during fetching data - // after block is formed (i.e. during copying data to MV / commiting) we ignore stop attempts - if (stopped) + if (hasMorePolledMessages()) { - was_stopped = true; - offsets_stored = 0; - return false; + allowed = true; + return true; } - if (stalled || was_stopped || !allowed || rebalance_happened) - return false; - + if (intermediate_commit) + commit(); - if (current == messages.end()) + while (true) { - if (intermediate_commit) - commit(); - - size_t waited_for_assignment = 0; - while (true) + stalled_status = NO_MESSAGES_RETURNED; + + // we already wait enough for assignment in the past, + // let's make polls shorter and not block other consumer + // which can work successfully in parallel + // POLL_TIMEOUT_WO_ASSIGNMENT_MS (50ms) is 100% enough just to check if we got assignment + // (see https://github.com/ClickHouse/ClickHouse/issues/11218) + auto actual_poll_timeout_ms = (waited_for_assignment >= MAX_TIME_TO_WAIT_FOR_ASSIGNMENT_MS) + ? std::min(POLL_TIMEOUT_WO_ASSIGNMENT_MS,poll_timeout) + : poll_timeout; + + /// Don't drop old messages immediately, since we may need them for virtual columns. + auto new_messages = consumer->poll_batch(batch_size, + std::chrono::milliseconds(actual_poll_timeout_ms)); + + resetIfStopped(); + if (stalled_status == CONSUMER_STOPPED) { - /// Don't drop old messages immediately, since we may need them for virtual columns. - auto new_messages = consumer->poll_batch(batch_size, std::chrono::milliseconds(poll_timeout)); - - if (stopped) + return false; + } + else if (stalled_status == REBALANCE_HAPPENED) + { + if (!new_messages.empty()) { - was_stopped = true; - offsets_stored = 0; - return false; - } - else if (rebalance_happened) - { - if (!new_messages.empty()) - { - // we have polled something just after rebalance. - // we will not use current batch, so we need to return to last commited position - // otherwise we will continue polling from that position - resetToLastCommitted("Rewind last poll after rebalance."); - } - - offsets_stored = 0; - return false; + // we have polled something just after rebalance. + // we will not use current batch, so we need to return to last commited position + // otherwise we will continue polling from that position + resetToLastCommitted("Rewind last poll after rebalance."); } + return false; + } - if (new_messages.empty()) + if (new_messages.empty()) + { + // While we wait for an assignment after subscription, we'll poll zero messages anyway. + // If we're doing a manual select then it's better to get something after a wait, then immediate nothing. + if (assignment.empty()) { - // While we wait for an assignment after subscription, we'll poll zero messages anyway. - // If we're doing a manual select then it's better to get something after a wait, then immediate nothing. - if (assignment.empty()) + waited_for_assignment += poll_timeout; // slightly innaccurate, but rough calculation is ok. + if (waited_for_assignment < MAX_TIME_TO_WAIT_FOR_ASSIGNMENT_MS) { - waited_for_assignment += poll_timeout; // slightly innaccurate, but rough calculation is ok. - if (waited_for_assignment < MAX_TIME_TO_WAIT_FOR_ASSIGNMENT_MS) - { - continue; - } - else - { - LOG_TRACE(log, "Can't get assignment"); - stalled = true; - return false; - } - + continue; } else { - LOG_TRACE(log, "Stalled"); - stalled = true; + LOG_WARNING(log, "Can't get assignment. It can be caused by some issue with consumer group (not enough partitions?). Will keep trying."); + stalled_status = NO_ASSIGNMENT; return false; } + } else { - messages = std::move(new_messages); - current = messages.begin(); - LOG_TRACE(log, "Polled batch of {} messages. Offset position: {}", messages.size(), consumer->get_offsets_position(consumer->get_assignment())); - break; + LOG_TRACE(log, "Stalled"); + return false; } } + else + { + messages = std::move(new_messages); + current = messages.begin(); + LOG_TRACE(log, "Polled batch of {} messages. Offset position: {}", messages.size(), consumer->get_offsets_position(consumer->get_assignment())); + break; + } } - if (auto err = current->get_error()) + while (auto err = current->get_error()) { ++current; // TODO: should throw exception instead LOG_ERROR(log, "Consumer error: {}", err); - return false; + if (current == messages.end()) + { + LOG_ERROR(log, "No actual messages polled, errors only."); + stalled_status = ERRORS_RETURNED; + return false; + } + } + stalled_status = NOT_STALLED; + allowed = true; + return true; +} + +void ReadBufferFromKafkaConsumer::resetIfStopped() +{ + // we can react on stop only during fetching data + // after block is formed (i.e. during copying data to MV / commiting) we ignore stop attempts + if (stopped) + { + stalled_status = CONSUMER_STOPPED; + cleanUnprocessed(); } +} + +/// Do commit messages implicitly after we processed the previous batch. +bool ReadBufferFromKafkaConsumer::nextImpl() +{ + + /// NOTE: ReadBuffer was implemented with an immutable underlying contents in mind. + /// If we failed to poll any message once - don't try again. + /// Otherwise, the |poll_timeout| expectations get flawn. + resetIfStopped(); + + if (!allowed || !hasMorePolledMessages()) + return false; // XXX: very fishy place with const casting. auto * new_position = reinterpret_cast(const_cast(current->get_payload().get_data())); @@ -411,7 +441,7 @@ bool ReadBufferFromKafkaConsumer::nextImpl() void ReadBufferFromKafkaConsumer::storeLastReadMessageOffset() { - if (!stalled && !was_stopped && !rebalance_happened) + if (!isStalled()) { consumer->store_offset(*(current - 1)); ++offsets_stored; diff --git a/src/Storages/Kafka/ReadBufferFromKafkaConsumer.h b/src/Storages/Kafka/ReadBufferFromKafkaConsumer.h index 7449f58c838f..c0fe53271a2c 100644 --- a/src/Storages/Kafka/ReadBufferFromKafkaConsumer.h +++ b/src/Storages/Kafka/ReadBufferFromKafkaConsumer.h @@ -29,20 +29,33 @@ class ReadBufferFromKafkaConsumer : public ReadBuffer const Names & _topics ); ~ReadBufferFromKafkaConsumer() override; - void allowNext() { allowed = true; } // Allow to read next message. void commit(); // Commit all processed messages. void subscribe(); // Subscribe internal consumer to topics. void unsubscribe(); // Unsubscribe internal consumer in case of failure. auto pollTimeout() const { return poll_timeout; } - bool hasMorePolledMessages() const; - bool polledDataUnusable() const { return (was_stopped || rebalance_happened); } - bool isStalled() const { return stalled; } + inline bool hasMorePolledMessages() const + { + return (stalled_status == NOT_STALLED) && (current != messages.end()); + } + + inline bool polledDataUnusable() const + { + return (stalled_status != NOT_STALLED) && (stalled_status != NO_MESSAGES_RETURNED); + } + + inline bool isStalled() const { return stalled_status != NOT_STALLED; } void storeLastReadMessageOffset(); void resetToLastCommitted(const char * msg); + // Polls batch of messages from Kafka or allows to read consecutive message by nextImpl + // returns true if there are some messages to process + // return false and sets stalled to false if there are no messages to process. + // additionally sets + bool poll(); + // Return values for the message that's being read. String currentTopic() const { return current[-1].get_topic(); } String currentKey() const { return current[-1].get_key(); } @@ -54,14 +67,27 @@ class ReadBufferFromKafkaConsumer : public ReadBuffer private: using Messages = std::vector; + enum StalledStatus + { + NOT_STALLED, + NO_MESSAGES_RETURNED, + REBALANCE_HAPPENED, + CONSUMER_STOPPED, + NO_ASSIGNMENT, + ERRORS_RETURNED + }; + ConsumerPtr consumer; Poco::Logger * log; const size_t batch_size = 1; const size_t poll_timeout = 0; size_t offsets_stored = 0; - bool stalled = false; + + StalledStatus stalled_status = NO_MESSAGES_RETURNED; + bool intermediate_commit = true; bool allowed = true; + size_t waited_for_assignment = 0; const std::atomic & stopped; @@ -69,15 +95,13 @@ class ReadBufferFromKafkaConsumer : public ReadBuffer Messages messages; Messages::const_iterator current; - bool rebalance_happened = false; - - bool was_stopped = false; - // order is important, need to be destructed before consumer cppkafka::TopicPartitionList assignment; const Names topics; void drain(); + void cleanUnprocessed(); + void resetIfStopped(); bool nextImpl() override; }; diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index bb721417c5b3..0300bf2118c3 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -69,7 +69,10 @@ namespace for (const auto & key : keys) { const String key_path = path + "." + key; - const String key_name = boost::replace_all_copy(key, "_", "."); + // log_level has valid underscore, rest librdkafka setting use dot.separated.format + // which is not acceptable for XML. + // See also https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md + const String key_name = (key == "log_level") ? key : boost::replace_all_copy(key, "_", "."); conf.set(key_name, config.getString(key_path)); } } @@ -221,7 +224,7 @@ Pipes StorageKafka::read( /// TODO: probably that leads to awful performance. /// FIXME: seems that doesn't help with extra reading and committing unprocessed messages. /// TODO: rewrite KafkaBlockInputStream to KafkaSource. Now it is used in other place. - pipes.emplace_back(std::make_shared(std::make_shared(*this, modified_context, column_names, 1))); + pipes.emplace_back(std::make_shared(std::make_shared(*this, modified_context, column_names, log, 1))); } LOG_DEBUG(log, "Starting reading {} streams", pipes.size()); @@ -535,7 +538,7 @@ bool StorageKafka::streamToViews() for (size_t i = 0; i < num_created_consumers; ++i) { auto stream - = std::make_shared(*this, kafka_context, block_io.out->getHeader().getNames(), block_size, false); + = std::make_shared(*this, kafka_context, block_io.out->getHeader().getNames(), log, block_size, false); streams.emplace_back(stream); // Limit read batch to maximum block size to allow DDL @@ -668,6 +671,16 @@ void registerStorageKafka(StorageFactory & factory) throw Exception("Number of consumers can not be lower than 1", ErrorCodes::BAD_ARGUMENTS); } + if (kafka_settings->kafka_max_block_size.changed && kafka_settings->kafka_max_block_size.value < 1) + { + throw Exception("kafka_max_block_size can not be lower than 1", ErrorCodes::BAD_ARGUMENTS); + } + + if (kafka_settings->kafka_poll_max_batch_size.changed && kafka_settings->kafka_poll_max_batch_size.value < 1) + { + throw Exception("kafka_poll_max_batch_size can not be lower than 1", ErrorCodes::BAD_ARGUMENTS); + } + return StorageKafka::create(args.table_id, args.context, args.columns, std::move(kafka_settings)); }; diff --git a/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/template_row.format b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/template_row.format new file mode 100644 index 000000000000..c910aa8589e0 --- /dev/null +++ b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/template_row.format @@ -0,0 +1 @@ +(id = ${id:Escaped}, blockNo = ${blockNo:Escaped}, val1 = ${val1:CSV}, val2 = ${val2:Escaped}, val3 = ${val3:Escaped}) \ No newline at end of file diff --git a/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.avsc b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.avsc new file mode 100644 index 000000000000..caf693313de5 --- /dev/null +++ b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.avsc @@ -0,0 +1,11 @@ +{ + "type": "record", + "name": "row", + "fields": [ + {"name": "id", "type": "long"}, + {"name": "blockNo", "type": "int"}, + {"name": "val1", "type": "string"}, + {"name": "val2", "type": "float"}, + {"name": "val3", "type": "int"} + ] + } \ No newline at end of file diff --git a/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.capnp b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.capnp new file mode 100644 index 000000000000..44f1961205b1 --- /dev/null +++ b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.capnp @@ -0,0 +1,10 @@ +@0x99f75f775fe63dae; + +struct TestRecordStruct +{ + id @0 : Int64; + blockNo @1 : UInt16; + val1 @2 : Text; + val2 @3 : Float32; + val3 @4 : UInt8; +} \ No newline at end of file diff --git a/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.proto b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.proto new file mode 100644 index 000000000000..cabdff04a780 --- /dev/null +++ b/tests/integration/test_storage_kafka/clickhouse_path/format_schemas/test.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +message TestMessage { + int64 id = 1; + uint32 blockNo = 2; + string val1 = 3; + float val2 = 4; + uint32 val3 = 5; +}; diff --git a/tests/integration/test_storage_kafka/test.py b/tests/integration/test_storage_kafka/test.py index 2a1b42f8e0e3..fe9177755915 100644 --- a/tests/integration/test_storage_kafka/test.py +++ b/tests/integration/test_storage_kafka/test.py @@ -103,6 +103,410 @@ def kafka_produce_protobuf_messages(topic, start_index, num_messages): print("Produced {} messages for topic {}".format(num_messages, topic)) +@pytest.mark.timeout(180) +def test_kafka_json_as_string(kafka_cluster): + kafka_produce('kafka_json_as_string', ['{"t": 123, "e": {"x": "woof"} }', '', '{"t": 124, "e": {"x": "test"} }', '{"F1":"V1","F2":{"F21":"V21","F22":{},"F23":"V23","F24":"2019-12-24T16:28:04"},"F3":"V3"}']) + + instance.query(''' + CREATE TABLE test.kafka (field String) + ENGINE = Kafka + SETTINGS kafka_broker_list = 'kafka1:19092', + kafka_topic_list = 'kafka_json_as_string', + kafka_group_name = 'kafka_json_as_string', + kafka_format = 'JSONAsString', + kafka_flush_interval_ms=1000; + ''') + + result = instance.query('SELECT * FROM test.kafka;') + expected = '''\ +{"t": 123, "e": {"x": "woof"} } +{"t": 124, "e": {"x": "test"} } +{"F1":"V1","F2":{"F21":"V21","F22":{},"F23":"V23","F24":"2019-12-24T16:28:04"},"F3":"V3"} +''' + assert TSV(result) == TSV(expected) + assert instance.contains_in_log("Parsing of message (topic: kafka_json_as_string, partition: 0, offset: 1) return no rows") + +@pytest.mark.timeout(300) +def test_kafka_formats(kafka_cluster): + # data was dumped from clickhouse itself in a following manner + # clickhouse-client --format=Native --query='SELECT toInt64(number) as id, toUInt16( intDiv( id, 65536 ) ) as blockNo, reinterpretAsString(19777) as val1, toFloat32(0.5) as val2, toUInt8(1) as val3 from numbers(100) ORDER BY id' | xxd -ps | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + + all_formats = { + ## Text formats ## + # dumped with clickhouse-client ... | perl -pe 's/\n/\\n/; s/\t/\\t/g;' + 'JSONEachRow' : { + 'data_sample' : [ + '{"id":"0","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n', + '{"id":"1","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"2","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"3","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"4","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"5","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"6","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"7","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"8","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"9","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"10","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"11","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"12","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"13","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"14","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n{"id":"15","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n', + '{"id":"0","blockNo":0,"val1":"AM","val2":0.5,"val3":1}\n', + '' # tolerates + ], + }, + # JSONAsString doesn't fit to that test, and tested separately + 'JSONCompactEachRow' : { + 'data_sample' : [ + '["0", 0, "AM", 0.5, 1]\n', + '["1", 0, "AM", 0.5, 1]\n["2", 0, "AM", 0.5, 1]\n["3", 0, "AM", 0.5, 1]\n["4", 0, "AM", 0.5, 1]\n["5", 0, "AM", 0.5, 1]\n["6", 0, "AM", 0.5, 1]\n["7", 0, "AM", 0.5, 1]\n["8", 0, "AM", 0.5, 1]\n["9", 0, "AM", 0.5, 1]\n["10", 0, "AM", 0.5, 1]\n["11", 0, "AM", 0.5, 1]\n["12", 0, "AM", 0.5, 1]\n["13", 0, "AM", 0.5, 1]\n["14", 0, "AM", 0.5, 1]\n["15", 0, "AM", 0.5, 1]\n', + '["0", 0, "AM", 0.5, 1]\n', + '' # tolerates + ], + }, + 'JSONCompactEachRowWithNamesAndTypes' : { + 'data_sample' : [ + '["id", "blockNo", "val1", "val2", "val3"]\n["Int64", "UInt16", "String", "Float32", "UInt8"]\n["0", 0, "AM", 0.5, 1]\n', + '["id", "blockNo", "val1", "val2", "val3"]\n["Int64", "UInt16", "String", "Float32", "UInt8"]\n["1", 0, "AM", 0.5, 1]\n["2", 0, "AM", 0.5, 1]\n["3", 0, "AM", 0.5, 1]\n["4", 0, "AM", 0.5, 1]\n["5", 0, "AM", 0.5, 1]\n["6", 0, "AM", 0.5, 1]\n["7", 0, "AM", 0.5, 1]\n["8", 0, "AM", 0.5, 1]\n["9", 0, "AM", 0.5, 1]\n["10", 0, "AM", 0.5, 1]\n["11", 0, "AM", 0.5, 1]\n["12", 0, "AM", 0.5, 1]\n["13", 0, "AM", 0.5, 1]\n["14", 0, "AM", 0.5, 1]\n["15", 0, "AM", 0.5, 1]\n', + '["id", "blockNo", "val1", "val2", "val3"]\n["Int64", "UInt16", "String", "Float32", "UInt8"]\n["0", 0, "AM", 0.5, 1]\n', + # '' + # On empty message exception: Cannot parse input: expected '[' at end of stream., Stack trace (when copying this message, always include the lines below): + # /src/IO/ReadHelpers.h:175: DB::assertChar(char, DB::ReadBuffer&) @ 0x15db231a in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/JSONCompactEachRowRowInputFormat.cpp:0: DB::JSONCompactEachRowRowInputFormat::readPrefix() @ 0x1dee6bd6 in /usr/bin/clickhouse + # /src/Processors/Formats/IRowInputFormat.cpp:0: DB::IRowInputFormat::generate() @ 0x1de72710 in /usr/bin/clickhouse + ], + }, + 'TSKV' : { + 'data_sample' : [ + 'id=0\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\n', + 'id=1\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=2\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=3\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=4\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=5\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=6\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=7\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=8\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=9\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=10\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=11\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=12\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=13\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=14\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\nid=15\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\n', + 'id=0\tblockNo=0\tval1=AM\tval2=0.5\tval3=1\n', + # '' + # On empty message exception: Unexpected end of stream while reading key name from TSKV format + # /src/Processors/Formats/Impl/TSKVRowInputFormat.cpp:88: DB::readName(DB::ReadBuffer&, StringRef&, std::__1::basic_string, std::__1::allocator >&) @ 0x1df8c098 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/TSKVRowInputFormat.cpp:114: DB::TSKVRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1df8ae3e in /usr/bin/clickhouse + # /src/Processors/Formats/IRowInputFormat.cpp:64: DB::IRowInputFormat::generate() @ 0x1de727cf in /usr/bin/clickhouse + ], + }, + 'CSV' : { + 'data_sample' : [ + '0,0,"AM",0.5,1\n', + '1,0,"AM",0.5,1\n2,0,"AM",0.5,1\n3,0,"AM",0.5,1\n4,0,"AM",0.5,1\n5,0,"AM",0.5,1\n6,0,"AM",0.5,1\n7,0,"AM",0.5,1\n8,0,"AM",0.5,1\n9,0,"AM",0.5,1\n10,0,"AM",0.5,1\n11,0,"AM",0.5,1\n12,0,"AM",0.5,1\n13,0,"AM",0.5,1\n14,0,"AM",0.5,1\n15,0,"AM",0.5,1\n', + '0,0,"AM",0.5,1\n', + '' # tolerates + ], + }, + 'TSV' : { + 'data_sample' : [ + '0\t0\tAM\t0.5\t1\n', + '1\t0\tAM\t0.5\t1\n2\t0\tAM\t0.5\t1\n3\t0\tAM\t0.5\t1\n4\t0\tAM\t0.5\t1\n5\t0\tAM\t0.5\t1\n6\t0\tAM\t0.5\t1\n7\t0\tAM\t0.5\t1\n8\t0\tAM\t0.5\t1\n9\t0\tAM\t0.5\t1\n10\t0\tAM\t0.5\t1\n11\t0\tAM\t0.5\t1\n12\t0\tAM\t0.5\t1\n13\t0\tAM\t0.5\t1\n14\t0\tAM\t0.5\t1\n15\t0\tAM\t0.5\t1\n', + '0\t0\tAM\t0.5\t1\n', + '' # tolerates + ], + }, + 'CSVWithNames' : { + 'data_sample' : [ + '"id","blockNo","val1","val2","val3"\n0,0,"AM",0.5,1\n', + '"id","blockNo","val1","val2","val3"\n1,0,"AM",0.5,1\n2,0,"AM",0.5,1\n3,0,"AM",0.5,1\n4,0,"AM",0.5,1\n5,0,"AM",0.5,1\n6,0,"AM",0.5,1\n7,0,"AM",0.5,1\n8,0,"AM",0.5,1\n9,0,"AM",0.5,1\n10,0,"AM",0.5,1\n11,0,"AM",0.5,1\n12,0,"AM",0.5,1\n13,0,"AM",0.5,1\n14,0,"AM",0.5,1\n15,0,"AM",0.5,1\n', + '"id","blockNo","val1","val2","val3"\n0,0,"AM",0.5,1\n', + # '', + # On empty message exception happens: Attempt to read after eof + # /src/IO/VarInt.h:122: DB::throwReadAfterEOF() @ 0x15c34487 in /usr/bin/clickhouse + # /src/IO/ReadHelpers.cpp:583: void DB::readCSVStringInto, std::__1::allocator > >(std::__1::basic_string, std::__1::allocator >&, DB::ReadBuffer&, DB::FormatSettings::CSV const&) @ 0x15c961e1 in /usr/bin/clickhouse + # /src/IO/ReadHelpers.cpp:678: DB::readCSVString(std::__1::basic_string, std::__1::allocator >&, DB::ReadBuffer&, DB::FormatSettings::CSV const&) @ 0x15c8dfae in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/CSVRowInputFormat.cpp:170: DB::CSVRowInputFormat::readPrefix() @ 0x1dec46f7 in /usr/bin/clickhouse + # /src/Processors/Formats/IRowInputFormat.cpp:0: DB::IRowInputFormat::generate() @ 0x1de72710 in /usr/bin/clickhouse + # /src/Processors/ISource.cpp:48: DB::ISource::work() @ 0x1dd79737 in /usr/bin/clickhouse + ], + }, + 'Values' : { + 'data_sample' : [ + "(0,0,'AM',0.5,1)", + "(1,0,'AM',0.5,1),(2,0,'AM',0.5,1),(3,0,'AM',0.5,1),(4,0,'AM',0.5,1),(5,0,'AM',0.5,1),(6,0,'AM',0.5,1),(7,0,'AM',0.5,1),(8,0,'AM',0.5,1),(9,0,'AM',0.5,1),(10,0,'AM',0.5,1),(11,0,'AM',0.5,1),(12,0,'AM',0.5,1),(13,0,'AM',0.5,1),(14,0,'AM',0.5,1),(15,0,'AM',0.5,1)", + "(0,0,'AM',0.5,1)", + '' # tolerates + ], + }, + 'TSVWithNames' : { + 'data_sample' : [ + 'id\tblockNo\tval1\tval2\tval3\n0\t0\tAM\t0.5\t1\n', + 'id\tblockNo\tval1\tval2\tval3\n1\t0\tAM\t0.5\t1\n2\t0\tAM\t0.5\t1\n3\t0\tAM\t0.5\t1\n4\t0\tAM\t0.5\t1\n5\t0\tAM\t0.5\t1\n6\t0\tAM\t0.5\t1\n7\t0\tAM\t0.5\t1\n8\t0\tAM\t0.5\t1\n9\t0\tAM\t0.5\t1\n10\t0\tAM\t0.5\t1\n11\t0\tAM\t0.5\t1\n12\t0\tAM\t0.5\t1\n13\t0\tAM\t0.5\t1\n14\t0\tAM\t0.5\t1\n15\t0\tAM\t0.5\t1\n', + 'id\tblockNo\tval1\tval2\tval3\n0\t0\tAM\t0.5\t1\n', + '' # tolerates + ], + }, + 'TSVWithNamesAndTypes' : { + 'data_sample' : [ + 'id\tblockNo\tval1\tval2\tval3\nInt64\tUInt16\tString\tFloat32\tUInt8\n0\t0\tAM\t0.5\t1\n', + 'id\tblockNo\tval1\tval2\tval3\nInt64\tUInt16\tString\tFloat32\tUInt8\n1\t0\tAM\t0.5\t1\n2\t0\tAM\t0.5\t1\n3\t0\tAM\t0.5\t1\n4\t0\tAM\t0.5\t1\n5\t0\tAM\t0.5\t1\n6\t0\tAM\t0.5\t1\n7\t0\tAM\t0.5\t1\n8\t0\tAM\t0.5\t1\n9\t0\tAM\t0.5\t1\n10\t0\tAM\t0.5\t1\n11\t0\tAM\t0.5\t1\n12\t0\tAM\t0.5\t1\n13\t0\tAM\t0.5\t1\n14\t0\tAM\t0.5\t1\n15\t0\tAM\t0.5\t1\n', + 'id\tblockNo\tval1\tval2\tval3\nInt64\tUInt16\tString\tFloat32\tUInt8\n0\t0\tAM\t0.5\t1\n', + # '', + # On empty message exception happens: Cannot parse input: expected '\n' at end of stream. + # /src/IO/ReadHelpers.cpp:84: DB::throwAtAssertionFailed(char const*, DB::ReadBuffer&) @ 0x15c8d8ec in /usr/bin/clickhouse + # /src/IO/ReadHelpers.h:175: DB::assertChar(char, DB::ReadBuffer&) @ 0x15db231a in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp:24: DB::skipTSVRow(DB::ReadBuffer&, unsigned long) @ 0x1df92fac in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/TabSeparatedRowInputFormat.cpp:168: DB::TabSeparatedRowInputFormat::readPrefix() @ 0x1df92df0 in /usr/bin/clickhouse + # /src/Processors/Formats/IRowInputFormat.cpp:0: DB::IRowInputFormat::generate() @ 0x1de72710 in /usr/bin/clickhouse + ], + }, + # 'Template' : { + # 'data_sample' : [ + # '(id = 0, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + # # '(id = 1, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 2, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 3, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 4, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 5, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 6, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 7, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 8, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 9, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 10, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 11, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 12, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 13, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 14, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 15, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + # # '(id = 0, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + # # '' # tolerates + # ], + # 'extra_settings': ", format_template_row='template_row.format'" + # }, + 'Regexp' : { + 'data_sample' : [ + '(id = 0, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + '(id = 1, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 2, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 3, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 4, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 5, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 6, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 7, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 8, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 9, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 10, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 11, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 12, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 13, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 14, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)\n(id = 15, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + '(id = 0, blockNo = 0, val1 = "AM", val2 = 0.5, val3 = 1)', + # '' + # On empty message exception happens: Line "" doesn't match the regexp.: (at row 1) + # /src/Processors/Formats/Impl/RegexpRowInputFormat.cpp:140: DB::RegexpRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1df82fcb in /usr/bin/clickhouse + ], + 'extra_settings': ", format_regexp='\(id = (.+?), blockNo = (.+?), val1 = \"(.+?)\", val2 = (.+?), val3 = (.+?)\)', format_regexp_escaping_rule='Escaped'" + }, + + ## BINARY FORMATS + # dumped with + # clickhouse-client ... | xxd -ps -c 200 | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + 'Native' : { + 'data_sample': [ + '\x05\x01\x02\x69\x64\x05\x49\x6e\x74\x36\x34\x00\x00\x00\x00\x00\x00\x00\x00\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x06\x55\x49\x6e\x74\x31\x36\x00\x00\x04\x76\x61\x6c\x31\x06\x53\x74\x72\x69\x6e\x67\x02\x41\x4d\x04\x76\x61\x6c\x32\x07\x46\x6c\x6f\x61\x74\x33\x32\x00\x00\x00\x3f\x04\x76\x61\x6c\x33\x05\x55\x49\x6e\x74\x38\x01', + '\x05\x0f\x02\x69\x64\x05\x49\x6e\x74\x36\x34\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x06\x55\x49\x6e\x74\x31\x36\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x76\x61\x6c\x31\x06\x53\x74\x72\x69\x6e\x67\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x02\x41\x4d\x04\x76\x61\x6c\x32\x07\x46\x6c\x6f\x61\x74\x33\x32\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x04\x76\x61\x6c\x33\x05\x55\x49\x6e\x74\x38\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01', + '\x05\x01\x02\x69\x64\x05\x49\x6e\x74\x36\x34\x00\x00\x00\x00\x00\x00\x00\x00\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x06\x55\x49\x6e\x74\x31\x36\x00\x00\x04\x76\x61\x6c\x31\x06\x53\x74\x72\x69\x6e\x67\x02\x41\x4d\x04\x76\x61\x6c\x32\x07\x46\x6c\x6f\x61\x74\x33\x32\x00\x00\x00\x3f\x04\x76\x61\x6c\x33\x05\x55\x49\x6e\x74\x38\x01', + # '' + # On empty message exception happens: DB::Exception: Attempt to read after eof + # /src/IO/VarInt.h:122: DB::throwReadAfterEOF() @ 0x15c34487 in /usr/bin/clickhouse + # /src/IO/VarInt.h:135: void DB::readVarUIntImpl(unsigned long&, DB::ReadBuffer&) @ 0x15c68bb7 in /usr/bin/clickhouse + # /src/IO/VarInt.h:149: DB::readVarUInt(unsigned long&, DB::ReadBuffer&) @ 0x15c68844 in /usr/bin/clickhouse + # /src/DataStreams/NativeBlockInputStream.cpp:124: DB::NativeBlockInputStream::readImpl() @ 0x1d3e2778 in /usr/bin/clickhouse + # /src/DataStreams/IBlockInputStream.cpp:60: DB::IBlockInputStream::read() @ 0x1c9c92fd in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/NativeFormat.cpp:42: DB::NativeInputFormatFromNativeBlockInputStream::generate() @ 0x1df1ea79 in /usr/bin/clickhouse + # /src/Processors/ISource.cpp:48: DB::ISource::work() @ 0x1dd79737 in /usr/bin/clickhouse + ], + }, + 'MsgPack' : { + 'data_sample' : [ + '\x00\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01', + '\x01\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x02\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x03\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x04\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x05\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x06\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x07\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x08\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x09\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0a\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0b\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0c\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0d\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0e\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01\x0f\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01', + '\x00\x00\xa2\x41\x4d\xca\x3f\x00\x00\x00\x01', + # '' + # On empty message exception happens: Unexpected end of file while parsing msgpack object.: (at row 1) + # coming from Processors/Formats/Impl/MsgPackRowInputFormat.cpp:170 + ], + }, + 'RowBinary' : { + 'data_sample' : [ + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + '\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + # '' + # On empty message exception happens: DB::Exception: Cannot read all data. Bytes read: 0. Bytes expected: 8. + # /src/IO/ReadBuffer.h:157: DB::ReadBuffer::readStrict(char*, unsigned long) @ 0x15c6894d in /usr/bin/clickhouse + # /src/IO/ReadHelpers.h:108: void DB::readPODBinary(long&, DB::ReadBuffer&) @ 0x15c67715 in /usr/bin/clickhouse + # /src/IO/ReadHelpers.h:737: std::__1::enable_if, void>::type DB::readBinary(long&, DB::ReadBuffer&) @ 0x15e7afbd in /usr/bin/clickhouse + # /src/DataTypes/DataTypeNumberBase.cpp:180: DB::DataTypeNumberBase::deserializeBinary(DB::IColumn&, DB::ReadBuffer&) const @ 0x1cace581 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/BinaryRowInputFormat.cpp:22: DB::BinaryRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1dea2c0b in /usr/bin/clickhouse + ], + }, + 'RowBinaryWithNamesAndTypes' : { + 'data_sample' : [ + '\x05\x02\x69\x64\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x04\x76\x61\x6c\x31\x04\x76\x61\x6c\x32\x04\x76\x61\x6c\x33\x05\x49\x6e\x74\x36\x34\x06\x55\x49\x6e\x74\x31\x36\x06\x53\x74\x72\x69\x6e\x67\x07\x46\x6c\x6f\x61\x74\x33\x32\x05\x55\x49\x6e\x74\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + '\x05\x02\x69\x64\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x04\x76\x61\x6c\x31\x04\x76\x61\x6c\x32\x04\x76\x61\x6c\x33\x05\x49\x6e\x74\x36\x34\x06\x55\x49\x6e\x74\x31\x36\x06\x53\x74\x72\x69\x6e\x67\x07\x46\x6c\x6f\x61\x74\x33\x32\x05\x55\x49\x6e\x74\x38\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + '\x05\x02\x69\x64\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x04\x76\x61\x6c\x31\x04\x76\x61\x6c\x32\x04\x76\x61\x6c\x33\x05\x49\x6e\x74\x36\x34\x06\x55\x49\x6e\x74\x31\x36\x06\x53\x74\x72\x69\x6e\x67\x07\x46\x6c\x6f\x61\x74\x33\x32\x05\x55\x49\x6e\x74\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x41\x4d\x00\x00\x00\x3f\x01', + #'' + # !!! On empty message segfault: Address not mapped to object + # /contrib/FastMemcpy/FastMemcpy.h:666: memcpy_fast @ 0x21742d65 in /usr/bin/clickhouse + # /contrib/FastMemcpy/memcpy_wrapper.c:5: memcpy @ 0x21738235 in /usr/bin/clickhouse + # /src/IO/ReadBuffer.h:145: DB::ReadBuffer::read(char*, unsigned long) @ 0x15c369d7 in /usr/bin/clickhouse + # /src/IO/ReadBuffer.h:155: DB::ReadBuffer::readStrict(char*, unsigned long) @ 0x15c68878 in /usr/bin/clickhouse + # /src/DataTypes/DataTypeString.cpp:84: DB::DataTypeString::deserializeBinary(DB::IColumn&, DB::ReadBuffer&) const @ 0x1cad12e7 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/BinaryRowInputFormat.cpp:22: DB::BinaryRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1dea2c0b in /usr/bin/clickhouse + ], + }, + 'Protobuf' : { + 'data_sample' : [ + '\x0b\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01', + '\x0d\x08\x01\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x02\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x03\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x04\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x05\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x06\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x07\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x08\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x09\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0a\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0b\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0c\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0d\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0e\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01\x0d\x08\x0f\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01', + '\x0b\x1a\x02\x41\x4d\x25\x00\x00\x00\x3f\x28\x01', + # '' + # On empty message exception: Attempt to read after eof + # /src/IO/ReadBuffer.h:184: DB::ReadBuffer::throwReadAfterEOF() @ 0x15c9699b in /usr/bin/clickhouse + # /src/Formats/ProtobufReader.h:115: DB::ProtobufReader::SimpleReader::startMessage() @ 0x1df4f828 in /usr/bin/clickhouse + # /src/Formats/ProtobufReader.cpp:1119: DB::ProtobufReader::startMessage() @ 0x1df5356c in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/ProtobufRowInputFormat.cpp:25: DB::ProtobufRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1df4cc71 in /usr/bin/clickhouse + # /src/Processors/Formats/IRowInputFormat.cpp:64: DB::IRowInputFormat::generate() @ 0x1de727cf in /usr/bin/clickhouse + ], + 'extra_settings': ", kafka_schema='test:TestMessage'" + }, + 'ORC' : { + 'data_sample' : [ + '\x4f\x52\x43\x11\x00\x00\x0a\x06\x12\x04\x08\x01\x50\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x01\x12\x06\x08\x00\x10\x00\x18\x00\x50\x00\x30\x00\x00\xe3\x12\xe7\x62\x65\x00\x01\x21\x3e\x0e\x46\x25\x0e\x2e\x46\x03\x21\x46\x03\x09\xa6\x00\x06\x00\x32\x00\x00\xe3\x92\xe4\x62\x65\x00\x01\x21\x01\x0e\x46\x25\x2e\x2e\x26\x47\x5f\x21\x20\x96\x60\x09\x60\x00\x00\x36\x00\x00\xe3\x92\xe1\x62\x65\x00\x01\x21\x61\x0e\x46\x23\x5e\x2e\x46\x03\x21\x66\x03\x3d\x53\x29\x10\x11\xc0\x00\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x01\x12\x06\x08\x02\x10\x02\x18\x02\x50\x00\x05\x00\x00\xff\x00\x03\x00\x00\x30\x07\x00\x00\x40\x00\x80\x05\x00\x00\x41\x4d\x07\x00\x00\x42\x00\x80\x03\x00\x00\x0a\x07\x00\x00\x42\x00\x80\x05\x00\x00\xff\x01\x88\x00\x00\x4d\xca\xc1\x0a\x80\x30\x0c\x03\xd0\x2e\x6b\xcb\x98\x17\xf1\x14\x50\xfc\xff\xcf\xb4\x66\x1e\x3c\x84\x47\x9a\xce\x1c\xb9\x1b\xb7\xf9\xda\x48\x09\x9e\xb2\xf3\x92\xce\x5b\x86\xf6\x56\x7f\x21\x41\x2f\x51\xa6\x7a\xd7\x1d\xe5\xea\xae\x3d\xca\xd5\x83\x71\x60\xd8\x17\xfc\x62\x0f\xa8\x00\x00\xe3\x4a\xe6\x62\xe1\x60\x0c\x60\xe0\xe2\xe3\x60\x14\x62\xe3\x60\x10\x60\x90\x60\x08\x60\x88\x60\xe5\x12\xe0\x60\x54\xe2\xe0\x62\x34\x10\x62\x34\x90\x60\x02\x8a\x70\x71\x09\x01\x45\xb8\xb8\x98\x1c\x7d\x85\x80\x58\x82\x05\x28\xc6\xcd\x25\xca\xc1\x68\xc4\x0b\x52\xc5\x6c\xa0\x67\x2a\x05\x22\xc0\x4a\x21\x86\x31\x09\x30\x81\xb5\xb2\x02\x00\x36\x01\x00\x25\x8c\xbd\x0a\xc2\x30\x14\x85\x73\x6f\x92\xf6\x92\x6a\x09\x01\x21\x64\x92\x4e\x75\x91\x58\x71\xc9\x64\x27\x5d\x2c\x1d\x5d\xfd\x59\xc4\x42\x37\x5f\xc0\x17\xe8\x23\x9b\xc6\xe1\x3b\x70\x0f\xdf\xb9\xc4\xf5\x17\x5d\x41\x5c\x4f\x60\x37\xeb\x53\x0d\x55\x4d\x0b\x23\x01\xb9\x90\x2e\xbf\x0f\xe3\xe3\xdd\x8d\x0e\x5f\x4f\x27\x3e\xb7\x61\x97\xb2\x49\xb9\xaf\x90\x20\x92\x27\x32\x2a\x6b\xf4\xf3\x0d\x1e\x82\x20\xe8\x59\x28\x09\x4c\x46\x4c\x33\xcb\x7a\x76\x95\x41\x47\x9f\x14\x78\x03\xde\x62\x6c\x54\x30\xb1\x51\x0a\xdb\x8b\x89\x58\x11\xbb\x22\xac\x08\x9a\xe5\x6c\x71\xbf\x3d\xb8\x39\x92\xfa\x7f\x86\x1a\xd3\x54\x1e\xa7\xee\xcc\x7e\x08\x9e\x01\x10\x01\x18\x80\x80\x10\x22\x02\x00\x0c\x28\x57\x30\x06\x82\xf4\x03\x03\x4f\x52\x43\x18', + '\x4f\x52\x43\x11\x00\x00\x0a\x06\x12\x04\x08\x0f\x50\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x0f\x12\x06\x08\x00\x10\x00\x18\x00\x50\x00\x30\x00\x00\xe3\x12\xe7\x62\x65\x00\x01\x21\x3e\x0e\x7e\x25\x0e\x2e\x46\x43\x21\x46\x4b\x09\xad\x00\x06\x00\x33\x00\x00\x0a\x17\x0a\x03\x00\x00\x00\x12\x10\x08\x0f\x22\x0a\x0a\x02\x41\x4d\x12\x02\x41\x4d\x18\x3c\x50\x00\x3a\x00\x00\xe3\x92\xe1\x62\x65\x00\x01\x21\x61\x0e\x7e\x23\x5e\x2e\x46\x03\x21\x66\x03\x3d\x53\x29\x66\x73\x3d\xd3\x00\x06\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x0f\x12\x06\x08\x02\x10\x02\x18\x1e\x50\x00\x05\x00\x00\x0c\x00\x2b\x00\x00\x31\x32\x33\x34\x35\x36\x37\x38\x39\x31\x30\x31\x31\x31\x32\x31\x33\x31\x34\x31\x35\x09\x00\x00\x06\x01\x03\x02\x09\x00\x00\xc0\x0e\x00\x00\x07\x00\x00\x42\x00\x80\x05\x00\x00\x41\x4d\x0a\x00\x00\xe3\xe2\x42\x01\x00\x09\x00\x00\xc0\x0e\x02\x00\x05\x00\x00\x0c\x01\x94\x00\x00\x2d\xca\xc1\x0e\x80\x30\x08\x03\xd0\xc1\x60\x2e\xf3\x62\x76\x6a\xe2\x0e\xfe\xff\x57\x5a\x3b\x0f\xe4\x51\xe8\x68\xbd\x5d\x05\xe7\xf8\x34\x40\x3a\x6e\x59\xb1\x64\xe0\x91\xa9\xbf\xb1\x97\xd2\x95\x9d\x1e\xca\x55\x3a\x6d\xb4\xd2\xdd\x0b\x74\x9a\x74\xf7\x12\x39\xbd\x97\x7f\x7c\x06\xbb\xa6\x8d\x97\x17\xb4\x00\x00\xe3\x4a\xe6\x62\xe1\xe0\x0f\x60\xe0\xe2\xe3\xe0\x17\x62\xe3\x60\x10\x60\x90\x60\x08\x60\x88\x60\xe5\x12\xe0\xe0\x57\xe2\xe0\x62\x34\x14\x62\xb4\x94\xd0\x02\x8a\xc8\x73\x09\x01\x45\xb8\xb8\x98\x1c\x7d\x85\x80\x58\xc2\x06\x28\x26\xc4\x25\xca\xc1\x6f\xc4\xcb\xc5\x68\x20\xc4\x6c\xa0\x67\x2a\xc5\x6c\xae\x67\x0a\x14\xe6\x87\x1a\xc6\x24\xc0\x24\x21\x07\x32\x0c\x00\x4a\x01\x00\xe3\x60\x16\x58\xc3\x24\xc5\xcd\xc1\x2c\x30\x89\x51\xc2\x4b\xc1\x57\x83\x5f\x49\x83\x83\x47\x88\x95\x91\x89\x99\x85\x55\x8a\x3d\x29\x27\x3f\x39\xdb\x2f\x5f\x8a\x29\x33\x45\x8a\xa5\x2c\x31\xc7\x10\x4c\x1a\x81\x49\x63\x25\x26\x0e\x46\x20\x66\x07\x63\x36\x0e\x3e\x0d\x26\x03\x10\x9f\xd1\x80\xdf\x8a\x85\x83\x3f\x80\xc1\x8a\x8f\x83\x5f\x88\x8d\x83\x41\x80\x41\x82\x21\x80\x21\x82\xd5\x4a\x80\x83\x5f\x89\x83\x8b\xd1\x50\x88\xd1\x52\x42\x0b\x28\x22\x6f\x25\x04\x14\xe1\xe2\x62\x72\xf4\x15\x02\x62\x09\x1b\xa0\x98\x90\x95\x28\x07\xbf\x11\x2f\x17\xa3\x81\x10\xb3\x81\x9e\xa9\x14\xb3\xb9\x9e\x29\x50\x98\x1f\x6a\x18\x93\x00\x93\x84\x1c\xc8\x30\x87\x09\x7e\x1e\x0c\x00\x08\xa8\x01\x10\x01\x18\x80\x80\x10\x22\x02\x00\x0c\x28\x5d\x30\x06\x82\xf4\x03\x03\x4f\x52\x43\x18', + '\x4f\x52\x43\x11\x00\x00\x0a\x06\x12\x04\x08\x01\x50\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x01\x12\x06\x08\x00\x10\x00\x18\x00\x50\x00\x30\x00\x00\xe3\x12\xe7\x62\x65\x00\x01\x21\x3e\x0e\x46\x25\x0e\x2e\x46\x03\x21\x46\x03\x09\xa6\x00\x06\x00\x32\x00\x00\xe3\x92\xe4\x62\x65\x00\x01\x21\x01\x0e\x46\x25\x2e\x2e\x26\x47\x5f\x21\x20\x96\x60\x09\x60\x00\x00\x36\x00\x00\xe3\x92\xe1\x62\x65\x00\x01\x21\x61\x0e\x46\x23\x5e\x2e\x46\x03\x21\x66\x03\x3d\x53\x29\x10\x11\xc0\x00\x00\x2b\x00\x00\x0a\x13\x0a\x03\x00\x00\x00\x12\x0c\x08\x01\x12\x06\x08\x02\x10\x02\x18\x02\x50\x00\x05\x00\x00\xff\x00\x03\x00\x00\x30\x07\x00\x00\x40\x00\x80\x05\x00\x00\x41\x4d\x07\x00\x00\x42\x00\x80\x03\x00\x00\x0a\x07\x00\x00\x42\x00\x80\x05\x00\x00\xff\x01\x88\x00\x00\x4d\xca\xc1\x0a\x80\x30\x0c\x03\xd0\x2e\x6b\xcb\x98\x17\xf1\x14\x50\xfc\xff\xcf\xb4\x66\x1e\x3c\x84\x47\x9a\xce\x1c\xb9\x1b\xb7\xf9\xda\x48\x09\x9e\xb2\xf3\x92\xce\x5b\x86\xf6\x56\x7f\x21\x41\x2f\x51\xa6\x7a\xd7\x1d\xe5\xea\xae\x3d\xca\xd5\x83\x71\x60\xd8\x17\xfc\x62\x0f\xa8\x00\x00\xe3\x4a\xe6\x62\xe1\x60\x0c\x60\xe0\xe2\xe3\x60\x14\x62\xe3\x60\x10\x60\x90\x60\x08\x60\x88\x60\xe5\x12\xe0\x60\x54\xe2\xe0\x62\x34\x10\x62\x34\x90\x60\x02\x8a\x70\x71\x09\x01\x45\xb8\xb8\x98\x1c\x7d\x85\x80\x58\x82\x05\x28\xc6\xcd\x25\xca\xc1\x68\xc4\x0b\x52\xc5\x6c\xa0\x67\x2a\x05\x22\xc0\x4a\x21\x86\x31\x09\x30\x81\xb5\xb2\x02\x00\x36\x01\x00\x25\x8c\xbd\x0a\xc2\x30\x14\x85\x73\x6f\x92\xf6\x92\x6a\x09\x01\x21\x64\x92\x4e\x75\x91\x58\x71\xc9\x64\x27\x5d\x2c\x1d\x5d\xfd\x59\xc4\x42\x37\x5f\xc0\x17\xe8\x23\x9b\xc6\xe1\x3b\x70\x0f\xdf\xb9\xc4\xf5\x17\x5d\x41\x5c\x4f\x60\x37\xeb\x53\x0d\x55\x4d\x0b\x23\x01\xb9\x90\x2e\xbf\x0f\xe3\xe3\xdd\x8d\x0e\x5f\x4f\x27\x3e\xb7\x61\x97\xb2\x49\xb9\xaf\x90\x20\x92\x27\x32\x2a\x6b\xf4\xf3\x0d\x1e\x82\x20\xe8\x59\x28\x09\x4c\x46\x4c\x33\xcb\x7a\x76\x95\x41\x47\x9f\x14\x78\x03\xde\x62\x6c\x54\x30\xb1\x51\x0a\xdb\x8b\x89\x58\x11\xbb\x22\xac\x08\x9a\xe5\x6c\x71\xbf\x3d\xb8\x39\x92\xfa\x7f\x86\x1a\xd3\x54\x1e\xa7\xee\xcc\x7e\x08\x9e\x01\x10\x01\x18\x80\x80\x10\x22\x02\x00\x0c\x28\x57\x30\x06\x82\xf4\x03\x03\x4f\x52\x43\x18', + #'' + # On empty message exception: IOError: File size too small, Stack trace (when copying this message, always include the lines below): + # /src/Processors/Formats/Impl/ORCBlockInputFormat.cpp:36: DB::ORCBlockInputFormat::generate() @ 0x1df282a6 in /usr/bin/clickhouse + # /src/Processors/ISource.cpp:48: DB::ISource::work() @ 0x1dd79737 in /usr/bin/clickhouse + ], + }, + 'CapnProto' : { + 'data_sample' : [ + '\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00', + '\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00', + '\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x3f\x01\x00\x00\x00\x1a\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00', + # '' + # On empty message exception: Cannot read all data. Bytes read: 0. Bytes expected: 4. + # /src/IO/ReadBuffer.h:157: DB::ReadBuffer::readStrict(char*, unsigned long) @ 0x15c6894d in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/CapnProtoRowInputFormat.cpp:212: DB::CapnProtoRowInputFormat::readMessage() @ 0x1ded1cab in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/CapnProtoRowInputFormat.cpp:241: DB::CapnProtoRowInputFormat::readRow(std::__1::vector::mutable_ptr, std::__1::allocator::mutable_ptr > >&, DB::RowReadExtension&) @ 0x1ded205d in /usr/bin/clickhouse + ], + 'extra_settings': ", kafka_schema='test:TestRecordStruct'" + }, + + + # 'Parquet' : { + # not working at all with Kafka: DB::Exception: IOError: Invalid Parquet file size is 0 bytes + # /contrib/libcxx/include/exception:129: std::exception::capture() @ 0x15c33fe8 in /usr/bin/clickhouse + # /contrib/libcxx/include/exception:109: std::exception::exception() @ 0x15c33fb5 in /usr/bin/clickhouse + # /contrib/poco/Foundation/src/Exception.cpp:27: Poco::Exception::Exception(std::__1::basic_string, std::__1::allocator > const&, int) @ 0x21877833 in /usr/bin/clickhouse + # /src/Common/Exception.cpp:37: DB::Exception::Exception(std::__1::basic_string, std::__1::allocator > const&, int) @ 0x15c2d2a3 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp:70: DB::ParquetBlockInputFormat::prepareReader() @ 0x1df2b0c2 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp:36: DB::ParquetBlockInputFormat::ParquetBlockInputFormat(DB::ReadBuffer&, DB::Block) @ 0x1df2af8b in /usr/bin/clickhouse + # /contrib/libcxx/include/memory:2214: std::__1::__compressed_pair_elem::__compressed_pair_elem(std::__1::piecewise_construct_t, std::__1::tuple, std::__1::__tuple_indices<0ul, 1ul>) @ 0x1df2dc88 in /usr/bin/clickhouse + # /contrib/libcxx/include/memory:2299: std::__1::__compressed_pair, DB::ParquetBlockInputFormat>::__compressed_pair&, DB::ReadBuffer&, DB::Block const&>(std::__1::piecewise_construct_t, std::__1::tuple&>, std::__1::tuple) @ 0x1df2d9c8 in /usr/bin/clickhouse + # /contrib/libcxx/include/memory:3569: std::__1::__shared_ptr_emplace >::__shared_ptr_emplace(std::__1::allocator, DB::ReadBuffer&, DB::Block const&) @ 0x1df2d687 in /usr/bin/clickhouse + # /contrib/libcxx/include/memory:4400: std::__1::enable_if::value), std::__1::shared_ptr >::type std::__1::make_shared(DB::ReadBuffer&, DB::Block const&) @ 0x1df2d455 in /usr/bin/clickhouse + # /src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp:95: DB::registerInputFormatProcessorParquet(DB::FormatFactory&)::$_0::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1df2cec7 in /usr/bin/clickhouse + # /contrib/libcxx/include/type_traits:3519: decltype(std::__1::forward(fp)(std::__1::forward(fp0), std::__1::forward(fp0), std::__1::forward(fp0), std::__1::forward(fp0))) std::__1::__invoke(DB::registerInputFormatProcessorParquet(DB::FormatFactory&)::$_0&, DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1df2ce6a in /usr/bin/clickhouse + # /contrib/libcxx/include/__functional_base:317: std::__1::shared_ptr std::__1::__invoke_void_return_wrapper >::__call(DB::registerInputFormatProcessorParquet(DB::FormatFactory&)::$_0&, DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1df2cd7d in /usr/bin/clickhouse + # /contrib/libcxx/include/functional:1540: std::__1::__function::__alloc_func, std::__1::shared_ptr (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1df2ccda in /usr/bin/clickhouse + # /contrib/libcxx/include/functional:1714: std::__1::__function::__func, std::__1::shared_ptr (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1df2bdec in /usr/bin/clickhouse + # /contrib/libcxx/include/functional:1867: std::__1::__function::__value_func (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1dd14dbd in /usr/bin/clickhouse + # /contrib/libcxx/include/functional:2473: std::__1::function (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1dd07035 in /usr/bin/clickhouse + # /src/Formats/FormatFactory.cpp:258: DB::FormatFactory::getInputFormat(std::__1::basic_string, std::__1::allocator > const&, DB::ReadBuffer&, DB::Block const&, DB::Context const&, unsigned long, std::__1::function) const @ 0x1dd04007 in /usr/bin/clickhouse + # /src/Storages/Kafka/KafkaBlockInputStream.cpp:76: DB::KafkaBlockInputStream::readImpl() @ 0x1d8f6559 in /usr/bin/clickhouse + # /src/DataStreams/IBlockInputStream.cpp:60: DB::IBlockInputStream::read() @ 0x1c9c92fd in /usr/bin/clickhouse + # /src/DataStreams/copyData.cpp:26: void DB::copyDataImpl*)::$_0&, void (&)(DB::Block const&)>(DB::IBlockInputStream&, DB::IBlockOutputStream&, DB::copyData(DB::IBlockInputStream&, DB::IBlockOutputStream&, std::__1::atomic*)::$_0&, void (&)(DB::Block const&)) @ 0x1c9ea01c in /usr/bin/clickhouse + # /src/DataStreams/copyData.cpp:63: DB::copyData(DB::IBlockInputStream&, DB::IBlockOutputStream&, std::__1::atomic*) @ 0x1c9e9fc7 in /usr/bin/clickhouse + # /src/Storages/Kafka/StorageKafka.cpp:565: DB::StorageKafka::streamToViews() @ 0x1d8cc3fa in /usr/bin/clickhouse + # # 'data_sample' : [ + # # '\x50\x41\x52\x31\x15\x04\x15\x10\x15\x14\x4c\x15\x02\x15\x04\x12\x00\x00\x08\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xbc\x01\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x02\x16\xac\x01\x16\xb4\x01\x26\x38\x26\x08\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xc8\x03\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xfc\x02\x26\xd4\x02\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x0c\x15\x10\x4c\x15\x02\x15\x04\x12\x00\x00\x06\x14\x02\x00\x00\x00\x41\x4d\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x03\x08\x01\x02\x00\x26\xa2\x05\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x02\x16\x68\x16\x70\x26\xde\x04\x26\xb2\x04\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x3f\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x03\x08\x01\x02\x00\x26\x8a\x07\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x02\x16\x84\x01\x16\x8c\x01\x26\xa6\x06\x26\xfe\x05\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x01\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xfe\x08\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xb2\x08\x26\x8a\x08\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x15\x02\x19\x6c\x35\x00\x18\x06\x73\x63\x68\x65\x6d\x61\x15\x0a\x00\x15\x04\x25\x00\x18\x02\x69\x64\x00\x15\x02\x25\x00\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x25\x18\x4c\xac\x13\x10\x12\x00\x00\x00\x15\x0c\x25\x00\x18\x04\x76\x61\x6c\x31\x25\x00\x4c\x1c\x00\x00\x00\x15\x08\x25\x00\x18\x04\x76\x61\x6c\x32\x00\x15\x02\x25\x00\x18\x04\x76\x61\x6c\x33\x25\x16\x4c\xac\x13\x08\x12\x00\x00\x00\x16\x02\x19\x1c\x19\x5c\x26\xbc\x01\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x02\x16\xac\x01\x16\xb4\x01\x26\x38\x26\x08\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\xc8\x03\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xfc\x02\x26\xd4\x02\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x26\xa2\x05\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x02\x16\x68\x16\x70\x26\xde\x04\x26\xb2\x04\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x26\x8a\x07\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x02\x16\x84\x01\x16\x8c\x01\x26\xa6\x06\x26\xfe\x05\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x26\xfe\x08\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xb2\x08\x26\x8a\x08\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x16\x98\x05\x16\x02\x00\x28\x22\x70\x61\x72\x71\x75\x65\x74\x2d\x63\x70\x70\x20\x76\x65\x72\x73\x69\x6f\x6e\x20\x31\x2e\x35\x2e\x31\x2d\x53\x4e\x41\x50\x53\x48\x4f\x54\x19\x5c\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x00\xc4\x01\x00\x00\x50\x41\x52\x31', + # # '\x50\x41\x52\x31\x15\x04\x15\xf0\x01\x15\x90\x01\x4c\x15\x1e\x15\x04\x12\x00\x00\x78\x04\x01\x00\x09\x01\x00\x02\x09\x07\x04\x00\x03\x0d\x08\x00\x04\x0d\x08\x00\x05\x0d\x08\x00\x06\x0d\x08\x00\x07\x0d\x08\x00\x08\x0d\x08\x00\x09\x0d\x08\x00\x0a\x0d\x08\x00\x0b\x0d\x08\x00\x0c\x0d\x08\x00\x0d\x0d\x08\x3c\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x15\x00\x15\x14\x15\x18\x2c\x15\x1e\x15\x04\x15\x06\x15\x06\x1c\x18\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x24\x04\x05\x10\x32\x54\x76\x98\xba\xdc\x0e\x26\xca\x02\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x1e\x16\x9e\x03\x16\xc2\x02\x26\xb8\x01\x26\x08\x1c\x18\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x1e\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x03\x08\x01\x1e\x00\x26\xd8\x04\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x1e\x16\x6c\x16\x74\x26\x8c\x04\x26\xe4\x03\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x0c\x15\x10\x4c\x15\x02\x15\x04\x12\x00\x00\x06\x14\x02\x00\x00\x00\x41\x4d\x15\x00\x15\x06\x15\x0a\x2c\x15\x1e\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x03\x08\x01\x1e\x00\x26\xb2\x06\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x1e\x16\x68\x16\x70\x26\xee\x05\x26\xc2\x05\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x3f\x15\x00\x15\x06\x15\x0a\x2c\x15\x1e\x15\x04\x15\x06\x15\x06\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x03\x08\x01\x1e\x00\x26\x9a\x08\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x1e\x16\x84\x01\x16\x8c\x01\x26\xb6\x07\x26\x8e\x07\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x01\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x1e\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x03\x08\x01\x1e\x00\x26\x8e\x0a\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x1e\x16\x6c\x16\x74\x26\xc2\x09\x26\x9a\x09\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x15\x02\x19\x6c\x35\x00\x18\x06\x73\x63\x68\x65\x6d\x61\x15\x0a\x00\x15\x04\x25\x00\x18\x02\x69\x64\x00\x15\x02\x25\x00\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x25\x18\x4c\xac\x13\x10\x12\x00\x00\x00\x15\x0c\x25\x00\x18\x04\x76\x61\x6c\x31\x25\x00\x4c\x1c\x00\x00\x00\x15\x08\x25\x00\x18\x04\x76\x61\x6c\x32\x00\x15\x02\x25\x00\x18\x04\x76\x61\x6c\x33\x25\x16\x4c\xac\x13\x08\x12\x00\x00\x00\x16\x1e\x19\x1c\x19\x5c\x26\xca\x02\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x1e\x16\x9e\x03\x16\xc2\x02\x26\xb8\x01\x26\x08\x1c\x18\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x0f\x00\x00\x00\x00\x00\x00\x00\x18\x08\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\xd8\x04\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x1e\x16\x6c\x16\x74\x26\x8c\x04\x26\xe4\x03\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x26\xb2\x06\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x1e\x16\x68\x16\x70\x26\xee\x05\x26\xc2\x05\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x26\x9a\x08\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x1e\x16\x84\x01\x16\x8c\x01\x26\xb6\x07\x26\x8e\x07\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x26\x8e\x0a\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x1e\x16\x6c\x16\x74\x26\xc2\x09\x26\x9a\x09\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x16\xa6\x06\x16\x1e\x00\x28\x22\x70\x61\x72\x71\x75\x65\x74\x2d\x63\x70\x70\x20\x76\x65\x72\x73\x69\x6f\x6e\x20\x31\x2e\x35\x2e\x31\x2d\x53\x4e\x41\x50\x53\x48\x4f\x54\x19\x5c\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x00\xc5\x01\x00\x00\x50\x41\x52\x31', + # # '\x50\x41\x52\x31\x15\x04\x15\x10\x15\x14\x4c\x15\x02\x15\x04\x12\x00\x00\x08\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xbc\x01\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x02\x16\xac\x01\x16\xb4\x01\x26\x38\x26\x08\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xc8\x03\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xfc\x02\x26\xd4\x02\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x15\x04\x15\x0c\x15\x10\x4c\x15\x02\x15\x04\x12\x00\x00\x06\x14\x02\x00\x00\x00\x41\x4d\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x03\x08\x01\x02\x00\x26\xa2\x05\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x02\x16\x68\x16\x70\x26\xde\x04\x26\xb2\x04\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x00\x00\x00\x3f\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x03\x08\x01\x02\x00\x26\x8a\x07\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x02\x16\x84\x01\x16\x8c\x01\x26\xa6\x06\x26\xfe\x05\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x15\x04\x15\x08\x15\x0c\x4c\x15\x02\x15\x04\x12\x00\x00\x04\x0c\x01\x00\x00\x00\x15\x00\x15\x06\x15\x0a\x2c\x15\x02\x15\x04\x15\x06\x15\x06\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x03\x08\x01\x02\x00\x26\xfe\x08\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xb2\x08\x26\x8a\x08\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x15\x02\x19\x6c\x35\x00\x18\x06\x73\x63\x68\x65\x6d\x61\x15\x0a\x00\x15\x04\x25\x00\x18\x02\x69\x64\x00\x15\x02\x25\x00\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x25\x18\x4c\xac\x13\x10\x12\x00\x00\x00\x15\x0c\x25\x00\x18\x04\x76\x61\x6c\x31\x25\x00\x4c\x1c\x00\x00\x00\x15\x08\x25\x00\x18\x04\x76\x61\x6c\x32\x00\x15\x02\x25\x00\x18\x04\x76\x61\x6c\x33\x25\x16\x4c\xac\x13\x08\x12\x00\x00\x00\x16\x02\x19\x1c\x19\x5c\x26\xbc\x01\x1c\x15\x04\x19\x35\x04\x00\x06\x19\x18\x02\x69\x64\x15\x02\x16\x02\x16\xac\x01\x16\xb4\x01\x26\x38\x26\x08\x1c\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x28\x08\x00\x00\x00\x00\x00\x00\x00\x00\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\xc8\x03\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x07\x62\x6c\x6f\x63\x6b\x4e\x6f\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xfc\x02\x26\xd4\x02\x1c\x36\x00\x28\x04\x00\x00\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x00\x26\xa2\x05\x1c\x15\x0c\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x31\x15\x02\x16\x02\x16\x68\x16\x70\x26\xde\x04\x26\xb2\x04\x1c\x36\x00\x28\x02\x41\x4d\x18\x02\x41\x4d\x00\x00\x00\x26\x8a\x07\x1c\x15\x08\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x32\x15\x02\x16\x02\x16\x84\x01\x16\x8c\x01\x26\xa6\x06\x26\xfe\x05\x1c\x18\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x16\x00\x28\x04\x00\x00\x00\x3f\x18\x04\x00\x00\x00\x3f\x00\x00\x00\x26\xfe\x08\x1c\x15\x02\x19\x35\x04\x00\x06\x19\x18\x04\x76\x61\x6c\x33\x15\x02\x16\x02\x16\x6c\x16\x74\x26\xb2\x08\x26\x8a\x08\x1c\x36\x00\x28\x04\x01\x00\x00\x00\x18\x04\x01\x00\x00\x00\x00\x00\x00\x16\x98\x05\x16\x02\x00\x28\x22\x70\x61\x72\x71\x75\x65\x74\x2d\x63\x70\x70\x20\x76\x65\x72\x73\x69\x6f\x6e\x20\x31\x2e\x35\x2e\x31\x2d\x53\x4e\x41\x50\x53\x48\x4f\x54\x19\x5c\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x1c\x00\x00\x00\xc4\x01\x00\x00\x50\x41\x52\x31', + # # '' + # # ], + # }, + # 'Avro' : { + # # TODO: Not working at all: avro::Exception, e.what() = EOF reached + # #./contrib/libcxx/src/support/runtime/stdexcept_default.ipp:33: std::runtime_error::runtime_error(std::__1::basic_string, std::__1::allocator > const&) @ 0x22ce2080 in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/api/Exception.hh:36: avro::Exception::Exception(std::__1::basic_string, std::__1::allocator > const&) @ 0x1de48a6e in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/api/Stream.hh:336: avro::StreamReader::more() @ 0x22717f56 in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/api/Stream.hh:0: avro::StreamReader::readBytes(unsigned char*, unsigned long) @ 0x22717d22 in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/impl/BinaryDecoder.cc:170: avro::BinaryDecoder::decodeFixed(unsigned long, std::__1::vector >&) @ 0x227177cb in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/api/Specific.hh:216: avro::codec_traits >::decode(avro::Decoder&, std::__1::array&) @ 0x22743624 in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/api/Specific.hh:342: void avro::decode >(avro::Decoder&, std::__1::array&) @ 0x2272970d in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/impl/DataFile.cc:487: avro::DataFileReaderBase::readHeader() @ 0x2272608d in /usr/bin/clickhouse + # #./contrib/avro/lang/c++/impl/DataFile.cc:280: avro::DataFileReaderBase::DataFileReaderBase(std::__1::unique_ptr >) @ 0x22726923 in /usr/bin/clickhouse + # #./src/Processors/Formats/Impl/AvroRowInputFormat.cpp:571: DB::AvroRowInputFormat::AvroRowInputFormat(DB::Block const&, DB::ReadBuffer&, DB::RowInputFormatParams) @ 0x1de19c9b in /usr/bin/clickhouse + # 'data_sample' : [ + # #'\x4f\x62\x6a\x01\x04\x14\x61\x76\x72\x6f\x2e\x63\x6f\x64\x65\x63\x0c\x73\x6e\x61\x70\x70\x79\x16\x61\x76\x72\x6f\x2e\x73\x63\x68\x65\x6d\x61\x80\x03\x7b\x22\x74\x79\x70\x65\x22\x3a\x22\x72\x65\x63\x6f\x72\x64\x22\x2c\x22\x6e\x61\x6d\x65\x22\x3a\x22\x72\x6f\x77\x22\x2c\x22\x66\x69\x65\x6c\x64\x73\x22\x3a\x5b\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x69\x64\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x6c\x6f\x6e\x67\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x62\x6c\x6f\x63\x6b\x4e\x6f\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x31\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x62\x79\x74\x65\x73\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x32\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x66\x6c\x6f\x61\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x33\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x5d\x7d\x00\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83\x02\x20\x0a\x24\x00\x00\x04\x41\x4d\x00\x00\x00\x3f\x02\x80\xaa\x4a\xe3\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83', + # #'\x4f\x62\x6a\x01\x04\x14\x61\x76\x72\x6f\x2e\x63\x6f\x64\x65\x63\x0c\x73\x6e\x61\x70\x70\x79\x16\x61\x76\x72\x6f\x2e\x73\x63\x68\x65\x6d\x61\x80\x03\x7b\x22\x74\x79\x70\x65\x22\x3a\x22\x72\x65\x63\x6f\x72\x64\x22\x2c\x22\x6e\x61\x6d\x65\x22\x3a\x22\x72\x6f\x77\x22\x2c\x22\x66\x69\x65\x6c\x64\x73\x22\x3a\x5b\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x69\x64\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x6c\x6f\x6e\x67\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x62\x6c\x6f\x63\x6b\x4e\x6f\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x31\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x62\x79\x74\x65\x73\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x32\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x66\x6c\x6f\x61\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x33\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x5d\x7d\x00\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83\x1e\x9e\x01\x96\x01\x28\x02\x00\x04\x41\x4d\x00\x00\x00\x3f\x02\x04\x15\x0a\x00\x06\x15\x0a\x00\x08\x15\x0a\x00\x0a\x15\x0a\x00\x0c\x15\x0a\x00\x0e\x15\x0a\x00\x10\x15\x0a\x00\x12\x15\x0a\x00\x14\x15\x0a\x00\x16\x15\x0a\x00\x18\x15\x0a\x00\x1a\x15\x0a\x00\x1c\x15\x0a\x24\x1e\x00\x04\x41\x4d\x00\x00\x00\x3f\x02\x49\x73\x4d\xca\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83', + # #'\x4f\x62\x6a\x01\x04\x14\x61\x76\x72\x6f\x2e\x63\x6f\x64\x65\x63\x0c\x73\x6e\x61\x70\x70\x79\x16\x61\x76\x72\x6f\x2e\x73\x63\x68\x65\x6d\x61\x80\x03\x7b\x22\x74\x79\x70\x65\x22\x3a\x22\x72\x65\x63\x6f\x72\x64\x22\x2c\x22\x6e\x61\x6d\x65\x22\x3a\x22\x72\x6f\x77\x22\x2c\x22\x66\x69\x65\x6c\x64\x73\x22\x3a\x5b\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x69\x64\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x6c\x6f\x6e\x67\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x62\x6c\x6f\x63\x6b\x4e\x6f\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x31\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x62\x79\x74\x65\x73\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x32\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x66\x6c\x6f\x61\x74\x22\x7d\x2c\x7b\x22\x6e\x61\x6d\x65\x22\x3a\x22\x76\x61\x6c\x33\x22\x2c\x22\x74\x79\x70\x65\x22\x3a\x22\x69\x6e\x74\x22\x7d\x5d\x7d\x00\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83\x02\x20\x0a\x24\x00\x00\x04\x41\x4d\x00\x00\x00\x3f\x02\x80\xaa\x4a\xe3\x73\x6e\x66\xa3\x62\x9f\x88\xed\x28\x08\x67\xf0\x75\xaf\x23\x83', + # # '' + # ], + # }, + # TODO: test for AvroConfluence + # 'Arrow' : { + # # Not working at all: DB::Exception: Error while opening a table: Invalid: File is too small: 0, Stack trace (when copying this message, always include the lines below): + # # /src/Common/Exception.cpp:37: DB::Exception::Exception(std::__1::basic_string, std::__1::allocator > const&, int) @ 0x15c2d2a3 in /usr/bin/clickhouse + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:88: DB::ArrowBlockInputFormat::prepareReader() @ 0x1ddff1c3 in /usr/bin/clickhouse + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:26: DB::ArrowBlockInputFormat::ArrowBlockInputFormat(DB::ReadBuffer&, DB::Block const&, bool) @ 0x1ddfef63 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:2214: std::__1::__compressed_pair_elem::__compressed_pair_elem(std::__1::piecewise_construct_t, std::__1::tuple, std::__1::__tuple_indices<0ul, 1ul, 2ul>) @ 0x1de0470f in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:2299: std::__1::__compressed_pair, DB::ArrowBlockInputFormat>::__compressed_pair&, DB::ReadBuffer&, DB::Block const&, bool&&>(std::__1::piecewise_construct_t, std::__1::tuple&>, std::__1::tuple) @ 0x1de04375 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:3569: std::__1::__shared_ptr_emplace >::__shared_ptr_emplace(std::__1::allocator, DB::ReadBuffer&, DB::Block const&, bool&&) @ 0x1de03f97 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:4400: std::__1::enable_if::value), std::__1::shared_ptr >::type std::__1::make_shared(DB::ReadBuffer&, DB::Block const&, bool&&) @ 0x1de03d4c in /usr/bin/clickhouse + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:107: DB::registerInputFormatProcessorArrow(DB::FormatFactory&)::$_0::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1de010df in /usr/bin/clickhouse + # 'data_sample' : [ + # '\x41\x52\x52\x4f\x57\x31\x00\x00\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x10\x00\x00\x00\x0c\x00\x14\x00\x06\x00\x08\x00\x0c\x00\x10\x00\x0c\x00\x00\x00\x00\x00\x03\x00\x3c\x00\x00\x00\x28\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x58\x01\x00\x00\x00\x00\x00\x00\x60\x01\x00\x00\x00\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\x78\x01\x00\x00\x41\x52\x52\x4f\x57\x31', + # '\x41\x52\x52\x4f\x57\x31\x00\x00\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x48\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\xd8\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x38\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x01\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x0a\x00\x00\x00\x0c\x00\x00\x00\x0e\x00\x00\x00\x10\x00\x00\x00\x12\x00\x00\x00\x14\x00\x00\x00\x16\x00\x00\x00\x18\x00\x00\x00\x1a\x00\x00\x00\x1c\x00\x00\x00\x1e\x00\x00\x00\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\xff\xff\xff\xff\x00\x00\x00\x00\x10\x00\x00\x00\x0c\x00\x14\x00\x06\x00\x08\x00\x0c\x00\x10\x00\x0c\x00\x00\x00\x00\x00\x03\x00\x3c\x00\x00\x00\x28\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x58\x01\x00\x00\x00\x00\x00\x00\x60\x01\x00\x00\x00\x00\x00\x00\x48\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\x78\x01\x00\x00\x41\x52\x52\x4f\x57\x31', + # '\x41\x52\x52\x4f\x57\x31\x00\x00\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x10\x00\x00\x00\x0c\x00\x14\x00\x06\x00\x08\x00\x0c\x00\x10\x00\x0c\x00\x00\x00\x00\x00\x03\x00\x3c\x00\x00\x00\x28\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x58\x01\x00\x00\x00\x00\x00\x00\x60\x01\x00\x00\x00\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\x78\x01\x00\x00\x41\x52\x52\x4f\x57\x31', + # ], + # }, + # 'ArrowStream' : { + # # Not working at all: + # # Error while opening a table: Invalid: Tried reading schema message, was null or length 0, Stack trace (when copying this message, always include the lines below): + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:88: DB::ArrowBlockInputFormat::prepareReader() @ 0x1ddff1c3 in /usr/bin/clickhouse + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:26: DB::ArrowBlockInputFormat::ArrowBlockInputFormat(DB::ReadBuffer&, DB::Block const&, bool) @ 0x1ddfef63 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:2214: std::__1::__compressed_pair_elem::__compressed_pair_elem(std::__1::piecewise_construct_t, std::__1::tuple, std::__1::__tuple_indices<0ul, 1ul, 2ul>) @ 0x1de0470f in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:2299: std::__1::__compressed_pair, DB::ArrowBlockInputFormat>::__compressed_pair&, DB::ReadBuffer&, DB::Block const&, bool&&>(std::__1::piecewise_construct_t, std::__1::tuple&>, std::__1::tuple) @ 0x1de04375 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:3569: std::__1::__shared_ptr_emplace >::__shared_ptr_emplace(std::__1::allocator, DB::ReadBuffer&, DB::Block const&, bool&&) @ 0x1de03f97 in /usr/bin/clickhouse + # # /contrib/libcxx/include/memory:4400: std::__1::enable_if::value), std::__1::shared_ptr >::type std::__1::make_shared(DB::ReadBuffer&, DB::Block const&, bool&&) @ 0x1de03d4c in /usr/bin/clickhouse + # # /src/Processors/Formats/Impl/ArrowBlockInputFormat.cpp:117: DB::registerInputFormatProcessorArrow(DB::FormatFactory&)::$_1::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1de0273f in /usr/bin/clickhouse + # # /contrib/libcxx/include/type_traits:3519: decltype(std::__1::forward(fp)(std::__1::forward(fp0), std::__1::forward(fp0), std::__1::forward(fp0), std::__1::forward(fp0))) std::__1::__invoke(DB::registerInputFormatProcessorArrow(DB::FormatFactory&)::$_1&, DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1de026da in /usr/bin/clickhouse + # # /contrib/libcxx/include/__functional_base:317: std::__1::shared_ptr std::__1::__invoke_void_return_wrapper >::__call(DB::registerInputFormatProcessorArrow(DB::FormatFactory&)::$_1&, DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1de025ed in /usr/bin/clickhouse + # # /contrib/libcxx/include/functional:1540: std::__1::__function::__alloc_func, std::__1::shared_ptr (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1de0254a in /usr/bin/clickhouse + # # /contrib/libcxx/include/functional:1714: std::__1::__function::__func, std::__1::shared_ptr (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) @ 0x1de0165c in /usr/bin/clickhouse + # # /contrib/libcxx/include/functional:1867: std::__1::__function::__value_func (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1dd14dbd in /usr/bin/clickhouse + # # /contrib/libcxx/include/functional:2473: std::__1::function (DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&)>::operator()(DB::ReadBuffer&, DB::Block const&, DB::RowInputFormatParams const&, DB::FormatSettings const&) const @ 0x1dd07035 in /usr/bin/clickhouse + # # /src/Formats/FormatFactory.cpp:258: DB::FormatFactory::getInputFormat(std::__1::basic_string, std::__1::allocator > const&, DB::ReadBuffer&, DB::Block const&, DB::Context const&, unsigned long, std::__1::function) const @ 0x1dd04007 in /usr/bin/clickhouse + # # /src/Storages/Kafka/KafkaBlockInputStream.cpp:76: DB::KafkaBlockInputStream::readImpl() @ 0x1d8f6559 in /usr/bin/clickhouse + # # /src/DataStreams/IBlockInputStream.cpp:60: DB::IBlockInputStream::read() @ 0x1c9c92fd in /usr/bin/clickhouse + # # /src/DataStreams/copyData.cpp:26: void DB::copyDataImpl*)::$_0&, void (&)(DB::Block const&)>(DB::IBlockInputStream&, DB::IBlockOutputStream&, DB::copyData(DB::IBlockInputStream&, DB::IBlockOutputStream&, std::__1::atomic*)::$_0&, void (&)(DB::Block const&)) @ 0x1c9ea01c in /usr/bin/clickhouse + # 'data_sample' : [ + # '\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00', + # '\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x48\x01\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\xd8\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x38\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x38\x01\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\x00\x08\x00\x00\x00\x0a\x00\x00\x00\x0c\x00\x00\x00\x0e\x00\x00\x00\x10\x00\x00\x00\x12\x00\x00\x00\x14\x00\x00\x00\x16\x00\x00\x00\x18\x00\x00\x00\x1a\x00\x00\x00\x1c\x00\x00\x00\x1e\x00\x00\x00\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x41\x4d\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\xff\xff\xff\xff\x00\x00\x00\x00', + # '\xff\xff\xff\xff\x48\x01\x00\x00\x10\x00\x00\x00\x00\x00\x0a\x00\x0c\x00\x06\x00\x05\x00\x08\x00\x0a\x00\x00\x00\x00\x01\x03\x00\x0c\x00\x00\x00\x08\x00\x08\x00\x00\x00\x04\x00\x08\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\xe4\x00\x00\x00\x9c\x00\x00\x00\x6c\x00\x00\x00\x34\x00\x00\x00\x04\x00\x00\x00\x40\xff\xff\xff\x00\x00\x00\x02\x18\x00\x00\x00\x0c\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x72\xff\xff\xff\x08\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x33\x00\x00\x00\x00\x6c\xff\xff\xff\x00\x00\x00\x03\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x06\x00\x06\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x76\x61\x6c\x32\x00\x00\x00\x00\xa0\xff\xff\xff\x00\x00\x00\x05\x18\x00\x00\x00\x10\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x04\x00\x04\x00\x00\x00\x04\x00\x00\x00\x76\x61\x6c\x31\x00\x00\x00\x00\xcc\xff\xff\xff\x00\x00\x00\x02\x20\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x08\x00\x04\x00\x06\x00\x00\x00\x10\x00\x00\x00\x07\x00\x00\x00\x62\x6c\x6f\x63\x6b\x4e\x6f\x00\x10\x00\x14\x00\x08\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x10\x00\x10\x00\x00\x00\x00\x00\x00\x02\x24\x00\x00\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x07\x00\x08\x00\x00\x00\x00\x00\x00\x01\x40\x00\x00\x00\x02\x00\x00\x00\x69\x64\x00\x00\xff\xff\xff\xff\x58\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x16\x00\x06\x00\x05\x00\x08\x00\x0c\x00\x0c\x00\x00\x00\x00\x03\x03\x00\x18\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x18\x00\x0c\x00\x04\x00\x08\x00\x0a\x00\x00\x00\xcc\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x41\x4d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00', + # ], + # }, + } + + for format_name in all_formats: + print('Set up {}'.format(format_name)) + topic_name='format_tests_{}'.format(format_name) + kafka_produce(topic_name, all_formats[format_name]['data_sample']) + instance.query(''' + DROP TABLE IF EXISTS test.kafka_{format_name}; + + CREATE TABLE test.kafka_{format_name} ( + id Int64, + blockNo UInt16, + val1 String, + val2 Float32, + val3 UInt8 + ) ENGINE = Kafka() + SETTINGS kafka_broker_list = 'kafka1:19092', + kafka_topic_list = '{topic_name}', + kafka_group_name = '{topic_name}_group', + kafka_format = '{format_name}', + kafka_flush_interval_ms = 1000 {extra_settings}; + + DROP TABLE IF EXISTS test.kafka_{format_name}_mv; + + CREATE MATERIALIZED VIEW test.kafka_{format_name}_mv Engine=Log AS + SELECT *, _topic, _partition, _offset FROM test.kafka_{format_name}; + '''.format(topic_name=topic_name, format_name=format_name, extra_settings=all_formats[format_name].get('extra_settings') or '')) + + time.sleep(12) + + for format_name in all_formats: + print('Checking {}'.format(format_name)) + topic_name='format_tests_{}'.format(format_name) + + result = instance.query('SELECT * FROM test.kafka_{format_name}_mv;'.format(format_name=format_name)) + expected = '''\ +0 0 AM 0.5 1 {topic_name} 0 0 +1 0 AM 0.5 1 {topic_name} 0 1 +2 0 AM 0.5 1 {topic_name} 0 1 +3 0 AM 0.5 1 {topic_name} 0 1 +4 0 AM 0.5 1 {topic_name} 0 1 +5 0 AM 0.5 1 {topic_name} 0 1 +6 0 AM 0.5 1 {topic_name} 0 1 +7 0 AM 0.5 1 {topic_name} 0 1 +8 0 AM 0.5 1 {topic_name} 0 1 +9 0 AM 0.5 1 {topic_name} 0 1 +10 0 AM 0.5 1 {topic_name} 0 1 +11 0 AM 0.5 1 {topic_name} 0 1 +12 0 AM 0.5 1 {topic_name} 0 1 +13 0 AM 0.5 1 {topic_name} 0 1 +14 0 AM 0.5 1 {topic_name} 0 1 +15 0 AM 0.5 1 {topic_name} 0 1 +0 0 AM 0.5 1 {topic_name} 0 2 +'''.format(topic_name=topic_name) + assert TSV(result) == TSV(expected), 'Proper result for format: {}'.format(format_name) + + # Since everything is async and shaky when receiving messages from Kafka, # we may want to try and check results multiple times in a loop. def kafka_check_result(result, check=False, ref_file='test_kafka_json.reference'): @@ -149,8 +553,6 @@ def kafka_cluster(): cluster.start() kafka_id = instance.cluster.kafka_docker_id print("kafka_id is {}".format(kafka_id)) - instance.query('CREATE DATABASE test') - yield cluster finally: @@ -159,12 +561,10 @@ def kafka_cluster(): @pytest.fixture(autouse=True) def kafka_setup_teardown(): - instance.query('DROP TABLE IF EXISTS test.kafka') + instance.query('DROP DATABASE IF EXISTS test; CREATE DATABASE test;') wait_kafka_is_available() - print("kafka is available - running test") + # print("kafka is available - running test") yield # run test - instance.query('DROP TABLE IF EXISTS test.kafka') - # Tests @@ -1064,12 +1464,11 @@ def test_kafka_flush_by_time(kafka_cluster): kafka_max_block_size = 100, kafka_row_delimiter = '\\n'; - CREATE TABLE test.view (key UInt64, value UInt64) + SELECT * FROM test.kafka; + + CREATE TABLE test.view (key UInt64, value UInt64, ts DateTime64(3) MATERIALIZED now64(3)) ENGINE = MergeTree() ORDER BY key; - - CREATE MATERIALIZED VIEW test.consumer TO test.view AS - SELECT * FROM test.kafka; ''') cancel = threading.Event() @@ -1079,16 +1478,20 @@ def produce(): messages = [] messages.append(json.dumps({'key': 0, 'value': 0})) kafka_produce('flush_by_time', messages) - time.sleep(1) + time.sleep(0.8) kafka_thread = threading.Thread(target=produce) kafka_thread.start() + instance.query(''' + CREATE MATERIALIZED VIEW test.consumer TO test.view AS + SELECT * FROM test.kafka; + ''') + time.sleep(18) - result = instance.query('SELECT count() FROM test.view') + result = instance.query('SELECT uniqExact(ts) = 2, count() > 15 FROM test.view') - print(result) cancel.set() kafka_thread.join() @@ -1099,8 +1502,7 @@ def produce(): DROP TABLE test.view; ''') - # 40 = 2 flushes (7.5 sec), 15 polls each, about 1 mgs per 1.5 sec - assert int(result) > 12, 'Messages from kafka should be flushed at least every stream_flush_interval_ms!' + assert TSV(result) == TSV('1 1') @pytest.mark.timeout(600) @@ -1297,10 +1699,10 @@ def produce(): # Some queries to debug... # SELECT * FROM test.destination where key in (SELECT key FROM test.destination group by key having count() <> 1) - # select number + 1 as key from numbers(4141) left join test.destination using (key) where test.destination.key = 0; + # select number + 1 as key from numbers(4141) x left join test.destination using (key) where test.destination.key = 0; # SELECT * FROM test.destination WHERE key between 2360 and 2370 order by key; # select _partition from test.destination group by _partition having count() <> max(_offset) + 1; - # select toUInt64(0) as _partition, number + 1 as _offset from numbers(400) left join test.destination using (_partition,_offset) where test.destination.key = 0 order by _offset; + # select toUInt64(0) as _partition, number + 1 as _offset from numbers(400) x left join test.destination using (_partition,_offset) where test.destination.key = 0 order by _offset; # SELECT * FROM test.destination WHERE _partition = 0 and _offset between 220 and 240 order by _offset; # CREATE TABLE test.reference (key UInt64, value UInt64) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka1:19092', @@ -1608,7 +2010,70 @@ def test_kafka_duplicates_when_commit_failed(kafka_cluster): # impossible. So we have a duplicate in that scenario, but we report that situation properly. assert TSV(result) == TSV('42\t22\t22') +# if we came to partition end we will repeat polling until reaching kafka_max_block_size or flush_interval +# that behavior is a bit quesionable - we can just take a bigger pauses between polls instead - +# to do more job in a single pass, and give more rest for a thread. +# But in cases of some peaky loads in kafka topic the current contract sounds more predictable and +# easier to understand, so let's keep it as is for now. +# also we can came to eof because we drained librdkafka internal queue too fast +@pytest.mark.timeout(120) +def test_premature_flush_on_eof(kafka_cluster): + instance.query(''' + CREATE TABLE test.kafka (key UInt64, value UInt64) + ENGINE = Kafka + SETTINGS kafka_broker_list = 'kafka1:19092', + kafka_topic_list = 'premature_flush_on_eof', + kafka_group_name = 'premature_flush_on_eof', + kafka_format = 'JSONEachRow'; + SELECT * FROM test.kafka LIMIT 1; + CREATE TABLE test.destination ( + key UInt64, + value UInt64, + _topic String, + _key String, + _offset UInt64, + _partition UInt64, + _timestamp Nullable(DateTime), + _consumed_by LowCardinality(String) + ) + ENGINE = MergeTree() + ORDER BY key; + ''') + + messages = [json.dumps({'key': j+1, 'value': j+1}) for j in range(1)] + kafka_produce('premature_flush_on_eof', messages) + + instance.query(''' + CREATE MATERIALIZED VIEW test.kafka_consumer TO test.destination AS + SELECT + key, + value, + _topic, + _key, + _offset, + _partition, + _timestamp + FROM test.kafka; + ''') + + + # all subscriptions/assignments done during select, so it start sending data to test.destination + # immediately after creation of MV + time.sleep(2) + # produce more messages after delay + kafka_produce('premature_flush_on_eof', messages) + # data was not flushed yet (it will be flushed 7.5 sec after creating MV) + assert int(instance.query("SELECT count() FROM test.destination")) == 0 + time.sleep(6) + + # it should be single part, i.e. single insert + result = instance.query('SELECT _part, count() FROM test.destination group by _part') + assert TSV(result) == TSV('all_1_1_0\t2') + instance.query(''' + DROP TABLE test.kafka_consumer; + DROP TABLE test.destination; + ''') if __name__ == '__main__': cluster.start() From 9ddd7b98cc877eaae432203fe1465810cc91bc0d Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 12:47:15 +0300 Subject: [PATCH 0430/1102] Fix tests. --- src/Interpreters/InterpreterSelectQuery.cpp | 5 +---- src/Processors/QueryPlan/ReadFromStorageStep.cpp | 13 ++++++++----- src/Processors/QueryPlan/ReadFromStorageStep.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index c11cbbe8c4d5..0e75707274cc 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -480,9 +480,6 @@ BlockIO InterpreterSelectQuery::execute() buildQueryPlan(query_plan); res.pipeline = std::move(*query_plan.buildQueryPipeline()); - res.pipeline.addInterpreterContext(context); - res.pipeline.addStorageHolder(storage); - return res; } @@ -1340,7 +1337,7 @@ void InterpreterSelectQuery::executeFetchColumns( auto read_step = std::make_unique( table_lock, options, storage, - required_columns, query_info, *context, processing_stage, max_block_size, max_streams); + required_columns, query_info, context, processing_stage, max_block_size, max_streams); read_step->setStepDescription("Read from " + storage->getName()); query_plan.addStep(std::move(read_step)); diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.cpp b/src/Processors/QueryPlan/ReadFromStorageStep.cpp index 8d8a9bf717b7..83b8682e09c8 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.cpp +++ b/src/Processors/QueryPlan/ReadFromStorageStep.cpp @@ -18,7 +18,7 @@ ReadFromStorageStep::ReadFromStorageStep( StoragePtr storage_, const Names & required_columns_, const SelectQueryInfo & query_info_, - const Context & context_, + std::shared_ptr context_, QueryProcessingStage::Enum processing_stage_, size_t max_block_size_, size_t max_streams_) @@ -27,7 +27,7 @@ ReadFromStorageStep::ReadFromStorageStep( , storage(std::move(storage_)) , required_columns(required_columns_) , query_info(query_info_) - , context(context_) + , context(std::move(context_)) , processing_stage(processing_stage_) , max_block_size(max_block_size_) , max_streams(max_streams_) @@ -35,7 +35,7 @@ ReadFromStorageStep::ReadFromStorageStep( /// Note: we read from storage in constructor of step because we don't know real header before reading. /// It will be fixed when storage return QueryPlanStep itself. - Pipes pipes = storage->read(required_columns, query_info, context, processing_stage, max_block_size, max_streams); + Pipes pipes = storage->read(required_columns, query_info, *context, processing_stage, max_block_size, max_streams); if (pipes.empty()) { @@ -72,7 +72,7 @@ ReadFromStorageStep::ReadFromStorageStep( /// Set the limits and quota for reading data, the speed and time of the query. { - const Settings & settings = context.getSettingsRef(); + const Settings & settings = context->getSettingsRef(); IBlockInputStream::LocalLimits limits; limits.mode = IBlockInputStream::LIMITS_TOTAL; @@ -99,7 +99,7 @@ ReadFromStorageStep::ReadFromStorageStep( limits.speed_limits.max_execution_bps = settings.max_execution_speed_bytes; limits.speed_limits.timeout_before_checking_execution_speed = settings.timeout_before_checking_execution_speed; - auto quota = context.getQuota(); + auto quota = context->getQuota(); for (auto & pipe : pipes) { @@ -119,6 +119,9 @@ ReadFromStorageStep::ReadFromStorageStep( pipeline->init(std::move(pipes)); + pipeline->addInterpreterContext(std::move(context)); + pipeline->addStorageHolder(std::move(storage)); + output_stream = DataStream{.header = pipeline->getHeader()}; } diff --git a/src/Processors/QueryPlan/ReadFromStorageStep.h b/src/Processors/QueryPlan/ReadFromStorageStep.h index 13d6b9e255d4..230e5acc1e07 100644 --- a/src/Processors/QueryPlan/ReadFromStorageStep.h +++ b/src/Processors/QueryPlan/ReadFromStorageStep.h @@ -23,7 +23,7 @@ class ReadFromStorageStep : public IQueryPlanStep StoragePtr storage, const Names & required_columns, const SelectQueryInfo & query_info, - const Context & context, + std::shared_ptr context, QueryProcessingStage::Enum processing_stage, size_t max_block_size, size_t max_streams); @@ -41,7 +41,7 @@ class ReadFromStorageStep : public IQueryPlanStep StoragePtr storage; const Names & required_columns; const SelectQueryInfo & query_info; - const Context & context; + std::shared_ptr context; QueryProcessingStage::Enum processing_stage; size_t max_block_size; size_t max_streams; From cac365689f53e83c7a859e1656e759fd5c88d110 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 13:45:48 +0300 Subject: [PATCH 0431/1102] Fix mx_threads in query_plan. --- src/Processors/QueryPlan/QueryPlan.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index 66e22d1704e2..f56948b58ea4 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -70,6 +70,9 @@ void QueryPlan::unitePlans(QueryPlanStepPtr step, std::vector plans) for (auto & plan : plans) root->children.emplace_back(plan.root); + + for (auto & plan : plans) + max_threads = std::max(max_threads, plan.max_threads); } void QueryPlan::addStep(QueryPlanStepPtr step) From 4b6db63fff8d78c273867d54663a9e8c6f1c24f5 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 13:53:20 +0300 Subject: [PATCH 0432/1102] Fix alter key when materialized column passed --- src/Storages/AlterCommands.cpp | 4 ++++ src/Storages/IStorage.cpp | 2 +- src/Storages/IndicesDescription.cpp | 5 +++++ src/Storages/IndicesDescription.h | 4 ++++ tests/integration/test_alter_codec/test.py | 6 ++---- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Storages/AlterCommands.cpp b/src/Storages/AlterCommands.cpp index c0fc53aa8e3e..436862a3804b 100644 --- a/src/Storages/AlterCommands.cpp +++ b/src/Storages/AlterCommands.cpp @@ -728,6 +728,10 @@ void AlterCommands::apply(StorageInMemoryMetadata & metadata, const Context & co metadata_copy.primary_key.definition_ast = nullptr; } + /// Changes in columns may lead to changes in secondary indices + for (auto & index : metadata_copy.secondary_indices) + index.recalculateWithNewColumns(metadata_copy.columns, context); + /// Changes in columns may lead to changes in TTL expressions. auto column_ttl_asts = metadata_copy.columns.getColumnTTLs(); for (const auto & [name, ast] : column_ttl_asts) diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index a244f836f5ce..01f6c7098331 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -289,7 +289,7 @@ void IStorage::check(const Block & block, bool need_all) const void IStorage::setColumns(ColumnsDescription columns_) { - if (columns_.getOrdinary().empty()) + if (columns_.getAllPhysical().empty()) throw Exception("Empty list of columns passed", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED); metadata.columns = std::move(columns_); } diff --git a/src/Storages/IndicesDescription.cpp b/src/Storages/IndicesDescription.cpp index ee9a9681e616..69b5d50dcb64 100644 --- a/src/Storages/IndicesDescription.cpp +++ b/src/Storages/IndicesDescription.cpp @@ -117,6 +117,10 @@ IndexDescription IndexDescription::getIndexFromAST(const ASTPtr & definition_ast return result; } +void IndexDescription::recalculateWithNewColumns(const ColumnsDescription & new_columns, const Context & context) +{ + *this = getIndexFromAST(definition_ast, new_columns, context); +} bool IndicesDescription::has(const String & name) const { @@ -154,6 +158,7 @@ IndicesDescription IndicesDescription::parse(const String & str, const ColumnsDe return result; } + ExpressionActionsPtr IndicesDescription::getSingleExpressionForIndices(const ColumnsDescription & columns, const Context & context) const { ASTPtr combined_expr_list = std::make_shared(); diff --git a/src/Storages/IndicesDescription.h b/src/Storages/IndicesDescription.h index b4d225c65115..18b41b05fbb4 100644 --- a/src/Storages/IndicesDescription.h +++ b/src/Storages/IndicesDescription.h @@ -55,6 +55,10 @@ struct IndexDescription /// unintentionaly share AST variables and modify them. IndexDescription(const IndexDescription & other); IndexDescription & operator=(const IndexDescription & other); + + /// Recalculate index with new columns because index expression may change + /// if something change in columns. + void recalculateWithNewColumns(const ColumnsDescription & new_columns, const Context & context); }; /// All secondary indices in storage diff --git a/tests/integration/test_alter_codec/test.py b/tests/integration/test_alter_codec/test.py index 573fe7f09612..e6ad8d664a87 100644 --- a/tests/integration/test_alter_codec/test.py +++ b/tests/integration/test_alter_codec/test.py @@ -46,8 +46,7 @@ def test_alter_codec_pk(started_cluster): with pytest.raises(QueryRuntimeException): node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 ALIAS 3 CODEC(Delta, LZ4)".format(name=name)) - with pytest.raises(QueryRuntimeException): - node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 MATERIALIZED 3 CODEC(Delta, LZ4)".format(name=name)) + node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 MATERIALIZED 3 CODEC(Delta, LZ4)".format(name=name)) node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64".format(name=name)) @@ -76,8 +75,7 @@ def test_alter_codec_index(started_cluster): with pytest.raises(QueryRuntimeException): node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 ALIAS 3 CODEC(Delta, LZ4)".format(name=name)) - with pytest.raises(QueryRuntimeException): - node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 MATERIALIZED 3 CODEC(Delta, LZ4)".format(name=name)) + node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64 MATERIALIZED 3 CODEC(Delta, LZ4)".format(name=name)) node1.query("ALTER TABLE {name} MODIFY COLUMN id UInt64".format(name=name)) From b85b6a6cc2fdcfe5f5e040f7940e64b451f391c4 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 14:27:07 +0300 Subject: [PATCH 0433/1102] Fix MergingAggregatedStep --- src/Processors/QueryPlan/MergingAggregatedStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/QueryPlan/MergingAggregatedStep.cpp b/src/Processors/QueryPlan/MergingAggregatedStep.cpp index 0d169073e7e0..459a0b900403 100644 --- a/src/Processors/QueryPlan/MergingAggregatedStep.cpp +++ b/src/Processors/QueryPlan/MergingAggregatedStep.cpp @@ -29,7 +29,7 @@ MergingAggregatedStep::MergingAggregatedStep( { /// Aggregation keys are distinct for (auto key : params->params.keys) - output_stream->distinct_columns.insert(params->params.src_header.getByPosition(key).name); + output_stream->distinct_columns.insert(params->params.intermediate_header.getByPosition(key).name); } void MergingAggregatedStep::transformPipeline(QueryPipeline & pipeline) From 016ee6316faa9bf1523e1a1f65a0aa3ece449234 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 15:05:29 +0300 Subject: [PATCH 0434/1102] Add missed check --- src/Storages/StorageInMemoryMetadata.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 81d1f3874245..c33361ec7bf5 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -16,6 +16,7 @@ namespace ErrorCodes extern const int NO_SUCH_COLUMN_IN_TABLE; extern const int NOT_FOUND_COLUMN_IN_BLOCK; extern const int TYPE_MISMATCH; + extern const int EMPTY_LIST_OF_COLUMNS_PASSED; } @@ -69,6 +70,8 @@ StorageInMemoryMetadata & StorageInMemoryMetadata::operator=(const StorageInMemo void StorageInMemoryMetadata::setColumns(ColumnsDescription columns_) { + if (columns_.getAllPhysical().empty()) + throw Exception("Empty list of columns passed", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED); columns = std::move(columns_); } From 7fda93f9233adb4abd85afea6f3211fc0a9631f3 Mon Sep 17 00:00:00 2001 From: Andrey Kadochnikov Date: Fri, 19 Jun 2020 16:07:22 +0300 Subject: [PATCH 0435/1102] Update replacingmergetree.md removes confusion --- .../table-engines/mergetree-family/replacingmergetree.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/engines/table-engines/mergetree-family/replacingmergetree.md b/docs/en/engines/table-engines/mergetree-family/replacingmergetree.md index 6eaabff9f830..a218fa1876fc 100644 --- a/docs/en/engines/table-engines/mergetree-family/replacingmergetree.md +++ b/docs/en/engines/table-engines/mergetree-family/replacingmergetree.md @@ -5,7 +5,7 @@ toc_title: ReplacingMergeTree # ReplacingMergeTree {#replacingmergetree} -The engine differs from [MergeTree](../../../engines/table-engines/mergetree-family/mergetree.md#table_engines-mergetree) in that it removes duplicate entries with the same primary key value (or more accurately, with the same [sorting key](../../../engines/table-engines/mergetree-family/mergetree.md) value). +The engine differs from [MergeTree](../../../engines/table-engines/mergetree-family/mergetree.md#table_engines-mergetree) in that it removes duplicate entries with the same [sorting key](../../../engines/table-engines/mergetree-family/mergetree.md) value. Data deduplication occurs only during a merge. Merging occurs in the background at an unknown time, so you can’t plan for it. Some of the data may remain unprocessed. Although you can run an unscheduled merge using the `OPTIMIZE` query, don’t count on using it, because the `OPTIMIZE` query will read and write a large amount of data. @@ -33,7 +33,7 @@ For a description of request parameters, see [request description](../../../sql- - `ver` — column with version. Type `UInt*`, `Date` or `DateTime`. Optional parameter. - When merging, `ReplacingMergeTree` from all the rows with the same primary key leaves only one: + When merging, `ReplacingMergeTree` from all the rows with the same sorting key leaves only one: - Last in the selection, if `ver` not set. - With the maximum version, if `ver` specified. From ec892775a5e99cbf92601622b3a777e64326b747 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 16:34:40 +0300 Subject: [PATCH 0436/1102] Fix Context leak in InterpreterSelectQuery --- src/Interpreters/InterpreterSelectQuery.cpp | 4 ++-- src/Processors/QueryPlan/ReadFromPreparedSource.cpp | 4 +++- src/Processors/QueryPlan/ReadFromPreparedSource.h | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 0e75707274cc..101c75ddc540 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -739,12 +739,12 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu if (prepared_input) { auto prepared_source_step = std::make_unique( - Pipe(std::make_shared(prepared_input))); + Pipe(std::make_shared(prepared_input)), context); query_plan.addStep(std::move(prepared_source_step)); } else if (prepared_pipe) { - auto prepared_source_step = std::make_unique(std::move(*prepared_pipe)); + auto prepared_source_step = std::make_unique(std::move(*prepared_pipe), context); query_plan.addStep(std::move(prepared_source_step)); } diff --git a/src/Processors/QueryPlan/ReadFromPreparedSource.cpp b/src/Processors/QueryPlan/ReadFromPreparedSource.cpp index fc88e13906b1..47393be1f10a 100644 --- a/src/Processors/QueryPlan/ReadFromPreparedSource.cpp +++ b/src/Processors/QueryPlan/ReadFromPreparedSource.cpp @@ -4,15 +4,17 @@ namespace DB { -ReadFromPreparedSource::ReadFromPreparedSource(Pipe pipe_) +ReadFromPreparedSource::ReadFromPreparedSource(Pipe pipe_, std::shared_ptr context_) : ISourceStep(DataStream{.header = pipe_.getHeader()}) , pipe(std::move(pipe_)) + , context(std::move(context_)) { } void ReadFromPreparedSource::initializePipeline(QueryPipeline & pipeline) { pipeline.init(std::move(pipe)); + pipeline.addInterpreterContext(std::move(context)); } } diff --git a/src/Processors/QueryPlan/ReadFromPreparedSource.h b/src/Processors/QueryPlan/ReadFromPreparedSource.h index bf3c36f072fd..e4a4a39a7bef 100644 --- a/src/Processors/QueryPlan/ReadFromPreparedSource.h +++ b/src/Processors/QueryPlan/ReadFromPreparedSource.h @@ -8,7 +8,7 @@ namespace DB class ReadFromPreparedSource : public ISourceStep { public: - explicit ReadFromPreparedSource(Pipe pipe_); + explicit ReadFromPreparedSource(Pipe pipe_, std::shared_ptr context_); String getName() const override { return "ReadNothing"; } @@ -16,6 +16,7 @@ class ReadFromPreparedSource : public ISourceStep private: Pipe pipe; + std::shared_ptr context; }; } From b504fc6a907937b5c400249c31b859aaa4838b7d Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 16:37:27 +0300 Subject: [PATCH 0437/1102] Fix error code --- .../0_stateless/01213_alter_rename_primary_key_zookeeper.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/01213_alter_rename_primary_key_zookeeper.sql b/tests/queries/0_stateless/01213_alter_rename_primary_key_zookeeper.sql index daec7666e2de..b7102fab2309 100644 --- a/tests/queries/0_stateless/01213_alter_rename_primary_key_zookeeper.sql +++ b/tests/queries/0_stateless/01213_alter_rename_primary_key_zookeeper.sql @@ -51,6 +51,6 @@ ALTER TABLE table_for_rename_with_primary_key RENAME COLUMN key2 TO renamed_key2 ALTER TABLE table_for_rename_with_primary_key RENAME COLUMN key3 TO renamed_key3; --{serverError 47} -ALTER TABLE table_for_rename_with_primary_key RENAME COLUMN value1 TO renamed_value1; --{serverError 524} +ALTER TABLE table_for_rename_with_primary_key RENAME COLUMN value1 TO renamed_value1; --{serverError 47} DROP TABLE IF EXISTS table_for_rename_with_primary_key; From 6c9c33f8709692f1e9dd0739b651a4db6e239947 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 17:00:11 +0300 Subject: [PATCH 0438/1102] Fix Context leak in InterpreterSelectQuery --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 101c75ddc540..311200cce986 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1049,7 +1049,7 @@ void InterpreterSelectQuery::executeFetchColumns( {std::move(column), std::make_shared(func, argument_types, desc.parameters), desc.column_name}}; auto istream = std::make_shared(block_with_count); - auto prepared_count = std::make_unique(Pipe(std::make_shared(istream))); + auto prepared_count = std::make_unique(Pipe(std::make_shared(istream)), context); prepared_count->setStepDescription("Optimized trivial count"); query_plan.addStep(std::move(prepared_count)); from_stage = QueryProcessingStage::WithMergeableState; From 07ad947c1ddb9081a32d9292a53ba9076cac2b96 Mon Sep 17 00:00:00 2001 From: Avogar Date: Fri, 19 Jun 2020 17:11:45 +0300 Subject: [PATCH 0439/1102] Add arrays support --- .../Formats/Impl/ORCBlockOutputFormat.cpp | 38 ++++++++++++++---- .../Formats/Impl/ORCBlockOutputFormat.h | 3 ++ .../01308_orc_output_format_arrays.reference | Bin 0 -> 567 bytes .../01308_orc_output_format_arrays.sh | 15 +++++++ 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 tests/queries/0_stateless/01308_orc_output_format_arrays.reference create mode 100755 tests/queries/0_stateless/01308_orc_output_format_arrays.sh diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp index d57a5b665ca7..77cdf1c352b7 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -104,13 +104,11 @@ ORC_UNIQUE_PTR ORCBlockOutputFormat::getORCType(const DataTypePtr & t { return getORCType(removeNullable(type)); } - /* case TypeIndex::Array: { const auto * array_type = typeid_cast(type.get()); return orc::createListType(getORCType(array_type->getNestedType())); } - */ case TypeIndex::Decimal32: { const auto * decimal_type = typeid_cast *>(type.get()); @@ -150,7 +148,10 @@ void ORCBlockOutputFormat::ORCBlockOutputFormat::writeNumbers( number_orc_column->notNull[i] = 0; continue; } - number_orc_column->data[i] = number_column.getElement(i); + if (std::is_same_v) + number_orc_column->data[i] = static_cast(number_column.getElement(i)); + else + number_orc_column->data[i] = number_column.getElement(i); } number_orc_column->numElements = number_column.size(); } @@ -362,7 +363,6 @@ void ORCBlockOutputFormat::writeColumn( writeColumn(orc_column, nullable_column.getNestedColumn(), nested_type, &new_null_bytemap); break; } - /* Doesn't work for unknown reason case TypeIndex::Array: { orc::ListVectorBatch * list_orc_column = dynamic_cast(orc_column); @@ -375,23 +375,45 @@ void ORCBlockOutputFormat::writeColumn( { list_orc_column->offsets[i + 1] = offsets[i]; } - const IColumn & nested_column = list_column.getData(); orc::ColumnVectorBatch * nested_orc_column = list_orc_column->elements.get(); - writeColumn(nested_orc_column, nested_column, nested_type, null_bytemap, nested_column.size()); + writeColumn(nested_orc_column, list_column.getData(), nested_type, null_bytemap); list_orc_column->numElements = list_column.size(); break; } - */ default: throw Exception("Type " + type->getName() + " is not supported for ORC output format", ErrorCodes::ILLEGAL_COLUMN); } } +size_t ORCBlockOutputFormat::getColumnSize(const IColumn & column, DataTypePtr & type) +{ + if (type->getTypeId() == TypeIndex::Array) + { + auto nested_type = assert_cast(*type).getNestedType(); + const IColumn & nested_column = assert_cast(column).getData(); + return getColumnSize(nested_column, nested_type); + } + return column.size(); +} + +size_t ORCBlockOutputFormat::getMaxColumnSize(Chunk & chunk) +{ + size_t columns_num = chunk.getNumColumns(); + size_t max_column_size = 0; + for (size_t i = 0; i != columns_num; ++i) + { + max_column_size = std::max(max_column_size, getColumnSize(*chunk.getColumns()[i], data_types[i])); + } + return max_column_size; +} + void ORCBlockOutputFormat::consume(Chunk chunk) { size_t columns_num = chunk.getNumColumns(); size_t rows_num = chunk.getNumRows(); - ORC_UNIQUE_PTR batch = writer->createRowBatch(rows_num); + /// getMaxColumnSize is needed to write arrays. + /// The size of the batch must be no less than total amount of array elements. + ORC_UNIQUE_PTR batch = writer->createRowBatch(getMaxColumnSize(chunk)); orc::StructVectorBatch *root = dynamic_cast(batch.get()); for (size_t i = 0; i != columns_num; ++i) { diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h index 0cd50959ad7d..0252afd30156 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.h +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.h @@ -55,6 +55,9 @@ class ORCBlockOutputFormat : public IOutputFormat void writeColumn(orc::ColumnVectorBatch * orc_column, const IColumn & column, DataTypePtr & type, const PaddedPODArray * null_bytemap); + size_t getColumnSize(const IColumn & column, DataTypePtr & ptr); + size_t getMaxColumnSize(Chunk & chunk); + const FormatSettings format_settings; ORCOutputStream output_stream; DataTypes data_types; diff --git a/tests/queries/0_stateless/01308_orc_output_format_arrays.reference b/tests/queries/0_stateless/01308_orc_output_format_arrays.reference new file mode 100644 index 0000000000000000000000000000000000000000..1f9646ac112132378f512bb4e3a610f6019698e1 GIT binary patch literal 567 zcmaKou}Z{15Qb-Fv+J_CUfj1(^yJ73Txpxm?(hk$eF1A9K)8pp z5ER#OZ*wFHHuLYd|I9G_i{;#AZq?1nOH zi@h-PB)k2_Z@x78PeSsIycPNP4-!J}r2;h0Sd#;1L6P@^5+4Osk&~#JS^h!#DJTY| z99adXz?c*3z$*GG`bp%3P4IQ>*K8yV+53Mj)#Y{L&fPY*Z5^i6IX{^Uqp&J}r7{({ zSCX4ST<`_rgrW2m5pNorkz6v0~6|4#a#fA`ch4R^#kxNzslh121M bv&Wk&w9hE%uGh(gAgwNH^YEtBVmaq;55+tO literal 0 HcmV?d00001 diff --git a/tests/queries/0_stateless/01308_orc_output_format_arrays.sh b/tests/queries/0_stateless/01308_orc_output_format_arrays.sh new file mode 100755 index 000000000000..8e36cf604eaa --- /dev/null +++ b/tests/queries/0_stateless/01308_orc_output_format_arrays.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +$CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS orc"; + +$CLICKHOUSE_CLIENT --query="CREATE TABLE orc (array1 Array(Int32), array2 Array(Array(Int32))) ENGINE = Memory"; + +$CLICKHOUSE_CLIENT --query="INSERT INTO orc VALUES ([1,2,3,4,5], [[1,2], [3,4], [5]]), ([42], [[42, 42], [42]])"; + +$CLICKHOUSE_CLIENT --query="SELECT * FROM orc FORMAT ORC"; + +$CLICKHOUSE_CLIENT --query="DROP TABLE orc"; + From f6cd3f43bb9a8177d73e8240a165f69daa0bd728 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 17:42:01 +0300 Subject: [PATCH 0440/1102] Fix Context leak in InterpreterSelectQuery --- src/Interpreters/InterpreterSelectQuery.cpp | 1 + src/Processors/QueryPlan/QueryPlan.cpp | 17 +++++++++++++++++ src/Processors/QueryPlan/QueryPlan.h | 8 ++++++++ 3 files changed, 26 insertions(+) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 311200cce986..75b1a480aabf 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1300,6 +1300,7 @@ void InterpreterSelectQuery::executeFetchColumns( } interpreter_subquery->buildQueryPlan(query_plan); + query_plan.addInterpreterContext(context); } else if (storage) { diff --git a/src/Processors/QueryPlan/QueryPlan.cpp b/src/Processors/QueryPlan/QueryPlan.cpp index f56948b58ea4..d5ac5d0572c9 100644 --- a/src/Processors/QueryPlan/QueryPlan.cpp +++ b/src/Processors/QueryPlan/QueryPlan.cpp @@ -11,6 +11,8 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } +QueryPlan::~QueryPlan() = default; + void QueryPlan::checkInitialized() const { if (!isInitialized()) @@ -72,7 +74,11 @@ void QueryPlan::unitePlans(QueryPlanStepPtr step, std::vector plans) root->children.emplace_back(plan.root); for (auto & plan : plans) + { max_threads = std::max(max_threads, plan.max_threads); + interpreter_context.insert(interpreter_context.end(), + plan.interpreter_context.begin(), plan.interpreter_context.end()); + } } void QueryPlan::addStep(QueryPlanStepPtr step) @@ -136,7 +142,10 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() auto & frame = stack.top(); if (last_pipeline) + { frame.pipelines.emplace_back(std::move(last_pipeline)); + last_pipeline = nullptr; + } size_t next_child = frame.pipelines.size(); if (next_child == frame.node->children.size()) @@ -153,7 +162,15 @@ QueryPipelinePtr QueryPlan::buildQueryPipeline() stack.push(Frame{.node = frame.node->children[next_child]}); } + for (auto & context : interpreter_context) + last_pipeline->addInterpreterContext(std::move(context)); + return last_pipeline; } +void QueryPlan::addInterpreterContext(std::shared_ptr context) +{ + interpreter_context.emplace_back(std::move(context)); +} + } diff --git a/src/Processors/QueryPlan/QueryPlan.h b/src/Processors/QueryPlan/QueryPlan.h index d47c5052a4f6..45dfd6cf601e 100644 --- a/src/Processors/QueryPlan/QueryPlan.h +++ b/src/Processors/QueryPlan/QueryPlan.h @@ -14,10 +14,14 @@ using QueryPlanStepPtr = std::unique_ptr; class QueryPipeline; using QueryPipelinePtr = std::unique_ptr; +class Context; + /// A tree of query steps. class QueryPlan { public: + ~QueryPlan(); + void unitePlans(QueryPlanStepPtr step, std::vector plans); void addStep(QueryPlanStepPtr step); @@ -31,6 +35,8 @@ class QueryPlan /// TODO: make it in a better way. void setMaxThreads(size_t max_threads_) { max_threads = max_threads_; } + void addInterpreterContext(std::shared_ptr context); + private: struct Node { @@ -47,6 +53,8 @@ class QueryPlan void checkNotCompleted() const; size_t max_threads = 0; + + std::vector> interpreter_context; }; } From f6ab431f2f036514aa5c53680f27e96a4e04f1e1 Mon Sep 17 00:00:00 2001 From: Avogar Date: Fri, 19 Jun 2020 17:50:44 +0300 Subject: [PATCH 0441/1102] Change String type to Binary --- src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp index 77cdf1c352b7..7400c30306b3 100644 --- a/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/ORCBlockOutputFormat.cpp @@ -98,7 +98,7 @@ ORC_UNIQUE_PTR ORCBlockOutputFormat::getORCType(const DataTypePtr & t case TypeIndex::FixedString: [[fallthrough]]; case TypeIndex::String: { - return orc::createPrimitiveType(orc::TypeKind::STRING); + return orc::createPrimitiveType(orc::TypeKind::BINARY); } case TypeIndex::Nullable: { From 3c47faa9daa9ac8bc9445ac914d6a7d266d5e239 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 18:14:08 +0300 Subject: [PATCH 0442/1102] Move partition key initialization into registerMergeTree --- src/Storages/IStorage.h | 2 ++ src/Storages/MergeTree/MergeTreeData.cpp | 6 +----- src/Storages/MergeTree/MergeTreeData.h | 2 +- src/Storages/MergeTree/registerStorageMergeTree.cpp | 6 ++++++ src/Storages/StorageInMemoryMetadata.h | 3 --- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 1309b727a74f..eb69264c6c87 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -138,7 +138,9 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: StorageInMemoryMetadata getInMemoryMetadata() const { return *metadata.get(); } + StorageMetadataPtr getInMemoryMetadataPtr() const { return metadata.get(); } + void setInMemoryMetadata(const StorageInMemoryMetadata & metadata_) { metadata.set(std::make_unique(metadata_)); diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 72937bd71027..dfc7636b3e40 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -118,7 +118,7 @@ const char * DELETE_ON_DESTROY_MARKER_PATH = "delete-on-destroy.txt"; MergeTreeData::MergeTreeData( const StorageID & table_id_, const String & relative_data_path_, - StorageInMemoryMetadata metadata_, + const StorageInMemoryMetadata & metadata_, Context & context_, const String & date_column_name, const MergingParams & merging_params_, @@ -143,15 +143,11 @@ MergeTreeData::MergeTreeData( throw Exception("MergeTree storages require data path", ErrorCodes::INCORRECT_FILE_NAME); MergeTreeDataFormatVersion min_format_version(0); - /// TODO(alesap) Move to register methods if (!date_column_name.empty()) { try { - auto partition_by_ast = makeASTFunction("toYYYYMM", std::make_shared(date_column_name)); - metadata_.partition_key = KeyDescription::getKeyFromAST(partition_by_ast, metadata_.columns, global_context); initPartitionKey(metadata_.partition_key); - if (minmax_idx_date_column_pos == -1) throw Exception("Could not find Date column", ErrorCodes::BAD_TYPE_OF_FIELD); } diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index 2e6c0bfc9031..c6721658d787 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -324,7 +324,7 @@ class MergeTreeData : public IStorage /// attach - whether the existing table is attached or the new table is created. MergeTreeData(const StorageID & table_id_, const String & relative_data_path_, - StorageInMemoryMetadata metadata_, + const StorageInMemoryMetadata & metadata_, Context & context_, const String & date_column_name, const MergingParams & merging_params_, diff --git a/src/Storages/MergeTree/registerStorageMergeTree.cpp b/src/Storages/MergeTree/registerStorageMergeTree.cpp index 98884de985e4..1ecac8f413dc 100644 --- a/src/Storages/MergeTree/registerStorageMergeTree.cpp +++ b/src/Storages/MergeTree/registerStorageMergeTree.cpp @@ -570,6 +570,12 @@ static StoragePtr create(const StorageFactory::Arguments & args) throw Exception( "Date column name must be an unquoted string" + getMergeTreeVerboseHelp(is_extended_storage_def), ErrorCodes::BAD_ARGUMENTS); + + auto partition_by_ast = makeASTFunction("toYYYYMM", std::make_shared(date_column_name)); + + metadata.partition_key = KeyDescription::getKeyFromAST(partition_by_ast, metadata.columns, args.context); + + ++arg_num; /// If there is an expression for sampling diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index bda48bc19cb0..756b11150fb9 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -14,9 +14,6 @@ namespace DB { -/// Structure represent table metadata stored in memory. -/// Only one storage engine support all fields -- MergeTree. -/// Complete table AST can be recreated from this struct. struct StorageInMemoryMetadata { /// Columns of table with their names, types, From a2e7e9f232edbb4e09a24a962b9aef2f2b8b9236 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 18:21:48 +0300 Subject: [PATCH 0443/1102] Remove unused constructor and rename method --- src/Storages/MergeTree/MergeTreeData.cpp | 6 +++--- src/Storages/MergeTree/MergeTreeData.h | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 11 ----------- src/Storages/StorageInMemoryMetadata.h | 1 - 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index dfc7636b3e40..f1567a1d18d4 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -147,7 +147,7 @@ MergeTreeData::MergeTreeData( { try { - initPartitionKey(metadata_.partition_key); + checkPartitionKeyAndInitMinMax(metadata_.partition_key); if (minmax_idx_date_column_pos == -1) throw Exception("Could not find Date column", ErrorCodes::BAD_TYPE_OF_FIELD); } @@ -161,7 +161,7 @@ MergeTreeData::MergeTreeData( else { is_custom_partitioned = true; - initPartitionKey(metadata_.partition_key); + checkPartitionKeyAndInitMinMax(metadata_.partition_key); min_format_version = MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING; } @@ -415,7 +415,7 @@ ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression(const } -void MergeTreeData::initPartitionKey(const KeyDescription & new_partition_key) +void MergeTreeData::checkPartitionKeyAndInitMinMax(const KeyDescription & new_partition_key) { if (new_partition_key.expression_list_ast->children.empty()) return; diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index c6721658d787..1b970d470ba9 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -789,7 +789,7 @@ class MergeTreeData : public IStorage void setProperties(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata, bool attach = false); - void initPartitionKey(const KeyDescription & new_partition_key); + void checkPartitionKeyAndInitMinMax(const KeyDescription & new_partition_key); void checkTTLExpressions(const StorageInMemoryMetadata & new_metadata, const StorageInMemoryMetadata & old_metadata) const; diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index c33361ec7bf5..e39dff6472e0 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -19,17 +19,6 @@ namespace ErrorCodes extern const int EMPTY_LIST_OF_COLUMNS_PASSED; } - -StorageInMemoryMetadata::StorageInMemoryMetadata( - const ColumnsDescription & columns_, - const IndicesDescription & secondary_indices_, - const ConstraintsDescription & constraints_) - : columns(columns_) - , secondary_indices(secondary_indices_) - , constraints(constraints_) -{ -} - StorageInMemoryMetadata::StorageInMemoryMetadata(const StorageInMemoryMetadata & other) : columns(other.columns) , secondary_indices(other.secondary_indices) diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 756b11150fb9..83f9180dcbb5 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -42,7 +42,6 @@ struct StorageInMemoryMetadata SelectQueryDescription select; StorageInMemoryMetadata() = default; - StorageInMemoryMetadata(const ColumnsDescription & columns_, const IndicesDescription & secondary_indices_, const ConstraintsDescription & constraints_); StorageInMemoryMetadata(const StorageInMemoryMetadata & other); StorageInMemoryMetadata & operator=(const StorageInMemoryMetadata & other); From c9fa5d2ec3e1ad7612702049e048a0193fdd991c Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 18:39:41 +0300 Subject: [PATCH 0444/1102] Better naming --- src/Storages/IStorage.h | 3 +-- src/Storages/Kafka/StorageKafka.cpp | 6 +++--- src/Storages/LiveView/StorageLiveView.cpp | 6 +++--- src/Storages/StorageBuffer.cpp | 8 ++++---- src/Storages/StorageDictionary.cpp | 6 +++--- src/Storages/StorageDistributed.cpp | 10 +++++----- src/Storages/StorageFile.cpp | 14 +++++++------- src/Storages/StorageGenerateRandom.cpp | 6 +++--- src/Storages/StorageHDFS.cpp | 8 ++++---- src/Storages/StorageInput.cpp | 6 +++--- src/Storages/StorageLog.cpp | 10 +++++----- src/Storages/StorageMaterializedView.cpp | 8 ++++---- src/Storages/StorageMemory.cpp | 8 ++++---- src/Storages/StorageMerge.cpp | 6 +++--- src/Storages/StorageMySQL.cpp | 8 ++++---- src/Storages/StorageS3.cpp | 8 ++++---- src/Storages/StorageSet.cpp | 8 ++++---- src/Storages/StorageStripeLog.cpp | 8 ++++---- src/Storages/StorageTinyLog.cpp | 10 +++++----- src/Storages/StorageURL.cpp | 8 ++++---- src/Storages/StorageValues.cpp | 6 +++--- src/Storages/StorageView.cpp | 8 ++++---- src/Storages/System/StorageSystemColumns.cpp | 6 +++--- src/Storages/System/StorageSystemDetachedParts.cpp | 6 +++--- src/Storages/System/StorageSystemDisks.cpp | 6 +++--- src/Storages/System/StorageSystemNumbers.cpp | 6 +++--- src/Storages/System/StorageSystemOne.cpp | 6 +++--- src/Storages/System/StorageSystemPartsBase.cpp | 6 +++--- src/Storages/System/StorageSystemReplicas.cpp | 6 +++--- .../System/StorageSystemStoragePolicies.cpp | 6 +++--- src/Storages/System/StorageSystemTables.cpp | 6 +++--- src/Storages/System/StorageSystemZeros.cpp | 6 +++--- 32 files changed, 114 insertions(+), 115 deletions(-) diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index eb69264c6c87..e980bad889cb 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -80,8 +80,7 @@ class IStorage : public std::enable_shared_from_this, public TypePromo { public: IStorage() = delete; - /// Storage fields should be initialized in separate methods like setColumns - /// or setTableTTLs. + /// Storage metadata can be set separately in setInMemoryMetadata method explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)), metadata(std::make_unique()) {} //-V730 virtual ~IStorage() = default; diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index b46cf0579ec1..e0949cd94201 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -137,9 +137,9 @@ StorageKafka::StorageKafka( , intermediate_commit(kafka_settings->kafka_commit_every_batch.value) , settings_adjustments(createSettingsAdjustments()) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); task->deactivate(); diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index f4f3c6b8642b..efd0a71b18fd 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -255,9 +255,9 @@ StorageLiveView::StorageLiveView( live_view_context = std::make_unique(global_context); live_view_context->makeQueryContext(); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index b4d6b66ebe79..bd3945c26e08 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -77,10 +77,10 @@ StorageBuffer::StorageBuffer( , log(&Poco::Logger::get("StorageBuffer (" + table_id_.getFullTableName() + ")")) , bg_pool(global_context.getBufferFlushSchedulePool()) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageDictionary.cpp b/src/Storages/StorageDictionary.cpp index 25126ad951da..83a093d56357 100644 --- a/src/Storages/StorageDictionary.cpp +++ b/src/Storages/StorageDictionary.cpp @@ -100,9 +100,9 @@ StorageDictionary::StorageDictionary( : IStorage(table_id_) , dictionary_name(dictionary_name_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription{getNamesAndTypes(dictionary_structure_)}); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription{getNamesAndTypes(dictionary_structure_)}); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 9c20e3f8e118..bf9e7f126c55 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -290,14 +290,14 @@ StorageDistributed::StorageDistributed( , storage_policy(storage_policy_) , relative_data_path(relative_data_path_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); if (sharding_key_) { - sharding_key_expr = buildShardingKeyExpression(sharding_key_, *global_context, metadata_.getColumns().getAllPhysical(), false); + sharding_key_expr = buildShardingKeyExpression(sharding_key_, *global_context, storage_metadata.getColumns().getAllPhysical(), false); sharding_key_column_name = sharding_key_->getColumnName(); } diff --git a/src/Storages/StorageFile.cpp b/src/Storages/StorageFile.cpp index 4867a0bc215c..c7671fd87593 100644 --- a/src/Storages/StorageFile.cpp +++ b/src/Storages/StorageFile.cpp @@ -167,9 +167,9 @@ StorageFile::StorageFile(const std::string & table_path_, const std::string & us Block header = StorageDistributedDirectoryMonitor::createStreamFromFile(first_path)->getHeader(); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription(header.getNamesAndTypesList())); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription(header.getNamesAndTypesList())); + setInMemoryMetadata(storage_metadata); } } } @@ -191,12 +191,12 @@ StorageFile::StorageFile(CommonArguments args) , compression_method(args.compression_method) , base_path(args.context.getPath()) { - StorageInMemoryMetadata metadata_; + StorageInMemoryMetadata storage_metadata; if (args.format_name != "Distributed") - metadata_.setColumns(args.columns); + storage_metadata.setColumns(args.columns); - metadata_.setConstraints(args.constraints); - setInMemoryMetadata(metadata_); + storage_metadata.setConstraints(args.constraints); + setInMemoryMetadata(storage_metadata); } class StorageFileSource : public SourceWithProgress diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index dad323f7b725..6d923f7678ca 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -388,9 +388,9 @@ StorageGenerateRandom::StorageGenerateRandom(const StorageID & table_id_, const : IStorage(table_id_), max_array_length(max_array_length_), max_string_length(max_string_length_) { random_seed = random_seed_ ? sipHash64(*random_seed_) : randomSeed(); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageHDFS.cpp b/src/Storages/StorageHDFS.cpp index ee5a426cedc7..ce492017a09c 100644 --- a/src/Storages/StorageHDFS.cpp +++ b/src/Storages/StorageHDFS.cpp @@ -50,10 +50,10 @@ StorageHDFS::StorageHDFS(const String & uri_, { context.getRemoteHostFilter().checkURL(Poco::URI(uri)); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); } namespace diff --git a/src/Storages/StorageInput.cpp b/src/Storages/StorageInput.cpp index 4430fb11186d..dc8d7ec15818 100644 --- a/src/Storages/StorageInput.cpp +++ b/src/Storages/StorageInput.cpp @@ -21,9 +21,9 @@ namespace ErrorCodes StorageInput::StorageInput(const StorageID & table_id, const ColumnsDescription & columns_) : IStorage(table_id) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index 542fb507d83a..a655373ce6e9 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -434,10 +434,10 @@ StorageLog::StorageLog( , max_compress_block_size(max_compress_block_size_) , file_checker(disk, table_path + "sizes.json") { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); @@ -445,7 +445,7 @@ StorageLog::StorageLog( /// create directories if they do not exist disk->createDirectories(table_path); - for (const auto & column : metadata_.getColumns().getAllPhysical()) + for (const auto & column : storage_metadata.getColumns().getAllPhysical()) addFiles(column.name, *column.type); marks_file_path = table_path + DBMS_STORAGE_LOG_MARKS_FILE_NAME; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 976b3c80decd..9e6245c3ced1 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -50,8 +50,8 @@ StorageMaterializedView::StorageMaterializedView( bool attach_) : IStorage(table_id_), global_context(local_context.getGlobalContext()) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); @@ -67,8 +67,8 @@ StorageMaterializedView::StorageMaterializedView( throw Exception("UNION is not supported for MATERIALIZED VIEW", ErrorCodes::QUERY_IS_NOT_SUPPORTED_IN_MATERIALIZED_VIEW); auto select = SelectQueryDescription::getSelectQueryFromASTForMatView(query.select->clone(), local_context); - metadata_.setSelectQuery(select); - setInMemoryMetadata(metadata_); + storage_metadata.setSelectQuery(select); + setInMemoryMetadata(storage_metadata); if (!has_inner_table) target_table_id = query.to_table_id; diff --git a/src/Storages/StorageMemory.cpp b/src/Storages/StorageMemory.cpp index 05b37ecf32ee..c77fddb23728 100644 --- a/src/Storages/StorageMemory.cpp +++ b/src/Storages/StorageMemory.cpp @@ -94,10 +94,10 @@ class MemoryBlockOutputStream : public IBlockOutputStream StorageMemory::StorageMemory(const StorageID & table_id_, ColumnsDescription columns_description_, ConstraintsDescription constraints_) : IStorage(table_id_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(std::move(columns_description_)); - metadata_.setConstraints(std::move(constraints_)); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(std::move(columns_description_)); + storage_metadata.setConstraints(std::move(constraints_)); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index f4030ed573fe..8a28387d24df 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -51,9 +51,9 @@ StorageMerge::StorageMerge( , table_name_regexp(table_name_regexp_) , global_context(context_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); } template diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index 3e9b48e976b3..919acd79fdd7 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -56,10 +56,10 @@ StorageMySQL::StorageMySQL( , pool(std::move(pool_)) , global_context(context_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index 7f237fd551f0..f8d3de3a2383 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -206,10 +206,10 @@ StorageS3::StorageS3( , compression_method(compression_method_) { context_global.getRemoteHostFilter().checkURL(uri_.uri); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); auto settings = context_.getStorageS3Settings().getSettings(uri.endpoint); Aws::Auth::AWSCredentials credentials(access_key_id_, secret_access_key_); diff --git a/src/Storages/StorageSet.cpp b/src/Storages/StorageSet.cpp index 58d5226c91a9..f2946afbbfd4 100644 --- a/src/Storages/StorageSet.cpp +++ b/src/Storages/StorageSet.cpp @@ -105,10 +105,10 @@ StorageSetOrJoinBase::StorageSetOrJoinBase( const Context & context_) : IStorage(table_id_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); if (relative_path_.empty()) diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index 3086e9711216..f773ab2ca1e3 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -237,10 +237,10 @@ StorageStripeLog::StorageStripeLog( , file_checker(disk, table_path + "sizes.json") , log(&Poco::Logger::get("StorageStripeLog")) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 7a399f35c9c6..9f19f44d16b7 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -339,10 +339,10 @@ StorageTinyLog::StorageTinyLog( , file_checker(disk, table_path + "sizes.json") , log(&Poco::Logger::get("StorageTinyLog")) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); if (relative_path_.empty()) throw Exception("Storage " + getName() + " requires data path", ErrorCodes::INCORRECT_FILE_NAME); @@ -353,7 +353,7 @@ StorageTinyLog::StorageTinyLog( disk->createDirectories(table_path); } - for (const auto & col : metadata_.getColumns().getAllPhysical()) + for (const auto & col : storage_metadata.getColumns().getAllPhysical()) addFiles(col.name, *col.type); } diff --git a/src/Storages/StorageURL.cpp b/src/Storages/StorageURL.cpp index 802ad0571a8e..fd9dde695ffd 100644 --- a/src/Storages/StorageURL.cpp +++ b/src/Storages/StorageURL.cpp @@ -44,10 +44,10 @@ IStorageURLBase::IStorageURLBase( { context_global.getRemoteHostFilter().checkURL(uri); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - metadata_.setConstraints(constraints_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + storage_metadata.setConstraints(constraints_); + setInMemoryMetadata(storage_metadata); } namespace diff --git a/src/Storages/StorageValues.cpp b/src/Storages/StorageValues.cpp index 063cd3d52243..e99aeb520189 100644 --- a/src/Storages/StorageValues.cpp +++ b/src/Storages/StorageValues.cpp @@ -16,9 +16,9 @@ StorageValues::StorageValues( const NamesAndTypesList & virtuals_) : IStorage(table_id_), res_block(res_block_), virtuals(virtuals_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); + setInMemoryMetadata(storage_metadata); } Pipes StorageValues::read( diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 006b1b3caec1..21d353f6bedc 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -38,8 +38,8 @@ StorageView::StorageView( const ColumnsDescription & columns_) : IStorage(table_id_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(columns_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(columns_); if (!query.select) throw Exception("SELECT query is not specified for " + getName(), ErrorCodes::INCORRECT_QUERY); @@ -47,8 +47,8 @@ StorageView::StorageView( SelectQueryDescription description; description.inner_query = query.select->ptr(); - metadata_.setSelectQuery(description); - setInMemoryMetadata(metadata_); + storage_metadata.setSelectQuery(description); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 85d0f6797082..6f5c8bc673c5 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -26,8 +26,8 @@ namespace ErrorCodes StorageSystemColumns::StorageSystemColumns(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription( + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription( { { "database", std::make_shared() }, { "table", std::make_shared() }, @@ -46,7 +46,7 @@ StorageSystemColumns::StorageSystemColumns(const std::string & name_) { "is_in_sampling_key", std::make_shared() }, { "compression_codec", std::make_shared() }, })); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/System/StorageSystemDetachedParts.cpp b/src/Storages/System/StorageSystemDetachedParts.cpp index 7228651d1402..c325df1251da 100644 --- a/src/Storages/System/StorageSystemDetachedParts.cpp +++ b/src/Storages/System/StorageSystemDetachedParts.cpp @@ -30,8 +30,8 @@ class StorageSystemDetachedParts final : explicit StorageSystemDetachedParts() : IStorage({"system", "detached_parts"}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription{{ + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription{{ {"database", std::make_shared()}, {"table", std::make_shared()}, {"partition_id", std::make_shared(std::make_shared())}, @@ -42,7 +42,7 @@ class StorageSystemDetachedParts final : {"max_block_number", std::make_shared(std::make_shared())}, {"level", std::make_shared(std::make_shared())} }}); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } Pipes read( diff --git a/src/Storages/System/StorageSystemDisks.cpp b/src/Storages/System/StorageSystemDisks.cpp index fbcdd78988a2..a270a96b8f76 100644 --- a/src/Storages/System/StorageSystemDisks.cpp +++ b/src/Storages/System/StorageSystemDisks.cpp @@ -14,8 +14,8 @@ namespace ErrorCodes StorageSystemDisks::StorageSystemDisks(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription( + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription( { {"name", std::make_shared()}, {"path", std::make_shared()}, @@ -23,7 +23,7 @@ StorageSystemDisks::StorageSystemDisks(const std::string & name_) {"total_space", std::make_shared()}, {"keep_free_space", std::make_shared()}, })); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } Pipes StorageSystemDisks::read( diff --git a/src/Storages/System/StorageSystemNumbers.cpp b/src/Storages/System/StorageSystemNumbers.cpp index 50921c53fb64..404eb1af99cc 100644 --- a/src/Storages/System/StorageSystemNumbers.cpp +++ b/src/Storages/System/StorageSystemNumbers.cpp @@ -118,9 +118,9 @@ class NumbersMultiThreadedSource : public SourceWithProgress StorageSystemNumbers::StorageSystemNumbers(const StorageID & table_id, bool multithreaded_, std::optional limit_, UInt64 offset_, bool even_distribution_) : IStorage(table_id), multithreaded(multithreaded_), even_distribution(even_distribution_), limit(limit_), offset(offset_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription({{"number", std::make_shared()}})); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription({{"number", std::make_shared()}})); + setInMemoryMetadata(storage_metadata); } Pipes StorageSystemNumbers::read( diff --git a/src/Storages/System/StorageSystemOne.cpp b/src/Storages/System/StorageSystemOne.cpp index 20d61d5da1b1..f6fba0d302c6 100644 --- a/src/Storages/System/StorageSystemOne.cpp +++ b/src/Storages/System/StorageSystemOne.cpp @@ -14,9 +14,9 @@ namespace DB StorageSystemOne::StorageSystemOne(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription({{"dummy", std::make_shared()}})); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription({{"dummy", std::make_shared()}})); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index b48f8a3cb6b6..928b146247d9 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -278,9 +278,9 @@ StorageSystemPartsBase::StorageSystemPartsBase(std::string name_, NamesAndTypesL add_alias("bytes", "bytes_on_disk"); add_alias("marks_size", "marks_bytes"); - StorageInMemoryMetadata metadata_; - metadata_.setColumns(tmp_columns); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(tmp_columns); + setInMemoryMetadata(storage_metadata); } NamesAndTypesList StorageSystemPartsBase::getVirtuals() const diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index f79e9138500d..26076d099140 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -19,8 +19,8 @@ namespace DB StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription({ + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription({ { "database", std::make_shared() }, { "table", std::make_shared() }, { "engine", std::make_shared() }, @@ -53,7 +53,7 @@ StorageSystemReplicas::StorageSystemReplicas(const std::string & name_) { "active_replicas", std::make_shared() }, { "zookeeper_exception", std::make_shared() }, })); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/System/StorageSystemStoragePolicies.cpp b/src/Storages/System/StorageSystemStoragePolicies.cpp index a6092a28a47e..e589955c8615 100644 --- a/src/Storages/System/StorageSystemStoragePolicies.cpp +++ b/src/Storages/System/StorageSystemStoragePolicies.cpp @@ -17,8 +17,8 @@ namespace ErrorCodes StorageSystemStoragePolicies::StorageSystemStoragePolicies(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns( + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns( ColumnsDescription({ {"policy_name", std::make_shared()}, {"volume_name", std::make_shared()}, @@ -27,7 +27,7 @@ StorageSystemStoragePolicies::StorageSystemStoragePolicies(const std::string & n {"max_data_part_size", std::make_shared()}, {"move_factor", std::make_shared()} })); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } Pipes StorageSystemStoragePolicies::read( diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index b7f029945d8d..deb8f0551eab 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -33,8 +33,8 @@ namespace ErrorCodes StorageSystemTables::StorageSystemTables(const std::string & name_) : IStorage({"system", name_}) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription( + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription( { {"database", std::make_shared()}, {"name", std::make_shared()}, @@ -56,7 +56,7 @@ StorageSystemTables::StorageSystemTables(const std::string & name_) {"total_rows", std::make_shared(std::make_shared())}, {"total_bytes", std::make_shared(std::make_shared())}, })); - setInMemoryMetadata(metadata_); + setInMemoryMetadata(storage_metadata); } diff --git a/src/Storages/System/StorageSystemZeros.cpp b/src/Storages/System/StorageSystemZeros.cpp index d325840091e6..9489ff249a5c 100644 --- a/src/Storages/System/StorageSystemZeros.cpp +++ b/src/Storages/System/StorageSystemZeros.cpp @@ -84,9 +84,9 @@ class ZerosSource : public SourceWithProgress StorageSystemZeros::StorageSystemZeros(const StorageID & table_id_, bool multithreaded_, std::optional limit_) : IStorage(table_id_), multithreaded(multithreaded_), limit(limit_) { - StorageInMemoryMetadata metadata_; - metadata_.setColumns(ColumnsDescription({{"zero", std::make_shared()}})); - setInMemoryMetadata(metadata_); + StorageInMemoryMetadata storage_metadata; + storage_metadata.setColumns(ColumnsDescription({{"zero", std::make_shared()}})); + setInMemoryMetadata(storage_metadata); } From 4cafa3d703df7f29f4d091516b18b873cfc3d24d Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 19:51:44 +0300 Subject: [PATCH 0445/1102] Try fix totals. --- src/Interpreters/InterpreterSelectQuery.cpp | 52 ++++++++++----------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 75b1a480aabf..cbd1a79ee6f9 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -686,6 +686,30 @@ static UInt64 getLimitForSorting(const ASTSelectQuery & query, const Context & c return 0; } + +static bool hasWithTotalsInAnySubqueryInFromClause(const ASTSelectQuery & query) +{ + if (query.group_by_with_totals) + return true; + + /** NOTE You can also check that the table in the subquery is distributed, and that it only looks at one shard. + * In other cases, totals will be computed on the initiating server of the query, and it is not necessary to read the data to the end. + */ + + if (auto query_table = extractTableExpression(query, 0)) + { + if (const auto * ast_union = query_table->as()) + { + for (const auto & elem : ast_union->list_of_selects->children) + if (hasWithTotalsInAnySubqueryInFromClause(elem->as())) + return true; + } + } + + return false; +} + + void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInputStreamPtr & prepared_input, std::optional prepared_pipe) { /** Streams of data. When the query is executed in parallel, we have several data streams. @@ -953,7 +977,7 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu * limiting the number of rows in each up to `offset + limit`. */ bool has_prelimit = false; - if (query.limitLength() && !query.limit_with_ties && + if (query.limitLength() && !query.limit_with_ties && !hasWithTotalsInAnySubqueryInFromClause(query) && !query.distinct && !expressions.hasLimitBy() && !settings.extremes) { executePreLimit(query_plan, false); @@ -1686,32 +1710,6 @@ void InterpreterSelectQuery::executeLimitBy(QueryPlan & query_plan) query_plan.addStep(std::move(limit_by)); } - -namespace -{ - bool hasWithTotalsInAnySubqueryInFromClause(const ASTSelectQuery & query) - { - if (query.group_by_with_totals) - return true; - - /** NOTE You can also check that the table in the subquery is distributed, and that it only looks at one shard. - * In other cases, totals will be computed on the initiating server of the query, and it is not necessary to read the data to the end. - */ - - if (auto query_table = extractTableExpression(query, 0)) - { - if (const auto * ast_union = query_table->as()) - { - for (const auto & elem : ast_union->list_of_selects->children) - if (hasWithTotalsInAnySubqueryInFromClause(elem->as())) - return true; - } - } - - return false; - } -} - void InterpreterSelectQuery::executeWithFill(QueryPlan & query_plan) { auto & query = getSelectQuery(); From 9ca456f255e3faee4d464b698ebd522392145347 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 19:58:27 +0300 Subject: [PATCH 0446/1102] Try fix totals. --- src/Interpreters/InterpreterSelectQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index cbd1a79ee6f9..b2361a4699c2 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -978,7 +978,7 @@ void InterpreterSelectQuery::executeImpl(QueryPlan & query_plan, const BlockInpu */ bool has_prelimit = false; if (query.limitLength() && !query.limit_with_ties && !hasWithTotalsInAnySubqueryInFromClause(query) && - !query.distinct && !expressions.hasLimitBy() && !settings.extremes) + !query.arrayJoinExpressionList() && !query.distinct && !expressions.hasLimitBy() && !settings.extremes) { executePreLimit(query_plan, false); has_prelimit = true; From 4c0879ae300c40ca0d1aa20e5f4e4856fb96c401 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 20:17:13 +0300 Subject: [PATCH 0447/1102] Better logging in storages --- src/Interpreters/InterpreterSelectQuery.cpp | 8 +++++--- src/Storages/IStorage.h | 4 +++- src/Storages/Kafka/KafkaBlockInputStream.cpp | 4 ++-- src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp | 2 +- src/Storages/MergeTree/MergeTreeReadPool.cpp | 2 +- .../MergeTree/MergeTreeReverseSelectProcessor.cpp | 2 +- src/Storages/MergeTree/MergeTreeSelectProcessor.cpp | 2 +- src/Storages/MergeTree/MergeTreeSequentialSource.cpp | 2 +- src/Storages/StorageBuffer.cpp | 3 +-- src/Storages/StorageGenerateRandom.cpp | 2 +- src/Storages/StorageInMemoryMetadata.cpp | 9 +++++---- src/Storages/StorageInMemoryMetadata.h | 4 ++-- src/Storages/StorageJoin.cpp | 4 ++-- src/Storages/StorageLog.cpp | 2 +- src/Storages/StorageMemory.cpp | 4 ++-- src/Storages/StorageMerge.cpp | 4 ++-- src/Storages/StorageMySQL.cpp | 2 +- src/Storages/StorageNull.h | 2 +- src/Storages/StorageStripeLog.cpp | 6 +++--- src/Storages/StorageTinyLog.cpp | 2 +- src/Storages/StorageValues.cpp | 2 +- src/Storages/StorageView.cpp | 2 +- src/Storages/StorageXDBC.cpp | 4 ++-- src/Storages/System/IStorageSystemOneBlock.h | 2 +- src/Storages/System/StorageSystemColumns.cpp | 2 +- src/Storages/System/StorageSystemDisks.cpp | 2 +- src/Storages/System/StorageSystemNumbers.cpp | 2 +- src/Storages/System/StorageSystemOne.cpp | 2 +- src/Storages/System/StorageSystemPartsBase.cpp | 2 +- src/Storages/System/StorageSystemReplicas.cpp | 2 +- src/Storages/System/StorageSystemStoragePolicies.cpp | 2 +- src/Storages/System/StorageSystemTables.cpp | 2 +- src/Storages/System/StorageSystemZeros.cpp | 2 +- 33 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index e0d5adf92b8e..187fdeb2b190 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -381,14 +381,15 @@ InterpreterSelectQuery::InterpreterSelectQuery( if (storage) { - source_header = metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals()); + source_header = metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals(), storage->getStorageID()); /// Fix source_header for filter actions. if (row_policy_filter) { filter_info = std::make_shared(); filter_info->column_name = generateFilterActions(filter_info->actions, row_policy_filter, required_columns); - source_header = metadata_snapshot->getSampleBlockForColumns(filter_info->actions->getRequiredColumns(), storage->getVirtuals()); + source_header = metadata_snapshot->getSampleBlockForColumns( + filter_info->actions->getRequiredColumns(), storage->getVirtuals(), storage->getStorageID()); } } @@ -1344,7 +1345,8 @@ void InterpreterSelectQuery::executeFetchColumns( if (pipes.empty()) { - Pipe pipe(std::make_shared(metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals()))); + Pipe pipe(std::make_shared( + metadata_snapshot->getSampleBlockForColumns(required_columns, storage->getVirtuals(), storage->getStorageID()))); if (query_info.prewhere_info) { diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index e980bad889cb..6abf310442eb 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -81,7 +81,9 @@ class IStorage : public std::enable_shared_from_this, public TypePromo public: IStorage() = delete; /// Storage metadata can be set separately in setInMemoryMetadata method - explicit IStorage(StorageID storage_id_) : storage_id(std::move(storage_id_)), metadata(std::make_unique()) {} //-V730 + explicit IStorage(StorageID storage_id_) + : storage_id(std::move(storage_id_)) + , metadata(std::make_unique()) {} //-V730 virtual ~IStorage() = default; IStorage(const IStorage &) = delete; diff --git a/src/Storages/Kafka/KafkaBlockInputStream.cpp b/src/Storages/Kafka/KafkaBlockInputStream.cpp index 847b0d915cd5..9634cded7c87 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockInputStream.cpp @@ -27,7 +27,7 @@ KafkaBlockInputStream::KafkaBlockInputStream( , commit_in_suffix(commit_in_suffix_) , non_virtual_header(metadata_snapshot->getSampleBlockNonMaterialized()) , virtual_header(metadata_snapshot->getSampleBlockForColumns( - {"_topic", "_key", "_offset", "_partition", "_timestamp", "_timestamp_ms", "_headers.name", "_headers.value"}, storage.getVirtuals())) + {"_topic", "_key", "_offset", "_partition", "_timestamp", "_timestamp_ms", "_headers.name", "_headers.value"}, storage.getVirtuals(), storage.getStorageID())) { } @@ -44,7 +44,7 @@ KafkaBlockInputStream::~KafkaBlockInputStream() Block KafkaBlockInputStream::getHeader() const { - return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals()); + return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals(), storage.getStorageID()); } void KafkaBlockInputStream::readPrefixImpl() diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 7f7fd203297d..fa91a9190e52 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -224,7 +224,7 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( std::multiset part_values = VirtualColumnUtils::extractSingleValueFromBlock(virtual_columns_block, "_part"); - metadata_snapshot->check(real_column_names, data.getVirtuals()); + metadata_snapshot->check(real_column_names, data.getVirtuals(), data.getStorageID()); const Settings & settings = context.getSettingsRef(); const auto & primary_key = metadata_snapshot->getPrimaryKey(); diff --git a/src/Storages/MergeTree/MergeTreeReadPool.cpp b/src/Storages/MergeTree/MergeTreeReadPool.cpp index fdf3908d21e8..d78f72d1dd08 100644 --- a/src/Storages/MergeTree/MergeTreeReadPool.cpp +++ b/src/Storages/MergeTree/MergeTreeReadPool.cpp @@ -150,7 +150,7 @@ MarkRanges MergeTreeReadPool::getRestMarks(const IMergeTreeDataPart & part, cons Block MergeTreeReadPool::getHeader() const { - return metadata_snapshot->getSampleBlockForColumns(column_names, data.getVirtuals()); + return metadata_snapshot->getSampleBlockForColumns(column_names, data.getVirtuals(), data.getStorageID()); } void MergeTreeReadPool::profileFeedback(const ReadBufferFromFileBase::ProfileInfo info) diff --git a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp index b71c343614be..1e6352824ef9 100644 --- a/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeReverseSelectProcessor.cpp @@ -50,7 +50,7 @@ MergeTreeReverseSelectProcessor::MergeTreeReverseSelectProcessor( bool quiet) : MergeTreeBaseSelectProcessor{ - replaceTypes(metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals()), owned_data_part_), + replaceTypes(metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals(), storage_.getStorageID()), owned_data_part_), storage_, metadata_snapshot_, prewhere_info_, max_block_size_rows_, preferred_block_size_bytes_, preferred_max_column_in_block_size_bytes_, reader_settings_, use_uncompressed_cache_, virt_column_names_}, diff --git a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp index b46b414bfe88..a9ba6f7836bf 100644 --- a/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp +++ b/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp @@ -30,7 +30,7 @@ MergeTreeSelectProcessor::MergeTreeSelectProcessor( bool quiet) : MergeTreeBaseSelectProcessor{ - metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals()), + metadata_snapshot_->getSampleBlockForColumns(required_columns_, storage_.getVirtuals(), storage_.getStorageID()), storage_, metadata_snapshot_, prewhere_info_, max_block_size_rows_, preferred_block_size_bytes_, preferred_max_column_in_block_size_bytes_, reader_settings_, use_uncompressed_cache_, virt_column_names_}, diff --git a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp index f8e31db2b5ab..edd63aadd29e 100644 --- a/src/Storages/MergeTree/MergeTreeSequentialSource.cpp +++ b/src/Storages/MergeTree/MergeTreeSequentialSource.cpp @@ -17,7 +17,7 @@ MergeTreeSequentialSource::MergeTreeSequentialSource( bool read_with_direct_io_, bool take_column_types_from_storage, bool quiet) - : SourceWithProgress(metadata_snapshot_->getSampleBlockForColumns(columns_to_read_, storage_.getVirtuals())) + : SourceWithProgress(metadata_snapshot_->getSampleBlockForColumns(columns_to_read_, storage_.getVirtuals(), storage_.getStorageID())) , storage(storage_) , metadata_snapshot(metadata_snapshot_) , data_part(std::move(data_part_)) diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index bd3945c26e08..e0bd19feba9b 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -90,7 +90,7 @@ class BufferSource : public SourceWithProgress public: BufferSource(const Names & column_names_, StorageBuffer::Buffer & buffer_, const StorageBuffer & storage, const StorageMetadataPtr & metadata_snapshot) : SourceWithProgress( - metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals())) + metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals(), storage.getStorageID())) , column_names(column_names_.begin(), column_names_.end()) , buffer(buffer_) {} @@ -468,7 +468,6 @@ bool StorageBuffer::mayBenefitFromIndexForIn( if (destination.get() == this) throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP); - /// TODO alesap (check destination metadata) return destination->mayBenefitFromIndexForIn(left_in_operand, query_context, destination->getInMemoryMetadataPtr()); } diff --git a/src/Storages/StorageGenerateRandom.cpp b/src/Storages/StorageGenerateRandom.cpp index 6d923f7678ca..1f227265d196 100644 --- a/src/Storages/StorageGenerateRandom.cpp +++ b/src/Storages/StorageGenerateRandom.cpp @@ -436,7 +436,7 @@ Pipes StorageGenerateRandom::read( size_t max_block_size, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Pipes pipes; pipes.reserve(num_streams); diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index e39dff6472e0..b7f4565a55a0 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -249,7 +249,8 @@ Block StorageInMemoryMetadata::getSampleBlock() const return res; } -Block StorageInMemoryMetadata::getSampleBlockForColumns(const Names & column_names, const NamesAndTypesList & virtuals) const +Block StorageInMemoryMetadata::getSampleBlockForColumns( + const Names & column_names, const NamesAndTypesList & virtuals, const StorageID & storage_id) const { Block res; @@ -274,7 +275,7 @@ Block StorageInMemoryMetadata::getSampleBlockForColumns(const Names & column_nam else { throw Exception( - "Column " + backQuote(name) + " not found in table " /*+ getStorageID().getNameForLogs() TODO(alesap)*/, + "Column " + backQuote(name) + " not found in table " + storage_id.getNameForLogs(), ErrorCodes::NOT_FOUND_COLUMN_IN_BLOCK); } } @@ -442,7 +443,7 @@ namespace } } -void StorageInMemoryMetadata::check(const Names & column_names, const NamesAndTypesList & virtuals) const +void StorageInMemoryMetadata::check(const Names & column_names, const NamesAndTypesList & virtuals, const StorageID & storage_id) const { NamesAndTypesList available_columns = getColumns().getAllPhysical(); available_columns.insert(available_columns.end(), virtuals.begin(), virtuals.end()); @@ -459,7 +460,7 @@ void StorageInMemoryMetadata::check(const Names & column_names, const NamesAndTy { if (columns_map.end() == columns_map.find(name)) throw Exception( - "There is no column with name " + backQuote(name) + " in table " + /* TODO alesap getStorageID().getNameForLogs() +*/ ". There are columns: " + list_of_columns, + "There is no column with name " + backQuote(name) + " in table " + storage_id.getNameForLogs() + ". There are columns: " + list_of_columns, ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); if (unique_names.end() != unique_names.find(name)) diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 83f9180dcbb5..1d392c2d2285 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -110,7 +110,7 @@ struct StorageInMemoryMetadata Block getSampleBlockNonMaterialized() const; /// ordinary. Block getSampleBlockWithVirtuals(const NamesAndTypesList & virtuals) const; /// ordinary + materialized + virtuals. Block getSampleBlockForColumns( - const Names & column_names, const NamesAndTypesList & virtuals) const; /// ordinary + materialized + aliases + virtuals. + const Names & column_names, const NamesAndTypesList & virtuals, const StorageID & storage_id) const; /// ordinary + materialized + aliases + virtuals. /// Returns structure with partition key. const KeyDescription & getPartitionKey() const; @@ -176,7 +176,7 @@ struct StorageInMemoryMetadata /// Verify that all the requested names are in the table and are set correctly: /// list of names is not empty and the names do not repeat. - void check(const Names & column_names, const NamesAndTypesList & virtuals) const; + void check(const Names & column_names, const NamesAndTypesList & virtuals, const StorageID & storage_id) const; /// Check that all the requested names are in the table and have the correct types. void check(const NamesAndTypesList & columns) const; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 21e4370c28ba..af1a8bf10d73 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -445,10 +445,10 @@ Pipes StorageJoin::read( size_t max_block_size, unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Pipes pipes; - pipes.emplace_back(std::make_shared(*join, max_block_size, metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); + pipes.emplace_back(std::make_shared(*join, max_block_size, metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID()))); return pipes; } diff --git a/src/Storages/StorageLog.cpp b/src/Storages/StorageLog.cpp index a655373ce6e9..39fa1d1af709 100644 --- a/src/Storages/StorageLog.cpp +++ b/src/Storages/StorageLog.cpp @@ -587,7 +587,7 @@ Pipes StorageLog::read( size_t max_block_size, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); loadMarks(); NamesAndTypesList all_columns = Nested::collect(metadata_snapshot->getColumns().getAllPhysical().addTypes(column_names)); diff --git a/src/Storages/StorageMemory.cpp b/src/Storages/StorageMemory.cpp index c77fddb23728..44413caaa578 100644 --- a/src/Storages/StorageMemory.cpp +++ b/src/Storages/StorageMemory.cpp @@ -28,7 +28,7 @@ class MemorySource : public SourceWithProgress BlocksList::iterator end_, const StorageMemory & storage, const StorageMetadataPtr & metadata_snapshot) - : SourceWithProgress(metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals())) + : SourceWithProgress(metadata_snapshot->getSampleBlockForColumns(column_names_, storage.getVirtuals(), storage.getStorageID())) , column_names(std::move(column_names_)) , begin(begin_) , end(end_) @@ -110,7 +110,7 @@ Pipes StorageMemory::read( size_t /*max_block_size*/, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); std::lock_guard lock(mutex); diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 8a28387d24df..0ef4e415ff3a 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -426,7 +426,7 @@ Block StorageMerge::getQueryHeader( { case QueryProcessingStage::FetchColumns: { - Block header = metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()); + Block header = metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID()); if (query_info.prewhere_info) { query_info.prewhere_info->prewhere_actions->execute(header); @@ -438,7 +438,7 @@ Block StorageMerge::getQueryHeader( case QueryProcessingStage::WithMergeableState: case QueryProcessingStage::Complete: return InterpreterSelectQuery( - query_info.query, context, std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals())), + query_info.query, context, std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID())), SelectQueryOptions(processed_stage).analyze()).getSampleBlock(); } throw Exception("Logical Error: unknown processed stage.", ErrorCodes::LOGICAL_ERROR); diff --git a/src/Storages/StorageMySQL.cpp b/src/Storages/StorageMySQL.cpp index 919acd79fdd7..6f57ea196d0e 100644 --- a/src/Storages/StorageMySQL.cpp +++ b/src/Storages/StorageMySQL.cpp @@ -72,7 +72,7 @@ Pipes StorageMySQL::read( size_t max_block_size_, unsigned) { - metadata_snapshot->check(column_names_, getVirtuals()); + metadata_snapshot->check(column_names_, getVirtuals(), getStorageID()); String query = transformQueryForExternalDatabase( query_info_, metadata_snapshot->getColumns().getOrdinary(), diff --git a/src/Storages/StorageNull.h b/src/Storages/StorageNull.h index e79174c25654..072a5e3bc32c 100644 --- a/src/Storages/StorageNull.h +++ b/src/Storages/StorageNull.h @@ -33,7 +33,7 @@ class StorageNull final : public ext::shared_ptr_helper, public ISt { Pipes pipes; pipes.emplace_back( - std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); + std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID()))); return pipes; } diff --git a/src/Storages/StorageStripeLog.cpp b/src/Storages/StorageStripeLog.cpp index f773ab2ca1e3..e55cc190f80d 100644 --- a/src/Storages/StorageStripeLog.cpp +++ b/src/Storages/StorageStripeLog.cpp @@ -60,7 +60,7 @@ class StripeLogSource final : public SourceWithProgress IndexForNativeFormat::Blocks::const_iterator index_end) { if (index_begin == index_end) - return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals()); + return metadata_snapshot->getSampleBlockForColumns(column_names, storage.getVirtuals(), storage.getStorageID()); /// TODO: check if possible to always return storage.getSampleBlock() @@ -276,7 +276,7 @@ Pipes StorageStripeLog::read( { std::shared_lock lock(rwlock); - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); NameSet column_names_set(column_names.begin(), column_names.end()); @@ -285,7 +285,7 @@ Pipes StorageStripeLog::read( String index_file = table_path + "index.mrk"; if (!disk->exists(index_file)) { - pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()))); + pipes.emplace_back(std::make_shared(metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID()))); return pipes; } diff --git a/src/Storages/StorageTinyLog.cpp b/src/Storages/StorageTinyLog.cpp index 9f19f44d16b7..ef8c30cacbe1 100644 --- a/src/Storages/StorageTinyLog.cpp +++ b/src/Storages/StorageTinyLog.cpp @@ -404,7 +404,7 @@ Pipes StorageTinyLog::read( const size_t max_block_size, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Pipes pipes; diff --git a/src/Storages/StorageValues.cpp b/src/Storages/StorageValues.cpp index e99aeb520189..d5585edde3b1 100644 --- a/src/Storages/StorageValues.cpp +++ b/src/Storages/StorageValues.cpp @@ -30,7 +30,7 @@ Pipes StorageValues::read( size_t /*max_block_size*/, unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Pipes pipes; diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index 21d353f6bedc..6e66b7704867 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -88,7 +88,7 @@ Pipes StorageView::read( { return std::make_shared( header, metadata_snapshot->getSampleBlockForColumns( - column_names, getVirtuals()), ConvertingTransform::MatchColumnsMode::Name); + column_names, getVirtuals(), getStorageID()), ConvertingTransform::MatchColumnsMode::Name); }); pipes = std::move(pipeline).getPipes(); diff --git a/src/Storages/StorageXDBC.cpp b/src/Storages/StorageXDBC.cpp index 05cf4ed5abff..fc4bbefe74c0 100644 --- a/src/Storages/StorageXDBC.cpp +++ b/src/Storages/StorageXDBC.cpp @@ -94,7 +94,7 @@ Pipes StorageXDBC::read( size_t max_block_size, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); bridge_helper->startBridgeSync(); return IStorageURLBase::read(column_names, metadata_snapshot, query_info, context, processed_stage, max_block_size, num_streams); @@ -130,7 +130,7 @@ BlockOutputStreamPtr StorageXDBC::write(const ASTPtr & /*query*/, const StorageM Block StorageXDBC::getHeaderBlock(const Names & column_names, const StorageMetadataPtr & metadata_snapshot) const { - return metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals()); + return metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID()); } std::string StorageXDBC::getName() const diff --git a/src/Storages/System/IStorageSystemOneBlock.h b/src/Storages/System/IStorageSystemOneBlock.h index b3a2a6fe53b4..7c2ef85f158d 100644 --- a/src/Storages/System/IStorageSystemOneBlock.h +++ b/src/Storages/System/IStorageSystemOneBlock.h @@ -37,7 +37,7 @@ class IStorageSystemOneBlock : public IStorage size_t /*max_block_size*/, unsigned /*num_streams*/) override { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Block sample_block = metadata_snapshot->getSampleBlock(); MutableColumns res_columns = sample_block.cloneEmptyColumns(); diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 6f5c8bc673c5..beb01bc61920 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -249,7 +249,7 @@ Pipes StorageSystemColumns::read( const size_t max_block_size, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); /// Create a mask of what columns are needed in the result. diff --git a/src/Storages/System/StorageSystemDisks.cpp b/src/Storages/System/StorageSystemDisks.cpp index a270a96b8f76..cf00bbb52546 100644 --- a/src/Storages/System/StorageSystemDisks.cpp +++ b/src/Storages/System/StorageSystemDisks.cpp @@ -35,7 +35,7 @@ Pipes StorageSystemDisks::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); MutableColumnPtr col_name = ColumnString::create(); MutableColumnPtr col_path = ColumnString::create(); diff --git a/src/Storages/System/StorageSystemNumbers.cpp b/src/Storages/System/StorageSystemNumbers.cpp index 404eb1af99cc..c70446ddeba0 100644 --- a/src/Storages/System/StorageSystemNumbers.cpp +++ b/src/Storages/System/StorageSystemNumbers.cpp @@ -132,7 +132,7 @@ Pipes StorageSystemNumbers::read( size_t max_block_size, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); if (limit && *limit < max_block_size) { diff --git a/src/Storages/System/StorageSystemOne.cpp b/src/Storages/System/StorageSystemOne.cpp index f6fba0d302c6..3329cbb035e1 100644 --- a/src/Storages/System/StorageSystemOne.cpp +++ b/src/Storages/System/StorageSystemOne.cpp @@ -29,7 +29,7 @@ Pipes StorageSystemOne::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); Block header{ColumnWithTypeAndName( DataTypeUInt8().createColumn(), diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index 928b146247d9..168b305605df 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -41,7 +41,7 @@ bool StorageSystemPartsBase::hasStateColumn(const Names & column_names, const St /// Do not check if only _state column is requested if (!(has_state_column && real_column_names.empty())) - metadata_snapshot->check(real_column_names, {}); + metadata_snapshot->check(real_column_names, {}, getStorageID()); return has_state_column; } diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index 26076d099140..27a9cd0c4bb0 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -66,7 +66,7 @@ Pipes StorageSystemReplicas::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); const auto access = context.getAccess(); const bool check_access_for_databases = !access->isGranted(AccessType::SHOW_TABLES); diff --git a/src/Storages/System/StorageSystemStoragePolicies.cpp b/src/Storages/System/StorageSystemStoragePolicies.cpp index e589955c8615..ec771ec24219 100644 --- a/src/Storages/System/StorageSystemStoragePolicies.cpp +++ b/src/Storages/System/StorageSystemStoragePolicies.cpp @@ -39,7 +39,7 @@ Pipes StorageSystemStoragePolicies::read( const size_t /*max_block_size*/, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); MutableColumnPtr col_policy_name = ColumnString::create(); MutableColumnPtr col_volume_name = ColumnString::create(); diff --git a/src/Storages/System/StorageSystemTables.cpp b/src/Storages/System/StorageSystemTables.cpp index deb8f0551eab..4b78416dabb2 100644 --- a/src/Storages/System/StorageSystemTables.cpp +++ b/src/Storages/System/StorageSystemTables.cpp @@ -458,7 +458,7 @@ Pipes StorageSystemTables::read( const size_t max_block_size, const unsigned /*num_streams*/) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); /// Create a mask of what columns are needed in the result. diff --git a/src/Storages/System/StorageSystemZeros.cpp b/src/Storages/System/StorageSystemZeros.cpp index 9489ff249a5c..3839439794b8 100644 --- a/src/Storages/System/StorageSystemZeros.cpp +++ b/src/Storages/System/StorageSystemZeros.cpp @@ -99,7 +99,7 @@ Pipes StorageSystemZeros::read( size_t max_block_size, unsigned num_streams) { - metadata_snapshot->check(column_names, getVirtuals()); + metadata_snapshot->check(column_names, getVirtuals(), getStorageID()); bool use_multiple_streams = multithreaded; From 8a048340b086d7b20a6d4a5ff85ad971bd6d6b21 Mon Sep 17 00:00:00 2001 From: Nikolai Kochetov Date: Fri, 19 Jun 2020 21:23:44 +0300 Subject: [PATCH 0448/1102] Fix tests. --- src/Interpreters/InterpreterInsertQuery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index e84186e91f72..c0b48bcb0958 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -178,7 +178,7 @@ BlockIO InterpreterInsertQuery::execute() /// INSERT SELECT query returns empty block auto in_stream = std::make_shared(std::move(connections), new_query_str, Block{}, context); - pipelines.emplace_back(); + pipelines.emplace_back(std::make_unique()); pipelines.back()->init(Pipe(std::make_shared(std::move(in_stream)))); pipelines.back()->setSinks([](const Block & header, QueryPipeline::StreamType) -> ProcessorPtr { From 85070ea2fd5e6a42d6ef5c4d3ab3aff909ce9652 Mon Sep 17 00:00:00 2001 From: alesapin Date: Fri, 19 Jun 2020 22:41:24 +0300 Subject: [PATCH 0449/1102] Remove unused variable --- src/Interpreters/InterpreterSelectQuery.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 187fdeb2b190..c97155231747 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -497,9 +497,6 @@ Block InterpreterSelectQuery::getSampleBlockImpl() bool second_stage = from_stage <= QueryProcessingStage::WithMergeableState && options.to_stage > QueryProcessingStage::WithMergeableState; - Names columns_required_for_sampling; - Names columns_required_for_; - analysis_result = ExpressionAnalysisResult( *query_analyzer, metadata_snapshot, From 85974dd699b53084bb1a140ddea1ec18494685c1 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Fri, 19 Jun 2020 22:56:18 +0300 Subject: [PATCH 0450/1102] Update 00816_long_concurrent_alter_column.sh --- tests/queries/0_stateless/00816_long_concurrent_alter_column.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh index 3ed0c6e1a6a2..965408065cfe 100755 --- a/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh +++ b/tests/queries/0_stateless/00816_long_concurrent_alter_column.sh @@ -59,7 +59,6 @@ wait echo "DROP TABLE concurrent_alter_column" | ${CLICKHOUSE_CLIENT} -sleep 7 # Check for deadlocks echo "SELECT * FROM system.processes WHERE query_id LIKE 'alter%'" | ${CLICKHOUSE_CLIENT} From 5f14622bfd39b63a98585d1c206c27c98485a4b2 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 19 Jun 2020 21:04:57 +0300 Subject: [PATCH 0451/1102] Preserve column alias with optimize_aggregators_of_group_by_keys optimize_aggregators_of_group_by_keys has been introduced in #11667 --- .../AggregateFunctionOfGroupByKeysVisitor.h | 8 +++++--- ...321_aggregate_functions_of_group_by_keys.reference | 11 ++++++++--- .../01321_aggregate_functions_of_group_by_keys.sql | 5 +++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Interpreters/AggregateFunctionOfGroupByKeysVisitor.h b/src/Interpreters/AggregateFunctionOfGroupByKeysVisitor.h index 575fcd95fea4..cac3e0432dcd 100644 --- a/src/Interpreters/AggregateFunctionOfGroupByKeysVisitor.h +++ b/src/Interpreters/AggregateFunctionOfGroupByKeysVisitor.h @@ -14,7 +14,7 @@ namespace DB { -///recursive traversal and check for optimizeAggregateFunctionsOfGroupByKeys +/// Recursive traversal and check for optimizeAggregateFunctionsOfGroupByKeys struct KeepAggregateFunctionMatcher { struct Data @@ -91,7 +91,7 @@ class SelectAggregateFunctionOfGroupByKeysMatcher static void visit(ASTPtr & ast, Data & data) { - ///check if function is min/max/any + /// Check if function is min/max/any auto * function_node = ast->as(); if (function_node && (function_node->name == "min" || function_node->name == "max" || function_node->name == "any" || function_node->name == "anyLast")) @@ -100,10 +100,12 @@ class SelectAggregateFunctionOfGroupByKeysMatcher KeepAggregateFunctionVisitor::Data keep_data{data.group_by_keys, keep_aggregator}; KeepAggregateFunctionVisitor(keep_data).visit(function_node->arguments); + /// Place argument of an aggregate function instead of function if (!keep_aggregator) { - ///place argument of an aggregate function instead of function + String alias = function_node->alias; ast = (function_node->arguments->children[0])->clone(); + ast->setAlias(alias); } } } diff --git a/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.reference b/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.reference index de478516b59c..0ca6bee85a3a 100644 --- a/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.reference +++ b/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.reference @@ -45,9 +45,12 @@ 18 20 24 -SELECT \n number % 2,\n number % 3\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n min(number % 2) AS a ASC,\n max(number % 3) AS b ASC -SELECT \n number % 2,\n number % 3\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n any(number % 2) AS a ASC,\n anyLast(number % 3) AS b ASC -SELECT (number % 5) * (number % 7)\nFROM numbers(10000000)\nGROUP BY \n number % 7,\n number % 5\nORDER BY max((number % 5) * (number % 7)) AS a ASC +0 +0 +SELECT \n number % 2 AS a,\n number % 3 AS b\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n min(number % 2) AS a ASC,\n max(number % 3) AS b ASC +SELECT \n number % 2 AS a,\n number % 3 AS b\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n any(number % 2) AS a ASC,\n anyLast(number % 3) AS b ASC +SELECT (number % 5) * (number % 7) AS a\nFROM numbers(10000000)\nGROUP BY \n number % 7,\n number % 5\nORDER BY max((number % 5) * (number % 7)) AS a ASC +SELECT foo\nFROM \n(\n SELECT number AS foo\n FROM numbers(1)\n GROUP BY number\n) 0 0 0 1 0 2 @@ -95,6 +98,8 @@ SELECT (number % 5) * (number % 7)\nFROM numbers(10000000)\nGROUP BY \n numbe 18 20 24 +0 SELECT \n min(number % 2) AS a,\n max(number % 3) AS b\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n a ASC,\n b ASC SELECT \n any(number % 2) AS a,\n anyLast(number % 3) AS b\nFROM numbers(10000000)\nGROUP BY \n number % 2,\n number % 3\nORDER BY \n a ASC,\n b ASC SELECT max((number % 5) * (number % 7)) AS a\nFROM numbers(10000000)\nGROUP BY \n number % 7,\n number % 5\nORDER BY a ASC +SELECT foo\nFROM \n(\n SELECT anyLast(number) AS foo\n FROM numbers(1)\n GROUP BY number\n) diff --git a/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.sql b/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.sql index c1b8dd0bd1f6..3d0395096402 100644 --- a/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.sql +++ b/tests/queries/0_stateless/01321_aggregate_functions_of_group_by_keys.sql @@ -5,17 +5,22 @@ set optimize_any_input = 0; SELECT min(number % 2) AS a, max(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; SELECT any(number % 2) AS a, anyLast(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; SELECT max((number % 5) * (number % 7)) AS a FROM numbers(10000000) GROUP BY number % 7, number % 5 ORDER BY a; +SELECT foo FROM (SELECT anyLast(number) AS foo FROM numbers(1) GROUP BY number); +SELECT anyLast(number) FROM numbers(1) GROUP BY number; analyze SELECT min(number % 2) AS a, max(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; analyze SELECT any(number % 2) AS a, anyLast(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; analyze SELECT max((number % 5) * (number % 7)) AS a FROM numbers(10000000) GROUP BY number % 7, number % 5 ORDER BY a; +analyze SELECT foo FROM (SELECT anyLast(number) AS foo FROM numbers(1) GROUP BY number); set optimize_aggregators_of_group_by_keys = 0; SELECT min(number % 2) AS a, max(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; SELECT any(number % 2) AS a, anyLast(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; SELECT max((number % 5) * (number % 7)) AS a FROM numbers(10000000) GROUP BY number % 7, number % 5 ORDER BY a; +SELECT foo FROM (SELECT anyLast(number) AS foo FROM numbers(1) GROUP BY number); analyze SELECT min(number % 2) AS a, max(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; analyze SELECT any(number % 2) AS a, anyLast(number % 3) AS b FROM numbers(10000000) GROUP BY number % 2, number % 3 ORDER BY a, b; analyze SELECT max((number % 5) * (number % 7)) AS a FROM numbers(10000000) GROUP BY number % 7, number % 5 ORDER BY a; +analyze SELECT foo FROM (SELECT anyLast(number) AS foo FROM numbers(1) GROUP BY number); From 7a76abeb2a0c55bb50d6b3ee87ba1c8732d361af Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Fri, 19 Jun 2020 23:13:07 +0300 Subject: [PATCH 0452/1102] distinct combinator for function of multiuple arguments --- .../AggregateFunctionDistinct.cpp | 19 +- .../AggregateFunctionDistinct.h | 266 ++++++++++-------- src/AggregateFunctions/Helpers.h | 13 + src/AggregateFunctions/KeyHolderHelpers.h | 2 +- .../01259_combinator_distinct.reference | 7 + .../0_stateless/01259_combinator_distinct.sql | 6 +- 6 files changed, 191 insertions(+), 122 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.cpp b/src/AggregateFunctions/AggregateFunctionDistinct.cpp index c77e977b0faf..1b1e0b872cf0 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.cpp +++ b/src/AggregateFunctions/AggregateFunctionDistinct.cpp @@ -36,21 +36,24 @@ class AggregateFunctionCombinatorDistinct final : public IAggregateFunctionCombi AggregateFunctionPtr res; if (arguments.size() == 1) { - res = AggregateFunctionPtr(createWithNumericType(*arguments[0], nested_function, arguments)); + res.reset(createWithNumericType< + AggregateFunctionDistinct, + AggregateFunctionDistinctSingleNumericData>(*arguments[0], nested_function, arguments)); + if (res) return res; if (arguments[0]->isValueUnambiguouslyRepresentedInContiguousMemoryRegion()) - return std::make_shared>(nested_function, arguments); + return std::make_shared< + AggregateFunctionDistinct< + AggregateFunctionDistinctSingleGenericData>>(nested_function, arguments); else - return std::make_shared>(nested_function, arguments); + return std::make_shared< + AggregateFunctionDistinct< + AggregateFunctionDistinctSingleGenericData>>(nested_function, arguments); } - if (!res) - throw Exception("Illegal type " /* + argument_type->getName() + */ - " of argument for aggregate function " + nested_function->getName() + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - return res; + return std::make_shared>(nested_function, arguments); } }; diff --git a/src/AggregateFunctions/AggregateFunctionDistinct.h b/src/AggregateFunctions/AggregateFunctionDistinct.h index 5c663bb64411..cb5fd526f6d9 100644 --- a/src/AggregateFunctions/AggregateFunctionDistinct.h +++ b/src/AggregateFunctions/AggregateFunctionDistinct.h @@ -6,8 +6,11 @@ #include #include #include - #include +#include +#include + +#include namespace DB { @@ -17,192 +20,231 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } + template struct AggregateFunctionDistinctSingleNumericData { /// When creating, the hash table must be small. using Set = HashSetWithStackMemory, 4>; - Set value; -}; + using Self = AggregateFunctionDistinctSingleNumericData; + Set set; -template -class AggregateFunctionDistinctBase : public IAggregateFunctionDataHelper -{ -protected: - static constexpr size_t prefix_size = sizeof(Data); - AggregateFunctionPtr nested_func; - size_t num_arguments; - - AggregateDataPtr getNestedPlace(AggregateDataPtr place) const noexcept + void add(const IColumn ** columns, size_t /* columns_num */, size_t row_num, Arena *) { - return place + prefix_size; + const auto & vec = assert_cast &>(*columns[0]).getData(); + set.insert(vec[row_num]); } - ConstAggregateDataPtr getNestedPlace(ConstAggregateDataPtr place) const noexcept + void merge(const Self & rhs, Arena *) { - return place + prefix_size; + set.merge(rhs.set); } -public: + void serialize(WriteBuffer & buf) const + { + set.write(buf); + } - size_t sizeOfData() const override + void deserialize(ReadBuffer & buf, Arena *) { - return prefix_size + nested_func->sizeOfData(); + set.read(buf); } - void create(AggregateDataPtr place) const override + MutableColumns getArguments(const DataTypes & argument_types) const { - new (place) Data; - nested_func->create(getNestedPlace(place)); + MutableColumns argument_columns; + argument_columns.emplace_back(argument_types[0]->createColumn()); + for (const auto & elem : set) + argument_columns[0]->insert(elem.getValue()); + + return argument_columns; } +}; - void destroy(AggregateDataPtr place) const noexcept override +struct AggregateFunctionDistinctGenericData +{ + /// When creating, the hash table must be small. + using Set = HashSetWithSavedHashWithStackMemory; + using Self = AggregateFunctionDistinctGenericData; + Set set; + + void merge(const Self & rhs, Arena * arena) { - this->data(place).~Data(); - nested_func->destroy(getNestedPlace(place)); + Set::LookupResult it; + bool inserted; + for (const auto & elem : rhs.set) + set.emplace(ArenaKeyHolder{elem.getValue(), *arena}, it, inserted); } - String getName() const override + void serialize(WriteBuffer & buf) const { - return nested_func->getName() + "Distinct"; + writeVarUInt(set.size(), buf); + for (const auto & elem : set) + writeStringBinary(elem.getValue(), buf); } - DataTypePtr getReturnType() const override + void deserialize(ReadBuffer & buf, Arena * arena) { - return nested_func->getReturnType(); + size_t size; + readVarUInt(size, buf); + for (size_t i = 0; i < size; ++i) + set.insert(readStringBinaryInto(*arena, buf)); } +}; - bool allocatesMemoryInArena() const override +template +struct AggregateFunctionDistinctSingleGenericData : public AggregateFunctionDistinctGenericData +{ + void add(const IColumn ** columns, size_t /* columns_num */, size_t row_num, Arena * arena) { - return true; + Set::LookupResult it; + bool inserted; + auto key_holder = getKeyHolder(*columns[0], row_num, *arena); + set.emplace(key_holder, it, inserted); } - AggregateFunctionDistinctBase(AggregateFunctionPtr nested, const DataTypes & arguments) - : IAggregateFunctionDataHelper(arguments, {}) - , nested_func(nested), num_arguments(arguments.size()) + MutableColumns getArguments(const DataTypes & argument_types) const { - if (arguments.empty()) - throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + MutableColumns argument_columns; + argument_columns.emplace_back(argument_types[0]->createColumn()); + for (const auto & elem : set) + deserializeAndInsert(elem.getValue(), *argument_columns[0]); + + return argument_columns; } }; +struct AggregateFunctionDistinctMultipleGenericData : public AggregateFunctionDistinctGenericData +{ + void add(const IColumn ** columns, size_t columns_num, size_t row_num, Arena * arena) + { + const char * begin = nullptr; + StringRef value(begin, 0); + SipHash hash; + for (size_t i = 0; i < columns_num; ++i) + { + columns[i]->updateHashWithValue(row_num, hash); + auto cur_ref = columns[i]->serializeValueIntoArena(row_num, *arena, begin); + value.data = cur_ref.data - value.size; + value.size += cur_ref.size; + } + + Set::LookupResult it; + bool inserted; + auto key_holder = SerializedKeyHolder{value, *arena}; + set.emplace(key_holder, it, inserted); + } + + MutableColumns getArguments(const DataTypes & argument_types) const + { + MutableColumns argument_columns(argument_types.size()); + for (size_t i = 0; i < argument_types.size(); ++i) + argument_columns[i] = argument_types[i]->createColumn(); + + for (const auto & elem : set) + { + const char * begin = elem.getValue().data; + for (auto & column : argument_columns) + begin = column->deserializeAndInsertFromArena(begin); + } + + return argument_columns; + } +}; /** Adaptor for aggregate functions. * Adding -Distinct suffix to aggregate function **/ -template -class AggregateFunctionDistinctSingleNumericImpl final - : public AggregateFunctionDistinctBase, - AggregateFunctionDistinctSingleNumericImpl> +template +class AggregateFunctionDistinct : public IAggregateFunctionDataHelper> { -public: +private: + static constexpr auto prefix_size = sizeof(Data); + AggregateFunctionPtr nested_func; + size_t arguments_num; - AggregateFunctionDistinctSingleNumericImpl(AggregateFunctionPtr nested, const DataTypes & arguments) - : AggregateFunctionDistinctBase< - AggregateFunctionDistinctSingleNumericData, - AggregateFunctionDistinctSingleNumericImpl>(nested, arguments) {} + AggregateDataPtr getNestedPlace(AggregateDataPtr place) const noexcept + { + return place + prefix_size; + } - void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena *) const override + ConstAggregateDataPtr getNestedPlace(ConstAggregateDataPtr place) const noexcept { - const auto & vec = assert_cast &>(*columns[0]).getData(); - this->data(place).value.insert(vec[row_num]); + return place + prefix_size; } - void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena *) const override +public: + AggregateFunctionDistinct(AggregateFunctionPtr nested_func_, const DataTypes & arguments) + : IAggregateFunctionDataHelper(arguments, nested_func_->getParameters()) + , nested_func(nested_func_) + , arguments_num(arguments.size()) { - this->data(place).value.merge(this->data(rhs).value); + if (arguments.empty()) + throw Exception("Aggregate function " + getName() + " require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + { + this->data(place).add(columns, arguments_num, row_num, arena); + } + + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override + { + this->data(place).merge(this->data(rhs), arena); } void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { - this->data(place).value.write(buf); + this->data(place).serialize(buf); } - void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena *) const override + void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { - this->data(place).value.read(buf); + this->data(place).deserialize(buf, arena); } void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override { - const auto & set = this->data(place).value; - auto arguments = this->argument_types[0]->createColumn(); - for (const auto & elem : set) - arguments->insert(elem.getValue()); + auto arguments = this->data(place).getArguments(this->argument_types); + ColumnRawPtrs arguments_raw(arguments.size()); + for (size_t i = 0; i < arguments.size(); ++i) + arguments_raw[i] = arguments[i].get(); - const auto * arguments_ptr = arguments.get(); - this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); + assert(!arguments.empty()); + this->nested_func->addBatchSinglePlace(arguments[0]->size(), this->getNestedPlace(place), arguments_raw.data(), arena); this->nested_func->insertResultInto(this->getNestedPlace(place), to, arena); } -}; - -struct AggregateFunctionDistinctSingleGenericData -{ - using Set = HashSetWithSavedHashWithStackMemory; - Set value; -}; - -template -class AggregateFunctionDistinctSingleGenericImpl final - : public AggregateFunctionDistinctBase> -{ -public: - using Data = AggregateFunctionDistinctSingleGenericData; - AggregateFunctionDistinctSingleGenericImpl(AggregateFunctionPtr nested, const DataTypes & arguments) - : AggregateFunctionDistinctBase< - AggregateFunctionDistinctSingleGenericData, - AggregateFunctionDistinctSingleGenericImpl>(nested, arguments) {} - - void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num, Arena * arena) const override + size_t sizeOfData() const override { - auto & set = this->data(place).value; - - Data::Set::LookupResult it; - bool inserted; - auto key_holder = getKeyHolder(*columns[0], row_num, *arena); - set.emplace(key_holder, it, inserted); + return prefix_size + nested_func->sizeOfData(); } - void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs, Arena * arena) const override + void create(AggregateDataPtr place) const override { - auto & cur_set = this->data(place).value; - const auto & rhs_set = this->data(rhs).value; - - Data::Set::LookupResult it; - bool inserted; - for (const auto & elem : rhs_set) - cur_set.emplace(ArenaKeyHolder{elem.getValue(), *arena}, it, inserted); + new (place) Data; + nested_func->create(getNestedPlace(place)); } - void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override + void destroy(AggregateDataPtr place) const noexcept override { - const auto & set = this->data(place).value; - writeVarUInt(set.size(), buf); - for (const auto & elem : set) - writeStringBinary(elem.getValue(), buf); + this->data(place).~Data(); + nested_func->destroy(getNestedPlace(place)); } - void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override + String getName() const override { - auto & set = this->data(place).value; - size_t size; - readVarUInt(size, buf); - for (size_t i = 0; i < size; ++i) - set.insert(readStringBinaryInto(*arena, buf)); + return nested_func->getName() + "Distinct"; } - void insertResultInto(AggregateDataPtr place, IColumn & to, Arena * arena) const override + DataTypePtr getReturnType() const override { - const auto & set = this->data(place).value; - auto arguments = this->argument_types[0]->createColumn(); - for (const auto & elem : set) - deserializeAndInsert(elem.getValue(), *arguments); + return nested_func->getReturnType(); + } - const auto * arguments_ptr = arguments.get(); - this->nested_func->addBatchSinglePlace(arguments->size(), this->getNestedPlace(place), &arguments_ptr, arena); - this->nested_func->insertResultInto(this->getNestedPlace(place), to, arena); + bool allocatesMemoryInArena() const override + { + return true; } }; diff --git a/src/AggregateFunctions/Helpers.h b/src/AggregateFunctions/Helpers.h index 6c03d25e0b15..bc24e53a7632 100644 --- a/src/AggregateFunctions/Helpers.h +++ b/src/AggregateFunctions/Helpers.h @@ -33,6 +33,19 @@ static IAggregateFunction * createWithNumericType(const IDataType & argument_typ return nullptr; } +template