From 96fb6da011d7fbf98b59542339fbf87ee5b7c5bf Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sun, 9 Mar 2014 18:24:04 +0400 Subject: [PATCH 001/281] Merge --- dbms/include/DB/Storages/MergeTreeData.h | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 dbms/include/DB/Storages/MergeTreeData.h diff --git a/dbms/include/DB/Storages/MergeTreeData.h b/dbms/include/DB/Storages/MergeTreeData.h new file mode 100644 index 00000000000..ec3f68d7354 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTreeData.h @@ -0,0 +1,27 @@ +#pragma once + +namespace DB +{ + +/** Отвечает за хранение локальных данных всех *MergeTree движков. + * - Поддерживает набор кусков на диске. Синхронизирует доступ к ним, поддерживает в памяти их список. + * - Полностью выполняет запросы SELECT. + * - Сам не принимает решений об изменении данных. + * - Умеет двавть рекомендации: + * - Говорить, какие куски нужно удалить, потому что они покрыты другими кусками. + * - Выбирать набор кусков для слияния. + * При этом нужна внешняя информация о том, какие куски с какими разрешено объединять. + * - Умеет изменять данные по запросу: + * - Записать новый кусок с данными из блока. + * - Слить указанные куски. + * - Сделать ALTER. + */ +class MergeTreeData +{ +public: + +private: + +}; + +} From 58ea3b108b1acd812ce1bccad3f06c7b4f0407b4 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sun, 9 Mar 2014 21:36:01 +0400 Subject: [PATCH 002/281] Merge --- .../include/DB/Storages/IColumnsDeclaration.h | 65 + dbms/include/DB/Storages/IStorage.h | 50 +- .../MergeTree/MergeTreeBlockInputStream.h | 12 +- .../DB/Storages/MergeTree/MergeTreeData.h | 527 ++++++ ...eam.h => MergeTreeDataBlockOutputStream.h} | 16 +- .../DB/Storages/MergeTree/MergeTreeReader.h | 6 +- .../MergeTree/MergedBlockOutputStream.h | 10 +- dbms/include/DB/Storages/MergeTreeData.h | 27 - dbms/include/DB/Storages/StorageMergeTree.h | 465 +----- .../{IStorage.cpp => IColumnsDeclaration.cpp} | 48 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 1468 +++++++++++++++++ dbms/src/Storages/StorageFactory.cpp | 4 +- dbms/src/Storages/StorageMergeTree.cpp | 1462 +--------------- dbms/src/Storages/tests/MergeLogicTester.cpp | 2 +- 14 files changed, 2154 insertions(+), 2008 deletions(-) create mode 100644 dbms/include/DB/Storages/IColumnsDeclaration.h create mode 100644 dbms/include/DB/Storages/MergeTree/MergeTreeData.h rename dbms/include/DB/Storages/MergeTree/{MergeTreeBlockOutputStream.h => MergeTreeDataBlockOutputStream.h} (96%) delete mode 100644 dbms/include/DB/Storages/MergeTreeData.h rename dbms/src/Storages/{IStorage.cpp => IColumnsDeclaration.cpp} (91%) create mode 100644 dbms/src/Storages/MergeTree/MergeTreeData.cpp diff --git a/dbms/include/DB/Storages/IColumnsDeclaration.h b/dbms/include/DB/Storages/IColumnsDeclaration.h new file mode 100644 index 00000000000..217e06e8827 --- /dev/null +++ b/dbms/include/DB/Storages/IColumnsDeclaration.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ + +class Context; + +/** Описание столбцов таблицы. + */ +class IColumnsDeclaration +{ +public: + /// Имя таблицы. Ни на что не влияет, используется только для сообщений об ошибках. + virtual std::string getTableName() const { return ""; } + + /** Получить список имён и типов столбцов таблицы, только невиртуальные. + */ + virtual const NamesAndTypesList & getColumnsList() const = 0; + + /** Получить описание реального (невиртуального) столбца по его имени. + */ + virtual NameAndTypePair getRealColumn(const String & column_name) const; + + /** Присутствует ли реальный (невиртуальный) столбец с таким именем. + */ + virtual bool hasRealColumn(const String & column_name) const; + + /** Получить описание любого столбца по его имени. + */ + virtual NameAndTypePair getColumn(const String & column_name) const; + + /** Присутствует ли столбец с таким именем. + */ + virtual bool hasColumn(const String & column_name) const; + + const DataTypePtr getDataTypeByName(const String & column_name) const; + + /** То же самое, но в виде блока-образца. + */ + Block getSampleBlock() const; + + /** Проверить, что все запрошенные имена есть в таблице и заданы корректно. + * (список имён не пустой и имена не повторяются) + */ + void check(const Names & column_names) const; + + /** Проверить, что блок с данными для записи содержит все столбцы таблицы с правильными типами, + * содержит только столбцы таблицы, и все столбцы различны. + * Если need_all, еще проверяет, что все столбцы таблицы есть в блоке. + */ + void check(const Block & block, bool need_all = false) const; + + /// реализация alter, модифицирующая список столбцов. + static void alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context); + + virtual ~IColumnsDeclaration() {} +}; + +} diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 321175335b5..2ce322f23d4 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -13,7 +13,8 @@ #include #include #include -#include "DatabaseDropper.h" +#include +#include #include @@ -30,41 +31,15 @@ class Context; * - структура хранения данных (сжатие, etc.) * - конкуррентный доступ к данным (блокировки, etc.) */ -class IStorage : private boost::noncopyable +class IStorage : private boost::noncopyable, public IColumnsDeclaration { public: - /// Основное имя типа таблицы (например, StorageWithoutKey). + /// Основное имя типа таблицы (например, StorageMergeTree). virtual std::string getName() const = 0; /// Имя самой таблицы (например, hits) virtual std::string getTableName() const = 0; - /** Получить список имён и типов столбцов таблицы, только невиртуальные. - */ - virtual const NamesAndTypesList & getColumnsList() const = 0; - - /** Получить описание реального (невиртуального) столбца по его имени. - */ - virtual NameAndTypePair getRealColumn(const String & column_name) const; - - /** Присутствует ли реальный (невиртуальный) столбец с таким именем. - */ - virtual bool hasRealColumn(const String & column_name) const; - - /** Получить описание любого столбца по его имени. - */ - virtual NameAndTypePair getColumn(const String & column_name) const; - - /** Присутствует ли столбец с таким именем. - */ - virtual bool hasColumn(const String & column_name) const; - - const DataTypePtr getDataTypeByName(const String & column_name) const; - - /** То же самое, но в виде блока-образца. - */ - Block getSampleBlock() const; - /** Возвращает true, если хранилище получает данные с удалённого сервера или серверов. */ virtual bool isRemote() const { return false; } @@ -172,19 +147,6 @@ public: */ virtual void shutdown() {} - virtual ~IStorage() {} - - /** Проверить, что все запрошенные имена есть в таблице и заданы корректно. - * (список имён не пустой и имена не повторяются) - */ - void check(const Names & column_names) const; - - /** Проверить, что блок с данными для записи содержит все столбцы таблицы с правильными типами, - * содержит только столбцы таблицы, и все столбцы различны. - * Если need_all, еще проверяет, что все столбцы таблицы есть в блоке. - */ - void check(const Block & block, bool need_all = false) const; - /** Возвращает владеющий указатель на себя. */ StoragePtr thisPtr() @@ -213,9 +175,7 @@ public: protected: IStorage() : drop_on_destroy(false) {} - - /// реализация alter, модифицирующая список столбцов. - void alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context) const; + private: boost::weak_ptr this_ptr; }; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h index 09931dba6e7..18cf15862dd 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include @@ -18,7 +18,7 @@ public: /// (например, поток, сливаящий куски). В таком случае сам storage должен следить, чтобы не удалить данные, пока их читают. MergeTreeBlockInputStream(const String & path_, /// Путь к куску size_t block_size_, const Names & column_names_, - StorageMergeTree & storage_, const StorageMergeTree::DataPartPtr & owned_data_part_, + MergeTreeData & storage_, const MergeTreeData::DataPartPtr & owned_data_part_, const MarkRanges & mark_ranges_, StoragePtr owned_storage, bool use_uncompressed_cache_, ExpressionActionsPtr prewhere_actions_, String prewhere_column_) : IProfilingBlockInputStream(owned_storage), @@ -74,8 +74,8 @@ public: /// Получает набор диапазонов засечек, вне которых не могут находиться ключи из заданного диапазона. static MarkRanges markRangesFromPkRange( - const StorageMergeTree::DataPart::Index & index, - StorageMergeTree & storage, + const MergeTreeData::DataPart::Index & index, + MergeTreeData & storage, PKCondition & key_condition) { MarkRanges res; @@ -321,8 +321,8 @@ private: Names column_names; NameSet column_name_set; Names pre_column_names; - StorageMergeTree & storage; - const StorageMergeTree::DataPartPtr owned_data_part; /// Кусок не будет удалён, пока им владеет этот объект. + MergeTreeData & storage; + const MergeTreeData::DataPartPtr owned_data_part; /// Кусок не будет удалён, пока им владеет этот объект. MarkRanges all_mark_ranges; /// В каких диапазонах засечек читать. В порядке возрастания номеров. MarkRanges remaining_mark_ranges; /// В каких диапазонах засечек еще не прочли. /// В порядке убывания номеров, чтобы можно было выбрасывать из конца. diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h new file mode 100644 index 00000000000..9c12ccd0419 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -0,0 +1,527 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace DB +{ + +/** Структура данных для *MergeTree движков. + * Используется merge tree для инкрементальной сортировки данных. + * Таблица представлена набором сортированных кусков. + * При вставке, данные сортируются по указанному выражению (первичному ключу) и пишутся в новый кусок. + * Куски объединяются в фоне, согласно некоторой эвристике. + * Для каждого куска, создаётся индексный файл, содержащий значение первичного ключа для каждой n-ой строки. + * Таким образом, реализуется эффективная выборка по диапазону первичного ключа. + * + * Дополнительно: + * + * Указывается столбец, содержащий дату. + * Для каждого куска пишется минимальная и максимальная дата. + * (по сути - ещё один индекс) + * + * Данные разделяются по разным месяцам (пишутся в разные куски для разных месяцев). + * Куски для разных месяцев не объединяются - для простоты эксплуатации. + * (дают локальность обновлений, что удобно для синхронизации и бэкапа) + * + * Структура файлов: + * / min-date _ max-date _ min-id _ max-id _ level / - директория с куском. + * Внутри директории с куском: + * primary.idx - индексный файл. + * Column.bin - данные столбца + * Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк. + * + * Имеется несколько режимов работы, определяющих, что делать при мердже: + * - Ordinary - ничего дополнительно не делать; + * - Collapsing - при склейке кусков "схлопывать" + * пары записей с разными значениями sign_column для одного значения первичного ключа. + * (см. CollapsingSortedBlockInputStream.h) + * - Summing - при склейке кусков, при совпадении PK суммировать все числовые столбцы, не входящие в PK. + */ + +/// NOTE: Следующее пока не правда. Сейчас тут практически весь StorageMergeTree. Лишние части нужно перенести отсюда в StorageMergeTree. + +/** Этот класс отвечает за хранение локальных данных всех *MergeTree движков. + * - Поддерживает набор кусков на диске. Синхронизирует доступ к ним, поддерживает в памяти их список. + * - Полностью выполняет запросы SELECT. + * - Сам не принимает решений об изменении данных. + * - Умеет дававть рекомендации: + * - Говорить, какие куски нужно удалить, потому что они покрыты другими кусками. + * - Выбирать набор кусков для слияния. + * При этом нужна внешняя информация о том, какие куски с какими разрешено объединять. + * - Умеет изменять данные по запросу: + * - Записать новый кусок с данными из блока. + * - Слить указанные куски. + * - Сделать ALTER. + */ + +struct MergeTreeSettings +{ + /// Набор кусков разрешено объединить, если среди них максимальный размер не более чем во столько раз больше суммы остальных. + double max_size_ratio_to_merge_parts = 5; + + /// Сколько за раз сливать кусков. + /// Трудоемкость выбора кусков O(N * max_parts_to_merge_at_once), так что не следует делать это число слишком большим. + /// С другой стороны, чтобы слияния точно не могли зайти в тупик, нужно хотя бы + /// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts). + size_t max_parts_to_merge_at_once = 10; + + /// Куски настолько большого размера в основном потоке объединять нельзя вообще. + size_t max_rows_to_merge_parts = 100 * 1024 * 1024; + + /// Куски настолько большого размера во втором потоке объединять нельзя вообще. + size_t max_rows_to_merge_parts_second = 1024 * 1024; + + /// Во столько раз ночью увеличиваем коэффициент. + size_t merge_parts_at_night_inc = 10; + + /// Сколько потоков использовать для объединения кусков. + size_t merging_threads = 2; + + /// Если из одного файла читается хотя бы столько строк, чтение можно распараллелить. + size_t min_rows_for_concurrent_read = 20 * 8192; + + /// Можно пропускать чтение более чем стольки строк ценой одного seek по файлу. + size_t min_rows_for_seek = 5 * 8192; + + /// Если отрезок индекса может содержать нужные ключи, делим его на столько частей и рекурсивно проверяем их. + size_t coarse_index_granularity = 8; + + /** Максимальное количество строк на запрос, для использования кэша разжатых данных. Если запрос большой - кэш не используется. + * (Чтобы большие запросы не вымывали кэш.) + */ + size_t max_rows_to_use_cache = 1024 * 1024; + + /// Через сколько секунд удалять old_куски. + time_t old_parts_lifetime = 5 * 60; +}; + +/// Пара засечек, определяющая диапазон строк в куске. Именно, диапазон имеет вид [begin * index_granularity, end * index_granularity). +struct MarkRange +{ + size_t begin; + size_t end; + + MarkRange() {} + MarkRange(size_t begin_, size_t end_) : begin(begin_), end(end_) {} +}; + +typedef std::vector MarkRanges; + + +class MergeTreeData : public IColumnsDeclaration +{ +friend class MergeTreeReader; +friend class MergeTreeBlockInputStream; +friend class MergeTreeDataBlockOutputStream; +friend class IMergedBlockOutputStream; +friend class MergedBlockOutputStream; +friend class MergedColumnOnlyOutputStream; + +public: + /// Режим работы. См. выше. + enum Mode + { + Ordinary, + Collapsing, + Summing, + }; + + /** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце), + * (корректность имён и путей не проверяется) + * состоящую из указанных столбцов. + * + * primary_expr_ast - выражение для сортировки; + * date_column_name - имя столбца с датой; + * index_granularity - на сколько строчек пишется одно значение индекса. + */ + MergeTreeData( StoragePtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + size_t index_granularity_, + Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_); + + void shutdown(); + ~MergeTreeData(); + + std::string getModePrefix() const + { + switch (mode) + { + case Ordinary: return ""; + case Collapsing: return "Collapsing"; + case Summing: return "Summing"; + + default: + throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR); + } + } + + std::string getTableName() const { return name; } + std::string getSignColumnName() const { return sign_column; } + bool supportsSampling() const { return !!sampling_expression; } + bool supportsFinal() const { return !sign_column.empty(); } + bool supportsPrewhere() const { return true; } + + const NamesAndTypesList & getColumnsList() const { return *columns; } + + /** При чтении, выбирается набор кусков, покрывающий нужный диапазон индекса. + */ + BlockInputStreams read( + const Names & column_names, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size = DEFAULT_BLOCK_SIZE, + unsigned threads = 1); + + /** При записи, данные сортируются и пишутся в новые куски. + */ + BlockOutputStreamPtr write(ASTPtr query); + + /** Выполнить очередной шаг объединения кусков. + */ + bool optimize() + { + merge(1, false, true); + return true; + } + + void dropImpl(); + + void rename(const String & new_path_to_db, const String & new_name); + + /// Метод ALTER позволяет добавлять и удалять столбцы. + /// Метод ALTER нужно применять, когда обращения к базе приостановлены. + /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение + void alter(const ASTAlterQuery::Parameters & params); + + class BigLock + { + public: + BigLock(MergeTreeData & storage) : merge_lock(storage.merge_lock), + write_lock(storage.write_lock), read_lock(storage.read_lock) + { + } + + private: + Poco::ScopedWriteRWLock merge_lock; + Poco::ScopedWriteRWLock write_lock; + Poco::ScopedWriteRWLock read_lock; + }; + + typedef Poco::SharedPtr BigLockPtr; + BigLockPtr lockAllOperations() + { + return new BigLock(*this); + } + +private: + StoragePtr owning_storage; + + String path; + String name; + String full_path; + NamesAndTypesListPtr columns; + + const Context & context; + ASTPtr primary_expr_ast; + String date_column_name; + ASTPtr sampling_expression; + size_t index_granularity; + + size_t min_marks_for_seek; + size_t min_marks_for_concurrent_read; + size_t max_marks_to_use_cache; + + /// Режим работы - какие дополнительные действия делать при мердже. + Mode mode; + /// Для схлопывания записей об изменениях, если используется Collapsing режим работы. + String sign_column; + + MergeTreeSettings settings; + + ExpressionActionsPtr primary_expr; + SortDescription sort_descr; + Block primary_key_sample; + + Increment increment; + + Logger * log; + volatile bool shutdown_called; + + /// Регулярное выражение соответсвующее названию директории с кусочками + Poco::RegularExpression file_name_regexp; + + /// Описание куска с данными. + struct DataPart + { + DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0), currently_merging(false) {} + + MergeTreeData & storage; + DayNum_t left_date; + DayNum_t right_date; + UInt64 left; + UInt64 right; + /// Уровень игнорируется. Использовался предыдущей эвристикой слияния. + UInt32 level; + + std::string name; + size_t size; /// в количестве засечек. + size_t size_in_bytes; /// размер в байтах, 0 - если не посчитано + time_t modification_time; + + DayNum_t left_month; + DayNum_t right_month; + + /// Смотреть и изменять это поле следует под залоченным data_parts_mutex. + bool currently_merging; + + /// Первичный ключ. Всегда загружается в оперативку. + typedef std::vector Index; + Index index; + + /// NOTE можно загружать засечки тоже в оперативку + + /// Вычисляем сумарный размер всей директории со всеми файлами + static size_t calcTotalSize(const String &from) + { + Poco::File cur(from); + if (cur.isFile()) + return cur.getSize(); + std::vector files; + cur.list(files); + size_t res = 0; + for (size_t i = 0; i < files.size(); ++i) + res += calcTotalSize(from + files[i]); + return res; + } + + void remove() const + { + String from = storage.full_path + name + "/"; + String to = storage.full_path + "tmp2_" + name + "/"; + + Poco::File(from).renameTo(to); + Poco::File(to).remove(true); + } + + void renameToOld() const + { + String from = storage.full_path + name + "/"; + String to = storage.full_path + "old_" + name + "/"; + + Poco::File f(from); + f.setLastModified(Poco::Timestamp::fromEpochTime(time(0))); + f.renameTo(to); + } + + bool operator< (const DataPart & rhs) const + { + if (left_month < rhs.left_month) + return true; + if (left_month > rhs.left_month) + return false; + if (right_month < rhs.right_month) + return true; + if (right_month > rhs.right_month) + return false; + + if (left < rhs.left) + return true; + if (left > rhs.left) + return false; + if (right < rhs.right) + return true; + if (right > rhs.right) + return false; + + if (level < rhs.level) + return true; + + return false; + } + + /// Содержит другой кусок (получен после объединения другого куска с каким-то ещё) + bool contains(const DataPart & rhs) const + { + return left_month == rhs.left_month /// Куски за разные месяцы не объединяются + && right_month == rhs.right_month + && level > rhs.level + && left_date <= rhs.left_date + && right_date >= rhs.right_date + && left <= rhs.left + && right >= rhs.right; + } + + /// Загрузить индекс и вычислить размер. + void loadIndex() + { + size_t key_size = storage.sort_descr.size(); + index.resize(key_size * size); + + String index_path = storage.full_path + name + "/primary.idx"; + ReadBufferFromFile index_file(index_path, std::min(static_cast(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize())); + + for (size_t i = 0; i < size; ++i) + for (size_t j = 0; j < key_size; ++j) + storage.primary_key_sample.getByPosition(j).type->deserializeBinary(index[i * key_size + j], index_file); + + if (!index_file.eof()) + throw Exception("index file " + index_path + " is unexpectedly long", ErrorCodes::EXPECTED_END_OF_FILE); + + size_in_bytes = calcTotalSize(storage.full_path + name + "/"); + } + }; + + typedef SharedPtr DataPartPtr; + struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } }; + typedef std::set DataParts; + + struct RangesInDataPart + { + DataPartPtr data_part; + MarkRanges ranges; + + RangesInDataPart() {} + + RangesInDataPart(DataPartPtr data_part_) + : data_part(data_part_) + { + } + }; + + /// Пока существует, помечает части как currently_merging и пересчитывает общий объем сливаемых данных. + /// Вероятно, что части будут помечены заранее. + class CurrentlyMergingPartsTagger + { + public: + std::vector parts; + Poco::FastMutex & data_mutex; + + CurrentlyMergingPartsTagger(const std::vector & parts_, Poco::FastMutex & data_mutex_) : parts(parts_), data_mutex(data_mutex_) + { + /// Здесь не лочится мьютекс, так как конструктор вызывается внутри selectPartsToMerge, где он уже залочен + /// Poco::ScopedLock lock(data_mutex); + for (size_t i = 0; i < parts.size(); ++i) + { + parts[i]->currently_merging = true; + MergeTreeData::total_size_of_currently_merging_parts += parts[i]->size_in_bytes; + } + } + + ~CurrentlyMergingPartsTagger() + { + Poco::ScopedLock lock(data_mutex); + for (size_t i = 0; i < parts.size(); ++i) + { + parts[i]->currently_merging = false; + MergeTreeData::total_size_of_currently_merging_parts -= parts[i]->size_in_bytes; + } + } + }; + + /// Сумарный размер currently_merging кусочков в байтах. + /// Нужно чтобы оценить количество места на диске, которое может понадобится для завершения этих мерджей. + static size_t total_size_of_currently_merging_parts; + + typedef std::vector RangesInDataParts; + + /** @warning Если берете насколько блокировок, то берите их везде в одинаковом порядке - в том же как они написаны в этом файле */ + /** Взятие этого лока на запись, запрещает мердж */ + Poco::RWLock merge_lock; + + /** Взятие этого лока на запись, запрещает запись */ + Poco::RWLock write_lock; + + /** Взятие этого лока на запись, запрещает чтение */ + Poco::RWLock read_lock; + + /** Актуальное множество кусков с данными. */ + DataParts data_parts; + Poco::FastMutex data_parts_mutex; + + /** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов). + * Ссылки на кусок есть отсюда, из списка актуальных кусков, и из каждого потока чтения, который его сейчас использует. + * То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить. + */ + DataParts all_data_parts; + Poco::FastMutex all_data_parts_mutex; + + static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); + + BlockInputStreams spreadMarkRangesAmongThreads( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column); + + BlockInputStreams spreadMarkRangesAmongThreadsFinal( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column); + + /// Создать выражение "Sign == 1". + void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column); + + /// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта. + void loadDataParts(); + + /// Удалить неактуальные куски. + void clearOldParts(); + + /** Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations = 0, объединяет, пока это возможно. + * Если aggressive - выбрать куски не обращая внимание на соотношение размеров и их новизну (для запроса OPTIMIZE). + */ + void merge(size_t iterations = 1, bool async = true, bool aggressive = false); + + /// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков. + void mergeThread(bool while_can, bool aggressive); + + /// Сразу помечает их как currently_merging. + /// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. + bool selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive); + + void mergeParts(Poco::SharedPtr & what); + + /// Дождаться, пока фоновые потоки закончат слияния. + void joinMergeThreads(); + + Poco::SharedPtr merge_threads; + + void removeColumnFiles(String column_name); + + /// Возвращает true если имя директории совпадает с форматом имени директории кусочков + bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const; + + /// Кладет в DataPart данные из имени кусочка. + void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); + + /// Определить, не битые ли данные в директории. Проверяет индекс и засечеки, но не сами данные. + bool isBrokenPart(const String & path); + + /// Найти самые большие old_куски, из которых получен этот кусок. + /// Переименовать их, убрав префикс old_ и вернуть их имена. + Strings tryRestorePart(const String & path, const String & file_name, Strings & old_parts); + + void createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column); +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h similarity index 96% rename from dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h rename to dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h index f74e82860c0..8cdad62fa9d 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h @@ -7,16 +7,16 @@ #include -#include +#include namespace DB { - -class MergeTreeBlockOutputStream : public IBlockOutputStream + +class MergeTreeDataBlockOutputStream : public IBlockOutputStream { public: - MergeTreeBlockOutputStream(StoragePtr owned_storage) : IBlockOutputStream(owned_storage), storage(dynamic_cast(*owned_storage)), flags(O_TRUNC | O_CREAT | O_WRONLY) + MergeTreeDataBlockOutputStream(MergeTreeData & data, StoragePtr owned_storage) : IBlockOutputStream(owned_storage), storage(data), flags(O_TRUNC | O_CREAT | O_WRONLY) { } @@ -25,7 +25,7 @@ public: Poco::ScopedReadRWLock write_lock(storage.write_lock); storage.check(block, true); - + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); size_t rows = block.rows(); @@ -82,7 +82,7 @@ public: } private: - StorageMergeTree & storage; + MergeTreeData & storage; const int flags; @@ -130,7 +130,7 @@ private: LOG_TRACE(storage.log, "Writing index."); /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. - StorageMergeTree::DataPart::Index index_vec; + MergeTreeData::DataPart::Index index_vec; index_vec.reserve(part_size * storage.sort_descr.size()); { @@ -183,7 +183,7 @@ private: String part_name = storage.getPartName(DayNum_t(min_date), DayNum_t(max_date), part_id, part_id, 0); String part_res_path = storage.full_path + part_name + "/"; - StorageMergeTree::DataPartPtr new_data_part = new StorageMergeTree::DataPart(storage); + MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(storage); new_data_part->left_date = DayNum_t(min_date); new_data_part->right_date = DayNum_t(max_date); new_data_part->left = part_id; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index 8c3833a6eaa..01342700ccc 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -22,7 +22,7 @@ class MergeTreeReader { public: MergeTreeReader(const String & path_, /// Путь к куску - const Names & columns_names_, bool use_uncompressed_cache_, StorageMergeTree & storage_) + const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_) : path(path_), column_names(columns_names_), use_uncompressed_cache(use_uncompressed_cache_), storage(storage_) { for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) @@ -220,7 +220,7 @@ private: FileStreams streams; Names column_names; bool use_uncompressed_cache; - StorageMergeTree & storage; + MergeTreeData & storage; void addStream(const String & name, const IDataType & type, size_t level = 0) { diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index a97e6eac4c8..e91a0d23883 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace DB @@ -11,7 +11,7 @@ namespace DB class IMergedBlockOutputStream : public IBlockOutputStream { public: - IMergedBlockOutputStream(StorageMergeTree & storage_) : storage(storage_), index_offset(0) + IMergedBlockOutputStream(MergeTreeData & storage_) : storage(storage_), index_offset(0) { } @@ -182,7 +182,7 @@ protected: } } - StorageMergeTree & storage; + MergeTreeData & storage; ColumnStreams column_streams; @@ -196,7 +196,7 @@ protected: class MergedBlockOutputStream : public IMergedBlockOutputStream { public: - MergedBlockOutputStream(StorageMergeTree & storage_, + MergedBlockOutputStream(MergeTreeData & storage_, UInt16 min_date, UInt16 max_date, UInt64 min_part_id, UInt64 max_part_id, UInt32 level) : IMergedBlockOutputStream(storage_), marks_count(0) { @@ -299,7 +299,7 @@ typedef Poco::SharedPtr MergedBlockOutputStreamPtr; class MergedColumnOnlyOutputStream : public IMergedBlockOutputStream { public: - MergedColumnOnlyOutputStream(StorageMergeTree & storage_, String part_path_, bool sync_ = false) : + MergedColumnOnlyOutputStream(MergeTreeData & storage_, String part_path_, bool sync_ = false) : IMergedBlockOutputStream(storage_), part_path(part_path_), initialized(false), sync(sync_) { } diff --git a/dbms/include/DB/Storages/MergeTreeData.h b/dbms/include/DB/Storages/MergeTreeData.h deleted file mode 100644 index ec3f68d7354..00000000000 --- a/dbms/include/DB/Storages/MergeTreeData.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -namespace DB -{ - -/** Отвечает за хранение локальных данных всех *MergeTree движков. - * - Поддерживает набор кусков на диске. Синхронизирует доступ к ним, поддерживает в памяти их список. - * - Полностью выполняет запросы SELECT. - * - Сам не принимает решений об изменении данных. - * - Умеет двавть рекомендации: - * - Говорить, какие куски нужно удалить, потому что они покрыты другими кусками. - * - Выбирать набор кусков для слияния. - * При этом нужна внешняя информация о том, какие куски с какими разрешено объединять. - * - Умеет изменять данные по запросу: - * - Записать новый кусок с данными из блока. - * - Слить указанные куски. - * - Сделать ALTER. - */ -class MergeTreeData -{ -public: - -private: - -}; - -} diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 3716f8a5538..366716bfd1c 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -1,123 +1,15 @@ #pragma once -#include -#include - -#include -#include -#include -#include -#include - +#include namespace DB { -/** Движок, использующий merge tree для инкрементальной сортировки данных. - * Таблица представлена набором сортированных кусков. - * При вставке, данные сортируются по указанному выражению (первичному ключу) и пишутся в новый кусок. - * Куски объединяются в фоне, согласно некоторой эвристике. - * Для каждого куска, создаётся индексный файл, содержащий значение первичного ключа для каждой n-ой строки. - * Таким образом, реализуется эффективная выборка по диапазону первичного ключа. - * - * Дополнительно: - * - * Указывается столбец, содержащий дату. - * Для каждого куска пишется минимальная и максимальная дата. - * (по сути - ещё один индекс) - * - * Данные разделяются по разным месяцам (пишутся в разные куски для разных месяцев). - * Куски для разных месяцев не объединяются - для простоты эксплуатации. - * (дают локальность обновлений, что удобно для синхронизации и бэкапа) - * - * Структура файлов: - * / increment.txt - файл, содержащий одно число, увеличивающееся на 1 - для генерации идентификаторов кусков. - * / min-date _ max-date _ min-id _ max-id _ level / - директория с куском. - * / min-date _ max-date _ min-id _ max-id _ level / primary.idx - индексный файл. - * Внутри директории с куском: - * Column.bin - данные столбца - * Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк. - * - * Имеется несколько режимов работы, определяющих, что делать при мердже: - * - Ordinary - ничего дополнительно не делать; - * - Collapsing - при склейке кусков "схлопывать" - * пары записей с разными значениями sign_column для одного значения первичного ключа. - * (см. CollapsingSortedBlockInputStream.h) - * - Summing - при склейке кусков, при совпадении PK суммировать все числовые столбцы, не входящие в PK. +/** См. описание структуры данных в MergeTreeData. */ - -struct StorageMergeTreeSettings -{ - /// Набор кусков разрешено объединить, если среди них максимальный размер не более чем во столько раз больше суммы остальных. - double max_size_ratio_to_merge_parts = 5; - - /// Сколько за раз сливать кусков. - /// Трудоемкость выбора кусков O(N * max_parts_to_merge_at_once), так что не следует делать это число слишком большим. - /// С другой стороны, чтобы слияния точно не могли зайти в тупик, нужно хотя бы - /// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts). - size_t max_parts_to_merge_at_once = 10; - - /// Куски настолько большого размера в основном потоке объединять нельзя вообще. - size_t max_rows_to_merge_parts = 100 * 1024 * 1024; - - /// Куски настолько большого размера во втором потоке объединять нельзя вообще. - size_t max_rows_to_merge_parts_second = 1024 * 1024; - - /// Во столько раз ночью увеличиваем коэффициент. - size_t merge_parts_at_night_inc = 10; - - /// Сколько потоков использовать для объединения кусков. - size_t merging_threads = 2; - - /// Если из одного файла читается хотя бы столько строк, чтение можно распараллелить. - size_t min_rows_for_concurrent_read = 20 * 8192; - - /// Можно пропускать чтение более чем стольки строк ценой одного seek по файлу. - size_t min_rows_for_seek = 5 * 8192; - - /// Если отрезок индекса может содержать нужные ключи, делим его на столько частей и рекурсивно проверяем их. - size_t coarse_index_granularity = 8; - - /** Максимальное количество строк на запрос, для использования кэша разжатых данных. Если запрос большой - кэш не используется. - * (Чтобы большие запросы не вымывали кэш.) - */ - size_t max_rows_to_use_cache = 1024 * 1024; - - /// Через сколько секунд удалять old_куски. - time_t old_parts_lifetime = 5 * 60; -}; - -/// Пара засечек, определяющая диапазон строк в куске. Именно, диапазон имеет вид [begin * index_granularity, end * index_granularity). -struct MarkRange -{ - size_t begin; - size_t end; - - MarkRange() {} - MarkRange(size_t begin_, size_t end_) : begin(begin_), end(end_) {} -}; - -typedef std::vector MarkRanges; - - class StorageMergeTree : public IStorage { -friend class MergeTreeReader; -friend class MergeTreeBlockInputStream; -friend class MergeTreeBlockOutputStream; -friend class IMergedBlockOutputStream; -friend class MergedBlockOutputStream; -friend class MergedColumnOnlyOutputStream; - public: - /// Режим работы. См. выше. - enum Mode - { - Ordinary, - Collapsing, - Summing, - }; - /** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце), * (корректность имён и путей не проверяется) * состоящую из указанных столбцов. @@ -132,36 +24,26 @@ public: const String & date_column_name_, const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. size_t index_granularity_, - Mode mode_ = Ordinary, + MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, const String & sign_column_ = "", - const StorageMergeTreeSettings & settings_ = StorageMergeTreeSettings()); + const MergeTreeSettings & settings_ = MergeTreeSettings()); void shutdown(); ~StorageMergeTree(); std::string getName() const { - switch (mode) - { - case Ordinary: return "MergeTree"; - case Collapsing: return "CollapsingMergeTree"; - case Summing: return "SummingMergeTree"; - - default: - throw Exception("Unknown mode of operation for StorageMergeTree: " + toString(mode), ErrorCodes::LOGICAL_ERROR); - } + return data.getModePrefix() + "MergeTree"; } - std::string getTableName() const { return name; } - std::string getSignColumnName() const { return sign_column; } - bool supportsSampling() const { return !!sampling_expression; } - bool supportsFinal() const { return !sign_column.empty(); } - bool supportsPrewhere() const { return true; } + std::string getTableName() const { return data.getTableName(); } + std::string getSignColumnName() const { return data.getSignColumnName(); } + bool supportsSampling() const { return data.supportsSampling(); } + bool supportsFinal() const { return data.supportsFinal(); } + bool supportsPrewhere() const { return data.supportsPrewhere(); } - const NamesAndTypesList & getColumnsList() const { return *columns; } + const NamesAndTypesList & getColumnsList() const { return data.getColumnsList(); } - /** При чтении, выбирается набор кусков, покрывающий нужный диапазон индекса. - */ BlockInputStreams read( const Names & column_names, ASTPtr query, @@ -170,20 +52,17 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1); - /** При записи, данные сортируются и пишутся в новые куски. - */ BlockOutputStreamPtr write(ASTPtr query); /** Выполнить очередной шаг объединения кусков. */ bool optimize() { - merge(1, false, true); - return true; + return data.optimize(); } void dropImpl(); - + void rename(const String & new_path_to_db, const String & new_name); /// Метод ALTER позволяет добавлять и удалять столбцы. @@ -191,255 +70,12 @@ public: /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение void alter(const ASTAlterQuery::Parameters & params); - class BigLock - { - public: - BigLock(StorageMergeTree & storage) : merge_lock(storage.merge_lock), - write_lock(storage.write_lock), read_lock(storage.read_lock) - { - } + typedef MergeTreeData::BigLockPtr BigLockPtr; - private: - Poco::ScopedWriteRWLock merge_lock; - Poco::ScopedWriteRWLock write_lock; - Poco::ScopedWriteRWLock read_lock; - }; - - typedef Poco::SharedPtr BigLockPtr; - BigLockPtr lockAllOperations() - { - return new BigLock(*this); - } + BigLockPtr lockAllOperations() { return data.lockAllOperations(); } private: - String path; - String name; - String full_path; - NamesAndTypesListPtr columns; - - const Context & context; - ASTPtr primary_expr_ast; - String date_column_name; - ASTPtr sampling_expression; - size_t index_granularity; - - size_t min_marks_for_seek; - size_t min_marks_for_concurrent_read; - size_t max_marks_to_use_cache; - - /// Режим работы - какие дополнительные действия делать при мердже. - Mode mode; - /// Для схлопывания записей об изменениях, если используется Collapsing режим работы. - String sign_column; - - StorageMergeTreeSettings settings; - - ExpressionActionsPtr primary_expr; - SortDescription sort_descr; - Block primary_key_sample; - - Increment increment; - - Logger * log; - volatile bool shutdown_called; - - /// Регулярное выражение соответсвующее названию директории с кусочками - Poco::RegularExpression file_name_regexp; - - /// Описание куска с данными. - struct DataPart - { - DataPart(StorageMergeTree & storage_) : storage(storage_), size_in_bytes(0), currently_merging(false) {} - - StorageMergeTree & storage; - DayNum_t left_date; - DayNum_t right_date; - UInt64 left; - UInt64 right; - /// Уровень игнорируется. Использовался предыдущей эвристикой слияния. - UInt32 level; - - std::string name; - size_t size; /// в количестве засечек. - size_t size_in_bytes; /// размер в байтах, 0 - если не посчитано - time_t modification_time; - - DayNum_t left_month; - DayNum_t right_month; - - /// Смотреть и изменять это поле следует под залоченным data_parts_mutex. - bool currently_merging; - - /// Первичный ключ. Всегда загружается в оперативку. - typedef std::vector Index; - Index index; - - /// NOTE можно загружать засечки тоже в оперативку - - /// Вычисляем сумарный размер всей директории со всеми файлами - static size_t calcTotalSize(const String &from) - { - Poco::File cur(from); - if (cur.isFile()) - return cur.getSize(); - std::vector files; - cur.list(files); - size_t res = 0; - for (size_t i = 0; i < files.size(); ++i) - res += calcTotalSize(from + files[i]); - return res; - } - - void remove() const - { - String from = storage.full_path + name + "/"; - String to = storage.full_path + "tmp2_" + name + "/"; - - Poco::File(from).renameTo(to); - Poco::File(to).remove(true); - } - - void renameToOld() const - { - String from = storage.full_path + name + "/"; - String to = storage.full_path + "old_" + name + "/"; - - Poco::File f(from); - f.setLastModified(Poco::Timestamp::fromEpochTime(time(0))); - f.renameTo(to); - } - - bool operator< (const DataPart & rhs) const - { - if (left_month < rhs.left_month) - return true; - if (left_month > rhs.left_month) - return false; - if (right_month < rhs.right_month) - return true; - if (right_month > rhs.right_month) - return false; - - if (left < rhs.left) - return true; - if (left > rhs.left) - return false; - if (right < rhs.right) - return true; - if (right > rhs.right) - return false; - - if (level < rhs.level) - return true; - - return false; - } - - /// Содержит другой кусок (получен после объединения другого куска с каким-то ещё) - bool contains(const DataPart & rhs) const - { - return left_month == rhs.left_month /// Куски за разные месяцы не объединяются - && right_month == rhs.right_month - && level > rhs.level - && left_date <= rhs.left_date - && right_date >= rhs.right_date - && left <= rhs.left - && right >= rhs.right; - } - - /// Загрузить индекс и вычислить размер. - void loadIndex() - { - size_t key_size = storage.sort_descr.size(); - index.resize(key_size * size); - - String index_path = storage.full_path + name + "/primary.idx"; - ReadBufferFromFile index_file(index_path, std::min(static_cast(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize())); - - for (size_t i = 0; i < size; ++i) - for (size_t j = 0; j < key_size; ++j) - storage.primary_key_sample.getByPosition(j).type->deserializeBinary(index[i * key_size + j], index_file); - - if (!index_file.eof()) - throw Exception("index file " + index_path + " is unexpectedly long", ErrorCodes::EXPECTED_END_OF_FILE); - - size_in_bytes = calcTotalSize(storage.full_path + name + "/"); - } - }; - - typedef SharedPtr DataPartPtr; - struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } }; - typedef std::set DataParts; - - struct RangesInDataPart - { - DataPartPtr data_part; - MarkRanges ranges; - - RangesInDataPart() {} - - RangesInDataPart(DataPartPtr data_part_) - : data_part(data_part_) - { - } - }; - - /// Пока существует, помечает части как currently_merging и пересчитывает общий объем сливаемых данных. - /// Вероятно, что части будут помечены заранее. - class CurrentlyMergingPartsTagger - { - public: - std::vector parts; - Poco::FastMutex & data_mutex; - - CurrentlyMergingPartsTagger(const std::vector & parts_, Poco::FastMutex & data_mutex_) : parts(parts_), data_mutex(data_mutex_) - { - /// Здесь не лочится мьютекс, так как конструктор вызывается внутри selectPartsToMerge, где он уже залочен - /// Poco::ScopedLock lock(data_mutex); - for (size_t i = 0; i < parts.size(); ++i) - { - parts[i]->currently_merging = true; - StorageMergeTree::total_size_of_currently_merging_parts += parts[i]->size_in_bytes; - } - } - - ~CurrentlyMergingPartsTagger() - { - Poco::ScopedLock lock(data_mutex); - for (size_t i = 0; i < parts.size(); ++i) - { - parts[i]->currently_merging = false; - StorageMergeTree::total_size_of_currently_merging_parts -= parts[i]->size_in_bytes; - } - } - }; - - /// Сумарный размер currently_merging кусочков в байтах. - /// Нужно чтобы оценить количество места на диске, которое может понадобится для завершения этих мерджей. - static size_t total_size_of_currently_merging_parts; - - typedef std::vector RangesInDataParts; - - /** @warning Если берете насколько блокировок, то берите их везде в одинаковом порядке - в том же как они написаны в этом файле */ - /** Взятие этого лока на запись, запрещает мердж */ - Poco::RWLock merge_lock; - - /** Взятие этого лока на запись, запрещает запись */ - Poco::RWLock write_lock; - - /** Взятие этого лока на запись, запрещает чтение */ - Poco::RWLock read_lock; - - /** Актуальное множество кусков с данными. */ - DataParts data_parts; - Poco::FastMutex data_parts_mutex; - - /** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов). - * Ссылки на кусок есть отсюда, из списка актуальных кусков, и из каждого потока чтения, который его сейчас использует. - * То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить. - */ - DataParts all_data_parts; - Poco::FastMutex all_data_parts_mutex; + MergeTreeData data; StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, @@ -447,74 +83,9 @@ private: const String & date_column_name_, const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. size_t index_granularity_, - Mode mode_ = Ordinary, + MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, const String & sign_column_ = "", - const StorageMergeTreeSettings & settings_ = StorageMergeTreeSettings()); - - static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); - - BlockInputStreams spreadMarkRangesAmongThreads( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column); - - BlockInputStreams spreadMarkRangesAmongThreadsFinal( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column); - - /// Создать выражение "Sign == 1". - void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column); - - /// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта. - void loadDataParts(); - - /// Удалить неактуальные куски. - void clearOldParts(); - - /** Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations = 0, объединяет, пока это возможно. - * Если aggressive - выбрать куски не обращая внимание на соотношение размеров и их новизну (для запроса OPTIMIZE). - */ - void merge(size_t iterations = 1, bool async = true, bool aggressive = false); - - /// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков. - void mergeThread(bool while_can, bool aggressive); - - /// Сразу помечает их как currently_merging. - /// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. - bool selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive); - - void mergeParts(Poco::SharedPtr & what); - - /// Дождаться, пока фоновые потоки закончат слияния. - void joinMergeThreads(); - - Poco::SharedPtr merge_threads; - - void removeColumnFiles(String column_name); - - /// Возвращает true если имя директории совпадает с форматом имени директории кусочков - bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const; - - /// Кладет в DataPart данные из имени кусочка. - void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); - - /// Определить, не битые ли данные в директории. Проверяет индекс и засечеки, но не сами данные. - bool isBrokenPart(const String & path); - - /// Найти самые большие old_куски, из которых получен этот кусок. - /// Переименовать их, убрав префикс old_ и вернуть их имена. - Strings tryRestorePart(const String & path, const String & file_name, Strings & old_parts); - - void createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column); + const MergeTreeSettings & settings_ = MergeTreeSettings()); }; } diff --git a/dbms/src/Storages/IStorage.cpp b/dbms/src/Storages/IColumnsDeclaration.cpp similarity index 91% rename from dbms/src/Storages/IStorage.cpp rename to dbms/src/Storages/IColumnsDeclaration.cpp index 9e98fb5f476..b6b21b904ba 100644 --- a/dbms/src/Storages/IStorage.cpp +++ b/dbms/src/Storages/IColumnsDeclaration.cpp @@ -1,21 +1,16 @@ -#include -#include - -#include #include - -#include +#include +#include #include -#include #include #include #include - +#include namespace DB { -bool IStorage::hasRealColumn(const String &column_name) const +bool IColumnsDeclaration::hasRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) @@ -25,7 +20,7 @@ bool IStorage::hasRealColumn(const String &column_name) const } -NameAndTypePair IStorage::getRealColumn(const String &column_name) const +NameAndTypePair IColumnsDeclaration::getRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) @@ -35,30 +30,30 @@ NameAndTypePair IStorage::getRealColumn(const String &column_name) const } -bool IStorage::hasColumn(const String &column_name) const +bool IColumnsDeclaration::hasColumn(const String &column_name) const { return hasRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет. } -NameAndTypePair IStorage::getColumn(const String &column_name) const +NameAndTypePair IColumnsDeclaration::getColumn(const String &column_name) const { return getRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет. } -const DataTypePtr IStorage::getDataTypeByName(const String & column_name) const +const DataTypePtr IColumnsDeclaration::getDataTypeByName(const String & column_name) const { const NamesAndTypesList & names_and_types = getColumnsList(); for (NamesAndTypesList::const_iterator it = names_and_types.begin(); it != names_and_types.end(); ++it) if (it->first == column_name) return it->second; - + throw Exception("There is no column " + column_name + " in table " + getTableName(), ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); } -Block IStorage::getSampleBlock() const +Block IColumnsDeclaration::getSampleBlock() const { Block res; const NamesAndTypesList & names_and_types = getColumnsList(); @@ -71,7 +66,7 @@ Block IStorage::getSampleBlock() const col.column = col.type->createColumn(); res.insert(col); } - + return res; } @@ -104,10 +99,10 @@ static NamesAndTypesMap getColumnsMap(const NamesAndTypesList & available_column } -void IStorage::check(const Names & column_names) const +void IColumnsDeclaration::check(const Names & column_names) const { const NamesAndTypesList & available_columns = getColumnsList(); - + if (column_names.empty()) throw Exception("Empty list of columns queried for table " + getTableName() + ". There are columns: " + listOfColumns(available_columns), @@ -134,14 +129,14 @@ void IStorage::check(const Names & column_names) const } -void IStorage::check(const Block & block, bool need_all) const +void IColumnsDeclaration::check(const Block & block, bool need_all) const { const NamesAndTypesList & available_columns = getColumnsList(); const NamesAndTypesMap & columns_map = getColumnsMap(available_columns); - + typedef std::unordered_set NameSet; NameSet names_in_block; - + for (size_t i = 0; i < block.columns(); ++i) { const ColumnWithNameAndType & column = block.getByPosition(i); @@ -163,7 +158,7 @@ void IStorage::check(const Block & block, bool need_all) const + ". 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 (NamesAndTypesList::const_iterator it = available_columns.begin(); it != available_columns.end(); ++it) @@ -174,7 +169,6 @@ void IStorage::check(const Block & block, bool need_all) const } } - /// одинаковыми считаются имена, если они совпадают целиком или name_without_dot совпадает с частью имени до точки static bool namesEqual(const String & name_without_dot, const DB::NameAndTypePair & name_type) { @@ -182,7 +176,7 @@ static bool namesEqual(const String & name_without_dot, const DB::NameAndTypePai return (name_with_dot == name_type.first.substr(0, name_without_dot.length() + 1) || name_without_dot == name_type.first); } -void IStorage::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context) const +void IColumnsDeclaration::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context) { if (params.type == ASTAlterQuery::ADD) { @@ -219,14 +213,14 @@ void IStorage::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTy else if (params.type == ASTAlterQuery::DROP) { String column_name = dynamic_cast(*(params.column)).name; - + /// Удаляем колонки из листа columns bool is_first = true; NamesAndTypesList::iterator column_it; do { column_it = std::find_if(columns->begin(), columns->end(), boost::bind(namesEqual, column_name, _1)); - + if (column_it == columns->end()) { if (is_first) @@ -252,7 +246,7 @@ void IStorage::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTy column_it->second = data_type; } else - throw Exception("Wrong parameter type in ALTER query", ErrorCodes::LOGICAL_ERROR); + throw Exception("Wrong parameter type in ALTER query", ErrorCodes::LOGICAL_ERROR); } } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp new file mode 100644 index 00000000000..7644f8104dc --- /dev/null +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -0,0 +1,1468 @@ +#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 + + +namespace DB +{ + +size_t MergeTreeData::total_size_of_currently_merging_parts = 0; + +MergeTreeData::MergeTreeData( + StoragePtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, const ASTPtr & sampling_expression_, + size_t index_granularity_, + Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_) + : owning_storage(owning_storage_), path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), columns(columns_), + context(context_), primary_expr_ast(primary_expr_ast_->clone()), + date_column_name(date_column_name_), sampling_expression(sampling_expression_), + index_granularity(index_granularity_), + mode(mode_), sign_column(sign_column_), + settings(settings_), + increment(full_path + "increment.txt"), log(&Logger::get("MergeTreeData: " + name)), shutdown_called(false), + file_name_regexp("^(\\d{8})_(\\d{8})_(\\d+)_(\\d+)_(\\d+)") +{ + min_marks_for_seek = (settings.min_rows_for_seek + index_granularity - 1) / index_granularity; + min_marks_for_concurrent_read = (settings.min_rows_for_concurrent_read + index_granularity - 1) / index_granularity; + max_marks_to_use_cache = (settings.max_rows_to_use_cache + index_granularity - 1) / index_granularity; + + /// создаём директорию, если её нет + Poco::File(full_path).createDirectories(); + + /// инициализируем описание сортировки + sort_descr.reserve(primary_expr_ast->children.size()); + for (ASTs::iterator it = primary_expr_ast->children.begin(); + it != primary_expr_ast->children.end(); + ++it) + { + String name = (*it)->getColumnName(); + sort_descr.push_back(SortColumnDescription(name, 1)); + } + + primary_expr = ExpressionAnalyzer(primary_expr_ast, context, *columns).getActions(false); + + ExpressionActionsPtr projected_expr = ExpressionAnalyzer(primary_expr_ast, context, *columns).getActions(true); + primary_key_sample = projected_expr->getSampleBlock(); + + merge_threads = new boost::threadpool::pool(settings.merging_threads); + + loadDataParts(); + clearOldParts(); + + UInt64 max_part_id = 0; + for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + max_part_id = std::max(max_part_id, (*it)->right); + } + increment.fixIfBroken(max_part_id); +} + +void MergeTreeData::shutdown() +{ + if (shutdown_called) + return; + shutdown_called = true; + + joinMergeThreads(); +} + + +MergeTreeData::~MergeTreeData() +{ + shutdown(); +} + + +BlockOutputStreamPtr MergeTreeData::write(ASTPtr query) +{ + return new MergeTreeDataBlockOutputStream(*this, owning_storage); +} + + +BlockInputStreams MergeTreeData::read( + const Names & column_names_to_return, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size, + unsigned threads) +{ + Poco::ScopedReadRWLock lock(read_lock); + + check(column_names_to_return); + processed_stage = QueryProcessingStage::FetchColumns; + + PKCondition key_condition(query, context, *columns, sort_descr); + PKCondition date_condition(query, context, *columns, SortDescription(1, SortColumnDescription(date_column_name, 1))); + + typedef std::vector PartsList; + PartsList parts; + + /// Выберем куски, в которых могут быть данные, удовлетворяющие date_condition. + { + Poco::ScopedLock lock(data_parts_mutex); + + for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + Field left = static_cast((*it)->left_date); + Field right = static_cast((*it)->right_date); + + if (date_condition.mayBeTrueInRange(&left, &right)) + parts.push_back(*it); + } + } + + /// Семплирование. + Names column_names_to_read = column_names_to_return; + UInt64 sampling_column_value_limit = 0; + typedef Poco::SharedPtr ASTFunctionPtr; + ASTFunctionPtr filter_function; + ExpressionActionsPtr filter_expression; + + ASTSelectQuery & select = *dynamic_cast(&*query); + if (select.sample_size) + { + double size = apply_visitor(FieldVisitorConvertToNumber(), + dynamic_cast(*select.sample_size).value); + + if (size < 0) + throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND); + + if (size > 1) + { + size_t requested_count = apply_visitor(FieldVisitorConvertToNumber(), dynamic_cast(*select.sample_size).value); + + /// Узнаем, сколько строк мы бы прочли без семплирования. + LOG_DEBUG(log, "Preliminary index scan with condition: " << key_condition.toString()); + size_t total_count = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + DataPartPtr & part = parts[i]; + MarkRanges ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); + + for (size_t j = 0; j < ranges.size(); ++j) + total_count += ranges[j].end - ranges[j].begin; + } + total_count *= index_granularity; + + size = std::min(1., static_cast(requested_count) / total_count); + + LOG_DEBUG(log, "Selected relative sample size: " << size); + } + + UInt64 sampling_column_max = 0; + DataTypePtr type = primary_expr->getSampleBlock().getByName(sampling_expression->getColumnName()).type; + + if (type->getName() == "UInt64") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt32") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt16") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt8") + sampling_column_max = std::numeric_limits::max(); + else + throw Exception("Invalid sampling column type in storage parameters: " + type->getName() + ". Must be unsigned integer type.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); + + /// Добавим условие, чтобы отсечь еще что-нибудь при повторном просмотре индекса. + sampling_column_value_limit = static_cast(size * sampling_column_max); + if (!key_condition.addCondition(sampling_expression->getColumnName(), + Range::createRightBounded(sampling_column_value_limit, true))) + throw Exception("Sampling column not in primary key", ErrorCodes::ILLEGAL_COLUMN); + + /// Выражение для фильтрации: sampling_expression <= sampling_column_value_limit + + ASTPtr filter_function_args = new ASTExpressionList; + filter_function_args->children.push_back(sampling_expression); + filter_function_args->children.push_back(new ASTLiteral(StringRange(), sampling_column_value_limit)); + + filter_function = new ASTFunction; + filter_function->name = "lessOrEquals"; + filter_function->arguments = filter_function_args; + filter_function->children.push_back(filter_function->arguments); + + filter_expression = ExpressionAnalyzer(filter_function, context, *columns).getActions(false); + + /// Добавим столбцы, нужные для sampling_expression. + std::vector add_columns = filter_expression->getRequiredColumns(); + column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); + std::sort(column_names_to_read.begin(), column_names_to_read.end()); + column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); + } + + LOG_DEBUG(log, "Key condition: " << key_condition.toString()); + LOG_DEBUG(log, "Date condition: " << date_condition.toString()); + + /// PREWHERE + ExpressionActionsPtr prewhere_actions; + String prewhere_column; + if (select.prewhere_expression) + { + ExpressionAnalyzer analyzer(select.prewhere_expression, context, *columns); + prewhere_actions = analyzer.getActions(false); + prewhere_column = select.prewhere_expression->getColumnName(); + } + + RangesInDataParts parts_with_ranges; + + /// Найдем, какой диапазон читать из каждого куска. + size_t sum_marks = 0; + size_t sum_ranges = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + DataPartPtr & part = parts[i]; + RangesInDataPart ranges(part); + ranges.ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); + + if (!ranges.ranges.empty()) + { + parts_with_ranges.push_back(ranges); + + sum_ranges += ranges.ranges.size(); + for (size_t j = 0; j < ranges.ranges.size(); ++j) + { + sum_marks += ranges.ranges[j].end - ranges.ranges[j].begin; + } + } + } + + LOG_DEBUG(log, "Selected " << parts.size() << " parts by date, " << parts_with_ranges.size() << " parts by key, " + << sum_marks << " marks to read from " << sum_ranges << " ranges"); + + BlockInputStreams res; + + if (select.final) + { + /// Добавим столбцы, нужные для вычисления первичного ключа и знака. + std::vector add_columns = primary_expr->getRequiredColumns(); + column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); + column_names_to_read.push_back(sign_column); + std::sort(column_names_to_read.begin(), column_names_to_read.end()); + column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); + + res = spreadMarkRangesAmongThreadsFinal( + parts_with_ranges, + threads, + column_names_to_read, + max_block_size, + settings.use_uncompressed_cache, + prewhere_actions, + prewhere_column); + } + else + { + res = spreadMarkRangesAmongThreads( + parts_with_ranges, + threads, + column_names_to_read, + max_block_size, + settings.use_uncompressed_cache, + prewhere_actions, + prewhere_column); + } + + if (select.sample_size) + { + for (size_t i = 0; i < res.size(); ++i) + { + BlockInputStreamPtr original_stream = res[i]; + BlockInputStreamPtr expression_stream = new ExpressionBlockInputStream(original_stream, filter_expression); + BlockInputStreamPtr filter_stream = new FilterBlockInputStream(expression_stream, filter_function->getColumnName()); + res[i] = filter_stream; + } + } + + return res; +} + + +/// Примерно поровну распределить засечки между потоками. +BlockInputStreams MergeTreeData::spreadMarkRangesAmongThreads( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column) +{ + /// На всякий случай перемешаем куски. + std::random_shuffle(parts.begin(), parts.end()); + + /// Посчитаем засечки для каждого куска. + std::vector sum_marks_in_parts(parts.size()); + size_t sum_marks = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + /// Пусть отрезки будут перечислены справа налево, чтобы можно было выбрасывать самый левый отрезок с помощью pop_back(). + std::reverse(parts[i].ranges.begin(), parts[i].ranges.end()); + + sum_marks_in_parts[i] = 0; + for (size_t j = 0; j < parts[i].ranges.size(); ++j) + { + MarkRange & range = parts[i].ranges[j]; + sum_marks_in_parts[i] += range.end - range.begin; + } + sum_marks += sum_marks_in_parts[i]; + } + + if (sum_marks > max_marks_to_use_cache) + use_uncompressed_cache = false; + + BlockInputStreams res; + + if (sum_marks > 0) + { + size_t min_marks_per_thread = (sum_marks - 1) / threads + 1; + + for (size_t i = 0; i < threads && !parts.empty(); ++i) + { + size_t need_marks = min_marks_per_thread; + BlockInputStreams streams; + + /// Цикл по кускам. + while (need_marks > 0 && !parts.empty()) + { + RangesInDataPart & part = parts.back(); + size_t & marks_in_part = sum_marks_in_parts.back(); + + /// Не будем брать из куска слишком мало строк. + if (marks_in_part >= min_marks_for_concurrent_read && + need_marks < min_marks_for_concurrent_read) + need_marks = min_marks_for_concurrent_read; + + /// Не будем оставлять в куске слишком мало строк. + if (marks_in_part > need_marks && + marks_in_part - need_marks < min_marks_for_concurrent_read) + need_marks = marks_in_part; + + /// Возьмем весь кусок, если он достаточно мал. + if (marks_in_part <= need_marks) + { + /// Восстановим порядок отрезков. + std::reverse(part.ranges.begin(), part.ranges.end()); + + streams.push_back(new MergeTreeBlockInputStream( + full_path + part.data_part->name + '/', max_block_size, column_names, *this, + part.data_part, part.ranges, owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column)); + need_marks -= marks_in_part; + parts.pop_back(); + sum_marks_in_parts.pop_back(); + continue; + } + + MarkRanges ranges_to_get_from_part; + + /// Цикл по отрезкам куска. + while (need_marks > 0) + { + if (part.ranges.empty()) + throw Exception("Unexpected end of ranges while spreading marks among threads", ErrorCodes::LOGICAL_ERROR); + + MarkRange & range = part.ranges.back(); + size_t marks_in_range = range.end - range.begin; + + size_t marks_to_get_from_range = std::min(marks_in_range, need_marks); + ranges_to_get_from_part.push_back(MarkRange(range.begin, range.begin + marks_to_get_from_range)); + range.begin += marks_to_get_from_range; + marks_in_part -= marks_to_get_from_range; + need_marks -= marks_to_get_from_range; + if (range.begin == range.end) + part.ranges.pop_back(); + } + + streams.push_back(new MergeTreeBlockInputStream( + full_path + part.data_part->name + '/', max_block_size, column_names, *this, + part.data_part, ranges_to_get_from_part, owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column)); + } + + if (streams.size() == 1) + res.push_back(streams[0]); + else + res.push_back(new ConcatBlockInputStream(streams)); + } + + if (!parts.empty()) + throw Exception("Couldn't spread marks among threads", ErrorCodes::LOGICAL_ERROR); + } + + return res; +} + + +/// Распределить засечки между потоками и сделать, чтобы в ответе (почти) все данные были сколлапсированы (модификатор FINAL). +BlockInputStreams MergeTreeData::spreadMarkRangesAmongThreadsFinal( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column) +{ + size_t sum_marks = 0; + for (size_t i = 0; i < parts.size(); ++i) + for (size_t j = 0; j < parts[i].ranges.size(); ++j) + sum_marks += parts[i].ranges[j].end - parts[i].ranges[j].begin; + + if (sum_marks > max_marks_to_use_cache) + use_uncompressed_cache = false; + + ExpressionActionsPtr sign_filter_expression; + String sign_filter_column; + createPositiveSignCondition(sign_filter_expression, sign_filter_column); + + BlockInputStreams to_collapse; + + for (size_t part_index = 0; part_index < parts.size(); ++part_index) + { + RangesInDataPart & part = parts[part_index]; + + BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( + full_path + part.data_part->name + '/', max_block_size, column_names, *this, + part.data_part, part.ranges, owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column); + + to_collapse.push_back(new ExpressionBlockInputStream(source_stream, primary_expr)); + } + + BlockInputStreams res; + if (to_collapse.size() == 1) + res.push_back(new FilterBlockInputStream(new ExpressionBlockInputStream(to_collapse[0], sign_filter_expression), sign_filter_column)); + else if (to_collapse.size() > 1) + res.push_back(new CollapsingFinalBlockInputStream(to_collapse, sort_descr, sign_column)); + + return res; +} + + +void MergeTreeData::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column) +{ + ASTFunction * function = new ASTFunction; + ASTPtr function_ptr = function; + + ASTExpressionList * arguments = new ASTExpressionList; + ASTPtr arguments_ptr = arguments; + + ASTIdentifier * sign = new ASTIdentifier; + ASTPtr sign_ptr = sign; + + ASTLiteral * one = new ASTLiteral; + ASTPtr one_ptr = one; + + function->name = "equals"; + function->arguments = arguments_ptr; + function->children.push_back(arguments_ptr); + + arguments->children.push_back(sign_ptr); + arguments->children.push_back(one_ptr); + + sign->name = sign_column; + sign->kind = ASTIdentifier::Column; + + one->type = new DataTypeInt8; + one->value = Field(static_cast(1)); + + out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); + out_column = function->getColumnName(); +} + + +String MergeTreeData::getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level) +{ + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + /// Имя директории для куска иммет вид: YYYYMMDD_YYYYMMDD_N_N_L. + String res; + { + unsigned left_date_id = Date2OrderedIdentifier(date_lut.fromDayNum(left_date)); + unsigned right_date_id = Date2OrderedIdentifier(date_lut.fromDayNum(right_date)); + + WriteBufferFromString wb(res); + + writeIntText(left_date_id, wb); + writeChar('_', wb); + writeIntText(right_date_id, wb); + writeChar('_', wb); + writeIntText(left_id, wb); + writeChar('_', wb); + writeIntText(right_id, wb); + writeChar('_', wb); + writeIntText(level, wb); + } + + return res; +} + + +void MergeTreeData::parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part) +{ + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + part.left_date = date_lut.toDayNum(OrderedIdentifier2Date(file_name.substr(matches[1].offset, matches[1].length))); + part.right_date = date_lut.toDayNum(OrderedIdentifier2Date(file_name.substr(matches[2].offset, matches[2].length))); + part.left = parse(file_name.substr(matches[3].offset, matches[3].length)); + part.right = parse(file_name.substr(matches[4].offset, matches[4].length)); + part.level = parse(file_name.substr(matches[5].offset, matches[5].length)); + + part.left_month = date_lut.toFirstDayNumOfMonth(part.left_date); + part.right_month = date_lut.toFirstDayNumOfMonth(part.right_date); +} + + +void MergeTreeData::loadDataParts() +{ + LOG_DEBUG(log, "Loading data parts"); + + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + + data_parts.clear(); + + Strings part_file_names; + Strings old_file_names; + Poco::DirectoryIterator end; + for (Poco::DirectoryIterator it(full_path); it != end; ++it) + { + String file_name = it.name(); + + /// Удаляем временные директории старше суток. + if (0 == file_name.compare(0, strlen("tmp_"), "tmp_")) + { + Poco::File tmp_dir(full_path + file_name); + + if (tmp_dir.isDirectory() && tmp_dir.getLastModified().epochTime() + 86400 < time(0)) + { + LOG_WARNING(log, "Removing temporary directory " << full_path << file_name); + Poco::File(full_path + file_name).remove(true); + } + + continue; + } + + if (0 == file_name.compare(0, strlen("old_"), "old_")) + old_file_names.push_back(file_name); + else + part_file_names.push_back(file_name); + } + + Poco::RegularExpression::MatchVec matches; + while (!part_file_names.empty()) + { + String file_name = part_file_names.back(); + part_file_names.pop_back(); + + if (!isPartDirectory(file_name, matches)) + continue; + + DataPartPtr part = new DataPart(*this); + parsePartName(file_name, matches, *part); + part->name = file_name; + + /// Для битых кусков, которые могут образовываться после грубого перезапуска сервера, попытаться восстановить куски, из которых они сделаны. + if (isBrokenPart(full_path + file_name)) + { + if (part->level == 0) + { + /// Восстановить куски нулевого уровня невозможно. + LOG_ERROR(log, "Removing broken part " << path + file_name << " because is't impossible to repair."); + part->remove(); + } + else + { + Strings new_parts = tryRestorePart(full_path, file_name, old_file_names); + part_file_names.insert(part_file_names.begin(), new_parts.begin(), new_parts.end()); + } + + continue; + } + + /// Размер - в количестве засечек. + part->size = Poco::File(full_path + file_name + "/" + escapeForFileName(columns->front().first) + ".mrk").getSize() + / MERGE_TREE_MARK_SIZE; + + part->modification_time = Poco::File(full_path + file_name).getLastModified().epochTime(); + + try + { + part->loadIndex(); + } + catch (...) + { + /// Не будем вставлять в набор кусок с битым индексом. Пропустим кусок и позволим серверу запуститься. + tryLogCurrentException(__PRETTY_FUNCTION__); + continue; + } + + data_parts.insert(part); + } + + all_data_parts = data_parts; + + /** Удаляем из набора актуальных кусков куски, которые содержатся в другом куске (которые были склеены), + * но по каким-то причинам остались лежать в файловой системе. + * Удаление файлов будет произведено потом в методе clearOldParts. + */ + + if (data_parts.size() >= 2) + { + DataParts::iterator prev_jt = data_parts.begin(); + DataParts::iterator curr_jt = prev_jt; + ++curr_jt; + while (curr_jt != data_parts.end()) + { + /// Куски данных за разные месяцы рассматривать не будем + if ((*curr_jt)->left_month != (*curr_jt)->right_month + || (*curr_jt)->right_month != (*prev_jt)->left_month + || (*prev_jt)->left_month != (*prev_jt)->right_month) + { + ++prev_jt; + ++curr_jt; + continue; + } + + if ((*curr_jt)->contains(**prev_jt)) + { + LOG_WARNING(log, "Part " << (*curr_jt)->name << " contains " << (*prev_jt)->name); + data_parts.erase(prev_jt); + prev_jt = curr_jt; + ++curr_jt; + } + else if ((*prev_jt)->contains(**curr_jt)) + { + LOG_WARNING(log, "Part " << (*prev_jt)->name << " contains " << (*curr_jt)->name); + data_parts.erase(curr_jt++); + } + else + { + ++prev_jt; + ++curr_jt; + } + } + } + + LOG_DEBUG(log, "Loaded data parts (" << data_parts.size() << " items)"); +} + + +void MergeTreeData::clearOldParts() +{ + Poco::ScopedTry lock; + + /// Если метод уже вызван из другого потока (или если all_data_parts прямо сейчас меняют), то можно ничего не делать. + if (!lock.lock(&all_data_parts_mutex)) + { + LOG_TRACE(log, "Already clearing or modifying old parts"); + return; + } + + LOG_TRACE(log, "Clearing old parts"); + for (DataParts::iterator it = all_data_parts.begin(); it != all_data_parts.end();) + { + int ref_count = it->referenceCount(); + if (ref_count == 1) /// После этого ref_count не может увеличиться. + { + LOG_DEBUG(log, "'Removing' part " << (*it)->name << " (prepending old_ to its name)"); + + (*it)->renameToOld(); + all_data_parts.erase(it++); + } + else + ++it; + } + + /// Удалим старые old_ куски. + Poco::DirectoryIterator end; + for (Poco::DirectoryIterator it(full_path); it != end; ++it) + { + if (0 != it.name().compare(0, strlen("old_"), "old_")) + continue; + if (it->isDirectory() && it->getLastModified().epochTime() + settings.old_parts_lifetime < time(0)) + { + it->remove(true); + } + } +} + + +void MergeTreeData::merge(size_t iterations, bool async, bool aggressive) +{ + bool while_can = false; + if (iterations == 0) + { + while_can = true; + iterations = settings.merging_threads; + } + + for (size_t i = 0; i < iterations; ++i) + merge_threads->schedule(boost::bind(&MergeTreeData::mergeThread, this, while_can, aggressive)); + + if (!async) + joinMergeThreads(); +} + + +void MergeTreeData::mergeThread(bool while_can, bool aggressive) +{ + try + { + while (!shutdown_called) + { + /// Удаляем старые куски. На случай, если в слиянии что-то сломано, и из следующего блока вылетит исключение. + clearOldParts(); + + { + Poco::ScopedReadRWLock lock(merge_lock); + + /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски + Poco::SharedPtr what; + + if (!selectPartsToMerge(what, false, aggressive) && !selectPartsToMerge(what, true, aggressive)) + break; + + mergeParts(what); + } + + if (shutdown_called) + break; + + /// Удаляем куски, которые мы только что сливали. + clearOldParts(); + + if (!while_can) + break; + } + } + catch (const Exception & e) + { + LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl + << std::endl + << "Stack trace:" << std::endl + << e.getStackTrace().toString()); + } + catch (const Poco::Exception & e) + { + LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); + } + catch (const std::exception & e) + { + LOG_ERROR(log, "std::exception: " << e.what()); + } + catch (...) + { + LOG_ERROR(log, "Unknown exception"); + } +} + + +void MergeTreeData::joinMergeThreads() +{ + LOG_DEBUG(log, "Waiting for merge threads to finish."); + merge_threads->wait(); +} + + +/// Выбираем отрезок из не более чем max_parts_to_merge_at_once кусков так, чтобы максимальный размер был меньше чем в max_size_ratio_to_merge_parts раз больше суммы остальных. +/// Это обеспечивает в худшем случае время O(n log n) на все слияния, независимо от выбора сливаемых кусков, порядка слияния и добавления. +/// При max_parts_to_merge_at_once >= log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts), +/// несложно доказать, что всегда будет что сливать, пока количество кусков больше +/// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts)*(количество кусков размером больше max_rows_to_merge_parts). +/// Дальше эвристики. +/// Будем выбирать максимальный по включению подходящий отрезок. +/// Из всех таких выбираем отрезок с минимальным максимумом размера. +/// Из всех таких выбираем отрезок с минимальным минимумом размера. +/// Из всех таких выбираем отрезок с максимальной длиной. +/// Дополнительно: +/// 1) с 1:00 до 5:00 ограничение сверху на размер куска в основном потоке увеличивается в несколько раз +/// 2) в зависимоти от возраста кусков меняется допустимая неравномерность при слиянии +/// 3) Молодые куски крупного размера (примерно больше 1 Гб) можно сливать не меньше чем по три +/// 4) Если в одном из потоков идет мердж крупных кусков, то во втором сливать только маленькие кусочки +/// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности + +bool MergeTreeData::selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive) +{ + LOG_DEBUG(log, "Selecting parts to merge"); + + Poco::ScopedLock lock(data_parts_mutex); + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + size_t min_max = -1U; + size_t min_min = -1U; + int max_len = 0; + DataParts::iterator best_begin; + bool found = false; + + DayNum_t now_day = date_lut.toDayNum(time(0)); + DayNum_t now_month = date_lut.toFirstDayNumOfMonth(now_day); + int now_hour = date_lut.toHourInaccurate(time(0)); + + size_t maybe_used_bytes = total_size_of_currently_merging_parts; + size_t total_free_bytes = 0; + struct statvfs fs; + + /// Смотрим количество свободного места в файловой системе + if (statvfs(full_path.c_str(), &fs) != 0) + throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS); + + total_free_bytes = fs.f_bfree * fs.f_bsize; + + /// Сколько кусков, начиная с текущего, можно включить в валидный отрезок, начинающийся левее текущего куска. + /// Нужно для определения максимальности по включению. + int max_count_from_left = 0; + + size_t cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts; + + /// Если ночь, можем мерджить сильно большие куски + if (now_hour >= 1 && now_hour <= 5) + cur_max_rows_to_merge_parts *= settings.merge_parts_at_night_inc; + + /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. + for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + if ((*it)->currently_merging && (*it)->size * index_granularity > 25 * 1024 * 1024) + { + cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts_second; + break; + } + } + + /// Левый конец отрезка. + for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + const DataPartPtr & first_part = *it; + + max_count_from_left = std::max(0, max_count_from_left - 1); + + /// Кусок не занят. + if (first_part->currently_merging) + continue; + + /// Кусок достаточно мал или слияние "агрессивное". + if (first_part->size * index_granularity > cur_max_rows_to_merge_parts + && !aggressive) + continue; + + /// Кусок в одном месяце. + if (first_part->left_month != first_part->right_month) + { + LOG_WARNING(log, "Part " << first_part->name << " spans more than one month"); + continue; + } + + /// Самый длинный валидный отрезок, начинающийся здесь. + size_t cur_longest_max = -1U; + size_t cur_longest_min = -1U; + int cur_longest_len = 0; + + /// Текущий отрезок, не обязательно валидный. + size_t cur_max = first_part->size; + size_t cur_min = first_part->size; + size_t cur_sum = first_part->size; + size_t cur_total_size = first_part->size_in_bytes; + int cur_len = 1; + + DayNum_t month = first_part->left_month; + UInt64 cur_id = first_part->right; + + /// Этот месяц кончился хотя бы день назад. + bool is_old_month = now_day - now_month >= 1 && now_month > month; + + time_t oldest_modification_time = first_part->modification_time; + + /// Правый конец отрезка. + DataParts::iterator jt = it; + for (++jt; jt != data_parts.end() && cur_len < static_cast(settings.max_parts_to_merge_at_once); ++jt) + { + const DataPartPtr & last_part = *jt; + + /// Кусок не занят и в одном правильном месяце. + if (last_part->currently_merging || + last_part->left_month != last_part->right_month || + last_part->left_month != month) + break; + + /// Кусок достаточно мал или слияние "агрессивное". + if (last_part->size * index_granularity > cur_max_rows_to_merge_parts + && !aggressive) + break; + + /// Кусок правее предыдущего. + if (last_part->left < cur_id) + { + LOG_WARNING(log, "Part " << last_part->name << " intersects previous part"); + break; + } + + oldest_modification_time = std::max(oldest_modification_time, last_part->modification_time); + cur_max = std::max(cur_max, last_part->size); + cur_min = std::min(cur_min, last_part->size); + cur_sum += last_part->size; + cur_total_size += last_part->size_in_bytes; + ++cur_len; + cur_id = last_part->right; + + int min_len = 2; + int cur_age_in_sec = time(0) - oldest_modification_time; + + /// Если куски примерно больше 1 Gb и образовались меньше 6 часов назад, то мерджить не меньше чем по 3. + if (cur_max * index_granularity * 150 > 1024*1024*1024 && cur_age_in_sec < 6*3600) + min_len = 3; + + /// Равен 0.5 если возраст порядка 0, равен 5 если возраст около месяца. + double time_ratio_modifier = 0.5 + 9 * static_cast(cur_age_in_sec) / (3600*24*30 + cur_age_in_sec); + + /// Двоичный логарифм суммарного размера кусочков + double log_cur_sum = std::log(cur_sum * index_granularity) / std::log(2); + /// Равен ~2 если куски маленькие, уменьшается до 0.5 с увеличением суммарного размера до 2^25. + double size_ratio_modifier = std::max(0.25, 2 - 3 * (log_cur_sum) / (25 + log_cur_sum)); + + /// Объединяем все в одну константу + double ratio = std::max(0.5, time_ratio_modifier * size_ratio_modifier * settings.max_size_ratio_to_merge_parts); + + /// Если отрезок валидный, то он самый длинный валидный, начинающийся тут. + if (cur_len >= min_len + && (static_cast(cur_max) / (cur_sum - cur_max) < ratio + /// За старый месяц объединяем что угодно, если разрешено и если этому хотя бы 15 дней + || (is_old_month && merge_anything_for_old_months && cur_age_in_sec > 3600*24*15) + /// Если слияние "агрессивное", то сливаем что угодно + || aggressive)) + { + /// Достаточно места на диске, чтобы покрыть уже активные и новый мерджи с запасом в 50% + if (total_free_bytes > (maybe_used_bytes + cur_total_size) * 1.5) + { + cur_longest_max = cur_max; + cur_longest_min = cur_min; + cur_longest_len = cur_len; + } + else + LOG_WARNING(log, "Won't merge parts from " << first_part->name << " to " << last_part->name + << " because not enough free space: " << total_free_bytes << " free, " + << maybe_used_bytes << " already involved in merge, " + << cur_total_size << " required now (+50% on overhead)"); + } + } + + /// Это максимальный по включению валидный отрезок. + if (cur_longest_len > max_count_from_left) + { + max_count_from_left = cur_longest_len; + + if (!found + || std::make_pair(std::make_pair(cur_longest_max, cur_longest_min), -cur_longest_len) + < std::make_pair(std::make_pair(min_max, min_min), -max_len)) + { + found = true; + min_max = cur_longest_max; + min_min = cur_longest_min; + max_len = cur_longest_len; + best_begin = it; + } + } + } + + if (found) + { + std::vector parts; + + DataParts::iterator it = best_begin; + for (int i = 0; i < max_len; ++i) + { + parts.push_back(*it); + ++it; + } + what = new CurrentlyMergingPartsTagger(parts, data_parts_mutex); + + LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); + } + else + { + LOG_DEBUG(log, "No parts to merge"); + } + + return found; +} + + +/// parts должны быть отсортированы. +void MergeTreeData::mergeParts(Poco::SharedPtr & what) +{ + const std::vector & parts(what->parts); + + LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); + + Names all_column_names; + for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it) + all_column_names.push_back(it->first); + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + MergeTreeData::DataPartPtr new_data_part = new DataPart(*this); + new_data_part->left_date = std::numeric_limits::max(); + new_data_part->right_date = std::numeric_limits::min(); + new_data_part->left = parts.front()->left; + new_data_part->right = parts.back()->right; + new_data_part->level = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + new_data_part->level = std::max(new_data_part->level, parts[i]->level); + new_data_part->left_date = std::min(new_data_part->left_date, parts[i]->left_date); + new_data_part->right_date = std::max(new_data_part->right_date, parts[i]->right_date); + } + ++new_data_part->level; + new_data_part->name = getPartName( + new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); + new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); + new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); + + /** Читаем из всех кусков, сливаем и пишем в новый. + * Попутно вычисляем выражение для сортировки. + */ + BlockInputStreams src_streams; + + for (size_t i = 0; i < parts.size(); ++i) + { + MarkRanges ranges(1, MarkRange(0, parts[i]->size)); + src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( + full_path + parts[i]->name + '/', DEFAULT_MERGE_BLOCK_SIZE, all_column_names, *this, parts[i], ranges, + StoragePtr(), false, NULL, ""), primary_expr)); + } + + /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. + /// В слитом куске строки с одинаковым ключом должны идти в порядке возрастания идентификатора исходного куска, то есть (примерного) возрастания времени вставки. + BlockInputStreamPtr merged_stream; + + switch (mode) + { + case Ordinary: + merged_stream = new MergingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + break; + + case Collapsing: + merged_stream = new CollapsingSortedBlockInputStream(src_streams, sort_descr, sign_column, DEFAULT_MERGE_BLOCK_SIZE); + break; + + case Summing: + merged_stream = new SummingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + break; + + default: + throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR); + } + + MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(*this, + new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); + + merged_stream->readPrefix(); + to->writePrefix(); + + Block block; + while (!shutdown_called && (block = merged_stream->read())) + to->write(block); + + if (shutdown_called) + { + LOG_INFO(log, "Shutdown requested while merging parts."); + return; + } + + merged_stream->readSuffix(); + to->writeSuffix(); + + /// В обычном режиме строчки не могут удалиться при мердже. + if (0 == to->marksCount() && mode == Ordinary) + throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); + + new_data_part->size = to->marksCount(); + new_data_part->modification_time = time(0); + + if (0 != to->marksCount()) + new_data_part->loadIndex(); /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи. + + { + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + + /// Добавляем новый кусок в набор, если он не пустой. + + for (size_t i = 0; i < parts.size(); ++i) + if (data_parts.end() == data_parts.find(parts[i])) + throw Exception("Logical error: cannot find data part " + parts[i]->name + " in list", ErrorCodes::LOGICAL_ERROR); + + if (0 == to->marksCount()) + { + LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); + } + else + { + data_parts.insert(new_data_part); + all_data_parts.insert(new_data_part); + } + + for (size_t i = 0; i < parts.size(); ++i) + data_parts.erase(data_parts.find(parts[i])); + } + + LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); +} + + +void MergeTreeData::rename(const String & new_path_to_db, const String & new_name) +{ + joinMergeThreads(); + + /// Кажется тут race condition - в этот момент мердж может запуститься снова. + + std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; + + Poco::File(full_path).renameTo(new_full_path); + + path = new_path_to_db; + full_path = new_full_path; + name = new_name; + + increment.setPath(full_path + "increment.txt"); +} + + +void MergeTreeData::dropImpl() +{ + joinMergeThreads(); + + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + + data_parts.clear(); + all_data_parts.clear(); + + Poco::File(full_path).remove(true); +} + +void MergeTreeData::removeColumnFiles(String column_name) +{ + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + + /// Регэксп выбирает файлы столбца для удаления + Poco::RegularExpression re(column_name + "(?:(?:\\.|\\%2E).+){0,1}" +"(?:\\.mrk|\\.bin|\\.size\\d+\\.bin|\\.size\\d+\\.mrk)"); + /// Цикл по всем директориям кусочков + Poco::RegularExpression::MatchVec matches; + Poco::DirectoryIterator end; + for (Poco::DirectoryIterator it_dir = Poco::DirectoryIterator(full_path); it_dir != end; ++it_dir) + { + std::string dir_name = it_dir.name(); + + if (!isPartDirectory(dir_name, matches)) + continue; + + /// Цикл по каждому из файлов в директории кусочков + String full_dir_name = full_path + dir_name + "/"; + for (Poco::DirectoryIterator it_file(full_dir_name); it_file != end; ++it_file) + { + if (re.match(it_file.name())) + { + Poco::File file(full_dir_name + it_file.name()); + if (file.exists()) + file.remove(); + } + } + } +} + +void MergeTreeData::createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column) +{ + ASTFunction * function = new ASTFunction; + ASTPtr function_ptr = function; + + ASTExpressionList * arguments = new ASTExpressionList; + ASTPtr arguments_ptr = arguments; + + function->name = "to" + out_type; + function->arguments = arguments_ptr; + function->children.push_back(arguments_ptr); + + ASTIdentifier * in_column = new ASTIdentifier; + ASTPtr in_column_ptr = in_column; + + arguments->children.push_back(in_column_ptr); + + in_column->name = in_column_name; + in_column->kind = ASTIdentifier::Column; + + out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); + out_column = function->getColumnName(); +} + +void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) +{ + if (params.type == ASTAlterQuery::MODIFY) + { + { + typedef std::vector PartsList; + PartsList parts; + { + Poco::ScopedLock lock(data_parts_mutex); + for (auto & data_part : data_parts) + { + parts.push_back(data_part); + } + } + + Names column_name; + const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); + StringRange type_range = name_type.type->range; + String type(type_range.first, type_range.second - type_range.first); + DB::DataTypePtr old_type_ptr = getDataTypeByName(name_type.name); + DB::DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); + if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || + dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) + throw DB::Exception("ALTER MODIFY not supported for nested and array types"); + + column_name.push_back(name_type.name); + DB::ExpressionActionsPtr expr; + String out_column; + createConvertExpression(name_type.name, type, expr, out_column); + + ColumnNumbers num(1, 0); + for (DataPartPtr & part : parts) + { + MarkRanges ranges(1, MarkRange(0, part->size)); + ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', + DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, StoragePtr(), false, NULL, ""), expr); + MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); + out.writePrefix(); + + try + { + while(DB::Block b = in.read()) + { + /// оставляем только столбец с результатом + b.erase(0); + out.write(b); + } + LOG_TRACE(log, "Write Suffix"); + out.writeSuffix(); + } + catch (const Exception & e) + { + if (e.code() != ErrorCodes::ALL_REQUESTED_COLUMNS_ARE_MISSING) + throw; + } + } + + /// переименовываем файлы + /// переименовываем старые столбцы, добавляя расширение .old + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin").exists()) + { + LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << path + ".bin" + ".old"); + Poco::File(path + ".bin").renameTo(path + ".bin" + ".old"); + LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << path + ".mrk" + ".old"); + Poco::File(path + ".mrk").renameTo(path + ".mrk" + ".old"); + } + } + + /// переименовываем временные столбцы + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(out_column); + std::string new_path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin").exists()) + { + LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << new_path + ".bin"); + Poco::File(path + ".bin").renameTo(new_path + ".bin"); + LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << new_path + ".mrk"); + Poco::File(path + ".mrk").renameTo(new_path + ".mrk"); + } + } + + // удаляем старые столбцы + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin" + ".old").exists()) + { + LOG_TRACE(log, "Removing old column " << path + ".bin" + ".old"); + Poco::File(path + ".bin" + ".old").remove(); + LOG_TRACE(log, "Removing old column " << path + ".mrk" + ".old"); + Poco::File(path + ".mrk" + ".old").remove(); + } + } + } + context.getUncompressedCache()->reset(); + context.getMarkCache()->reset(); + } + { + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + alterColumns(params, columns, context); + } + if (params.type == ASTAlterQuery::DROP) + { + String column_name = dynamic_cast(*params.column).name; + removeColumnFiles(column_name); + } +} + + +bool MergeTreeData::isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const +{ + return (file_name_regexp.match(dir_name, 0, matches) && 6 == matches.size()); +} + + +bool MergeTreeData::isBrokenPart(const String & path) +{ + /// Проверяем, что первичный ключ непуст. + + Poco::File index_file(path + "/primary.idx"); + + if (!index_file.exists() || index_file.getSize() == 0) + { + LOG_ERROR(log, "Part " << path << " is broken: primary key is empty."); + + return true; + } + + /// Проверяем, что все засечки непусты и имеют одинаковый размер. + + ssize_t marks_size = -1; + for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it) + { + Poco::File marks_file(path + "/" + escapeForFileName(it->first) + ".mrk"); + + /// при добавлении нового столбца в таблицу файлы .mrk не создаются. Не будем ничего удалять. + if (!marks_file.exists()) + continue; + + if (marks_size == -1) + { + marks_size = marks_file.getSize(); + + if (0 == marks_size) + { + LOG_ERROR(log, "Part " << path << " is broken: " << marks_file.path() << " is empty."); + + return true; + } + } + else + { + if (static_cast(marks_file.getSize()) != marks_size) + { + LOG_ERROR(log, "Part " << path << " is broken: marks have different sizes."); + + return true; + } + } + } + + return false; +} + +Strings MergeTreeData::tryRestorePart(const String & path, const String & file_name, Strings & old_parts) +{ + LOG_ERROR(log, "Restoring all old_ parts covered by " << file_name); + + Poco::RegularExpression::MatchVec matches; + Strings restored_parts; + + isPartDirectory(file_name, matches); + DataPart broken_part(*this); + parsePartName(file_name, matches, broken_part); + + for (int i = static_cast(old_parts.size()) - 1; i >= 0; --i) + { + DataPart old_part(*this); + String name = old_parts[i].substr(strlen("old_")); + if (!isPartDirectory(name, matches)) + { + LOG_ERROR(log, "Strange file name: " << path + old_parts[i] << "; ignoring"); + old_parts.erase(old_parts.begin() + i); + continue; + } + parsePartName(name, matches, old_part); + if (broken_part.contains(old_part)) + { + /// Восстанавливаем все содержащиеся куски. Если некоторые из них содержатся в других, их удалит loadDataParts. + LOG_ERROR(log, "Restoring part " << path + old_parts[i]); + Poco::File(path + old_parts[i]).renameTo(path + name); + old_parts.erase(old_parts.begin() + i); + restored_parts.push_back(name); + } + } + + if (restored_parts.size() >= 2) + { + LOG_ERROR(log, "Removing broken part " << path + file_name << " because at least 2 old_ parts were restored in its place"); + Poco::File(path + file_name).remove(true); + } + else + { + LOG_ERROR(log, "Not removing broken part " << path + file_name + << " because less than 2 old_ parts were restored in its place. You need to resolve this manually"); + } + + return restored_parts; +} + +} diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index 42077e75b9c..8ef0d0d3d83 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -192,7 +192,7 @@ StoragePtr StorageFactory::get( return StorageMergeTree::create( data_path, table_name, columns, context, primary_expr, date_column_name, sampling_expression, index_granularity, - name == "SummingMergeTree" ? StorageMergeTree::Summing : StorageMergeTree::Ordinary); + name == "SummingMergeTree" ? MergeTreeData::Summing : MergeTreeData::Ordinary); } else if (name == "CollapsingMergeTree") { @@ -234,7 +234,7 @@ StoragePtr StorageFactory::get( return StorageMergeTree::create( data_path, table_name, columns, context, primary_expr, date_column_name, - sampling_expression, index_granularity, StorageMergeTree::Collapsing, sign_column_name); + sampling_expression, index_granularity, MergeTreeData::Collapsing, sign_column_name); } else if (name == "SystemNumbers") { diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 537a9cf0f89..2fce5fdd26e 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -1,1484 +1,72 @@ -#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 - namespace DB { -size_t StorageMergeTree::total_size_of_currently_merging_parts = 0; - -StorageMergeTree::StorageMergeTree( - const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, - ASTPtr & primary_expr_ast_, - const String & date_column_name_, const ASTPtr & sampling_expression_, - size_t index_granularity_, - Mode mode_, - const String & sign_column_, - const StorageMergeTreeSettings & settings_) - : path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), columns(columns_), - context(context_), primary_expr_ast(primary_expr_ast_->clone()), - date_column_name(date_column_name_), sampling_expression(sampling_expression_), - index_granularity(index_granularity_), - mode(mode_), sign_column(sign_column_), - settings(settings_), - increment(full_path + "increment.txt"), log(&Logger::get("StorageMergeTree: " + name)), shutdown_called(false), - file_name_regexp("^(\\d{8})_(\\d{8})_(\\d+)_(\\d+)_(\\d+)") -{ - min_marks_for_seek = (settings.min_rows_for_seek + index_granularity - 1) / index_granularity; - min_marks_for_concurrent_read = (settings.min_rows_for_concurrent_read + index_granularity - 1) / index_granularity; - max_marks_to_use_cache = (settings.max_rows_to_use_cache + index_granularity - 1) / index_granularity; - - /// создаём директорию, если её нет - Poco::File(full_path).createDirectories(); - - /// инициализируем описание сортировки - sort_descr.reserve(primary_expr_ast->children.size()); - for (ASTs::iterator it = primary_expr_ast->children.begin(); - it != primary_expr_ast->children.end(); - ++it) - { - String name = (*it)->getColumnName(); - sort_descr.push_back(SortColumnDescription(name, 1)); - } - - primary_expr = ExpressionAnalyzer(primary_expr_ast, context, *columns).getActions(false); - - ExpressionActionsPtr projected_expr = ExpressionAnalyzer(primary_expr_ast, context, *columns).getActions(true); - primary_key_sample = projected_expr->getSampleBlock(); - - merge_threads = new boost::threadpool::pool(settings.merging_threads); - - loadDataParts(); - clearOldParts(); - - UInt64 max_part_id = 0; - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - max_part_id = std::max(max_part_id, (*it)->right); - } - increment.fixIfBroken(max_part_id); -} +StorageMergeTree::StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + size_t index_granularity_, + MergeTreeData::Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_) + : data( thisPtr(), path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, + index_granularity_,mode_, sign_column_, settings_) {} StoragePtr StorageMergeTree::create( const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, - const String & date_column_name_, const ASTPtr & sampling_expression_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, size_t index_granularity_, - Mode mode_, + MergeTreeData::Mode mode_, const String & sign_column_, - const StorageMergeTreeSettings & settings_) + const MergeTreeSettings & settings_) { return (new StorageMergeTree( path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); } - void StorageMergeTree::shutdown() { - if (shutdown_called) - return; - shutdown_called = true; - - joinMergeThreads(); -} - - -StorageMergeTree::~StorageMergeTree() -{ - shutdown(); -} - - -BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) -{ - return new MergeTreeBlockOutputStream(thisPtr()); + data.shutdown(); } +StorageMergeTree::~StorageMergeTree() {} BlockInputStreams StorageMergeTree::read( - const Names & column_names_to_return, + const Names & column_names, ASTPtr query, const Settings & settings, QueryProcessingStage::Enum & processed_stage, size_t max_block_size, unsigned threads) { - Poco::ScopedReadRWLock lock(read_lock); - - check(column_names_to_return); - processed_stage = QueryProcessingStage::FetchColumns; - - PKCondition key_condition(query, context, *columns, sort_descr); - PKCondition date_condition(query, context, *columns, SortDescription(1, SortColumnDescription(date_column_name, 1))); - - typedef std::vector PartsList; - PartsList parts; - - /// Выберем куски, в которых могут быть данные, удовлетворяющие date_condition. - { - Poco::ScopedLock lock(data_parts_mutex); - - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - Field left = static_cast((*it)->left_date); - Field right = static_cast((*it)->right_date); - - if (date_condition.mayBeTrueInRange(&left, &right)) - parts.push_back(*it); - } - } - - /// Семплирование. - Names column_names_to_read = column_names_to_return; - UInt64 sampling_column_value_limit = 0; - typedef Poco::SharedPtr ASTFunctionPtr; - ASTFunctionPtr filter_function; - ExpressionActionsPtr filter_expression; - - ASTSelectQuery & select = *dynamic_cast(&*query); - if (select.sample_size) - { - double size = apply_visitor(FieldVisitorConvertToNumber(), - dynamic_cast(*select.sample_size).value); - - if (size < 0) - throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND); - - if (size > 1) - { - size_t requested_count = apply_visitor(FieldVisitorConvertToNumber(), dynamic_cast(*select.sample_size).value); - - /// Узнаем, сколько строк мы бы прочли без семплирования. - LOG_DEBUG(log, "Preliminary index scan with condition: " << key_condition.toString()); - size_t total_count = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - DataPartPtr & part = parts[i]; - MarkRanges ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); - - for (size_t j = 0; j < ranges.size(); ++j) - total_count += ranges[j].end - ranges[j].begin; - } - total_count *= index_granularity; - - size = std::min(1., static_cast(requested_count) / total_count); - - LOG_DEBUG(log, "Selected relative sample size: " << size); - } - - UInt64 sampling_column_max = 0; - DataTypePtr type = primary_expr->getSampleBlock().getByName(sampling_expression->getColumnName()).type; - - if (type->getName() == "UInt64") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt32") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt16") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt8") - sampling_column_max = std::numeric_limits::max(); - else - throw Exception("Invalid sampling column type in storage parameters: " + type->getName() + ". Must be unsigned integer type.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); - - /// Добавим условие, чтобы отсечь еще что-нибудь при повторном просмотре индекса. - sampling_column_value_limit = static_cast(size * sampling_column_max); - if (!key_condition.addCondition(sampling_expression->getColumnName(), - Range::createRightBounded(sampling_column_value_limit, true))) - throw Exception("Sampling column not in primary key", ErrorCodes::ILLEGAL_COLUMN); - - /// Выражение для фильтрации: sampling_expression <= sampling_column_value_limit - - ASTPtr filter_function_args = new ASTExpressionList; - filter_function_args->children.push_back(sampling_expression); - filter_function_args->children.push_back(new ASTLiteral(StringRange(), sampling_column_value_limit)); - - filter_function = new ASTFunction; - filter_function->name = "lessOrEquals"; - filter_function->arguments = filter_function_args; - filter_function->children.push_back(filter_function->arguments); - - filter_expression = ExpressionAnalyzer(filter_function, context, *columns).getActions(false); - - /// Добавим столбцы, нужные для sampling_expression. - std::vector add_columns = filter_expression->getRequiredColumns(); - column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); - std::sort(column_names_to_read.begin(), column_names_to_read.end()); - column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); - } - - LOG_DEBUG(log, "Key condition: " << key_condition.toString()); - LOG_DEBUG(log, "Date condition: " << date_condition.toString()); - - /// PREWHERE - ExpressionActionsPtr prewhere_actions; - String prewhere_column; - if (select.prewhere_expression) - { - ExpressionAnalyzer analyzer(select.prewhere_expression, context, *columns); - prewhere_actions = analyzer.getActions(false); - prewhere_column = select.prewhere_expression->getColumnName(); - } - - RangesInDataParts parts_with_ranges; - - /// Найдем, какой диапазон читать из каждого куска. - size_t sum_marks = 0; - size_t sum_ranges = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - DataPartPtr & part = parts[i]; - RangesInDataPart ranges(part); - ranges.ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); - - if (!ranges.ranges.empty()) - { - parts_with_ranges.push_back(ranges); - - sum_ranges += ranges.ranges.size(); - for (size_t j = 0; j < ranges.ranges.size(); ++j) - { - sum_marks += ranges.ranges[j].end - ranges.ranges[j].begin; - } - } - } - - LOG_DEBUG(log, "Selected " << parts.size() << " parts by date, " << parts_with_ranges.size() << " parts by key, " - << sum_marks << " marks to read from " << sum_ranges << " ranges"); - - BlockInputStreams res; - - if (select.final) - { - /// Добавим столбцы, нужные для вычисления первичного ключа и знака. - std::vector add_columns = primary_expr->getRequiredColumns(); - column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); - column_names_to_read.push_back(sign_column); - std::sort(column_names_to_read.begin(), column_names_to_read.end()); - column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); - - res = spreadMarkRangesAmongThreadsFinal( - parts_with_ranges, - threads, - column_names_to_read, - max_block_size, - settings.use_uncompressed_cache, - prewhere_actions, - prewhere_column); - } - else - { - res = spreadMarkRangesAmongThreads( - parts_with_ranges, - threads, - column_names_to_read, - max_block_size, - settings.use_uncompressed_cache, - prewhere_actions, - prewhere_column); - } - - if (select.sample_size) - { - for (size_t i = 0; i < res.size(); ++i) - { - BlockInputStreamPtr original_stream = res[i]; - BlockInputStreamPtr expression_stream = new ExpressionBlockInputStream(original_stream, filter_expression); - BlockInputStreamPtr filter_stream = new FilterBlockInputStream(expression_stream, filter_function->getColumnName()); - res[i] = filter_stream; - } - } - - return res; + return data.read(column_names, query, settings, processed_stage, max_block_size, threads); } - -/// Примерно поровну распределить засечки между потоками. -BlockInputStreams StorageMergeTree::spreadMarkRangesAmongThreads( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column) +BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) { - /// На всякий случай перемешаем куски. - std::random_shuffle(parts.begin(), parts.end()); - - /// Посчитаем засечки для каждого куска. - std::vector sum_marks_in_parts(parts.size()); - size_t sum_marks = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - /// Пусть отрезки будут перечислены справа налево, чтобы можно было выбрасывать самый левый отрезок с помощью pop_back(). - std::reverse(parts[i].ranges.begin(), parts[i].ranges.end()); - - sum_marks_in_parts[i] = 0; - for (size_t j = 0; j < parts[i].ranges.size(); ++j) - { - MarkRange & range = parts[i].ranges[j]; - sum_marks_in_parts[i] += range.end - range.begin; - } - sum_marks += sum_marks_in_parts[i]; - } - - if (sum_marks > max_marks_to_use_cache) - use_uncompressed_cache = false; - - BlockInputStreams res; - - if (sum_marks > 0) - { - size_t min_marks_per_thread = (sum_marks - 1) / threads + 1; - - for (size_t i = 0; i < threads && !parts.empty(); ++i) - { - size_t need_marks = min_marks_per_thread; - BlockInputStreams streams; - - /// Цикл по кускам. - while (need_marks > 0 && !parts.empty()) - { - RangesInDataPart & part = parts.back(); - size_t & marks_in_part = sum_marks_in_parts.back(); - - /// Не будем брать из куска слишком мало строк. - if (marks_in_part >= min_marks_for_concurrent_read && - need_marks < min_marks_for_concurrent_read) - need_marks = min_marks_for_concurrent_read; - - /// Не будем оставлять в куске слишком мало строк. - if (marks_in_part > need_marks && - marks_in_part - need_marks < min_marks_for_concurrent_read) - need_marks = marks_in_part; - - /// Возьмем весь кусок, если он достаточно мал. - if (marks_in_part <= need_marks) - { - /// Восстановим порядок отрезков. - std::reverse(part.ranges.begin(), part.ranges.end()); - - streams.push_back(new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, part.ranges, thisPtr(), use_uncompressed_cache, - prewhere_actions, prewhere_column)); - need_marks -= marks_in_part; - parts.pop_back(); - sum_marks_in_parts.pop_back(); - continue; - } - - MarkRanges ranges_to_get_from_part; - - /// Цикл по отрезкам куска. - while (need_marks > 0) - { - if (part.ranges.empty()) - throw Exception("Unexpected end of ranges while spreading marks among threads", ErrorCodes::LOGICAL_ERROR); - - MarkRange & range = part.ranges.back(); - size_t marks_in_range = range.end - range.begin; - - size_t marks_to_get_from_range = std::min(marks_in_range, need_marks); - ranges_to_get_from_part.push_back(MarkRange(range.begin, range.begin + marks_to_get_from_range)); - range.begin += marks_to_get_from_range; - marks_in_part -= marks_to_get_from_range; - need_marks -= marks_to_get_from_range; - if (range.begin == range.end) - part.ranges.pop_back(); - } - - streams.push_back(new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, ranges_to_get_from_part, thisPtr(), use_uncompressed_cache, - prewhere_actions, prewhere_column)); - } - - if (streams.size() == 1) - res.push_back(streams[0]); - else - res.push_back(new ConcatBlockInputStream(streams)); - } - - if (!parts.empty()) - throw Exception("Couldn't spread marks among threads", ErrorCodes::LOGICAL_ERROR); - } - - return res; + return data.write(query); } - -/// Распределить засечки между потоками и сделать, чтобы в ответе (почти) все данные были сколлапсированы (модификатор FINAL). -BlockInputStreams StorageMergeTree::spreadMarkRangesAmongThreadsFinal( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column) -{ - size_t sum_marks = 0; - for (size_t i = 0; i < parts.size(); ++i) - for (size_t j = 0; j < parts[i].ranges.size(); ++j) - sum_marks += parts[i].ranges[j].end - parts[i].ranges[j].begin; - - if (sum_marks > max_marks_to_use_cache) - use_uncompressed_cache = false; - - ExpressionActionsPtr sign_filter_expression; - String sign_filter_column; - createPositiveSignCondition(sign_filter_expression, sign_filter_column); - - BlockInputStreams to_collapse; - - for (size_t part_index = 0; part_index < parts.size(); ++part_index) - { - RangesInDataPart & part = parts[part_index]; - - BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, part.ranges, thisPtr(), use_uncompressed_cache, - prewhere_actions, prewhere_column); - - to_collapse.push_back(new ExpressionBlockInputStream(source_stream, primary_expr)); - } - - BlockInputStreams res; - if (to_collapse.size() == 1) - res.push_back(new FilterBlockInputStream(new ExpressionBlockInputStream(to_collapse[0], sign_filter_expression), sign_filter_column)); - else if (to_collapse.size() > 1) - res.push_back(new CollapsingFinalBlockInputStream(to_collapse, sort_descr, sign_column)); - - return res; -} - - -void StorageMergeTree::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column) -{ - ASTFunction * function = new ASTFunction; - ASTPtr function_ptr = function; - - ASTExpressionList * arguments = new ASTExpressionList; - ASTPtr arguments_ptr = arguments; - - ASTIdentifier * sign = new ASTIdentifier; - ASTPtr sign_ptr = sign; - - ASTLiteral * one = new ASTLiteral; - ASTPtr one_ptr = one; - - function->name = "equals"; - function->arguments = arguments_ptr; - function->children.push_back(arguments_ptr); - - arguments->children.push_back(sign_ptr); - arguments->children.push_back(one_ptr); - - sign->name = sign_column; - sign->kind = ASTIdentifier::Column; - - one->type = new DataTypeInt8; - one->value = Field(static_cast(1)); - - out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); - out_column = function->getColumnName(); -} - - -String StorageMergeTree::getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level) -{ - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - /// Имя директории для куска иммет вид: YYYYMMDD_YYYYMMDD_N_N_L. - String res; - { - unsigned left_date_id = Date2OrderedIdentifier(date_lut.fromDayNum(left_date)); - unsigned right_date_id = Date2OrderedIdentifier(date_lut.fromDayNum(right_date)); - - WriteBufferFromString wb(res); - - writeIntText(left_date_id, wb); - writeChar('_', wb); - writeIntText(right_date_id, wb); - writeChar('_', wb); - writeIntText(left_id, wb); - writeChar('_', wb); - writeIntText(right_id, wb); - writeChar('_', wb); - writeIntText(level, wb); - } - - return res; -} - - -void StorageMergeTree::parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part) -{ - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - part.left_date = date_lut.toDayNum(OrderedIdentifier2Date(file_name.substr(matches[1].offset, matches[1].length))); - part.right_date = date_lut.toDayNum(OrderedIdentifier2Date(file_name.substr(matches[2].offset, matches[2].length))); - part.left = parse(file_name.substr(matches[3].offset, matches[3].length)); - part.right = parse(file_name.substr(matches[4].offset, matches[4].length)); - part.level = parse(file_name.substr(matches[5].offset, matches[5].length)); - - part.left_month = date_lut.toFirstDayNumOfMonth(part.left_date); - part.right_month = date_lut.toFirstDayNumOfMonth(part.right_date); -} - - -void StorageMergeTree::loadDataParts() -{ - LOG_DEBUG(log, "Loading data parts"); - - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - - data_parts.clear(); - - Strings part_file_names; - Strings old_file_names; - Poco::DirectoryIterator end; - for (Poco::DirectoryIterator it(full_path); it != end; ++it) - { - String file_name = it.name(); - - /// Удаляем временные директории старше суток. - if (0 == file_name.compare(0, strlen("tmp_"), "tmp_")) - { - Poco::File tmp_dir(full_path + file_name); - - if (tmp_dir.isDirectory() && tmp_dir.getLastModified().epochTime() + 86400 < time(0)) - { - LOG_WARNING(log, "Removing temporary directory " << full_path << file_name); - Poco::File(full_path + file_name).remove(true); - } - - continue; - } - - if (0 == file_name.compare(0, strlen("old_"), "old_")) - old_file_names.push_back(file_name); - else - part_file_names.push_back(file_name); - } - - Poco::RegularExpression::MatchVec matches; - while (!part_file_names.empty()) - { - String file_name = part_file_names.back(); - part_file_names.pop_back(); - - if (!isPartDirectory(file_name, matches)) - continue; - - DataPartPtr part = new DataPart(*this); - parsePartName(file_name, matches, *part); - part->name = file_name; - - /// Для битых кусков, которые могут образовываться после грубого перезапуска сервера, попытаться восстановить куски, из которых они сделаны. - if (isBrokenPart(full_path + file_name)) - { - if (part->level == 0) - { - /// Восстановить куски нулевого уровня невозможно. - LOG_ERROR(log, "Removing broken part " << path + file_name << " because is't impossible to repair."); - part->remove(); - } - else - { - Strings new_parts = tryRestorePart(full_path, file_name, old_file_names); - part_file_names.insert(part_file_names.begin(), new_parts.begin(), new_parts.end()); - } - - continue; - } - - /// Размер - в количестве засечек. - part->size = Poco::File(full_path + file_name + "/" + escapeForFileName(columns->front().first) + ".mrk").getSize() - / MERGE_TREE_MARK_SIZE; - - part->modification_time = Poco::File(full_path + file_name).getLastModified().epochTime(); - - try - { - part->loadIndex(); - } - catch (...) - { - /// Не будем вставлять в набор кусок с битым индексом. Пропустим кусок и позволим серверу запуститься. - tryLogCurrentException(__PRETTY_FUNCTION__); - continue; - } - - data_parts.insert(part); - } - - all_data_parts = data_parts; - - /** Удаляем из набора актуальных кусков куски, которые содержатся в другом куске (которые были склеены), - * но по каким-то причинам остались лежать в файловой системе. - * Удаление файлов будет произведено потом в методе clearOldParts. - */ - - if (data_parts.size() >= 2) - { - DataParts::iterator prev_jt = data_parts.begin(); - DataParts::iterator curr_jt = prev_jt; - ++curr_jt; - while (curr_jt != data_parts.end()) - { - /// Куски данных за разные месяцы рассматривать не будем - if ((*curr_jt)->left_month != (*curr_jt)->right_month - || (*curr_jt)->right_month != (*prev_jt)->left_month - || (*prev_jt)->left_month != (*prev_jt)->right_month) - { - ++prev_jt; - ++curr_jt; - continue; - } - - if ((*curr_jt)->contains(**prev_jt)) - { - LOG_WARNING(log, "Part " << (*curr_jt)->name << " contains " << (*prev_jt)->name); - data_parts.erase(prev_jt); - prev_jt = curr_jt; - ++curr_jt; - } - else if ((*prev_jt)->contains(**curr_jt)) - { - LOG_WARNING(log, "Part " << (*prev_jt)->name << " contains " << (*curr_jt)->name); - data_parts.erase(curr_jt++); - } - else - { - ++prev_jt; - ++curr_jt; - } - } - } - - LOG_DEBUG(log, "Loaded data parts (" << data_parts.size() << " items)"); -} - - -void StorageMergeTree::clearOldParts() -{ - Poco::ScopedTry lock; - - /// Если метод уже вызван из другого потока (или если all_data_parts прямо сейчас меняют), то можно ничего не делать. - if (!lock.lock(&all_data_parts_mutex)) - { - LOG_TRACE(log, "Already clearing or modifying old parts"); - return; - } - - LOG_TRACE(log, "Clearing old parts"); - for (DataParts::iterator it = all_data_parts.begin(); it != all_data_parts.end();) - { - int ref_count = it->referenceCount(); - if (ref_count == 1) /// После этого ref_count не может увеличиться. - { - LOG_DEBUG(log, "'Removing' part " << (*it)->name << " (prepending old_ to its name)"); - - (*it)->renameToOld(); - all_data_parts.erase(it++); - } - else - ++it; - } - - /// Удалим старые old_ куски. - Poco::DirectoryIterator end; - for (Poco::DirectoryIterator it(full_path); it != end; ++it) - { - if (0 != it.name().compare(0, strlen("old_"), "old_")) - continue; - if (it->isDirectory() && it->getLastModified().epochTime() + settings.old_parts_lifetime < time(0)) - { - it->remove(true); - } - } -} - - -void StorageMergeTree::merge(size_t iterations, bool async, bool aggressive) -{ - bool while_can = false; - if (iterations == 0) - { - while_can = true; - iterations = settings.merging_threads; - } - - for (size_t i = 0; i < iterations; ++i) - merge_threads->schedule(boost::bind(&StorageMergeTree::mergeThread, this, while_can, aggressive)); - - if (!async) - joinMergeThreads(); -} - - -void StorageMergeTree::mergeThread(bool while_can, bool aggressive) -{ - try - { - while (!shutdown_called) - { - /// Удаляем старые куски. На случай, если в слиянии что-то сломано, и из следующего блока вылетит исключение. - clearOldParts(); - - { - Poco::ScopedReadRWLock lock(merge_lock); - - /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски - Poco::SharedPtr what; - - if (!selectPartsToMerge(what, false, aggressive) && !selectPartsToMerge(what, true, aggressive)) - break; - - mergeParts(what); - } - - if (shutdown_called) - break; - - /// Удаляем куски, которые мы только что сливали. - clearOldParts(); - - if (!while_can) - break; - } - } - catch (const Exception & e) - { - LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTrace().toString()); - } - catch (const Poco::Exception & e) - { - LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); - } - catch (const std::exception & e) - { - LOG_ERROR(log, "std::exception: " << e.what()); - } - catch (...) - { - LOG_ERROR(log, "Unknown exception"); - } -} - - -void StorageMergeTree::joinMergeThreads() -{ - LOG_DEBUG(log, "Waiting for merge threads to finish."); - merge_threads->wait(); -} - - -/// Выбираем отрезок из не более чем max_parts_to_merge_at_once кусков так, чтобы максимальный размер был меньше чем в max_size_ratio_to_merge_parts раз больше суммы остальных. -/// Это обеспечивает в худшем случае время O(n log n) на все слияния, независимо от выбора сливаемых кусков, порядка слияния и добавления. -/// При max_parts_to_merge_at_once >= log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts), -/// несложно доказать, что всегда будет что сливать, пока количество кусков больше -/// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts)*(количество кусков размером больше max_rows_to_merge_parts). -/// Дальше эвристики. -/// Будем выбирать максимальный по включению подходящий отрезок. -/// Из всех таких выбираем отрезок с минимальным максимумом размера. -/// Из всех таких выбираем отрезок с минимальным минимумом размера. -/// Из всех таких выбираем отрезок с максимальной длиной. -/// Дополнительно: -/// 1) с 1:00 до 5:00 ограничение сверху на размер куска в основном потоке увеличивается в несколько раз -/// 2) в зависимоти от возраста кусков меняется допустимая неравномерность при слиянии -/// 3) Молодые куски крупного размера (примерно больше 1 Гб) можно сливать не меньше чем по три -/// 4) Если в одном из потоков идет мердж крупных кусков, то во втором сливать только маленькие кусочки -/// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности - -bool StorageMergeTree::selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive) -{ - LOG_DEBUG(log, "Selecting parts to merge"); - - Poco::ScopedLock lock(data_parts_mutex); - - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - size_t min_max = -1U; - size_t min_min = -1U; - int max_len = 0; - DataParts::iterator best_begin; - bool found = false; - - DayNum_t now_day = date_lut.toDayNum(time(0)); - DayNum_t now_month = date_lut.toFirstDayNumOfMonth(now_day); - int now_hour = date_lut.toHourInaccurate(time(0)); - - size_t maybe_used_bytes = total_size_of_currently_merging_parts; - size_t total_free_bytes = 0; - struct statvfs fs; - - /// Смотрим количество свободного места в файловой системе - if (statvfs(full_path.c_str(), &fs) != 0) - throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS); - - total_free_bytes = fs.f_bfree * fs.f_bsize; - - /// Сколько кусков, начиная с текущего, можно включить в валидный отрезок, начинающийся левее текущего куска. - /// Нужно для определения максимальности по включению. - int max_count_from_left = 0; - - size_t cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts; - - /// Если ночь, можем мерджить сильно большие куски - if (now_hour >= 1 && now_hour <= 5) - cur_max_rows_to_merge_parts *= settings.merge_parts_at_night_inc; - - /// Если есть активный мердж крупных кусков, то ограничаемся мерджем только маленьких частей. - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - if ((*it)->currently_merging && (*it)->size * index_granularity > 25 * 1024 * 1024) - { - cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts_second; - break; - } - } - - /// Левый конец отрезка. - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - const DataPartPtr & first_part = *it; - - max_count_from_left = std::max(0, max_count_from_left - 1); - - /// Кусок не занят. - if (first_part->currently_merging) - continue; - - /// Кусок достаточно мал или слияние "агрессивное". - if (first_part->size * index_granularity > cur_max_rows_to_merge_parts - && !aggressive) - continue; - - /// Кусок в одном месяце. - if (first_part->left_month != first_part->right_month) - { - LOG_WARNING(log, "Part " << first_part->name << " spans more than one month"); - continue; - } - - /// Самый длинный валидный отрезок, начинающийся здесь. - size_t cur_longest_max = -1U; - size_t cur_longest_min = -1U; - int cur_longest_len = 0; - - /// Текущий отрезок, не обязательно валидный. - size_t cur_max = first_part->size; - size_t cur_min = first_part->size; - size_t cur_sum = first_part->size; - size_t cur_total_size = first_part->size_in_bytes; - int cur_len = 1; - - DayNum_t month = first_part->left_month; - UInt64 cur_id = first_part->right; - - /// Этот месяц кончился хотя бы день назад. - bool is_old_month = now_day - now_month >= 1 && now_month > month; - - time_t oldest_modification_time = first_part->modification_time; - - /// Правый конец отрезка. - DataParts::iterator jt = it; - for (++jt; jt != data_parts.end() && cur_len < static_cast(settings.max_parts_to_merge_at_once); ++jt) - { - const DataPartPtr & last_part = *jt; - - /// Кусок не занят и в одном правильном месяце. - if (last_part->currently_merging || - last_part->left_month != last_part->right_month || - last_part->left_month != month) - break; - - /// Кусок достаточно мал или слияние "агрессивное". - if (last_part->size * index_granularity > cur_max_rows_to_merge_parts - && !aggressive) - break; - - /// Кусок правее предыдущего. - if (last_part->left < cur_id) - { - LOG_WARNING(log, "Part " << last_part->name << " intersects previous part"); - break; - } - - oldest_modification_time = std::max(oldest_modification_time, last_part->modification_time); - cur_max = std::max(cur_max, last_part->size); - cur_min = std::min(cur_min, last_part->size); - cur_sum += last_part->size; - cur_total_size += last_part->size_in_bytes; - ++cur_len; - cur_id = last_part->right; - - int min_len = 2; - int cur_age_in_sec = time(0) - oldest_modification_time; - - /// Если куски примерно больше 1 Gb и образовались меньше 6 часов назад, то мерджить не меньше чем по 3. - if (cur_max * index_granularity * 150 > 1024*1024*1024 && cur_age_in_sec < 6*3600) - min_len = 3; - - /// Равен 0.5 если возраст порядка 0, равен 5 если возраст около месяца. - double time_ratio_modifier = 0.5 + 9 * static_cast(cur_age_in_sec) / (3600*24*30 + cur_age_in_sec); - - /// Двоичный логарифм суммарного размера кусочков - double log_cur_sum = std::log(cur_sum * index_granularity) / std::log(2); - /// Равен ~2 если куски маленькие, уменьшается до 0.5 с увеличением суммарного размера до 2^25. - double size_ratio_modifier = std::max(0.25, 2 - 3 * (log_cur_sum) / (25 + log_cur_sum)); - - /// Объединяем все в одну константу - double ratio = std::max(0.5, time_ratio_modifier * size_ratio_modifier * settings.max_size_ratio_to_merge_parts); - - /// Если отрезок валидный, то он самый длинный валидный, начинающийся тут. - if (cur_len >= min_len - && (static_cast(cur_max) / (cur_sum - cur_max) < ratio - /// За старый месяц объединяем что угодно, если разрешено и если этому хотя бы 15 дней - || (is_old_month && merge_anything_for_old_months && cur_age_in_sec > 3600*24*15) - /// Если слияние "агрессивное", то сливаем что угодно - || aggressive)) - { - /// Достаточно места на диске, чтобы покрыть уже активные и новый мерджи с запасом в 50% - if (total_free_bytes > (maybe_used_bytes + cur_total_size) * 1.5) - { - cur_longest_max = cur_max; - cur_longest_min = cur_min; - cur_longest_len = cur_len; - } - else - LOG_WARNING(log, "Won't merge parts from " << first_part->name << " to " << last_part->name - << " because not enough free space: " << total_free_bytes << " free, " - << maybe_used_bytes << " already involved in merge, " - << cur_total_size << " required now (+50% on overhead)"); - } - } - - /// Это максимальный по включению валидный отрезок. - if (cur_longest_len > max_count_from_left) - { - max_count_from_left = cur_longest_len; - - if (!found - || std::make_pair(std::make_pair(cur_longest_max, cur_longest_min), -cur_longest_len) - < std::make_pair(std::make_pair(min_max, min_min), -max_len)) - { - found = true; - min_max = cur_longest_max; - min_min = cur_longest_min; - max_len = cur_longest_len; - best_begin = it; - } - } - } - - if (found) - { - std::vector parts; - - DataParts::iterator it = best_begin; - for (int i = 0; i < max_len; ++i) - { - parts.push_back(*it); - ++it; - } - what = new CurrentlyMergingPartsTagger(parts, data_parts_mutex); - - LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); - } - else - { - LOG_DEBUG(log, "No parts to merge"); - } - - return found; -} - - -/// parts должны быть отсортированы. -void StorageMergeTree::mergeParts(Poco::SharedPtr & what) -{ - const std::vector & parts(what->parts); - - LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); - - Names all_column_names; - for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it) - all_column_names.push_back(it->first); - - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - StorageMergeTree::DataPartPtr new_data_part = new DataPart(*this); - new_data_part->left_date = std::numeric_limits::max(); - new_data_part->right_date = std::numeric_limits::min(); - new_data_part->left = parts.front()->left; - new_data_part->right = parts.back()->right; - new_data_part->level = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - new_data_part->level = std::max(new_data_part->level, parts[i]->level); - new_data_part->left_date = std::min(new_data_part->left_date, parts[i]->left_date); - new_data_part->right_date = std::max(new_data_part->right_date, parts[i]->right_date); - } - ++new_data_part->level; - new_data_part->name = getPartName( - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); - new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); - new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); - - /** Читаем из всех кусков, сливаем и пишем в новый. - * Попутно вычисляем выражение для сортировки. - */ - BlockInputStreams src_streams; - - for (size_t i = 0; i < parts.size(); ++i) - { - MarkRanges ranges(1, MarkRange(0, parts[i]->size)); - src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( - full_path + parts[i]->name + '/', DEFAULT_MERGE_BLOCK_SIZE, all_column_names, *this, parts[i], ranges, - StoragePtr(), false, NULL, ""), primary_expr)); - } - - /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. - /// В слитом куске строки с одинаковым ключом должны идти в порядке возрастания идентификатора исходного куска, то есть (примерного) возрастания времени вставки. - BlockInputStreamPtr merged_stream; - - switch (mode) - { - case Ordinary: - merged_stream = new MergingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); - break; - - case Collapsing: - merged_stream = new CollapsingSortedBlockInputStream(src_streams, sort_descr, sign_column, DEFAULT_MERGE_BLOCK_SIZE); - break; - - case Summing: - merged_stream = new SummingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); - break; - - default: - throw Exception("Unknown mode of operation for StorageMergeTree: " + toString(mode), ErrorCodes::LOGICAL_ERROR); - } - - MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(*this, - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); - - merged_stream->readPrefix(); - to->writePrefix(); - - Block block; - while (!shutdown_called && (block = merged_stream->read())) - to->write(block); - - if (shutdown_called) - { - LOG_INFO(log, "Shutdown requested while merging parts."); - return; - } - - merged_stream->readSuffix(); - to->writeSuffix(); - - /// В обычном режиме строчки не могут удалиться при мердже. - if (0 == to->marksCount() && mode == Ordinary) - throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); - - new_data_part->size = to->marksCount(); - new_data_part->modification_time = time(0); - - if (0 != to->marksCount()) - new_data_part->loadIndex(); /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи. - - { - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - - /// Добавляем новый кусок в набор, если он не пустой. - - for (size_t i = 0; i < parts.size(); ++i) - if (data_parts.end() == data_parts.find(parts[i])) - throw Exception("Logical error: cannot find data part " + parts[i]->name + " in list", ErrorCodes::LOGICAL_ERROR); - - if (0 == to->marksCount()) - { - LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); - } - else - { - data_parts.insert(new_data_part); - all_data_parts.insert(new_data_part); - } - - for (size_t i = 0; i < parts.size(); ++i) - data_parts.erase(data_parts.find(parts[i])); - } - - LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); -} - - -void StorageMergeTree::rename(const String & new_path_to_db, const String & new_name) -{ - joinMergeThreads(); - - /// Кажется тут race condition - в этот момент мердж может запуститься снова. - - std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; - - Poco::File(full_path).renameTo(new_full_path); - - path = new_path_to_db; - full_path = new_full_path; - name = new_name; - - increment.setPath(full_path + "increment.txt"); -} - - void StorageMergeTree::dropImpl() { - joinMergeThreads(); - - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - - data_parts.clear(); - all_data_parts.clear(); - - Poco::File(full_path).remove(true); + data.dropImpl(); } -void StorageMergeTree::removeColumnFiles(String column_name) +void StorageMergeTree::rename(const String & new_path_to_db, const String & new_name) { - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - - /// Регэксп выбирает файлы столбца для удаления - Poco::RegularExpression re(column_name + "(?:(?:\\.|\\%2E).+){0,1}" +"(?:\\.mrk|\\.bin|\\.size\\d+\\.bin|\\.size\\d+\\.mrk)"); - /// Цикл по всем директориям кусочков - Poco::RegularExpression::MatchVec matches; - Poco::DirectoryIterator end; - for (Poco::DirectoryIterator it_dir = Poco::DirectoryIterator(full_path); it_dir != end; ++it_dir) - { - std::string dir_name = it_dir.name(); - - if (!isPartDirectory(dir_name, matches)) - continue; - - /// Цикл по каждому из файлов в директории кусочков - String full_dir_name = full_path + dir_name + "/"; - for (Poco::DirectoryIterator it_file(full_dir_name); it_file != end; ++it_file) - { - if (re.match(it_file.name())) - { - Poco::File file(full_dir_name + it_file.name()); - if (file.exists()) - file.remove(); - } - } - } -} - -void StorageMergeTree::createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column) -{ - ASTFunction * function = new ASTFunction; - ASTPtr function_ptr = function; - - ASTExpressionList * arguments = new ASTExpressionList; - ASTPtr arguments_ptr = arguments; - - function->name = "to" + out_type; - function->arguments = arguments_ptr; - function->children.push_back(arguments_ptr); - - ASTIdentifier * in_column = new ASTIdentifier; - ASTPtr in_column_ptr = in_column; - - arguments->children.push_back(in_column_ptr); - - in_column->name = in_column_name; - in_column->kind = ASTIdentifier::Column; - - out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); - out_column = function->getColumnName(); + data.rename(new_path_to_db, new_name); } void StorageMergeTree::alter(const ASTAlterQuery::Parameters & params) { - if (params.type == ASTAlterQuery::MODIFY) - { - { - typedef std::vector PartsList; - PartsList parts; - { - Poco::ScopedLock lock(data_parts_mutex); - for (auto & data_part : data_parts) - { - parts.push_back(data_part); - } - } - - Names column_name; - const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); - StringRange type_range = name_type.type->range; - String type(type_range.first, type_range.second - type_range.first); - DB::DataTypePtr old_type_ptr = getDataTypeByName(name_type.name); - DB::DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); - if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || - dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) - throw DB::Exception("ALTER MODIFY not supported for nested and array types"); - - column_name.push_back(name_type.name); - DB::ExpressionActionsPtr expr; - String out_column; - createConvertExpression(name_type.name, type, expr, out_column); - - ColumnNumbers num(1, 0); - for (DataPartPtr & part : parts) - { - MarkRanges ranges(1, MarkRange(0, part->size)); - ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', - DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, StoragePtr(), false, NULL, ""), expr); - MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); - out.writePrefix(); - - try - { - while(DB::Block b = in.read()) - { - /// оставляем только столбец с результатом - b.erase(0); - out.write(b); - } - LOG_TRACE(log, "Write Suffix"); - out.writeSuffix(); - } - catch (const Exception & e) - { - if (e.code() != ErrorCodes::ALL_REQUESTED_COLUMNS_ARE_MISSING) - throw; - } - } - - /// переименовываем файлы - /// переименовываем старые столбцы, добавляя расширение .old - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin").exists()) - { - LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << path + ".bin" + ".old"); - Poco::File(path + ".bin").renameTo(path + ".bin" + ".old"); - LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << path + ".mrk" + ".old"); - Poco::File(path + ".mrk").renameTo(path + ".mrk" + ".old"); - } - } - - /// переименовываем временные столбцы - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(out_column); - std::string new_path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin").exists()) - { - LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << new_path + ".bin"); - Poco::File(path + ".bin").renameTo(new_path + ".bin"); - LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << new_path + ".mrk"); - Poco::File(path + ".mrk").renameTo(new_path + ".mrk"); - } - } - - // удаляем старые столбцы - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin" + ".old").exists()) - { - LOG_TRACE(log, "Removing old column " << path + ".bin" + ".old"); - Poco::File(path + ".bin" + ".old").remove(); - LOG_TRACE(log, "Removing old column " << path + ".mrk" + ".old"); - Poco::File(path + ".mrk" + ".old").remove(); - } - } - } - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); - } - { - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - alterColumns(params, columns, context); - } - if (params.type == ASTAlterQuery::DROP) - { - String column_name = dynamic_cast(*params.column).name; - removeColumnFiles(column_name); - } -} - - -bool StorageMergeTree::isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const -{ - return (file_name_regexp.match(dir_name, 0, matches) && 6 == matches.size()); -} - - -bool StorageMergeTree::isBrokenPart(const String & path) -{ - /// Проверяем, что первичный ключ непуст. - - Poco::File index_file(path + "/primary.idx"); - - if (!index_file.exists() || index_file.getSize() == 0) - { - LOG_ERROR(log, "Part " << path << " is broken: primary key is empty."); - - return true; - } - - /// Проверяем, что все засечки непусты и имеют одинаковый размер. - - ssize_t marks_size = -1; - for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it) - { - Poco::File marks_file(path + "/" + escapeForFileName(it->first) + ".mrk"); - - /// при добавлении нового столбца в таблицу файлы .mrk не создаются. Не будем ничего удалять. - if (!marks_file.exists()) - continue; - - if (marks_size == -1) - { - marks_size = marks_file.getSize(); - - if (0 == marks_size) - { - LOG_ERROR(log, "Part " << path << " is broken: " << marks_file.path() << " is empty."); - - return true; - } - } - else - { - if (static_cast(marks_file.getSize()) != marks_size) - { - LOG_ERROR(log, "Part " << path << " is broken: marks have different sizes."); - - return true; - } - } - } - - return false; -} - -Strings StorageMergeTree::tryRestorePart(const String & path, const String & file_name, Strings & old_parts) -{ - LOG_ERROR(log, "Restoring all old_ parts covered by " << file_name); - - Poco::RegularExpression::MatchVec matches; - Strings restored_parts; - - isPartDirectory(file_name, matches); - DataPart broken_part(*this); - parsePartName(file_name, matches, broken_part); - - for (int i = static_cast(old_parts.size()) - 1; i >= 0; --i) - { - DataPart old_part(*this); - String name = old_parts[i].substr(strlen("old_")); - if (!isPartDirectory(name, matches)) - { - LOG_ERROR(log, "Strange file name: " << path + old_parts[i] << "; ignoring"); - old_parts.erase(old_parts.begin() + i); - continue; - } - parsePartName(name, matches, old_part); - if (broken_part.contains(old_part)) - { - /// Восстанавливаем все содержащиеся куски. Если некоторые из них содержатся в других, их удалит loadDataParts. - LOG_ERROR(log, "Restoring part " << path + old_parts[i]); - Poco::File(path + old_parts[i]).renameTo(path + name); - old_parts.erase(old_parts.begin() + i); - restored_parts.push_back(name); - } - } - - if (restored_parts.size() >= 2) - { - LOG_ERROR(log, "Removing broken part " << path + file_name << " because at least 2 old_ parts were restored in its place"); - Poco::File(path + file_name).remove(true); - } - else - { - LOG_ERROR(log, "Not removing broken part " << path + file_name - << " because less than 2 old_ parts were restored in its place. You need to resolve this manually"); - } - - return restored_parts; + data.alter(params); } } diff --git a/dbms/src/Storages/tests/MergeLogicTester.cpp b/dbms/src/Storages/tests/MergeLogicTester.cpp index 6719da88dfb..e03b503c07d 100644 --- a/dbms/src/Storages/tests/MergeLogicTester.cpp +++ b/dbms/src/Storages/tests/MergeLogicTester.cpp @@ -37,7 +37,7 @@ DataParts copy(const DataParts &a) const int RowsPerSec = 100000; -StorageMergeTreeSettings settings; +MergeTreeSettings settings; size_t index_granularity = 1; From 989a3162441533b1a59fda8469067e6aa767912d Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sun, 9 Mar 2014 21:58:27 +0400 Subject: [PATCH 003/281] Merge --- dbms/include/DB/Storages/IStorage.h | 3 ++- .../DB/Storages/MergeTree/MergeTreeData.h | 6 +++-- dbms/include/DB/Storages/StoragePtr.h | 22 ++++++++++++++++++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 2 +- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 2ce322f23d4..e78294bb44b 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace DB @@ -153,7 +154,7 @@ public: { if (!this_ptr.lock()) { - boost::shared_ptr p(new StoragePtr::Wrapper(this)); + auto p = boost::make_shared(this); this_ptr = p; return StoragePtr(this_ptr); } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 9c12ccd0419..ae1d323d7ba 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -140,8 +140,10 @@ public: * primary_expr_ast - выражение для сортировки; * date_column_name - имя столбца с датой; * index_granularity - на сколько строчек пишется одно значение индекса. + * + * owning_storage используется только чтобы отдавать его возвращаемым потокам блоков. */ - MergeTreeData( StoragePtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, + MergeTreeData( StorageWeakPtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, @@ -227,7 +229,7 @@ public: } private: - StoragePtr owning_storage; + StorageWeakPtr owning_storage; String path; String name; diff --git a/dbms/include/DB/Storages/StoragePtr.h b/dbms/include/DB/Storages/StoragePtr.h index b1ca8dc9f84..d19756c0105 100644 --- a/dbms/include/DB/Storages/StoragePtr.h +++ b/dbms/include/DB/Storages/StoragePtr.h @@ -11,7 +11,7 @@ namespace DB { class IStorage; - +class StorageWeakPtr; class StoragePtr { @@ -33,10 +33,12 @@ private: boost::shared_ptr ptr; friend class IStorage; + friend class StorageWeakPtr; public: StoragePtr() {} StoragePtr(const StoragePtr & p) : ptr(p.ptr) {} + StoragePtr(const StorageWeakPtr & p); StoragePtr& operator= (const StoragePtr & p) { @@ -83,4 +85,22 @@ public: } }; +class StorageWeakPtr +{ +public: + StorageWeakPtr(const StoragePtr & p) : ptr(p.ptr) {} + + StoragePtr lock() + { + return StoragePtr(ptr); + } + +private: + friend class StoragePtr; + + boost::weak_ptr ptr; +}; + +StoragePtr::StoragePtr(const StorageWeakPtr & p) : ptr(p.ptr) {} + } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 7644f8104dc..230a1512e7e 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -60,7 +60,7 @@ namespace DB size_t MergeTreeData::total_size_of_currently_merging_parts = 0; MergeTreeData::MergeTreeData( - StoragePtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, + StorageWeakPtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, From be8855dcd28d1867d014108227d37ae584e7d6a3 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sun, 9 Mar 2014 22:16:52 +0400 Subject: [PATCH 004/281] fixed build; not it segfaults :) [#METR-10202] --- dbms/include/DB/Storages/StoragePtr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/include/DB/Storages/StoragePtr.h b/dbms/include/DB/Storages/StoragePtr.h index d19756c0105..89aea28cb2d 100644 --- a/dbms/include/DB/Storages/StoragePtr.h +++ b/dbms/include/DB/Storages/StoragePtr.h @@ -38,7 +38,7 @@ private: public: StoragePtr() {} StoragePtr(const StoragePtr & p) : ptr(p.ptr) {} - StoragePtr(const StorageWeakPtr & p); + inline StoragePtr(const StorageWeakPtr & p); StoragePtr& operator= (const StoragePtr & p) { @@ -101,6 +101,6 @@ private: boost::weak_ptr ptr; }; -StoragePtr::StoragePtr(const StorageWeakPtr & p) : ptr(p.ptr) {} +inline StoragePtr::StoragePtr(const StorageWeakPtr & p) : ptr(p.ptr) {} } From 36a86a4fc94ae2ae11f7c3552955cbab25f849d9 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 13 Mar 2014 16:48:07 +0400 Subject: [PATCH 005/281] Merge --- .../DB/Storages/MergeTree/DiskSpaceMonitor.h | 87 ++ .../MergeTree/MergeTreeBlockInputStream.h | 80 +- .../MergeTree/MergeTreeBlockOutputStream.h | 31 + .../DB/Storages/MergeTree/MergeTreeData.h | 421 +++----- .../Storages/MergeTree/MergeTreeDataMerger.h | 44 + .../MergeTree/MergeTreeDataSelectExecutor.h | 78 ++ ...ckOutputStream.h => MergeTreeDataWriter.h} | 30 +- .../DB/Storages/MergeTree/MergeTreeReader.h | 30 +- .../MergeTree/MergedBlockOutputStream.h | 22 +- dbms/include/DB/Storages/StorageMergeTree.h | 52 +- dbms/include/DB/Storages/StoragePtr.h | 1 + .../Storages/MergeTree/DiskSpaceMonitor.cpp | 9 + dbms/src/Storages/MergeTree/MergeTreeData.cpp | 923 ++---------------- .../MergeTree/MergeTreeDataMerger.cpp | 339 +++++++ .../MergeTree/MergeTreeDataSelectExecutor.cpp | 472 +++++++++ .../MergeTree/MergeTreeDataWriter.cpp | 0 dbms/src/Storages/StorageMergeTree.cpp | 138 ++- 17 files changed, 1524 insertions(+), 1233 deletions(-) create mode 100644 dbms/include/DB/Storages/MergeTree/DiskSpaceMonitor.h create mode 100644 dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h create mode 100644 dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h create mode 100644 dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h rename dbms/include/DB/Storages/MergeTree/{MergeTreeDataBlockOutputStream.h => MergeTreeDataWriter.h} (86%) create mode 100644 dbms/src/Storages/MergeTree/DiskSpaceMonitor.cpp create mode 100644 dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp create mode 100644 dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp create mode 100644 dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp diff --git a/dbms/include/DB/Storages/MergeTree/DiskSpaceMonitor.h b/dbms/include/DB/Storages/MergeTree/DiskSpaceMonitor.h new file mode 100644 index 00000000000..f9e34822df8 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/DiskSpaceMonitor.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +/** Узнает количество свободного места в файловой системе. + * Можно "резервировать" место, чтобы разные операции могли согласованно планировать использование диска. + * Резервирования не разделяются по файловым системам. + * Вместо этого при запросе свободного места считается, что все резервирования сделаны в той же файловой системе. + */ +class DiskSpaceMonitor +{ +public: + class Reservation : private boost::noncopyable + { + friend class DiskSpaceMonitor; + public: + ~Reservation() + { + try + { + Poco::ScopedLock lock(DiskSpaceMonitor::reserved_bytes_mutex); + if (DiskSpaceMonitor::reserved_bytes < size) + { + DiskSpaceMonitor::reserved_bytes = 0; + LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservations; it's a bug"); + } + else + { + DiskSpaceMonitor::reserved_bytes -= size; + } + } + catch (...) + { + tryLogCurrentException("~DiskSpaceMonitor"); + } + } + private: + Reservation(size_t size_) : size(size_) + { + Poco::ScopedLock lock(DiskSpaceMonitor::reserved_bytes_mutex); + DiskSpaceMonitor::reserved_bytes += size; + } + size_t size; + }; + + typedef Poco::SharedPtr ReservationPtr; + + static size_t getUnreservedFreeSpace(const std::string & path) + { + struct statvfs fs; + + if (statvfs(path.c_str(), &fs) != 0) + throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS); + + size_t res = fs.f_bfree * fs.f_bsize; + + Poco::ScopedLock lock(reserved_bytes_mutex); + + if (reserved_bytes > res) + res = 0; + else + res -= reserved_bytes; + + return res; + } + + /// Если места (приблизительно) недостаточно, возвращает nullptr. + static ReservationPtr reserve(const std::string & path, size_t size) + { + if (getUnreservedFreeSpace(path) < size) + return nullptr; + return new Reservation(size); + } + +private: + static size_t reserved_bytes; + static Poco::FastMutex reserved_bytes_mutex; +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h index 18cf15862dd..c7ae24d26cf 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h @@ -3,7 +3,6 @@ #include #include #include - #include @@ -17,6 +16,7 @@ public: /// Параметры storage_ и owned_storage разделены, чтобы можно было сделать поток, не владеющий своим storage /// (например, поток, сливаящий куски). В таком случае сам storage должен следить, чтобы не удалить данные, пока их читают. MergeTreeBlockInputStream(const String & path_, /// Путь к куску + MergeTreeData::LockedTableStructurePtr structure_lock_, size_t block_size_, const Names & column_names_, MergeTreeData & storage_, const MergeTreeData::DataPartPtr & owned_data_part_, const MarkRanges & mark_ranges_, StoragePtr owned_storage, bool use_uncompressed_cache_, @@ -26,7 +26,9 @@ public: storage(storage_), owned_data_part(owned_data_part_), all_mark_ranges(mark_ranges_), remaining_mark_ranges(mark_ranges_), use_uncompressed_cache(use_uncompressed_cache_), - prewhere_actions(prewhere_actions_), prewhere_column(prewhere_column_) + prewhere_actions(prewhere_actions_), prewhere_column(prewhere_column_), + log(&Logger::get("MergeTreeBlockInputStream")), + structure_lock(structure_lock_) { std::reverse(remaining_mark_ranges.begin(), remaining_mark_ranges.end()); @@ -48,7 +50,7 @@ public: } column_name_set.insert(column_names.begin(), column_names.end()); - LOG_TRACE(storage.log, "Reading " << all_mark_ranges.size() << " ranges from part " << owned_data_part->name + LOG_TRACE(log, "Reading " << all_mark_ranges.size() << " ranges from part " << owned_data_part->name << ", up to " << (all_mark_ranges.back().end - all_mark_ranges.front().begin) * storage.index_granularity << " rows starting from " << all_mark_ranges.front().begin * storage.index_granularity); } @@ -72,70 +74,6 @@ public: return res.str(); } - /// Получает набор диапазонов засечек, вне которых не могут находиться ключи из заданного диапазона. - static MarkRanges markRangesFromPkRange( - const MergeTreeData::DataPart::Index & index, - MergeTreeData & storage, - PKCondition & key_condition) - { - MarkRanges res; - - size_t key_size = storage.sort_descr.size(); - size_t marks_count = index.size() / key_size; - - /// Если индекс не используется. - if (key_condition.alwaysTrue()) - { - res.push_back(MarkRange(0, marks_count)); - } - else - { - /** В стеке всегда будут находиться непересекающиеся подозрительные отрезки, самый левый наверху (back). - * На каждом шаге берем левый отрезок и проверяем, подходит ли он. - * Если подходит, разбиваем его на более мелкие и кладем их в стек. Если нет - выбрасываем его. - * Если отрезок уже длиной в одну засечку, добавляем его в ответ и выбрасываем. - */ - std::vector ranges_stack; - ranges_stack.push_back(MarkRange(0, marks_count)); - while (!ranges_stack.empty()) - { - MarkRange range = ranges_stack.back(); - ranges_stack.pop_back(); - - bool may_be_true; - if (range.end == marks_count) - may_be_true = key_condition.mayBeTrueAfter(&index[range.begin * key_size]); - else - may_be_true = key_condition.mayBeTrueInRange(&index[range.begin * key_size], &index[range.end * key_size]); - - if (!may_be_true) - continue; - - if (range.end == range.begin + 1) - { - /// Увидели полезный промежуток между соседними засечками. Либо добавим его к последнему диапазону, либо начнем новый диапазон. - if (res.empty() || range.begin - res.back().end > storage.min_marks_for_seek) - res.push_back(range); - else - res.back().end = range.end; - } - else - { - /// Разбиваем отрезок и кладем результат в стек справа налево. - size_t step = (range.end - range.begin - 1) / storage.settings.coarse_index_granularity + 1; - size_t end; - - for (end = range.end; end > range.begin + step; end -= step) - ranges_stack.push_back(MarkRange(end - step, end)); - - ranges_stack.push_back(MarkRange(range.begin, end)); - } - } - } - - return res; - } - protected: /// Будем вызывать progressImpl самостоятельно. void progress(size_t rows, size_t bytes) {} @@ -150,9 +88,9 @@ protected: if (!reader) { UncompressedCache * uncompressed_cache = use_uncompressed_cache ? storage.context.getUncompressedCache() : NULL; - reader = new MergeTreeReader(path, column_names, uncompressed_cache, storage); + reader = new MergeTreeReader(path, column_names, uncompressed_cache, storage, structure_lock); if (prewhere_actions) - pre_reader = new MergeTreeReader(path, pre_column_names, uncompressed_cache, storage); + pre_reader = new MergeTreeReader(path, pre_column_names, uncompressed_cache, storage, structure_lock); } if (prewhere_actions) @@ -332,6 +270,10 @@ private: ExpressionActionsPtr prewhere_actions; String prewhere_column; bool remove_prewhere_column; + + Logger * log; + + MergeTreeData::LockedTableStructurePtr structure_lock; }; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h new file mode 100644 index 00000000000..0b7f2a7782e --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace DB +{ + +class MergeTreeBlockOutputStream : public IBlockOutputStream +{ +public: + MergeTreeBlockOutputStream(StoragePtr storage_) + : IBlockOutputStream(storage_), storage(dynamic_cast(*storage_)), + structure(storage.data.getLockedStructure(true)) {} + + void write(const Block & block) + { + BlocksList part_blocks = storage.writer.splitBlockIntoParts(block, structure); + for (const Block & current_block : part_blocks) + { + UInt64 temp_index = storage.increment.get(); + String temp_name = storage.writer.writeTempPart(current_block, temp_index, structure); + storage.writer.renameTempPart(temp_name, &storage.increment); + } + } + +private: + StorageMergeTree & storage; + MergeTreeData::LockedTableStructurePtr structure; +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index ae1d323d7ba..d8ad6cd0893 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -47,18 +47,11 @@ namespace DB /// NOTE: Следующее пока не правда. Сейчас тут практически весь StorageMergeTree. Лишние части нужно перенести отсюда в StorageMergeTree. -/** Этот класс отвечает за хранение локальных данных всех *MergeTree движков. - * - Поддерживает набор кусков на диске. Синхронизирует доступ к ним, поддерживает в памяти их список. - * - Полностью выполняет запросы SELECT. - * - Сам не принимает решений об изменении данных. - * - Умеет дававть рекомендации: - * - Говорить, какие куски нужно удалить, потому что они покрыты другими кусками. - * - Выбирать набор кусков для слияния. - * При этом нужна внешняя информация о том, какие куски с какими разрешено объединять. - * - Умеет изменять данные по запросу: - * - Записать новый кусок с данными из блока. - * - Слить указанные куски. - * - Сделать ALTER. +/** Этот класс хранит список кусков и параметры структуры данных. + * Для чтения и изменения данных используются отдельные классы: + * - MergeTreeDataReader + * - MergeTreeDataWriter + * - MergeTreeDataMerger */ struct MergeTreeSettings @@ -102,169 +95,9 @@ struct MergeTreeSettings time_t old_parts_lifetime = 5 * 60; }; -/// Пара засечек, определяющая диапазон строк в куске. Именно, диапазон имеет вид [begin * index_granularity, end * index_granularity). -struct MarkRange +class MergeTreeData { - size_t begin; - size_t end; - - MarkRange() {} - MarkRange(size_t begin_, size_t end_) : begin(begin_), end(end_) {} -}; - -typedef std::vector MarkRanges; - - -class MergeTreeData : public IColumnsDeclaration -{ -friend class MergeTreeReader; -friend class MergeTreeBlockInputStream; -friend class MergeTreeDataBlockOutputStream; -friend class IMergedBlockOutputStream; -friend class MergedBlockOutputStream; -friend class MergedColumnOnlyOutputStream; - public: - /// Режим работы. См. выше. - enum Mode - { - Ordinary, - Collapsing, - Summing, - }; - - /** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце), - * (корректность имён и путей не проверяется) - * состоящую из указанных столбцов. - * - * primary_expr_ast - выражение для сортировки; - * date_column_name - имя столбца с датой; - * index_granularity - на сколько строчек пишется одно значение индекса. - * - * owning_storage используется только чтобы отдавать его возвращаемым потокам блоков. - */ - MergeTreeData( StorageWeakPtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, - ASTPtr & primary_expr_ast_, - const String & date_column_name_, - const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. - size_t index_granularity_, - Mode mode_, - const String & sign_column_, - const MergeTreeSettings & settings_); - - void shutdown(); - ~MergeTreeData(); - - std::string getModePrefix() const - { - switch (mode) - { - case Ordinary: return ""; - case Collapsing: return "Collapsing"; - case Summing: return "Summing"; - - default: - throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR); - } - } - - std::string getTableName() const { return name; } - std::string getSignColumnName() const { return sign_column; } - bool supportsSampling() const { return !!sampling_expression; } - bool supportsFinal() const { return !sign_column.empty(); } - bool supportsPrewhere() const { return true; } - - const NamesAndTypesList & getColumnsList() const { return *columns; } - - /** При чтении, выбирается набор кусков, покрывающий нужный диапазон индекса. - */ - BlockInputStreams read( - const Names & column_names, - ASTPtr query, - const Settings & settings, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size = DEFAULT_BLOCK_SIZE, - unsigned threads = 1); - - /** При записи, данные сортируются и пишутся в новые куски. - */ - BlockOutputStreamPtr write(ASTPtr query); - - /** Выполнить очередной шаг объединения кусков. - */ - bool optimize() - { - merge(1, false, true); - return true; - } - - void dropImpl(); - - void rename(const String & new_path_to_db, const String & new_name); - - /// Метод ALTER позволяет добавлять и удалять столбцы. - /// Метод ALTER нужно применять, когда обращения к базе приостановлены. - /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение - void alter(const ASTAlterQuery::Parameters & params); - - class BigLock - { - public: - BigLock(MergeTreeData & storage) : merge_lock(storage.merge_lock), - write_lock(storage.write_lock), read_lock(storage.read_lock) - { - } - - private: - Poco::ScopedWriteRWLock merge_lock; - Poco::ScopedWriteRWLock write_lock; - Poco::ScopedWriteRWLock read_lock; - }; - - typedef Poco::SharedPtr BigLockPtr; - BigLockPtr lockAllOperations() - { - return new BigLock(*this); - } - -private: - StorageWeakPtr owning_storage; - - String path; - String name; - String full_path; - NamesAndTypesListPtr columns; - - const Context & context; - ASTPtr primary_expr_ast; - String date_column_name; - ASTPtr sampling_expression; - size_t index_granularity; - - size_t min_marks_for_seek; - size_t min_marks_for_concurrent_read; - size_t max_marks_to_use_cache; - - /// Режим работы - какие дополнительные действия делать при мердже. - Mode mode; - /// Для схлопывания записей об изменениях, если используется Collapsing режим работы. - String sign_column; - - MergeTreeSettings settings; - - ExpressionActionsPtr primary_expr; - SortDescription sort_descr; - Block primary_key_sample; - - Increment increment; - - Logger * log; - volatile bool shutdown_called; - - /// Регулярное выражение соответсвующее названию директории с кусочками - Poco::RegularExpression file_name_regexp; - /// Описание куска с данными. struct DataPart { @@ -309,7 +142,7 @@ private: return res; } - void remove() const + void remove() { String from = storage.full_path + name + "/"; String to = storage.full_path + "tmp2_" + name + "/"; @@ -389,125 +222,183 @@ private: typedef SharedPtr DataPartPtr; struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } }; typedef std::set DataParts; + typedef std::vector DataPartsVector; - struct RangesInDataPart + + /// Режим работы. См. выше. + enum Mode { - DataPartPtr data_part; - MarkRanges ranges; - - RangesInDataPart() {} - - RangesInDataPart(DataPartPtr data_part_) - : data_part(data_part_) - { - } + Ordinary, + Collapsing, + Summing, }; - /// Пока существует, помечает части как currently_merging и пересчитывает общий объем сливаемых данных. - /// Вероятно, что части будут помечены заранее. - class CurrentlyMergingPartsTagger + /** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце), + * (корректность имён и путей не проверяется) + * состоящую из указанных столбцов. + * + * primary_expr_ast - выражение для сортировки; + * date_column_name - имя столбца с датой; + * index_granularity - на сколько строчек пишется одно значение индекса. + */ + MergeTreeData( const String & full_path_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + size_t index_granularity_, + Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_); + + /** + * owning_storage используется только чтобы отдавать его потокам блоков. + */ + void setOwningStorage(StoragePtr storage) { owning_storage = storage; } + + std::string getModePrefix() const; + + std::string getSignColumnName() const { return sign_column; } + bool supportsSampling() const { return !!sampling_expression; } + bool supportsFinal() const { return !sign_column.empty(); } + bool supportsPrewhere() const { return true; } + + UInt64 getMaxDataPartIndex(); + + /** Возвращает копию списка, чтобы снаружи можно было не заботиться о блокировках. + */ + DataParts getDataParts(); + + /** Удаляет куски old_parts и добавляет кусок new_part. Если какого-нибудь из удаляемых кусков нет, бросает исключение. + */ + void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); + + /** Удалить неактуальные куски. + */ + void clearOldParts(); + + /** После вызова dropAllData больше ничего вызывать нельзя. + */ + void dropAllData(); + + /** Поменять путь к директории с данными. Предполагается, что все данные из старой директории туда перенесли. + * Нужно вызывать под залоченным lockStructure(). + */ + void setPath(const String & full_path); + + /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. + * Нужно вызывать под залоченным lockStructure(). + * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. Для этого есть parts_writing_lock. + */ + void alter(const ASTAlterQuery::Parameters & params); + + static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); + + /** Изменяемая часть описания таблицы. Содержит лок, запрещающий изменение описания таблицы. + * Если в течение какой-то операции структура таблицы должна оставаться неизменной, нужно держать один лок на все ее время. + * Например, нужно держать такой лок на время всего запроса SELECT или INSERT и на все время слияния набора кусков + * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). + * NOTE: можно перенести сюда другие поля, чтобы сделать их динамически изменяемыми. + * Например, index_granularity, sign_column, primary_expr_ast. + */ + class LockedTableStructure : public IColumnsDeclaration { public: - std::vector parts; - Poco::FastMutex & data_mutex; + const NamesAndTypesList & getColumnsList() const { return *data.columns; } - CurrentlyMergingPartsTagger(const std::vector & parts_, Poco::FastMutex & data_mutex_) : parts(parts_), data_mutex(data_mutex_) - { - /// Здесь не лочится мьютекс, так как конструктор вызывается внутри selectPartsToMerge, где он уже залочен - /// Poco::ScopedLock lock(data_mutex); - for (size_t i = 0; i < parts.size(); ++i) - { - parts[i]->currently_merging = true; - MergeTreeData::total_size_of_currently_merging_parts += parts[i]->size_in_bytes; - } - } + String getFullPath() const { return data.full_path; } - ~CurrentlyMergingPartsTagger() - { - Poco::ScopedLock lock(data_mutex); - for (size_t i = 0; i < parts.size(); ++i) - { - parts[i]->currently_merging = false; - MergeTreeData::total_size_of_currently_merging_parts -= parts[i]->size_in_bytes; - } - } + private: + friend class MergeTreeData; + + const MergeTreeData & data; + Poco::SharedPtr parts_lock; + Poco::ScopedReadRWLock structure_lock; + + LockedTableStructure(const MergeTreeData & data_, bool lock_writing) + : data(data_), parts_lock(lock_writing ? new Poco::ScopedReadRWLock(data.parts_writing_lock) : nullptr), structure_lock(data.table_structure_lock) {} }; - /// Сумарный размер currently_merging кусочков в байтах. - /// Нужно чтобы оценить количество места на диске, которое может понадобится для завершения этих мерджей. - static size_t total_size_of_currently_merging_parts; + typedef Poco::SharedPtr LockedTableStructurePtr; - typedef std::vector RangesInDataParts; + /** Если в рамках этого лока будут добавлены или удалены куски данных, обязательно указать will_modify_parts=true. + * Это возьмет дополнительный лок, не позволяющий начать ALTER MODIFY. + */ + LockedTableStructurePtr getLockedStructure(bool will_modify_parts) const + { + return new LockedTableStructure(*this, will_modify_parts); + } - /** @warning Если берете насколько блокировок, то берите их везде в одинаковом порядке - в том же как они написаны в этом файле */ - /** Взятие этого лока на запись, запрещает мердж */ - Poco::RWLock merge_lock; + typedef Poco::SharedPtr TableStructureWriteLockPtr; - /** Взятие этого лока на запись, запрещает запись */ - Poco::RWLock write_lock; + TableStructureWriteLockPtr lockStructure() { return new Poco::ScopedWriteRWLock(table_structure_lock); } - /** Взятие этого лока на запись, запрещает чтение */ - Poco::RWLock read_lock; + /// Эти поля не нужно изменять снаружи. NOTE нужно спрятать их и сделать методы get*. + const Context & context; + ASTPtr primary_expr_ast; + String date_column_name; + ASTPtr sampling_expression; + size_t index_granularity; + + /// Режим работы - какие дополнительные действия делать при мердже. + Mode mode; + /// Для схлопывания записей об изменениях, если используется Collapsing режим работы. + String sign_column; + + MergeTreeSettings settings; + + ExpressionActionsPtr primary_expr; + SortDescription sort_descr; + Block primary_key_sample; + + StorageWeakPtr owning_storage; + +private: + String full_path; + + NamesAndTypesListPtr columns; + + Logger * log; + volatile bool shutdown_called; + + /// Регулярное выражение соответсвующее названию директории с кусочками + Poco::RegularExpression file_name_regexp; + + /// Брать следующие два лока всегда нужно в этом порядке. + + /** Берется на чтение на все время запроса INSERT и на все время слияния кусков. Берется на запись на все время ALTER MODIFY. + * + * Формально: + * Ввзятие на запись гарантирует, что: + * 1) множество кусков не изменится, пока лок жив, + * 2) все операции над множеством кусков после отпускания лока будут основаны на структуре таблицы на момент после отпускания лока. + * Взятие на чтение обязательно, если будет добавляться или удалять кусок. + * Брать на чтение нужно до получения структуры таблицы, которой будет соответствовать добавляемый кусок. + */ + mutable Poco::RWLock parts_writing_lock; + + /** Лок для множества столбцов и пути к таблице. Берется на запись в RENAME и ALTER (для ALTER MODIFY ненадолго). + * + * Взятие этого лока на запись - строго более "сильная" операция, чем взятие parts_writing_lock на запись. + * То есть, если этот лок взят на запись, о parts_writing_lock можно не заботиться. + * parts_writing_lock нужен только для случаев, когда не хочется брать table_structure_lock надолго. + */ + mutable Poco::RWLock table_structure_lock; /** Актуальное множество кусков с данными. */ DataParts data_parts; Poco::FastMutex data_parts_mutex; /** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов). - * Ссылки на кусок есть отсюда, из списка актуальных кусков, и из каждого потока чтения, который его сейчас использует. + * Ссылки на кусок есть отсюда, из списка актуальных кусков и из каждого потока чтения, который его сейчас использует. * То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить. */ DataParts all_data_parts; Poco::FastMutex all_data_parts_mutex; - static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); - - BlockInputStreams spreadMarkRangesAmongThreads( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column); - - BlockInputStreams spreadMarkRangesAmongThreadsFinal( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column); - - /// Создать выражение "Sign == 1". - void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column); - /// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта. void loadDataParts(); - /// Удалить неактуальные куски. - void clearOldParts(); - - /** Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations = 0, объединяет, пока это возможно. - * Если aggressive - выбрать куски не обращая внимание на соотношение размеров и их новизну (для запроса OPTIMIZE). - */ - void merge(size_t iterations = 1, bool async = true, bool aggressive = false); - - /// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков. - void mergeThread(bool while_can, bool aggressive); - - /// Сразу помечает их как currently_merging. - /// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. - bool selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive); - - void mergeParts(Poco::SharedPtr & what); - - /// Дождаться, пока фоновые потоки закончат слияния. - void joinMergeThreads(); - - Poco::SharedPtr merge_threads; - void removeColumnFiles(String column_name); /// Возвращает true если имя директории совпадает с форматом имени директории кусочков diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h new file mode 100644 index 00000000000..028bee5acd2 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +namespace DB +{ + +/** Умеет выбирать куски для слияния и сливать их. + * Требуется внешний механизм координации слияний со вставками и другими слияниями, обеспечивающий: + * - Куски, между которыми еще может появиться новый кусок, нельзя сливать. См. METR-7001. + * - Кусок, который уже сливаются с кем-то в одном месте, нельзя начать сливать в кем-то другим в другом месте. + */ +class MergeTreeDataMerger +{ +public: + MergeTreeDataMerger(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataMerger")), canceled(false) {} + + /// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. + bool selectPartsToMerge( + MergeTreeData::DataPartsVector & what, + size_t available_disk_space, + bool merge_anything_for_old_months, + bool aggressive); + + /// Возвращает название нового куска. Если слияние отменили, возвращает пустую строку. + String mergeParts(const MergeTreeData::DataPartsVector & parts); + + /// Примерное количество места на диске, нужное для мерджа. С запасом. + size_t estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts); + + /** Отменяет все текущие мерджи. Все выполняющиеся сейчас вызовы mergeParts скоро отменят слияние и вернут пустую строку. + * После этого с этим экземпляром ничего делать нельзя. + */ + void cancelAll() { canceled = true; } + +private: + MergeTreeData & data; + + Logger * log; + + volatile bool canceled; +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h new file mode 100644 index 00000000000..72b03c24952 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +namespace DB +{ + + +/** Выполняет запросы SELECT на данных из merge-дерева. + */ +class MergeTreeDataSelectExecutor +{ +public: + MergeTreeDataSelectExecutor(MergeTreeData & data_); + + /** При чтении, выбирается набор кусков, покрывающий нужный диапазон индекса. + */ + BlockInputStreams read( + const Names & column_names, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size = DEFAULT_BLOCK_SIZE, + unsigned threads = 1); + +private: + MergeTreeData & data; + + Logger * log; + + struct RangesInDataPart + { + MergeTreeData::DataPartPtr data_part; + MarkRanges ranges; + + RangesInDataPart() {} + + RangesInDataPart(MergeTreeData::DataPartPtr data_part_) + : data_part(data_part_) + { + } + }; + + typedef std::vector RangesInDataParts; + + size_t min_marks_for_seek; + size_t min_marks_for_concurrent_read; + size_t max_marks_to_use_cache; + + BlockInputStreams spreadMarkRangesAmongThreads( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column, + const MergeTreeData::LockedTableStructurePtr & structure); + + BlockInputStreams spreadMarkRangesAmongThreadsFinal( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column, + const MergeTreeData::LockedTableStructurePtr & structure); + + /// Создать выражение "Sign == 1". + void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column, + const MergeTreeData::LockedTableStructurePtr & structure); + + MarkRanges markRangesFromPkRange(const MergeTreeData::DataPart::Index & index, PKCondition & key_condition); +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h similarity index 86% rename from dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h rename to dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index 8cdad62fa9d..c0766d89f23 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -13,6 +13,34 @@ namespace DB { +/** Записывает новые куски с данными в merge-дерево. + */ +class MergeTreeDataWriter +{ +public: + MergeTreeDataWriter(MergeTreeData & data_) : data(data_) {} + + /** Разбивает блок на блоки, каждый из которых нужно записать в отдельный кусок. + * (читай: разбивает строки по месяцам) + * Работает детерминированно: если отдать на вход такой же блок, на выходе получатся такие же блоки в таком же порядке. + */ + BlocksList splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure); + + /** Все строки должны относиться к одному месяцу. Возвращает название временного куска. + * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. + * NOTE потом понадобится возвращать отсюда структуру с контрольными суммами и размерами. + */ + String writeTempPart(const Block & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); + + /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. + * Если increment!=nullptr, индекс куска бурется из инкремента. Иначе индекс куска не меняется. + */ + String renameTempPart(const String & temp_name, Increment * increment); + +private: + MergeTreeData & data; +}; +#if 0 class MergeTreeDataBlockOutputStream : public IBlockOutputStream { public: @@ -294,5 +322,5 @@ private: } } }; - +#endif } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index 01342700ccc..3c33a262244 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -3,10 +3,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #define MERGE_TREE_MARK_SIZE (2 * sizeof(size_t)) @@ -15,6 +18,20 @@ namespace DB { +/** Пара засечек, определяющая диапазон строк в куске. Именно, диапазон имеет вид [begin * index_granularity, end * index_granularity). + */ +struct MarkRange +{ + size_t begin; + size_t end; + + MarkRange() {} + MarkRange(size_t begin_, size_t end_) : begin(begin_), end(end_) {} +}; + +typedef std::vector MarkRanges; + + /** Умеет читать данные между парой засечек из одного куска. При чтении последовательных отрезков не делает лишних seek-ов. * При чтении почти последовательных отрезков делает seek-и быстро, не выбрасывая содержимое буфера. */ @@ -22,11 +39,13 @@ class MergeTreeReader { public: MergeTreeReader(const String & path_, /// Путь к куску - const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_) - : path(path_), column_names(columns_names_), use_uncompressed_cache(use_uncompressed_cache_), storage(storage_) + const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_, + MergeTreeData::LockedTableStructurePtr structure_) + : path(path_), column_names(columns_names_), use_uncompressed_cache(use_uncompressed_cache_), storage(storage_), + structure(structure_) { for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) - addStream(*it, *storage.getDataTypeByName(*it)); + addStream(*it, *structure->getDataTypeByName(*it)); } /** Если столбцов нет в блоке, добавляет их, если есть - добавляет прочитанные значения к ним в конец. @@ -59,7 +78,7 @@ public: ColumnWithNameAndType column; column.name = *it; - column.type = storage.getDataTypeByName(*it); + column.type = structure->getDataTypeByName(*it); if (append) column.column = res.getByName(column.name).column; @@ -114,7 +133,7 @@ public: { ColumnWithNameAndType column; column.name = *it; - column.type = storage.getDataTypeByName(*it); + column.type = structure->getDataTypeByName(*it); /** Нужно превратить константный столбец в полноценный, так как в части блоков (из других кусков), * он может быть полноценным (а то интерпретатор может посчитать, что он константный везде). @@ -221,6 +240,7 @@ private: Names column_names; bool use_uncompressed_cache; MergeTreeData & storage; + MergeTreeData::LockedTableStructurePtr structure; void addStream(const String & name, const IDataType & type, size_t level = 0) { diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index e91a0d23883..a5af398ceef 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -196,25 +196,26 @@ protected: class MergedBlockOutputStream : public IMergedBlockOutputStream { public: - MergedBlockOutputStream(MergeTreeData & storage_, + MergedBlockOutputStream(MergeTreeData & storage_, MergeTreeData::LockedTableStructurePtr structure_, UInt16 min_date, UInt16 max_date, UInt64 min_part_id, UInt64 max_part_id, UInt32 level) - : IMergedBlockOutputStream(storage_), marks_count(0) + : IMergedBlockOutputStream(storage_), structure(structure_), marks_count(0) { part_name = storage.getPartName( DayNum_t(min_date), DayNum_t(max_date), min_part_id, max_part_id, level); - part_tmp_path = storage.full_path + "tmp_" + part_name + "/"; - part_res_path = storage.full_path + part_name + "/"; + part_tmp_path = structure->getFullPath() + "tmp_" + part_name + "/"; + part_res_path = structure->getFullPath() + part_name + "/"; Poco::File(part_tmp_path).createDirectories(); index_stream = new WriteBufferFromFile(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); - for (NamesAndTypesList::const_iterator it = storage.columns->begin(); it != storage.columns->end(); ++it) - addStream(part_tmp_path, it->first, *it->second); + columns_list = structure->getColumnsList(); + for (const auto & it : columns_list) + addStream(part_tmp_path, it.first, *it.second); } - + void write(const Block & block) { size_t rows = block.rows(); @@ -243,9 +244,9 @@ public: OffsetColumns offset_columns; /// Теперь пишем данные. - for (NamesAndTypesList::const_iterator it = storage.columns->begin(); it != storage.columns->end(); ++it) + for (const auto & it : columns_list) { - const ColumnWithNameAndType & column = block.getByName(it->first); + const ColumnWithNameAndType & column = block.getByName(it.first); writeData(column.name, *column.type, *column.column, offset_columns); } @@ -285,6 +286,9 @@ public: } private: + MergeTreeData::LockedTableStructurePtr structure; + NamesAndTypesList columns_list; + String part_name; String part_tmp_path; String part_res_path; diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 366716bfd1c..f9c692d3211 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -1,6 +1,9 @@ #pragma once #include +#include "MergeTree/MergeTreeDataSelectExecutor.h" +#include "MergeTree/MergeTreeDataWriter.h" +#include "MergeTree/MergeTreeDataMerger.h" namespace DB { @@ -9,6 +12,8 @@ namespace DB */ class StorageMergeTree : public IStorage { +friend class MergeTreeBlockOutputStream; + public: /** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце), * (корректность имён и путей не проверяется) @@ -36,13 +41,13 @@ public: return data.getModePrefix() + "MergeTree"; } - std::string getTableName() const { return data.getTableName(); } + std::string getTableName() const { return name; } std::string getSignColumnName() const { return data.getSignColumnName(); } bool supportsSampling() const { return data.supportsSampling(); } bool supportsFinal() const { return data.supportsFinal(); } bool supportsPrewhere() const { return data.supportsPrewhere(); } - const NamesAndTypesList & getColumnsList() const { return data.getColumnsList(); } + const NamesAndTypesList & getColumnsList() const { return data.getLockedStructure(false)->getColumnsList(); } BlockInputStreams read( const Names & column_names, @@ -58,7 +63,8 @@ public: */ bool optimize() { - return data.optimize(); + merge(1, false, true); + return true; } void dropImpl(); @@ -70,12 +76,27 @@ public: /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение void alter(const ASTAlterQuery::Parameters & params); - typedef MergeTreeData::BigLockPtr BigLockPtr; + typedef MergeTreeData::TableStructureWriteLockPtr BigLockPtr; - BigLockPtr lockAllOperations() { return data.lockAllOperations(); } + BigLockPtr lockAllOperations() + { + return data.lockStructure(); + } private: + String path; + String name; + String full_path; + Increment increment; + MergeTreeData data; + MergeTreeDataSelectExecutor reader; + MergeTreeDataWriter writer; + MergeTreeDataMerger merger; + + Logger * log; + + volatile bool shutdown_called; StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, @@ -83,9 +104,24 @@ private: const String & date_column_name_, const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. size_t index_granularity_, - MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, - const String & sign_column_ = "", - const MergeTreeSettings & settings_ = MergeTreeSettings()); + MergeTreeData::Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_); + + + + /** Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations = 0, объединяет, пока это возможно. + * Если aggressive - выбрать куски не обращая внимание на соотношение размеров и их новизну (для запроса OPTIMIZE). + */ + void merge(size_t iterations = 1, bool async = true, bool aggressive = false); + + /// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков. + void mergeThread(bool while_can, bool aggressive); + + /// Дождаться, пока фоновые потоки закончат слияния. + void joinMergeThreads(); + + Poco::SharedPtr merge_threads; }; } diff --git a/dbms/include/DB/Storages/StoragePtr.h b/dbms/include/DB/Storages/StoragePtr.h index 89aea28cb2d..96bd2a9a489 100644 --- a/dbms/include/DB/Storages/StoragePtr.h +++ b/dbms/include/DB/Storages/StoragePtr.h @@ -88,6 +88,7 @@ public: class StorageWeakPtr { public: + StorageWeakPtr() {} StorageWeakPtr(const StoragePtr & p) : ptr(p.ptr) {} StoragePtr lock() diff --git a/dbms/src/Storages/MergeTree/DiskSpaceMonitor.cpp b/dbms/src/Storages/MergeTree/DiskSpaceMonitor.cpp new file mode 100644 index 00000000000..2175fa5c8fe --- /dev/null +++ b/dbms/src/Storages/MergeTree/DiskSpaceMonitor.cpp @@ -0,0 +1,9 @@ +#include + +namespace DB +{ + +size_t DiskSpaceMonitor::reserved_bytes; +Poco::FastMutex DiskSpaceMonitor::reserved_bytes_mutex; + +} diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 230a1512e7e..ee67b5bd3f4 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -47,7 +46,6 @@ #include #include -#include #include #include @@ -57,10 +55,21 @@ namespace DB { -size_t MergeTreeData::total_size_of_currently_merging_parts = 0; +static String lastTwoPathComponents(const String & path) +{ + if (!path.empty() && *path.rbegin() == '/') + path.erase(path.end() - 1); + size_t slash = path.rfind('/'); + if (slash == String::npos || slash == 0) + return path; + slash = path.rfind('/', slash - 1); + if (slash == String::npos) + return path; + return path.substr(slash + 1); +} MergeTreeData::MergeTreeData( - StorageWeakPtr owning_storage_, const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const String & full_path_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, @@ -68,19 +77,15 @@ MergeTreeData::MergeTreeData( Mode mode_, const String & sign_column_, const MergeTreeSettings & settings_) - : owning_storage(owning_storage_), path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), columns(columns_), - context(context_), primary_expr_ast(primary_expr_ast_->clone()), + : context(context_), primary_expr_ast(primary_expr_ast_->clone()), date_column_name(date_column_name_), sampling_expression(sampling_expression_), index_granularity(index_granularity_), mode(mode_), sign_column(sign_column_), settings(settings_), - increment(full_path + "increment.txt"), log(&Logger::get("MergeTreeData: " + name)), shutdown_called(false), + full_path(full_path_), columns(columns_), + log(&Logger::get("MergeTreeData: " + lastTwoPathComponents(full_path))), file_name_regexp("^(\\d{8})_(\\d{8})_(\\d+)_(\\d+)_(\\d+)") { - min_marks_for_seek = (settings.min_rows_for_seek + index_granularity - 1) / index_granularity; - min_marks_for_concurrent_read = (settings.min_rows_for_concurrent_read + index_granularity - 1) / index_granularity; - max_marks_to_use_cache = (settings.max_rows_to_use_cache + index_granularity - 1) / index_granularity; - /// создаём директорию, если её нет Poco::File(full_path).createDirectories(); @@ -99,430 +104,34 @@ MergeTreeData::MergeTreeData( ExpressionActionsPtr projected_expr = ExpressionAnalyzer(primary_expr_ast, context, *columns).getActions(true); primary_key_sample = projected_expr->getSampleBlock(); - merge_threads = new boost::threadpool::pool(settings.merging_threads); - loadDataParts(); clearOldParts(); +} +UInt64 MergeTreeData::getMaxDataPartIndex() +{ UInt64 max_part_id = 0; for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) { max_part_id = std::max(max_part_id, (*it)->right); } - increment.fixIfBroken(max_part_id); + return max_part_id; } -void MergeTreeData::shutdown() +std::string MergeTreeData::getModePrefix() const { - if (shutdown_called) - return; - shutdown_called = true; + switch (mode) + { + case Ordinary: return ""; + case Collapsing: return "Collapsing"; + case Summing: return "Summing"; - joinMergeThreads(); + default: + throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR); + } } -MergeTreeData::~MergeTreeData() -{ - shutdown(); -} - - -BlockOutputStreamPtr MergeTreeData::write(ASTPtr query) -{ - return new MergeTreeDataBlockOutputStream(*this, owning_storage); -} - - -BlockInputStreams MergeTreeData::read( - const Names & column_names_to_return, - ASTPtr query, - const Settings & settings, - QueryProcessingStage::Enum & processed_stage, - size_t max_block_size, - unsigned threads) -{ - Poco::ScopedReadRWLock lock(read_lock); - - check(column_names_to_return); - processed_stage = QueryProcessingStage::FetchColumns; - - PKCondition key_condition(query, context, *columns, sort_descr); - PKCondition date_condition(query, context, *columns, SortDescription(1, SortColumnDescription(date_column_name, 1))); - - typedef std::vector PartsList; - PartsList parts; - - /// Выберем куски, в которых могут быть данные, удовлетворяющие date_condition. - { - Poco::ScopedLock lock(data_parts_mutex); - - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - Field left = static_cast((*it)->left_date); - Field right = static_cast((*it)->right_date); - - if (date_condition.mayBeTrueInRange(&left, &right)) - parts.push_back(*it); - } - } - - /// Семплирование. - Names column_names_to_read = column_names_to_return; - UInt64 sampling_column_value_limit = 0; - typedef Poco::SharedPtr ASTFunctionPtr; - ASTFunctionPtr filter_function; - ExpressionActionsPtr filter_expression; - - ASTSelectQuery & select = *dynamic_cast(&*query); - if (select.sample_size) - { - double size = apply_visitor(FieldVisitorConvertToNumber(), - dynamic_cast(*select.sample_size).value); - - if (size < 0) - throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND); - - if (size > 1) - { - size_t requested_count = apply_visitor(FieldVisitorConvertToNumber(), dynamic_cast(*select.sample_size).value); - - /// Узнаем, сколько строк мы бы прочли без семплирования. - LOG_DEBUG(log, "Preliminary index scan with condition: " << key_condition.toString()); - size_t total_count = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - DataPartPtr & part = parts[i]; - MarkRanges ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); - - for (size_t j = 0; j < ranges.size(); ++j) - total_count += ranges[j].end - ranges[j].begin; - } - total_count *= index_granularity; - - size = std::min(1., static_cast(requested_count) / total_count); - - LOG_DEBUG(log, "Selected relative sample size: " << size); - } - - UInt64 sampling_column_max = 0; - DataTypePtr type = primary_expr->getSampleBlock().getByName(sampling_expression->getColumnName()).type; - - if (type->getName() == "UInt64") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt32") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt16") - sampling_column_max = std::numeric_limits::max(); - else if (type->getName() == "UInt8") - sampling_column_max = std::numeric_limits::max(); - else - throw Exception("Invalid sampling column type in storage parameters: " + type->getName() + ". Must be unsigned integer type.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); - - /// Добавим условие, чтобы отсечь еще что-нибудь при повторном просмотре индекса. - sampling_column_value_limit = static_cast(size * sampling_column_max); - if (!key_condition.addCondition(sampling_expression->getColumnName(), - Range::createRightBounded(sampling_column_value_limit, true))) - throw Exception("Sampling column not in primary key", ErrorCodes::ILLEGAL_COLUMN); - - /// Выражение для фильтрации: sampling_expression <= sampling_column_value_limit - - ASTPtr filter_function_args = new ASTExpressionList; - filter_function_args->children.push_back(sampling_expression); - filter_function_args->children.push_back(new ASTLiteral(StringRange(), sampling_column_value_limit)); - - filter_function = new ASTFunction; - filter_function->name = "lessOrEquals"; - filter_function->arguments = filter_function_args; - filter_function->children.push_back(filter_function->arguments); - - filter_expression = ExpressionAnalyzer(filter_function, context, *columns).getActions(false); - - /// Добавим столбцы, нужные для sampling_expression. - std::vector add_columns = filter_expression->getRequiredColumns(); - column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); - std::sort(column_names_to_read.begin(), column_names_to_read.end()); - column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); - } - - LOG_DEBUG(log, "Key condition: " << key_condition.toString()); - LOG_DEBUG(log, "Date condition: " << date_condition.toString()); - - /// PREWHERE - ExpressionActionsPtr prewhere_actions; - String prewhere_column; - if (select.prewhere_expression) - { - ExpressionAnalyzer analyzer(select.prewhere_expression, context, *columns); - prewhere_actions = analyzer.getActions(false); - prewhere_column = select.prewhere_expression->getColumnName(); - } - - RangesInDataParts parts_with_ranges; - - /// Найдем, какой диапазон читать из каждого куска. - size_t sum_marks = 0; - size_t sum_ranges = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - DataPartPtr & part = parts[i]; - RangesInDataPart ranges(part); - ranges.ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition); - - if (!ranges.ranges.empty()) - { - parts_with_ranges.push_back(ranges); - - sum_ranges += ranges.ranges.size(); - for (size_t j = 0; j < ranges.ranges.size(); ++j) - { - sum_marks += ranges.ranges[j].end - ranges.ranges[j].begin; - } - } - } - - LOG_DEBUG(log, "Selected " << parts.size() << " parts by date, " << parts_with_ranges.size() << " parts by key, " - << sum_marks << " marks to read from " << sum_ranges << " ranges"); - - BlockInputStreams res; - - if (select.final) - { - /// Добавим столбцы, нужные для вычисления первичного ключа и знака. - std::vector add_columns = primary_expr->getRequiredColumns(); - column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); - column_names_to_read.push_back(sign_column); - std::sort(column_names_to_read.begin(), column_names_to_read.end()); - column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); - - res = spreadMarkRangesAmongThreadsFinal( - parts_with_ranges, - threads, - column_names_to_read, - max_block_size, - settings.use_uncompressed_cache, - prewhere_actions, - prewhere_column); - } - else - { - res = spreadMarkRangesAmongThreads( - parts_with_ranges, - threads, - column_names_to_read, - max_block_size, - settings.use_uncompressed_cache, - prewhere_actions, - prewhere_column); - } - - if (select.sample_size) - { - for (size_t i = 0; i < res.size(); ++i) - { - BlockInputStreamPtr original_stream = res[i]; - BlockInputStreamPtr expression_stream = new ExpressionBlockInputStream(original_stream, filter_expression); - BlockInputStreamPtr filter_stream = new FilterBlockInputStream(expression_stream, filter_function->getColumnName()); - res[i] = filter_stream; - } - } - - return res; -} - - -/// Примерно поровну распределить засечки между потоками. -BlockInputStreams MergeTreeData::spreadMarkRangesAmongThreads( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column) -{ - /// На всякий случай перемешаем куски. - std::random_shuffle(parts.begin(), parts.end()); - - /// Посчитаем засечки для каждого куска. - std::vector sum_marks_in_parts(parts.size()); - size_t sum_marks = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - /// Пусть отрезки будут перечислены справа налево, чтобы можно было выбрасывать самый левый отрезок с помощью pop_back(). - std::reverse(parts[i].ranges.begin(), parts[i].ranges.end()); - - sum_marks_in_parts[i] = 0; - for (size_t j = 0; j < parts[i].ranges.size(); ++j) - { - MarkRange & range = parts[i].ranges[j]; - sum_marks_in_parts[i] += range.end - range.begin; - } - sum_marks += sum_marks_in_parts[i]; - } - - if (sum_marks > max_marks_to_use_cache) - use_uncompressed_cache = false; - - BlockInputStreams res; - - if (sum_marks > 0) - { - size_t min_marks_per_thread = (sum_marks - 1) / threads + 1; - - for (size_t i = 0; i < threads && !parts.empty(); ++i) - { - size_t need_marks = min_marks_per_thread; - BlockInputStreams streams; - - /// Цикл по кускам. - while (need_marks > 0 && !parts.empty()) - { - RangesInDataPart & part = parts.back(); - size_t & marks_in_part = sum_marks_in_parts.back(); - - /// Не будем брать из куска слишком мало строк. - if (marks_in_part >= min_marks_for_concurrent_read && - need_marks < min_marks_for_concurrent_read) - need_marks = min_marks_for_concurrent_read; - - /// Не будем оставлять в куске слишком мало строк. - if (marks_in_part > need_marks && - marks_in_part - need_marks < min_marks_for_concurrent_read) - need_marks = marks_in_part; - - /// Возьмем весь кусок, если он достаточно мал. - if (marks_in_part <= need_marks) - { - /// Восстановим порядок отрезков. - std::reverse(part.ranges.begin(), part.ranges.end()); - - streams.push_back(new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, part.ranges, owning_storage, use_uncompressed_cache, - prewhere_actions, prewhere_column)); - need_marks -= marks_in_part; - parts.pop_back(); - sum_marks_in_parts.pop_back(); - continue; - } - - MarkRanges ranges_to_get_from_part; - - /// Цикл по отрезкам куска. - while (need_marks > 0) - { - if (part.ranges.empty()) - throw Exception("Unexpected end of ranges while spreading marks among threads", ErrorCodes::LOGICAL_ERROR); - - MarkRange & range = part.ranges.back(); - size_t marks_in_range = range.end - range.begin; - - size_t marks_to_get_from_range = std::min(marks_in_range, need_marks); - ranges_to_get_from_part.push_back(MarkRange(range.begin, range.begin + marks_to_get_from_range)); - range.begin += marks_to_get_from_range; - marks_in_part -= marks_to_get_from_range; - need_marks -= marks_to_get_from_range; - if (range.begin == range.end) - part.ranges.pop_back(); - } - - streams.push_back(new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, ranges_to_get_from_part, owning_storage, use_uncompressed_cache, - prewhere_actions, prewhere_column)); - } - - if (streams.size() == 1) - res.push_back(streams[0]); - else - res.push_back(new ConcatBlockInputStream(streams)); - } - - if (!parts.empty()) - throw Exception("Couldn't spread marks among threads", ErrorCodes::LOGICAL_ERROR); - } - - return res; -} - - -/// Распределить засечки между потоками и сделать, чтобы в ответе (почти) все данные были сколлапсированы (модификатор FINAL). -BlockInputStreams MergeTreeData::spreadMarkRangesAmongThreadsFinal( - RangesInDataParts parts, - size_t threads, - const Names & column_names, - size_t max_block_size, - bool use_uncompressed_cache, - ExpressionActionsPtr prewhere_actions, - const String & prewhere_column) -{ - size_t sum_marks = 0; - for (size_t i = 0; i < parts.size(); ++i) - for (size_t j = 0; j < parts[i].ranges.size(); ++j) - sum_marks += parts[i].ranges[j].end - parts[i].ranges[j].begin; - - if (sum_marks > max_marks_to_use_cache) - use_uncompressed_cache = false; - - ExpressionActionsPtr sign_filter_expression; - String sign_filter_column; - createPositiveSignCondition(sign_filter_expression, sign_filter_column); - - BlockInputStreams to_collapse; - - for (size_t part_index = 0; part_index < parts.size(); ++part_index) - { - RangesInDataPart & part = parts[part_index]; - - BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( - full_path + part.data_part->name + '/', max_block_size, column_names, *this, - part.data_part, part.ranges, owning_storage, use_uncompressed_cache, - prewhere_actions, prewhere_column); - - to_collapse.push_back(new ExpressionBlockInputStream(source_stream, primary_expr)); - } - - BlockInputStreams res; - if (to_collapse.size() == 1) - res.push_back(new FilterBlockInputStream(new ExpressionBlockInputStream(to_collapse[0], sign_filter_expression), sign_filter_column)); - else if (to_collapse.size() > 1) - res.push_back(new CollapsingFinalBlockInputStream(to_collapse, sort_descr, sign_column)); - - return res; -} - - -void MergeTreeData::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column) -{ - ASTFunction * function = new ASTFunction; - ASTPtr function_ptr = function; - - ASTExpressionList * arguments = new ASTExpressionList; - ASTPtr arguments_ptr = arguments; - - ASTIdentifier * sign = new ASTIdentifier; - ASTPtr sign_ptr = sign; - - ASTLiteral * one = new ASTLiteral; - ASTPtr one_ptr = one; - - function->name = "equals"; - function->arguments = arguments_ptr; - function->children.push_back(arguments_ptr); - - arguments->children.push_back(sign_ptr); - arguments->children.push_back(one_ptr); - - sign->name = sign_column; - sign->kind = ASTIdentifier::Column; - - one->type = new DataTypeInt8; - one->value = Field(static_cast(1)); - - out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); - out_column = function->getColumnName(); -} String MergeTreeData::getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level) @@ -622,7 +231,7 @@ void MergeTreeData::loadDataParts() if (part->level == 0) { /// Восстановить куски нулевого уровня невозможно. - LOG_ERROR(log, "Removing broken part " << path + file_name << " because is't impossible to repair."); + LOG_ERROR(log, "Removing broken part " << full_path + file_name << " because is't impossible to repair."); part->remove(); } else @@ -741,454 +350,13 @@ void MergeTreeData::clearOldParts() } } - -void MergeTreeData::merge(size_t iterations, bool async, bool aggressive) +void MergeTreeData::setPath(const String & new_full_path) { - bool while_can = false; - if (iterations == 0) - { - while_can = true; - iterations = settings.merging_threads; - } - - for (size_t i = 0; i < iterations; ++i) - merge_threads->schedule(boost::bind(&MergeTreeData::mergeThread, this, while_can, aggressive)); - - if (!async) - joinMergeThreads(); -} - - -void MergeTreeData::mergeThread(bool while_can, bool aggressive) -{ - try - { - while (!shutdown_called) - { - /// Удаляем старые куски. На случай, если в слиянии что-то сломано, и из следующего блока вылетит исключение. - clearOldParts(); - - { - Poco::ScopedReadRWLock lock(merge_lock); - - /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски - Poco::SharedPtr what; - - if (!selectPartsToMerge(what, false, aggressive) && !selectPartsToMerge(what, true, aggressive)) - break; - - mergeParts(what); - } - - if (shutdown_called) - break; - - /// Удаляем куски, которые мы только что сливали. - clearOldParts(); - - if (!while_can) - break; - } - } - catch (const Exception & e) - { - LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTrace().toString()); - } - catch (const Poco::Exception & e) - { - LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); - } - catch (const std::exception & e) - { - LOG_ERROR(log, "std::exception: " << e.what()); - } - catch (...) - { - LOG_ERROR(log, "Unknown exception"); - } -} - - -void MergeTreeData::joinMergeThreads() -{ - LOG_DEBUG(log, "Waiting for merge threads to finish."); - merge_threads->wait(); -} - - -/// Выбираем отрезок из не более чем max_parts_to_merge_at_once кусков так, чтобы максимальный размер был меньше чем в max_size_ratio_to_merge_parts раз больше суммы остальных. -/// Это обеспечивает в худшем случае время O(n log n) на все слияния, независимо от выбора сливаемых кусков, порядка слияния и добавления. -/// При max_parts_to_merge_at_once >= log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts), -/// несложно доказать, что всегда будет что сливать, пока количество кусков больше -/// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts)*(количество кусков размером больше max_rows_to_merge_parts). -/// Дальше эвристики. -/// Будем выбирать максимальный по включению подходящий отрезок. -/// Из всех таких выбираем отрезок с минимальным максимумом размера. -/// Из всех таких выбираем отрезок с минимальным минимумом размера. -/// Из всех таких выбираем отрезок с максимальной длиной. -/// Дополнительно: -/// 1) с 1:00 до 5:00 ограничение сверху на размер куска в основном потоке увеличивается в несколько раз -/// 2) в зависимоти от возраста кусков меняется допустимая неравномерность при слиянии -/// 3) Молодые куски крупного размера (примерно больше 1 Гб) можно сливать не меньше чем по три -/// 4) Если в одном из потоков идет мердж крупных кусков, то во втором сливать только маленькие кусочки -/// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности - -bool MergeTreeData::selectPartsToMerge(Poco::SharedPtr & what, bool merge_anything_for_old_months, bool aggressive) -{ - LOG_DEBUG(log, "Selecting parts to merge"); - - Poco::ScopedLock lock(data_parts_mutex); - - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - size_t min_max = -1U; - size_t min_min = -1U; - int max_len = 0; - DataParts::iterator best_begin; - bool found = false; - - DayNum_t now_day = date_lut.toDayNum(time(0)); - DayNum_t now_month = date_lut.toFirstDayNumOfMonth(now_day); - int now_hour = date_lut.toHourInaccurate(time(0)); - - size_t maybe_used_bytes = total_size_of_currently_merging_parts; - size_t total_free_bytes = 0; - struct statvfs fs; - - /// Смотрим количество свободного места в файловой системе - if (statvfs(full_path.c_str(), &fs) != 0) - throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS); - - total_free_bytes = fs.f_bfree * fs.f_bsize; - - /// Сколько кусков, начиная с текущего, можно включить в валидный отрезок, начинающийся левее текущего куска. - /// Нужно для определения максимальности по включению. - int max_count_from_left = 0; - - size_t cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts; - - /// Если ночь, можем мерджить сильно большие куски - if (now_hour >= 1 && now_hour <= 5) - cur_max_rows_to_merge_parts *= settings.merge_parts_at_night_inc; - - /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - if ((*it)->currently_merging && (*it)->size * index_granularity > 25 * 1024 * 1024) - { - cur_max_rows_to_merge_parts = settings.max_rows_to_merge_parts_second; - break; - } - } - - /// Левый конец отрезка. - for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) - { - const DataPartPtr & first_part = *it; - - max_count_from_left = std::max(0, max_count_from_left - 1); - - /// Кусок не занят. - if (first_part->currently_merging) - continue; - - /// Кусок достаточно мал или слияние "агрессивное". - if (first_part->size * index_granularity > cur_max_rows_to_merge_parts - && !aggressive) - continue; - - /// Кусок в одном месяце. - if (first_part->left_month != first_part->right_month) - { - LOG_WARNING(log, "Part " << first_part->name << " spans more than one month"); - continue; - } - - /// Самый длинный валидный отрезок, начинающийся здесь. - size_t cur_longest_max = -1U; - size_t cur_longest_min = -1U; - int cur_longest_len = 0; - - /// Текущий отрезок, не обязательно валидный. - size_t cur_max = first_part->size; - size_t cur_min = first_part->size; - size_t cur_sum = first_part->size; - size_t cur_total_size = first_part->size_in_bytes; - int cur_len = 1; - - DayNum_t month = first_part->left_month; - UInt64 cur_id = first_part->right; - - /// Этот месяц кончился хотя бы день назад. - bool is_old_month = now_day - now_month >= 1 && now_month > month; - - time_t oldest_modification_time = first_part->modification_time; - - /// Правый конец отрезка. - DataParts::iterator jt = it; - for (++jt; jt != data_parts.end() && cur_len < static_cast(settings.max_parts_to_merge_at_once); ++jt) - { - const DataPartPtr & last_part = *jt; - - /// Кусок не занят и в одном правильном месяце. - if (last_part->currently_merging || - last_part->left_month != last_part->right_month || - last_part->left_month != month) - break; - - /// Кусок достаточно мал или слияние "агрессивное". - if (last_part->size * index_granularity > cur_max_rows_to_merge_parts - && !aggressive) - break; - - /// Кусок правее предыдущего. - if (last_part->left < cur_id) - { - LOG_WARNING(log, "Part " << last_part->name << " intersects previous part"); - break; - } - - oldest_modification_time = std::max(oldest_modification_time, last_part->modification_time); - cur_max = std::max(cur_max, last_part->size); - cur_min = std::min(cur_min, last_part->size); - cur_sum += last_part->size; - cur_total_size += last_part->size_in_bytes; - ++cur_len; - cur_id = last_part->right; - - int min_len = 2; - int cur_age_in_sec = time(0) - oldest_modification_time; - - /// Если куски примерно больше 1 Gb и образовались меньше 6 часов назад, то мерджить не меньше чем по 3. - if (cur_max * index_granularity * 150 > 1024*1024*1024 && cur_age_in_sec < 6*3600) - min_len = 3; - - /// Равен 0.5 если возраст порядка 0, равен 5 если возраст около месяца. - double time_ratio_modifier = 0.5 + 9 * static_cast(cur_age_in_sec) / (3600*24*30 + cur_age_in_sec); - - /// Двоичный логарифм суммарного размера кусочков - double log_cur_sum = std::log(cur_sum * index_granularity) / std::log(2); - /// Равен ~2 если куски маленькие, уменьшается до 0.5 с увеличением суммарного размера до 2^25. - double size_ratio_modifier = std::max(0.25, 2 - 3 * (log_cur_sum) / (25 + log_cur_sum)); - - /// Объединяем все в одну константу - double ratio = std::max(0.5, time_ratio_modifier * size_ratio_modifier * settings.max_size_ratio_to_merge_parts); - - /// Если отрезок валидный, то он самый длинный валидный, начинающийся тут. - if (cur_len >= min_len - && (static_cast(cur_max) / (cur_sum - cur_max) < ratio - /// За старый месяц объединяем что угодно, если разрешено и если этому хотя бы 15 дней - || (is_old_month && merge_anything_for_old_months && cur_age_in_sec > 3600*24*15) - /// Если слияние "агрессивное", то сливаем что угодно - || aggressive)) - { - /// Достаточно места на диске, чтобы покрыть уже активные и новый мерджи с запасом в 50% - if (total_free_bytes > (maybe_used_bytes + cur_total_size) * 1.5) - { - cur_longest_max = cur_max; - cur_longest_min = cur_min; - cur_longest_len = cur_len; - } - else - LOG_WARNING(log, "Won't merge parts from " << first_part->name << " to " << last_part->name - << " because not enough free space: " << total_free_bytes << " free, " - << maybe_used_bytes << " already involved in merge, " - << cur_total_size << " required now (+50% on overhead)"); - } - } - - /// Это максимальный по включению валидный отрезок. - if (cur_longest_len > max_count_from_left) - { - max_count_from_left = cur_longest_len; - - if (!found - || std::make_pair(std::make_pair(cur_longest_max, cur_longest_min), -cur_longest_len) - < std::make_pair(std::make_pair(min_max, min_min), -max_len)) - { - found = true; - min_max = cur_longest_max; - min_min = cur_longest_min; - max_len = cur_longest_len; - best_begin = it; - } - } - } - - if (found) - { - std::vector parts; - - DataParts::iterator it = best_begin; - for (int i = 0; i < max_len; ++i) - { - parts.push_back(*it); - ++it; - } - what = new CurrentlyMergingPartsTagger(parts, data_parts_mutex); - - LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); - } - else - { - LOG_DEBUG(log, "No parts to merge"); - } - - return found; -} - - -/// parts должны быть отсортированы. -void MergeTreeData::mergeParts(Poco::SharedPtr & what) -{ - const std::vector & parts(what->parts); - - LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); - - Names all_column_names; - for (NamesAndTypesList::const_iterator it = columns->begin(); it != columns->end(); ++it) - all_column_names.push_back(it->first); - - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - MergeTreeData::DataPartPtr new_data_part = new DataPart(*this); - new_data_part->left_date = std::numeric_limits::max(); - new_data_part->right_date = std::numeric_limits::min(); - new_data_part->left = parts.front()->left; - new_data_part->right = parts.back()->right; - new_data_part->level = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - new_data_part->level = std::max(new_data_part->level, parts[i]->level); - new_data_part->left_date = std::min(new_data_part->left_date, parts[i]->left_date); - new_data_part->right_date = std::max(new_data_part->right_date, parts[i]->right_date); - } - ++new_data_part->level; - new_data_part->name = getPartName( - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); - new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); - new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); - - /** Читаем из всех кусков, сливаем и пишем в новый. - * Попутно вычисляем выражение для сортировки. - */ - BlockInputStreams src_streams; - - for (size_t i = 0; i < parts.size(); ++i) - { - MarkRanges ranges(1, MarkRange(0, parts[i]->size)); - src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( - full_path + parts[i]->name + '/', DEFAULT_MERGE_BLOCK_SIZE, all_column_names, *this, parts[i], ranges, - StoragePtr(), false, NULL, ""), primary_expr)); - } - - /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. - /// В слитом куске строки с одинаковым ключом должны идти в порядке возрастания идентификатора исходного куска, то есть (примерного) возрастания времени вставки. - BlockInputStreamPtr merged_stream; - - switch (mode) - { - case Ordinary: - merged_stream = new MergingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); - break; - - case Collapsing: - merged_stream = new CollapsingSortedBlockInputStream(src_streams, sort_descr, sign_column, DEFAULT_MERGE_BLOCK_SIZE); - break; - - case Summing: - merged_stream = new SummingSortedBlockInputStream(src_streams, sort_descr, DEFAULT_MERGE_BLOCK_SIZE); - break; - - default: - throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR); - } - - MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(*this, - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); - - merged_stream->readPrefix(); - to->writePrefix(); - - Block block; - while (!shutdown_called && (block = merged_stream->read())) - to->write(block); - - if (shutdown_called) - { - LOG_INFO(log, "Shutdown requested while merging parts."); - return; - } - - merged_stream->readSuffix(); - to->writeSuffix(); - - /// В обычном режиме строчки не могут удалиться при мердже. - if (0 == to->marksCount() && mode == Ordinary) - throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); - - new_data_part->size = to->marksCount(); - new_data_part->modification_time = time(0); - - if (0 != to->marksCount()) - new_data_part->loadIndex(); /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи. - - { - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - - /// Добавляем новый кусок в набор, если он не пустой. - - for (size_t i = 0; i < parts.size(); ++i) - if (data_parts.end() == data_parts.find(parts[i])) - throw Exception("Logical error: cannot find data part " + parts[i]->name + " in list", ErrorCodes::LOGICAL_ERROR); - - if (0 == to->marksCount()) - { - LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); - } - else - { - data_parts.insert(new_data_part); - all_data_parts.insert(new_data_part); - } - - for (size_t i = 0; i < parts.size(); ++i) - data_parts.erase(data_parts.find(parts[i])); - } - - LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); -} - - -void MergeTreeData::rename(const String & new_path_to_db, const String & new_name) -{ - joinMergeThreads(); - - /// Кажется тут race condition - в этот момент мердж может запуститься снова. - - std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; - - Poco::File(full_path).renameTo(new_full_path); - - path = new_path_to_db; full_path = new_full_path; - name = new_name; - - increment.setPath(full_path + "increment.txt"); } - -void MergeTreeData::dropImpl() +void MergeTreeData::dropAllData() { - joinMergeThreads(); - - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock lock_all(all_data_parts_mutex); - data_parts.clear(); all_data_parts.clear(); @@ -1252,7 +420,7 @@ void MergeTreeData::createConvertExpression(const String & in_column_name, const void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { - if (params.type == ASTAlterQuery::MODIFY) + /*if (params.type == ASTAlterQuery::MODIFY) { { typedef std::vector PartsList; @@ -1360,7 +528,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { String column_name = dynamic_cast(*params.column).name; removeColumnFiles(column_name); - } + }*/ } @@ -1465,4 +633,27 @@ Strings MergeTreeData::tryRestorePart(const String & path, const String & file_n return restored_parts; } +void MergeTreeData::replaceParts(DataPartsVector old_parts, DataPartPtr new_part) +{ + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock all_lock(all_data_parts_mutex); + + for (size_t i = 0; i < old_parts.size(); ++i) + if (data_parts.end() == data_parts.find(old_parts[i])) + throw Exception("Logical error: cannot find data part " + old_parts[i]->name + " in list", ErrorCodes::LOGICAL_ERROR); + + data_parts.insert(new_part); + all_data_parts.insert(new_part); + + for (size_t i = 0; i < old_parts.size(); ++i) + data_parts.erase(data_parts.find(old_parts[i])); +} + +MergeTreeData::DataParts MergeTreeData::getDataParts() +{ + Poco::ScopedLock lock(data_parts_mutex); + + return data_parts; +} + } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp new file mode 100644 index 00000000000..66ff9e0542f --- /dev/null +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +/// Не будем соглашаться мерджить куски, если места на диске менее чем во столько раз больше суммарного размера кусков. +static const double DISK_USAGE_COEFFICIENT = 1.5; + + +/// Выбираем отрезок из не более чем max_parts_to_merge_at_once кусков так, чтобы максимальный размер был меньше чем в max_size_ratio_to_merge_parts раз больше суммы остальных. +/// Это обеспечивает в худшем случае время O(n log n) на все слияния, независимо от выбора сливаемых кусков, порядка слияния и добавления. +/// При max_parts_to_merge_at_once >= log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts), +/// несложно доказать, что всегда будет что сливать, пока количество кусков больше +/// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts)*(количество кусков размером больше max_rows_to_merge_parts). +/// Дальше эвристики. +/// Будем выбирать максимальный по включению подходящий отрезок. +/// Из всех таких выбираем отрезок с минимальным максимумом размера. +/// Из всех таких выбираем отрезок с минимальным минимумом размера. +/// Из всех таких выбираем отрезок с максимальной длиной. +/// Дополнительно: +/// 1) с 1:00 до 5:00 ограничение сверху на размер куска в основном потоке увеличивается в несколько раз +/// 2) в зависимоти от возраста кусков меняется допустимая неравномерность при слиянии +/// 3) Молодые куски крупного размера (примерно больше 1 Гб) можно сливать не меньше чем по три +/// 4) Если в одном из потоков идет мердж крупных кусков, то во втором сливать только маленькие кусочки +/// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности + +bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & parts, size_t available_disk_space, + bool merge_anything_for_old_months, bool aggressive) +{ + LOG_DEBUG(log, "Selecting parts to merge"); + + MergeTreeData::DataParts data_parts = data.getDataParts(); + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + size_t min_max = -1U; + size_t min_min = -1U; + int max_len = 0; + MergeTreeData::DataParts::iterator best_begin; + bool found = false; + + DayNum_t now_day = date_lut.toDayNum(time(0)); + DayNum_t now_month = date_lut.toFirstDayNumOfMonth(now_day); + int now_hour = date_lut.toHourInaccurate(time(0)); + + /// Сколько кусков, начиная с текущего, можно включить в валидный отрезок, начинающийся левее текущего куска. + /// Нужно для определения максимальности по включению. + int max_count_from_left = 0; + + size_t cur_max_rows_to_merge_parts = data.settings.max_rows_to_merge_parts; + + /// Если ночь, можем мерджить сильно большие куски + if (now_hour >= 1 && now_hour <= 5) + cur_max_rows_to_merge_parts *= data.settings.merge_parts_at_night_inc; + + /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. + for (MergeTreeData::DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + if ((*it)->currently_merging && (*it)->size * data.index_granularity > 25 * 1024 * 1024) + { + cur_max_rows_to_merge_parts = data.settings.max_rows_to_merge_parts_second; + break; + } + } + + /// Левый конец отрезка. + for (MergeTreeData::DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + const MergeTreeData::DataPartPtr & first_part = *it; + + max_count_from_left = std::max(0, max_count_from_left - 1); + + /// Кусок не занят. + if (first_part->currently_merging) + continue; + + /// Кусок достаточно мал или слияние "агрессивное". + if (first_part->size * data.index_granularity > cur_max_rows_to_merge_parts + && !aggressive) + continue; + + /// Кусок в одном месяце. + if (first_part->left_month != first_part->right_month) + { + LOG_WARNING(log, "Part " << first_part->name << " spans more than one month"); + continue; + } + + /// Самый длинный валидный отрезок, начинающийся здесь. + size_t cur_longest_max = -1U; + size_t cur_longest_min = -1U; + int cur_longest_len = 0; + + /// Текущий отрезок, не обязательно валидный. + size_t cur_max = first_part->size; + size_t cur_min = first_part->size; + size_t cur_sum = first_part->size; + size_t cur_total_size = first_part->size_in_bytes; + int cur_len = 1; + + DayNum_t month = first_part->left_month; + UInt64 cur_id = first_part->right; + + /// Этот месяц кончился хотя бы день назад. + bool is_old_month = now_day - now_month >= 1 && now_month > month; + + time_t oldest_modification_time = first_part->modification_time; + + /// Правый конец отрезка. + MergeTreeData::DataParts::iterator jt = it; + for (++jt; jt != data_parts.end() && cur_len < static_cast(data.settings.max_parts_to_merge_at_once); ++jt) + { + const MergeTreeData::DataPartPtr & last_part = *jt; + + /// Кусок не занят и в одном правильном месяце. + if (last_part->currently_merging || + last_part->left_month != last_part->right_month || + last_part->left_month != month) + break; + + /// Кусок достаточно мал или слияние "агрессивное". + if (last_part->size * data.index_granularity > cur_max_rows_to_merge_parts + && !aggressive) + break; + + /// Кусок правее предыдущего. + if (last_part->left < cur_id) + { + LOG_WARNING(log, "Part " << last_part->name << " intersects previous part"); + break; + } + + oldest_modification_time = std::max(oldest_modification_time, last_part->modification_time); + cur_max = std::max(cur_max, last_part->size); + cur_min = std::min(cur_min, last_part->size); + cur_sum += last_part->size; + cur_total_size += last_part->size_in_bytes; + ++cur_len; + cur_id = last_part->right; + + int min_len = 2; + int cur_age_in_sec = time(0) - oldest_modification_time; + + /// Если куски примерно больше 1 Gb и образовались меньше 6 часов назад, то мерджить не меньше чем по 3. + if (cur_max * data.index_granularity * 150 > 1024*1024*1024 && cur_age_in_sec < 6*3600) + min_len = 3; + + /// Равен 0.5 если возраст порядка 0, равен 5 если возраст около месяца. + double time_ratio_modifier = 0.5 + 9 * static_cast(cur_age_in_sec) / (3600*24*30 + cur_age_in_sec); + + /// Двоичный логарифм суммарного размера кусочков + double log_cur_sum = std::log(cur_sum * data.index_granularity) / std::log(2); + /// Равен ~2 если куски маленькие, уменьшается до 0.5 с увеличением суммарного размера до 2^25. + double size_ratio_modifier = std::max(0.25, 2 - 3 * (log_cur_sum) / (25 + log_cur_sum)); + + /// Объединяем все в одну константу + double ratio = std::max(0.5, time_ratio_modifier * size_ratio_modifier * data.settings.max_size_ratio_to_merge_parts); + + /// Если отрезок валидный, то он самый длинный валидный, начинающийся тут. + if (cur_len >= min_len + && (static_cast(cur_max) / (cur_sum - cur_max) < ratio + /// За старый месяц объединяем что угодно, если разрешено и если этому хотя бы 15 дней + || (is_old_month && merge_anything_for_old_months && cur_age_in_sec > 3600*24*15) + /// Если слияние "агрессивное", то сливаем что угодно + || aggressive)) + { + /// Достаточно места на диске, чтобы покрыть новый мердж с запасом. + if (available_disk_space > cur_total_size * DISK_USAGE_COEFFICIENT) + { + cur_longest_max = cur_max; + cur_longest_min = cur_min; + cur_longest_len = cur_len; + } + else + LOG_WARNING(log, "Won't merge parts from " << first_part->name << " to " << last_part->name + << " because not enough free space: " << available_disk_space << " free and unreserved, " + << cur_total_size << " required now (+" << static_cast((DISK_USAGE_COEFFICIENT - 1.0) * 100) + << "% on overhead)"); + } + } + + /// Это максимальный по включению валидный отрезок. + if (cur_longest_len > max_count_from_left) + { + max_count_from_left = cur_longest_len; + + if (!found + || std::make_pair(std::make_pair(cur_longest_max, cur_longest_min), -cur_longest_len) + < std::make_pair(std::make_pair(min_max, min_min), -max_len)) + { + found = true; + min_max = cur_longest_max; + min_min = cur_longest_min; + max_len = cur_longest_len; + best_begin = it; + } + } + } + + if (found) + { + parts.clear(); + + MergeTreeData::DataParts::iterator it = best_begin; + for (int i = 0; i < max_len; ++i) + { + parts.push_back(*it); + ++it; + } + + LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); + } + else + { + LOG_DEBUG(log, "No parts to merge"); + } + + return found; +} + + +/// parts должны быть отсортированы. +String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & parts) +{ + LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); + + auto structure = data.getLockedStructure(true); + + Names all_column_names; + NamesAndTypesList columns_list = structure->getColumnsList(); + for (const auto & it : columns_list) + all_column_names.push_back(it.first); + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(data); + new_data_part->left_date = std::numeric_limits::max(); + new_data_part->right_date = std::numeric_limits::min(); + new_data_part->left = parts.front()->left; + new_data_part->right = parts.back()->right; + new_data_part->level = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + new_data_part->level = std::max(new_data_part->level, parts[i]->level); + new_data_part->left_date = std::min(new_data_part->left_date, parts[i]->left_date); + new_data_part->right_date = std::max(new_data_part->right_date, parts[i]->right_date); + } + ++new_data_part->level; + new_data_part->name = MergeTreeData::getPartName( + new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); + new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); + new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); + + /** Читаем из всех кусков, сливаем и пишем в новый. + * Попутно вычисляем выражение для сортировки. + */ + BlockInputStreams src_streams; + + for (size_t i = 0; i < parts.size(); ++i) + { + MarkRanges ranges(1, MarkRange(0, parts[i]->size)); + src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( + structure->getFullPath() + parts[i]->name + '/', structure, DEFAULT_MERGE_BLOCK_SIZE, all_column_names, data, parts[i], ranges, + StoragePtr(), false, NULL, ""), data.primary_expr)); + } + + /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. + /// В слитом куске строки с одинаковым ключом должны идти в порядке возрастания идентификатора исходного куска, то есть (примерного) возрастания времени вставки. + BlockInputStreamPtr merged_stream; + + switch (data.mode) + { + case MergeTreeData::Ordinary: + merged_stream = new MergingSortedBlockInputStream(src_streams, data.sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + break; + + case MergeTreeData::Collapsing: + merged_stream = new CollapsingSortedBlockInputStream(src_streams, data.sort_descr, data.sign_column, DEFAULT_MERGE_BLOCK_SIZE); + break; + + case MergeTreeData::Summing: + merged_stream = new SummingSortedBlockInputStream(src_streams, data.sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + break; + + default: + throw Exception("Unknown mode of operation for MergeTreeData: " + toString(data.mode), ErrorCodes::LOGICAL_ERROR); + } + + MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, structure, + new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); + + merged_stream->readPrefix(); + to->writePrefix(); + + Block block; + while (!canceled && (block = merged_stream->read())) + to->write(block); + + if (canceled) + { + LOG_INFO(log, "Canceled merging parts."); + return ""; + } + + merged_stream->readSuffix(); + to->writeSuffix(); + + /// В обычном режиме строчки не могут удалиться при мердже. + if (0 == to->marksCount() && data.mode == MergeTreeData::Ordinary) + throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); + + new_data_part->size = to->marksCount(); + new_data_part->modification_time = time(0); + + if (0 == to->marksCount()) + { + LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); + return ""; + } + + /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи. + new_data_part->loadIndex(); + + /// Добавляем новый кусок в набор. + data.replaceParts(parts, new_data_part); + + LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); + + return new_data_part->name; +} + +} diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp new file mode 100644 index 00000000000..0508f653128 --- /dev/null +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -0,0 +1,472 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +MergeTreeDataSelectExecutor::MergeTreeDataSelectExecutor(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataSelectExecutor")) +{ + min_marks_for_seek = (data.settings.min_rows_for_seek + data.index_granularity - 1) / data.index_granularity; + min_marks_for_concurrent_read = (data.settings.min_rows_for_concurrent_read + data.index_granularity - 1) / data.index_granularity; + max_marks_to_use_cache = (data.settings.max_rows_to_use_cache + data.index_granularity - 1) / data.index_granularity; + + +} + +BlockInputStreams MergeTreeDataSelectExecutor::read( + const Names & column_names_to_return, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size, + unsigned threads) +{ + MergeTreeData::LockedTableStructurePtr structure = data.getLockedStructure(false); + + structure->check(column_names_to_return); + processed_stage = QueryProcessingStage::FetchColumns; + + PKCondition key_condition(query, data.context, structure->getColumnsList(), data.sort_descr); + PKCondition date_condition(query, data.context, structure->getColumnsList(), SortDescription(1, SortColumnDescription(data.date_column_name, 1))); + + MergeTreeData::DataPartsVector parts; + + /// Выберем куски, в которых могут быть данные, удовлетворяющие date_condition. + { + MergeTreeData::DataParts data_parts = data.getDataParts(); + + for (MergeTreeData::DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + { + Field left = static_cast((*it)->left_date); + Field right = static_cast((*it)->right_date); + + if (date_condition.mayBeTrueInRange(&left, &right)) + parts.push_back(*it); + } + } + + /// Семплирование. + Names column_names_to_read = column_names_to_return; + UInt64 sampling_column_value_limit = 0; + typedef Poco::SharedPtr ASTFunctionPtr; + ASTFunctionPtr filter_function; + ExpressionActionsPtr filter_expression; + + ASTSelectQuery & select = *dynamic_cast(&*query); + if (select.sample_size) + { + double size = apply_visitor(FieldVisitorConvertToNumber(), + dynamic_cast(*select.sample_size).value); + + if (size < 0) + throw Exception("Negative sample size", ErrorCodes::ARGUMENT_OUT_OF_BOUND); + + if (size > 1) + { + size_t requested_count = apply_visitor(FieldVisitorConvertToNumber(), dynamic_cast(*select.sample_size).value); + + /// Узнаем, сколько строк мы бы прочли без семплирования. + LOG_DEBUG(log, "Preliminary index scan with condition: " << key_condition.toString()); + size_t total_count = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + MergeTreeData::DataPartPtr & part = parts[i]; + MarkRanges ranges = markRangesFromPkRange(part->index, key_condition); + + for (size_t j = 0; j < ranges.size(); ++j) + total_count += ranges[j].end - ranges[j].begin; + } + total_count *= data.index_granularity; + + size = std::min(1., static_cast(requested_count) / total_count); + + LOG_DEBUG(log, "Selected relative sample size: " << size); + } + + UInt64 sampling_column_max = 0; + DataTypePtr type = data.primary_expr->getSampleBlock().getByName(data.sampling_expression->getColumnName()).type; + + if (type->getName() == "UInt64") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt32") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt16") + sampling_column_max = std::numeric_limits::max(); + else if (type->getName() == "UInt8") + sampling_column_max = std::numeric_limits::max(); + else + throw Exception("Invalid sampling column type in storage parameters: " + type->getName() + ". Must be unsigned integer type.", ErrorCodes::ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER); + + /// Добавим условие, чтобы отсечь еще что-нибудь при повторном просмотре индекса. + sampling_column_value_limit = static_cast(size * sampling_column_max); + if (!key_condition.addCondition(data.sampling_expression->getColumnName(), + Range::createRightBounded(sampling_column_value_limit, true))) + throw Exception("Sampling column not in primary key", ErrorCodes::ILLEGAL_COLUMN); + + /// Выражение для фильтрации: sampling_expression <= sampling_column_value_limit + + ASTPtr filter_function_args = new ASTExpressionList; + filter_function_args->children.push_back(data.sampling_expression); + filter_function_args->children.push_back(new ASTLiteral(StringRange(), sampling_column_value_limit)); + + filter_function = new ASTFunction; + filter_function->name = "lessOrEquals"; + filter_function->arguments = filter_function_args; + filter_function->children.push_back(filter_function->arguments); + + filter_expression = ExpressionAnalyzer(filter_function, data.context, structure->getColumnsList()).getActions(false); + + /// Добавим столбцы, нужные для sampling_expression. + std::vector add_columns = filter_expression->getRequiredColumns(); + column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); + std::sort(column_names_to_read.begin(), column_names_to_read.end()); + column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); + } + + LOG_DEBUG(log, "Key condition: " << key_condition.toString()); + LOG_DEBUG(log, "Date condition: " << date_condition.toString()); + + /// PREWHERE + ExpressionActionsPtr prewhere_actions; + String prewhere_column; + if (select.prewhere_expression) + { + ExpressionAnalyzer analyzer(select.prewhere_expression, data.context, structure->getColumnsList()); + prewhere_actions = analyzer.getActions(false); + prewhere_column = select.prewhere_expression->getColumnName(); + } + + RangesInDataParts parts_with_ranges; + + /// Найдем, какой диапазон читать из каждого куска. + size_t sum_marks = 0; + size_t sum_ranges = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + MergeTreeData::DataPartPtr & part = parts[i]; + RangesInDataPart ranges(part); + ranges.ranges = markRangesFromPkRange(part->index, key_condition); + + if (!ranges.ranges.empty()) + { + parts_with_ranges.push_back(ranges); + + sum_ranges += ranges.ranges.size(); + for (size_t j = 0; j < ranges.ranges.size(); ++j) + { + sum_marks += ranges.ranges[j].end - ranges.ranges[j].begin; + } + } + } + + LOG_DEBUG(log, "Selected " << parts.size() << " parts by date, " << parts_with_ranges.size() << " parts by key, " + << sum_marks << " marks to read from " << sum_ranges << " ranges"); + + BlockInputStreams res; + + if (select.final) + { + /// Добавим столбцы, нужные для вычисления первичного ключа и знака. + std::vector add_columns = data.primary_expr->getRequiredColumns(); + column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); + column_names_to_read.push_back(data.sign_column); + std::sort(column_names_to_read.begin(), column_names_to_read.end()); + column_names_to_read.erase(std::unique(column_names_to_read.begin(), column_names_to_read.end()), column_names_to_read.end()); + + res = spreadMarkRangesAmongThreadsFinal( + parts_with_ranges, + threads, + column_names_to_read, + max_block_size, + settings.use_uncompressed_cache, + prewhere_actions, + prewhere_column, + structure); + } + else + { + res = spreadMarkRangesAmongThreads( + parts_with_ranges, + threads, + column_names_to_read, + max_block_size, + settings.use_uncompressed_cache, + prewhere_actions, + prewhere_column, + structure); + } + + if (select.sample_size) + { + for (size_t i = 0; i < res.size(); ++i) + { + BlockInputStreamPtr original_stream = res[i]; + BlockInputStreamPtr expression_stream = new ExpressionBlockInputStream(original_stream, filter_expression); + BlockInputStreamPtr filter_stream = new FilterBlockInputStream(expression_stream, filter_function->getColumnName()); + res[i] = filter_stream; + } + } + + return res; +} + +BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column, + const MergeTreeData::LockedTableStructurePtr & structure) +{ + /// На всякий случай перемешаем куски. + std::random_shuffle(parts.begin(), parts.end()); + + /// Посчитаем засечки для каждого куска. + std::vector sum_marks_in_parts(parts.size()); + size_t sum_marks = 0; + for (size_t i = 0; i < parts.size(); ++i) + { + /// Пусть отрезки будут перечислены справа налево, чтобы можно было выбрасывать самый левый отрезок с помощью pop_back(). + std::reverse(parts[i].ranges.begin(), parts[i].ranges.end()); + + sum_marks_in_parts[i] = 0; + for (size_t j = 0; j < parts[i].ranges.size(); ++j) + { + MarkRange & range = parts[i].ranges[j]; + sum_marks_in_parts[i] += range.end - range.begin; + } + sum_marks += sum_marks_in_parts[i]; + } + + if (sum_marks > max_marks_to_use_cache) + use_uncompressed_cache = false; + + BlockInputStreams res; + + if (sum_marks > 0) + { + size_t min_marks_per_thread = (sum_marks - 1) / threads + 1; + + for (size_t i = 0; i < threads && !parts.empty(); ++i) + { + size_t need_marks = min_marks_per_thread; + BlockInputStreams streams; + + /// Цикл по кускам. + while (need_marks > 0 && !parts.empty()) + { + RangesInDataPart & part = parts.back(); + size_t & marks_in_part = sum_marks_in_parts.back(); + + /// Не будем брать из куска слишком мало строк. + if (marks_in_part >= min_marks_for_concurrent_read && + need_marks < min_marks_for_concurrent_read) + need_marks = min_marks_for_concurrent_read; + + /// Не будем оставлять в куске слишком мало строк. + if (marks_in_part > need_marks && + marks_in_part - need_marks < min_marks_for_concurrent_read) + need_marks = marks_in_part; + + /// Возьмем весь кусок, если он достаточно мал. + if (marks_in_part <= need_marks) + { + /// Восстановим порядок отрезков. + std::reverse(part.ranges.begin(), part.ranges.end()); + + streams.push_back(new MergeTreeBlockInputStream( + structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, + part.data_part, part.ranges, data.owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column)); + need_marks -= marks_in_part; + parts.pop_back(); + sum_marks_in_parts.pop_back(); + continue; + } + + MarkRanges ranges_to_get_from_part; + + /// Цикл по отрезкам куска. + while (need_marks > 0) + { + if (part.ranges.empty()) + throw Exception("Unexpected end of ranges while spreading marks among threads", ErrorCodes::LOGICAL_ERROR); + + MarkRange & range = part.ranges.back(); + size_t marks_in_range = range.end - range.begin; + + size_t marks_to_get_from_range = std::min(marks_in_range, need_marks); + ranges_to_get_from_part.push_back(MarkRange(range.begin, range.begin + marks_to_get_from_range)); + range.begin += marks_to_get_from_range; + marks_in_part -= marks_to_get_from_range; + need_marks -= marks_to_get_from_range; + if (range.begin == range.end) + part.ranges.pop_back(); + } + + streams.push_back(new MergeTreeBlockInputStream( + structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, + part.data_part, ranges_to_get_from_part, data.owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column)); + } + + if (streams.size() == 1) + res.push_back(streams[0]); + else + res.push_back(new ConcatBlockInputStream(streams)); + } + + if (!parts.empty()) + throw Exception("Couldn't spread marks among threads", ErrorCodes::LOGICAL_ERROR); + } + + return res; +} + +BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal( + RangesInDataParts parts, + size_t threads, + const Names & column_names, + size_t max_block_size, + bool use_uncompressed_cache, + ExpressionActionsPtr prewhere_actions, + const String & prewhere_column, + const MergeTreeData::LockedTableStructurePtr & structure) +{ + size_t sum_marks = 0; + for (size_t i = 0; i < parts.size(); ++i) + for (size_t j = 0; j < parts[i].ranges.size(); ++j) + sum_marks += parts[i].ranges[j].end - parts[i].ranges[j].begin; + + if (sum_marks > max_marks_to_use_cache) + use_uncompressed_cache = false; + + ExpressionActionsPtr sign_filter_expression; + String sign_filter_column; + createPositiveSignCondition(sign_filter_expression, sign_filter_column, structure); + + BlockInputStreams to_collapse; + + for (size_t part_index = 0; part_index < parts.size(); ++part_index) + { + RangesInDataPart & part = parts[part_index]; + + BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( + structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, + part.data_part, part.ranges, data.owning_storage, use_uncompressed_cache, + prewhere_actions, prewhere_column); + + to_collapse.push_back(new ExpressionBlockInputStream(source_stream, data.primary_expr)); + } + + BlockInputStreams res; + if (to_collapse.size() == 1) + res.push_back(new FilterBlockInputStream(new ExpressionBlockInputStream(to_collapse[0], sign_filter_expression), sign_filter_column)); + else if (to_collapse.size() > 1) + res.push_back(new CollapsingFinalBlockInputStream(to_collapse, data.sort_descr, data.sign_column)); + + return res; +} + +void MergeTreeDataSelectExecutor::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column, + const MergeTreeData::LockedTableStructurePtr & structure) +{ + ASTFunction * function = new ASTFunction; + ASTPtr function_ptr = function; + + ASTExpressionList * arguments = new ASTExpressionList; + ASTPtr arguments_ptr = arguments; + + ASTIdentifier * sign = new ASTIdentifier; + ASTPtr sign_ptr = sign; + + ASTLiteral * one = new ASTLiteral; + ASTPtr one_ptr = one; + + function->name = "equals"; + function->arguments = arguments_ptr; + function->children.push_back(arguments_ptr); + + arguments->children.push_back(sign_ptr); + arguments->children.push_back(one_ptr); + + sign->name = data.sign_column; + sign->kind = ASTIdentifier::Column; + + one->type = new DataTypeInt8; + one->value = Field(static_cast(1)); + + out_expression = ExpressionAnalyzer(function_ptr, data.context, structure->getColumnsList()).getActions(false); + out_column = function->getColumnName(); +} + +/// Получает набор диапазонов засечек, вне которых не могут находиться ключи из заданного диапазона. +MarkRanges MergeTreeDataSelectExecutor::markRangesFromPkRange(const MergeTreeData::DataPart::Index & index, PKCondition & key_condition) +{ + MarkRanges res; + + size_t key_size = data.sort_descr.size(); + size_t marks_count = index.size() / key_size; + + /// Если индекс не используется. + if (key_condition.alwaysTrue()) + { + res.push_back(MarkRange(0, marks_count)); + } + else + { + /** В стеке всегда будут находиться непересекающиеся подозрительные отрезки, самый левый наверху (back). + * На каждом шаге берем левый отрезок и проверяем, подходит ли он. + * Если подходит, разбиваем его на более мелкие и кладем их в стек. Если нет - выбрасываем его. + * Если отрезок уже длиной в одну засечку, добавляем его в ответ и выбрасываем. + */ + std::vector ranges_stack; + ranges_stack.push_back(MarkRange(0, marks_count)); + while (!ranges_stack.empty()) + { + MarkRange range = ranges_stack.back(); + ranges_stack.pop_back(); + + bool may_be_true; + if (range.end == marks_count) + may_be_true = key_condition.mayBeTrueAfter(&index[range.begin * key_size]); + else + may_be_true = key_condition.mayBeTrueInRange(&index[range.begin * key_size], &index[range.end * key_size]); + + if (!may_be_true) + continue; + + if (range.end == range.begin + 1) + { + /// Увидели полезный промежуток между соседними засечками. Либо добавим его к последнему диапазону, либо начнем новый диапазон. + if (res.empty() || range.begin - res.back().end > min_marks_for_seek) + res.push_back(range); + else + res.back().end = range.end; + } + else + { + /// Разбиваем отрезок и кладем результат в стек справа налево. + size_t step = (range.end - range.begin - 1) / data.settings.coarse_index_granularity + 1; + size_t end; + + for (end = range.end; end > range.begin + step; end -= step) + ranges_stack.push_back(MarkRange(end - step, end)); + + ranges_stack.push_back(MarkRange(range.begin, end)); + } + } + } + + return res; +} + +} diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 2fce5fdd26e..0d16416009e 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -1,8 +1,12 @@ #include +#include +#include +#include namespace DB { + StorageMergeTree::StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -12,8 +16,17 @@ StorageMergeTree::StorageMergeTree(const String & path_, const String & name_, N MergeTreeData::Mode mode_, const String & sign_column_, const MergeTreeSettings & settings_) - : data( thisPtr(), path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, - index_granularity_,mode_, sign_column_, settings_) {} + : path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), increment(full_path + "increment.txt"), + data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, + index_granularity_,mode_, sign_column_, settings_), + reader(data), writer(data), merger(data), + log(&Logger::get("StorageMergeTree")), + shutdown_called(false) +{ + merge_threads = new boost::threadpool::pool(data.settings.merging_threads); + + increment.fixIfBroken(data.getMaxDataPartIndex()); +} StoragePtr StorageMergeTree::create( const String & path_, const String & name_, NamesAndTypesListPtr columns_, @@ -26,17 +39,29 @@ StoragePtr StorageMergeTree::create( const String & sign_column_, const MergeTreeSettings & settings_) { - return (new StorageMergeTree( + StorageMergeTree * storage = new StorageMergeTree( path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, - sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); + sampling_expression_, index_granularity_, mode_, sign_column_, settings_); + StoragePtr ptr = storage->thisPtr(); + storage->data.setOwningStorage(ptr); + return ptr; } void StorageMergeTree::shutdown() { - data.shutdown(); + if (shutdown_called) + return; + shutdown_called = true; + merger.cancelAll(); + + joinMergeThreads(); } -StorageMergeTree::~StorageMergeTree() {} + +StorageMergeTree::~StorageMergeTree() +{ + shutdown(); +} BlockInputStreams StorageMergeTree::read( const Names & column_names, @@ -46,27 +71,120 @@ BlockInputStreams StorageMergeTree::read( size_t max_block_size, unsigned threads) { - return data.read(column_names, query, settings, processed_stage, max_block_size, threads); + return reader.read(column_names, query, settings, processed_stage, max_block_size, threads); } BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) { - return data.write(query); + return nullptr; + //return new MergeTreeBlockOutputStream(thisPtr()); } void StorageMergeTree::dropImpl() { - data.dropImpl(); + merger.cancelAll(); + joinMergeThreads(); + data.dropAllData(); } void StorageMergeTree::rename(const String & new_path_to_db, const String & new_name) { - data.rename(new_path_to_db, new_name); + BigLockPtr lock = lockAllOperations(); + + std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; + Poco::File(full_path).renameTo(new_full_path); + + path = new_path_to_db; + name = new_name; + full_path = new_full_path; + + data.setPath(full_path); + + increment.setPath(full_path + "increment.txt"); } void StorageMergeTree::alter(const ASTAlterQuery::Parameters & params) { + /// InterpreterAlterQuery уже взял BigLock. + data.alter(params); } +void StorageMergeTree::merge(size_t iterations, bool async, bool aggressive) +{ + bool while_can = false; + if (iterations == 0) + { + while_can = true; + iterations = data.settings.merging_threads; + } + + for (size_t i = 0; i < iterations; ++i) + merge_threads->schedule(boost::bind(&StorageMergeTree::mergeThread, this, while_can, aggressive)); + + if (!async) + joinMergeThreads(); +} + + +void StorageMergeTree::mergeThread(bool while_can, bool aggressive) +{ + try + { + while (!shutdown_called) + { + /// Удаляем старые куски. На случай, если в слиянии что-то сломано, и из следующего блока вылетит исключение. + data.clearOldParts(); + + size_t disk_space = DiskSpaceMonitor::getUnreservedFreeSpace(full_path); + + { + /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски + MergeTreeData::DataPartsVector parts; + + if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive) && + !merger.selectPartsToMerge(parts, disk_space, true, aggressive)) + break; + + merger.mergeParts(parts); + } + + if (shutdown_called) + break; + + /// Удаляем куски, которые мы только что сливали. + data.clearOldParts(); + + if (!while_can) + break; + } + } + catch (const Exception & e) + { + LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl + << std::endl + << "Stack trace:" << std::endl + << e.getStackTrace().toString()); + } + catch (const Poco::Exception & e) + { + LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); + } + catch (const std::exception & e) + { + LOG_ERROR(log, "std::exception: " << e.what()); + } + catch (...) + { + LOG_ERROR(log, "Unknown exception"); + } +} + + +void StorageMergeTree::joinMergeThreads() +{ + LOG_DEBUG(log, "Waiting for merge threads to finish."); + merge_threads->wait(); +} + } From 00d9c285719981a7fcd1dc5ee7d820cb59674771 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 13 Mar 2014 21:44:00 +0400 Subject: [PATCH 006/281] Merge --- .../MergeTree/MergeTreeBlockOutputStream.h | 9 +- .../DB/Storages/MergeTree/MergeTreeData.h | 115 +++---- .../Storages/MergeTree/MergeTreeDataMerger.h | 20 +- .../Storages/MergeTree/MergeTreeDataWriter.h | 308 ++---------------- dbms/include/DB/Storages/StorageMergeTree.h | 44 ++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 37 ++- .../MergeTree/MergeTreeDataMerger.cpp | 47 ++- .../MergeTree/MergeTreeDataWriter.cpp | 247 ++++++++++++++ dbms/src/Storages/StorageMergeTree.cpp | 31 +- 9 files changed, 479 insertions(+), 379 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h index 0b7f2a7782e..0b2993fbecf 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h @@ -14,12 +14,13 @@ public: void write(const Block & block) { - BlocksList part_blocks = storage.writer.splitBlockIntoParts(block, structure); - for (const Block & current_block : part_blocks) + auto part_blocks = storage.writer.splitBlockIntoParts(block, structure); + for (auto & current_block : part_blocks) { UInt64 temp_index = storage.increment.get(); - String temp_name = storage.writer.writeTempPart(current_block, temp_index, structure); - storage.writer.renameTempPart(temp_name, &storage.increment); + MergeTreeData::DataPartPtr part = storage.writer.writeTempPart(current_block, temp_index, structure); + storage.data.renameTempPartAndAdd(part, &storage.increment, structure); + storage.merge(2); } } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index d8ad6cd0893..0e63ad93b72 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -101,7 +101,9 @@ public: /// Описание куска с данными. struct DataPart { - DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0), currently_merging(false) {} + DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} + + /// Не изменяйте никакие поля для кусков, уже вставленных в таблицу. TODO заменить почти везде const DataPart. MergeTreeData & storage; DayNum_t left_date; @@ -119,9 +121,6 @@ public: DayNum_t left_month; DayNum_t right_month; - /// Смотреть и изменять это поле следует под залоченным data_parts_mutex. - bool currently_merging; - /// Первичный ключ. Всегда загружается в оперативку. typedef std::vector Index; Index index; @@ -163,26 +162,18 @@ public: bool operator< (const DataPart & rhs) const { - if (left_month < rhs.left_month) - return true; - if (left_month > rhs.left_month) - return false; - if (right_month < rhs.right_month) - return true; - if (right_month > rhs.right_month) - return false; + if (left_month != rhs.left_month) + return left_month < rhs.left_month; + if (right_month != rhs.right_month) + return right_month < rhs.right_month; - if (left < rhs.left) - return true; - if (left > rhs.left) - return false; - if (right < rhs.right) - return true; - if (right > rhs.right) - return false; + if (left != rhs.left) + return left < rhs.left; + if (right != rhs.right) + return right < rhs.right; - if (level < rhs.level) - return true; + if (level != rhs.level) + return level < rhs.level; return false; } @@ -265,41 +256,23 @@ public: UInt64 getMaxDataPartIndex(); - /** Возвращает копию списка, чтобы снаружи можно было не заботиться о блокировках. - */ - DataParts getDataParts(); - - /** Удаляет куски old_parts и добавляет кусок new_part. Если какого-нибудь из удаляемых кусков нет, бросает исключение. - */ - void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); - - /** Удалить неактуальные куски. - */ - void clearOldParts(); - - /** После вызова dropAllData больше ничего вызывать нельзя. - */ - void dropAllData(); - - /** Поменять путь к директории с данными. Предполагается, что все данные из старой директории туда перенесли. - * Нужно вызывать под залоченным lockStructure(). - */ - void setPath(const String & full_path); - - /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. - * Нужно вызывать под залоченным lockStructure(). - * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. Для этого есть parts_writing_lock. - */ - void alter(const ASTAlterQuery::Parameters & params); - static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level); + /// Возвращает true если имя директории совпадает с форматом имени директории кусочков + bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const; + + /// Кладет в DataPart данные из имени кусочка. + void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); + /** Изменяемая часть описания таблицы. Содержит лок, запрещающий изменение описания таблицы. * Если в течение какой-то операции структура таблицы должна оставаться неизменной, нужно держать один лок на все ее время. * Например, нужно держать такой лок на время всего запроса SELECT или INSERT и на все время слияния набора кусков * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). - * NOTE: можно перенести сюда другие поля, чтобы сделать их динамически изменяемыми. + * NOTE: Можно перенести сюда другие поля, чтобы сделать их динамически изменяемыми. * Например, index_granularity, sign_column, primary_expr_ast. + * NOTE: Можно вынести эту штуку в IStorage и брать ее в Interpreter-ах, + * чтобы избавиться от оставшихся небольших race conditions. + * Скорее всего, даже можно заменить этой штукой весь механизм отложенного дропа таблиц и убрать owned_storage из потоков блоков. */ class LockedTableStructure : public IColumnsDeclaration { @@ -333,9 +306,41 @@ public: TableStructureWriteLockPtr lockStructure() { return new Poco::ScopedWriteRWLock(table_structure_lock); } + /** Возвращает копию списка, чтобы снаружи можно было не заботиться о блокировках. + */ + DataParts getDataParts(); + + /** Удаляет куски old_parts и добавляет кусок new_part. Если какого-нибудь из удаляемых кусков нет, бросает исключение. + */ + void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); + + /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. + * Если increment!=nullptr, индекс куска бурется из инкремента. Иначе индекс куска не меняется. + */ + void renameTempPartAndAdd(DataPartPtr part, Increment * increment, + const LockedTableStructurePtr & structure); + + /** Удалить неактуальные куски. + */ + void clearOldParts(); + + /** После вызова dropAllData больше ничего вызывать нельзя. + */ + void dropAllData(); + + /** Поменять путь к директории с данными. Предполагается, что все данные из старой директории туда перенесли. + * Нужно вызывать под залоченным lockStructure(). + */ + void setPath(const String & full_path); + + /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. + * Нужно вызывать под залоченным lockStructure(). + * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. Для этого есть parts_writing_lock. + */ + void alter(const ASTAlterQuery::Parameters & params); + /// Эти поля не нужно изменять снаружи. NOTE нужно спрятать их и сделать методы get*. const Context & context; - ASTPtr primary_expr_ast; String date_column_name; ASTPtr sampling_expression; size_t index_granularity; @@ -354,6 +359,8 @@ public: StorageWeakPtr owning_storage; private: + ASTPtr primary_expr_ast; + String full_path; NamesAndTypesListPtr columns; @@ -401,12 +408,6 @@ private: void removeColumnFiles(String column_name); - /// Возвращает true если имя директории совпадает с форматом имени директории кусочков - bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const; - - /// Кладет в DataPart данные из имени кусочка. - void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); - /// Определить, не битые ли данные в директории. Проверяет индекс и засечеки, но не сами данные. bool isBrokenPart(const String & path); diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h index 028bee5acd2..5f10ca5df0e 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h @@ -6,23 +6,31 @@ namespace DB { /** Умеет выбирать куски для слияния и сливать их. - * Требуется внешний механизм координации слияний со вставками и другими слияниями, обеспечивающий: - * - Куски, между которыми еще может появиться новый кусок, нельзя сливать. См. METR-7001. - * - Кусок, который уже сливаются с кем-то в одном месте, нельзя начать сливать в кем-то другим в другом месте. */ class MergeTreeDataMerger { public: MergeTreeDataMerger(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataMerger")), canceled(false) {} - /// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. + typedef boost::function AllowedMergingPredicate; + + /** Выбирает, какие куски слить. Использует кучу эвристик. + * Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. + * + * can_merge - функция, определяющая, можно ли объединить пару соседних кусков. + * Эта функция должна координировать слияния со вставками и другими слияниями, обеспечивая, что: + * - Куски, между которыми еще может появиться новый кусок, нельзя сливать. См. METR-7001. + * - Кусок, который уже сливается с кем-то в одном месте, нельзя начать сливать в кем-то другим в другом месте. + */ bool selectPartsToMerge( MergeTreeData::DataPartsVector & what, size_t available_disk_space, bool merge_anything_for_old_months, - bool aggressive); + bool aggressive, + bool only_small, + const AllowedMergingPredicate & can_merge); - /// Возвращает название нового куска. Если слияние отменили, возвращает пустую строку. + /// Сливает куски. Возвращает название нового куска. Если слияние отменили, возвращает пустую строку. String mergeParts(const MergeTreeData::DataPartsVector & parts); /// Примерное количество места на диске, нужное для мерджа. С запасом. diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index c0766d89f23..eb7c1e45456 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -13,314 +13,50 @@ namespace DB { +struct BlockWithDateInterval +{ + Block block; + UInt16 min_date; + UInt16 max_date; + + BlockWithDateInterval() : min_date(std::numeric_limits::max()), max_date(0) {} + BlockWithDateInterval(const Block & block_, UInt16 min_date_, UInt16 max_date_) + : block(block_), min_date(min_date_), max_date(max_date_) {} +}; + +typedef std::list BlocksWithDateIntervals; + /** Записывает новые куски с данными в merge-дерево. */ class MergeTreeDataWriter { public: - MergeTreeDataWriter(MergeTreeData & data_) : data(data_) {} + MergeTreeDataWriter(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataWriter")), flags(O_TRUNC | O_CREAT | O_WRONLY) {} /** Разбивает блок на блоки, каждый из которых нужно записать в отдельный кусок. * (читай: разбивает строки по месяцам) * Работает детерминированно: если отдать на вход такой же блок, на выходе получатся такие же блоки в таком же порядке. */ - BlocksList splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure); + BlocksWithDateIntervals splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure); /** Все строки должны относиться к одному месяцу. Возвращает название временного куска. * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. - * NOTE потом понадобится возвращать отсюда структуру с контрольными суммами и размерами. + * Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData. */ - String writeTempPart(const Block & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); - - /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. - * Если increment!=nullptr, индекс куска бурется из инкремента. Иначе индекс куска не меняется. - */ - String renameTempPart(const String & temp_name, Increment * increment); + MergeTreeData::DataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); private: MergeTreeData & data; -}; -#if 0 -class MergeTreeDataBlockOutputStream : public IBlockOutputStream -{ -public: - MergeTreeDataBlockOutputStream(MergeTreeData & data, StoragePtr owned_storage) : IBlockOutputStream(owned_storage), storage(data), flags(O_TRUNC | O_CREAT | O_WRONLY) - { - } - - void write(const Block & block) - { - Poco::ScopedReadRWLock write_lock(storage.write_lock); - - storage.check(block, true); - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - size_t rows = block.rows(); - size_t columns = block.columns(); - - /// Достаём столбец с датой. - const ColumnUInt16::Container_t & dates = - dynamic_cast(*block.getByName(storage.date_column_name).column).getData(); - - /// Минимальная и максимальная дата. - UInt16 min_date = std::numeric_limits::max(); - UInt16 max_date = std::numeric_limits::min(); - for (ColumnUInt16::Container_t::const_iterator it = dates.begin(); it != dates.end(); ++it) - { - if (*it < min_date) - min_date = *it; - if (*it > max_date) - max_date = *it; - } - - /// Разделяем на блоки по месяцам. Для каждого ещё посчитаем минимальную и максимальную дату. - typedef std::map BlocksByMonth; - BlocksByMonth blocks_by_month; - - UInt16 min_month = date_lut.toFirstDayNumOfMonth(DayNum_t(min_date)); - UInt16 max_month = date_lut.toFirstDayNumOfMonth(DayNum_t(max_date)); - - /// Типичный случай - когда месяц один (ничего разделять не нужно). - if (min_month == max_month) - blocks_by_month[min_month] = BlockWithDateInterval(block, min_date, max_date); - else - { - for (size_t i = 0; i < rows; ++i) - { - UInt16 month = date_lut.toFirstDayNumOfMonth(DayNum_t(dates[i])); - - BlockWithDateInterval & block_for_month = blocks_by_month[month]; - if (!block_for_month.block) - block_for_month.block = block.cloneEmpty(); - - if (dates[i] < block_for_month.min_date) - block_for_month.min_date = dates[i]; - if (dates[i] > block_for_month.max_date) - block_for_month.max_date = dates[i]; - - for (size_t j = 0; j < columns; ++j) - block_for_month.block.getByPosition(j).column->insert((*block.getByPosition(j).column)[i]); - } - } - - /// Для каждого месяца. - for (BlocksByMonth::iterator it = blocks_by_month.begin(); it != blocks_by_month.end(); ++it) - writePart(it->second.block, it->second.min_date, it->second.max_date); - } - -private: - MergeTreeData & storage; - + Logger * log; + const int flags; - - struct BlockWithDateInterval - { - Block block; - UInt16 min_date; - UInt16 max_date; - - BlockWithDateInterval() : min_date(std::numeric_limits::max()), max_date(0) {} - BlockWithDateInterval(const Block & block_, UInt16 min_date_, UInt16 max_date_) - : block(block_), min_date(min_date_), max_date(max_date_) {} - }; - + typedef std::set OffsetColumns; - - void writePart(Block & block, UInt16 min_date, UInt16 max_date) - { - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - - size_t rows = block.rows(); - size_t columns = block.columns(); - UInt64 tmp_part_id = storage.increment.get(false); - size_t part_size = (rows + storage.index_granularity - 1) / storage.index_granularity; - - String tmp_part_name = storage.getPartName( - DayNum_t(min_date), DayNum_t(max_date), - tmp_part_id, tmp_part_id, 0); - - String part_tmp_path = storage.full_path + "tmp_" + tmp_part_name + "/"; - - Poco::File(part_tmp_path).createDirectories(); - - LOG_TRACE(storage.log, "Calculating primary expression."); - - /// Если для сортировки надо вычислить некоторые столбцы - делаем это. - storage.primary_expr->execute(block); - - LOG_TRACE(storage.log, "Sorting by primary key."); - - /// Сортируем. - stableSortBlock(block, storage.sort_descr); - - /// Наконец-то можно писать данные на диск. - LOG_TRACE(storage.log, "Writing index."); - - /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. - MergeTreeData::DataPart::Index index_vec; - index_vec.reserve(part_size * storage.sort_descr.size()); - - { - WriteBufferFromFile index(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); - - typedef std::vector PrimaryColumns; - PrimaryColumns primary_columns; - - for (size_t i = 0, size = storage.sort_descr.size(); i < size; ++i) - primary_columns.push_back( - !storage.sort_descr[i].column_name.empty() - ? &block.getByName(storage.sort_descr[i].column_name) - : &block.getByPosition(storage.sort_descr[i].column_number)); - - for (size_t i = 0; i < rows; i += storage.index_granularity) - { - for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it) - { - index_vec.push_back((*(*it)->column)[i]); - (*it)->type->serializeBinary(index_vec.back(), index); - } - } - index.next(); - } - - LOG_TRACE(storage.log, "Writing data."); - - /// Множество записанных столбцов со смещениями, чтобы не писать общие для вложенных структур столбцы несколько раз - OffsetColumns offset_columns; - - for (size_t i = 0; i < columns; ++i) - { - const ColumnWithNameAndType & column = block.getByPosition(i); - writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns); - } - - LOG_TRACE(storage.log, "Renaming."); - - /// Добавляем новый кусок в набор. - { - Poco::ScopedLock lock(storage.data_parts_mutex); - Poco::ScopedLock lock_all(storage.all_data_parts_mutex); - - /** Важно, что получение номера куска происходит атомарно с добавлением этого куска в набор. - * Иначе есть race condition - может произойти слияние пары кусков, диапазоны номеров которых - * содержат ещё не добавленный кусок. - */ - UInt64 part_id = storage.increment.get(false); - String part_name = storage.getPartName(DayNum_t(min_date), DayNum_t(max_date), part_id, part_id, 0); - String part_res_path = storage.full_path + part_name + "/"; - - MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(storage); - new_data_part->left_date = DayNum_t(min_date); - new_data_part->right_date = DayNum_t(max_date); - new_data_part->left = part_id; - new_data_part->right = part_id; - new_data_part->level = 0; - new_data_part->name = part_name; - new_data_part->size = part_size; - new_data_part->modification_time = time(0); - new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); - new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); - new_data_part->index.swap(index_vec); - - /// Переименовываем кусок. - Poco::File(part_tmp_path).renameTo(part_res_path); - - storage.data_parts.insert(new_data_part); - storage.all_data_parts.insert(new_data_part); - } - - /// Если на каждую запись делать по две итерации слияния, то дерево будет максимально компактно. - storage.merge(2); - } - /// Записать данные одного столбца. void writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, size_t level = 0) - { - String escaped_column_name = escapeForFileName(name); - size_t size = column.size(); - - /// Для массивов требуется сначала сериализовать размеры, а потом значения. - if (const DataTypeArray * type_arr = dynamic_cast(&type)) - { - String size_name = escapeForFileName(DataTypeNested::extractNestedTableName(name)) - + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - if (offset_columns.count(size_name) == 0) - { - offset_columns.insert(size_name); - - WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); - - size_t prev_mark = 0; - while (prev_mark < size) - { - /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) - writeIntBinary(plain.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type_arr->serializeOffsets(column, compressed, prev_mark, storage.index_granularity); - prev_mark += storage.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain.next(); - marks.next(); - } - } - if (const DataTypeNested * type_nested = dynamic_cast(&type)) - { - String size_name = escaped_column_name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - - WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); - - size_t prev_mark = 0; - while (prev_mark < size) - { - /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) - writeIntBinary(plain.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type_nested->serializeOffsets(column, compressed, prev_mark, storage.index_granularity); - prev_mark += storage.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain.next(); - marks.next(); - } - - { - WriteBufferFromFile plain(path + escaped_column_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + escaped_column_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); - - size_t prev_mark = 0; - while (prev_mark < size) - { - writeIntBinary(plain.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type.serializeBinary(column, compressed, prev_mark, storage.index_granularity); - prev_mark += storage.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain.next(); - marks.next(); - } - } + OffsetColumns & offset_columns, size_t level = 0); }; -#endif + } diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index f9c692d3211..308f7aaf365 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -4,6 +4,7 @@ #include "MergeTree/MergeTreeDataSelectExecutor.h" #include "MergeTree/MergeTreeDataWriter.h" #include "MergeTree/MergeTreeDataMerger.h" +#include "MergeTree/DiskSpaceMonitor.h" namespace DB { @@ -94,10 +95,50 @@ private: MergeTreeDataWriter writer; MergeTreeDataMerger merger; + MergeTreeData::DataParts currently_merging; + Poco::FastMutex currently_merging_mutex; + Logger * log; volatile bool shutdown_called; + Poco::SharedPtr merge_threads; + + /// Пока существует, помечает части как currently_merging и держит резерв места. + /// Вероятно, что части будут помечены заранее. + struct CurrentlyMergingPartsTagger + { + MergeTreeData::DataPartsVector parts; + DiskSpaceMonitor::ReservationPtr reserved_space; + StorageMergeTree & storage; + + CurrentlyMergingPartsTagger(const MergeTreeData::DataPartsVector & parts_, size_t total_size, StorageMergeTree & storage_) + : parts(parts_), storage(storage_) + { + /// Здесь не лочится мьютекс, так как конструктор вызывается внутри mergeThread, где он уже залочен. + reserved_space = DiskSpaceMonitor::reserve(storage.full_path, total_size); /// Может бросить исключение. + storage.currently_merging.insert(parts.begin(), parts.end()); + } + + ~CurrentlyMergingPartsTagger() + { + try + { + Poco::ScopedLock lock(storage.currently_merging_mutex); + for (size_t i = 0; i < parts.size(); ++i) + { + storage.currently_merging.erase(parts[i]); + } + } + catch (...) + { + tryLogCurrentException("~CurrentlyMergingPartsTagger"); + } + } + }; + + typedef Poco::SharedPtr CurrentlyMergingPartsTaggerPtr; + StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -121,7 +162,8 @@ private: /// Дождаться, пока фоновые потоки закончат слияния. void joinMergeThreads(); - Poco::SharedPtr merge_threads; + /// Вызывается во время выбора кусков для слияния. + bool canMergeParts(const MergeTreeData::DataPartPtr & left, const MergeTreeData::DataPartPtr & right); }; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index ee67b5bd3f4..114dfbf1090 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -55,7 +55,7 @@ namespace DB { -static String lastTwoPathComponents(const String & path) +static String lastTwoPathComponents(String path) { if (!path.empty() && *path.rbegin() == '/') path.erase(path.end() - 1); @@ -77,11 +77,11 @@ MergeTreeData::MergeTreeData( Mode mode_, const String & sign_column_, const MergeTreeSettings & settings_) - : context(context_), primary_expr_ast(primary_expr_ast_->clone()), + : context(context_), date_column_name(date_column_name_), sampling_expression(sampling_expression_), index_granularity(index_granularity_), mode(mode_), sign_column(sign_column_), - settings(settings_), + settings(settings_), primary_expr_ast(primary_expr_ast_->clone()), full_path(full_path_), columns(columns_), log(&Logger::get("MergeTreeData: " + lastTwoPathComponents(full_path))), file_name_regexp("^(\\d{8})_(\\d{8})_(\\d+)_(\\d+)_(\\d+)") @@ -649,6 +649,37 @@ void MergeTreeData::replaceParts(DataPartsVector old_parts, DataPartPtr new_part data_parts.erase(data_parts.find(old_parts[i])); } +void MergeTreeData::renameTempPartAndAdd(DataPartPtr part, Increment * increment, + const MergeTreeData::LockedTableStructurePtr & structure) +{ + LOG_TRACE(log, "Renaming."); + + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + + String old_path = structure->getFullPath() + part->name + "/"; + + UInt64 part_id = part->left; + + /** Важно, что получение номера куска происходит атомарно с добавлением этого куска в набор. + * Иначе есть race condition - может произойти слияние пары кусков, диапазоны номеров которых + * содержат ещё не добавленный кусок. + */ + if (increment) + part_id = increment->get(false); + + part->left = part->right = part_id; + part->name = getPartName(part->left_date, part->right_date, part_id, part_id, 0); + + String new_path = structure->getFullPath() + part->name + "/"; + + /// Переименовываем кусок. + Poco::File(old_path).renameTo(new_path); + + data_parts.insert(part); + all_data_parts.insert(part); +} + MergeTreeData::DataParts MergeTreeData::getDataParts() { Poco::ScopedLock lock(data_parts_mutex); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 66ff9e0542f..9a8a5e079f5 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -11,7 +11,11 @@ namespace DB { /// Не будем соглашаться мерджить куски, если места на диске менее чем во столько раз больше суммарного размера кусков. -static const double DISK_USAGE_COEFFICIENT = 1.5; +static const double DISK_USAGE_COEFFICIENT_TO_SELECT = 1.6; + +/// Объединяя куски, зарезервируем столько места на диске. Лучше сделать немного меньше, чем DISK_USAGE_COEFFICIENT_TO_SELECT, +/// потому что между выбором кусков и резервированием места места может стать немного меньше. +static const double DISK_USAGE_COEFFICIENT_TO_RESERVE = 1.4; /// Выбираем отрезок из не более чем max_parts_to_merge_at_once кусков так, чтобы максимальный размер был меньше чем в max_size_ratio_to_merge_parts раз больше суммы остальных. @@ -32,7 +36,7 @@ static const double DISK_USAGE_COEFFICIENT = 1.5; /// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & parts, size_t available_disk_space, - bool merge_anything_for_old_months, bool aggressive) + bool merge_anything_for_old_months, bool aggressive, bool only_small, const AllowedMergingPredicate & can_merge) { LOG_DEBUG(log, "Selecting parts to merge"); @@ -60,14 +64,9 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa if (now_hour >= 1 && now_hour <= 5) cur_max_rows_to_merge_parts *= data.settings.merge_parts_at_night_inc; - /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. - for (MergeTreeData::DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it) + if (only_small) { - if ((*it)->currently_merging && (*it)->size * data.index_granularity > 25 * 1024 * 1024) - { - cur_max_rows_to_merge_parts = data.settings.max_rows_to_merge_parts_second; - break; - } + cur_max_rows_to_merge_parts = data.settings.max_rows_to_merge_parts_second; } /// Левый конец отрезка. @@ -77,10 +76,6 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa max_count_from_left = std::max(0, max_count_from_left - 1); - /// Кусок не занят. - if (first_part->currently_merging) - continue; - /// Кусок достаточно мал или слияние "агрессивное". if (first_part->size * data.index_granularity > cur_max_rows_to_merge_parts && !aggressive) @@ -115,12 +110,18 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa /// Правый конец отрезка. MergeTreeData::DataParts::iterator jt = it; - for (++jt; jt != data_parts.end() && cur_len < static_cast(data.settings.max_parts_to_merge_at_once); ++jt) + for (; cur_len < static_cast(data.settings.max_parts_to_merge_at_once);) { + const MergeTreeData::DataPartPtr & prev_part = *jt; + ++jt; + + if (jt == data_parts.end()) + break; + const MergeTreeData::DataPartPtr & last_part = *jt; - /// Кусок не занят и в одном правильном месяце. - if (last_part->currently_merging || + /// Кусок разрешено сливать с предыдущим, и в одном правильном месяце. + if (!can_merge(prev_part, last_part) || last_part->left_month != last_part->right_month || last_part->left_month != month) break; @@ -172,7 +173,7 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa || aggressive)) { /// Достаточно места на диске, чтобы покрыть новый мердж с запасом. - if (available_disk_space > cur_total_size * DISK_USAGE_COEFFICIENT) + if (available_disk_space > cur_total_size * DISK_USAGE_COEFFICIENT_TO_SELECT) { cur_longest_max = cur_max; cur_longest_min = cur_min; @@ -181,7 +182,7 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa else LOG_WARNING(log, "Won't merge parts from " << first_part->name << " to " << last_part->name << " because not enough free space: " << available_disk_space << " free and unreserved, " - << cur_total_size << " required now (+" << static_cast((DISK_USAGE_COEFFICIENT - 1.0) * 100) + << cur_total_size << " required now (+" << static_cast((DISK_USAGE_COEFFICIENT_TO_SELECT - 1.0) * 100) << "% on overhead)"); } } @@ -336,4 +337,14 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa return new_data_part->name; } +size_t MergeTreeDataMerger::estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts) +{ + size_t res = 0; + for (const MergeTreeData::DataPartPtr & part : parts) + { + res += part->size_in_bytes; + } + return static_cast(res * DISK_USAGE_COEFFICIENT_TO_RESERVE); +} + } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index e69de29bb2d..473b387aa51 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -0,0 +1,247 @@ +#include +#include +#include +#include + +namespace DB +{ + +BlocksWithDateIntervals MergeTreeDataWriter::splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure) +{ + structure->check(block, true); + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + size_t rows = block.rows(); + size_t columns = block.columns(); + + /// Достаём столбец с датой. + const ColumnUInt16::Container_t & dates = + dynamic_cast(*block.getByName(data.date_column_name).column).getData(); + + /// Минимальная и максимальная дата. + UInt16 min_date = std::numeric_limits::max(); + UInt16 max_date = std::numeric_limits::min(); + for (ColumnUInt16::Container_t::const_iterator it = dates.begin(); it != dates.end(); ++it) + { + if (*it < min_date) + min_date = *it; + if (*it > max_date) + max_date = *it; + } + + BlocksWithDateIntervals res; + + UInt16 min_month = date_lut.toFirstDayNumOfMonth(DayNum_t(min_date)); + UInt16 max_month = date_lut.toFirstDayNumOfMonth(DayNum_t(max_date)); + + /// Типичный случай - когда месяц один (ничего разделять не нужно). + if (min_month == max_month) + { + res.push_back(BlockWithDateInterval(block, min_date, max_date)); + return res; + } + + /// Разделяем на блоки по месяцам. Для каждого ещё посчитаем минимальную и максимальную дату. + typedef std::map BlocksByMonth; + BlocksByMonth blocks_by_month; + + for (size_t i = 0; i < rows; ++i) + { + UInt16 month = date_lut.toFirstDayNumOfMonth(DayNum_t(dates[i])); + + BlockWithDateInterval *& block_for_month = blocks_by_month[month]; + if (!block_for_month) + { + block_for_month = &*res.insert(res.end(), BlockWithDateInterval()); + block_for_month->block = block.cloneEmpty(); + } + + if (dates[i] < block_for_month->min_date) + block_for_month->min_date = dates[i]; + if (dates[i] > block_for_month->max_date) + block_for_month->max_date = dates[i]; + + for (size_t j = 0; j < columns; ++j) + block_for_month->block.getByPosition(j).column->insert((*block.getByPosition(j).column)[i]); + } + + return res; +} + +MergeTreeData::DataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInterval & block_with_dates, UInt64 temp_index, + const MergeTreeData::LockedTableStructurePtr & structure) +{ + Block & block = block_with_dates.block; + UInt16 min_date = block_with_dates.min_date; + UInt16 max_date = block_with_dates.max_date; + + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + + size_t rows = block.rows(); + size_t columns = block.columns(); + size_t part_size = (rows + data.index_granularity - 1) / data.index_granularity; + + String tmp_part_name = "tmp_" + data.getPartName( + DayNum_t(min_date), DayNum_t(max_date), + temp_index, temp_index, 0); + + String part_tmp_path = structure->getFullPath() + tmp_part_name + "/"; + + Poco::File(part_tmp_path).createDirectories(); + + LOG_TRACE(log, "Calculating primary expression."); + + /// Если для сортировки надо вычислить некоторые столбцы - делаем это. + data.primary_expr->execute(block); + + LOG_TRACE(log, "Sorting by primary key."); + + /// Сортируем. + stableSortBlock(block, data.sort_descr); + + /// Наконец-то можно писать данные на диск. + LOG_TRACE(log, "Writing index."); + + /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. + MergeTreeData::DataPart::Index index_vec; + index_vec.reserve(part_size * data.sort_descr.size()); + + { + WriteBufferFromFile index(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); + + typedef std::vector PrimaryColumns; + PrimaryColumns primary_columns; + + for (size_t i = 0, size = data.sort_descr.size(); i < size; ++i) + primary_columns.push_back( + !data.sort_descr[i].column_name.empty() + ? &block.getByName(data.sort_descr[i].column_name) + : &block.getByPosition(data.sort_descr[i].column_number)); + + for (size_t i = 0; i < rows; i += data.index_granularity) + { + for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it) + { + index_vec.push_back((*(*it)->column)[i]); + (*it)->type->serializeBinary(index_vec.back(), index); + } + } + + index.next(); + } + + LOG_TRACE(log, "Writing data."); + + /// Множество записанных столбцов со смещениями, чтобы не писать общие для вложенных структур столбцы несколько раз + OffsetColumns offset_columns; + + for (size_t i = 0; i < columns; ++i) + { + const ColumnWithNameAndType & column = block.getByPosition(i); + writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns); + } + + MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(data); + new_data_part->left_date = DayNum_t(min_date); + new_data_part->right_date = DayNum_t(max_date); + new_data_part->left = temp_index; + new_data_part->right = temp_index; + new_data_part->level = 0; + new_data_part->name = tmp_part_name; + new_data_part->size = part_size; + new_data_part->modification_time = time(0); + new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); + new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); + new_data_part->index.swap(index_vec); + + return new_data_part; +} + +void MergeTreeDataWriter::writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, + OffsetColumns & offset_columns, size_t level) +{ + String escaped_column_name = escapeForFileName(name); + size_t size = column.size(); + + /// Для массивов требуется сначала сериализовать размеры, а потом значения. + if (const DataTypeArray * type_arr = dynamic_cast(&type)) + { + String size_name = escapeForFileName(DataTypeNested::extractNestedTableName(name)) + + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); + if (offset_columns.count(size_name) == 0) + { + offset_columns.insert(size_name); + + WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); + CompressedWriteBuffer compressed(plain); + + size_t prev_mark = 0; + while (prev_mark < size) + { + /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) + writeIntBinary(plain.count(), marks); + writeIntBinary(compressed.offset(), marks); + + type_arr->serializeOffsets(column, compressed, prev_mark, data.index_granularity); + prev_mark += data.index_granularity; + + compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. + } + + compressed.next(); + plain.next(); + marks.next(); + } + } + if (const DataTypeNested * type_nested = dynamic_cast(&type)) + { + String size_name = escaped_column_name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); + + WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); + CompressedWriteBuffer compressed(plain); + + size_t prev_mark = 0; + while (prev_mark < size) + { + /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) + writeIntBinary(plain.count(), marks); + writeIntBinary(compressed.offset(), marks); + + type_nested->serializeOffsets(column, compressed, prev_mark, data.index_granularity); + prev_mark += data.index_granularity; + + compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. + } + + compressed.next(); + plain.next(); + marks.next(); + } + + { + WriteBufferFromFile plain(path + escaped_column_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile marks(path + escaped_column_name + ".mrk", 4096, flags); + CompressedWriteBuffer compressed(plain); + + size_t prev_mark = 0; + while (prev_mark < size) + { + writeIntBinary(plain.count(), marks); + writeIntBinary(compressed.offset(), marks); + + type.serializeBinary(column, compressed, prev_mark, data.index_granularity); + prev_mark += data.index_granularity; + + compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. + } + + compressed.next(); + plain.next(); + marks.next(); + } +} + +} diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 0d16416009e..e8ba4624172 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -76,8 +76,7 @@ BlockInputStreams StorageMergeTree::read( BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) { - return nullptr; - //return new MergeTreeBlockOutputStream(thisPtr()); + return new MergeTreeBlockOutputStream(thisPtr()); } void StorageMergeTree::dropImpl() @@ -139,13 +138,32 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) size_t disk_space = DiskSpaceMonitor::getUnreservedFreeSpace(full_path); { + /// Нужно вызывать деструктор под незалоченным currently_merging_mutex. + CurrentlyMergingPartsTaggerPtr merging_tagger; + + Poco::ScopedLock lock(currently_merging_mutex); + /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски MergeTreeData::DataPartsVector parts; + auto can_merge = boost::bind(&StorageMergeTree::canMergeParts, this, _1, _2); + bool only_small = false; - if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive) && - !merger.selectPartsToMerge(parts, disk_space, true, aggressive)) + /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. + for (const auto & part : currently_merging) + { + if (part->size * data.index_granularity > 25 * 1024 * 1024) + { + only_small = true; + break; + } + } + + if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && + !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) break; + merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); + merger.mergeParts(parts); } @@ -187,4 +205,9 @@ void StorageMergeTree::joinMergeThreads() merge_threads->wait(); } +bool StorageMergeTree::canMergeParts(const MergeTreeData::DataPartPtr & left, const MergeTreeData::DataPartPtr & right) +{ + return !currently_merging.count(left) && !currently_merging.count(right); +} + } From 2a766770fc76593e7352f8a07803883797f6f4cb Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 13 Mar 2014 23:07:17 +0400 Subject: [PATCH 007/281] Merge --- .../DB/Storages/MergeTree/MergeTreeData.h | 1 + dbms/include/DB/Storages/StorageMergeTree.h | 11 ++++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 3 ++ dbms/src/Storages/StorageMergeTree.cpp | 40 ++++++++++--------- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 0e63ad93b72..e9de97a851b 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -329,6 +329,7 @@ public: void dropAllData(); /** Поменять путь к директории с данными. Предполагается, что все данные из старой директории туда перенесли. + * Сбрасывает кеши разжатых блоков и засечек. * Нужно вызывать под залоченным lockStructure(). */ void setPath(const String & full_path); diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 308f7aaf365..970bd24093a 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -117,6 +117,11 @@ private: { /// Здесь не лочится мьютекс, так как конструктор вызывается внутри mergeThread, где он уже залочен. reserved_space = DiskSpaceMonitor::reserve(storage.full_path, total_size); /// Может бросить исключение. + for (const auto & part : parts) + { + if (storage.currently_merging.count(part)) + throw Exception("Tagging alreagy tagged part " + part->name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + } storage.currently_merging.insert(parts.begin(), parts.end()); } @@ -125,9 +130,11 @@ private: try { Poco::ScopedLock lock(storage.currently_merging_mutex); - for (size_t i = 0; i < parts.size(); ++i) + for (const auto & part : parts) { - storage.currently_merging.erase(parts[i]); + if (!storage.currently_merging.count(part)) + throw Exception("Untagging already untagged part " + part->name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + storage.currently_merging.erase(part); } } catch (...) diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 114dfbf1090..9f872d999b0 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -353,6 +353,9 @@ void MergeTreeData::clearOldParts() void MergeTreeData::setPath(const String & new_full_path) { full_path = new_full_path; + context.getUncompressedCache()->reset(); + context.getMarkCache()->reset(); + log = &Logger::get(lastTwoPathComponents(full_path)); } void MergeTreeData::dropAllData() diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index e8ba4624172..244f799a851 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -138,33 +138,35 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) size_t disk_space = DiskSpaceMonitor::getUnreservedFreeSpace(full_path); { + /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски /// Нужно вызывать деструктор под незалоченным currently_merging_mutex. CurrentlyMergingPartsTaggerPtr merging_tagger; - Poco::ScopedLock lock(currently_merging_mutex); - - /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски - MergeTreeData::DataPartsVector parts; - auto can_merge = boost::bind(&StorageMergeTree::canMergeParts, this, _1, _2); - bool only_small = false; - - /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. - for (const auto & part : currently_merging) { - if (part->size * data.index_granularity > 25 * 1024 * 1024) + Poco::ScopedLock lock(currently_merging_mutex); + + MergeTreeData::DataPartsVector parts; + auto can_merge = boost::bind(&StorageMergeTree::canMergeParts, this, _1, _2); + bool only_small = false; + + /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. + for (const auto & part : currently_merging) { - only_small = true; - break; + if (part->size * data.index_granularity > 25 * 1024 * 1024) + { + only_small = true; + break; + } } + + if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && + !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) + break; + + merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); } - if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && - !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) - break; - - merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); - - merger.mergeParts(parts); + merger.mergeParts(merging_tagger->parts); } if (shutdown_called) From 5b67cc94ce0a3eb430a555f8c4c03e23d409bef8 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 13 Mar 2014 23:14:25 +0400 Subject: [PATCH 008/281] Small style change. [#METR-10202] --- dbms/include/DB/Storages/MergeTree/MergeTreeData.h | 3 ++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 6 ++++++ dbms/src/Storages/StorageMergeTree.cpp | 5 ++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index e9de97a851b..20629798f25 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -325,10 +325,11 @@ public: void clearOldParts(); /** После вызова dropAllData больше ничего вызывать нельзя. + * Удаляет директорию с данными и сбрасывает кеши разжатых блоков и засечек. */ void dropAllData(); - /** Поменять путь к директории с данными. Предполагается, что все данные из старой директории туда перенесли. + /** Перемещает всю директорию с данными. * Сбрасывает кеши разжатых блоков и засечек. * Нужно вызывать под залоченным lockStructure(). */ diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 9f872d999b0..09e461174cb 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -352,9 +352,12 @@ void MergeTreeData::clearOldParts() void MergeTreeData::setPath(const String & new_full_path) { + Poco::File(full_path).renameTo(new_full_path); full_path = new_full_path; + context.getUncompressedCache()->reset(); context.getMarkCache()->reset(); + log = &Logger::get(lastTwoPathComponents(full_path)); } @@ -363,6 +366,9 @@ void MergeTreeData::dropAllData() data_parts.clear(); all_data_parts.clear(); + context.getUncompressedCache()->reset(); + context.getMarkCache()->reset(); + Poco::File(full_path).remove(true); } diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 244f799a851..9b38477a71c 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -91,14 +91,13 @@ void StorageMergeTree::rename(const String & new_path_to_db, const String & new_ BigLockPtr lock = lockAllOperations(); std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; - Poco::File(full_path).renameTo(new_full_path); + + data.setPath(new_full_path); path = new_path_to_db; name = new_name; full_path = new_full_path; - data.setPath(full_path); - increment.setPath(full_path + "increment.txt"); } From 2c81592a9bfce2950ef3ee1438ab983b41bac13a Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 13 Mar 2014 23:36:28 +0400 Subject: [PATCH 009/281] Merge --- .../DB/Storages/MergeTree/MergeTreeData.h | 18 ++++----- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 38 ++++++++++++------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 20629798f25..7bcfc436b24 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -45,11 +45,9 @@ namespace DB * - Summing - при склейке кусков, при совпадении PK суммировать все числовые столбцы, не входящие в PK. */ -/// NOTE: Следующее пока не правда. Сейчас тут практически весь StorageMergeTree. Лишние части нужно перенести отсюда в StorageMergeTree. - /** Этот класс хранит список кусков и параметры структуры данных. * Для чтения и изменения данных используются отдельные классы: - * - MergeTreeDataReader + * - MergeTreeDataSelectExecutor * - MergeTreeDataWriter * - MergeTreeDataMerger */ @@ -103,7 +101,7 @@ public: { DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} - /// Не изменяйте никакие поля для кусков, уже вставленных в таблицу. TODO заменить почти везде const DataPart. + /// Не изменяйте никакие поля для кусков, уже вставленных в таблицу. TODO заменить почти везде на const DataPart. MergeTreeData & storage; DayNum_t left_date; @@ -286,10 +284,12 @@ public: const MergeTreeData & data; Poco::SharedPtr parts_lock; - Poco::ScopedReadRWLock structure_lock; + Poco::SharedPtr structure_lock; - LockedTableStructure(const MergeTreeData & data_, bool lock_writing) - : data(data_), parts_lock(lock_writing ? new Poco::ScopedReadRWLock(data.parts_writing_lock) : nullptr), structure_lock(data.table_structure_lock) {} + LockedTableStructure(const MergeTreeData & data_, bool lock_structure, bool lock_writing) + : data(data_), + parts_lock(lock_writing ? new Poco::ScopedReadRWLock(data.parts_writing_lock) : nullptr), + structure_lock(lock_structure ? new Poco::ScopedReadRWLock(data.table_structure_lock) : nullptr) {} }; typedef Poco::SharedPtr LockedTableStructurePtr; @@ -299,7 +299,7 @@ public: */ LockedTableStructurePtr getLockedStructure(bool will_modify_parts) const { - return new LockedTableStructure(*this, will_modify_parts); + return new LockedTableStructure(*this, true, will_modify_parts); } typedef Poco::SharedPtr TableStructureWriteLockPtr; @@ -337,7 +337,7 @@ public: /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. * Нужно вызывать под залоченным lockStructure(). - * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. Для этого есть parts_writing_lock. + * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. На долгую часть достаточно лочить parts_writing_lock. */ void alter(const ASTAlterQuery::Parameters & params); diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 09e461174cb..01d5ab7a4e1 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -427,33 +427,39 @@ void MergeTreeData::createConvertExpression(const String & in_column_name, const out_column = function->getColumnName(); } +static DataTypePtr getDataTypeByName(const String & name, const NamesAndTypesList & columns) +{ + for (const auto & it : columns) + { + if (it.first == name) + return it.second; + } + throw Exception("No column " + name + " in table", ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); +} + void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { - /*if (params.type == ASTAlterQuery::MODIFY) + if (params.type == ASTAlterQuery::MODIFY) { { - typedef std::vector PartsList; - PartsList parts; + DataPartsVector parts; { Poco::ScopedLock lock(data_parts_mutex); - for (auto & data_part : data_parts) - { - parts.push_back(data_part); - } + parts = DataPartsVector(data_parts.begin(), data_parts.end()); } Names column_name; const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); StringRange type_range = name_type.type->range; String type(type_range.first, type_range.second - type_range.first); - DB::DataTypePtr old_type_ptr = getDataTypeByName(name_type.name); - DB::DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); + DataTypePtr old_type_ptr = getDataTypeByName(name_type.name, *columns); + DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) - throw DB::Exception("ALTER MODIFY not supported for nested and array types"); + throw Exception("ALTER MODIFY not supported for nested and array types"); column_name.push_back(name_type.name); - DB::ExpressionActionsPtr expr; + ExpressionActionsPtr expr; String out_column; createConvertExpression(name_type.name, type, expr, out_column); @@ -462,7 +468,8 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { MarkRanges ranges(1, MarkRange(0, part->size)); ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', - DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, StoragePtr(), false, NULL, ""), expr); + new LockedTableStructure(*this, false, false), /// Не блокируем структуру таблицы, она уже заблокирована снаружи. + DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, StoragePtr(), false, NULL, ""), expr); MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); out.writePrefix(); @@ -531,13 +538,16 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { Poco::ScopedLock lock(data_parts_mutex); Poco::ScopedLock lock_all(all_data_parts_mutex); - alterColumns(params, columns, context); + IColumnsDeclaration::alterColumns(params, columns, context); } if (params.type == ASTAlterQuery::DROP) { String column_name = dynamic_cast(*params.column).name; removeColumnFiles(column_name); - }*/ + + context.getUncompressedCache()->reset(); + context.getMarkCache()->reset(); + } } From d5e957818bd4929996e0132c993ddf3866c18f5e Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 14 Mar 2014 11:05:43 +0400 Subject: [PATCH 010/281] Merge --- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 56 +++---------------- 1 file changed, 8 insertions(+), 48 deletions(-) diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 01d5ab7a4e1..763f90ee449 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -1,55 +1,15 @@ -#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 namespace DB From ba9315d26469318c9cbf5c6b33dc972b15fa2f4d Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 14 Mar 2014 21:03:52 +0400 Subject: [PATCH 011/281] Merge --- .../CollapsingFinalBlockInputStream.h | 2 +- .../CollapsingSortedBlockInputStream.h | 2 +- .../MergingSortedBlockInputStream.h | 2 +- .../SummingSortedBlockInputStream.h | 2 +- .../DB/Storages/MergeTree/MergeTreeData.h | 21 ++++++++++++------- .../MergeTree/MergedBlockOutputStream.h | 8 +++---- .../MergeTree/MergeTreeDataMerger.cpp | 8 +++---- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 18 ++++++++-------- .../MergeTree/MergeTreeDataWriter.cpp | 16 +++++++------- 9 files changed, 43 insertions(+), 36 deletions(-) diff --git a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h index 29b916ce63c..33f32d964ce 100644 --- a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h +++ b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h @@ -14,7 +14,7 @@ namespace DB class CollapsingFinalBlockInputStream : public IProfilingBlockInputStream { public: - CollapsingFinalBlockInputStream(BlockInputStreams inputs_, SortDescription & description_, + CollapsingFinalBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, const String & sign_column_) : description(description_), sign_column(sign_column_), log(&Logger::get("CollapsingSortedBlockInputStream")), diff --git a/dbms/include/DB/DataStreams/CollapsingSortedBlockInputStream.h b/dbms/include/DB/DataStreams/CollapsingSortedBlockInputStream.h index 34968d00543..91cf5d8908a 100644 --- a/dbms/include/DB/DataStreams/CollapsingSortedBlockInputStream.h +++ b/dbms/include/DB/DataStreams/CollapsingSortedBlockInputStream.h @@ -23,7 +23,7 @@ namespace DB class CollapsingSortedBlockInputStream : public MergingSortedBlockInputStream { public: - CollapsingSortedBlockInputStream(BlockInputStreams inputs_, SortDescription & description_, + CollapsingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, const String & sign_column_, size_t max_block_size_) : MergingSortedBlockInputStream(inputs_, description_, max_block_size_), sign_column(sign_column_), sign_column_number(0), diff --git a/dbms/include/DB/DataStreams/MergingSortedBlockInputStream.h b/dbms/include/DB/DataStreams/MergingSortedBlockInputStream.h index 698bb30e59b..72a2de8ded2 100644 --- a/dbms/include/DB/DataStreams/MergingSortedBlockInputStream.h +++ b/dbms/include/DB/DataStreams/MergingSortedBlockInputStream.h @@ -19,7 +19,7 @@ class MergingSortedBlockInputStream : public IProfilingBlockInputStream { public: /// limit - если не 0, то можно выдать только первые limit строк в сортированном порядке. - MergingSortedBlockInputStream(BlockInputStreams inputs_, SortDescription & description_, size_t max_block_size_, size_t limit_ = 0) + MergingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, size_t max_block_size_, size_t limit_ = 0) : description(description_), max_block_size(max_block_size_), limit(limit_), total_merged_rows(0), first(true), has_collation(false), num_columns(0), source_blocks(inputs_.size()), cursors(inputs_.size()), log(&Logger::get("MergingSortedBlockInputStream")) { diff --git a/dbms/include/DB/DataStreams/SummingSortedBlockInputStream.h b/dbms/include/DB/DataStreams/SummingSortedBlockInputStream.h index 8e7c9a1ca31..7c90a8b3d36 100644 --- a/dbms/include/DB/DataStreams/SummingSortedBlockInputStream.h +++ b/dbms/include/DB/DataStreams/SummingSortedBlockInputStream.h @@ -18,7 +18,7 @@ namespace DB class SummingSortedBlockInputStream : public MergingSortedBlockInputStream { public: - SummingSortedBlockInputStream(BlockInputStreams inputs_, SortDescription & description_, size_t max_block_size_) + SummingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, size_t max_block_size_) : MergingSortedBlockInputStream(inputs_, description_, max_block_size_), log(&Logger::get("SummingSortedBlockInputStream")), current_row_is_zero(false) { diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 7bcfc436b24..5955feb87f0 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -245,6 +245,8 @@ public: */ void setOwningStorage(StoragePtr storage) { owning_storage = storage; } + StoragePtr getOwningStorage() const { return owning_storage; } + std::string getModePrefix() const; std::string getSignColumnName() const { return sign_column; } @@ -341,26 +343,29 @@ public: */ void alter(const ASTAlterQuery::Parameters & params); - /// Эти поля не нужно изменять снаружи. NOTE нужно спрятать их и сделать методы get*. + + ExpressionActionsPtr getPrimaryExpression() const { return primary_expr; } + SortDescription getSortDescription() const { return sort_descr; } + const Context & context; - String date_column_name; - ASTPtr sampling_expression; - size_t index_granularity; + const String date_column_name; + const ASTPtr sampling_expression; + const size_t index_granularity; /// Режим работы - какие дополнительные действия делать при мердже. - Mode mode; + const Mode mode; /// Для схлопывания записей об изменениях, если используется Collapsing режим работы. - String sign_column; + const String sign_column; - MergeTreeSettings settings; + const MergeTreeSettings settings; +private: ExpressionActionsPtr primary_expr; SortDescription sort_descr; Block primary_key_sample; StorageWeakPtr owning_storage; -private: ASTPtr primary_expr_ast; String full_path; diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index a5af398ceef..b90d527d74c 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -224,11 +224,11 @@ public: typedef std::vector PrimaryColumns; PrimaryColumns primary_columns; - for (size_t i = 0, size = storage.sort_descr.size(); i < size; ++i) + for (const auto & descr : storage.getSortDescription()) primary_columns.push_back( - !storage.sort_descr[i].column_name.empty() - ? &block.getByName(storage.sort_descr[i].column_name) - : &block.getByPosition(storage.sort_descr[i].column_number)); + !descr.column_name.empty() + ? &block.getByName(descr.column_name) + : &block.getByPosition(descr.column_number)); for (size_t i = index_offset; i < rows; i += storage.index_granularity) { diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 9a8a5e079f5..e10029449e3 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -269,7 +269,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa MarkRanges ranges(1, MarkRange(0, parts[i]->size)); src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( structure->getFullPath() + parts[i]->name + '/', structure, DEFAULT_MERGE_BLOCK_SIZE, all_column_names, data, parts[i], ranges, - StoragePtr(), false, NULL, ""), data.primary_expr)); + StoragePtr(), false, NULL, ""), data.getPrimaryExpression())); } /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. @@ -279,15 +279,15 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa switch (data.mode) { case MergeTreeData::Ordinary: - merged_stream = new MergingSortedBlockInputStream(src_streams, data.sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + merged_stream = new MergingSortedBlockInputStream(src_streams, data.getSortDescription(), DEFAULT_MERGE_BLOCK_SIZE); break; case MergeTreeData::Collapsing: - merged_stream = new CollapsingSortedBlockInputStream(src_streams, data.sort_descr, data.sign_column, DEFAULT_MERGE_BLOCK_SIZE); + merged_stream = new CollapsingSortedBlockInputStream(src_streams, data.getSortDescription(), data.sign_column, DEFAULT_MERGE_BLOCK_SIZE); break; case MergeTreeData::Summing: - merged_stream = new SummingSortedBlockInputStream(src_streams, data.sort_descr, DEFAULT_MERGE_BLOCK_SIZE); + merged_stream = new SummingSortedBlockInputStream(src_streams, data.getSortDescription(), DEFAULT_MERGE_BLOCK_SIZE); break; default: diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 0508f653128..ddcc811901d 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -32,7 +32,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( structure->check(column_names_to_return); processed_stage = QueryProcessingStage::FetchColumns; - PKCondition key_condition(query, data.context, structure->getColumnsList(), data.sort_descr); + PKCondition key_condition(query, data.context, structure->getColumnsList(), data.getSortDescription()); PKCondition date_condition(query, data.context, structure->getColumnsList(), SortDescription(1, SortColumnDescription(data.date_column_name, 1))); MergeTreeData::DataPartsVector parts; @@ -90,7 +90,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( } UInt64 sampling_column_max = 0; - DataTypePtr type = data.primary_expr->getSampleBlock().getByName(data.sampling_expression->getColumnName()).type; + DataTypePtr type = data.getPrimaryExpression()->getSampleBlock().getByName(data.sampling_expression->getColumnName()).type; if (type->getName() == "UInt64") sampling_column_max = std::numeric_limits::max(); @@ -173,7 +173,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( if (select.final) { /// Добавим столбцы, нужные для вычисления первичного ключа и знака. - std::vector add_columns = data.primary_expr->getRequiredColumns(); + std::vector add_columns = data.getPrimaryExpression()->getRequiredColumns(); column_names_to_read.insert(column_names_to_read.end(), add_columns.begin(), add_columns.end()); column_names_to_read.push_back(data.sign_column); std::sort(column_names_to_read.begin(), column_names_to_read.end()); @@ -284,7 +284,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( streams.push_back(new MergeTreeBlockInputStream( structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, part.ranges, data.owning_storage, use_uncompressed_cache, + part.data_part, part.ranges, data.getOwningStorage(), use_uncompressed_cache, prewhere_actions, prewhere_column)); need_marks -= marks_in_part; parts.pop_back(); @@ -314,7 +314,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( streams.push_back(new MergeTreeBlockInputStream( structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, ranges_to_get_from_part, data.owning_storage, use_uncompressed_cache, + part.data_part, ranges_to_get_from_part, data.getOwningStorage(), use_uncompressed_cache, prewhere_actions, prewhere_column)); } @@ -361,17 +361,17 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, part.ranges, data.owning_storage, use_uncompressed_cache, + part.data_part, part.ranges, data.getOwningStorage(), use_uncompressed_cache, prewhere_actions, prewhere_column); - to_collapse.push_back(new ExpressionBlockInputStream(source_stream, data.primary_expr)); + to_collapse.push_back(new ExpressionBlockInputStream(source_stream, data.getPrimaryExpression())); } BlockInputStreams res; if (to_collapse.size() == 1) res.push_back(new FilterBlockInputStream(new ExpressionBlockInputStream(to_collapse[0], sign_filter_expression), sign_filter_column)); else if (to_collapse.size() > 1) - res.push_back(new CollapsingFinalBlockInputStream(to_collapse, data.sort_descr, data.sign_column)); + res.push_back(new CollapsingFinalBlockInputStream(to_collapse, data.getSortDescription(), data.sign_column)); return res; } @@ -413,7 +413,7 @@ MarkRanges MergeTreeDataSelectExecutor::markRangesFromPkRange(const MergeTreeDat { MarkRanges res; - size_t key_size = data.sort_descr.size(); + size_t key_size = data.getSortDescription().size(); size_t marks_count = index.size() / key_size; /// Если индекс не используется. diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 473b387aa51..daf42ae50de 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -93,19 +93,21 @@ MergeTreeData::DataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInter LOG_TRACE(log, "Calculating primary expression."); /// Если для сортировки надо вычислить некоторые столбцы - делаем это. - data.primary_expr->execute(block); + data.getPrimaryExpression()->execute(block); LOG_TRACE(log, "Sorting by primary key."); + SortDescription sort_descr = data.getSortDescription(); + /// Сортируем. - stableSortBlock(block, data.sort_descr); + stableSortBlock(block, sort_descr); /// Наконец-то можно писать данные на диск. LOG_TRACE(log, "Writing index."); /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. MergeTreeData::DataPart::Index index_vec; - index_vec.reserve(part_size * data.sort_descr.size()); + index_vec.reserve(part_size * sort_descr.size()); { WriteBufferFromFile index(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); @@ -113,11 +115,11 @@ MergeTreeData::DataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInter typedef std::vector PrimaryColumns; PrimaryColumns primary_columns; - for (size_t i = 0, size = data.sort_descr.size(); i < size; ++i) + for (size_t i = 0, size = sort_descr.size(); i < size; ++i) primary_columns.push_back( - !data.sort_descr[i].column_name.empty() - ? &block.getByName(data.sort_descr[i].column_name) - : &block.getByPosition(data.sort_descr[i].column_number)); + !sort_descr[i].column_name.empty() + ? &block.getByName(sort_descr[i].column_name) + : &block.getByPosition(sort_descr[i].column_number)); for (size_t i = 0; i < rows; i += data.index_granularity) { From 758b1414a77a68b58a91488fcd37aff1d91d4cf5 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 14 Mar 2014 21:19:38 +0400 Subject: [PATCH 012/281] Merge --- .../Storages/MergeTree/MergeTreeBlockOutputStream.h | 2 +- dbms/include/DB/Storages/MergeTree/MergeTreeData.h | 12 ++++++------ .../DB/Storages/MergeTree/MergeTreeDataWriter.h | 2 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 6 +++--- dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp | 2 +- dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h index 0b2993fbecf..097ea2b6cd1 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h @@ -18,7 +18,7 @@ public: for (auto & current_block : part_blocks) { UInt64 temp_index = storage.increment.get(); - MergeTreeData::DataPartPtr part = storage.writer.writeTempPart(current_block, temp_index, structure); + MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, temp_index, structure); storage.data.renameTempPartAndAdd(part, &storage.increment, structure); storage.merge(2); } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 5955feb87f0..4dab22b75c8 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -101,9 +101,7 @@ public: { DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} - /// Не изменяйте никакие поля для кусков, уже вставленных в таблицу. TODO заменить почти везде на const DataPart. - - MergeTreeData & storage; + MergeTreeData & storage; DayNum_t left_date; DayNum_t right_date; UInt64 left; @@ -208,7 +206,9 @@ public: } }; - typedef SharedPtr DataPartPtr; + typedef std::shared_ptr MutableDataPartPtr; + /// После добавление в рабочее множество DataPart нельзя изменять. + typedef std::shared_ptr DataPartPtr; struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } }; typedef std::set DataParts; typedef std::vector DataPartsVector; @@ -317,9 +317,9 @@ public: void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. - * Если increment!=nullptr, индекс куска бурется из инкремента. Иначе индекс куска не меняется. + * Если increment!=nullptr, индекс куска берется из инкремента. Иначе индекс куска не меняется. */ - void renameTempPartAndAdd(DataPartPtr part, Increment * increment, + void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment, const LockedTableStructurePtr & structure); /** Удалить неактуальные куски. diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index eb7c1e45456..87c4f149ff2 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -43,7 +43,7 @@ public: * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. * Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData. */ - MergeTreeData::DataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); + MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); private: MergeTreeData & data; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 763f90ee449..d321ea37734 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -181,7 +181,7 @@ void MergeTreeData::loadDataParts() if (!isPartDirectory(file_name, matches)) continue; - DataPartPtr part = new DataPart(*this); + MutableDataPartPtr part = std::make_shared(*this); parsePartName(file_name, matches, *part); part->name = file_name; @@ -285,7 +285,7 @@ void MergeTreeData::clearOldParts() LOG_TRACE(log, "Clearing old parts"); for (DataParts::iterator it = all_data_parts.begin(); it != all_data_parts.end();) { - int ref_count = it->referenceCount(); + int ref_count = it->use_count(); if (ref_count == 1) /// После этого ref_count не может увеличиться. { LOG_DEBUG(log, "'Removing' part " << (*it)->name << " (prepending old_ to its name)"); @@ -628,7 +628,7 @@ void MergeTreeData::replaceParts(DataPartsVector old_parts, DataPartPtr new_part data_parts.erase(data_parts.find(old_parts[i])); } -void MergeTreeData::renameTempPartAndAdd(DataPartPtr part, Increment * increment, +void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment, const MergeTreeData::LockedTableStructurePtr & structure) { LOG_TRACE(log, "Renaming."); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index e10029449e3..20384fb9103 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -241,7 +241,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(data); + MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); new_data_part->left_date = std::numeric_limits::max(); new_data_part->right_date = std::numeric_limits::min(); new_data_part->left = parts.front()->left; diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index daf42ae50de..bb9d41568b4 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -69,7 +69,7 @@ BlocksWithDateIntervals MergeTreeDataWriter::splitBlockIntoParts(const Block & b return res; } -MergeTreeData::DataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInterval & block_with_dates, UInt64 temp_index, +MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInterval & block_with_dates, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure) { Block & block = block_with_dates.block; @@ -144,7 +144,7 @@ MergeTreeData::DataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInter writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns); } - MergeTreeData::DataPartPtr new_data_part = new MergeTreeData::DataPart(data); + MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); new_data_part->left_date = DayNum_t(min_date); new_data_part->right_date = DayNum_t(max_date); new_data_part->left = temp_index; From f7e2ba67c01174a3489a13d76ead0ad4ebd9b152 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 17 Mar 2014 23:56:37 +0400 Subject: [PATCH 013/281] dbms: fixed error with query formatting [#METR-10476]. --- dbms/src/Parsers/formatAST.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 77d01059ec3..42aae30c55f 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -496,6 +496,7 @@ void formatAST(const ASTFunction & ast, std::ostream & s, size_t indent, bool "less", " < ", "greater", " > ", "equals", " = ", + "lambda", " -> ", "like", " LIKE ", "notLike", " NOT LIKE ", "in", " IN ", From 5d04a9ba347aacd4ed147c7cc60e9d8cee35102c Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 18 Mar 2014 16:01:13 +0400 Subject: [PATCH 014/281] dbms: bug-fix, exception on empty shard list in table function remote [METR-10477] --- dbms/include/DB/TableFunctions/TableFunctionRemote.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index a4303eec87b..a048ee51cc7 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -56,9 +56,13 @@ public: std::vector > names; std::vector shards = parseDescription(descripton, 0, descripton.size(), ','); + for (size_t i = 0; i < shards.size(); ++i) names.push_back(parseDescription(shards[i], 0, shards[i].size(), '|')); + if (names.empty()) + throw Exception("Shard list is empty after parsing first argument", ErrorCodes::BAD_ARGUMENTS); + SharedPtr cluster = new Cluster(context.getSettings(), context.getDataTypeFactory(), names, username, password); return StorageDistributed::create(getName(), chooseColumns(*cluster, remote_database, remote_table, context), @@ -69,6 +73,7 @@ private: /// Узнать имена и типы столбцов для создания таблицы NamesAndTypesListPtr chooseColumns(Cluster & cluster, const String & database, const String & table, const Context & context) const { + std::cerr << "Here" << std::endl; /// Запрос на описание таблицы String query = "DESC TABLE " + database + "." + table; Settings settings = context.getSettings(); From a92d7255ef60a99bbc37c418ba8135c467fd48c4 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 18 Mar 2014 17:19:22 +0400 Subject: [PATCH 015/281] dbms: removed cerr in table function remote [METR-10477] --- dbms/include/DB/TableFunctions/TableFunctionRemote.h | 1 - 1 file changed, 1 deletion(-) diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index a048ee51cc7..b5edb9a8a21 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -73,7 +73,6 @@ private: /// Узнать имена и типы столбцов для создания таблицы NamesAndTypesListPtr chooseColumns(Cluster & cluster, const String & database, const String & table, const Context & context) const { - std::cerr << "Here" << std::endl; /// Запрос на описание таблицы String query = "DESC TABLE " + database + "." + table; Settings settings = context.getSettings(); From ce98defebe4da6026bfa4025dfb9ab1fe6bd54ca Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 18 Mar 2014 21:20:44 +0400 Subject: [PATCH 016/281] dbms: fixed error [#METR-10489]. --- dbms/src/Parsers/formatAST.cpp | 2 +- dbms/src/Storages/StorageFactory.cpp | 43 +++++++++++++++++++--------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 42aae30c55f..04d3e459aeb 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -580,7 +580,7 @@ void formatAST(const ASTFunction & ast, std::ostream & s, size_t indent, bool written = true; } - if (!written && 0 == strcmp(ast.name.c_str(), "tuple")) + if (!written && 0 == strcmp(ast.name.c_str(), "tuple") && ast.arguments->children.size() >= 2) { s << (hilite ? hilite_operator : "") << '(' << (hilite ? hilite_none : ""); for (size_t i = 0; i < ast.arguments->children.size(); ++i) diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index 08cb6904183..c70fd96d9b3 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -29,6 +29,31 @@ namespace DB { + +/** Для StorageMergeTree: достать первичный ключ в виде ASTExpressionList. + * Он может быть указан в кортеже: (CounterID, Date), + * или как один столбец: CounterID. + */ +static ASTPtr extractPrimaryKey(const ASTPtr & node, const std::string & storage_name) +{ + const ASTFunction * primary_expr_func = dynamic_cast(&*node); + + if (primary_expr_func && primary_expr_func->name == "tuple") + { + /// Первичный ключ указан в кортеже. + return primary_expr_func->children.at(0); + } + else + { + /// Первичный ключ состоит из одного столбца. + ASTExpressionList * res = new ASTExpressionList; + ASTPtr res_ptr = res; + res->children.push_back(node); + return res_ptr; + } +} + + StoragePtr StorageFactory::get( const String & name, const String & data_path, @@ -182,16 +207,11 @@ StoragePtr StorageFactory::get( String date_column_name = dynamic_cast(*args[0]).name; ASTPtr sampling_expression = arg_offset == 0 ? NULL : args[1]; UInt64 index_granularity = safeGet(dynamic_cast(*args[arg_offset + 2]).value); - ASTFunction & primary_expr_func = dynamic_cast(*args[arg_offset + 1]); - - if (primary_expr_func.name != "tuple") - throw Exception("Primary expression for storage " + name + " must be in parentheses.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - ASTPtr primary_expr = primary_expr_func.children.at(0); + ASTPtr primary_expr_list = extractPrimaryKey(args[arg_offset + 1], name); return StorageMergeTree::create( - data_path, table_name, columns, context, primary_expr, date_column_name, sampling_expression, index_granularity, + data_path, table_name, columns, context, primary_expr_list, date_column_name, sampling_expression, index_granularity, name == "SummingMergeTree" ? StorageMergeTree::Summing : StorageMergeTree::Ordinary); } else if (name == "CollapsingMergeTree") @@ -224,16 +244,11 @@ StoragePtr StorageFactory::get( ASTPtr sampling_expression = arg_offset == 0 ? NULL : args[1]; UInt64 index_granularity = safeGet(dynamic_cast(*args[arg_offset + 2]).value); String sign_column_name = dynamic_cast(*args[arg_offset + 3]).name; - ASTFunction & primary_expr_func = dynamic_cast(*args[arg_offset + 1]); - if (primary_expr_func.name != "tuple") - throw Exception("Primary expression for storage CollapsingMergeTree must be in parentheses.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - ASTPtr primary_expr = primary_expr_func.children.at(0); + ASTPtr primary_expr_list = extractPrimaryKey(args[arg_offset + 1], name); return StorageMergeTree::create( - data_path, table_name, columns, context, primary_expr, date_column_name, + data_path, table_name, columns, context, primary_expr_list, date_column_name, sampling_expression, index_granularity, StorageMergeTree::Collapsing, sign_column_name); } else if (name == "SystemNumbers") From 68dd884b1fbf4ffc8fa05d46e1c0681cdb238d7c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 18 Mar 2014 21:23:30 +0400 Subject: [PATCH 017/281] dbms: tiny modification [#METR-10489]. --- dbms/src/Parsers/formatAST.cpp | 39 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 04d3e459aeb..556a2e6a53e 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -565,33 +565,30 @@ void formatAST(const ASTFunction & ast, std::ostream & s, size_t indent, bool } } - if (!written && ast.arguments->children.size() >= 1) + if (!written && ast.arguments->children.size() >= 1 && 0 == strcmp(ast.name.c_str(), "array")) { - if (!written && 0 == strcmp(ast.name.c_str(), "array")) + s << (hilite ? hilite_operator : "") << '[' << (hilite ? hilite_none : ""); + for (size_t i = 0; i < ast.arguments->children.size(); ++i) { - s << (hilite ? hilite_operator : "") << '[' << (hilite ? hilite_none : ""); - for (size_t i = 0; i < ast.arguments->children.size(); ++i) - { - if (i != 0) - s << ", "; - formatAST(*ast.arguments->children[i], s, indent, hilite, one_line, false); - } - s << (hilite ? hilite_operator : "") << ']' << (hilite ? hilite_none : ""); - written = true; + if (i != 0) + s << ", "; + formatAST(*ast.arguments->children[i], s, indent, hilite, one_line, false); } + s << (hilite ? hilite_operator : "") << ']' << (hilite ? hilite_none : ""); + written = true; + } - if (!written && 0 == strcmp(ast.name.c_str(), "tuple") && ast.arguments->children.size() >= 2) + if (!written && ast.arguments->children.size() >= 2 && 0 == strcmp(ast.name.c_str(), "tuple")) + { + s << (hilite ? hilite_operator : "") << '(' << (hilite ? hilite_none : ""); + for (size_t i = 0; i < ast.arguments->children.size(); ++i) { - s << (hilite ? hilite_operator : "") << '(' << (hilite ? hilite_none : ""); - for (size_t i = 0; i < ast.arguments->children.size(); ++i) - { - if (i != 0) - s << ", "; - formatAST(*ast.arguments->children[i], s, indent, hilite, one_line, false); - } - s << (hilite ? hilite_operator : "") << ')' << (hilite ? hilite_none : ""); - written = true; + if (i != 0) + s << ", "; + formatAST(*ast.arguments->children[i], s, indent, hilite, one_line, false); } + s << (hilite ? hilite_operator : "") << ')' << (hilite ? hilite_none : ""); + written = true; } } From 9ffad47bdc602f7c509a4c9b8424565d8c2f6e6a Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 18 Mar 2014 21:45:54 +0400 Subject: [PATCH 018/281] dbms: fixed error [#METR-10489]. --- dbms/src/Parsers/formatAST.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 556a2e6a53e..505d31228eb 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -496,7 +496,6 @@ void formatAST(const ASTFunction & ast, std::ostream & s, size_t indent, bool "less", " < ", "greater", " > ", "equals", " = ", - "lambda", " -> ", "like", " LIKE ", "notLike", " NOT LIKE ", "in", " IN ", From 67c563a4d34a98eb0f2969cf59c92b9be79465cf Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 19 Mar 2014 14:45:13 +0400 Subject: [PATCH 019/281] Added locks to IStorage. Some race conditions are fixed, some are introduced, along with some possible deadlocks. [#METR-10202] --- dbms/include/DB/Common/VirtualColumnUtils.h | 1 - .../DB/DataStreams/IBlockInputStream.h | 17 +-- .../DB/DataStreams/IBlockOutputStream.h | 12 +- .../DataStreams/IProfilingBlockInputStream.h | 6 +- .../Interpreters/InterpreterDescribeQuery.h | 9 +- .../Interpreters/InterpreterOptimizeQuery.h | 4 +- .../DB/Interpreters/InterpreterSelectQuery.h | 7 +- dbms/include/DB/Storages/IStorage.h | 116 ++++++++++++++++-- ...lumnsDeclaration.h => ITableDeclaration.h} | 10 +- .../MergeTree/MergeTreeBlockInputStream.h | 20 ++- .../MergeTree/MergeTreeBlockOutputStream.h | 12 +- .../DB/Storages/MergeTree/MergeTreeData.h | 81 +----------- .../MergeTree/MergeTreeDataSelectExecutor.h | 9 +- .../Storages/MergeTree/MergeTreeDataWriter.h | 4 +- .../DB/Storages/MergeTree/MergeTreeReader.h | 13 +- .../MergeTree/MergedBlockOutputStream.h | 11 +- .../DB/Storages/MergeTree/PKCondition.h | 1 - dbms/include/DB/Storages/StorageLog.h | 17 +-- dbms/include/DB/Storages/StorageMemory.h | 7 +- dbms/include/DB/Storages/StorageMergeTree.h | 9 +- dbms/include/DB/Storages/StorageTinyLog.h | 17 +-- .../Interpreters/InterpreterAlterQuery.cpp | 6 +- .../Interpreters/InterpreterInsertQuery.cpp | 13 +- .../Interpreters/InterpreterRenameQuery.cpp | 4 +- .../Interpreters/InterpreterSelectQuery.cpp | 86 ++++++------- ...sDeclaration.cpp => ITableDeclaration.cpp} | 20 +-- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 14 +-- .../MergeTree/MergeTreeDataMerger.cpp | 10 +- .../MergeTree/MergeTreeDataSelectExecutor.cpp | 43 +++---- .../MergeTree/MergeTreeDataWriter.cpp | 9 +- dbms/src/Storages/StorageDistributed.cpp | 4 - dbms/src/Storages/StorageLog.cpp | 28 +++-- dbms/src/Storages/StorageMemory.cpp | 12 +- dbms/src/Storages/StorageMergeTree.cpp | 23 ++-- dbms/src/Storages/StorageTinyLog.cpp | 25 +++- 35 files changed, 342 insertions(+), 338 deletions(-) rename dbms/include/DB/Storages/{IColumnsDeclaration.h => ITableDeclaration.h} (89%) rename dbms/src/Storages/{IColumnsDeclaration.cpp => ITableDeclaration.cpp} (91%) diff --git a/dbms/include/DB/Common/VirtualColumnUtils.h b/dbms/include/DB/Common/VirtualColumnUtils.h index c6a2328eb61..7ededa3be5c 100644 --- a/dbms/include/DB/Common/VirtualColumnUtils.h +++ b/dbms/include/DB/Common/VirtualColumnUtils.h @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace DB diff --git a/dbms/include/DB/DataStreams/IBlockInputStream.h b/dbms/include/DB/DataStreams/IBlockInputStream.h index db49955c310..c07c93b2640 100644 --- a/dbms/include/DB/DataStreams/IBlockInputStream.h +++ b/dbms/include/DB/DataStreams/IBlockInputStream.h @@ -5,7 +5,7 @@ #include #include -#include +#include namespace DB @@ -30,11 +30,7 @@ public: typedef SharedPtr BlockInputStreamPtr; typedef std::vector BlockInputStreams; - /** Листовой BlockInputStream обычно требует, чтобы был жив какой-то Storage. - * Переданный сюда указатель на Storage будет просто храниться в этом экземпляре, - * не позволяя уничтожить Storage раньше этого BlockInputStream. - */ - IBlockInputStream(StoragePtr owned_storage_ = StoragePtr()) : owned_storage(owned_storage_) {} + IBlockInputStream() {} /** Прочитать следующий блок. * Если блоков больше нет - вернуть пустой блок (для которого operator bool возвращает false). @@ -80,10 +76,12 @@ public: */ size_t checkDepth(size_t max_depth) const; - void setOwnedStorage(StoragePtr owned_storage_) { owned_storage = owned_storage_; } + /** Не давать изменить таблицу, пока жив поток блоков. + */ + void addTableLock(const IStorage::TableStructureReadLockPtr & lock) { table_locks.push_back(lock); } protected: - StoragePtr owned_storage; + IStorage::TableStructureReadLocks table_locks; BlockInputStreams children; @@ -99,8 +97,5 @@ private: }; -typedef IBlockInputStream::BlockInputStreamPtr BlockInputStreamPtr; -typedef IBlockInputStream::BlockInputStreams BlockInputStreams; - } diff --git a/dbms/include/DB/DataStreams/IBlockOutputStream.h b/dbms/include/DB/DataStreams/IBlockOutputStream.h index f9100557d1f..72769711008 100644 --- a/dbms/include/DB/DataStreams/IBlockOutputStream.h +++ b/dbms/include/DB/DataStreams/IBlockOutputStream.h @@ -6,7 +6,7 @@ #include #include -#include +#include namespace DB @@ -21,7 +21,7 @@ class IBlockOutputStream : private boost::noncopyable { public: - IBlockOutputStream(StoragePtr owned_storage_ = StoragePtr()) : owned_storage(owned_storage_) {} + IBlockOutputStream() {} /** Записать блок. */ @@ -39,11 +39,13 @@ public: virtual void setExtremes(const Block & extremes) {} virtual ~IBlockOutputStream() {} + + /** Не давать изменить таблицу, пока жив поток блоков. + */ + void addTableLock(const IStorage::TableStructureReadLockPtr & lock) { table_locks.push_back(lock); } protected: - StoragePtr owned_storage; + IStorage::TableStructureReadLocks table_locks; }; -typedef SharedPtr BlockOutputStreamPtr; - } diff --git a/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h b/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h index 5b923ee6c2e..337bb74d155 100644 --- a/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h +++ b/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h @@ -69,7 +69,7 @@ private: mutable bool calculated_rows_before_limit; /// Вычислялось ли поле rows_before_limit }; - + /** Смотрит за тем, как работает источник блоков. * Позволяет получить информацию для профайлинга: * строк в секунду, блоков в секунду, мегабайт в секунду и т. п. @@ -78,8 +78,8 @@ private: class IProfilingBlockInputStream : public IBlockInputStream { public: - IProfilingBlockInputStream(StoragePtr owned_storage_ = StoragePtr()) - : IBlockInputStream(owned_storage_), is_cancelled(false), process_list_elem(NULL), + IProfilingBlockInputStream() + : is_cancelled(false), process_list_elem(NULL), enabled_extremes(false), quota(NULL), prev_elapsed(0) {} Block read(); diff --git a/dbms/include/DB/Interpreters/InterpreterDescribeQuery.h b/dbms/include/DB/Interpreters/InterpreterDescribeQuery.h index cf7d55cab03..c665d1ca97f 100644 --- a/dbms/include/DB/Interpreters/InterpreterDescribeQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterDescribeQuery.h @@ -76,12 +76,9 @@ private: NamesAndTypesList columns; { - Poco::ScopedLock lock(context.getMutex()); - - if (!context.isTableExist(ast.database, ast.table)) - throw Exception("Table " + (ast.database.empty() ? "" : ast.database + ".") + ast.table + " doesn't exist", ErrorCodes::UNKNOWN_TABLE); - - columns = context.getTable(ast.database, ast.table)->getColumnsList(); + StoragePtr table = context.getTable(ast.database, ast.table); + auto table_lock = table->lockStructure(false); + columns = table->getColumnsList(); } ColumnString * name_column = new ColumnString; diff --git a/dbms/include/DB/Interpreters/InterpreterOptimizeQuery.h b/dbms/include/DB/Interpreters/InterpreterOptimizeQuery.h index 32111c8aaf6..f22e7d8ae4f 100644 --- a/dbms/include/DB/Interpreters/InterpreterOptimizeQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterOptimizeQuery.h @@ -20,7 +20,9 @@ public: void execute() { const ASTOptimizeQuery & ast = dynamic_cast(*query_ptr); - context.getTable(ast.database, ast.table)->optimize(); + StoragePtr table = context.getTable(ast.database, ast.table); + auto table_lock = table->lockStructure(true); + table->optimize(); } private: diff --git a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h index f932066e15c..73a3baf271f 100644 --- a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h @@ -47,8 +47,6 @@ private: */ void getDatabaseAndTableNames(String & database_name, String & table_name); - StoragePtr getTable(); - /** Выбрать из списка столбцов какой-нибудь, лучше - минимального размера. */ String getAnyColumn(); @@ -83,7 +81,10 @@ private: size_t subquery_depth; ExpressionAnalyzerPtr query_analyzer; BlockInputStreams streams; - StoragePtr table_function_storage; + + /// Таблица, откуда читать данные, если не подзапрос. + StoragePtr storage; + IStorage::TableStructureReadLockPtr table_lock; Logger * log; }; diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index e78294bb44b..24bcb53d2d4 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -7,15 +7,14 @@ #include #include #include -#include -#include #include #include #include #include -#include +#include #include #include +#include #include @@ -23,6 +22,12 @@ namespace DB { class Context; +class IBlockInputStream; +class IBlockOutputStream; + +typedef SharedPtr BlockOutputStreamPtr; +typedef SharedPtr BlockInputStreamPtr; +typedef std::vector BlockInputStreams; /** Хранилище. Отвечает за: @@ -32,15 +37,12 @@ class Context; * - структура хранения данных (сжатие, etc.) * - конкуррентный доступ к данным (блокировки, etc.) */ -class IStorage : private boost::noncopyable, public IColumnsDeclaration +class IStorage : private boost::noncopyable, public ITableDeclaration { public: /// Основное имя типа таблицы (например, StorageMergeTree). virtual std::string getName() const = 0; - /// Имя самой таблицы (например, hits) - virtual std::string getTableName() const = 0; - /** Возвращает true, если хранилище получает данные с удалённого сервера или серверов. */ virtual bool isRemote() const { return false; } @@ -57,6 +59,55 @@ public: */ virtual bool supportsPrewhere() const { return false; } + /** Не дает изменять описание таблицы (в том числе переименовывать и удалять таблицу). + * Если в течение какой-то операции структура таблицы должна оставаться неизменной, нужно держать такой лок на все ее время. + * Например, нужно держать такой лок на время всего запроса SELECT или INSERT и на все время слияния набора кусков + * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). + * NOTE: Это лок на "чтение" описания таблицы. Чтобы изменить описание таблицы, нужно взять TableStructureWriteLock. + */ + class TableStructureReadLock + { + private: + friend class IStorage; + + /// Порядок важен. + Poco::SharedPtr data_lock; + Poco::SharedPtr structure_lock; + + TableStructureReadLock(IStorage & storage, bool lock_structure, bool lock_data) + : data_lock(lock_data ? new Poco::ScopedReadRWLock(storage. data_lock) : nullptr), + structure_lock(lock_structure ? new Poco::ScopedReadRWLock(storage.structure_lock) : nullptr) {} + }; + + typedef Poco::SharedPtr TableStructureReadLockPtr; + typedef std::vector TableStructureReadLocks; + + /** Не дает изменять структуру или имя таблицы. + * Если в рамках этого лока будут изменены данные в таблице, нужно указать will_modify_data=true. + * Это возьмет дополнительный лок, не позволяющий начать ALTER MODIFY. + * + * WARNING: Вызывать методы из ITableDeclaration нужно под такой блокировкой. Без нее они не thread safe. + * WARNING: Чтобы не было дедлоков, нельзя вызывать это метод при захваченном мьютексе в Context. + */ + TableStructureReadLockPtr lockStructure(bool will_modify_data) + { + return new TableStructureReadLock(*this, true, will_modify_data); + } + + typedef Poco::SharedPtr TableStructureWriteLockPtr; + typedef Poco::SharedPtr TableDataWriteLockPtr; + + /** Не дает читать структуру таблицы. Берется для ALTER, RENAME и DROP. + */ + TableStructureWriteLockPtr lockStructureForAlter() { return new Poco::ScopedWriteRWLock(structure_lock); } + + /** Не дает изменять данные в таблице. (Более того, не дает посмотреть на структуру таблицы с намерением изменить данные). + * Берется на время записи временных данных в ALTER MODIFY. + * Под этим локом можно брать lockStructureForAlter(), чтобы изменить структуру таблицы. + */ + TableDataWriteLockPtr lockDataForAlter() { return new Poco::ScopedWriteRWLock(data_lock); } + + /** Читать набор столбцов из таблицы. * Принимает список столбцов, которых нужно прочитать, а также описание запроса, * из которого может быть извлечена информация о том, каким способом извлекать данные @@ -73,6 +124,8 @@ public: * * threads - рекомендация, сколько потоков возвращать, * если хранилище может возвращать разное количество потоков. + * + * Гарантируется, что структура таблицы не изменится за время жизни возвращенных потоков (то есть не будет ALTER, RENAME и DROP). */ virtual BlockInputStreams read( const Names & column_names, @@ -88,6 +141,8 @@ public: /** Пишет данные в таблицу. * Принимает описание запроса, в котором может содержаться информация о методе записи данных. * Возвращает объект, с помощью которого можно последовательно писать данные. + * + * Гарантируется, что структура таблицы не изменится за время жизни возвращенных потоков (то есть не будет ALTER, RENAME и DROP). */ virtual BlockOutputStreamPtr write( ASTPtr query) @@ -101,7 +156,7 @@ public: { drop_on_destroy = true; } - + /** Вызывается перед удалением директории с данными и вызовом деструктора. * Если не требуется никаких действий, кроме удаления директории с данными, этот метод можно оставить пустым. */ @@ -110,6 +165,7 @@ public: /** Переименовать таблицу. * Переименование имени в файле с метаданными, имени в списке таблиц в оперативке, осуществляется отдельно. * В этой функции нужно переименовать директорию с данными, если она есть. + * Вызывается при заблокированной на запись структуре таблицы. */ virtual void rename(const String & new_path_to_db, const String & new_name) { @@ -118,12 +174,31 @@ public: /** ALTER таблицы в виде изменения столбцов, не затрагивающий изменение Storage или его параметров. * (ALTER, затрагивающий изменение движка, делается внешним кодом, путём копирования данных.) + * Вызывается при заблокированной на запись структуре таблицы. + * Для ALTER MODIFY используются другие методы (см. ниже). TODO: Пока эта строчка не верна, и для ALTER MODIFY используется метод alter. */ virtual void alter(const ASTAlterQuery::Parameters & params) { throw Exception("Method alter is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } + /** ALTER ... MODIFY (изменение типа столбца) выполняется в два вызова: + * Сначала вызывается prepareAlterModify при заблокированной записи данных, но незаблокированной структуре таблицы. + * В нем можно выполнить долгую работу по записи сконвертированных данных, оставляя доступными существующие данные. + * Потом вызывается commitAlterModify при заблокированной структуре таблицы. + * В нем нужно закончить изменение типа столбца. + */ + + virtual void prepareAlterModify(const ASTAlterQuery::Parameters & params) + { + throw Exception("Method prepareAlterModify is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); + } + + virtual void commitAlterModify(const ASTAlterQuery::Parameters & params) + { + throw Exception("Method commitAlterModify is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); + } + /** Выполнить какую-либо фоновую работу. Например, объединение кусков в таблице типа MergeTree. * Возвращает - была ли выполнена какая-либо работа. */ @@ -167,7 +242,7 @@ public: /** Не дает удалить БД до удаления таблицы. Присваивается перед удалением таблицы или БД. */ DatabaseDropperPtr database_to_drop; - + bool drop_on_destroy; /** Директория с данными. Будет удалена после удаления таблицы (после вызова dropImpl). @@ -179,7 +254,30 @@ protected: private: boost::weak_ptr this_ptr; + + /// Брать следующие два лока всегда нужно в этом порядке. + + /** Берется на чтение на все время запроса INSERT и на все время слияния кусков (для MergeTree). + * Берется на запись на все время ALTER MODIFY. + * + * Формально: + * Ввзятие на запись гарантирует, что: + * 1) данные в таблице не изменится, пока лок жив, + * 2) все изменения данных после отпускания лока будут основаны на структуре таблицы на момент после отпускания лока. + * Нужно брать на чтение на все время операции, изменяющей данные. + */ + mutable Poco::RWLock data_lock; + + /** Лок для множества столбцов и пути к таблице. Берется на запись в RENAME, ALTER (для ALTER MODIFY ненадолго) и DROP. + * Берется на чтение на все время SELECT, INSERT и слияния кусков (для MergeTree). + * + * Взятие этого лока на запись - строго более "сильная" операция, чем взятие parts_writing_lock на запись. + * То есть, если этот лок взят на запись, о parts_writing_lock можно не заботиться. + * parts_writing_lock нужен только для случаев, когда не хочется брать table_structure_lock надолго (ALTER MODIFY). + */ + mutable Poco::RWLock structure_lock; }; typedef std::vector StorageVector; + } diff --git a/dbms/include/DB/Storages/IColumnsDeclaration.h b/dbms/include/DB/Storages/ITableDeclaration.h similarity index 89% rename from dbms/include/DB/Storages/IColumnsDeclaration.h rename to dbms/include/DB/Storages/ITableDeclaration.h index 217e06e8827..1307d1cc5e0 100644 --- a/dbms/include/DB/Storages/IColumnsDeclaration.h +++ b/dbms/include/DB/Storages/ITableDeclaration.h @@ -11,12 +11,14 @@ namespace DB class Context; -/** Описание столбцов таблицы. +/** Описание таблицы. + * Не thread safe. См. IStorage::lockStructure(). */ -class IColumnsDeclaration +class ITableDeclaration { public: - /// Имя таблицы. Ни на что не влияет, используется только для сообщений об ошибках. + /** Имя таблицы. + */ virtual std::string getTableName() const { return ""; } /** Получить список имён и типов столбцов таблицы, только невиртуальные. @@ -59,7 +61,7 @@ public: /// реализация alter, модифицирующая список столбцов. static void alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context); - virtual ~IColumnsDeclaration() {} + virtual ~ITableDeclaration() {} }; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h index 44422fce13f..eb716cfe50a 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h @@ -13,22 +13,18 @@ namespace DB class MergeTreeBlockInputStream : public IProfilingBlockInputStream { public: - /// Параметры storage_ и owned_storage разделены, чтобы можно было сделать поток, не владеющий своим storage - /// (например, поток, сливаящий куски). В таком случае сам storage должен следить, чтобы не удалить данные, пока их читают. MergeTreeBlockInputStream(const String & path_, /// Путь к куску - MergeTreeData::LockedTableStructurePtr structure_lock_, size_t block_size_, const Names & column_names_, MergeTreeData & storage_, const MergeTreeData::DataPartPtr & owned_data_part_, - const MarkRanges & mark_ranges_, StoragePtr owned_storage, bool use_uncompressed_cache_, - ExpressionActionsPtr prewhere_actions_, String prewhere_column_, bool take_read_lock) - : IProfilingBlockInputStream(owned_storage), + const MarkRanges & mark_ranges_, bool use_uncompressed_cache_, + ExpressionActionsPtr prewhere_actions_, String prewhere_column_) + : path(path_), block_size(block_size_), column_names(column_names_), storage(storage_), owned_data_part(owned_data_part_), all_mark_ranges(mark_ranges_), remaining_mark_ranges(mark_ranges_), use_uncompressed_cache(use_uncompressed_cache_), prewhere_actions(prewhere_actions_), prewhere_column(prewhere_column_), - log(&Logger::get("MergeTreeBlockInputStream")), - structure_lock(structure_lock_) + log(&Logger::get("MergeTreeBlockInputStream")) { std::reverse(remaining_mark_ranges.begin(), remaining_mark_ranges.end()); @@ -60,7 +56,7 @@ public: String getID() const { std::stringstream res; - res << "MergeTree(" << owned_storage->getTableName() << ", " << path << ", columns"; + res << "MergeTree(" << path << ", columns"; for (size_t i = 0; i < column_names.size(); ++i) res << ", " << column_names[i]; @@ -88,9 +84,9 @@ protected: if (!reader) { UncompressedCache * uncompressed_cache = use_uncompressed_cache ? storage.context.getUncompressedCache() : NULL; - reader = new MergeTreeReader(path, column_names, uncompressed_cache, storage, structure_lock); + reader = new MergeTreeReader(path, column_names, uncompressed_cache, storage); if (prewhere_actions) - pre_reader = new MergeTreeReader(path, pre_column_names, uncompressed_cache, storage, structure_lock); + pre_reader = new MergeTreeReader(path, pre_column_names, uncompressed_cache, storage); } if (prewhere_actions) @@ -272,8 +268,6 @@ private: bool remove_prewhere_column; Logger * log; - - MergeTreeData::LockedTableStructurePtr structure_lock; }; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h index 097ea2b6cd1..b382b660471 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockOutputStream.h @@ -8,25 +8,23 @@ namespace DB class MergeTreeBlockOutputStream : public IBlockOutputStream { public: - MergeTreeBlockOutputStream(StoragePtr storage_) - : IBlockOutputStream(storage_), storage(dynamic_cast(*storage_)), - structure(storage.data.getLockedStructure(true)) {} + MergeTreeBlockOutputStream(StorageMergeTree & storage_) + : storage(storage_) {} void write(const Block & block) { - auto part_blocks = storage.writer.splitBlockIntoParts(block, structure); + auto part_blocks = storage.writer.splitBlockIntoParts(block); for (auto & current_block : part_blocks) { UInt64 temp_index = storage.increment.get(); - MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, temp_index, structure); - storage.data.renameTempPartAndAdd(part, &storage.increment, structure); + MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, temp_index); + storage.data.renameTempPartAndAdd(part, &storage.increment); storage.merge(2); } } private: StorageMergeTree & storage; - MergeTreeData::LockedTableStructurePtr structure; }; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 4dab22b75c8..5d03c3f80eb 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -93,7 +93,7 @@ struct MergeTreeSettings time_t old_parts_lifetime = 5 * 60; }; -class MergeTreeData +class MergeTreeData : public ITableDeclaration { public: /// Описание куска с данными. @@ -240,13 +240,6 @@ public: const String & sign_column_, const MergeTreeSettings & settings_); - /** - * owning_storage используется только чтобы отдавать его потокам блоков. - */ - void setOwningStorage(StoragePtr storage) { owning_storage = storage; } - - StoragePtr getOwningStorage() const { return owning_storage; } - std::string getModePrefix() const; std::string getSignColumnName() const { return sign_column; } @@ -264,49 +257,11 @@ public: /// Кладет в DataPart данные из имени кусочка. void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); - /** Изменяемая часть описания таблицы. Содержит лок, запрещающий изменение описания таблицы. - * Если в течение какой-то операции структура таблицы должна оставаться неизменной, нужно держать один лок на все ее время. - * Например, нужно держать такой лок на время всего запроса SELECT или INSERT и на все время слияния набора кусков - * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). - * NOTE: Можно перенести сюда другие поля, чтобы сделать их динамически изменяемыми. - * Например, index_granularity, sign_column, primary_expr_ast. - * NOTE: Можно вынести эту штуку в IStorage и брать ее в Interpreter-ах, - * чтобы избавиться от оставшихся небольших race conditions. - * Скорее всего, даже можно заменить этой штукой весь механизм отложенного дропа таблиц и убрать owned_storage из потоков блоков. - */ - class LockedTableStructure : public IColumnsDeclaration - { - public: - const NamesAndTypesList & getColumnsList() const { return *data.columns; } + std::string getTableName() { return ""; } - String getFullPath() const { return data.full_path; } + const NamesAndTypesList & getColumnsList() const { return *columns; } - private: - friend class MergeTreeData; - - const MergeTreeData & data; - Poco::SharedPtr parts_lock; - Poco::SharedPtr structure_lock; - - LockedTableStructure(const MergeTreeData & data_, bool lock_structure, bool lock_writing) - : data(data_), - parts_lock(lock_writing ? new Poco::ScopedReadRWLock(data.parts_writing_lock) : nullptr), - structure_lock(lock_structure ? new Poco::ScopedReadRWLock(data.table_structure_lock) : nullptr) {} - }; - - typedef Poco::SharedPtr LockedTableStructurePtr; - - /** Если в рамках этого лока будут добавлены или удалены куски данных, обязательно указать will_modify_parts=true. - * Это возьмет дополнительный лок, не позволяющий начать ALTER MODIFY. - */ - LockedTableStructurePtr getLockedStructure(bool will_modify_parts) const - { - return new LockedTableStructure(*this, true, will_modify_parts); - } - - typedef Poco::SharedPtr TableStructureWriteLockPtr; - - TableStructureWriteLockPtr lockStructure() { return new Poco::ScopedWriteRWLock(table_structure_lock); } + String getFullPath() const { return full_path; } /** Возвращает копию списка, чтобы снаружи можно было не заботиться о блокировках. */ @@ -319,8 +274,7 @@ public: /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. * Если increment!=nullptr, индекс куска берется из инкремента. Иначе индекс куска не меняется. */ - void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment, - const LockedTableStructurePtr & structure); + void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment); /** Удалить неактуальные куски. */ @@ -339,7 +293,7 @@ public: /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. * Нужно вызывать под залоченным lockStructure(). - * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. На долгую часть достаточно лочить parts_writing_lock. + * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. */ void alter(const ASTAlterQuery::Parameters & params); @@ -364,8 +318,6 @@ private: SortDescription sort_descr; Block primary_key_sample; - StorageWeakPtr owning_storage; - ASTPtr primary_expr_ast; String full_path; @@ -378,27 +330,6 @@ private: /// Регулярное выражение соответсвующее названию директории с кусочками Poco::RegularExpression file_name_regexp; - /// Брать следующие два лока всегда нужно в этом порядке. - - /** Берется на чтение на все время запроса INSERT и на все время слияния кусков. Берется на запись на все время ALTER MODIFY. - * - * Формально: - * Ввзятие на запись гарантирует, что: - * 1) множество кусков не изменится, пока лок жив, - * 2) все операции над множеством кусков после отпускания лока будут основаны на структуре таблицы на момент после отпускания лока. - * Взятие на чтение обязательно, если будет добавляться или удалять кусок. - * Брать на чтение нужно до получения структуры таблицы, которой будет соответствовать добавляемый кусок. - */ - mutable Poco::RWLock parts_writing_lock; - - /** Лок для множества столбцов и пути к таблице. Берется на запись в RENAME и ALTER (для ALTER MODIFY ненадолго). - * - * Взятие этого лока на запись - строго более "сильная" операция, чем взятие parts_writing_lock на запись. - * То есть, если этот лок взят на запись, о parts_writing_lock можно не заботиться. - * parts_writing_lock нужен только для случаев, когда не хочется брать table_structure_lock надолго. - */ - mutable Poco::RWLock table_structure_lock; - /** Актуальное множество кусков с данными. */ DataParts data_parts; Poco::FastMutex data_parts_mutex; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h index 72b03c24952..7ea10726200 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -55,8 +55,7 @@ private: size_t max_block_size, bool use_uncompressed_cache, ExpressionActionsPtr prewhere_actions, - const String & prewhere_column, - const MergeTreeData::LockedTableStructurePtr & structure); + const String & prewhere_column); BlockInputStreams spreadMarkRangesAmongThreadsFinal( RangesInDataParts parts, @@ -65,12 +64,10 @@ private: size_t max_block_size, bool use_uncompressed_cache, ExpressionActionsPtr prewhere_actions, - const String & prewhere_column, - const MergeTreeData::LockedTableStructurePtr & structure); + const String & prewhere_column); /// Создать выражение "Sign == 1". - void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column, - const MergeTreeData::LockedTableStructurePtr & structure); + void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column); MarkRanges markRangesFromPkRange(const MergeTreeData::DataPart::Index & index, PKCondition & key_condition); }; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index 87c4f149ff2..6e720bfd791 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -37,13 +37,13 @@ public: * (читай: разбивает строки по месяцам) * Работает детерминированно: если отдать на вход такой же блок, на выходе получатся такие же блоки в таком же порядке. */ - BlocksWithDateIntervals splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure); + BlocksWithDateIntervals splitBlockIntoParts(const Block & block); /** Все строки должны относиться к одному месяцу. Возвращает название временного куска. * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. * Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData. */ - MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index, const MergeTreeData::LockedTableStructurePtr & structure); + MergeTreeData::MutableDataPartPtr writeTempPart(BlockWithDateInterval & block, UInt64 temp_index); private: MergeTreeData & data; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index 3c33a262244..f6ee8257d0b 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -39,13 +39,11 @@ class MergeTreeReader { public: MergeTreeReader(const String & path_, /// Путь к куску - const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_, - MergeTreeData::LockedTableStructurePtr structure_) - : path(path_), column_names(columns_names_), use_uncompressed_cache(use_uncompressed_cache_), storage(storage_), - structure(structure_) + const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_) + : path(path_), column_names(columns_names_), use_uncompressed_cache(use_uncompressed_cache_), storage(storage_) { for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) - addStream(*it, *structure->getDataTypeByName(*it)); + addStream(*it, *storage.getDataTypeByName(*it)); } /** Если столбцов нет в блоке, добавляет их, если есть - добавляет прочитанные значения к ним в конец. @@ -78,7 +76,7 @@ public: ColumnWithNameAndType column; column.name = *it; - column.type = structure->getDataTypeByName(*it); + column.type = storage.getDataTypeByName(*it); if (append) column.column = res.getByName(column.name).column; @@ -133,7 +131,7 @@ public: { ColumnWithNameAndType column; column.name = *it; - column.type = structure->getDataTypeByName(*it); + column.type = storage.getDataTypeByName(*it); /** Нужно превратить константный столбец в полноценный, так как в части блоков (из других кусков), * он может быть полноценным (а то интерпретатор может посчитать, что он константный везде). @@ -240,7 +238,6 @@ private: Names column_names; bool use_uncompressed_cache; MergeTreeData & storage; - MergeTreeData::LockedTableStructurePtr structure; void addStream(const String & name, const IDataType & type, size_t level = 0) { diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index b90d527d74c..6a745460985 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -196,22 +196,22 @@ protected: class MergedBlockOutputStream : public IMergedBlockOutputStream { public: - MergedBlockOutputStream(MergeTreeData & storage_, MergeTreeData::LockedTableStructurePtr structure_, + MergedBlockOutputStream(MergeTreeData & storage_, UInt16 min_date, UInt16 max_date, UInt64 min_part_id, UInt64 max_part_id, UInt32 level) - : IMergedBlockOutputStream(storage_), structure(structure_), marks_count(0) + : IMergedBlockOutputStream(storage_), marks_count(0) { part_name = storage.getPartName( DayNum_t(min_date), DayNum_t(max_date), min_part_id, max_part_id, level); - part_tmp_path = structure->getFullPath() + "tmp_" + part_name + "/"; - part_res_path = structure->getFullPath() + part_name + "/"; + part_tmp_path = storage.getFullPath() + "tmp_" + part_name + "/"; + part_res_path = storage.getFullPath() + part_name + "/"; Poco::File(part_tmp_path).createDirectories(); index_stream = new WriteBufferFromFile(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); - columns_list = structure->getColumnsList(); + columns_list = storage.getColumnsList(); for (const auto & it : columns_list) addStream(part_tmp_path, it.first, *it.second); } @@ -286,7 +286,6 @@ public: } private: - MergeTreeData::LockedTableStructurePtr structure; NamesAndTypesList columns_list; String part_name; diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index d55dd38b037..b90e4f8ee2e 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include diff --git a/dbms/include/DB/Storages/StorageLog.h b/dbms/include/DB/Storages/StorageLog.h index 91ca50e5193..0c74c712a92 100644 --- a/dbms/include/DB/Storages/StorageLog.h +++ b/dbms/include/DB/Storages/StorageLog.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace DB @@ -36,20 +37,10 @@ typedef std::vector Marks; class LogBlockInputStream : public IProfilingBlockInputStream { public: - LogBlockInputStream(size_t block_size_, const Names & column_names_, StoragePtr owned_storage, size_t mark_number_, size_t rows_limit_); + LogBlockInputStream(size_t block_size_, const Names & column_names_, StorageLog & storage_, size_t mark_number_, size_t rows_limit_); String getName() const { return "LogBlockInputStream"; } - String getID() const - { - std::stringstream res; - res << "Log(" << owned_storage->getTableName() << ", " << &*owned_storage << ", " << mark_number << ", " << rows_limit; - - for (size_t i = 0; i < column_names.size(); ++i) - res << ", " << column_names[i]; - - res << ")"; - return res.str(); - } + String getID() const; protected: Block readImpl(); @@ -88,7 +79,7 @@ private: class LogBlockOutputStream : public IBlockOutputStream { public: - LogBlockOutputStream(StoragePtr owned_storage); + LogBlockOutputStream(StorageLog & storage_); void write(const Block & block); void writeSuffix(); private: diff --git a/dbms/include/DB/Storages/StorageMemory.h b/dbms/include/DB/Storages/StorageMemory.h index bf18e22271a..67c543b57ba 100644 --- a/dbms/include/DB/Storages/StorageMemory.h +++ b/dbms/include/DB/Storages/StorageMemory.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace DB @@ -15,13 +16,13 @@ class StorageMemory; class MemoryBlockInputStream : public IProfilingBlockInputStream { public: - MemoryBlockInputStream(const Names & column_names_, BlocksList::iterator begin_, BlocksList::iterator end_, StoragePtr owned_storage); + MemoryBlockInputStream(const Names & column_names_, BlocksList::iterator begin_, BlocksList::iterator end_); String getName() const { return "MemoryBlockInputStream"; } String getID() const { std::stringstream res; - res << "Memory(" << owned_storage->getTableName() << ", " << &*owned_storage << ", " << &*begin << ", " << &*end; + res << "Memory(" << &*begin << ", " << &*end; for (size_t i = 0; i < column_names.size(); ++i) res << ", " << column_names[i]; @@ -43,7 +44,7 @@ private: class MemoryBlockOutputStream : public IBlockOutputStream { public: - MemoryBlockOutputStream(StoragePtr owned_storage); + MemoryBlockOutputStream(StorageMemory & storage_); void write(const Block & block); private: StorageMemory & storage; diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 970bd24093a..4a70a07d1ad 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -48,7 +48,7 @@ public: bool supportsFinal() const { return data.supportsFinal(); } bool supportsPrewhere() const { return data.supportsPrewhere(); } - const NamesAndTypesList & getColumnsList() const { return data.getLockedStructure(false)->getColumnsList(); } + const NamesAndTypesList & getColumnsList() const { return data.getColumnsList(); } BlockInputStreams read( const Names & column_names, @@ -77,13 +77,6 @@ public: /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение void alter(const ASTAlterQuery::Parameters & params); - typedef MergeTreeData::TableStructureWriteLockPtr BigLockPtr; - - BigLockPtr lockAllOperations() - { - return data.lockStructure(); - } - private: String path; String name; diff --git a/dbms/include/DB/Storages/StorageTinyLog.h b/dbms/include/DB/Storages/StorageTinyLog.h index f47ef0c915a..2b43fbd24d6 100644 --- a/dbms/include/DB/Storages/StorageTinyLog.h +++ b/dbms/include/DB/Storages/StorageTinyLog.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace DB @@ -22,20 +23,10 @@ class StorageTinyLog; class TinyLogBlockInputStream : public IProfilingBlockInputStream { public: - TinyLogBlockInputStream(size_t block_size_, const Names & column_names_, StoragePtr owned_storage); + TinyLogBlockInputStream(size_t block_size_, const Names & column_names_, StorageTinyLog & storage_); String getName() const { return "TinyLogBlockInputStream"; } - String getID() const - { - std::stringstream res; - res << "TinyLog(" << owned_storage->getTableName() << ", " << &*owned_storage; - - for (size_t i = 0; i < column_names.size(); ++i) - res << ", " << column_names[i]; - - res << ")"; - return res.str(); - } + String getID() const; protected: Block readImpl(); @@ -68,7 +59,7 @@ private: class TinyLogBlockOutputStream : public IBlockOutputStream { public: - TinyLogBlockOutputStream(StoragePtr owned_storage); + TinyLogBlockOutputStream(StorageTinyLog & storage_); void write(const Block & block); void writeSuffix(); private: diff --git a/dbms/src/Interpreters/InterpreterAlterQuery.cpp b/dbms/src/Interpreters/InterpreterAlterQuery.cpp index ff7533eed90..22a1a96fc75 100644 --- a/dbms/src/Interpreters/InterpreterAlterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterAlterQuery.cpp @@ -46,11 +46,7 @@ void InterpreterAlterQuery::execute() String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database; StoragePtr table = context.getTable(database_name, table_name); - - /// для merge tree запрещаем все операции - StorageMergeTree::BigLockPtr merge_tree_lock; - if (StorageMergeTree * table_merge_tree = dynamic_cast(table.get())) - merge_tree_lock = table_merge_tree->lockAllOperations(); + auto table_lock = table->lockStructureForAlter(); /// Poco::Mutex является рекурсивным, т.е. взятие мьютекса дважды из одного потока не приводит к блокировке Poco::ScopedLock lock(context.getMutex()); diff --git a/dbms/src/Interpreters/InterpreterInsertQuery.cpp b/dbms/src/Interpreters/InterpreterInsertQuery.cpp index 967db071f02..46a45215ee9 100644 --- a/dbms/src/Interpreters/InterpreterInsertQuery.cpp +++ b/dbms/src/Interpreters/InterpreterInsertQuery.cpp @@ -64,14 +64,20 @@ void InterpreterInsertQuery::execute(ReadBuffer * remaining_data_istr) { ASTInsertQuery & query = dynamic_cast(*query_ptr); StoragePtr table = getTable(); - + + auto table_lock = table->lockStructure(true); + BlockInputStreamPtr in; NamesAndTypesListPtr required_columns = new NamesAndTypesList (table->getSampleBlock().getColumnsList()); /// Надо убедиться, что запрос идет в таблицу, которая поддерживает вставку. table->write(query_ptr); /// Создаем кортеж из нескольких стримов, в которые будем писать данные. + BlockOutputStreamPtr out = new AddingDefaultBlockOutputStream(new PushingToViewsBlockOutputStream(query.database, query.table, context, query_ptr), required_columns); + /// TODO: Взять также IStorage::TableStructureReadLock-и для всех затронутых materialized views. + out->addTableLock(table_lock); + /// Какой тип запроса: INSERT VALUES | INSERT FORMAT | INSERT SELECT? if (!query.select) { @@ -117,6 +123,8 @@ BlockOutputStreamPtr InterpreterInsertQuery::execute() ASTInsertQuery & query = dynamic_cast(*query_ptr); StoragePtr table = getTable(); + auto table_lock = table->lockStructure(true); + NamesAndTypesListPtr required_columns = new NamesAndTypesList(table->getSampleBlock().getColumnsList()); /// Надо убедиться, что запрос идет в таблицу, которая поддерживает вставку. @@ -124,6 +132,9 @@ BlockOutputStreamPtr InterpreterInsertQuery::execute() /// Создаем кортеж из нескольких стримов, в которые будем писать данные. BlockOutputStreamPtr out = new AddingDefaultBlockOutputStream(new PushingToViewsBlockOutputStream(query.database, query.table, context, query_ptr), required_columns); + /// TODO: Взять также IStorage::TableStructureReadLock-и для всех затронутых materialized views. + out->addTableLock(table_lock); + /// Какой тип запроса: INSERT или INSERT SELECT? if (!query.select) return out; diff --git a/dbms/src/Interpreters/InterpreterRenameQuery.cpp b/dbms/src/Interpreters/InterpreterRenameQuery.cpp index 246d1c3d17c..efb8c7b113b 100644 --- a/dbms/src/Interpreters/InterpreterRenameQuery.cpp +++ b/dbms/src/Interpreters/InterpreterRenameQuery.cpp @@ -57,7 +57,9 @@ void InterpreterRenameQuery::execute() context.assertTableDoesntExist(to_database_name, to_table_name); /// Уведомляем таблицу о том, что она переименовается. Если таблица не поддерживает переименование - кинется исключение. - context.getTable(from_database_name, from_table_name)->rename(path + "data/" + to_database_name_escaped + "/", to_table_name); + StoragePtr table = context.getTable(from_database_name, from_table_name); + auto table_lock = table->lockStructureForAlter(); // TODO: Тут возможен дедлок. + table->rename(path + "data/" + to_database_name_escaped + "/", to_table_name); /// Пишем новый файл с метаданными. { diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 6dc8a892e85..d0f48e276aa 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -41,28 +41,42 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType throw Exception("Too deep subqueries. Maximum: " + toString(settings.limits.max_subquery_depth), ErrorCodes::TOO_DEEP_SUBQUERIES); - /// Если имееем дело с табличной функцией - if (query.table && dynamic_cast(&*query.table)) + if (query.table && dynamic_cast(&*query.table)) { - /// Получить табличную функцию - TableFunctionPtr table_function_ptr = context.getTableFunctionFactory().get(dynamic_cast(&*query.table)->name, context); - /// Выполнить ее и запомнить результат - table_function_storage = table_function_ptr->execute(query.table, context); + if (table_column_names.empty()) + context.setColumns(InterpreterSelectQuery(query.table, context).getSampleBlock().getColumnsList()); + } + else + { + if (query.table && dynamic_cast(&*query.table)) + { + /// Получить табличную функцию + TableFunctionPtr table_function_ptr = context.getTableFunctionFactory().get(dynamic_cast(&*query.table)->name, context); + /// Выполнить ее и запомнить результат + storage = table_function_ptr->execute(query.table, context); + } + else + { + String database_name; + String table_name; + + getDatabaseAndTableNames(database_name, table_name); + + storage = context.getTable(database_name, table_name); + } + + table_lock = storage->lockStructure(false); + if (table_column_names.empty()) + context.setColumns(storage->getColumnsList()); } - if (table_function_storage) - context.setColumns(table_function_storage->getColumnsList()); - else if (!table_column_names.empty()) + if (!table_column_names.empty()) context.setColumns(table_column_names); - else - context.setColumns(!query.table || !dynamic_cast(&*query.table) - ? getTable()->getColumnsList() - : InterpreterSelectQuery(query.table, context).getSampleBlock().getColumnsList()); if (context.getColumns().empty()) throw Exception("There are no available columns", ErrorCodes::THERE_IS_NO_COLUMN); - query_analyzer = new ExpressionAnalyzer(query_ptr, context, table_function_storage, subquery_depth); + query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth); if (input_) streams.push_back(input_); @@ -127,16 +141,6 @@ void InterpreterSelectQuery::getDatabaseAndTableNames(String & database_name, St } -StoragePtr InterpreterSelectQuery::getTable() -{ - String database_name; - String table_name; - - getDatabaseAndTableNames(database_name, table_name); - return context.getTable(database_name, table_name); -} - - ASTPtr InterpreterSelectQuery::getCreateQuery() { String database_name; @@ -436,25 +440,13 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu if (!streams.empty()) return QueryProcessingStage::FetchColumns; - /// Таблица, откуда читать данные, если не подзапрос. - StoragePtr table; /// Интерпретатор подзапроса, если подзапрос SharedPtr interpreter_subquery; /// Список столбцов, которых нужно прочитать, чтобы выполнить запрос. Names required_columns = query_analyzer->getRequiredColumns(); - if (table_function_storage) - { - /// Если в запросе была указана табличная функция, данные читаем из нее. - table = table_function_storage; - } - else if (!query.table || !dynamic_cast(&*query.table)) - { - /// Запрос из обычной таблицы или без секции FROM. - table = getTable(); - } - else if (dynamic_cast(&*query.table)) + if (dynamic_cast(&*query.table)) { /** Для подзапроса не действуют ограничения на максимальный размер результата. * Так как результат поздапроса - ещё не результат всего запроса. @@ -477,13 +469,13 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu if (!query.sample_size && settings.default_sample != 1) query.sample_size = new ASTLiteral(StringRange(NULL, NULL), Float64(settings.default_sample)); - if (query.sample_size && (!table || !table->supportsSampling())) + if (query.sample_size && (!storage || !storage->supportsSampling())) throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED); - if (query.final && (!table || !table->supportsFinal())) + if (query.final && (!storage || !storage->supportsFinal())) throw Exception("Illegal FINAL", ErrorCodes::ILLEGAL_FINAL); - if (query.prewhere_expression && (!table || !table->supportsPrewhere())) + if (query.prewhere_expression && (!storage || !storage->supportsPrewhere())) throw Exception("Illegal PREWHERE", ErrorCodes::ILLEGAL_PREWHERE); /** При распределённой обработке запроса, в потоках почти не делается вычислений, @@ -498,7 +490,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu * и там должно быть оригинальное значение max_threads, а не увеличенное. */ Settings settings_for_storage = settings; - if (table && table->isRemote()) + if (storage && storage->isRemote()) settings.max_threads = settings.max_distributed_connections; /// Ограничение на количество столбцов для чтения. @@ -528,9 +520,17 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu /// Инициализируем изначальные потоки данных, на которые накладываются преобразования запроса. Таблица или подзапрос? if (!query.table || !dynamic_cast(&*query.table)) - streams = table->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); + { + streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); + for (auto stream : streams) + { + stream->addTableLock(table_lock); + } + } else + { streams.push_back(maybeAsynchronous(interpreter_subquery->execute(), settings.asynchronous)); + } /** Если истчоников слишком много, то склеим их в max_threads источников. * (Иначе действия в каждом маленьком источнике, а затем объединение состояний, слишком неэффективно.) @@ -542,7 +542,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu * Такие ограничения проверяются на сервере-инициаторе запроса, а не на удалённых серверах. * Потому что сервер-инициатор имеет суммарные данные о выполнении запроса на всех серверах. */ - if (table && to_stage == QueryProcessingStage::Complete) + if (storage && to_stage == QueryProcessingStage::Complete) { IProfilingBlockInputStream::LocalLimits limits; limits.mode = IProfilingBlockInputStream::LIMITS_TOTAL; diff --git a/dbms/src/Storages/IColumnsDeclaration.cpp b/dbms/src/Storages/ITableDeclaration.cpp similarity index 91% rename from dbms/src/Storages/IColumnsDeclaration.cpp rename to dbms/src/Storages/ITableDeclaration.cpp index b6b21b904ba..3de3049fa62 100644 --- a/dbms/src/Storages/IColumnsDeclaration.cpp +++ b/dbms/src/Storages/ITableDeclaration.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -10,7 +10,7 @@ namespace DB { -bool IColumnsDeclaration::hasRealColumn(const String &column_name) const +bool ITableDeclaration::hasRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) @@ -20,7 +20,7 @@ bool IColumnsDeclaration::hasRealColumn(const String &column_name) const } -NameAndTypePair IColumnsDeclaration::getRealColumn(const String &column_name) const +NameAndTypePair ITableDeclaration::getRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) @@ -30,19 +30,19 @@ NameAndTypePair IColumnsDeclaration::getRealColumn(const String &column_name) co } -bool IColumnsDeclaration::hasColumn(const String &column_name) const +bool ITableDeclaration::hasColumn(const String &column_name) const { return hasRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет. } -NameAndTypePair IColumnsDeclaration::getColumn(const String &column_name) const +NameAndTypePair ITableDeclaration::getColumn(const String &column_name) const { return getRealColumn(column_name); /// По умолчанию считаем, что виртуальных столбцов в сторадже нет. } -const DataTypePtr IColumnsDeclaration::getDataTypeByName(const String & column_name) const +const DataTypePtr ITableDeclaration::getDataTypeByName(const String & column_name) const { const NamesAndTypesList & names_and_types = getColumnsList(); for (NamesAndTypesList::const_iterator it = names_and_types.begin(); it != names_and_types.end(); ++it) @@ -53,7 +53,7 @@ const DataTypePtr IColumnsDeclaration::getDataTypeByName(const String & column_n } -Block IColumnsDeclaration::getSampleBlock() const +Block ITableDeclaration::getSampleBlock() const { Block res; const NamesAndTypesList & names_and_types = getColumnsList(); @@ -99,7 +99,7 @@ static NamesAndTypesMap getColumnsMap(const NamesAndTypesList & available_column } -void IColumnsDeclaration::check(const Names & column_names) const +void ITableDeclaration::check(const Names & column_names) const { const NamesAndTypesList & available_columns = getColumnsList(); @@ -129,7 +129,7 @@ void IColumnsDeclaration::check(const Names & column_names) const } -void IColumnsDeclaration::check(const Block & block, bool need_all) const +void ITableDeclaration::check(const Block & block, bool need_all) const { const NamesAndTypesList & available_columns = getColumnsList(); const NamesAndTypesMap & columns_map = getColumnsMap(available_columns); @@ -176,7 +176,7 @@ static bool namesEqual(const String & name_without_dot, const DB::NameAndTypePai return (name_with_dot == name_type.first.substr(0, name_without_dot.length() + 1) || name_without_dot == name_type.first); } -void IColumnsDeclaration::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context) +void ITableDeclaration::alterColumns(const ASTAlterQuery::Parameters & params, NamesAndTypesListPtr & columns, const Context & context) { if (params.type == ASTAlterQuery::ADD) { diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index d321ea37734..0d6af769b3f 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -412,7 +412,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); StringRange type_range = name_type.type->range; String type(type_range.first, type_range.second - type_range.first); - DataTypePtr old_type_ptr = getDataTypeByName(name_type.name, *columns); + DataTypePtr old_type_ptr = DB::getDataTypeByName(name_type.name, *columns); DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) @@ -428,8 +428,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { MarkRanges ranges(1, MarkRange(0, part->size)); ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', - new LockedTableStructure(*this, false, false), /// Не блокируем структуру таблицы, она уже заблокирована снаружи. - DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, StoragePtr(), false, NULL, ""), expr); + DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, false, NULL, ""), expr); MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); out.writePrefix(); @@ -498,7 +497,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { Poco::ScopedLock lock(data_parts_mutex); Poco::ScopedLock lock_all(all_data_parts_mutex); - IColumnsDeclaration::alterColumns(params, columns, context); + alterColumns(params, columns, context); } if (params.type == ASTAlterQuery::DROP) { @@ -628,15 +627,14 @@ void MergeTreeData::replaceParts(DataPartsVector old_parts, DataPartPtr new_part data_parts.erase(data_parts.find(old_parts[i])); } -void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment, - const MergeTreeData::LockedTableStructurePtr & structure) +void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment) { LOG_TRACE(log, "Renaming."); Poco::ScopedLock lock(data_parts_mutex); Poco::ScopedLock lock_all(all_data_parts_mutex); - String old_path = structure->getFullPath() + part->name + "/"; + String old_path = getFullPath() + part->name + "/"; UInt64 part_id = part->left; @@ -650,7 +648,7 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in part->left = part->right = part_id; part->name = getPartName(part->left_date, part->right_date, part_id, part_id, 0); - String new_path = structure->getFullPath() + part->name + "/"; + String new_path = getFullPath() + part->name + "/"; /// Переименовываем кусок. Poco::File(old_path).renameTo(new_path); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 20384fb9103..160b4dc2b95 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -232,10 +232,8 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa { LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); - auto structure = data.getLockedStructure(true); - Names all_column_names; - NamesAndTypesList columns_list = structure->getColumnsList(); + NamesAndTypesList columns_list = data.getColumnsList(); for (const auto & it : columns_list) all_column_names.push_back(it.first); @@ -268,8 +266,8 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa { MarkRanges ranges(1, MarkRange(0, parts[i]->size)); src_streams.push_back(new ExpressionBlockInputStream(new MergeTreeBlockInputStream( - structure->getFullPath() + parts[i]->name + '/', structure, DEFAULT_MERGE_BLOCK_SIZE, all_column_names, data, parts[i], ranges, - StoragePtr(), false, NULL, ""), data.getPrimaryExpression())); + data.getFullPath() + parts[i]->name + '/', DEFAULT_MERGE_BLOCK_SIZE, all_column_names, data, + parts[i], ranges, false, NULL, ""), data.getPrimaryExpression())); } /// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника. @@ -294,7 +292,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa throw Exception("Unknown mode of operation for MergeTreeData: " + toString(data.mode), ErrorCodes::LOGICAL_ERROR); } - MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, structure, + MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); merged_stream->readPrefix(); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index ddcc811901d..07931d21c34 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -27,13 +27,11 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( size_t max_block_size, unsigned threads) { - MergeTreeData::LockedTableStructurePtr structure = data.getLockedStructure(false); - - structure->check(column_names_to_return); + data.check(column_names_to_return); processed_stage = QueryProcessingStage::FetchColumns; - PKCondition key_condition(query, data.context, structure->getColumnsList(), data.getSortDescription()); - PKCondition date_condition(query, data.context, structure->getColumnsList(), SortDescription(1, SortColumnDescription(data.date_column_name, 1))); + PKCondition key_condition(query, data.context, data.getColumnsList(), data.getSortDescription()); + PKCondition date_condition(query, data.context, data.getColumnsList(), SortDescription(1, SortColumnDescription(data.date_column_name, 1))); MergeTreeData::DataPartsVector parts; @@ -120,7 +118,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( filter_function->arguments = filter_function_args; filter_function->children.push_back(filter_function->arguments); - filter_expression = ExpressionAnalyzer(filter_function, data.context, structure->getColumnsList()).getActions(false); + filter_expression = ExpressionAnalyzer(filter_function, data.context, data.getColumnsList()).getActions(false); /// Добавим столбцы, нужные для sampling_expression. std::vector add_columns = filter_expression->getRequiredColumns(); @@ -137,7 +135,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( String prewhere_column; if (select.prewhere_expression) { - ExpressionAnalyzer analyzer(select.prewhere_expression, data.context, structure->getColumnsList()); + ExpressionAnalyzer analyzer(select.prewhere_expression, data.context, data.getColumnsList()); prewhere_actions = analyzer.getActions(false); prewhere_column = select.prewhere_expression->getColumnName(); } @@ -186,8 +184,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( max_block_size, settings.use_uncompressed_cache, prewhere_actions, - prewhere_column, - structure); + prewhere_column); } else { @@ -198,8 +195,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( max_block_size, settings.use_uncompressed_cache, prewhere_actions, - prewhere_column, - structure); + prewhere_column); } if (select.sample_size) @@ -223,8 +219,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( size_t max_block_size, bool use_uncompressed_cache, ExpressionActionsPtr prewhere_actions, - const String & prewhere_column, - const MergeTreeData::LockedTableStructurePtr & structure) + const String & prewhere_column) { /// На всякий случай перемешаем куски. std::random_shuffle(parts.begin(), parts.end()); @@ -283,8 +278,8 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( std::reverse(part.ranges.begin(), part.ranges.end()); streams.push_back(new MergeTreeBlockInputStream( - structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, part.ranges, data.getOwningStorage(), use_uncompressed_cache, + data.getFullPath() + part.data_part->name + '/', max_block_size, column_names, data, + part.data_part, part.ranges, use_uncompressed_cache, prewhere_actions, prewhere_column)); need_marks -= marks_in_part; parts.pop_back(); @@ -313,8 +308,8 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreads( } streams.push_back(new MergeTreeBlockInputStream( - structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, ranges_to_get_from_part, data.getOwningStorage(), use_uncompressed_cache, + data.getFullPath() + part.data_part->name + '/', max_block_size, column_names, data, + part.data_part, ranges_to_get_from_part, use_uncompressed_cache, prewhere_actions, prewhere_column)); } @@ -338,8 +333,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal size_t max_block_size, bool use_uncompressed_cache, ExpressionActionsPtr prewhere_actions, - const String & prewhere_column, - const MergeTreeData::LockedTableStructurePtr & structure) + const String & prewhere_column) { size_t sum_marks = 0; for (size_t i = 0; i < parts.size(); ++i) @@ -351,7 +345,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal ExpressionActionsPtr sign_filter_expression; String sign_filter_column; - createPositiveSignCondition(sign_filter_expression, sign_filter_column, structure); + createPositiveSignCondition(sign_filter_expression, sign_filter_column); BlockInputStreams to_collapse; @@ -360,8 +354,8 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal RangesInDataPart & part = parts[part_index]; BlockInputStreamPtr source_stream = new MergeTreeBlockInputStream( - structure->getFullPath() + part.data_part->name + '/', structure, max_block_size, column_names, data, - part.data_part, part.ranges, data.getOwningStorage(), use_uncompressed_cache, + data.getFullPath() + part.data_part->name + '/', max_block_size, column_names, data, + part.data_part, part.ranges, use_uncompressed_cache, prewhere_actions, prewhere_column); to_collapse.push_back(new ExpressionBlockInputStream(source_stream, data.getPrimaryExpression())); @@ -376,8 +370,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::spreadMarkRangesAmongThreadsFinal return res; } -void MergeTreeDataSelectExecutor::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column, - const MergeTreeData::LockedTableStructurePtr & structure) +void MergeTreeDataSelectExecutor::createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column) { ASTFunction * function = new ASTFunction; ASTPtr function_ptr = function; @@ -404,7 +397,7 @@ void MergeTreeDataSelectExecutor::createPositiveSignCondition(ExpressionActionsP one->type = new DataTypeInt8; one->value = Field(static_cast(1)); - out_expression = ExpressionAnalyzer(function_ptr, data.context, structure->getColumnsList()).getActions(false); + out_expression = ExpressionAnalyzer(function_ptr, data.context, data.getColumnsList()).getActions(false); out_column = function->getColumnName(); } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index bb9d41568b4..a7c1550c7ee 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -6,9 +6,9 @@ namespace DB { -BlocksWithDateIntervals MergeTreeDataWriter::splitBlockIntoParts(const Block & block, const MergeTreeData::LockedTableStructurePtr & structure) +BlocksWithDateIntervals MergeTreeDataWriter::splitBlockIntoParts(const Block & block) { - structure->check(block, true); + data.check(block, true); DateLUTSingleton & date_lut = DateLUTSingleton::instance(); @@ -69,8 +69,7 @@ BlocksWithDateIntervals MergeTreeDataWriter::splitBlockIntoParts(const Block & b return res; } -MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInterval & block_with_dates, UInt64 temp_index, - const MergeTreeData::LockedTableStructurePtr & structure) +MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDateInterval & block_with_dates, UInt64 temp_index) { Block & block = block_with_dates.block; UInt16 min_date = block_with_dates.min_date; @@ -86,7 +85,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa DayNum_t(min_date), DayNum_t(max_date), temp_index, temp_index, 0); - String part_tmp_path = structure->getFullPath() + tmp_part_name + "/"; + String part_tmp_path = data.getFullPath() + tmp_part_name + "/"; Poco::File(part_tmp_path).createDirectories(); diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index ede4fb735ad..908868bac43 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -239,10 +239,6 @@ BlockInputStreams StorageDistributed::read( } } - /// Не дадим уничтожать объект до конца обработки запроса. - for (auto & stream : res) - stream->setOwnedStorage(thisPtr()); - return res; } diff --git a/dbms/src/Storages/StorageLog.cpp b/dbms/src/Storages/StorageLog.cpp index d9c5a19dbf1..24ea5ce5b2b 100644 --- a/dbms/src/Storages/StorageLog.cpp +++ b/dbms/src/Storages/StorageLog.cpp @@ -31,12 +31,26 @@ namespace DB using Poco::SharedPtr; -LogBlockInputStream::LogBlockInputStream(size_t block_size_, const Names & column_names_, StoragePtr owned_storage, size_t mark_number_, size_t rows_limit_) - : IProfilingBlockInputStream(owned_storage), block_size(block_size_), column_names(column_names_), storage(dynamic_cast(*owned_storage)), mark_number(mark_number_), rows_limit(rows_limit_), rows_read(0), current_mark(mark_number_) +LogBlockInputStream::LogBlockInputStream(size_t block_size_, const Names & column_names_, StorageLog & storage_, size_t mark_number_, size_t rows_limit_) + : block_size(block_size_), column_names(column_names_), storage(storage_), + mark_number(mark_number_), rows_limit(rows_limit_), rows_read(0), current_mark(mark_number_) { } +String LogBlockInputStream::getID() const +{ + std::stringstream res; + res << "Log(" << storage.getTableName() << ", " << &storage << ", " << mark_number << ", " << rows_limit; + + for (size_t i = 0; i < column_names.size(); ++i) + res << ", " << column_names[i]; + + res << ")"; + return res.str(); +} + + Block LogBlockInputStream::readImpl() { Block res; @@ -232,8 +246,8 @@ void LogBlockInputStream::readData(const String & name, const IDataType & type, } -LogBlockOutputStream::LogBlockOutputStream(StoragePtr owned_storage) - : IBlockOutputStream(owned_storage), storage(dynamic_cast(*owned_storage)), +LogBlockOutputStream::LogBlockOutputStream(StorageLog & storage_) + : storage(storage_), lock(storage.rwlock), marks_stream(storage.marks_file.path(), 4096, O_APPEND | O_CREAT | O_WRONLY) { for (NamesAndTypesList::const_iterator it = storage.columns->begin(); it != storage.columns->end(); ++it) @@ -599,7 +613,7 @@ BlockInputStreams StorageLog::read( res.push_back(new LogBlockInputStream( max_block_size, column_names, - thisPtr(), + *this, 0, std::numeric_limits::max())); } else @@ -621,7 +635,7 @@ BlockInputStreams StorageLog::read( res.push_back(new LogBlockInputStream( max_block_size, column_names, - thisPtr(), + *this, from_mark + thread * (to_mark - from_mark) / threads, marks[from_mark + (thread + 1) * (to_mark - from_mark) / threads - 1].rows - ((thread == 0 && from_mark == 0) @@ -650,7 +664,7 @@ BlockOutputStreamPtr StorageLog::write( ASTPtr query) { loadMarks(); - return new LogBlockOutputStream(thisPtr()); + return new LogBlockOutputStream(*this); } diff --git a/dbms/src/Storages/StorageMemory.cpp b/dbms/src/Storages/StorageMemory.cpp index f63ee7b0f7d..0aa3cb5d917 100644 --- a/dbms/src/Storages/StorageMemory.cpp +++ b/dbms/src/Storages/StorageMemory.cpp @@ -12,8 +12,8 @@ namespace DB using Poco::SharedPtr; -MemoryBlockInputStream::MemoryBlockInputStream(const Names & column_names_, BlocksList::iterator begin_, BlocksList::iterator end_, StoragePtr owned_storage) - : IProfilingBlockInputStream(owned_storage), column_names(column_names_), begin(begin_), end(end_), it(begin) +MemoryBlockInputStream::MemoryBlockInputStream(const Names & column_names_, BlocksList::iterator begin_, BlocksList::iterator end_) + : column_names(column_names_), begin(begin_), end(end_), it(begin) { } @@ -39,8 +39,8 @@ Block MemoryBlockInputStream::readImpl() } -MemoryBlockOutputStream::MemoryBlockOutputStream(StoragePtr owned_storage) - : IBlockOutputStream(owned_storage), storage(dynamic_cast(*owned_storage)) +MemoryBlockOutputStream::MemoryBlockOutputStream(StorageMemory & storage_) + : storage(storage_) { } @@ -92,7 +92,7 @@ BlockInputStreams StorageMemory::read( std::advance(begin, thread * size / threads); std::advance(end, (thread + 1) * size / threads); - res.push_back(new MemoryBlockInputStream(column_names, begin, end, thisPtr())); + res.push_back(new MemoryBlockInputStream(column_names, begin, end)); } return res; @@ -102,7 +102,7 @@ BlockInputStreams StorageMemory::read( BlockOutputStreamPtr StorageMemory::write( ASTPtr query) { - return new MemoryBlockOutputStream(thisPtr()); + return new MemoryBlockOutputStream(*this); } diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 9b38477a71c..ac603df1a0b 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -39,12 +39,9 @@ StoragePtr StorageMergeTree::create( const String & sign_column_, const MergeTreeSettings & settings_) { - StorageMergeTree * storage = new StorageMergeTree( + return (new StorageMergeTree( path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, - sampling_expression_, index_granularity_, mode_, sign_column_, settings_); - StoragePtr ptr = storage->thisPtr(); - storage->data.setOwningStorage(ptr); - return ptr; + sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); } void StorageMergeTree::shutdown() @@ -76,7 +73,7 @@ BlockInputStreams StorageMergeTree::read( BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) { - return new MergeTreeBlockOutputStream(thisPtr()); + return new MergeTreeBlockOutputStream(*this); } void StorageMergeTree::dropImpl() @@ -88,8 +85,6 @@ void StorageMergeTree::dropImpl() void StorageMergeTree::rename(const String & new_path_to_db, const String & new_name) { - BigLockPtr lock = lockAllOperations(); - std::string new_full_path = new_path_to_db + escapeForFileName(new_name) + '/'; data.setPath(new_full_path); @@ -103,8 +98,6 @@ void StorageMergeTree::rename(const String & new_path_to_db, const String & new_ void StorageMergeTree::alter(const ASTAlterQuery::Parameters & params) { - /// InterpreterAlterQuery уже взял BigLock. - data.alter(params); } @@ -158,13 +151,17 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) } } - if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && - !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) - break; + { + auto structure_lock = lockStructure(false); + if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && + !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) + break; + } merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); } + auto structure_lock = lockStructure(true); merger.mergeParts(merging_tagger->parts); } diff --git a/dbms/src/Storages/StorageTinyLog.cpp b/dbms/src/Storages/StorageTinyLog.cpp index ec365d0321a..63e527e700f 100644 --- a/dbms/src/Storages/StorageTinyLog.cpp +++ b/dbms/src/Storages/StorageTinyLog.cpp @@ -27,12 +27,25 @@ namespace DB using Poco::SharedPtr; -TinyLogBlockInputStream::TinyLogBlockInputStream(size_t block_size_, const Names & column_names_, StoragePtr owned_storage) - : IProfilingBlockInputStream(owned_storage), block_size(block_size_), column_names(column_names_), storage(dynamic_cast(*owned_storage)), finished(false) +TinyLogBlockInputStream::TinyLogBlockInputStream(size_t block_size_, const Names & column_names_, StorageTinyLog & storage_) + : block_size(block_size_), column_names(column_names_), storage(storage_), finished(false) { } +String TinyLogBlockInputStream::getID() const +{ + std::stringstream res; + res << "TinyLog(" << storage.getTableName() << ", " << &storage; + + for (size_t i = 0; i < column_names.size(); ++i) + res << ", " << column_names[i]; + + res << ")"; + return res.str(); +} + + Block TinyLogBlockInputStream::readImpl() { Block res; @@ -172,8 +185,8 @@ void TinyLogBlockInputStream::readData(const String & name, const IDataType & ty } -TinyLogBlockOutputStream::TinyLogBlockOutputStream(StoragePtr owned_storage) - : IBlockOutputStream(owned_storage), storage(dynamic_cast(*owned_storage)) +TinyLogBlockOutputStream::TinyLogBlockOutputStream(StorageTinyLog & storage_) + : storage(storage_) { for (NamesAndTypesList::const_iterator it = storage.columns->begin(); it != storage.columns->end(); ++it) addStream(it->first, *it->second); @@ -363,14 +376,14 @@ BlockInputStreams StorageTinyLog::read( { check(column_names); processed_stage = QueryProcessingStage::FetchColumns; - return BlockInputStreams(1, new TinyLogBlockInputStream(max_block_size, column_names, thisPtr())); + return BlockInputStreams(1, new TinyLogBlockInputStream(max_block_size, column_names, *this)); } BlockOutputStreamPtr StorageTinyLog::write( ASTPtr query) { - return new TinyLogBlockOutputStream(thisPtr()); + return new TinyLogBlockOutputStream(*this); } From 4f46874933992b2f30d7bda78f35c1fb3fef1557 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 19 Mar 2014 15:44:41 +0400 Subject: [PATCH 020/281] added a couple of comments. [#METR-10202] --- dbms/include/DB/Interpreters/ExpressionAnalyzer.h | 2 ++ dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 307aede49c5..a16810e0a8f 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -15,6 +15,8 @@ namespace DB { /** Превращает выражение из синтаксического дерева в последовательность действий для его выполнения. + * + * NOTE: если ast - запрос SELECT из таблицы, структура этой таблицы не должна меняться во все время жизни ExpressionAnalyzer-а. */ class ExpressionAnalyzer : private boost::noncopyable { diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 07931d21c34..ae60e78e71d 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -138,6 +138,7 @@ BlockInputStreams MergeTreeDataSelectExecutor::read( ExpressionAnalyzer analyzer(select.prewhere_expression, data.context, data.getColumnsList()); prewhere_actions = analyzer.getActions(false); prewhere_column = select.prewhere_expression->getColumnName(); + /// TODO: Чтобы работали подзапросы в PREWHERE, можно тут сохранить analyzer.getSetsWithSubqueries(), а потом их выполнить. } RangesInDataParts parts_with_ranges; From d123bbc271aa726a5471a9b5f5216004e79a487d Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 19 Mar 2014 16:01:39 +0400 Subject: [PATCH 021/281] clickhouse: fixed bad_cast when formatting AST with PREWHERE. [#METR-10504] --- dbms/src/Parsers/formatAST.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 505d31228eb..dff56b9c087 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -168,9 +168,7 @@ void formatAST(const ASTSelectQuery & ast, std::ostream & s, size_t indent, bo if (ast.prewhere_expression) { s << (hilite ? hilite_keyword : "") << nl_or_ws << indent_str << "PREWHERE " << (hilite ? hilite_none : ""); - one_line - ? formatAST(*ast.prewhere_expression, s, indent, hilite, one_line) - : formatExpressionListMultiline(dynamic_cast(*ast.prewhere_expression), s, indent, hilite); + formatAST(*ast.prewhere_expression, s, indent, hilite, one_line); } if (ast.where_expression) From 730079e99dee88f22c0ede3b53e808fdc14e47e5 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 19 Mar 2014 16:35:27 +0400 Subject: [PATCH 022/281] dbms: fixed bug with incorrect to_stage calculation in storage distributed [METR-10499] --- dbms/include/DB/Common/VirtualColumnUtils.h | 8 ++++---- dbms/src/Storages/StorageChunkMerger.cpp | 2 +- dbms/src/Storages/StorageChunks.cpp | 2 +- dbms/src/Storages/StorageDistributed.cpp | 4 ++-- dbms/src/Storages/StorageMerge.cpp | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dbms/include/DB/Common/VirtualColumnUtils.h b/dbms/include/DB/Common/VirtualColumnUtils.h index c6a2328eb61..ea44e41c119 100644 --- a/dbms/include/DB/Common/VirtualColumnUtils.h +++ b/dbms/include/DB/Common/VirtualColumnUtils.h @@ -35,9 +35,9 @@ BlockInputStreamPtr getVirtualColumnsBlocks(ASTPtr query, const Block & input, c /// Извлечь из входного потока множество значений столбца name template -std::set extractSingleValueFromBlocks(BlockInputStreamPtr input, const String & name) +std::multiset extractSingleValueFromBlocks(BlockInputStreamPtr input, const String & name) { - std::set res; + std::multiset res; input->readPrefix(); while(1) { @@ -52,10 +52,10 @@ std::set extractSingleValueFromBlocks(BlockInputStreamPtr input, const Strin /// Извлечь из входного потока множество пар значений в столбцах first_name и second_name template -std::set< std::pair > extractTwoValuesFromBlocks(BlockInputStreamPtr input, +std::multiset< std::pair > extractTwoValuesFromBlocks(BlockInputStreamPtr input, const String & first_name, const String & second_name) { - std::set< std::pair > res; + std::multiset< std::pair > res; input->readPrefix(); while(1) { diff --git a/dbms/src/Storages/StorageChunkMerger.cpp b/dbms/src/Storages/StorageChunkMerger.cpp index cba0e3bd0a3..c69c7d43a11 100644 --- a/dbms/src/Storages/StorageChunkMerger.cpp +++ b/dbms/src/Storages/StorageChunkMerger.cpp @@ -130,7 +130,7 @@ BlockInputStreams StorageChunkMerger::read( else /// Иначе, считаем допустимыми все возможные значения virtual_columns = new OneBlockInputStream(virtual_columns_block); - std::set values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); + std::multiset values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); bool all_inclusive = (values.size() == virtual_columns_block.rows()); for (Storages::iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) diff --git a/dbms/src/Storages/StorageChunks.cpp b/dbms/src/Storages/StorageChunks.cpp index e85f47e28ee..7ff9741a3f6 100644 --- a/dbms/src/Storages/StorageChunks.cpp +++ b/dbms/src/Storages/StorageChunks.cpp @@ -57,7 +57,7 @@ BlockInputStreams StorageChunks::read( Block virtual_columns_block = getBlockWithVirtualColumns(); BlockInputStreamPtr virtual_columns = VirtualColumnUtils::getVirtualColumnsBlocks(query->clone(), virtual_columns_block, context); - std::set values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); + std::multiset values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); bool all_inclusive = (values.size() == virtual_columns_block.rows()); if (all_inclusive) diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index ede4fb735ad..7ca9d698be5 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -177,12 +177,12 @@ BlockInputStreams StorageDistributed::read( else /// Иначе, считаем допустимыми все возможные значения virtual_columns = new OneBlockInputStream(virtual_columns_block); - std::set< std::pair > values = + std::multiset< std::pair > values = VirtualColumnUtils::extractTwoValuesFromBlocks(virtual_columns, _host_column_name, _port_column_name); bool all_inclusive = values.size() == virtual_columns_block.rows(); size_t result_size = values.size(); - if (values.find(std::make_pair("localhost", clickhouse_port)) != values.end()) + if (cluster.getLocalNodesNum() > 0 && values.find(std::make_pair("localhost", clickhouse_port)) != values.end()) result_size += cluster.getLocalNodesNum() - 1; processed_stage = result_size == 1 diff --git a/dbms/src/Storages/StorageMerge.cpp b/dbms/src/Storages/StorageMerge.cpp index 204ff60b203..d13fad3f01b 100644 --- a/dbms/src/Storages/StorageMerge.cpp +++ b/dbms/src/Storages/StorageMerge.cpp @@ -81,7 +81,7 @@ BlockInputStreams StorageMerge::read( else /// Иначе, считаем допустимыми все возможные значения virtual_columns = new OneBlockInputStream(virtual_columns_block); - std::set values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); + std::multiset values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); bool all_inclusive = (values.size() == virtual_columns_block.rows()); for (SelectedTables::iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) From e10125a052d4fc1277a15cc16e6ff8a842995856 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 20 Mar 2014 14:59:45 +0400 Subject: [PATCH 023/281] Using IStorage locks properly in most places. Got rid of StoragePtr and DatabaseDropper. Drops are now synchronous. [#METR-10202] --- dbms/include/DB/Core/ErrorCodes.h | 2 + .../PushingToViewsBlockOutputStream.h | 3 + dbms/include/DB/Interpreters/Context.h | 7 - .../DB/Interpreters/InterpreterDropQuery.h | 3 + .../DB/Interpreters/InterpreterInsertQuery.h | 7 +- .../DB/Interpreters/InterpreterSelectQuery.h | 4 - .../Interpreters/InterpreterShowCreateQuery.h | 15 +- dbms/include/DB/Storages/DatabaseDropper.h | 37 ---- dbms/include/DB/Storages/IStorage.h | 68 ++++--- dbms/include/DB/Storages/StorageMerge.h | 2 - dbms/include/DB/Storages/StoragePtr.h | 107 ----------- .../DB/TableFunctions/ITableFunction.h | 2 +- dbms/src/Common/VirtualColumnUtils.cpp | 2 +- dbms/src/Interpreters/Context.cpp | 35 ++-- .../Interpreters/InterpreterCreateQuery.cpp | 32 ++-- .../src/Interpreters/InterpreterDropQuery.cpp | 175 ++++++++++-------- .../Interpreters/InterpreterInsertQuery.cpp | 17 +- dbms/src/Interpreters/InterpreterQuery.cpp | 3 +- .../Interpreters/InterpreterRenameQuery.cpp | 14 +- .../Interpreters/InterpreterSelectQuery.cpp | 10 - dbms/src/Storages/StorageChunkMerger.cpp | 40 ++-- dbms/src/Storages/StorageMerge.cpp | 38 ++-- dbms/src/Storages/StoragePtr.cpp | 35 ---- 23 files changed, 248 insertions(+), 410 deletions(-) delete mode 100644 dbms/include/DB/Storages/DatabaseDropper.h delete mode 100644 dbms/include/DB/Storages/StoragePtr.h delete mode 100644 dbms/src/Storages/StoragePtr.cpp diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index 8fba41fc125..b875e465ba4 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -225,6 +225,8 @@ namespace ErrorCodes NOT_AN_AGGREGATE, QUERY_WITH_SAME_ID_IS_ALREADY_RUNNING, CLIENT_HAS_CONNECTED_TO_WRONG_PORT, + TABLE_IS_DROPPED, + DATABASE_NOT_EMPTY, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/DataStreams/PushingToViewsBlockOutputStream.h b/dbms/include/DB/DataStreams/PushingToViewsBlockOutputStream.h index f6aad23c077..6e3591e203a 100644 --- a/dbms/include/DB/DataStreams/PushingToViewsBlockOutputStream.h +++ b/dbms/include/DB/DataStreams/PushingToViewsBlockOutputStream.h @@ -23,7 +23,10 @@ public: { if (database.empty()) database = context.getCurrentDatabase(); + storage = context.getTable(database, table); + addTableLock(storage->lockStructure(true)); + Dependencies dependencies = context.getDependencies(DatabaseAndTableName(database, table)); for (size_t i = 0; i < dependencies.size(); ++i) { diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 7e7838fe18c..cc71891d2d3 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -41,9 +41,6 @@ typedef std::map Tables; /// имя БД -> таблицы typedef std::map Databases; -/// имя БД -> dropper -typedef std::map DatabaseDroppers; - /// (имя базы данных, имя таблицы) typedef std::pair DatabaseAndTableName; @@ -76,7 +73,6 @@ struct ContextShared String path; /// Путь к директории с данными, со слешем на конце. Databases databases; /// Список БД и таблиц в них. - DatabaseDroppers database_droppers; /// Reference counter'ы для ленивого удаления БД. TableFunctionFactory table_function_factory; /// Табличные функции. FunctionFactory function_factory; /// Обычные функции. AggregateFunctionFactory aggregate_function_factory; /// Агрегатные функции. @@ -137,7 +133,6 @@ struct ContextShared { Poco::ScopedLock lock(mutex); - database_droppers.clear(); current_databases = databases; } @@ -232,8 +227,6 @@ public: String getDefaultFormat() const; /// Если default_format не задан - возвращается некоторый глобальный формат по-умолчанию. void setDefaultFormat(const String & name); - DatabaseDropperPtr getDatabaseDropper(const String & name); - Settings getSettings() const; void setSettings(const Settings & settings_); diff --git a/dbms/include/DB/Interpreters/InterpreterDropQuery.h b/dbms/include/DB/Interpreters/InterpreterDropQuery.h index 62bc8edd7e1..2593fa6a02b 100644 --- a/dbms/include/DB/Interpreters/InterpreterDropQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterDropQuery.h @@ -18,6 +18,9 @@ public: /// Удаляет таблицу. void execute(); + /// Удаляет таблицу, уже отцепленную от контекста (Context::detach). + static void dropDetachedTable(String database_name, StoragePtr table, Context & context); + private: ASTPtr query_ptr; Context context; diff --git a/dbms/include/DB/Interpreters/InterpreterInsertQuery.h b/dbms/include/DB/Interpreters/InterpreterInsertQuery.h index ac912c79b1d..c9a66a8da97 100644 --- a/dbms/include/DB/Interpreters/InterpreterInsertQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterInsertQuery.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -24,13 +25,13 @@ public: /** Подготовить запрос к выполнению. Вернуть поток блоков, в который можно писать данные для выполнения запроса. * Или вернуть NULL, если запрос INSERT SELECT (самодостаточный запрос - не принимает входные данные). */ - BlockOutputStreamPtr execute(); - - Block getSampleBlock(); + BlockIO execute(); private: StoragePtr getTable(); + Block getSampleBlock(); + ASTPtr query_ptr; Context context; }; diff --git a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h index 73a3baf271f..55991248a49 100644 --- a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h @@ -34,10 +34,6 @@ public: DataTypes getReturnTypes(); Block getSampleBlock(); - /** Получить CREATE запрос для таблицы, из которой идёт выбор. - */ - ASTPtr getCreateQuery(); - private: typedef Poco::SharedPtr ExpressionAnalyzerPtr; diff --git a/dbms/include/DB/Interpreters/InterpreterShowCreateQuery.h b/dbms/include/DB/Interpreters/InterpreterShowCreateQuery.h index 94119b57e7b..e0ff1f71d26 100644 --- a/dbms/include/DB/Interpreters/InterpreterShowCreateQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterShowCreateQuery.h @@ -68,18 +68,9 @@ private: { const ASTShowCreateQuery & ast = dynamic_cast(*query_ptr); - String res; - - { - Poco::ScopedLock lock(context.getMutex()); - - if (!context.isTableExist(ast.database, ast.table)) - throw Exception("Table " + (ast.database.empty() ? "" : ast.database + ".") + ast.table + " doesn't exist", ErrorCodes::UNKNOWN_TABLE); - - std::stringstream stream; - formatAST(*context.getCreateQuery(ast.database, ast.table), stream, 0, false, true); - res = stream.str(); - } + std::stringstream stream; + formatAST(*context.getCreateQuery(ast.database, ast.table), stream, 0, false, true); + String res = stream.str(); ColumnWithNameAndType col; col.name = "statement"; diff --git a/dbms/include/DB/Storages/DatabaseDropper.h b/dbms/include/DB/Storages/DatabaseDropper.h deleted file mode 100644 index ebce3d6774d..00000000000 --- a/dbms/include/DB/Storages/DatabaseDropper.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - - -/// Удаляет директорию в деструкторе. -class DatabaseDropper -{ -public: - DatabaseDropper(const std::string & data_path_) : drop_on_destroy(false), data_path(data_path_) {} - - ~DatabaseDropper() - { - if (drop_on_destroy) - { - try - { - if (std::uncaught_exception()) - LOG_ERROR(&Logger::get("DatabaseDropper"), "Didn't remove database data directory because of uncaught exception."); - else - Poco::File(data_path).remove(false); - } - catch(...) - { - } - } - } - - bool drop_on_destroy; - -private: - std::string data_path; -}; - -typedef boost::shared_ptr DatabaseDropperPtr; diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 24bcb53d2d4..9d6f1ec7fd8 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -10,12 +10,9 @@ #include #include #include -#include #include -#include #include #include -#include namespace DB @@ -91,7 +88,10 @@ public: */ TableStructureReadLockPtr lockStructure(bool will_modify_data) { - return new TableStructureReadLock(*this, true, will_modify_data); + TableStructureReadLockPtr res = new TableStructureReadLock(*this, true, will_modify_data); + if (is_dropped) + throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return res; } typedef Poco::SharedPtr TableStructureWriteLockPtr; @@ -99,13 +99,25 @@ public: /** Не дает читать структуру таблицы. Берется для ALTER, RENAME и DROP. */ - TableStructureWriteLockPtr lockStructureForAlter() { return new Poco::ScopedWriteRWLock(structure_lock); } + TableStructureWriteLockPtr lockStructureForAlter() + { + TableStructureWriteLockPtr res = new Poco::ScopedWriteRWLock(structure_lock); + if (is_dropped) + throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return res; + } /** Не дает изменять данные в таблице. (Более того, не дает посмотреть на структуру таблицы с намерением изменить данные). * Берется на время записи временных данных в ALTER MODIFY. * Под этим локом можно брать lockStructureForAlter(), чтобы изменить структуру таблицы. */ - TableDataWriteLockPtr lockDataForAlter() { return new Poco::ScopedWriteRWLock(data_lock); } + TableDataWriteLockPtr lockDataForAlter() + { + TableDataWriteLockPtr res = new Poco::ScopedWriteRWLock(data_lock); + if (is_dropped) + throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return res; + } /** Читать набор столбцов из таблицы. @@ -150,17 +162,10 @@ public: throw Exception("Method write is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } - /** Удалить данные таблицы. После вызова этого метода, использование объекта некорректно (его можно лишь уничтожить). - */ - void drop() - { - drop_on_destroy = true; - } - - /** Вызывается перед удалением директории с данными и вызовом деструктора. + /** Удалить данные таблицы. Вызывается перед удалением директории с данными. * Если не требуется никаких действий, кроме удаления директории с данными, этот метод можно оставить пустым. */ - virtual void dropImpl() {} + virtual void drop(); /** Переименовать таблицу. * Переименование имени в файле с метаданными, имени в списке таблиц в оперативке, осуществляется отдельно. @@ -220,40 +225,30 @@ public: /** Если при уничтожении объекта надо сделать какую-то сложную работу - сделать её заранее. * Например, если таблица содержит какие-нибудь потоки для фоновой работы - попросить их завершиться и дождаться завершения. * По-умолчанию - ничего не делать. + * Может вызываться одновременно из разных потоков, даже после вызова drop(). */ virtual void shutdown() {} - + /** Возвращает владеющий указатель на себя. */ - StoragePtr thisPtr() + std::shared_ptr thisPtr() { - if (!this_ptr.lock()) + std::shared_ptr res = this_ptr.lock(); + if (!res) { - auto p = boost::make_shared(this); - this_ptr = p; - return StoragePtr(this_ptr); - } - else - { - return StoragePtr(this_ptr); + res.reset(this); + this_ptr = res; } + return res; } - - /** Не дает удалить БД до удаления таблицы. Присваивается перед удалением таблицы или БД. - */ - DatabaseDropperPtr database_to_drop; - bool drop_on_destroy; - - /** Директория с данными. Будет удалена после удаления таблицы (после вызова dropImpl). - */ - std::string path_to_remove_on_drop; + bool is_dropped; protected: - IStorage() : drop_on_destroy(false) {} + IStorage() : is_dropped(false) {} private: - boost::weak_ptr this_ptr; + std::weak_ptr this_ptr; /// Брать следующие два лока всегда нужно в этом порядке. @@ -278,6 +273,7 @@ private: mutable Poco::RWLock structure_lock; }; +typedef std::shared_ptr StoragePtr; typedef std::vector StorageVector; } diff --git a/dbms/include/DB/Storages/StorageMerge.h b/dbms/include/DB/Storages/StorageMerge.h index 9fe18d20dec..54163a682ae 100644 --- a/dbms/include/DB/Storages/StorageMerge.h +++ b/dbms/include/DB/Storages/StorageMerge.h @@ -17,8 +17,6 @@ typedef Poco::SharedPtr StorageMergePtr; */ class StorageMerge : public IStorage { -typedef std::vector SelectedTables; - public: static StoragePtr create( const std::string & name_, /// Имя таблицы. diff --git a/dbms/include/DB/Storages/StoragePtr.h b/dbms/include/DB/Storages/StoragePtr.h deleted file mode 100644 index 96bd2a9a489..00000000000 --- a/dbms/include/DB/Storages/StoragePtr.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - - -namespace DB -{ - -class IStorage; -class StorageWeakPtr; - -class StoragePtr -{ -private: - /// Содержит IStorage. В деструкторе при необходимости вызывает IStorage::dropImpl() перед уничтожением IStorage. - struct Wrapper - { - Wrapper(); - Wrapper(IStorage * s); - - boost::scoped_ptr storage; - - ~Wrapper(); - }; - - - StoragePtr(boost::weak_ptr p) : ptr(p) {} - - boost::shared_ptr ptr; - - friend class IStorage; - friend class StorageWeakPtr; - -public: - StoragePtr() {} - StoragePtr(const StoragePtr & p) : ptr(p.ptr) {} - inline StoragePtr(const StorageWeakPtr & p); - - StoragePtr& operator= (const StoragePtr & p) - { - ptr = p.ptr; - return *this; - } - - IStorage* get() const - { - if (ptr == NULL) - return NULL; - else - return ptr->storage.get(); - } - - bool operator== (const IStorage * p) const - { - return get() == p; - } - - IStorage* operator-> () const - { - return get(); - } - - IStorage& operator* () const - { - return *get(); - } - - operator IStorage*() const - { - return get(); - } - - operator bool() const - { - return bool(ptr); - } - - bool operator! () const - { - return !bool(ptr); - } -}; - -class StorageWeakPtr -{ -public: - StorageWeakPtr() {} - StorageWeakPtr(const StoragePtr & p) : ptr(p.ptr) {} - - StoragePtr lock() - { - return StoragePtr(ptr); - } - -private: - friend class StoragePtr; - - boost::weak_ptr ptr; -}; - -inline StoragePtr::StoragePtr(const StorageWeakPtr & p) : ptr(p.ptr) {} - -} diff --git a/dbms/include/DB/TableFunctions/ITableFunction.h b/dbms/include/DB/TableFunctions/ITableFunction.h index 6a0ef4f5df5..86540e8d56e 100644 --- a/dbms/include/DB/TableFunctions/ITableFunction.h +++ b/dbms/include/DB/TableFunctions/ITableFunction.h @@ -2,7 +2,7 @@ #include -#include +#include #include #include diff --git a/dbms/src/Common/VirtualColumnUtils.cpp b/dbms/src/Common/VirtualColumnUtils.cpp index cebb5a89a35..a287317a92e 100644 --- a/dbms/src/Common/VirtualColumnUtils.cpp +++ b/dbms/src/Common/VirtualColumnUtils.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include namespace DB diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index b4b1b30888b..1fcc148ff9d 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -199,7 +199,7 @@ StoragePtr Context::tryGetTable(const String & database_name, const String & tab void Context::addTable(const String & database_name, const String & table_name, StoragePtr table) { Poco::ScopedLock lock(shared->mutex); - + String db = database_name.empty() ? current_database : database_name; assertDatabaseExists(db); @@ -240,19 +240,22 @@ void Context::detachDatabase(const String & database_name) String db = database_name.empty() ? current_database : database_name; assertDatabaseExists(db); - shared->databases.erase(shared->databases.find(db)); - shared->database_droppers.erase(db); + shared->databases.erase(db); } ASTPtr Context::getCreateQuery(const String & database_name, const String & table_name) const { - Poco::ScopedLock lock(shared->mutex); + StoragePtr table; + String db; - String db = database_name.empty() ? current_database : database_name; + { + Poco::ScopedLock lock(shared->mutex); + db = database_name.empty() ? current_database : database_name; + table = getTable(db, table_name); + } - assertDatabaseExists(db); - assertTableExists(db, table_name); + auto table_lock = table->lockStructure(false); /// Здесь хранится определение таблицы String metadata_path = shared->path + "metadata/" + escapeForFileName(db) + "/" + escapeForFileName(table_name) + ".sql"; @@ -262,7 +265,7 @@ ASTPtr Context::getCreateQuery(const String & database_name, const String & tabl try { /// Если файл .sql не предусмотрен (например, для таблиц типа ChunkRef), то движок может сам предоставить запрос CREATE. - return getTable(database_name, table_name)->getCustomCreateQuery(*this); + return table->getCustomCreateQuery(*this); } catch (...) { @@ -304,22 +307,6 @@ ASTPtr Context::getCreateQuery(const String & database_name, const String & tabl } -DatabaseDropperPtr Context::getDatabaseDropper(const String & name) -{ - Poco::ScopedLock lock(shared->mutex); - - String db = name.empty() ? current_database : name; - - if (shared->databases.count(db) == 0) - throw Exception("Database " + db + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE); - - if (shared->database_droppers.count(db) == 0) - shared->database_droppers[db] = DatabaseDropperPtr(new DatabaseDropper(shared->path + "data/" + escapeForFileName(db))); - - return shared->database_droppers[db]; -} - - Settings Context::getSettings() const { Poco::ScopedLock lock(shared->mutex); diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index 60e01f5fd5c..07913765133 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -74,11 +74,26 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) return StoragePtr(); } - StoragePtr res; SharedPtr interpreter_select; + Block select_sample; + if (create.select && !create.attach) + { + interpreter_select = new InterpreterSelectQuery(create.select, context); + select_sample = interpreter_select->getSampleBlock(); + } + + StoragePtr res; String storage_name; NamesAndTypesListPtr columns = new NamesAndTypesList; + StoragePtr as_storage; + IStorage::TableStructureReadLockPtr as_storage_lock; + if (!as_table_name.empty()) + { + as_storage = context.getTable(as_database_name, as_table_name); + as_storage_lock = as_storage->lockStructure(false); + } + { Poco::ScopedLock lock(context.getMutex()); @@ -92,12 +107,6 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); } - if (!create.as_table.empty()) - context.assertTableExists(as_database_name, as_table_name); - - if (create.select && !create.attach) - interpreter_select = new InterpreterSelectQuery(create.select, context); - /// Получаем список столбцов if (create.columns) { @@ -112,13 +121,12 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) } } else if (!create.as_table.empty()) - columns = new NamesAndTypesList(context.getTable(as_database_name, as_table_name)->getColumnsList()); + columns = new NamesAndTypesList(as_storage->getColumnsList()); else if (create.select) { - Block sample = interpreter_select->getSampleBlock(); columns = new NamesAndTypesList; - for (size_t i = 0; i < sample.columns(); ++i) - columns->push_back(NameAndTypePair(sample.getByPosition(i).name, sample.getByPosition(i).type)); + for (size_t i = 0; i < select_sample.columns(); ++i) + columns->push_back(NameAndTypePair(select_sample.getByPosition(i).name, select_sample.getByPosition(i).type)); } else throw Exception("Incorrect CREATE query: required list of column descriptions or AS section or SELECT.", ErrorCodes::INCORRECT_QUERY); @@ -159,7 +167,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) } else if (!create.as_table.empty()) { - storage_name = context.getTable(as_database_name, as_table_name)->getName(); + storage_name = as_storage->getName(); create.storage = dynamic_cast(*context.getCreateQuery(as_database_name, as_table_name)).storage; } else if (create.is_view) diff --git a/dbms/src/Interpreters/InterpreterDropQuery.cpp b/dbms/src/Interpreters/InterpreterDropQuery.cpp index 9649cc8b6b9..25c45e735b5 100644 --- a/dbms/src/Interpreters/InterpreterDropQuery.cpp +++ b/dbms/src/Interpreters/InterpreterDropQuery.cpp @@ -1,10 +1,9 @@ #include #include - #include - #include +#include namespace DB @@ -19,8 +18,6 @@ InterpreterDropQuery::InterpreterDropQuery(ASTPtr query_ptr_, Context & context_ void InterpreterDropQuery::execute() { - Poco::ScopedLock lock(context.getMutex()); - String path = context.getPath(); String current_database = context.getCurrentDatabase(); @@ -28,92 +25,116 @@ void InterpreterDropQuery::execute() String database_name = drop.database.empty() ? current_database : drop.database; String database_name_escaped = escapeForFileName(database_name); - String table_name = drop.table; - String table_name_escaped = escapeForFileName(table_name); - String data_path = path + "data/" + database_name_escaped + "/" + table_name_escaped; - String metadata_path = path + "metadata/" + database_name_escaped + "/" + (!table_name.empty() ? table_name_escaped + ".sql" : ""); + String data_path = path + "data/" + database_name_escaped + "/"; + String metadata_path = path + "metadata/" + database_name_escaped + "/"; - if (!drop.if_exists) - context.assertDatabaseExists(database_name); + StorageVector tables_to_drop; if (!drop.table.empty()) { - /// Удаление таблицы - if (!context.isTableExist(database_name, table_name)) - { - if (!drop.if_exists) - throw Exception("Table " + database_name + "." + table_name + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE); - } + StoragePtr table; + + if (drop.if_exists) + table = context.tryGetTable(database_name, drop.table); else - { - /// Удаляем данные таблицы - if (!drop.detach) - { - StoragePtr table = context.getTable(database_name, table_name); - DatabaseDropperPtr database_dropper = context.getDatabaseDropper(database_name); - table->path_to_remove_on_drop = data_path; - /// Присвоим database_to_drop на случай, если БД попробуют удалить до завершения удаления этой таблицы. - table->database_to_drop = database_dropper; - table->drop(); + table = context.getTable(database_name, drop.table); - /// Для таблиц типа ChunkRef, файла с метаданными не существует. - if (Poco::File(metadata_path).exists()) - Poco::File(metadata_path).remove(); - } - - /// Удаляем информацию о таблице из оперативки - StoragePtr detached_table = context.detachTable(database_name, table_name); - - { - /** Во время уничтожения объекта с таблицей, mutex должен быть разблокирован, - * так как таблица может ожидать завершения потоков, которые прямо сейчас ждут этого mutex-а. - */ - Poco::ScopedUnlock unlock(context.getMutex()); - detached_table->shutdown(); - detached_table = StoragePtr(); - } - } + if (table) + tables_to_drop.push_back(table); + else + return; } else { - if (context.isDatabaseExist(database_name)) + Poco::ScopedLock lock(context.getMutex()); + + if (!drop.if_exists) + context.assertDatabaseExists(database_name); + else if (!context.isDatabaseExist(database_name)) + return; + + Tables tables = context.getDatabases()[database_name]; + + for (auto & it : tables) { - /// Удаление базы данных - if (!drop.detach) - { - /// Тот, кто удалит директорию с БД, когда все ее таблицы будут удалены. - DatabaseDropperPtr database_dropper = context.getDatabaseDropper(database_name); - database_dropper->drop_on_destroy = true; - - Tables detached_tables = context.getDatabases()[database_name]; - - /// Удаление всех таблиц - for (Tables::iterator it = detached_tables.begin(); it != detached_tables.end(); ++it) - context.detachTable(database_name, it->first); - - { - Poco::ScopedUnlock unlock(context.getMutex()); - - for (Tables::iterator it = detached_tables.begin(); it != detached_tables.end(); ++it) - { - StoragePtr & table = it->second; - - table->path_to_remove_on_drop = data_path + escapeForFileName(it->first); - table->database_to_drop = database_dropper; - table->drop(); - table->shutdown(); - table = StoragePtr(); - } - } - - Poco::File(metadata_path).remove(true); - } - - /// Удаляем информацию о БД из оперативки - context.detachDatabase(database_name); + tables_to_drop.push_back(it.second); } } + + for (StoragePtr table : tables_to_drop) + { + table->shutdown(); + + /// Если кто-то успел удалить эту таблицу, выбросит исключение. + auto table_lock = table->lockStructureForAlter(); + + String current_table_name = table->getTableName(); + + /// Удаляем информацию о таблице из оперативки + context.detachTable(database_name, current_table_name); + + /// Удаляем данные таблицы + if (!drop.detach) + { + String current_data_path = data_path + escapeForFileName(current_table_name); + String current_metadata_path = metadata_path + escapeForFileName(current_table_name) + ".sql"; + + /// Для таблиц типа ChunkRef, файла с метаданными не существует. + if (Poco::File(current_metadata_path).exists()) + Poco::File(current_metadata_path).remove(); + + table->drop(); + table->is_dropped = true; + + if (Poco::File(current_data_path).exists()) + Poco::File(current_data_path).remove(true); + } + } + + if (drop.table.empty()) + { + /// Удаление базы данных. Таблицы в ней уже удалены. + + Poco::ScopedLock lock(context.getMutex()); + + /// Кто-то мог успеть удалить БД до нас. + context.assertDatabaseExists(database_name); + + /// Кто-то мог успеть создать таблицу в удаляемой БД, пока мы удаляли таблицы без лока контекста. + if (!context.getDatabases()[database_name].empty()) + throw Exception("New table appeared in database being dropped. Try dropping it again.", ErrorCodes::DATABASE_NOT_EMPTY); + + /// Удаляем информацию о БД из оперативки + context.detachDatabase(database_name); + + Poco::File(data_path).remove(false); + Poco::File(metadata_path).remove(false); + } +} + +void InterpreterDropQuery::dropDetachedTable(String database_name, StoragePtr table, Context & context) +{ + table->shutdown(); + + auto table_lock = table->lockStructureForAlter(); + + String table_name = table->getTableName(); + + String path = context.getPath(); + String database_name_escaped = escapeForFileName(database_name); + + String data_path = path + "data/" + database_name_escaped + "/" + escapeForFileName(table_name); + String metadata_path = path + "metadata/" + database_name_escaped + "/" + escapeForFileName(table_name) + ".sql"; + + if (Poco::File(metadata_path).exists()) + Poco::File(metadata_path).remove(); + + table->drop(); + table->is_dropped = true; + + if (Poco::File(data_path).exists()) + Poco::File(data_path).remove(true); } diff --git a/dbms/src/Interpreters/InterpreterInsertQuery.cpp b/dbms/src/Interpreters/InterpreterInsertQuery.cpp index 46a45215ee9..f8242d30717 100644 --- a/dbms/src/Interpreters/InterpreterInsertQuery.cpp +++ b/dbms/src/Interpreters/InterpreterInsertQuery.cpp @@ -75,9 +75,6 @@ void InterpreterInsertQuery::execute(ReadBuffer * remaining_data_istr) BlockOutputStreamPtr out = new AddingDefaultBlockOutputStream(new PushingToViewsBlockOutputStream(query.database, query.table, context, query_ptr), required_columns); - /// TODO: Взять также IStorage::TableStructureReadLock-и для всех затронутых materialized views. - out->addTableLock(table_lock); - /// Какой тип запроса: INSERT VALUES | INSERT FORMAT | INSERT SELECT? if (!query.select) { @@ -118,7 +115,7 @@ void InterpreterInsertQuery::execute(ReadBuffer * remaining_data_istr) } -BlockOutputStreamPtr InterpreterInsertQuery::execute() +BlockIO InterpreterInsertQuery::execute() { ASTInsertQuery & query = dynamic_cast(*query_ptr); StoragePtr table = getTable(); @@ -132,21 +129,23 @@ BlockOutputStreamPtr InterpreterInsertQuery::execute() /// Создаем кортеж из нескольких стримов, в которые будем писать данные. BlockOutputStreamPtr out = new AddingDefaultBlockOutputStream(new PushingToViewsBlockOutputStream(query.database, query.table, context, query_ptr), required_columns); - /// TODO: Взять также IStorage::TableStructureReadLock-и для всех затронутых materialized views. - out->addTableLock(table_lock); + BlockIO res; + res.out_sample = getSampleBlock(); /// Какой тип запроса: INSERT или INSERT SELECT? if (!query.select) - return out; + { + res.out = out; + } else { InterpreterSelectQuery interpreter_select(query.select, context); BlockInputStreamPtr in = interpreter_select.execute(); in = new MaterializingBlockInputStream(in); copyData(*in, *out); - - return NULL; } + + return res; } diff --git a/dbms/src/Interpreters/InterpreterQuery.cpp b/dbms/src/Interpreters/InterpreterQuery.cpp index b6f044d37c8..c742a3f1b5e 100644 --- a/dbms/src/Interpreters/InterpreterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterQuery.cpp @@ -135,8 +135,7 @@ BlockIO InterpreterQuery::execute() { throwIfReadOnly(); InterpreterInsertQuery interpreter(query_ptr, context); - res.out = interpreter.execute(); - res.out_sample = interpreter.getSampleBlock(); + res = interpreter.execute(); } else if (dynamic_cast(&*query_ptr)) { diff --git a/dbms/src/Interpreters/InterpreterRenameQuery.cpp b/dbms/src/Interpreters/InterpreterRenameQuery.cpp index efb8c7b113b..37ea5d47eb2 100644 --- a/dbms/src/Interpreters/InterpreterRenameQuery.cpp +++ b/dbms/src/Interpreters/InterpreterRenameQuery.cpp @@ -27,9 +27,6 @@ InterpreterRenameQuery::InterpreterRenameQuery(ASTPtr query_ptr_, Context & cont void InterpreterRenameQuery::execute() { - /** Все таблицы переименовываются под глобальной блокировкой. */ - Poco::ScopedLock lock(context.getMutex()); - String path = context.getPath(); String current_database = context.getCurrentDatabase(); @@ -53,12 +50,17 @@ void InterpreterRenameQuery::execute() String to_table_name_escaped = escapeForFileName(to_table_name); String to_metadata_path = path + "metadata/" + to_database_name_escaped + "/" + (!to_table_name.empty() ? to_table_name_escaped + ".sql" : ""); - context.assertTableExists(from_database_name, from_table_name); + /// Заблокировать таблицу нужно при незаблокированном контексте. + StoragePtr table = context.getTable(from_database_name, from_table_name); + auto table_lock = table->lockStructureForAlter(); + + /** Все таблицы переименовываются под глобальной блокировкой. */ + Poco::ScopedLock lock(context.getMutex()); + context.assertTableDoesntExist(to_database_name, to_table_name); /// Уведомляем таблицу о том, что она переименовается. Если таблица не поддерживает переименование - кинется исключение. - StoragePtr table = context.getTable(from_database_name, from_table_name); - auto table_lock = table->lockStructureForAlter(); // TODO: Тут возможен дедлок. + table->rename(path + "data/" + to_database_name_escaped + "/", to_table_name); /// Пишем новый файл с метаданными. diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index d0f48e276aa..ebc74a1117f 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -141,16 +141,6 @@ void InterpreterSelectQuery::getDatabaseAndTableNames(String & database_name, St } -ASTPtr InterpreterSelectQuery::getCreateQuery() -{ - String database_name; - String table_name; - - getDatabaseAndTableNames(database_name, table_name); - return context.getCreateQuery(database_name, table_name); -} - - DataTypes InterpreterSelectQuery::getReturnTypes() { DataTypes res; diff --git a/dbms/src/Storages/StorageChunkMerger.cpp b/dbms/src/Storages/StorageChunkMerger.cpp index cba0e3bd0a3..bc0dafc4797 100644 --- a/dbms/src/Storages/StorageChunkMerger.cpp +++ b/dbms/src/Storages/StorageChunkMerger.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -387,16 +388,16 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) } currently_written_groups.insert(new_table_full_name); - - /// Уроним Chunks таблицу с таким именем, если она есть. Она могла остаться в результате прерванного слияния той же группы чанков. - executeQuery("DROP TABLE IF EXISTS " + new_table_full_name, context, true); - - /// Выполним запрос для создания Chunks таблицы. - executeQuery("CREATE TABLE " + new_table_full_name + " " + formatted_columns + " ENGINE = Chunks", context, true); - - new_storage_ptr = context.getTable(source_database, new_table_name); } - + + /// Уроним Chunks таблицу с таким именем, если она есть. Она могла остаться в результате прерванного слияния той же группы чанков. + executeQuery("DROP TABLE IF EXISTS " + new_table_full_name, context, true); + + /// Выполним запрос для создания Chunks таблицы. + executeQuery("CREATE TABLE " + new_table_full_name + " " + formatted_columns + " ENGINE = Chunks", context, true); + + new_storage_ptr = context.getTable(source_database, new_table_name); + /// Скопируем данные в новую таблицу. StorageChunks & new_storage = dynamic_cast(*new_storage_ptr); @@ -455,6 +456,9 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) } /// Атомарно подменим исходные таблицы ссылками на новую. + /// При этом удалять таблицы под мьютексом контекста нельзя, пока только отцепим их. + Storages tables_to_drop; + { Poco::ScopedLock lock(context.getMutex()); @@ -467,13 +471,13 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) std::string src_name = src_storage->getTableName(); /// Если таблицу успели удалить, ничего не делаем. - if (!context.getDatabases()[source_database].count(src_name)) + if (!context.isTableExist(source_database, src_name)) continue; - /// Роняем исходную таблицу. - executeQuery("DROP TABLE " + backQuoteIfNeed(source_database) + "." + backQuoteIfNeed(src_name), context, true); - - /// Создаем на ее месте ChunkRef + /// Отцепляем исходную таблицу. Ее данные и метаданные остаются на диске. + tables_to_drop.push_back(context.detachTable(source_database, src_name)); + + /// Создаем на ее месте ChunkRef. Это возможно только потому что у ChunkRef нет ни, ни метаданных. try { context.addTable(source_database, src_name, StorageChunkRef::create(src_name, context, source_database, new_table_name, false)); @@ -490,6 +494,14 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) currently_written_groups.erase(new_table_full_name); } + /// Теперь удалим данные отцепленных таблиц. + for (StoragePtr table : tables_to_drop) + { + InterpreterDropQuery::dropDetachedTable(source_database, table, context); + /// NOTE: Если между подменой таблицы и этой строчкой кто-то успеет попытаться создать новую таблицу на ее месте, + /// что-нибудь может сломаться. + } + /// Сейчас на new_storage ссылаются таблицы типа ChunkRef. Удалим лишнюю ссылку, которая была при создании. new_storage.removeReference(); diff --git a/dbms/src/Storages/StorageMerge.cpp b/dbms/src/Storages/StorageMerge.cpp index 204ff60b203..13b36c809ff 100644 --- a/dbms/src/Storages/StorageMerge.cpp +++ b/dbms/src/Storages/StorageMerge.cpp @@ -55,7 +55,7 @@ BlockInputStreams StorageMerge::read( else virt_column_names.push_back(it); - SelectedTables selected_tables; + StorageVector selected_tables; /// Среди всех стадий, до которых обрабатывается запрос в таблицах-источниках, выберем минимальную. processed_stage = QueryProcessingStage::Complete; @@ -72,6 +72,15 @@ BlockInputStreams StorageMerge::read( getSelectedTables(selected_tables); } + typedef std::vector TableLocks; + TableLocks table_locks; + + /// Нельзя, чтобы эти таблицы кто-нибудь удалил, пока мы их читаем. + for (auto table : selected_tables) + { + table_locks.push_back(table->lockStructure(false)); + } + Block virtual_columns_block = getBlockWithVirtualColumns(selected_tables); BlockInputStreamPtr virtual_columns; @@ -84,20 +93,23 @@ BlockInputStreams StorageMerge::read( std::set values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); bool all_inclusive = (values.size() == virtual_columns_block.rows()); - for (SelectedTables::iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) + for (size_t i = 0; i < selected_tables.size(); ++i) { - if (!all_inclusive && values.find((*it)->getTableName()) == values.end()) + StoragePtr table = selected_tables[i]; + auto table_lock = table_locks[i]; + + if (!all_inclusive && values.find(table->getTableName()) == values.end()) continue; /// Если в запросе только виртуальные столбцы, надо запросить хотя бы один любой другой. if (real_column_names.size() == 0) - real_column_names.push_back(ExpressionActions::getSmallestColumn((*it)->getColumnsList())); + real_column_names.push_back(ExpressionActions::getSmallestColumn(table->getColumnsList())); /// Подменяем виртуальный столбец на его значение ASTPtr modified_query_ast = query->clone(); - VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _table_column_name, (*it)->getTableName()); + VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _table_column_name, table->getTableName()); - BlockInputStreams source_streams = (*it)->read( + BlockInputStreams source_streams = table->read( real_column_names, modified_query_ast, settings, @@ -105,17 +117,21 @@ BlockInputStreams StorageMerge::read( max_block_size, selected_tables.size() > threads ? 1 : (threads / selected_tables.size())); + for (auto & stream : source_streams) + { + stream->addTableLock(table_lock); + } + for (auto & virtual_column : virt_column_names) { if (virtual_column == _table_column_name) { for (auto & stream : source_streams) - stream = new AddingConstColumnBlockInputStream(stream, new DataTypeString, (*it)->getTableName(), _table_column_name); + stream = new AddingConstColumnBlockInputStream(stream, new DataTypeString, table->getTableName(), _table_column_name); } } - for (BlockInputStreams::iterator jt = source_streams.begin(); jt != source_streams.end(); ++jt) - res.push_back(*jt); + res.insert(res.end(), source_streams.begin(), source_streams.end()); if (tmp_processed_stage < processed_stage) processed_stage = tmp_processed_stage; @@ -135,7 +151,7 @@ Block StorageMerge::getBlockWithVirtualColumns(const std::vector & s Block res; ColumnWithNameAndType _table(new ColumnString, new DataTypeString, _table_column_name); - for (SelectedTables::const_iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) + for (StorageVector::const_iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) _table.column->insert((*it)->getTableName()); res.insert(_table); @@ -146,7 +162,7 @@ void StorageMerge::getSelectedTables(StorageVector & selected_tables) { const Tables & tables = context.getDatabases().at(source_database); for (Tables::const_iterator it = tables.begin(); it != tables.end(); ++it) - if (it->second != this && table_name_regexp.match(it->first)) + if (it->second.get() != this && table_name_regexp.match(it->first)) selected_tables.push_back(it->second); } diff --git a/dbms/src/Storages/StoragePtr.cpp b/dbms/src/Storages/StoragePtr.cpp deleted file mode 100644 index 5986e754d9c..00000000000 --- a/dbms/src/Storages/StoragePtr.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include - - -namespace DB -{ - -StoragePtr::Wrapper::Wrapper() {} - -StoragePtr::Wrapper::Wrapper(IStorage * s) : storage(s) {} - -StoragePtr::Wrapper::~Wrapper() -{ - try - { - if (std::uncaught_exception()) - LOG_ERROR(&Logger::get("StoragePtr"), "Maybe ignored drop table query because of uncaught exception."); - else - { - if (storage && storage->drop_on_destroy) - { - storage->dropImpl(); - - if (Poco::File(storage->path_to_remove_on_drop).exists()) - Poco::File(storage->path_to_remove_on_drop).remove(true); - } - } - } - catch(...) - { - } -} - -} From 059e0f7e11e4159de026669e6350855709da8dff Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 20 Mar 2014 15:01:56 +0400 Subject: [PATCH 024/281] Fixed build. [#METR-10202] --- dbms/include/DB/Storages/IStorage.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 9d6f1ec7fd8..8674fec2e84 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -165,7 +165,7 @@ public: /** Удалить данные таблицы. Вызывается перед удалением директории с данными. * Если не требуется никаких действий, кроме удаления директории с данными, этот метод можно оставить пустым. */ - virtual void drop(); + virtual void drop() {} /** Переименовать таблицу. * Переименование имени в файле с метаданными, имени в списке таблиц в оперативке, осуществляется отдельно. From c5da5fd5f09384005a49251b8ff47fbdc64636c7 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 20 Mar 2014 17:00:42 +0400 Subject: [PATCH 025/281] Merge --- dbms/include/DB/Storages/IStorage.h | 29 ++- .../DB/Storages/MergeTree/MergeTreeData.h | 7 +- dbms/include/DB/Storages/StorageMergeTree.h | 5 +- .../DB/TableFunctions/TableFunctionMerge.h | 27 ++- .../Interpreters/InterpreterAlterQuery.cpp | 30 ++- .../src/Interpreters/InterpreterDropQuery.cpp | 4 +- .../Interpreters/InterpreterRenameQuery.cpp | 2 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 213 ++++++++++-------- dbms/src/Storages/StorageChunkMerger.cpp | 45 +++- dbms/src/Storages/StorageMerge.cpp | 1 - dbms/src/Storages/StorageMergeTree.cpp | 10 + 11 files changed, 230 insertions(+), 143 deletions(-) diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 8674fec2e84..90c068aa008 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -96,15 +96,13 @@ public: typedef Poco::SharedPtr TableStructureWriteLockPtr; typedef Poco::SharedPtr TableDataWriteLockPtr; + typedef std::pair TableFullWriteLockPtr; /** Не дает читать структуру таблицы. Берется для ALTER, RENAME и DROP. */ - TableStructureWriteLockPtr lockStructureForAlter() + TableFullWriteLockPtr lockForAlter() { - TableStructureWriteLockPtr res = new Poco::ScopedWriteRWLock(structure_lock); - if (is_dropped) - throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); - return res; + return std::make_pair(lockDataForAlter(), lockStructureForAlter()); } /** Не дает изменять данные в таблице. (Более того, не дает посмотреть на структуру таблицы с намерением изменить данные). @@ -119,6 +117,14 @@ public: return res; } + TableStructureWriteLockPtr lockStructureForAlter() + { + TableStructureWriteLockPtr res = new Poco::ScopedWriteRWLock(structure_lock); + if (is_dropped) + throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); + return res; + } + /** Читать набор столбцов из таблицы. * Принимает список столбцов, которых нужно прочитать, а также описание запроса, @@ -180,28 +186,26 @@ public: /** ALTER таблицы в виде изменения столбцов, не затрагивающий изменение Storage или его параметров. * (ALTER, затрагивающий изменение движка, делается внешним кодом, путём копирования данных.) * Вызывается при заблокированной на запись структуре таблицы. - * Для ALTER MODIFY используются другие методы (см. ниже). TODO: Пока эта строчка не верна, и для ALTER MODIFY используется метод alter. + * Для ALTER MODIFY можно использовать другие методы (см. ниже). */ virtual void alter(const ASTAlterQuery::Parameters & params) { throw Exception("Method alter is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); } - /** ALTER ... MODIFY (изменение типа столбца) выполняется в два вызова: + /** ALTER MODIFY (изменение типа столбца) выполняется в два вызова: * Сначала вызывается prepareAlterModify при заблокированной записи данных, но незаблокированной структуре таблицы. * В нем можно выполнить долгую работу по записи сконвертированных данных, оставляя доступными существующие данные. * Потом вызывается commitAlterModify при заблокированной структуре таблицы. * В нем нужно закончить изменение типа столбца. + * Для движков с тривиальным ALTER MODIFY можно оставить реализацию по умолчанию, вызывающую alter. */ - virtual void prepareAlterModify(const ASTAlterQuery::Parameters & params) - { - throw Exception("Method prepareAlterModify is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); - } + virtual void prepareAlterModify(const ASTAlterQuery::Parameters & params) {} virtual void commitAlterModify(const ASTAlterQuery::Parameters & params) { - throw Exception("Method commitAlterModify is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); + alter(params); } /** Выполнить какую-либо фоновую работу. Например, объединение кусков в таблице типа MergeTree. @@ -275,5 +279,6 @@ private: typedef std::shared_ptr StoragePtr; typedef std::vector StorageVector; +typedef IStorage::TableStructureReadLocks TableLocks; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 5d03c3f80eb..593eef78765 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -291,12 +291,9 @@ public: */ void setPath(const String & full_path); - /** Метод ALTER позволяет добавлять и удалять столбцы и менять их тип. - * Нужно вызывать под залоченным lockStructure(). - * TODO: сделать, чтобы ALTER MODIFY не лочил чтения надолго. - */ void alter(const ASTAlterQuery::Parameters & params); - + void prepareAlterModify(const ASTAlterQuery::Parameters & params); + void commitAlterModify(const ASTAlterQuery::Parameters & params); ExpressionActionsPtr getPrimaryExpression() const { return primary_expr; } SortDescription getSortDescription() const { return sort_descr; } diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 4a70a07d1ad..f1d38fc10d7 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -72,10 +72,9 @@ public: void rename(const String & new_path_to_db, const String & new_name); - /// Метод ALTER позволяет добавлять и удалять столбцы. - /// Метод ALTER нужно применять, когда обращения к базе приостановлены. - /// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение void alter(const ASTAlterQuery::Parameters & params); + void prepareAlterModify(const ASTAlterQuery::Parameters & params); + void commitAlterModify(const ASTAlterQuery::Parameters & params); private: String path; diff --git a/dbms/include/DB/TableFunctions/TableFunctionMerge.h b/dbms/include/DB/TableFunctions/TableFunctionMerge.h index c5926e131c4..b56ce106fbc 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionMerge.h +++ b/dbms/include/DB/TableFunctions/TableFunctionMerge.h @@ -55,15 +55,26 @@ private: { OptimizedRegularExpression table_name_regexp(table_name_regexp_); - /// Список таблиц могут менять в другом потоке. - Poco::ScopedLock lock(context.getMutex()); - context.assertDatabaseExists(source_database); - const Tables & tables = context.getDatabases().at(source_database); - for (Tables::const_iterator it = tables.begin(); it != tables.end(); ++it) - if (table_name_regexp.match(it->first)) - return new NamesAndTypesList((it->second)->getColumnsList()); + StoragePtr any_table; - throw Exception("Error while executing table function merge. In database " + source_database + " no one matches regular expression: " + table_name_regexp_, ErrorCodes::UNKNOWN_TABLE); + { + /// Список таблиц могут менять в другом потоке. + Poco::ScopedLock lock(context.getMutex()); + context.assertDatabaseExists(source_database); + const Tables & tables = context.getDatabases().at(source_database); + for (Tables::const_iterator it = tables.begin(); it != tables.end(); ++it) + if (table_name_regexp.match(it->first)) + { + any_table = it->second; + break; + } + } + + if (!any_table) + throw Exception("Error while executing table function merge. In database " + source_database + " no one matches regular expression: " + table_name_regexp_, ErrorCodes::UNKNOWN_TABLE); + + auto table_lock = any_table->lockStructure(false); + return new NamesAndTypesList(any_table->getColumnsList()); } }; diff --git a/dbms/src/Interpreters/InterpreterAlterQuery.cpp b/dbms/src/Interpreters/InterpreterAlterQuery.cpp index 22a1a96fc75..5de38fc697c 100644 --- a/dbms/src/Interpreters/InterpreterAlterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterAlterQuery.cpp @@ -46,10 +46,7 @@ void InterpreterAlterQuery::execute() String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database; StoragePtr table = context.getTable(database_name, table_name); - auto table_lock = table->lockStructureForAlter(); - - /// Poco::Mutex является рекурсивным, т.е. взятие мьютекса дважды из одного потока не приводит к блокировке - Poco::ScopedLock lock(context.getMutex()); + auto table_soft_lock = table->lockDataForAlter(); const DataTypeFactory & data_type_factory = context.getDataTypeFactory(); String path = context.getPath(); @@ -64,7 +61,6 @@ void InterpreterAlterQuery::execute() attach.attach = true; ASTs & columns = dynamic_cast(*attach.columns).children; - /// Различные проверки, на возможность выполнения запроса ASTs columns_copy = columns; IdentifierNameSet identifier_names; @@ -133,13 +129,35 @@ void InterpreterAlterQuery::execute() } } + /// Пока разрешим читать из таблицы. Запретим при первой попытке изменить структуру таблицы. + /// Это позволит сделать большую часть первого MODIFY, не останавливая чтение из таблицы. + IStorage::TableStructureWriteLockPtr table_hard_lock; + + Poco::ScopedLock lock(context.getMutex()); + /// todo cycle over sub tables and tables /// Применяем изменения for (ASTAlterQuery::ParameterContainer::const_iterator alter_it = alter.parameters.begin(); alter_it != alter.parameters.end(); ++alter_it) { const ASTAlterQuery::Parameters & params = *alter_it; - table->alter(params); + + if (params.type == ASTAlterQuery::MODIFY) + { + table->prepareAlterModify(params); + + if (!table_hard_lock) + table_hard_lock = table->lockStructureForAlter(); + + table->commitAlterModify(params); + } + else + { + if (!table_hard_lock) + table_hard_lock = table->lockStructureForAlter(); + + table->alter(params); + } if (params.type == ASTAlterQuery::ADD) { diff --git a/dbms/src/Interpreters/InterpreterDropQuery.cpp b/dbms/src/Interpreters/InterpreterDropQuery.cpp index 25c45e735b5..53c5dd18999 100644 --- a/dbms/src/Interpreters/InterpreterDropQuery.cpp +++ b/dbms/src/Interpreters/InterpreterDropQuery.cpp @@ -67,7 +67,7 @@ void InterpreterDropQuery::execute() table->shutdown(); /// Если кто-то успел удалить эту таблицу, выбросит исключение. - auto table_lock = table->lockStructureForAlter(); + auto table_lock = table->lockForAlter(); String current_table_name = table->getTableName(); @@ -117,7 +117,7 @@ void InterpreterDropQuery::dropDetachedTable(String database_name, StoragePtr ta { table->shutdown(); - auto table_lock = table->lockStructureForAlter(); + auto table_lock = table->lockForAlter(); String table_name = table->getTableName(); diff --git a/dbms/src/Interpreters/InterpreterRenameQuery.cpp b/dbms/src/Interpreters/InterpreterRenameQuery.cpp index 37ea5d47eb2..6c55ec2c99a 100644 --- a/dbms/src/Interpreters/InterpreterRenameQuery.cpp +++ b/dbms/src/Interpreters/InterpreterRenameQuery.cpp @@ -52,7 +52,7 @@ void InterpreterRenameQuery::execute() /// Заблокировать таблицу нужно при незаблокированном контексте. StoragePtr table = context.getTable(from_database_name, from_table_name); - auto table_lock = table->lockStructureForAlter(); + auto table_lock = table->lockForAlter(); /** Все таблицы переименовываются под глобальной блокировкой. */ Poco::ScopedLock lock(context.getMutex()); diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 0d6af769b3f..580504feb0e 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -399,101 +399,6 @@ static DataTypePtr getDataTypeByName(const String & name, const NamesAndTypesLis void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { - if (params.type == ASTAlterQuery::MODIFY) - { - { - DataPartsVector parts; - { - Poco::ScopedLock lock(data_parts_mutex); - parts = DataPartsVector(data_parts.begin(), data_parts.end()); - } - - Names column_name; - const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); - StringRange type_range = name_type.type->range; - String type(type_range.first, type_range.second - type_range.first); - DataTypePtr old_type_ptr = DB::getDataTypeByName(name_type.name, *columns); - DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); - if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || - dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) - throw Exception("ALTER MODIFY not supported for nested and array types"); - - column_name.push_back(name_type.name); - ExpressionActionsPtr expr; - String out_column; - createConvertExpression(name_type.name, type, expr, out_column); - - ColumnNumbers num(1, 0); - for (DataPartPtr & part : parts) - { - MarkRanges ranges(1, MarkRange(0, part->size)); - ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', - DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, false, NULL, ""), expr); - MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); - out.writePrefix(); - - try - { - while(DB::Block b = in.read()) - { - /// оставляем только столбец с результатом - b.erase(0); - out.write(b); - } - LOG_TRACE(log, "Write Suffix"); - out.writeSuffix(); - } - catch (const Exception & e) - { - if (e.code() != ErrorCodes::ALL_REQUESTED_COLUMNS_ARE_MISSING) - throw; - } - } - - /// переименовываем файлы - /// переименовываем старые столбцы, добавляя расширение .old - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin").exists()) - { - LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << path + ".bin" + ".old"); - Poco::File(path + ".bin").renameTo(path + ".bin" + ".old"); - LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << path + ".mrk" + ".old"); - Poco::File(path + ".mrk").renameTo(path + ".mrk" + ".old"); - } - } - - /// переименовываем временные столбцы - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(out_column); - std::string new_path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin").exists()) - { - LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << new_path + ".bin"); - Poco::File(path + ".bin").renameTo(new_path + ".bin"); - LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << new_path + ".mrk"); - Poco::File(path + ".mrk").renameTo(new_path + ".mrk"); - } - } - - // удаляем старые столбцы - for (DataPartPtr & part : parts) - { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); - if (Poco::File(path + ".bin" + ".old").exists()) - { - LOG_TRACE(log, "Removing old column " << path + ".bin" + ".old"); - Poco::File(path + ".bin" + ".old").remove(); - LOG_TRACE(log, "Removing old column " << path + ".mrk" + ".old"); - Poco::File(path + ".mrk" + ".old").remove(); - } - } - } - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); - } { Poco::ScopedLock lock(data_parts_mutex); Poco::ScopedLock lock_all(all_data_parts_mutex); @@ -509,6 +414,124 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) } } +void MergeTreeData::prepareAlterModify(const ASTAlterQuery::Parameters & params) +{ + DataPartsVector parts; + { + Poco::ScopedLock lock(data_parts_mutex); + parts = DataPartsVector(data_parts.begin(), data_parts.end()); + } + + Names column_name; + const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); + StringRange type_range = name_type.type->range; + String type(type_range.first, type_range.second - type_range.first); + DataTypePtr old_type_ptr = DB::getDataTypeByName(name_type.name, *columns); + DataTypePtr new_type_ptr = context.getDataTypeFactory().get(type); + if (dynamic_cast(old_type_ptr.get()) || dynamic_cast(old_type_ptr.get()) || + dynamic_cast(new_type_ptr.get()) || dynamic_cast(new_type_ptr.get())) + throw Exception("ALTER MODIFY not supported for nested and array types"); + + column_name.push_back(name_type.name); + ExpressionActionsPtr expr; + String out_column; + createConvertExpression(name_type.name, type, expr, out_column); + + ColumnNumbers num(1, 0); + for (DataPartPtr & part : parts) + { + MarkRanges ranges(1, MarkRange(0, part->size)); + ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', + DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, false, NULL, ""), expr); + MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); + out.writePrefix(); + + try + { + while(DB::Block b = in.read()) + { + /// оставляем только столбец с результатом + b.erase(0); + out.write(b); + } + LOG_TRACE(log, "Write Suffix"); + out.writeSuffix(); + } + catch (const Exception & e) + { + if (e.code() != ErrorCodes::ALL_REQUESTED_COLUMNS_ARE_MISSING) + throw; + } + } +} + +void MergeTreeData::commitAlterModify(const ASTAlterQuery::Parameters & params) +{ + DataPartsVector parts; + { + Poco::ScopedLock lock(data_parts_mutex); + parts = DataPartsVector(data_parts.begin(), data_parts.end()); + } + + const ASTNameTypePair & name_type = dynamic_cast(*params.name_type); + StringRange type_range = name_type.type->range; + String type(type_range.first, type_range.second - type_range.first); + + ExpressionActionsPtr expr; + String out_column; + createConvertExpression(name_type.name, type, expr, out_column); + + /// переименовываем файлы + /// переименовываем старые столбцы, добавляя расширение .old + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin").exists()) + { + LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << path + ".bin" + ".old"); + Poco::File(path + ".bin").renameTo(path + ".bin" + ".old"); + LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << path + ".mrk" + ".old"); + Poco::File(path + ".mrk").renameTo(path + ".mrk" + ".old"); + } + } + + /// переименовываем временные столбцы + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(out_column); + std::string new_path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin").exists()) + { + LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << new_path + ".bin"); + Poco::File(path + ".bin").renameTo(new_path + ".bin"); + LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << new_path + ".mrk"); + Poco::File(path + ".mrk").renameTo(new_path + ".mrk"); + } + } + + // удаляем старые столбцы + for (DataPartPtr & part : parts) + { + std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + if (Poco::File(path + ".bin" + ".old").exists()) + { + LOG_TRACE(log, "Removing old column " << path + ".bin" + ".old"); + Poco::File(path + ".bin" + ".old").remove(); + LOG_TRACE(log, "Removing old column " << path + ".mrk" + ".old"); + Poco::File(path + ".mrk" + ".old").remove(); + } + } + + context.getUncompressedCache()->reset(); + context.getMarkCache()->reset(); + + { + Poco::ScopedLock lock(data_parts_mutex); + Poco::ScopedLock lock_all(all_data_parts_mutex); + alterColumns(params, columns, context); + } +} + bool MergeTreeData::isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const { diff --git a/dbms/src/Storages/StorageChunkMerger.cpp b/dbms/src/Storages/StorageChunkMerger.cpp index bc0dafc4797..dd38c981770 100644 --- a/dbms/src/Storages/StorageChunkMerger.cpp +++ b/dbms/src/Storages/StorageChunkMerger.cpp @@ -86,7 +86,7 @@ BlockInputStreams StorageChunkMerger::read( { if (chunk_ref->source_database_name != source_database) { - LOG_WARNING(log, "ChunkRef " + chunk_ref->getTableName() + " points to another database, ignoring"); + LOG_WARNING(log, "ChunkRef " + it->first + " points to another database, ignoring"); continue; } if (!chunks_table_names.count(chunk_ref->source_table_name)) @@ -98,7 +98,7 @@ BlockInputStreams StorageChunkMerger::read( } else { - LOG_WARNING(log, "ChunkRef " + chunk_ref->getTableName() + " points to non-existing Chunks table, ignoring"); + LOG_WARNING(log, "ChunkRef " + it->first + " points to non-existing Chunks table, ignoring"); } } } @@ -110,6 +110,14 @@ BlockInputStreams StorageChunkMerger::read( } } + TableLocks table_locks; + + /// Нельзя, чтобы эти таблицы кто-нибудь удалил, пока мы их читаем. + for (auto table : selected_tables) + { + table_locks.push_back(table->lockStructure(false)); + } + BlockInputStreams res; /// Среди всех стадий, до которых обрабатывается запрос в таблицах-источниках, выберем минимальную. @@ -134,30 +142,33 @@ BlockInputStreams StorageChunkMerger::read( std::set values = VirtualColumnUtils::extractSingleValueFromBlocks(virtual_columns, _table_column_name); bool all_inclusive = (values.size() == virtual_columns_block.rows()); - for (Storages::iterator it = selected_tables.begin(); it != selected_tables.end(); ++it) + for (size_t i = 0; i < selected_tables.size(); ++i) { - if ((*it)->getName() != "Chunks" && !all_inclusive && values.find((*it)->getTableName()) == values.end()) + StoragePtr table = selected_tables[i]; + auto table_lock = table_locks[i]; + + if (table->getName() != "Chunks" && !all_inclusive && values.find(table->getTableName()) == values.end()) continue; /// Список виртуальных столбцов, которые мы заполним сейчас и список столбцов, которые передадим дальше Names virt_column_names, real_column_names; for (const auto & column : column_names) - if (column == _table_column_name && (*it)->getName() != "Chunks") /// таблица Chunks сама заполняет столбец _table + if (column == _table_column_name && table->getName() != "Chunks") /// таблица Chunks сама заполняет столбец _table virt_column_names.push_back(column); else real_column_names.push_back(column); /// Если в запросе только виртуальные столбцы, надо запросить хотя бы один любой другой. if (real_column_names.size() == 0) - real_column_names.push_back(ExpressionActions::getSmallestColumn((*it)->getColumnsList())); + real_column_names.push_back(ExpressionActions::getSmallestColumn(table->getColumnsList())); ASTPtr modified_query_ast = query->clone(); /// Подменяем виртуальный столбец на его значение if (!virt_column_names.empty()) - VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _table_column_name, (*it)->getTableName()); + VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _table_column_name, table->getTableName()); - BlockInputStreams source_streams = (*it)->read( + BlockInputStreams source_streams = table->read( real_column_names, modified_query_ast, settings, @@ -165,6 +176,11 @@ BlockInputStreams StorageChunkMerger::read( max_block_size, selected_tables.size() > threads ? 1 : (threads / selected_tables.size())); + for (auto & stream : source_streams) + { + stream->addTableLock(table_lock); + } + /// Добавляем в ответ вирутальные столбцы for (const auto & virtual_column : virt_column_names) { @@ -172,7 +188,7 @@ BlockInputStreams StorageChunkMerger::read( { for (auto & stream : source_streams) { - stream = new AddingConstColumnBlockInputStream(stream, new DataTypeString, (*it)->getTableName(), _table_column_name); + stream = new AddingConstColumnBlockInputStream(stream, new DataTypeString, table->getTableName(), _table_column_name); } } } @@ -336,6 +352,14 @@ static ASTPtr newIdentifier(const std::string & name, ASTIdentifier::Kind kind) bool StorageChunkMerger::mergeChunks(const Storages & chunks) { typedef std::map ColumnsMap; + + TableLocks table_locks; + + /// Нельзя, чтобы эти таблицы кто-нибудь удалил, пока мы их читаем. + for (auto table : chunks) + { + table_locks.push_back(table->lockStructure(false)); + } /// Объединим множества столбцов сливаемых чанков. ColumnsMap known_columns_types(columns->begin(), columns->end()); @@ -436,7 +460,7 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) DEFAULT_MERGE_BLOCK_SIZE); BlockInputStreamPtr input = new AddingDefaultBlockInputStream(new ConcatBlockInputStream(input_streams), required_columns); - + input->readPrefix(); output->writePrefix(); @@ -495,6 +519,7 @@ bool StorageChunkMerger::mergeChunks(const Storages & chunks) } /// Теперь удалим данные отцепленных таблиц. + table_locks.clear(); for (StoragePtr table : tables_to_drop) { InterpreterDropQuery::dropDetachedTable(source_database, table, context); diff --git a/dbms/src/Storages/StorageMerge.cpp b/dbms/src/Storages/StorageMerge.cpp index 13b36c809ff..82d618f6491 100644 --- a/dbms/src/Storages/StorageMerge.cpp +++ b/dbms/src/Storages/StorageMerge.cpp @@ -72,7 +72,6 @@ BlockInputStreams StorageMerge::read( getSelectedTables(selected_tables); } - typedef std::vector TableLocks; TableLocks table_locks; /// Нельзя, чтобы эти таблицы кто-нибудь удалил, пока мы их читаем. diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index ac603df1a0b..80daf41a528 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -101,6 +101,16 @@ void StorageMergeTree::alter(const ASTAlterQuery::Parameters & params) data.alter(params); } +void StorageMergeTree::prepareAlterModify(const ASTAlterQuery::Parameters & params) +{ + data.prepareAlterModify(params); +} + +void StorageMergeTree::commitAlterModify(const ASTAlterQuery::Parameters & params) +{ + data.commitAlterModify(params); +} + void StorageMergeTree::merge(size_t iterations, bool async, bool aggressive) { bool while_can = false; From 6df8500852f7ec8808db20557c7684c08c9b6667 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 20 Mar 2014 17:28:49 +0400 Subject: [PATCH 026/281] Fixed dropping tables. [#METR-10202] --- dbms/include/DB/Storages/StorageChunkRef.h | 2 +- dbms/include/DB/Storages/StorageDistributed.h | 2 +- dbms/include/DB/Storages/StorageMaterializedView.h | 2 +- dbms/include/DB/Storages/StorageMemory.h | 2 +- dbms/include/DB/Storages/StorageMerge.h | 2 +- dbms/include/DB/Storages/StorageMergeTree.h | 2 +- dbms/include/DB/Storages/StorageTinyLog.h | 2 +- dbms/include/DB/Storages/StorageView.h | 2 +- dbms/src/Storages/StorageChunkRef.cpp | 2 +- dbms/src/Storages/StorageMaterializedView.cpp | 3 ++- dbms/src/Storages/StorageMemory.cpp | 2 +- dbms/src/Storages/StorageMergeTree.cpp | 2 +- dbms/src/Storages/StorageTinyLog.cpp | 2 +- dbms/src/Storages/StorageView.cpp | 2 +- 14 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dbms/include/DB/Storages/StorageChunkRef.h b/dbms/include/DB/Storages/StorageChunkRef.h index 28a2cf6fdf2..d6e63744828 100644 --- a/dbms/include/DB/Storages/StorageChunkRef.h +++ b/dbms/include/DB/Storages/StorageChunkRef.h @@ -32,7 +32,7 @@ public: ASTPtr getCustomCreateQuery(const Context & context) const; - void dropImpl(); + void drop() override; String source_database_name; String source_table_name; diff --git a/dbms/include/DB/Storages/StorageDistributed.h b/dbms/include/DB/Storages/StorageDistributed.h index 2ffa9ce2791..a856c8c0412 100644 --- a/dbms/include/DB/Storages/StorageDistributed.h +++ b/dbms/include/DB/Storages/StorageDistributed.h @@ -58,7 +58,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1); - void dropImpl() {} + void drop() override {} void rename(const String & new_path_to_db, const String & new_name) { name = new_name; } /// в подтаблицах добавлять и удалять столбы нужно вручную /// структура подтаблиц не проверяется diff --git a/dbms/include/DB/Storages/StorageMaterializedView.h b/dbms/include/DB/Storages/StorageMaterializedView.h index c29269376b4..bbe2807270f 100644 --- a/dbms/include/DB/Storages/StorageMaterializedView.h +++ b/dbms/include/DB/Storages/StorageMaterializedView.h @@ -16,7 +16,7 @@ public: std::string getInnerTableName() const { return ".inner." + table_name; } BlockOutputStreamPtr write(ASTPtr query); - void dropImpl(); + void drop() override; bool optimize(); BlockInputStreams read( diff --git a/dbms/include/DB/Storages/StorageMemory.h b/dbms/include/DB/Storages/StorageMemory.h index 67c543b57ba..e6ab8d97b4b 100644 --- a/dbms/include/DB/Storages/StorageMemory.h +++ b/dbms/include/DB/Storages/StorageMemory.h @@ -80,7 +80,7 @@ public: BlockOutputStreamPtr write( ASTPtr query); - void dropImpl(); + void drop() override; void rename(const String & new_path_to_db, const String & new_name) { name = new_name; } private: diff --git a/dbms/include/DB/Storages/StorageMerge.h b/dbms/include/DB/Storages/StorageMerge.h index 54163a682ae..f0a474f1e6a 100644 --- a/dbms/include/DB/Storages/StorageMerge.h +++ b/dbms/include/DB/Storages/StorageMerge.h @@ -41,7 +41,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1); - void dropImpl() {} + void drop() override {} void rename(const String & new_path_to_db, const String & new_name) { name = new_name; } void getSelectedTables(StorageVector & selected_tables); diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index f1d38fc10d7..bb8c5b8cf45 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -68,7 +68,7 @@ public: return true; } - void dropImpl(); + void drop() override; void rename(const String & new_path_to_db, const String & new_name); diff --git a/dbms/include/DB/Storages/StorageTinyLog.h b/dbms/include/DB/Storages/StorageTinyLog.h index 2b43fbd24d6..682efb70215 100644 --- a/dbms/include/DB/Storages/StorageTinyLog.h +++ b/dbms/include/DB/Storages/StorageTinyLog.h @@ -125,7 +125,7 @@ public: BlockOutputStreamPtr write( ASTPtr query); - void dropImpl(); + void drop() override; void rename(const String & new_path_to_db, const String & new_name); diff --git a/dbms/include/DB/Storages/StorageView.h b/dbms/include/DB/Storages/StorageView.h index af42c3a78e4..547b38049f4 100644 --- a/dbms/include/DB/Storages/StorageView.h +++ b/dbms/include/DB/Storages/StorageView.h @@ -26,7 +26,7 @@ public: size_t max_block_size = DEFAULT_BLOCK_SIZE, unsigned threads = 1); - virtual void dropImpl(); + virtual void drop() override; protected: String select_database_name; diff --git a/dbms/src/Storages/StorageChunkRef.cpp b/dbms/src/Storages/StorageChunkRef.cpp index 1f42a1ddb3f..4c0ada7e39f 100644 --- a/dbms/src/Storages/StorageChunkRef.cpp +++ b/dbms/src/Storages/StorageChunkRef.cpp @@ -43,7 +43,7 @@ ASTPtr StorageChunkRef::getCustomCreateQuery(const Context & context) const return res; } -void StorageChunkRef::dropImpl() +void StorageChunkRef::drop() { try { diff --git a/dbms/src/Storages/StorageMaterializedView.cpp b/dbms/src/Storages/StorageMaterializedView.cpp index 560df682415..5d704018b16 100644 --- a/dbms/src/Storages/StorageMaterializedView.cpp +++ b/dbms/src/Storages/StorageMaterializedView.cpp @@ -74,7 +74,8 @@ BlockOutputStreamPtr StorageMaterializedView::write(ASTPtr query) return data->write(query); } -void StorageMaterializedView::dropImpl() { +void StorageMaterializedView::drop() +{ context.getGlobalContext().removeDependency(DatabaseAndTableName(select_database_name, select_table_name), DatabaseAndTableName(database_name, table_name)); /// Состваляем и выполняем запрос drop для внутреннего хранилища. diff --git a/dbms/src/Storages/StorageMemory.cpp b/dbms/src/Storages/StorageMemory.cpp index 0aa3cb5d917..e790106ddc7 100644 --- a/dbms/src/Storages/StorageMemory.cpp +++ b/dbms/src/Storages/StorageMemory.cpp @@ -106,7 +106,7 @@ BlockOutputStreamPtr StorageMemory::write( } -void StorageMemory::dropImpl() +void StorageMemory::drop() { Poco::ScopedLock lock(mutex); data.clear(); diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 80daf41a528..5e2b2dfd549 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -76,7 +76,7 @@ BlockOutputStreamPtr StorageMergeTree::write(ASTPtr query) return new MergeTreeBlockOutputStream(*this); } -void StorageMergeTree::dropImpl() +void StorageMergeTree::drop() { merger.cancelAll(); joinMergeThreads(); diff --git a/dbms/src/Storages/StorageTinyLog.cpp b/dbms/src/Storages/StorageTinyLog.cpp index 63e527e700f..ab179fb5492 100644 --- a/dbms/src/Storages/StorageTinyLog.cpp +++ b/dbms/src/Storages/StorageTinyLog.cpp @@ -387,7 +387,7 @@ BlockOutputStreamPtr StorageTinyLog::write( } -void StorageTinyLog::dropImpl() +void StorageTinyLog::drop() { for (Files_t::iterator it = files.begin(); it != files.end(); ++it) if (it->second.data_file.exists()) diff --git a/dbms/src/Storages/StorageView.cpp b/dbms/src/Storages/StorageView.cpp index a2edea772e5..ab494a1b382 100644 --- a/dbms/src/Storages/StorageView.cpp +++ b/dbms/src/Storages/StorageView.cpp @@ -65,7 +65,7 @@ BlockInputStreams StorageView::read( } -void StorageView::dropImpl() { +void StorageView::drop() { context.getGlobalContext().removeDependency(DatabaseAndTableName(select_database_name, select_table_name), DatabaseAndTableName(database_name, table_name)); } From 280dd19e637bfc6bc7dd60868e5501fde03dba16 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 20 Mar 2014 21:05:22 +0400 Subject: [PATCH 027/281] Fixed SELECT without table. [#METR-10202] --- dbms/src/Interpreters/InterpreterSelectQuery.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index ebc74a1117f..518a42c2f39 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -436,7 +436,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu /// Список столбцов, которых нужно прочитать, чтобы выполнить запрос. Names required_columns = query_analyzer->getRequiredColumns(); - if (dynamic_cast(&*query.table)) + if (query.table && dynamic_cast(&*query.table)) { /** Для подзапроса не действуют ограничения на максимальный размер результата. * Так как результат поздапроса - ещё не результат всего запроса. @@ -509,7 +509,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns; /// Инициализируем изначальные потоки данных, на которые накладываются преобразования запроса. Таблица или подзапрос? - if (!query.table || !dynamic_cast(&*query.table)) + if (!interpreter_subquery) { streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); for (auto stream : streams) From 7dc31a3ba339e7773262ad55f359a7bf3e9333ad Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 19 Mar 2014 20:00:56 +0400 Subject: [PATCH 028/281] olap compability: removed ClickPhraseID, ClickTargetPhraseID [#METR-10322] --- dbms/src/Server/OLAPAttributesMetadata.h | 4 ---- dbms/src/Server/OLAPQueryConverter.cpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/dbms/src/Server/OLAPAttributesMetadata.h b/dbms/src/Server/OLAPAttributesMetadata.h index 586ff7e7aca..fa5d6827812 100644 --- a/dbms/src/Server/OLAPAttributesMetadata.h +++ b/dbms/src/Server/OLAPAttributesMetadata.h @@ -523,7 +523,6 @@ struct UserAgentID : public IAttributeMetadata typedef AttributeIntBase ClickGoodEvent; typedef AttributeIntBase ClickPriorityID; typedef AttributeIntBase ClickBannerID; -typedef AttributeIntBase ClickPhraseID; typedef AttributeIntBase ClickPageID; typedef AttributeIntBase ClickPlaceID; typedef AttributeIntBase ClickTypeID; @@ -532,7 +531,6 @@ typedef AttributeUIntBase ClickDomainID; typedef AttributeUIntBase ClickCost; typedef AttributeHashBase ClickURLHash; typedef AttributeUIntBase ClickOrderID; -typedef AttributeUIntBase ClickTargetPhraseID; typedef AttributeUIntBase GoalReachesAny; typedef AttributeUIntBase GoalReachesDepth; typedef AttributeUIntBase GoalReachesURL; @@ -728,7 +726,6 @@ inline AttributeMetadatas GetOLAPAttributeMetadata() ("ClickGoodEvent", new ClickGoodEvent) ("ClickPriorityID", new ClickPriorityID) ("ClickBannerID", new ClickBannerID) - ("ClickPhraseID", new ClickPhraseID) ("ClickPageID", new ClickPageID) ("ClickPlaceID", new ClickPlaceID) ("ClickTypeID", new ClickTypeID) @@ -737,7 +734,6 @@ inline AttributeMetadatas GetOLAPAttributeMetadata() ("ClickCost", new ClickCost) ("ClickURLHash", new ClickURLHash) ("ClickOrderID", new ClickOrderID) - ("ClickTargetPhraseID", new ClickTargetPhraseID) ("GoalReaches", new GoalReaches) ("GoalReachesAny", new GoalReachesAny) ("GoalReachesDepth", new GoalReachesDepth) diff --git a/dbms/src/Server/OLAPQueryConverter.cpp b/dbms/src/Server/OLAPQueryConverter.cpp index 0309ac1e1d0..9fb4b3b3d4b 100644 --- a/dbms/src/Server/OLAPQueryConverter.cpp +++ b/dbms/src/Server/OLAPQueryConverter.cpp @@ -550,7 +550,6 @@ void QueryConverter::fillNumericAttributeMap() M("ClickGoodEvent", "Clicks.GoodEvent[1]") M("ClickPriorityID", "Clicks.PriorityID[1]") M("ClickBannerID", "Clicks.BannerID[1]") - M("ClickPhraseID", "Clicks.PhraseID[1]") M("ClickPageID", "Clicks.PageID[1]") M("ClickPlaceID", "Clicks.PlaceID[1]") M("ClickTypeID", "Clicks.TypeID[1]") @@ -559,7 +558,6 @@ void QueryConverter::fillNumericAttributeMap() M("ClickCost", "Clicks.Cost[1]") M("ClickURLHash", "Clicks.URLHash[1]") M("ClickOrderID", "Clicks.OrderID[1]") - M("ClickTargetPhraseID", "Clicks.TargetPhraseID[1]") M("GoalReachesAny", "GoalReachesAny") M("GoalReachesDepth", "GoalReachesDepth") M("GoalReachesURL", "GoalReachesURL") From 3a6d19d6391ee174690cc889700378e3ef8a19a5 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 13:26:01 +0300 Subject: [PATCH 029/281] Removed unnecessary context lock during ALTER. [METR-10202] --- dbms/src/Interpreters/InterpreterAlterQuery.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterAlterQuery.cpp b/dbms/src/Interpreters/InterpreterAlterQuery.cpp index 5de38fc697c..ae4808d338b 100644 --- a/dbms/src/Interpreters/InterpreterAlterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterAlterQuery.cpp @@ -133,8 +133,6 @@ void InterpreterAlterQuery::execute() /// Это позволит сделать большую часть первого MODIFY, не останавливая чтение из таблицы. IStorage::TableStructureWriteLockPtr table_hard_lock; - Poco::ScopedLock lock(context.getMutex()); - /// todo cycle over sub tables and tables /// Применяем изменения for (ASTAlterQuery::ParameterContainer::const_iterator alter_it = alter.parameters.begin(); From 750cb44f94426def52853ab4b2474d75a562dea9 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 17:42:14 +0400 Subject: [PATCH 030/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 2 + dbms/include/DB/Interpreters/Context.h | 4 + .../DB/Interpreters/InterserverIOHandler.h | 62 ++++++++ .../DB/Storages/StorageReplicatedMergeTree.h | 150 ++++++++++++++++++ dbms/src/Server/InterserverIOHTTPHandler.cpp | 98 ++++++++++++ dbms/src/Server/InterserverIOHTTPHandler.h | 28 ++++ dbms/src/Server/OLAPHTTPHandler.h | 41 +++-- dbms/src/Server/Server.cpp | 28 +++- .../Storages/StorageReplicatedMergeTree.cpp | 47 ++++++ 9 files changed, 432 insertions(+), 28 deletions(-) create mode 100644 dbms/include/DB/Interpreters/InterserverIOHandler.h create mode 100644 dbms/include/DB/Storages/StorageReplicatedMergeTree.h create mode 100644 dbms/src/Server/InterserverIOHTTPHandler.cpp create mode 100644 dbms/src/Server/InterserverIOHTTPHandler.h create mode 100644 dbms/src/Storages/StorageReplicatedMergeTree.cpp diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index b875e465ba4..0e935c2455f 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -227,6 +227,8 @@ namespace ErrorCodes CLIENT_HAS_CONNECTED_TO_WRONG_PORT, TABLE_IS_DROPPED, DATABASE_NOT_EMPTY, + DUPLICATE_INTERSERVER_IO_ENDPOINT, + NO_SUCH_INTERSERVER_IO_ENDPOINT, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index cc71891d2d3..a5f87006761 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ struct ContextShared ProcessList process_list; /// Исполняющиеся в данный момент запросы. ViewDependencies view_dependencies; /// Текущие зависимости ConfigurationPtr users_config; /// Конфиг с секциями users, profiles и quotas. + InterserverIOHandler interserver_io_handler; /// Обработчик для межсерверной передачи данных. /// Кластеры для distributed таблиц /// Создаются при создании Distributed таблиц, так как нужно дождаться пока будут выставлены Settings @@ -246,6 +248,8 @@ public: const FormatFactory & getFormatFactory() const { return shared->format_factory; } const Dictionaries & getDictionaries() const; + InterserverIOHandler & getInterserverIOHandler() { return shared->interserver_io_handler; } + /// Получить запрос на CREATE таблицы. ASTPtr getCreateQuery(const String & database_name, const String & table_name) const; diff --git a/dbms/include/DB/Interpreters/InterserverIOHandler.h b/dbms/include/DB/Interpreters/InterserverIOHandler.h new file mode 100644 index 00000000000..bddf00f05f4 --- /dev/null +++ b/dbms/include/DB/Interpreters/InterserverIOHandler.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ + +/** Обработчик запросов от других серверов. + */ +class InterserverIOEndpoint +{ +public: + virtual void processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) = 0; + + virtual ~InterserverIOEndpoint() {} +}; + +typedef Poco::SharedPtr InterserverIOEndpointPtr; + + +/** Сюда можно зарегистрировать сервис, обрататывающий запросы от других серверов. + * Используется для передачи данных в ReplicatedMergeTree. + */ +class InterserverIOHandler +{ +public: + void addEndpoint(const String & name, InterserverIOEndpointPtr endpoint) + { + Poco::ScopedLock lock(mutex); + if (endpoint_map.count(name)) + throw Exception("Duplicate interserver IO endpoint: " + name, ErrorCodes::DUPLICATE_INTERSERVER_IO_ENDPOINT); + endpoint_map[name] = endpoint; + } + + void removeEndpoint(const String & name) + { + Poco::ScopedLock lock(mutex); + if (!endpoint_map.count(name)) + throw Exception("No interserver IO endpoint named " + name, ErrorCodes::NO_SUCH_INTERSERVER_IO_ENDPOINT); + endpoint_map.erase(name); + } + + InterserverIOEndpointPtr getEndpoint(const String & name) + { + Poco::ScopedLock lock(mutex); + if (!endpoint_map.count(name)) + throw Exception("No interserver IO endpoint named " + name, ErrorCodes::NO_SUCH_INTERSERVER_IO_ENDPOINT); + return endpoint_map[name]; + } + +private: + typedef std::map EndpointMap; + + EndpointMap endpoint_map; + Poco::FastMutex mutex; +}; + +} diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h new file mode 100644 index 00000000000..657807b3a05 --- /dev/null +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ + +class StorageReplicatedMergeTree : public IStorage +{ +public: + static StoragePtr create( + const String & zookeeper_path_, + const String & replica_name_, + const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + size_t index_granularity_, + MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, + const String & sign_column_ = "", + const MergeTreeSettings & settings_ = MergeTreeSettings()); + + void shutdown(); + ~StorageReplicatedMergeTree(); + + std::string getName() const + { + return "Replicated" + data.getModePrefix() + "MergeTree"; + } + + std::string getTableName() const { return name; } + std::string getSignColumnName() const { return data.getSignColumnName(); } + bool supportsSampling() const { return data.supportsSampling(); } + bool supportsFinal() const { return data.supportsFinal(); } + bool supportsPrewhere() const { return data.supportsPrewhere(); } + + const NamesAndTypesList & getColumnsList() const { return data.getColumnsList(); } + + BlockInputStreams read( + const Names & column_names, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size = DEFAULT_BLOCK_SIZE, + unsigned threads = 1); + + BlockOutputStreamPtr write(ASTPtr query); + + void drop() override; + +private: + struct LogEntry + { + enum Type + { + GET_PART, + MERGE_PARTS, + }; + + Type type; + String znode_name; + + String new_part_name; + Strings parts_to_merge; + }; + + typedef std::list LogEntries; + + /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из Zookeeper (/replicas/me/queue/). + * В ZK записи в хронологическом порядке. Здесь записи в том порядке, в котором их лучше выполнять. + */ + LogEntries queue; + Poco::FastMutex queue_mutex; + + String path; + String name; + String full_path; + + String zookeeper_path; + String replica_name; + + /** Является ли эта реплика "главной". Главная реплика выбирает куски для слияния. + */ + bool is_master_node; + + MergeTreeData data; + MergeTreeDataSelectExecutor reader; + MergeTreeDataWriter writer; + + Logger * log; + + volatile bool shutdown_called; + + StorageReplicatedMergeTree( + const String & zookeeper_path_, + const String & replica_name_, + const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, + size_t index_granularity_, + MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, + const String & sign_column_ = "", + const MergeTreeSettings & settings_ = MergeTreeSettings()); + + /** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). + * Если нет - бросить исключение. + */ + void checkTableStructure(); + + /** Проверить, что множество кусков соответствует тому, что в ZK (/replicas/me/parts/). + * Если каких-то кусков, описанных в ZK нет локально, бросить исключение. + * Если какие-то локальные куски не упоминаются в ZK, удалить их. + * Но если таких слишком много, на всякий случай бросить исключение - скорее всего, это ошибка конфигурации. + */ + void checkParts(); + + /** Кладет в queue записи из Zookeeper (/replicas/me/queue/). + */ + void loadQueue(); + + /** Копирует новые записи из логов всех реплик в очередь этой реплики. + */ + void pullLogsToQueue(); + + /** Делает преобразования над очередью: + * - Если есть MERGE_PARTS кусков, не все из которых у нас есть, заменяем его на GET_PART и + * убираем GET_PART для всех составляющих его кусков. NOTE: Наверно, это будет плохо работать. Придумать эвристики получше. + */ + void optimizeQueue(); + + /** По порядку пытается выполнить действия из очереди, пока не получится. Что получилось, выбрасывает из очереди. + */ + void executeSomeQueueEntry(); + + /** Попробовать выполнить действие из очереди. Возвращает false, если не получилось по какой-то ожидаемой причине: + * - GET_PART, и ни у кого нет этого куска. Это возможно, если этот кусок уже слили с кем-то и удалили. + * - Не смогли скачать у кого-то кусок, потому что его там уже нет. + * - Не смогли объединить куски, потому что не все из них у нас есть. + */ + bool tryExecute(const LogEntry & entry); +}; + +} diff --git a/dbms/src/Server/InterserverIOHTTPHandler.cpp b/dbms/src/Server/InterserverIOHTTPHandler.cpp new file mode 100644 index 00000000000..4a7f14a9db2 --- /dev/null +++ b/dbms/src/Server/InterserverIOHTTPHandler.cpp @@ -0,0 +1,98 @@ +#include "InterserverIOHTTPHandler.h" +#include +#include + + +namespace DB +{ + + +void InterserverIOHTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) +{ + HTMLForm params(request); + + std::ostringstream request_ostream; + request_ostream << request.stream().rdbuf(); + std::string request_string = request_ostream.str(); + + LOG_TRACE(log, "Request URI: " << request.getURI()); + LOG_TRACE(log, "Request body: " << request_string); + + std::istringstream request_istream(request_string); + + /// NOTE: Тут можно сделать аутентификацию, если понадобится. + + String endpoint_name = params.get("endpoint"); + bool compress = params.get("compress") == "true"; + + WriteBufferFromHTTPServerResponse out(response); + + auto endpoint = server.global_context->getInterserverIOHandler().getEndpoint(endpoint_name); + + if (compress) + { + CompressedWriteBuffer compressed_out(out); + endpoint->processQuery(params, compressed_out); + } + else + { + endpoint->processQuery(params, out); + } + + out.finalize(); +} + + +void InterserverIOHTTPHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) +{ + /// Для того, чтобы работал keep-alive. + if (request.getVersion() == Poco::Net::HTTPServerRequest::HTTP_1_1) + response.setChunkedTransferEncoding(true); + + try + { + processQuery(request, response); + LOG_INFO(log, "Done processing query"); + } + catch (Exception & e) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + std::stringstream s; + s << "Code: " << e.code() + << ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what(); + if (!response.sent()) + response.send() << s.str() << std::endl; + LOG_ERROR(log, s.str()); + } + catch (Poco::Exception & e) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + std::stringstream s; + s << "Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code() + << ", e.displayText() = " << e.displayText() << ", e.what() = " << e.what(); + if (!response.sent()) + response.send() << s.str() << std::endl; + LOG_ERROR(log, s.str()); + } + catch (std::exception & e) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + std::stringstream s; + s << "Code: " << ErrorCodes::STD_EXCEPTION << ". " << e.what(); + if (!response.sent()) + response.send() << s.str() << std::endl; + LOG_ERROR(log, s.str()); + } + catch (...) + { + response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + std::stringstream s; + s << "Code: " << ErrorCodes::UNKNOWN_EXCEPTION << ". Unknown exception."; + if (!response.sent()) + response.send() << s.str() << std::endl; + LOG_ERROR(log, s.str()); + } +} + + +} diff --git a/dbms/src/Server/InterserverIOHTTPHandler.h b/dbms/src/Server/InterserverIOHTTPHandler.h new file mode 100644 index 00000000000..b0ac507ea5e --- /dev/null +++ b/dbms/src/Server/InterserverIOHTTPHandler.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Server.h" + + +namespace DB +{ + +class InterserverIOHTTPHandler : public Poco::Net::HTTPRequestHandler +{ +public: + InterserverIOHTTPHandler(Server & server_) + : server(server_) + , log(&Logger::get("InterserverIOHTTPHandler ")) + { + } + + void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); + +private: + Server & server; + + Logger * log; + + void processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); +}; + +} diff --git a/dbms/src/Server/OLAPHTTPHandler.h b/dbms/src/Server/OLAPHTTPHandler.h index b9b666adf3d..c9d61faa152 100644 --- a/dbms/src/Server/OLAPHTTPHandler.h +++ b/dbms/src/Server/OLAPHTTPHandler.h @@ -5,26 +5,25 @@ namespace DB { - - /// Обработчик http-запросов в формате OLAP-server. - class OLAPHTTPHandler : public Poco::Net::HTTPRequestHandler + +/// Обработчик http-запросов в формате OLAP-server. +class OLAPHTTPHandler : public Poco::Net::HTTPRequestHandler +{ +public: + OLAPHTTPHandler(Server & server_) + : server(server_) + , log(&Logger::get("OLAPHTTPHandler")) { - public: - OLAPHTTPHandler(Server & server_) - : server(server_) - , log(&Logger::get("OLAPHTTPHandler")) - { - LOG_TRACE(log, "In constructor."); - } - - void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); - - private: - Server & server; - - Logger * log; - - void processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); - }; - + } + + void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); + +private: + Server & server; + + Logger * log; + + void processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response); +}; + } diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index 552dfee7a8d..a4364bb4232 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -14,6 +14,7 @@ #include "Server.h" #include "HTTPHandler.h" +#include "InterserverIOHTTPHandler.h" #include "OLAPHTTPHandler.h" #include "TCPHandler.h" @@ -253,7 +254,7 @@ int Server::main(const std::vector & args) Poco::Timespan keep_alive_timeout(config.getInt("keep_alive_timeout", 10), 0); Poco::ThreadPool server_pool(3, config.getInt("max_connections", 1024)); - Poco::Net::HTTPServerParams * http_params = new Poco::Net::HTTPServerParams; + Poco::Net::HTTPServerParams::Ptr http_params = new Poco::Net::HTTPServerParams; http_params->setTimeout(settings.receive_timeout); http_params->setKeepAliveTimeout(keep_alive_timeout); @@ -277,6 +278,21 @@ int Server::main(const std::vector & args) tcp_socket, new Poco::Net::TCPServerParams); + /// Interserver IO HTTP + Poco::SharedPtr interserver_io_http_server; + if (config.has("interserver_http_port")) + { + Poco::Net::ServerSocket interserver_io_http_socket(Poco::Net::SocketAddress("[::]:" + + config.getString("interserver_http_port"))); + interserver_io_http_socket.setReceiveTimeout(settings.receive_timeout); + interserver_io_http_socket.setSendTimeout(settings.send_timeout); + interserver_io_http_server = new Poco::Net::HTTPServer( + new HTTPRequestHandlerFactory(*this, "InterserverIOHTTPHandler-factory"), + server_pool, + interserver_io_http_socket, + http_params); + } + /// OLAP HTTP Poco::SharedPtr olap_http_server; if (use_olap_server) @@ -284,10 +300,6 @@ int Server::main(const std::vector & args) olap_parser = new OLAP::QueryParser(); olap_converter = new OLAP::QueryConverter(config); - Poco::Net::HTTPServerParams * olap_http_params = new Poco::Net::HTTPServerParams; - olap_http_params->setTimeout(settings.receive_timeout); - olap_http_params->setKeepAliveTimeout(keep_alive_timeout); - Poco::Net::ServerSocket olap_http_socket(Poco::Net::SocketAddress("[::]:" + config.getString("olap_http_port"))); olap_http_socket.setReceiveTimeout(settings.receive_timeout); olap_http_socket.setSendTimeout(settings.send_timeout); @@ -295,12 +307,14 @@ int Server::main(const std::vector & args) new HTTPRequestHandlerFactory(*this, "OLAPHTTPHandler-factory"), server_pool, olap_http_socket, - olap_http_params); + http_params); } http_server.start(); tcp_server.start(); - if (use_olap_server) + if (interserver_io_http_server) + interserver_io_http_server->start(); + if (olap_http_server) olap_http_server->start(); LOG_INFO(log, "Ready for connections."); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp new file mode 100644 index 00000000000..525aad2341f --- /dev/null +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -0,0 +1,47 @@ +#include + +namespace DB +{ + +StorageReplicatedMergeTree::StorageReplicatedMergeTree( + const String & zookeeper_path_, + const String & replica_name_, + const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, + size_t index_granularity_, + MergeTreeData::Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_) + : + path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), zookeeper_path(zookeeper_path_), + replica_name(replica_name_), + data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, + index_granularity_,mode_, sign_column_, settings_), + reader(data), writer(data), + log(&Logger::get("StorageReplicatedMergeTree")), + shutdown_called(false) +{ + +} + +StoragePtr StorageReplicatedMergeTree::create( + const String & zookeeper_path_, + const String & replica_name_, + const String & path_, const String & name_, NamesAndTypesListPtr columns_, + const Context & context_, + ASTPtr & primary_expr_ast_, + const String & date_column_name_, + const ASTPtr & sampling_expression_, + size_t index_granularity_, + MergeTreeData::Mode mode_, + const String & sign_column_, + const MergeTreeSettings & settings_) +{ + return (new StorageReplicatedMergeTree(zookeeper_path_, replica_name_, path_, name_, columns_, context_, primary_expr_ast_, + date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); +} + +} From af094cbc7619a1b5779ee44218a9ad843dbe916b Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 21:23:09 +0400 Subject: [PATCH 031/281] dbms: Extracted most of RemoteReadBuffer into ReadBufferFromHTTP. [#METR-10202] --- dbms/include/DB/IO/ReadBufferFromHTTP.h | 86 +++++++++++++++++++ dbms/include/DB/IO/RemoteReadBuffer.h | 107 +++++------------------- 2 files changed, 109 insertions(+), 84 deletions(-) create mode 100644 dbms/include/DB/IO/ReadBufferFromHTTP.h diff --git a/dbms/include/DB/IO/ReadBufferFromHTTP.h b/dbms/include/DB/IO/ReadBufferFromHTTP.h new file mode 100644 index 00000000000..d94f8d53bbc --- /dev/null +++ b/dbms/include/DB/IO/ReadBufferFromHTTP.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define DEFAULT_HTTP_READ_BUFFER_TIMEOUT 1800 + + +namespace DB +{ + +/** Делает указанный HTTP-запрос и отдает ответ. + */ +class ReadBufferFromHTTP : public ReadBuffer +{ +private: + std::string host; + int port; + std::string params; + + Poco::Net::HTTPClientSession session; + std::istream * istr; /// этим владеет session + Poco::SharedPtr impl; + +public: + ReadBufferFromHTTP( + const std::string & host_, + int port_, + const std::string & params_, + size_t timeout_ = 0, + size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE) + : ReadBuffer(NULL, 0), host(host_), port(port_), params(params_) + { + std::string encoded_path; + Poco::URI::encode(path, "&#", encoded_path); + + std::stringstream uri; + uri << "http://" << host << ":" << port << "/?" << params; + + session.setHost(host); + session.setPort(port); + + /// устанавливаем таймаут + session.setTimeout(Poco::Timespan(timeout_ ? timeout_ : DEFAULT_REMOTE_READ_BUFFER_TIMEOUT, 0)); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uri.str()); + Poco::Net::HTTPResponse response; + + LOG_TRACE((&Logger::get("ReadBufferFromHTTP")), "Sending request to " << uri.str()); + + session.sendRequest(request); + istr = &session.receiveResponse(response); + + Poco::Net::HTTPResponse::HTTPStatus status = response.getStatus(); + + if (status != Poco::Net::HTTPResponse::HTTP_OK) + { + std::stringstream error_message; + error_message << "Received error from remote server " << uri.str() << ". HTTP status code: " + << status << ", body: " << istr->rdbuf(); + + throw Exception(error_message.str(), ErrorCodes::RECEIVED_ERROR_FROM_REMOTE_IO_SERVER); + } + + impl = new ReadBufferFromIStream(*istr, buffer_size_); + } + + bool nextImpl() + { + if (!impl->next()) + return false; + internal_buffer = impl->buffer(); + working_buffer = internal_buffer; + return true; + } +}; + +} diff --git a/dbms/include/DB/IO/RemoteReadBuffer.h b/dbms/include/DB/IO/RemoteReadBuffer.h index 5f35211f118..0366da65a18 100644 --- a/dbms/include/DB/IO/RemoteReadBuffer.h +++ b/dbms/include/DB/IO/RemoteReadBuffer.h @@ -1,78 +1,35 @@ #pragma once -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define DEFAULT_REMOTE_READ_BUFFER_TIMEOUT 1800 +#include +#include "ReadHelpers.h" namespace DB { -/** Позволяет читать файл с удалённого сервера. +/** Позволяет читать файл с удалённого сервера через riod. */ class RemoteReadBuffer : public ReadBuffer { private: - std::string host; - int port; - std::string path; - bool compress; - - Poco::Net::HTTPClientSession session; - std::istream * istr; /// этим владеет session - Poco::SharedPtr impl; + Poco::SharedPtr impl; public: RemoteReadBuffer( - const std::string & host_, - int port_, - const std::string & path_, - bool compress_ = true, - size_t timeout_ = 0, - size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE) - : ReadBuffer(NULL, 0), host(host_), port(port_), path(path_), compress(compress_) + const std::string & host, + int port, + const std::string & path, + bool compress = true, + size_t timeout = 0, + size_t buffer_size = DBMS_DEFAULT_BUFFER_SIZE) { std::string encoded_path; Poco::URI::encode(path, "&#", encoded_path); - std::stringstream uri; - uri << "http://" << host << ":" << port << "/?action=read&path=" << encoded_path << "&compress=" << (compress ? "true" : "false"); + std::stringstream params; + params << "action=read&path=" << encoded_path << "&compress=" << (compress ? "true" : "false"); - session.setHost(host); - session.setPort(port); - - /// устанавливаем таймаут - session.setTimeout(Poco::Timespan(timeout_ ? timeout_ : DEFAULT_REMOTE_READ_BUFFER_TIMEOUT, 0)); - - Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uri.str()); - Poco::Net::HTTPResponse response; - - LOG_TRACE((&Logger::get("RemoteReadBuffer")), "Sending request to " << uri.str()); - - session.sendRequest(request); - istr = &session.receiveResponse(response); - - Poco::Net::HTTPResponse::HTTPStatus status = response.getStatus(); - - if (status != Poco::Net::HTTPResponse::HTTP_OK) - { - std::stringstream error_message; - error_message << "Received error from remote server " << uri.str() << ". HTTP status code: " - << status << ", body: " << istr->rdbuf(); - - throw Exception(error_message.str(), ErrorCodes::RECEIVED_ERROR_FROM_REMOTE_IO_SERVER); - } - - impl = new ReadBufferFromIStream(*istr, buffer_size_); + impl = new ReadBufferFromHTTP(host, port, params.str, timeout, buffer_size); } bool nextImpl() @@ -89,42 +46,24 @@ public: const std::string & host, int port, const std::string & path, - size_t timeout_ = 0) + size_t timeout = 0) { std::string encoded_path; Poco::URI::encode(path, "&#", encoded_path); - std::stringstream uri; - uri << "http://" << host << ":" << port << "/?action=list&path=" << encoded_path; + std::stringstream params; + params << "action=list&path=" << encoded_path; - Poco::Net::HTTPClientSession session; - session.setHost(host); - session.setPort(port); - session.setTimeout(Poco::Timespan(timeout_ ? timeout_ : DEFAULT_REMOTE_READ_BUFFER_TIMEOUT, 0)); - - Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uri.str()); - Poco::Net::HTTPResponse response; - - LOG_TRACE((&Logger::get("RemoteReadBuffer")), "Sending request to " << uri.str()); - - session.sendRequest(request); - std::istream * istr = &session.receiveResponse(response); - - Poco::Net::HTTPResponse::HTTPStatus status = response.getStatus(); - - if (status != Poco::Net::HTTPResponse::HTTP_OK) - { - std::stringstream error_message; - error_message << "Received error from remote server " << uri.str() << ". HTTP status code: " - << status << ", body: " << istr->rdbuf(); - - throw Exception(error_message.str(), ErrorCodes::RECEIVED_ERROR_FROM_REMOTE_IO_SERVER); - } + ReadBufferFromHTTP in(host, port, params.str(), timeout); std::vector files; - std::string s; - while (getline(*istr, s, '\n') && !s.empty()) + while (!in.eof()) + { + std::string s; + readString(s, in); + skipWhitespaceIfAny(in); files.push_back(s); + } return files; } From 2809291432d810208024c250546514fa30df8ab6 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 22:58:24 +0400 Subject: [PATCH 032/281] zkutil: Thread safety. [#METR-10202] --- libs/libzkutil/include/zkutil/ZooKeeper.h | 4 +++- libs/libzkutil/src/ZooKeeper.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libs/libzkutil/include/zkutil/ZooKeeper.h b/libs/libzkutil/include/zkutil/ZooKeeper.h index 09c8d01b552..0c8c35cbec1 100644 --- a/libs/libzkutil/include/zkutil/ZooKeeper.h +++ b/libs/libzkutil/include/zkutil/ZooKeeper.h @@ -116,8 +116,10 @@ private: friend struct StateWatch; zk::ZooKeeper impl; - ACLs default_acl; WatchFunction * state_watch; + + Poco::FastMutex mutex; + ACLs default_acl; SessionState::type session_state; void stateChanged(WatchEvent::type event, SessionState::type state, const std::string& path); diff --git a/libs/libzkutil/src/ZooKeeper.cpp b/libs/libzkutil/src/ZooKeeper.cpp index c82c5f44e5b..5ca07b51c63 100644 --- a/libs/libzkutil/src/ZooKeeper.cpp +++ b/libs/libzkutil/src/ZooKeeper.cpp @@ -100,6 +100,8 @@ ZooKeeper::ZooKeeper(const Poco::Util::LayeredConfiguration & config, const std: void ZooKeeper::stateChanged(WatchEvent::type event, SessionState::type state, const std::string & path) { + Poco::ScopedLock lock(mutex); + session_state = state; if (state_watch) (*state_watch)(event, state, path); @@ -107,16 +109,22 @@ void ZooKeeper::stateChanged(WatchEvent::type event, SessionState::type state, c bool ZooKeeper::disconnected() { + Poco::ScopedLock lock(mutex); + return session_state == SessionState::Expired || session_state == SessionState::AuthFailed; } void ZooKeeper::setDefaultACL(ACLs & acl) { + Poco::ScopedLock lock(mutex); + default_acl = acl; } ACLs ZooKeeper::getDefaultACL() { + Poco::ScopedLock lock(mutex); + return default_acl; } @@ -148,6 +156,8 @@ bool ZooKeeper::tryGetChildren(const std::string & path, Strings & res, std::string ZooKeeper::create(const std::string & path, const std::string & data, CreateMode::type mode) { + Poco::ScopedLock lock(mutex); + std::string res; CHECKED(impl.create(path, data, default_acl, mode, res)); return res; @@ -155,6 +165,8 @@ std::string ZooKeeper::create(const std::string & path, const std::string & data ReturnCode::type ZooKeeper::tryCreate(const std::string & path, const std::string & data, CreateMode::type mode, std::string & pathCreated) { + Poco::ScopedLock lock(mutex); + ReturnCode::type code = impl.create(path, data, default_acl, mode, pathCreated); if (!( code == ReturnCode::Ok || code == ReturnCode::NoNode || From 7084f03ab9923f878a7e107562eccaecf17ab9ad Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 23:17:59 +0400 Subject: [PATCH 033/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 2 + dbms/include/DB/Interpreters/Context.h | 6 + .../DB/Storages/StorageReplicatedMergeTree.h | 56 ++++-- dbms/src/Interpreters/Context.cpp | 16 ++ dbms/src/Server/Server.cpp | 3 + dbms/src/Storages/StorageFactory.cpp | 173 ++++++++++++------ .../Storages/StorageReplicatedMergeTree.cpp | 103 ++++++++++- libs/libzkutil/src/ZooKeeper.cpp | 1 - 8 files changed, 283 insertions(+), 77 deletions(-) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index 0e935c2455f..6a1e293a8bc 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -229,6 +229,8 @@ namespace ErrorCodes DATABASE_NOT_EMPTY, DUPLICATE_INTERSERVER_IO_ENDPOINT, NO_SUCH_INTERSERVER_IO_ENDPOINT, + ADDING_REPLICA_TO_NON_EMPTY_TABLE, + UNEXPECTED_AST_STRUCTURE, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index a5f87006761..39ddbc45966 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace DB @@ -72,6 +73,8 @@ struct ContextShared mutable Poco::Mutex mutex; /// Для доступа и модификации разделяемых объектов. + mutable SharedPtr zookeeper; /// Клиент для ZooKeeper. + String path; /// Путь к директории с данными, со слешем на конце. Databases databases; /// Список БД и таблиц в них. TableFunctionFactory table_function_factory; /// Табличные функции. @@ -293,6 +296,9 @@ public: void setUncompressedCache(size_t cache_size_in_cells); UncompressedCachePtr getUncompressedCache() const; + void setZooKeeper(SharedPtr zookeeper); + zkutil::ZooKeeper * getZooKeeper() const; + /// Создать кэш засечек указанного размера. Это можно сделать только один раз. void setMarkCache(size_t cache_size_in_bytes); MarkCachePtr getMarkCache() const; diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 657807b3a05..544c050aa98 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -5,16 +5,22 @@ #include #include #include +#include namespace DB { +/** Движок, использующий merge-дерево и реплицируемый через ZooKeeper. + */ class StorageReplicatedMergeTree : public IStorage { public: + /** Если !attach, либо создает новую таблицу в ZK, либо добавляет реплику в существующую таблицу. + */ static StoragePtr create( const String & zookeeper_path_, const String & replica_name_, + bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -28,18 +34,18 @@ public: void shutdown(); ~StorageReplicatedMergeTree(); - std::string getName() const + std::string getName() const override { return "Replicated" + data.getModePrefix() + "MergeTree"; } - std::string getTableName() const { return name; } + std::string getTableName() const override { return name; } std::string getSignColumnName() const { return data.getSignColumnName(); } - bool supportsSampling() const { return data.supportsSampling(); } - bool supportsFinal() const { return data.supportsFinal(); } - bool supportsPrewhere() const { return data.supportsPrewhere(); } + bool supportsSampling() const override { return data.supportsSampling(); } + bool supportsFinal() const override { return data.supportsFinal(); } + bool supportsPrewhere() const override { return data.supportsPrewhere(); } - const NamesAndTypesList & getColumnsList() const { return data.getColumnsList(); } + const NamesAndTypesList & getColumnsList() const override { return data.getColumnsList(); } BlockInputStreams read( const Names & column_names, @@ -47,10 +53,12 @@ public: const Settings & settings, QueryProcessingStage::Enum & processed_stage, size_t max_block_size = DEFAULT_BLOCK_SIZE, - unsigned threads = 1); + unsigned threads = 1) override; - BlockOutputStreamPtr write(ASTPtr query); + BlockOutputStreamPtr write(ASTPtr query) override; + /** Удаляет реплику из ZooKeeper. Если других реплик нет, удаляет всю таблицу из ZooKeeper. + */ void drop() override; private: @@ -71,7 +79,7 @@ private: typedef std::list LogEntries; - /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из Zookeeper (/replicas/me/queue/). + /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из ZooKeeper (/replicas/me/queue/). * В ZK записи в хронологическом порядке. Здесь записи в том порядке, в котором их лучше выполнять. */ LogEntries queue; @@ -84,14 +92,16 @@ private: String zookeeper_path; String replica_name; - /** Является ли эта реплика "главной". Главная реплика выбирает куски для слияния. + /** Является ли эта реплика "ведущей". Ведущая реплика выбирает куски для слияния. */ - bool is_master_node; + bool is_leader_node; MergeTreeData data; MergeTreeDataSelectExecutor reader; MergeTreeDataWriter writer; + zkutil::ZooKeeper * zookeeper; + Logger * log; volatile bool shutdown_called; @@ -99,6 +109,7 @@ private: StorageReplicatedMergeTree( const String & zookeeper_path_, const String & replica_name_, + bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -109,6 +120,17 @@ private: const String & sign_column_ = "", const MergeTreeSettings & settings_ = MergeTreeSettings()); + /// Инициализация. + + bool isTableExistsInZooKeeper(); + bool isTableEmptyInZooKeeper(); + + void createNewTableInZooKeeper(); + void createNewReplicaInZooKeeper(); + + void removeReplicaInZooKeeper(); + void removeTableInZooKeeper(); + /** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). * Если нет - бросить исключение. */ @@ -121,7 +143,9 @@ private: */ void checkParts(); - /** Кладет в queue записи из Zookeeper (/replicas/me/queue/). + /// Работа с очередью и логом. + + /** Кладет в queue записи из ZooKeeper (/replicas/me/queue/). */ void loadQueue(); @@ -145,6 +169,14 @@ private: * - Не смогли объединить куски, потому что не все из них у нас есть. */ bool tryExecute(const LogEntry & entry); + + /// Обмен кусками. + + void registerEndpoint(); + void unregisterEndpoint(); + + String findReplicaHavingPart(const String & part_name); + void getPart(const String & name, const String & replica_name); }; } diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 1fcc148ff9d..3258007eeb4 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -474,6 +474,22 @@ MarkCachePtr Context::getMarkCache() const return shared->mark_cache; } +void Context::setZooKeeper(SharedPtr zookeeper) +{ + Poco::ScopedLock lock(shared->mutex); + + if (shared->zookeeper) + throw Exception("ZooKeeper client has already been set.", ErrorCodes::LOGICAL_ERROR); + + shared->zookeeper = zookeeper; +} + +zkutil::ZooKeeper * Context::getZooKeeper() const +{ + return shared->zookeeper.get(); +} + + void Context::initClusters() { Poco::ScopedLock lock(shared->mutex); diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index a4364bb4232..e4f59e72f95 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -213,6 +213,9 @@ int Server::main(const std::vector & args) global_context->setGlobalContext(*global_context); global_context->setPath(config.getString("path")); + if (config.has("zookeeper")) + global_context->setZooKeeper(new zkutil::ZooKeeper(config, "zookeeper")); + std::string users_config_path = config.getString("users_config", config.getString("config-file", "config.xml")); users_config_reloader = new UsersConfigReloader(users_config_path, global_context); diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index 315390328b5..3a3959e417a 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,15 @@ namespace DB { +static bool endsWith(const std::string & s, const std::string & suffix) +{ + return s.size() >= suffix.size() && s.substr(s.size() - suffix.size()) == suffix; +} + +static bool startsWith(const std::string & s, const std::string & prefix) +{ + return s.size() >= prefix.size() && s.substr(0, prefix.size()) == prefix; +} /** Для StorageMergeTree: достать первичный ключ в виде ASTExpressionList. * Он может быть указан в кортеже: (CounterID, Date), @@ -176,80 +186,121 @@ StoragePtr StorageFactory::get( return StorageDistributed::create(table_name, columns, remote_database, remote_table, cluster_name, context, sign_column_name); } - else if (name == "MergeTree" || name == "SummingMergeTree") + else if (endsWith(name, "MergeTree")) { - /** В качестве аргумента для движка должно быть указано: + /** Движки [Replicated][Summing|Collapsing]MergeTree (6 комбинаций) + * В качестве аргумента для движка должно быть указано: + * - (для Replicated) Путь к таблице в ZooKeeper + * - (для Replicated) Имя реплики в ZooKeeper * - имя столбца с датой; - * - имя столбца для семплирования (запрос с SAMPLE x будет выбирать строки, у которых в этом столбце значение меньше, чем x*UINT32_MAX); - * - выражение для сортировки в скобках; - * - index_granularity. - * Например: ENGINE = MergeTree(EventDate, intHash32(UniqID), (CounterID, EventDate, intHash32(UniqID), EventTime), 8192). - * - * SummingMergeTree - вариант, в котором при слиянии делается суммирование всех числовых столбцов кроме PK - * - для Баннерной Крутилки. - */ - ASTs & args_func = dynamic_cast(*dynamic_cast(*query).storage).children; - - if (args_func.size() != 1) - throw Exception("Storage " + name + " requires 3 or 4 parameters" - " - name of column with date, [name of column for sampling], primary key expression, index granularity.", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - ASTs & args = dynamic_cast(*args_func.at(0)).children; - - if (args.size() != 3 && args.size() != 4) - throw Exception("Storage " + name + " requires 3 or 4 parameters" - " - name of column with date, [name of column for sampling], primary key expression, index granularity.", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - size_t arg_offset = args.size() - 3; - - String date_column_name = dynamic_cast(*args[0]).name; - ASTPtr sampling_expression = arg_offset == 0 ? NULL : args[1]; - UInt64 index_granularity = safeGet(dynamic_cast(*args[arg_offset + 2]).value); - - ASTPtr primary_expr_list = extractPrimaryKey(args[arg_offset + 1], name); - - return StorageMergeTree::create( - data_path, table_name, columns, context, primary_expr_list, date_column_name, sampling_expression, index_granularity, - name == "SummingMergeTree" ? MergeTreeData::Summing : MergeTreeData::Ordinary); - } - else if (name == "CollapsingMergeTree") - { - /** В качестве аргумента для движка должно быть указано: - * - имя столбца с датой; - * - имя столбца для семплирования (запрос с SAMPLE x будет выбирать строки, у которых в этом столбце значение меньше, чем x*UINT32_MAX); - * - выражение для сортировки в скобках; + * - (не обязательно) имя столбца для семплирования (запрос с SAMPLE x будет выбирать строки, у которых в этом столбце значение меньше, чем x*UINT32_MAX); + * - выражение для сортировки (либо скалярное выражение, либо tuple из нескольких); * - index_granularity; - * - имя столбца, содержащего тип строчки с изменением "визита" (принимающего значения 1 и -1). - * Например: ENGINE = CollapsingMergeTree(EventDate, (CounterID, EventDate, intHash32(UniqID), VisitID), 8192, Sign). + * - (для Collapsing) имя столбца, содержащего тип строчки с изменением "визита" (принимающего значения 1 и -1). + * Например: ENGINE = ReplicatedCollapsingMergeTree('/tables/mytable', 'rep02', EventDate, (CounterID, EventDate, intHash32(UniqID), VisitID), 8192, Sign). */ + + String name_part = name.substr(0, name.size() - strlen("MergeTree")); + + bool replicated = startsWith(name_part, "Replicated"); + if (replicated) + name_part = name_part.substr(strlen("Replicated")); + + MergeTreeData::Mode mode = MergeTreeData::Ordinary; + + if (name_part == "Collapsing") + mode = MergeTreeData::Collapsing; + else if (name_part == "Summing") + mode = MergeTreeData::Summing; + else if (!name_part.empty()) + throw Exception("Unknown storage " + name, ErrorCodes::UNKNOWN_STORAGE); + ASTs & args_func = dynamic_cast(*dynamic_cast(*query).storage).children; if (args_func.size() != 1) - throw Exception("Storage CollapsingMergeTree requires 4 or 5 parameters" - " - name of column with date, [name of column for sampling], primary key expression, index granularity, sign_column.", + throw Exception("Unexpected AST structure.", + ErrorCodes::UNEXPECTED_AST_STRUCTURE); + + ASTs args = dynamic_cast(*args_func.at(0)).children; + + size_t additional_params = (replicated ? 2 : 0) + (mode == MergeTreeData::Collapsing ? 1 : 0); + if (args.size() != additional_params + 3 && args.size() != additional_params + 4) + { + String params; + if (replicated) + params += "path in ZooKeeper, replica name, "; + params += "name of column with date, [name of column for sampling], primary key expression, index granularity"; + if (mode == MergeTreeData::Collapsing) + params += "sign column"; + throw Exception("Storage CollapsingMergeTree requires " + toString(additional_params + 3) + " or " + + toString(additional_params + 4) +" parameters: " + params, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } - ASTs & args = dynamic_cast(*args_func.at(0)).children; + String zookeeper_path; + String replica_name; - if (args.size() != 4 && args.size() != 5) - throw Exception("Storage CollapsingMergeTree requires 4 or 5 parameters" - " - name of column with date, [name of column for sampling], primary key expression, index granularity, sign_column.", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + String date_column_name; + ASTPtr primary_expr_list; + ASTPtr sampling_expression; + UInt64 index_granularity; - size_t arg_offset = args.size() - 4; - - String date_column_name = dynamic_cast(*args[0]).name; - ASTPtr sampling_expression = arg_offset == 0 ? NULL : args[1]; - UInt64 index_granularity = safeGet(dynamic_cast(*args[arg_offset + 2]).value); - String sign_column_name = dynamic_cast(*args[arg_offset + 3]).name; + String sign_column_name; - ASTPtr primary_expr_list = extractPrimaryKey(args[arg_offset + 1], name); + if (replicated) + { + auto ast = dynamic_cast(&*args[0]); + if (ast && ast->value.getType() == Field::Types::String) + zookeeper_path = safeGet(ast->value); + else + throw Exception("Path in ZooKeeper must be a string literal", ErrorCodes::BAD_ARGUMENTS); - return StorageMergeTree::create( - data_path, table_name, columns, context, primary_expr_list, date_column_name, - sampling_expression, index_granularity, MergeTreeData::Collapsing, sign_column_name); + ast = dynamic_cast(&*args[1]); + if (ast && ast->value.getType() == Field::Types::String) + replica_name = safeGet(ast->value); + else + throw Exception("Replica name must be a string literal", ErrorCodes::BAD_ARGUMENTS); + + args.erase(args.begin(), args.begin() + 2); + } + + if (mode == MergeTreeData::Collapsing) + { + if (auto ast = dynamic_cast(&*args.back())) + sign_column_name = ast->name; + else + throw Exception("Sign column name must be an unquoted string", ErrorCodes::BAD_ARGUMENTS); + + args.pop_back(); + } + + if (args.size() == 4) + { + sampling_expression = args[1]; + args.erase(args.begin() + 1); + } + + if (auto ast = dynamic_cast(&*args[0])) + date_column_name = ast->name; + else + throw Exception("Date column name must be an unquoted string", ErrorCodes::BAD_ARGUMENTS); + + primary_expr_list = extractPrimaryKey(args[1], name); + + auto ast = dynamic_cast(&*args[2]); + if (ast && ast->value.getType() == Field::Types::UInt64) + index_granularity = safeGet(ast->value); + else + throw Exception("Index granularity must be a positive integer", ErrorCodes::BAD_ARGUMENTS); + + if (replicated) + return StorageReplicatedMergeTree::create(zookeeper_path, replica_name, attach, data_path, table_name, + columns, context, primary_expr_list, date_column_name, + sampling_expression, index_granularity, mode, sign_column_name); + else + return StorageMergeTree::create(data_path, table_name, + columns, context, primary_expr_list, date_column_name, + sampling_expression, index_granularity, mode, sign_column_name); } else if (name == "SystemNumbers") { diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 525aad2341f..8566aa22b02 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -6,6 +6,7 @@ namespace DB StorageReplicatedMergeTree::StorageReplicatedMergeTree( const String & zookeeper_path_, const String & replica_name_, + bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -21,15 +22,34 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), reader(data), writer(data), - log(&Logger::get("StorageReplicatedMergeTree")), + zookeeper(context_.getZooKeeper()), log(&Logger::get("StorageReplicatedMergeTree")), shutdown_called(false) { - + if (!attach) + { + if (isTableExistsInZooKeeper()) + { + if (!isTableEmptyInZooKeeper()) + throw Exception("Can't add new replica to non-empty table", ErrorCodes::ADDING_REPLICA_TO_NON_EMPTY_TABLE); + checkTableStructure(); + createNewReplicaInZooKeeper(); + } + else + { + createNewTableInZooKeeper(); + } + } + else + { + checkTableStructure(); + checkParts(); + } } StoragePtr StorageReplicatedMergeTree::create( const String & zookeeper_path_, const String & replica_name_, + bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, const Context & context_, ASTPtr & primary_expr_ast_, @@ -40,8 +60,85 @@ StoragePtr StorageReplicatedMergeTree::create( const String & sign_column_, const MergeTreeSettings & settings_) { - return (new StorageReplicatedMergeTree(zookeeper_path_, replica_name_, path_, name_, columns_, context_, primary_expr_ast_, + return (new StorageReplicatedMergeTree(zookeeper_path_, replica_name_, attach, path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); } +void StorageReplicatedMergeTree::shutdown() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +StorageReplicatedMergeTree::~StorageReplicatedMergeTree() +{ + +} + +BlockInputStreams StorageReplicatedMergeTree::read( + const Names & column_names, + ASTPtr query, + const Settings & settings, + QueryProcessingStage::Enum & processed_stage, + size_t max_block_size, + unsigned threads) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Удаляет реплику из ZooKeeper. Если других реплик нет, удаляет всю таблицу из ZooKeeper. + */ +void StorageReplicatedMergeTree::drop() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +bool StorageReplicatedMergeTree::isTableExistsInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +bool StorageReplicatedMergeTree::isTableEmptyInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::createNewTableInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::createNewReplicaInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::removeReplicaInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::removeTableInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). + * Если нет - бросить исключение. + */ +void StorageReplicatedMergeTree::checkTableStructure() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Проверить, что множество кусков соответствует тому, что в ZK (/replicas/me/parts/). + * Если каких-то кусков, описанных в ZK нет локально, бросить исключение. + * Если какие-то локальные куски не упоминаются в ZK, удалить их. + * Но если таких слишком много, на всякий случай бросить исключение - скорее всего, это ошибка конфигурации. + */ +void StorageReplicatedMergeTree::checkParts() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/// Работа с очередью и логом. + +/** Кладет в queue записи из ZooKeeper (/replicas/me/queue/). + */ +void StorageReplicatedMergeTree::loadQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Копирует новые записи из логов всех реплик в очередь этой реплики. + */ +void StorageReplicatedMergeTree::pullLogsToQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Делает преобразования над очередью: + * - Если есть MERGE_PARTS кусков, не все из которых у нас есть, заменяем его на GET_PART и + * убираем GET_PART для всех составляющих его кусков. NOTE: Наверно, это будет плохо работать. Придумать эвристики получше. + */ +void StorageReplicatedMergeTree::optimizeQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** По порядку пытается выполнить действия из очереди, пока не получится. Что получилось, выбрасывает из очереди. + */ +void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/** Попробовать выполнить действие из очереди. Возвращает false, если не получилось по какой-то ожидаемой причине: + * - GET_PART, и ни у кого нет этого куска. Это возможно, если этот кусок уже слили с кем-то и удалили. + * - Не смогли скачать у кого-то кусок, потому что его там уже нет. + * - Не смогли объединить куски, потому что не все из них у нас есть. + */ +bool StorageReplicatedMergeTree::tryExecute(const LogEntry & entry) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +/// Обмен кусками. + +void StorageReplicatedMergeTree::registerEndpoint() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::unregisterEndpoint() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::getPart(const String & name, const String & replica_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + } diff --git a/libs/libzkutil/src/ZooKeeper.cpp b/libs/libzkutil/src/ZooKeeper.cpp index 5ca07b51c63..ed4e5133d45 100644 --- a/libs/libzkutil/src/ZooKeeper.cpp +++ b/libs/libzkutil/src/ZooKeeper.cpp @@ -68,7 +68,6 @@ struct ZooKeeperArgs Poco::Util::AbstractConfiguration::Keys keys; config.keys(config_name, keys); std::string node_key = "node"; - std::string node_key_ext = "node["; session_timeout_ms = DEFAULT_SESSION_TIMEOUT; for (const auto & key : keys) From 36b152f2b1d5698338d9dce6ccd3d189592190d5 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 21 Mar 2014 23:49:27 +0400 Subject: [PATCH 034/281] Added interserver IO host and port to context. [#METR-10202] --- dbms/include/DB/Interpreters/Context.h | 8 ++++++++ dbms/src/Interpreters/Context.cpp | 17 +++++++++++++++++ dbms/src/Server/Server.cpp | 13 ++++++++++++- .../src/Storages/StorageReplicatedMergeTree.cpp | 3 +++ libs/libzkutil/src/ZooKeeper.cpp | 2 +- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 39ddbc45966..90c5b96135f 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -75,6 +75,9 @@ struct ContextShared mutable SharedPtr zookeeper; /// Клиент для ZooKeeper. + String interserver_io_host; /// Имя хоста по которым это сервер доступен для других серверов. + int interserver_io_port; /// и порт, + String path; /// Путь к директории с данными, со слешем на конце. Databases databases; /// Список БД и таблиц в них. TableFunctionFactory table_function_factory; /// Табличные функции. @@ -253,6 +256,11 @@ public: InterserverIOHandler & getInterserverIOHandler() { return shared->interserver_io_handler; } + /// Как другие серверы могут обратиться к этому. + void setInterserverIOHost(const String & host, int port); + String getInterserverIOHost() const; + int getInterserverIOPort() const; + /// Получить запрос на CREATE таблицы. ASTPtr getCreateQuery(const String & database_name, const String & table_name) const; diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 3258007eeb4..c1e897076e6 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -490,6 +490,23 @@ zkutil::ZooKeeper * Context::getZooKeeper() const } +void Context::setInterserverIOHost(const String & host, int port) +{ + shared->interserver_io_host = host; + shared->interserver_io_port = port; +} + +String Context::getInterserverIOHost() const +{ + return shared->interserver_io_host; +} + +int Context::getInterserverIOPort() const +{ + return shared->interserver_io_port; +} + + void Context::initClusters() { Poco::ScopedLock lock(shared->mutex); diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index e4f59e72f95..28e79e0d6e3 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -285,8 +285,19 @@ int Server::main(const std::vector & args) Poco::SharedPtr interserver_io_http_server; if (config.has("interserver_http_port")) { + String this_host; + if (config.has("interserver_http_host")) + this_host = config.getString("interserver_http_host"); + else + this_host = Poco::Net::DNS::hostName(); + + String port_str = config.getString("interserver_http_port"); + int port = parse(port_str); + + global_context->setInterserverIOHost(this_host, port); + Poco::Net::ServerSocket interserver_io_http_socket(Poco::Net::SocketAddress("[::]:" - + config.getString("interserver_http_port"))); + + port_str)); interserver_io_http_socket.setReceiveTimeout(settings.receive_timeout); interserver_io_http_socket.setSendTimeout(settings.send_timeout); interserver_io_http_server = new Poco::Net::HTTPServer( diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 8566aa22b02..5f1aaa08f41 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -25,6 +25,9 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( zookeeper(context_.getZooKeeper()), log(&Logger::get("StorageReplicatedMergeTree")), shutdown_called(false) { + if (zookeeper_path.empty() || *zookeeper_path.rbegin() != '/') + zookeeper_path += '/'; + if (!attach) { if (isTableExistsInZooKeeper()) diff --git a/libs/libzkutil/src/ZooKeeper.cpp b/libs/libzkutil/src/ZooKeeper.cpp index ed4e5133d45..f7c6c147b97 100644 --- a/libs/libzkutil/src/ZooKeeper.cpp +++ b/libs/libzkutil/src/ZooKeeper.cpp @@ -75,7 +75,7 @@ struct ZooKeeperArgs if (key == node_key || key.compare(0, node_key.size(), node_key) == 0) { if (hosts.size()) - hosts += std::string(" "); + hosts += std::string(","); hosts += config.getString(config_name + "." + key + ".host") + ":" + config.getString(config_name + "." + key + ".port"); } else if (key == "session_timeout_ms") From 7b6ce306564529fe4073a1d92e9e65f8651c7a9b Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Sat, 22 Mar 2014 18:44:44 +0400 Subject: [PATCH 035/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 2 + dbms/include/DB/IO/ReadHelpers.h | 5 + dbms/include/DB/Interpreters/Context.h | 2 +- .../DB/Interpreters/InterserverIOHandler.h | 31 +- .../DB/Storages/MergeTree/MergeTreeData.h | 4 +- .../DB/Storages/StorageReplicatedMergeTree.h | 47 +++- dbms/src/Interpreters/Context.cpp | 6 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 6 +- .../Storages/StorageReplicatedMergeTree.cpp | 265 ++++++++++++------ libs/libzkutil/include/zkutil/ZooKeeper.h | 57 +++- libs/libzkutil/src/ZooKeeper.cpp | 21 ++ 11 files changed, 343 insertions(+), 103 deletions(-) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index 6a1e293a8bc..60c068defd3 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -231,6 +231,8 @@ namespace ErrorCodes NO_SUCH_INTERSERVER_IO_ENDPOINT, ADDING_REPLICA_TO_NON_EMPTY_TABLE, UNEXPECTED_AST_STRUCTURE, + REPLICA_IS_ALREADY_ACTIVE, + NO_ZOOKEEPER, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/IO/ReadHelpers.h b/dbms/include/DB/IO/ReadHelpers.h index cb336d7194b..b5c482eba6f 100644 --- a/dbms/include/DB/IO/ReadHelpers.h +++ b/dbms/include/DB/IO/ReadHelpers.h @@ -115,6 +115,11 @@ inline void readChar(char & x, ReadBuffer & buf) void assertString(const char * s, ReadBuffer & buf); +void assertString(const String & s, ReadBuffer & buf) +{ + assertString(s.c_str(), buf); +} + inline void readBoolText(bool & x, ReadBuffer & buf) { diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 90c5b96135f..41a30099264 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -305,7 +305,7 @@ public: UncompressedCachePtr getUncompressedCache() const; void setZooKeeper(SharedPtr zookeeper); - zkutil::ZooKeeper * getZooKeeper() const; + zkutil::ZooKeeper & getZooKeeper() const; /// Создать кэш засечек указанного размера. Это можно сделать только один раз. void setMarkCache(size_t cache_size_in_bytes); diff --git a/dbms/include/DB/Interpreters/InterserverIOHandler.h b/dbms/include/DB/Interpreters/InterserverIOHandler.h index bddf00f05f4..16248887918 100644 --- a/dbms/include/DB/Interpreters/InterserverIOHandler.h +++ b/dbms/include/DB/Interpreters/InterserverIOHandler.h @@ -23,7 +23,7 @@ typedef Poco::SharedPtr InterserverIOEndpointPtr; /** Сюда можно зарегистрировать сервис, обрататывающий запросы от других серверов. - * Используется для передачи данных в ReplicatedMergeTree. + * Используется для передачи кусков в ReplicatedMergeTree. */ class InterserverIOHandler { @@ -59,4 +59,33 @@ private: Poco::FastMutex mutex; }; +/// В конструкторе вызывает addEndpoint, в деструкторе - removeEndpoint. +class InterserverIOEndpointHolder +{ +public: + InterserverIOEndpointHolder(const String & name_, InterserverIOEndpointPtr endpoint, InterserverIOHandler & handler_) + : name(name_), handler(handler_) + { + handler.addEndpoint(name, endpoint); + } + + ~InterserverIOEndpointHolder() + { + try + { + handler.removeEndpoint(name); + } + catch (...) + { + tryLogCurrentException("~InterserverIOEndpointHolder"); + } + } + +private: + String name; + InterserverIOHandler & handler; +}; + +typedef Poco::SharedPtr InterserverIOEndpointHolderPtr; + } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 593eef78765..8b8c6699378 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -310,13 +310,13 @@ public: const MergeTreeSettings settings; + const ASTPtr primary_expr_ast; + private: ExpressionActionsPtr primary_expr; SortDescription sort_descr; Block primary_key_sample; - ASTPtr primary_expr_ast; - String full_path; NamesAndTypesListPtr columns; diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 544c050aa98..aad819a6d29 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -22,7 +22,7 @@ public: const String & replica_name_, bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, + Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. @@ -79,6 +79,21 @@ private: typedef std::list LogEntries; + class MyInterserverIOEndpoint : public InterserverIOEndpoint + { + public: + MyInterserverIOEndpoint(StorageReplicatedMergeTree & storage_) : storage(storage_), owned_storage(storage.thisPtr()) {} + + void processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) override; + + private: + StorageReplicatedMergeTree & storage; + StoragePtr owned_storage; + }; + + Context & context; + zkutil::ZooKeeper & zookeeper; + /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из ZooKeeper (/replicas/me/queue/). * В ZK записи в хронологическом порядке. Здесь записи в том порядке, в котором их лучше выполнять. */ @@ -91,17 +106,22 @@ private: String zookeeper_path; String replica_name; + String replica_path; + + /** /replicas/me/is_active. + */ + zkutil::EphemeralNodeHolderPtr replica_is_active_node; /** Является ли эта реплика "ведущей". Ведущая реплика выбирает куски для слияния. */ bool is_leader_node; + InterserverIOEndpointHolderPtr endpoint_holder; + MergeTreeData data; MergeTreeDataSelectExecutor reader; MergeTreeDataWriter writer; - zkutil::ZooKeeper * zookeeper; - Logger * log; volatile bool shutdown_called; @@ -111,7 +131,7 @@ private: const String & replica_name_, bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, + Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, @@ -122,14 +142,18 @@ private: /// Инициализация. - bool isTableExistsInZooKeeper(); - bool isTableEmptyInZooKeeper(); + /** Проверяет, что в ZooKeeper в таблице нет данных. + */ + bool isTableEmpty(); - void createNewTableInZooKeeper(); - void createNewReplicaInZooKeeper(); + /** Создает минимальный набор нод в ZooKeeper. + */ + void createTable(); + void createReplica(); - void removeReplicaInZooKeeper(); - void removeTableInZooKeeper(); + /** Отметить в ZooKeeper, что эта реплика сейчас активна. + */ + void activateReplica(); /** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). * Если нет - бросить исключение. @@ -172,9 +196,6 @@ private: /// Обмен кусками. - void registerEndpoint(); - void unregisterEndpoint(); - String findReplicaHavingPart(const String & part_name); void getPart(const String & name, const String & replica_name); }; diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index c1e897076e6..1bb88179373 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -484,9 +484,11 @@ void Context::setZooKeeper(SharedPtr zookeeper) shared->zookeeper = zookeeper; } -zkutil::ZooKeeper * Context::getZooKeeper() const +zkutil::ZooKeeper & Context::getZooKeeper() const { - return shared->zookeeper.get(); + if (!shared->zookeeper) + throw Exception("No ZooKeeper in Context", ErrorCodes::NO_ZOOKEEPER); + return *shared->zookeeper; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 580504feb0e..accfc3a8f93 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -51,11 +51,9 @@ MergeTreeData::MergeTreeData( /// инициализируем описание сортировки sort_descr.reserve(primary_expr_ast->children.size()); - for (ASTs::iterator it = primary_expr_ast->children.begin(); - it != primary_expr_ast->children.end(); - ++it) + for (const ASTPtr & ast : primary_expr_ast->children) { - String name = (*it)->getColumnName(); + String name = ast->getColumnName(); sort_descr.push_back(SortColumnDescription(name, 1)); } diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 5f1aaa08f41..0a39e3ea2fd 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -1,14 +1,25 @@ #include +#include +#include +#include namespace DB { +void StorageReplicatedMergeTree::MyInterserverIOEndpoint::processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) +{ + writeString("Hello. You requested part ", out); + writeString(params.get("part"), out); + writeString(".", out); +} + + StorageReplicatedMergeTree::StorageReplicatedMergeTree( const String & zookeeper_path_, const String & replica_name_, bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, + Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, @@ -17,36 +28,41 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( const String & sign_column_, const MergeTreeSettings & settings_) : + context(context_), zookeeper(context.getZooKeeper()), path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), zookeeper_path(zookeeper_path_), replica_name(replica_name_), data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), reader(data), writer(data), - zookeeper(context_.getZooKeeper()), log(&Logger::get("StorageReplicatedMergeTree")), + log(&Logger::get("StorageReplicatedMergeTree")), shutdown_called(false) { - if (zookeeper_path.empty() || *zookeeper_path.rbegin() != '/') - zookeeper_path += '/'; + if (!zookeeper_path.empty() && *zookeeper_path.rbegin() == '/') + zookeeper_path.erase(zookeeper_path.end() - 1); + replica_path = zookeeper_path + "/replicas/" + replica_name; if (!attach) { - if (isTableExistsInZooKeeper()) - { - if (!isTableEmptyInZooKeeper()) - throw Exception("Can't add new replica to non-empty table", ErrorCodes::ADDING_REPLICA_TO_NON_EMPTY_TABLE); - checkTableStructure(); - createNewReplicaInZooKeeper(); - } - else - { - createNewTableInZooKeeper(); - } + if (!zookeeper.exists(zookeeper_path)) + createTable(); + + if (!isTableEmpty()) + throw Exception("Can't add new replica to non-empty table", ErrorCodes::ADDING_REPLICA_TO_NON_EMPTY_TABLE); + + checkTableStructure(); + createReplica(); } else { checkTableStructure(); checkParts(); } + + String endpoint_name = "ReplicatedMergeTree:" + replica_path; + InterserverIOEndpointPtr endpoint = new MyInterserverIOEndpoint(*this); + endpoint_holder = new InterserverIOEndpointHolder(endpoint_name, endpoint, context.getInterserverIOHandler()); + + activateReplica(); } StoragePtr StorageReplicatedMergeTree::create( @@ -54,7 +70,7 @@ StoragePtr StorageReplicatedMergeTree::create( const String & replica_name_, bool attach, const String & path_, const String & name_, NamesAndTypesListPtr columns_, - const Context & context_, + Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, const ASTPtr & sampling_expression_, @@ -67,11 +83,153 @@ StoragePtr StorageReplicatedMergeTree::create( date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); } -void StorageReplicatedMergeTree::shutdown() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +static String formattedAST(const ASTPtr & ast) +{ + if (!ast) + return ""; + std::stringstream ss; + formatAST(*ast, ss, 0, false, true); + return ss.str(); +} + +void StorageReplicatedMergeTree::createTable() +{ + zookeeper.create(zookeeper_path, "", zkutil::CreateMode::Persistent); + + /// Запишем метаданные таблицы, чтобы реплики могли сверять с ними свою локальную структуру таблицы. + std::stringstream metadata; + metadata << "metadata format version: 1" << std::endl; + metadata << "date column: " << data.date_column_name << std::endl; + metadata << "sampling expression: " << formattedAST(data.sampling_expression) << std::endl; + metadata << "index granularity: " << data.index_granularity << std::endl; + metadata << "mode: " << static_cast(data.mode) << std::endl; + metadata << "sign column: " << data.sign_column << std::endl; + metadata << "primary key: " << formattedAST(data.primary_expr_ast) << std::endl; + metadata << "columns:" << std::endl; + WriteBufferFromOStream buf(metadata); + for (auto & it : data.getColumnsList()) + { + writeBackQuotedString(it.first, buf); + writeChar(' ', buf); + writeString(it.second->getName(), buf); + writeChar('\n', buf); + } + buf.next(); + + zookeeper.create(zookeeper_path + "/metadata", metadata.str(), zkutil::CreateMode::Persistent); + + /// Создадим нужные "директории". + zookeeper.create(zookeeper_path + "/replicas", "", zkutil::CreateMode::Persistent); + zookeeper.create(zookeeper_path + "/blocks", "", zkutil::CreateMode::Persistent); + zookeeper.create(zookeeper_path + "/block-numbers", "", zkutil::CreateMode::Persistent); +} + +/** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). + * Если нет - бросить исключение. + */ +void StorageReplicatedMergeTree::checkTableStructure() +{ + String metadata_str = zookeeper.get(zookeeper_path + "/metadata"); + ReadBufferFromString buf(metadata_str); + assertString("metadata format version: 1", buf); + assertString("\ndate column: ", buf); + assertString(data.date_column_name, buf); + assertString("\nsampling expression: ", buf); + assertString(formattedAST(data.sampling_expression), buf); + assertString("\nindex granularity: ", buf); + assertString(toString(data.index_granularity), buf); + assertString("\nmode: ", buf); + assertString(toString(static_cast(data.mode)), buf); + assertString("\nsign column: ", buf); + assertString(data.sign_column, buf); + assertString("\nprimary key: ", buf); + assertString(formattedAST(data.primary_expr_ast), buf); + assertString("\ncolumns:\n", buf); + for (auto & it : data.getColumnsList()) + { + String name; + readBackQuotedString(name, buf); + if (name != it.first) + throw Exception("Unexpected column name in ZooKeeper: expected " + it.first + ", found " + name, + ErrorCodes::UNKNOWN_IDENTIFIER); + assertString(" ", buf); + assertString(it.second->getName(), buf); + assertString("\n", buf); + } +} + +void StorageReplicatedMergeTree::createReplica() +{ + zookeeper.create(replica_path, "", zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/host", "", zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/log", "", zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/log_pointers", "", zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/queue", "", zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/parts", "", zkutil::CreateMode::Persistent); +} + +void StorageReplicatedMergeTree::activateReplica() +{ + std::stringstream host; + host << "host: " << context.getInterserverIOHost() << std::endl; + host << "port: " << context.getInterserverIOPort() << std::endl; + + /// Одновременно объявим, что эта реплика активна и обновим хост. + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create(replica_path + "/is_active", "", zookeeper.getDefaultACL(), zkutil::CreateMode::Ephemeral)); + ops.push_back(new zkutil::Op::SetData(replica_path + "/host", host.str(), -1)); + zookeeper.multi(ops); + + replica_is_active_node = zkutil::EphemeralNodeHolder::existing(replica_path + "/is_active", zookeeper); +} + +bool StorageReplicatedMergeTree::isTableEmpty() +{ + Strings replicas = zookeeper.getChildren(zookeeper_path + "/replicas"); + for (const auto & replica : replicas) + { + if (!zookeeper.getChildren(zookeeper_path + "/replicas/" + replica + "/parts").empty()) + return false; + } + return true; +} + +void StorageReplicatedMergeTree::checkParts() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::loadQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::pullLogsToQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::optimizeQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +bool StorageReplicatedMergeTree::tryExecute(const LogEntry & entry) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::getPart(const String & name, const String & replica_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + +void StorageReplicatedMergeTree::shutdown() +{ + if (shutdown_called) + return; + shutdown_called = true; + replica_is_active_node = nullptr; + endpoint_holder = nullptr; + + /// Кажется, чтобы был невозможен дедлок, тут придется дождаться удаления MyInterserverIOEndpoint. +} StorageReplicatedMergeTree::~StorageReplicatedMergeTree() { - + try + { + shutdown(); + } + catch(...) + { + tryLogCurrentException("~StorageReplicatedMergeTree"); + } } BlockInputStreams StorageReplicatedMergeTree::read( @@ -80,68 +238,19 @@ BlockInputStreams StorageReplicatedMergeTree::read( const Settings & settings, QueryProcessingStage::Enum & processed_stage, size_t max_block_size, - unsigned threads) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + unsigned threads) +{ + return reader.read(column_names, query, settings, processed_stage, max_block_size, threads); +} BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -/** Удаляет реплику из ZooKeeper. Если других реплик нет, удаляет всю таблицу из ZooKeeper. - */ -void StorageReplicatedMergeTree::drop() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -bool StorageReplicatedMergeTree::isTableExistsInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -bool StorageReplicatedMergeTree::isTableEmptyInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -void StorageReplicatedMergeTree::createNewTableInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -void StorageReplicatedMergeTree::createNewReplicaInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -void StorageReplicatedMergeTree::removeReplicaInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -void StorageReplicatedMergeTree::removeTableInZooKeeper() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). - * Если нет - бросить исключение. - */ -void StorageReplicatedMergeTree::checkTableStructure() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** Проверить, что множество кусков соответствует тому, что в ZK (/replicas/me/parts/). - * Если каких-то кусков, описанных в ZK нет локально, бросить исключение. - * Если какие-то локальные куски не упоминаются в ZK, удалить их. - * Но если таких слишком много, на всякий случай бросить исключение - скорее всего, это ошибка конфигурации. - */ -void StorageReplicatedMergeTree::checkParts() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/// Работа с очередью и логом. - -/** Кладет в queue записи из ZooKeeper (/replicas/me/queue/). - */ -void StorageReplicatedMergeTree::loadQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** Копирует новые записи из логов всех реплик в очередь этой реплики. - */ -void StorageReplicatedMergeTree::pullLogsToQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** Делает преобразования над очередью: - * - Если есть MERGE_PARTS кусков, не все из которых у нас есть, заменяем его на GET_PART и - * убираем GET_PART для всех составляющих его кусков. NOTE: Наверно, это будет плохо работать. Придумать эвристики получше. - */ -void StorageReplicatedMergeTree::optimizeQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** По порядку пытается выполнить действия из очереди, пока не получится. Что получилось, выбрасывает из очереди. - */ -void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/** Попробовать выполнить действие из очереди. Возвращает false, если не получилось по какой-то ожидаемой причине: - * - GET_PART, и ни у кого нет этого куска. Это возможно, если этот кусок уже слили с кем-то и удалили. - * - Не смогли скачать у кого-то кусок, потому что его там уже нет. - * - Не смогли объединить куски, потому что не все из них у нас есть. - */ -bool StorageReplicatedMergeTree::tryExecute(const LogEntry & entry) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -/// Обмен кусками. - -void StorageReplicatedMergeTree::registerEndpoint() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -void StorageReplicatedMergeTree::unregisterEndpoint() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } - -String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } -void StorageReplicatedMergeTree::getPart(const String & name, const String & replica_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::drop() +{ + replica_is_active_node = nullptr; + zookeeper.removeRecursive(replica_path); + if (zookeeper.getChildren(zookeeper_path + "/replicas").empty()) + zookeeper.removeRecursive(zookeeper_path); +} } diff --git a/libs/libzkutil/include/zkutil/ZooKeeper.h b/libs/libzkutil/include/zkutil/ZooKeeper.h index 0c8c35cbec1..bb1005d5ff9 100644 --- a/libs/libzkutil/include/zkutil/ZooKeeper.h +++ b/libs/libzkutil/include/zkutil/ZooKeeper.h @@ -36,6 +36,8 @@ public: ZooKeeper(const Poco::Util::LayeredConfiguration & config, const std::string & config_name, WatchFunction * watch = nullptr); + ~ZooKeeper(); + /** Возвращает true, если сессия навсегда завершена. * Это возможно только если соединение было установлено, а потом разорвалось. Это достаточно редкая ситуация. * С другой стороны, если, например, указан неправильный сервер или порт, попытки соединения будут продолжаться бесконечно, @@ -73,7 +75,7 @@ public: bool exists(const std::string & path, Stat * stat = nullptr, WatchFuture * watch = nullptr); - std::string get(const std::string & path, Stat * stat, WatchFuture * watch); + std::string get(const std::string & path, Stat * stat = nullptr, WatchFuture * watch = nullptr); /** Не бросает исключение при следующих ошибках: * - Такой ноды нет. В таком случае возвращает false. @@ -108,9 +110,14 @@ public: OpResultsPtr multi(const Ops & ops); /** Бросает исключение только если какая-нибудь операция вернула "неожиданную" ошибку - такую ошибку, - * увидев которую соответствующий метод try* бросил бы исключение. */ + * увидев которую соответствующий метод try* бросил бы исключение. */ OpResultsPtr tryMulti(const Ops & ops); + + /** Удаляет ноду вместе с поддеревом. Если в это время кто-то добавит иили удалит ноду в поддереве, результат не определен. + */ + void removeRecursive(const std::string & path); + private: void init(const std::string & hosts, int32_t sessionTimeoutMs, WatchFunction * watch_); friend struct StateWatch; @@ -125,4 +132,50 @@ private: void stateChanged(WatchEvent::type event, SessionState::type state, const std::string& path); }; + +/** В конструкторе создает эфемерную ноду, в деструкторе - удаляет. + */ +class EphemeralNodeHolder +{ +public: + typedef Poco::SharedPtr Ptr; + + EphemeralNodeHolder(const std::string & path_, ZooKeeper & zookeeper_, bool create, bool sequential, const std::string & data) + : path(path_), zookeeper(zookeeper_) + { + if (create) + zookeeper.create(path, data, sequential ? CreateMode::EphemeralSequential : CreateMode::Ephemeral); + } + + static Ptr create(const std::string & path, ZooKeeper & zookeeper, const std::string & data = "") + { + return new EphemeralNodeHolder(path, zookeeper, true, false, data); + } + + static Ptr createSequential(const std::string & path, ZooKeeper & zookeeper, const std::string & data = "") + { + return new EphemeralNodeHolder(path, zookeeper, true, true, data); + } + + static Ptr existing(const std::string & path, ZooKeeper & zookeeper) + { + return new EphemeralNodeHolder(path, zookeeper, false, false, ""); + } + + ~EphemeralNodeHolder() + { + try + { + zookeeper.tryRemove(path); + } + catch (KeeperException) {} + } + +private: + std::string path; + ZooKeeper & zookeeper; +}; + +typedef EphemeralNodeHolder::Ptr EphemeralNodeHolderPtr; + } diff --git a/libs/libzkutil/src/ZooKeeper.cpp b/libs/libzkutil/src/ZooKeeper.cpp index f7c6c147b97..3e188e1b7a1 100644 --- a/libs/libzkutil/src/ZooKeeper.cpp +++ b/libs/libzkutil/src/ZooKeeper.cpp @@ -1,5 +1,6 @@ #include #include +#include #define CHECKED(x) { ReturnCode::type code = x; if (code != ReturnCode::Ok) throw KeeperException(code); } @@ -302,9 +303,29 @@ OpResultsPtr ZooKeeper::tryMulti(const Ops & ops) return res; } +void ZooKeeper::removeRecursive(const std::string & path) +{ + Strings children = getChildren(path); + for (const std::string & child : children) + removeRecursive(path + "/" + child); + remove(path); +} + void ZooKeeper::close() { CHECKED(impl.close()); } +ZooKeeper::~ZooKeeper() +{ + try + { + close(); + } + catch(...) + { + LOG_ERROR(&Logger::get("~ZooKeeper"), "Failed to close ZooKeeper session"); + } +} + } From 2ff699ce6f81dceb579e416cbdee1970b97bff14 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 22 Mar 2014 22:27:19 +0400 Subject: [PATCH 036/281] Merge From 40b02cae63cfca6fcab8d61329b8c92e9e0e9f9e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 07:26:07 +0400 Subject: [PATCH 037/281] dbms: added test script [#METR-2944]. --- dbms/tests/clickhouse-test | 66 +++++++++++++++++++ .../queries/format_ast_and_remote_table.sql | 1 + dbms/tests/queries/select_1.reference | 1 + dbms/tests/queries/select_1.sql | 1 + dbms/tests/queries/system_numbers.reference | 10 +++ dbms/tests/queries/system_numbers.sql | 1 + 6 files changed, 80 insertions(+) create mode 100755 dbms/tests/clickhouse-test create mode 100644 dbms/tests/queries/format_ast_and_remote_table.sql create mode 100644 dbms/tests/queries/select_1.reference create mode 100644 dbms/tests/queries/select_1.sql create mode 100644 dbms/tests/queries/system_numbers.reference create mode 100644 dbms/tests/queries/system_numbers.sql diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test new file mode 100755 index 00000000000..40986eb6e52 --- /dev/null +++ b/dbms/tests/clickhouse-test @@ -0,0 +1,66 @@ +#!/bin/bash + +# Скрипт для тестирования запросов к ClickHouse. +# Из файлов *.sql в заданной директории, в алфавитном порядке, отправляются все запросы. +# Результат сравнивается с эталоном. + +QUERIES_DIR="./queries" +CLIENT_PROGRAM="clickhouse-client" + + +COLOR_RESET="\033[0m" +COLOR_WHITE="\033[1;37m" +COLOR_FAIL="\033[1;31m" +COLOR_UNKNOWN="\033[1;30m" +COLOR_OK="\033[1;32m" + +MSG_FAIL="${COLOR_WHITE}[ ${COLOR_FAIL}FAIL${COLOR_WHITE} ]${COLOR_RESET}" +MSG_UNKNOWN="${COLOR_WHITE}[ ${COLOR_UNKNOWN}UNKNOWN${COLOR_WHITE} ]${COLOR_RESET}" +MSG_OK="${COLOR_WHITE}[ ${COLOR_OK}OK${COLOR_WHITE} ]${COLOR_RESET}" +MSG_GENERATED="${COLOR_WHITE}[ ${COLOR_UNKNOWN}GENERATED${COLOR_WHITE} ]${COLOR_RESET}" + + +for query_file in $(ls $QUERIES_DIR/*.sql) +do + test_name=$(basename -s .sql $query_file) + + result_file=$QUERIES_DIR/$test_name.result + error_file=$QUERIES_DIR/$test_name.error + reference_file=$QUERIES_DIR/$test_name.reference + diff_file=$QUERIES_DIR/$test_name.diff + + printf "%-30s" "$test_name: " + + $CLIENT_PROGRAM < $query_file > $result_file 2> $error_file + ret_code=$? + + if [ $ret_code -ne 0 ]; then + echo -e "$MSG_FAIL - return code $ret_code" + if [ -s "$error_file" ]; then + cat $error_file + fi + # разорвано соединение с сервером + if grep -q -E "Connection refused|Attempt to read after eof" $error_file; then + exit 1; + fi + elif [ -s "$error_file" ]; then + echo -e "$MSG_FAIL - having stderror:" + cat $error_file + elif [ ! -e "$reference_file" ]; then + # надо сгенерировать эталонный результат + if [ "$1" == "--generate" ]; then + cp $result_file $reference_file + echo -e "$MSG_GENERATED - no reference file" + else + echo -e "$MSG_UNKNOWN - no reference file (use --generate to create)" + fi + else + diff $reference_file $result_file > $diff_file + if [ -s "$diff_file" ]; then + echo -e "$MSG_FAIL - result differs with reference:" + cat $diff_file + else + echo -e "$MSG_OK" + fi + fi +done diff --git a/dbms/tests/queries/format_ast_and_remote_table.sql b/dbms/tests/queries/format_ast_and_remote_table.sql new file mode 100644 index 00000000000..dd20fd1d8e4 --- /dev/null +++ b/dbms/tests/queries/format_ast_and_remote_table.sql @@ -0,0 +1 @@ +SELECT (dummy AS x) - 1 FROM remote('127.0.0.{1,2}', system, one) diff --git a/dbms/tests/queries/select_1.reference b/dbms/tests/queries/select_1.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/dbms/tests/queries/select_1.reference @@ -0,0 +1 @@ +1 diff --git a/dbms/tests/queries/select_1.sql b/dbms/tests/queries/select_1.sql new file mode 100644 index 00000000000..2e3761f7a2c --- /dev/null +++ b/dbms/tests/queries/select_1.sql @@ -0,0 +1 @@ +SELECT 1 diff --git a/dbms/tests/queries/system_numbers.reference b/dbms/tests/queries/system_numbers.reference new file mode 100644 index 00000000000..8b1acc12b63 --- /dev/null +++ b/dbms/tests/queries/system_numbers.reference @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/dbms/tests/queries/system_numbers.sql b/dbms/tests/queries/system_numbers.sql new file mode 100644 index 00000000000..bc9269495bc --- /dev/null +++ b/dbms/tests/queries/system_numbers.sql @@ -0,0 +1 @@ +SELECT * FROM system.numbers LIMIT 10 From 18a7749a0491cc84a7185812fb14293f443a6ae5 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 07:37:51 +0400 Subject: [PATCH 038/281] dbms: modified test [#METR-2944]. --- dbms/tests/clickhouse-test | 1 + 1 file changed, 1 insertion(+) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 40986eb6e52..cfd6c85f82b 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -61,6 +61,7 @@ do cat $diff_file else echo -e "$MSG_OK" + rm $error_file $result_file $diff_file fi fi done From 84ce7d62258a077b0b738cb0498632236520d89b Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 07:40:09 +0400 Subject: [PATCH 039/281] dbms: modified test [#METR-2944]. --- dbms/tests/clickhouse-test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index cfd6c85f82b..2b7bec67bdc 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -19,6 +19,8 @@ MSG_UNKNOWN="${COLOR_WHITE}[ ${COLOR_UNKNOWN}UNKNOWN${COLOR_WHITE} ]${COLOR_RESE MSG_OK="${COLOR_WHITE}[ ${COLOR_OK}OK${COLOR_WHITE} ]${COLOR_RESET}" MSG_GENERATED="${COLOR_WHITE}[ ${COLOR_UNKNOWN}GENERATED${COLOR_WHITE} ]${COLOR_RESET}" +HAS_ERROR=0 + for query_file in $(ls $QUERIES_DIR/*.sql) do @@ -35,6 +37,7 @@ do ret_code=$? if [ $ret_code -ne 0 ]; then + HAS_ERROR=1 echo -e "$MSG_FAIL - return code $ret_code" if [ -s "$error_file" ]; then cat $error_file @@ -44,6 +47,7 @@ do exit 1; fi elif [ -s "$error_file" ]; then + HAS_ERROR=1 echo -e "$MSG_FAIL - having stderror:" cat $error_file elif [ ! -e "$reference_file" ]; then @@ -57,6 +61,7 @@ do else diff $reference_file $result_file > $diff_file if [ -s "$diff_file" ]; then + HAS_ERROR=1 echo -e "$MSG_FAIL - result differs with reference:" cat $diff_file else @@ -65,3 +70,5 @@ do fi fi done + +exit $HAS_ERROR From a646848ded048582bd83c019af0471f50d2aba9d Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 07:46:25 +0400 Subject: [PATCH 040/281] dbms: modified test [#METR-2944]. --- dbms/tests/clickhouse-test | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 2b7bec67bdc..5b853748301 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -19,7 +19,7 @@ MSG_UNKNOWN="${COLOR_WHITE}[ ${COLOR_UNKNOWN}UNKNOWN${COLOR_WHITE} ]${COLOR_RESE MSG_OK="${COLOR_WHITE}[ ${COLOR_OK}OK${COLOR_WHITE} ]${COLOR_RESET}" MSG_GENERATED="${COLOR_WHITE}[ ${COLOR_UNKNOWN}GENERATED${COLOR_WHITE} ]${COLOR_RESET}" -HAS_ERROR=0 +ERRORS=0 for query_file in $(ls $QUERIES_DIR/*.sql) @@ -37,7 +37,7 @@ do ret_code=$? if [ $ret_code -ne 0 ]; then - HAS_ERROR=1 + ERRORS=$(($ERRORS + 1)) echo -e "$MSG_FAIL - return code $ret_code" if [ -s "$error_file" ]; then cat $error_file @@ -47,7 +47,7 @@ do exit 1; fi elif [ -s "$error_file" ]; then - HAS_ERROR=1 + ERRORS=$(($ERRORS + 1)) echo -e "$MSG_FAIL - having stderror:" cat $error_file elif [ ! -e "$reference_file" ]; then @@ -61,7 +61,7 @@ do else diff $reference_file $result_file > $diff_file if [ -s "$diff_file" ]; then - HAS_ERROR=1 + ERRORS=$(($ERRORS + 1)) echo -e "$MSG_FAIL - result differs with reference:" cat $diff_file else @@ -71,4 +71,13 @@ do fi done -exit $HAS_ERROR + +echo + +if [ $ERRORS -gt 0 ]; then + echo -e "${COLOR_FAIL}Having $ERRORS errors!${COLOR_RESET}" + exit 1 +else + echo -e "${COLOR_OK}All tests succeeded.${COLOR_RESET}" + exit 0 +fi From 93472c9103e736dc583401e1204fd50aa6e9e529 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 19:08:31 +0400 Subject: [PATCH 041/281] dbms: added test [#METR-2944]. --- dbms/tests/queries/format_ast_and_remote_table.reference | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 dbms/tests/queries/format_ast_and_remote_table.reference diff --git a/dbms/tests/queries/format_ast_and_remote_table.reference b/dbms/tests/queries/format_ast_and_remote_table.reference new file mode 100644 index 00000000000..343ee5c2f6c --- /dev/null +++ b/dbms/tests/queries/format_ast_and_remote_table.reference @@ -0,0 +1,2 @@ +-1 +-1 From fc45d3cc5b6b322f8683055adc2c98ac7fe91d6e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 23 Mar 2014 19:16:58 +0400 Subject: [PATCH 042/281] dbms: mofified test [#METR-2944]. --- dbms/tests/clickhouse-test | 4 ++-- .../queries/{select_1.reference => 00001_select_1.reference} | 0 dbms/tests/queries/{select_1.sql => 00001_select_1.sql} | 0 ...ystem_numbers.reference => 00002_system_numbers.reference} | 0 .../queries/{system_numbers.sql => 00002_system_numbers.sql} | 0 dbms/tests/queries/00003_reinterpret_as_string.reference | 1 + dbms/tests/queries/00003_reinterpret_as_string.sql | 1 + ....reference => 00004_format_ast_and_remote_table.reference} | 0 ...remote_table.sql => 00004_format_ast_and_remote_table.sql} | 0 9 files changed, 4 insertions(+), 2 deletions(-) rename dbms/tests/queries/{select_1.reference => 00001_select_1.reference} (100%) rename dbms/tests/queries/{select_1.sql => 00001_select_1.sql} (100%) rename dbms/tests/queries/{system_numbers.reference => 00002_system_numbers.reference} (100%) rename dbms/tests/queries/{system_numbers.sql => 00002_system_numbers.sql} (100%) create mode 100644 dbms/tests/queries/00003_reinterpret_as_string.reference create mode 100644 dbms/tests/queries/00003_reinterpret_as_string.sql rename dbms/tests/queries/{format_ast_and_remote_table.reference => 00004_format_ast_and_remote_table.reference} (100%) rename dbms/tests/queries/{format_ast_and_remote_table.sql => 00004_format_ast_and_remote_table.sql} (100%) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 5b853748301..60d66269645 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -2,7 +2,7 @@ # Скрипт для тестирования запросов к ClickHouse. # Из файлов *.sql в заданной директории, в алфавитном порядке, отправляются все запросы. -# Результат сравнивается с эталоном. +# Результаты сравниваются с эталонами. QUERIES_DIR="./queries" CLIENT_PROGRAM="clickhouse-client" @@ -31,7 +31,7 @@ do reference_file=$QUERIES_DIR/$test_name.reference diff_file=$QUERIES_DIR/$test_name.diff - printf "%-30s" "$test_name: " + printf "%-60s" "$test_name: " $CLIENT_PROGRAM < $query_file > $result_file 2> $error_file ret_code=$? diff --git a/dbms/tests/queries/select_1.reference b/dbms/tests/queries/00001_select_1.reference similarity index 100% rename from dbms/tests/queries/select_1.reference rename to dbms/tests/queries/00001_select_1.reference diff --git a/dbms/tests/queries/select_1.sql b/dbms/tests/queries/00001_select_1.sql similarity index 100% rename from dbms/tests/queries/select_1.sql rename to dbms/tests/queries/00001_select_1.sql diff --git a/dbms/tests/queries/system_numbers.reference b/dbms/tests/queries/00002_system_numbers.reference similarity index 100% rename from dbms/tests/queries/system_numbers.reference rename to dbms/tests/queries/00002_system_numbers.reference diff --git a/dbms/tests/queries/system_numbers.sql b/dbms/tests/queries/00002_system_numbers.sql similarity index 100% rename from dbms/tests/queries/system_numbers.sql rename to dbms/tests/queries/00002_system_numbers.sql diff --git a/dbms/tests/queries/00003_reinterpret_as_string.reference b/dbms/tests/queries/00003_reinterpret_as_string.reference new file mode 100644 index 00000000000..fc75583cf02 --- /dev/null +++ b/dbms/tests/queries/00003_reinterpret_as_string.reference @@ -0,0 +1 @@ +33232 diff --git a/dbms/tests/queries/00003_reinterpret_as_string.sql b/dbms/tests/queries/00003_reinterpret_as_string.sql new file mode 100644 index 00000000000..1204f6280f2 --- /dev/null +++ b/dbms/tests/queries/00003_reinterpret_as_string.sql @@ -0,0 +1 @@ +SELECT number FROM system.numbers WHERE reinterpretAsString(number) = 'Ё' LIMIT 1 diff --git a/dbms/tests/queries/format_ast_and_remote_table.reference b/dbms/tests/queries/00004_format_ast_and_remote_table.reference similarity index 100% rename from dbms/tests/queries/format_ast_and_remote_table.reference rename to dbms/tests/queries/00004_format_ast_and_remote_table.reference diff --git a/dbms/tests/queries/format_ast_and_remote_table.sql b/dbms/tests/queries/00004_format_ast_and_remote_table.sql similarity index 100% rename from dbms/tests/queries/format_ast_and_remote_table.sql rename to dbms/tests/queries/00004_format_ast_and_remote_table.sql From dae349ba00069620d212efb44160869eeff75947 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 00:40:55 +0400 Subject: [PATCH 043/281] dbms: added tests [#METR-2944]. --- dbms/tests/clickhouse-test | 6 +++++- ...ormat_ast_and_remote_table_lambda.reference | 1 + ...0005_format_ast_and_remote_table_lambda.sql | 1 + .../queries/00006_1_set_extremes.reference | 0 dbms/tests/queries/00006_1_set_extremes.sql | 1 + ...0006_2_extremes_and_subquery_from.reference | 18 ++++++++++++++++++ .../00006_2_extremes_and_subquery_from.sql | 2 ++ .../queries/00006_3_unset_extremes.reference | 0 dbms/tests/queries/00006_3_unset_extremes.sql | 1 + dbms/tests/queries/00007_array.reference | 1 + dbms/tests/queries/00007_array.sql | 1 + dbms/tests/queries/00008_array_join.reference | 2 ++ dbms/tests/queries/00008_array_join.sql | 1 + .../00009_array_join_subquery.reference | 2 ++ .../queries/00009_array_join_subquery.sql | 1 + .../queries/00010_big_array_join.reference | 6 ++++++ dbms/tests/queries/00010_big_array_join.sql | 1 + .../queries/00011_array_join_alias.reference | 6 ++++++ dbms/tests/queries/00011_array_join_alias.sql | 1 + .../queries/00012_array_join_alias_2.reference | 6 ++++++ .../tests/queries/00012_array_join_alias_2.sql | 1 + ..._drop_if_exists_table_with_arrays.reference | 0 ...0013_1_drop_if_exists_table_with_arrays.sql | 1 + .../00013_2_create_table_with_arrays.reference | 0 .../00013_2_create_table_with_arrays.sql | 1 + ...3_3_insert_into_table_with_arrays.reference | 0 .../00013_3_insert_into_table_with_arrays.sql | 1 + ...3_4_select_from_table_with_arrays.reference | 3 +++ .../00013_4_select_from_table_with_arrays.sql | 1 + ...3_5_select_from_table_with_arrays.reference | 5 +++++ .../00013_5_select_from_table_with_arrays.sql | 1 + ...3_6_select_from_table_with_arrays.reference | 5 +++++ .../00013_6_select_from_table_with_arrays.sql | 1 + ...3_7_select_from_table_with_arrays.reference | 5 +++++ .../00013_7_select_from_table_with_arrays.sql | 1 + ...3_8_select_from_table_with_arrays.reference | 5 +++++ .../00013_8_select_from_table_with_arrays.sql | 1 + ...3_9_select_from_table_with_arrays.reference | 5 +++++ .../00013_9_select_from_table_with_arrays.sql | 1 + ...3_a_select_from_table_with_arrays.reference | 5 +++++ .../00013_a_select_from_table_with_arrays.sql | 1 + ..._drop_if_exists_table_with_nested.reference | 0 ...0014_1_drop_if_exists_table_with_nested.sql | 1 + .../00014_2_create_table_with_nested.reference | 0 .../00014_2_create_table_with_nested.sql | 1 + ...4_3_insert_into_table_with_nested.reference | 0 .../00014_3_insert_into_table_with_nested.sql | 1 + ...4_4_select_from_table_with_nested.reference | 3 +++ .../00014_4_select_from_table_with_nested.sql | 1 + ...4_5_select_from_table_with_nested.reference | 5 +++++ .../00014_5_select_from_table_with_nested.sql | 1 + ...4_6_select_from_table_with_nested.reference | 5 +++++ .../00014_6_select_from_table_with_nested.sql | 1 + ...4_7_select_from_table_with_nested.reference | 5 +++++ .../00014_7_select_from_table_with_nested.sql | 1 + ...4_9_select_from_table_with_nested.reference | 5 +++++ .../00014_9_select_from_table_with_nested.sql | 1 + ...4_a_select_from_table_with_nested.reference | 5 +++++ .../00014_a_select_from_table_with_nested.sql | 1 + ...4_b_select_from_table_with_nested.reference | 5 +++++ .../00014_b_select_from_table_with_nested.sql | 1 + ...4_c_select_from_table_with_nested.reference | 5 +++++ .../00014_c_select_from_table_with_nested.sql | 1 + .../00015_totals_having_constants.reference | 12 ++++++++++++ .../queries/00015_totals_having_constants.sql | 1 + .../00016_totals_having_constants.reference | 3 +++ .../queries/00016_totals_having_constants.sql | 1 + ...017_in_subquery_with_empty_result.reference | 18 ++++++++++++++++++ .../00017_in_subquery_with_empty_result.sql | 2 ++ .../00018_distinct_in_subquery.reference | 2 ++ .../queries/00018_distinct_in_subquery.sql | 1 + 71 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference create mode 100644 dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql create mode 100644 dbms/tests/queries/00006_1_set_extremes.reference create mode 100644 dbms/tests/queries/00006_1_set_extremes.sql create mode 100644 dbms/tests/queries/00006_2_extremes_and_subquery_from.reference create mode 100644 dbms/tests/queries/00006_2_extremes_and_subquery_from.sql create mode 100644 dbms/tests/queries/00006_3_unset_extremes.reference create mode 100644 dbms/tests/queries/00006_3_unset_extremes.sql create mode 100644 dbms/tests/queries/00007_array.reference create mode 100644 dbms/tests/queries/00007_array.sql create mode 100644 dbms/tests/queries/00008_array_join.reference create mode 100644 dbms/tests/queries/00008_array_join.sql create mode 100644 dbms/tests/queries/00009_array_join_subquery.reference create mode 100644 dbms/tests/queries/00009_array_join_subquery.sql create mode 100644 dbms/tests/queries/00010_big_array_join.reference create mode 100644 dbms/tests/queries/00010_big_array_join.sql create mode 100644 dbms/tests/queries/00011_array_join_alias.reference create mode 100644 dbms/tests/queries/00011_array_join_alias.sql create mode 100644 dbms/tests/queries/00012_array_join_alias_2.reference create mode 100644 dbms/tests/queries/00012_array_join_alias_2.sql create mode 100644 dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_2_create_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_2_create_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_3_insert_into_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_4_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_4_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_5_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_5_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_6_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_6_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_7_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_7_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_8_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_8_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_9_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_9_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00013_a_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/00013_a_select_from_table_with_arrays.sql create mode 100644 dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_2_create_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_2_create_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_3_insert_into_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_3_insert_into_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_4_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_4_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_5_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_5_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_6_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_6_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_7_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_7_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_9_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_9_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_a_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_a_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_b_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_b_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00014_c_select_from_table_with_nested.reference create mode 100644 dbms/tests/queries/00014_c_select_from_table_with_nested.sql create mode 100644 dbms/tests/queries/00015_totals_having_constants.reference create mode 100644 dbms/tests/queries/00015_totals_having_constants.sql create mode 100644 dbms/tests/queries/00016_totals_having_constants.reference create mode 100644 dbms/tests/queries/00016_totals_having_constants.sql create mode 100644 dbms/tests/queries/00017_in_subquery_with_empty_result.reference create mode 100644 dbms/tests/queries/00017_in_subquery_with_empty_result.sql create mode 100644 dbms/tests/queries/00018_distinct_in_subquery.reference create mode 100644 dbms/tests/queries/00018_distinct_in_subquery.sql diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 60d66269645..1bfd91ab468 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -5,7 +5,7 @@ # Результаты сравниваются с эталонами. QUERIES_DIR="./queries" -CLIENT_PROGRAM="clickhouse-client" +CLIENT_PROGRAM="curl -sS http://localhost:8123/ --data-binary @-" COLOR_RESET="\033[0m" @@ -50,6 +50,10 @@ do ERRORS=$(($ERRORS + 1)) echo -e "$MSG_FAIL - having stderror:" cat $error_file + elif grep -q "Exception" $result_file; then + ERRORS=$(($ERRORS + 1)) + echo -e "$MSG_FAIL - having exception:" + cat $result_file elif [ ! -e "$reference_file" ]; then # надо сгенерировать эталонный результат if [ "$1" == "--generate" ]; then diff --git a/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference b/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference new file mode 100644 index 00000000000..0cfbf08886f --- /dev/null +++ b/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference @@ -0,0 +1 @@ +2 diff --git a/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql b/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql new file mode 100644 index 00000000000..fb401df5184 --- /dev/null +++ b/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql @@ -0,0 +1 @@ +SELECT count() FROM remote('127.0.0.{1,2}', system, one) WHERE arrayExists((x) -> x = 1, [1, 2, 3]) diff --git a/dbms/tests/queries/00006_1_set_extremes.reference b/dbms/tests/queries/00006_1_set_extremes.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00006_1_set_extremes.sql b/dbms/tests/queries/00006_1_set_extremes.sql new file mode 100644 index 00000000000..53bd0c96ce2 --- /dev/null +++ b/dbms/tests/queries/00006_1_set_extremes.sql @@ -0,0 +1 @@ +SET GLOBAL extremes = 1 diff --git a/dbms/tests/queries/00006_2_extremes_and_subquery_from.reference b/dbms/tests/queries/00006_2_extremes_and_subquery_from.reference new file mode 100644 index 00000000000..841ae17708f --- /dev/null +++ b/dbms/tests/queries/00006_2_extremes_and_subquery_from.reference @@ -0,0 +1,18 @@ +{ + "meta": + [ + { + "name": "'Hello, world'", + "type": "String" + } + ], + + "data": + [ + + ], + + "rows": 0, + + "rows_before_limit_at_least": 10 +} diff --git a/dbms/tests/queries/00006_2_extremes_and_subquery_from.sql b/dbms/tests/queries/00006_2_extremes_and_subquery_from.sql new file mode 100644 index 00000000000..c8e1e1bf66f --- /dev/null +++ b/dbms/tests/queries/00006_2_extremes_and_subquery_from.sql @@ -0,0 +1,2 @@ +SELECT 'Hello, world' FROM (SELECT number FROM system.numbers LIMIT 10) WHERE number < 0 +FORMAT JSONCompact \ No newline at end of file diff --git a/dbms/tests/queries/00006_3_unset_extremes.reference b/dbms/tests/queries/00006_3_unset_extremes.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00006_3_unset_extremes.sql b/dbms/tests/queries/00006_3_unset_extremes.sql new file mode 100644 index 00000000000..31e8a97da82 --- /dev/null +++ b/dbms/tests/queries/00006_3_unset_extremes.sql @@ -0,0 +1 @@ +SET GLOBAL extremes = 0 diff --git a/dbms/tests/queries/00007_array.reference b/dbms/tests/queries/00007_array.reference new file mode 100644 index 00000000000..2a64c8ea7b2 --- /dev/null +++ b/dbms/tests/queries/00007_array.reference @@ -0,0 +1 @@ +['Hello','Goodbye'] diff --git a/dbms/tests/queries/00007_array.sql b/dbms/tests/queries/00007_array.sql new file mode 100644 index 00000000000..7c1f27f1978 --- /dev/null +++ b/dbms/tests/queries/00007_array.sql @@ -0,0 +1 @@ +SELECT ['Hello', 'Goodbye'] diff --git a/dbms/tests/queries/00008_array_join.reference b/dbms/tests/queries/00008_array_join.reference new file mode 100644 index 00000000000..c86756d1938 --- /dev/null +++ b/dbms/tests/queries/00008_array_join.reference @@ -0,0 +1,2 @@ +Hello +Goodbye diff --git a/dbms/tests/queries/00008_array_join.sql b/dbms/tests/queries/00008_array_join.sql new file mode 100644 index 00000000000..abb35cbbfc8 --- /dev/null +++ b/dbms/tests/queries/00008_array_join.sql @@ -0,0 +1 @@ +SELECT arrayJoin(['Hello', 'Goodbye']) diff --git a/dbms/tests/queries/00009_array_join_subquery.reference b/dbms/tests/queries/00009_array_join_subquery.reference new file mode 100644 index 00000000000..c86756d1938 --- /dev/null +++ b/dbms/tests/queries/00009_array_join_subquery.reference @@ -0,0 +1,2 @@ +Hello +Goodbye diff --git a/dbms/tests/queries/00009_array_join_subquery.sql b/dbms/tests/queries/00009_array_join_subquery.sql new file mode 100644 index 00000000000..378baadd026 --- /dev/null +++ b/dbms/tests/queries/00009_array_join_subquery.sql @@ -0,0 +1 @@ +SELECT x FROM (SELECT arrayJoin(['Hello', 'Goodbye']) AS x) diff --git a/dbms/tests/queries/00010_big_array_join.reference b/dbms/tests/queries/00010_big_array_join.reference new file mode 100644 index 00000000000..e9c80bae6d0 --- /dev/null +++ b/dbms/tests/queries/00010_big_array_join.reference @@ -0,0 +1,6 @@ +Hello +Hello +Hello +Goodbye +Goodbye +Goodbye diff --git a/dbms/tests/queries/00010_big_array_join.sql b/dbms/tests/queries/00010_big_array_join.sql new file mode 100644 index 00000000000..f7b9160b578 --- /dev/null +++ b/dbms/tests/queries/00010_big_array_join.sql @@ -0,0 +1 @@ +SELECT x FROM (SELECT arrayJoin(['Hello', 'Goodbye']) AS x, [1, 2, 3] AS arr) ARRAY JOIN arr diff --git a/dbms/tests/queries/00011_array_join_alias.reference b/dbms/tests/queries/00011_array_join_alias.reference new file mode 100644 index 00000000000..ff5ad16d763 --- /dev/null +++ b/dbms/tests/queries/00011_array_join_alias.reference @@ -0,0 +1,6 @@ +Hello 1 +Hello 2 +Hello 3 +Goodbye 1 +Goodbye 2 +Goodbye 3 diff --git a/dbms/tests/queries/00011_array_join_alias.sql b/dbms/tests/queries/00011_array_join_alias.sql new file mode 100644 index 00000000000..228038c1509 --- /dev/null +++ b/dbms/tests/queries/00011_array_join_alias.sql @@ -0,0 +1 @@ +SELECT x, a FROM (SELECT arrayJoin(['Hello', 'Goodbye']) AS x, [1, 2, 3] AS arr) ARRAY JOIN arr AS a diff --git a/dbms/tests/queries/00012_array_join_alias_2.reference b/dbms/tests/queries/00012_array_join_alias_2.reference new file mode 100644 index 00000000000..ebad7b98d93 --- /dev/null +++ b/dbms/tests/queries/00012_array_join_alias_2.reference @@ -0,0 +1,6 @@ +Hello 1 [1,2,3] +Hello 2 [1,2,3] +Hello 3 [1,2,3] +Goodbye 1 [1,2,3] +Goodbye 2 [1,2,3] +Goodbye 3 [1,2,3] diff --git a/dbms/tests/queries/00012_array_join_alias_2.sql b/dbms/tests/queries/00012_array_join_alias_2.sql new file mode 100644 index 00000000000..a45cf2d87b8 --- /dev/null +++ b/dbms/tests/queries/00012_array_join_alias_2.sql @@ -0,0 +1 @@ +SELECT x, a, arr FROM (SELECT arrayJoin(['Hello', 'Goodbye']) AS x, [1, 2, 3] AS arr) ARRAY JOIN arr AS a diff --git a/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.reference b/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql b/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql new file mode 100644 index 00000000000..8dbef3a0abb --- /dev/null +++ b/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS arrays_test diff --git a/dbms/tests/queries/00013_2_create_table_with_arrays.reference b/dbms/tests/queries/00013_2_create_table_with_arrays.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00013_2_create_table_with_arrays.sql b/dbms/tests/queries/00013_2_create_table_with_arrays.sql new file mode 100644 index 00000000000..8585ba14140 --- /dev/null +++ b/dbms/tests/queries/00013_2_create_table_with_arrays.sql @@ -0,0 +1 @@ +CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory diff --git a/dbms/tests/queries/00013_3_insert_into_table_with_arrays.reference b/dbms/tests/queries/00013_3_insert_into_table_with_arrays.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql b/dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql new file mode 100644 index 00000000000..5b41e56db66 --- /dev/null +++ b/dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql @@ -0,0 +1 @@ +INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []) diff --git a/dbms/tests/queries/00013_4_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_4_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..5a5af40fc00 --- /dev/null +++ b/dbms/tests/queries/00013_4_select_from_table_with_arrays.reference @@ -0,0 +1,3 @@ +Hello [1,2] +World [3,4,5] +Goodbye [] diff --git a/dbms/tests/queries/00013_4_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_4_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..af4b5dd8538 --- /dev/null +++ b/dbms/tests/queries/00013_4_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT * FROM arrays_test diff --git a/dbms/tests/queries/00013_5_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_5_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..02ff6407fd6 --- /dev/null +++ b/dbms/tests/queries/00013_5_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello 1 +Hello 2 +World 3 +World 4 +World 5 diff --git a/dbms/tests/queries/00013_5_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_5_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..238f888202d --- /dev/null +++ b/dbms/tests/queries/00013_5_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr FROM arrays_test ARRAY JOIN arr diff --git a/dbms/tests/queries/00013_6_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_6_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..45d362a350e --- /dev/null +++ b/dbms/tests/queries/00013_6_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello [1,2] 1 +Hello [1,2] 2 +World [3,4,5] 3 +World [3,4,5] 4 +World [3,4,5] 5 diff --git a/dbms/tests/queries/00013_6_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_6_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..d1c9936af30 --- /dev/null +++ b/dbms/tests/queries/00013_6_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr, a FROM arrays_test ARRAY JOIN arr AS a \ No newline at end of file diff --git a/dbms/tests/queries/00013_7_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_7_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..577260f94f0 --- /dev/null +++ b/dbms/tests/queries/00013_7_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello [1,2] 1 1 +Hello [1,2] 2 2 +World [3,4,5] 3 1 +World [3,4,5] 4 2 +World [3,4,5] 5 3 diff --git a/dbms/tests/queries/00013_7_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_7_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..a75b97e6faa --- /dev/null +++ b/dbms/tests/queries/00013_7_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr, a, num FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num \ No newline at end of file diff --git a/dbms/tests/queries/00013_8_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_8_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..e29fe6d82a1 --- /dev/null +++ b/dbms/tests/queries/00013_8_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello [1,2] 1 1 [1,2] +Hello [1,2] 2 2 [1,2] +World [3,4,5] 3 1 [1,2,3] +World [3,4,5] 4 2 [1,2,3] +World [3,4,5] 5 3 [1,2,3] diff --git a/dbms/tests/queries/00013_8_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_8_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..d7b3a7b44b9 --- /dev/null +++ b/dbms/tests/queries/00013_8_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr, a, num, arrayEnumerate(arr) FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num \ No newline at end of file diff --git a/dbms/tests/queries/00013_9_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_9_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..58328ca2588 --- /dev/null +++ b/dbms/tests/queries/00013_9_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello [1,2] 1 2 +Hello [1,2] 2 3 +World [3,4,5] 3 4 +World [3,4,5] 4 5 +World [3,4,5] 5 6 diff --git a/dbms/tests/queries/00013_9_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_9_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..010faea3d76 --- /dev/null +++ b/dbms/tests/queries/00013_9_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr, a, mapped FROM arrays_test ARRAY JOIN arr AS a, arrayMap(x -> x + 1, arr) AS mapped diff --git a/dbms/tests/queries/00013_a_select_from_table_with_arrays.reference b/dbms/tests/queries/00013_a_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..14686f65be3 --- /dev/null +++ b/dbms/tests/queries/00013_a_select_from_table_with_arrays.reference @@ -0,0 +1,5 @@ +Hello [1,2] 1 1 2 +Hello [1,2] 2 2 3 +World [3,4,5] 3 1 4 +World [3,4,5] 4 2 5 +World [3,4,5] 5 3 6 diff --git a/dbms/tests/queries/00013_a_select_from_table_with_arrays.sql b/dbms/tests/queries/00013_a_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..1c4b2b02e19 --- /dev/null +++ b/dbms/tests/queries/00013_a_select_from_table_with_arrays.sql @@ -0,0 +1 @@ +SELECT s, arr, a, num, mapped FROM arrays_test ARRAY JOIN arr AS a, arrayEnumerate(arr) AS num, arrayMap(x -> x + 1, arr) AS mapped diff --git a/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.reference b/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql b/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql new file mode 100644 index 00000000000..257afc58919 --- /dev/null +++ b/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS nested_test diff --git a/dbms/tests/queries/00014_2_create_table_with_nested.reference b/dbms/tests/queries/00014_2_create_table_with_nested.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00014_2_create_table_with_nested.sql b/dbms/tests/queries/00014_2_create_table_with_nested.sql new file mode 100644 index 00000000000..49f015ce1aa --- /dev/null +++ b/dbms/tests/queries/00014_2_create_table_with_nested.sql @@ -0,0 +1 @@ +CREATE TABLE nested_test (s String, nest Nested(x UInt8, y UInt32)) ENGINE = Memory diff --git a/dbms/tests/queries/00014_3_insert_into_table_with_nested.reference b/dbms/tests/queries/00014_3_insert_into_table_with_nested.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/00014_3_insert_into_table_with_nested.sql b/dbms/tests/queries/00014_3_insert_into_table_with_nested.sql new file mode 100644 index 00000000000..44fdbc820ff --- /dev/null +++ b/dbms/tests/queries/00014_3_insert_into_table_with_nested.sql @@ -0,0 +1 @@ +INSERT INTO nested_test VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], []) diff --git a/dbms/tests/queries/00014_4_select_from_table_with_nested.reference b/dbms/tests/queries/00014_4_select_from_table_with_nested.reference new file mode 100644 index 00000000000..32bd08bad02 --- /dev/null +++ b/dbms/tests/queries/00014_4_select_from_table_with_nested.reference @@ -0,0 +1,3 @@ +Hello [1,2] [10,20] +World [3,4,5] [30,40,50] +Goodbye [] [] diff --git a/dbms/tests/queries/00014_4_select_from_table_with_nested.sql b/dbms/tests/queries/00014_4_select_from_table_with_nested.sql new file mode 100644 index 00000000000..509911959ac --- /dev/null +++ b/dbms/tests/queries/00014_4_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT * FROM nested_test diff --git a/dbms/tests/queries/00014_5_select_from_table_with_nested.reference b/dbms/tests/queries/00014_5_select_from_table_with_nested.reference new file mode 100644 index 00000000000..05200edd33b --- /dev/null +++ b/dbms/tests/queries/00014_5_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 +Hello 2 20 +World 3 30 +World 4 40 +World 5 50 diff --git a/dbms/tests/queries/00014_5_select_from_table_with_nested.sql b/dbms/tests/queries/00014_5_select_from_table_with_nested.sql new file mode 100644 index 00000000000..31e1ac1b05e --- /dev/null +++ b/dbms/tests/queries/00014_5_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest diff --git a/dbms/tests/queries/00014_6_select_from_table_with_nested.reference b/dbms/tests/queries/00014_6_select_from_table_with_nested.reference new file mode 100644 index 00000000000..8ea4568b73c --- /dev/null +++ b/dbms/tests/queries/00014_6_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 [10,20] +Hello 2 [10,20] +World 3 [30,40,50] +World 4 [30,40,50] +World 5 [30,40,50] diff --git a/dbms/tests/queries/00014_6_select_from_table_with_nested.sql b/dbms/tests/queries/00014_6_select_from_table_with_nested.sql new file mode 100644 index 00000000000..3648cb4852c --- /dev/null +++ b/dbms/tests/queries/00014_6_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x diff --git a/dbms/tests/queries/00014_7_select_from_table_with_nested.reference b/dbms/tests/queries/00014_7_select_from_table_with_nested.reference new file mode 100644 index 00000000000..05200edd33b --- /dev/null +++ b/dbms/tests/queries/00014_7_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 +Hello 2 20 +World 3 30 +World 4 40 +World 5 50 diff --git a/dbms/tests/queries/00014_7_select_from_table_with_nested.sql b/dbms/tests/queries/00014_7_select_from_table_with_nested.sql new file mode 100644 index 00000000000..9380d7d285f --- /dev/null +++ b/dbms/tests/queries/00014_7_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, nest.x, nest.y FROM nested_test ARRAY JOIN nest.x, nest.y \ No newline at end of file diff --git a/dbms/tests/queries/00014_9_select_from_table_with_nested.reference b/dbms/tests/queries/00014_9_select_from_table_with_nested.reference new file mode 100644 index 00000000000..05200edd33b --- /dev/null +++ b/dbms/tests/queries/00014_9_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 +Hello 2 20 +World 3 30 +World 4 40 +World 5 50 diff --git a/dbms/tests/queries/00014_9_select_from_table_with_nested.sql b/dbms/tests/queries/00014_9_select_from_table_with_nested.sql new file mode 100644 index 00000000000..945f3dc79a1 --- /dev/null +++ b/dbms/tests/queries/00014_9_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, n.x, n.y FROM nested_test ARRAY JOIN nest AS n \ No newline at end of file diff --git a/dbms/tests/queries/00014_a_select_from_table_with_nested.reference b/dbms/tests/queries/00014_a_select_from_table_with_nested.reference new file mode 100644 index 00000000000..d40754269c6 --- /dev/null +++ b/dbms/tests/queries/00014_a_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 [1,2] +Hello 2 20 [1,2] +World 3 30 [3,4,5] +World 4 40 [3,4,5] +World 5 50 [3,4,5] diff --git a/dbms/tests/queries/00014_a_select_from_table_with_nested.sql b/dbms/tests/queries/00014_a_select_from_table_with_nested.sql new file mode 100644 index 00000000000..4e275aa4e12 --- /dev/null +++ b/dbms/tests/queries/00014_a_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, n.x, n.y, nest.x FROM nested_test ARRAY JOIN nest AS n \ No newline at end of file diff --git a/dbms/tests/queries/00014_b_select_from_table_with_nested.reference b/dbms/tests/queries/00014_b_select_from_table_with_nested.reference new file mode 100644 index 00000000000..7129270657e --- /dev/null +++ b/dbms/tests/queries/00014_b_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 [1,2] [10,20] +Hello 2 20 [1,2] [10,20] +World 3 30 [3,4,5] [30,40,50] +World 4 40 [3,4,5] [30,40,50] +World 5 50 [3,4,5] [30,40,50] diff --git a/dbms/tests/queries/00014_b_select_from_table_with_nested.sql b/dbms/tests/queries/00014_b_select_from_table_with_nested.sql new file mode 100644 index 00000000000..4643e428942 --- /dev/null +++ b/dbms/tests/queries/00014_b_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, n.x, n.y, nest.x, nest.y FROM nested_test ARRAY JOIN nest AS n \ No newline at end of file diff --git a/dbms/tests/queries/00014_c_select_from_table_with_nested.reference b/dbms/tests/queries/00014_c_select_from_table_with_nested.reference new file mode 100644 index 00000000000..fa7a8052ce9 --- /dev/null +++ b/dbms/tests/queries/00014_c_select_from_table_with_nested.reference @@ -0,0 +1,5 @@ +Hello 1 10 [1,2] [10,20] 1 +Hello 2 20 [1,2] [10,20] 2 +World 3 30 [3,4,5] [30,40,50] 1 +World 4 40 [3,4,5] [30,40,50] 2 +World 5 50 [3,4,5] [30,40,50] 3 diff --git a/dbms/tests/queries/00014_c_select_from_table_with_nested.sql b/dbms/tests/queries/00014_c_select_from_table_with_nested.sql new file mode 100644 index 00000000000..6033dd1c011 --- /dev/null +++ b/dbms/tests/queries/00014_c_select_from_table_with_nested.sql @@ -0,0 +1 @@ +SELECT s, n.x, n.y, nest.x, nest.y, num FROM nested_test ARRAY JOIN nest AS n, arrayEnumerate(nest.x) AS num \ No newline at end of file diff --git a/dbms/tests/queries/00015_totals_having_constants.reference b/dbms/tests/queries/00015_totals_having_constants.reference new file mode 100644 index 00000000000..1b62b5f7c43 --- /dev/null +++ b/dbms/tests/queries/00015_totals_having_constants.reference @@ -0,0 +1,12 @@ +0 10 +1 10 +5 10 +7 10 +2 10 +6 10 +4 10 +8 10 +3 10 +9 10 + +0 100 diff --git a/dbms/tests/queries/00015_totals_having_constants.sql b/dbms/tests/queries/00015_totals_having_constants.sql new file mode 100644 index 00000000000..586bef3ac13 --- /dev/null +++ b/dbms/tests/queries/00015_totals_having_constants.sql @@ -0,0 +1 @@ +SELECT number, count() / 0.1 FROM (SELECT number FROM system.numbers LIMIT 10) GROUP BY number WITH TOTALS HAVING count() > 0.1 diff --git a/dbms/tests/queries/00016_totals_having_constants.reference b/dbms/tests/queries/00016_totals_having_constants.reference new file mode 100644 index 00000000000..4804131baba --- /dev/null +++ b/dbms/tests/queries/00016_totals_having_constants.reference @@ -0,0 +1,3 @@ +0 10 + +0 10 diff --git a/dbms/tests/queries/00016_totals_having_constants.sql b/dbms/tests/queries/00016_totals_having_constants.sql new file mode 100644 index 00000000000..c50659b8140 --- /dev/null +++ b/dbms/tests/queries/00016_totals_having_constants.sql @@ -0,0 +1 @@ +SELECT dummy, count() / 0.1 GROUP BY dummy WITH TOTALS HAVING count() > 0.1 diff --git a/dbms/tests/queries/00017_in_subquery_with_empty_result.reference b/dbms/tests/queries/00017_in_subquery_with_empty_result.reference new file mode 100644 index 00000000000..e25c5780b65 --- /dev/null +++ b/dbms/tests/queries/00017_in_subquery_with_empty_result.reference @@ -0,0 +1,18 @@ +{ + "meta": + [ + { + "name": "count()", + "type": "UInt64" + } + ], + + "data": + [ + + ], + + "rows": 0, + + "rows_before_limit_at_least": 1000 +} diff --git a/dbms/tests/queries/00017_in_subquery_with_empty_result.sql b/dbms/tests/queries/00017_in_subquery_with_empty_result.sql new file mode 100644 index 00000000000..b83d597159d --- /dev/null +++ b/dbms/tests/queries/00017_in_subquery_with_empty_result.sql @@ -0,0 +1,2 @@ +SELECT count() FROM (SELECT * FROM system.numbers LIMIT 1000) WHERE 1 IN (SELECT 0 WHERE 0) +FORMAT JSON diff --git a/dbms/tests/queries/00018_distinct_in_subquery.reference b/dbms/tests/queries/00018_distinct_in_subquery.reference new file mode 100644 index 00000000000..6ed281c757a --- /dev/null +++ b/dbms/tests/queries/00018_distinct_in_subquery.reference @@ -0,0 +1,2 @@ +1 +1 diff --git a/dbms/tests/queries/00018_distinct_in_subquery.sql b/dbms/tests/queries/00018_distinct_in_subquery.sql new file mode 100644 index 00000000000..b0bce846904 --- /dev/null +++ b/dbms/tests/queries/00018_distinct_in_subquery.sql @@ -0,0 +1 @@ +SELECT x FROM (SELECT DISTINCT 1 AS x, arrayJoin([1, 2]) AS y) From 2813909ed3c92f41ee83d4eeeacd9c27726cb21c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 00:45:42 +0400 Subject: [PATCH 044/281] dbms: modified test [#METR-2944]. --- dbms/tests/clickhouse-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 1bfd91ab468..4c918cf30a7 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -24,7 +24,7 @@ ERRORS=0 for query_file in $(ls $QUERIES_DIR/*.sql) do - test_name=$(basename -s .sql $query_file) + test_name=$(basename $query_file .sql) result_file=$QUERIES_DIR/$test_name.result error_file=$QUERIES_DIR/$test_name.error From e2292225ef4a1bfd6ab948ceea897d4541d7aeb4 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 04:37:39 +0400 Subject: [PATCH 045/281] Added gitignore [#METR-2944]. --- dbms/tests/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 dbms/tests/.gitignore diff --git a/dbms/tests/.gitignore b/dbms/tests/.gitignore new file mode 100644 index 00000000000..3b3e487c63a --- /dev/null +++ b/dbms/tests/.gitignore @@ -0,0 +1,4 @@ +*.result +*.diff +*.error +test_data From 8aaecae5fbb7044c44939fe214899c2e9633b9bd Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 06:50:25 +0400 Subject: [PATCH 046/281] dbms: updated tests [#METR-2944]. --- dbms/tests/queries/{ => 0_stateless}/00001_select_1.reference | 0 dbms/tests/queries/{ => 0_stateless}/00001_select_1.sql | 0 .../queries/{ => 0_stateless}/00002_system_numbers.reference | 0 dbms/tests/queries/{ => 0_stateless}/00002_system_numbers.sql | 0 .../{ => 0_stateless}/00003_reinterpret_as_string.reference | 0 .../queries/{ => 0_stateless}/00003_reinterpret_as_string.sql | 0 .../{ => 0_stateless}/00004_format_ast_and_remote_table.reference | 0 .../{ => 0_stateless}/00004_format_ast_and_remote_table.sql | 0 .../00005_format_ast_and_remote_table_lambda.reference | 0 .../00005_format_ast_and_remote_table_lambda.sql | 0 .../queries/{ => 0_stateless}/00006_1_set_extremes.reference | 0 dbms/tests/queries/{ => 0_stateless}/00006_1_set_extremes.sql | 0 .../00006_2_extremes_and_subquery_from.reference | 0 .../{ => 0_stateless}/00006_2_extremes_and_subquery_from.sql | 0 .../queries/{ => 0_stateless}/00006_3_unset_extremes.reference | 0 dbms/tests/queries/{ => 0_stateless}/00006_3_unset_extremes.sql | 0 dbms/tests/queries/{ => 0_stateless}/00007_array.reference | 0 dbms/tests/queries/{ => 0_stateless}/00007_array.sql | 0 dbms/tests/queries/{ => 0_stateless}/00008_array_join.reference | 0 dbms/tests/queries/{ => 0_stateless}/00008_array_join.sql | 0 .../queries/{ => 0_stateless}/00009_array_join_subquery.reference | 0 .../tests/queries/{ => 0_stateless}/00009_array_join_subquery.sql | 0 .../queries/{ => 0_stateless}/00010_big_array_join.reference | 0 dbms/tests/queries/{ => 0_stateless}/00010_big_array_join.sql | 0 .../queries/{ => 0_stateless}/00011_array_join_alias.reference | 0 dbms/tests/queries/{ => 0_stateless}/00011_array_join_alias.sql | 0 .../queries/{ => 0_stateless}/00012_array_join_alias_2.reference | 0 dbms/tests/queries/{ => 0_stateless}/00012_array_join_alias_2.sql | 0 .../00013_1_drop_if_exists_table_with_arrays.reference | 0 .../00013_1_drop_if_exists_table_with_arrays.sql | 0 .../{ => 0_stateless}/00013_2_create_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_2_create_table_with_arrays.sql | 0 .../00013_3_insert_into_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_3_insert_into_table_with_arrays.sql | 0 .../00013_4_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_4_select_from_table_with_arrays.sql | 0 .../00013_5_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_5_select_from_table_with_arrays.sql | 0 .../00013_6_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_6_select_from_table_with_arrays.sql | 0 .../00013_7_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_7_select_from_table_with_arrays.sql | 0 .../00013_8_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_8_select_from_table_with_arrays.sql | 0 .../00013_9_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_9_select_from_table_with_arrays.sql | 0 .../00013_a_select_from_table_with_arrays.reference | 0 .../{ => 0_stateless}/00013_a_select_from_table_with_arrays.sql | 0 .../00014_1_drop_if_exists_table_with_nested.reference | 0 .../00014_1_drop_if_exists_table_with_nested.sql | 0 .../{ => 0_stateless}/00014_2_create_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_2_create_table_with_nested.sql | 0 .../00014_3_insert_into_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_3_insert_into_table_with_nested.sql | 0 .../00014_4_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_4_select_from_table_with_nested.sql | 0 .../00014_5_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_5_select_from_table_with_nested.sql | 0 .../00014_6_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_6_select_from_table_with_nested.sql | 0 .../00014_7_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_7_select_from_table_with_nested.sql | 0 .../00014_9_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_9_select_from_table_with_nested.sql | 0 .../00014_a_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_a_select_from_table_with_nested.sql | 0 .../00014_b_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_b_select_from_table_with_nested.sql | 0 .../00014_c_select_from_table_with_nested.reference | 0 .../{ => 0_stateless}/00014_c_select_from_table_with_nested.sql | 0 .../{ => 0_stateless}/00015_totals_having_constants.reference | 0 .../queries/{ => 0_stateless}/00015_totals_having_constants.sql | 0 .../{ => 0_stateless}/00016_totals_having_constants.reference | 0 .../queries/{ => 0_stateless}/00016_totals_having_constants.sql | 0 .../00017_in_subquery_with_empty_result.reference | 0 .../{ => 0_stateless}/00017_in_subquery_with_empty_result.sql | 0 .../{ => 0_stateless}/00018_distinct_in_subquery.reference | 0 .../queries/{ => 0_stateless}/00018_distinct_in_subquery.sql | 0 78 files changed, 0 insertions(+), 0 deletions(-) rename dbms/tests/queries/{ => 0_stateless}/00001_select_1.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00001_select_1.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00002_system_numbers.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00002_system_numbers.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00003_reinterpret_as_string.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00003_reinterpret_as_string.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00004_format_ast_and_remote_table.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00004_format_ast_and_remote_table.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00005_format_ast_and_remote_table_lambda.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00005_format_ast_and_remote_table_lambda.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_1_set_extremes.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_1_set_extremes.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_2_extremes_and_subquery_from.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_2_extremes_and_subquery_from.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_3_unset_extremes.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00006_3_unset_extremes.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00007_array.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00007_array.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00008_array_join.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00008_array_join.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00009_array_join_subquery.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00009_array_join_subquery.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00010_big_array_join.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00010_big_array_join.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00011_array_join_alias.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00011_array_join_alias.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00012_array_join_alias_2.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00012_array_join_alias_2.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_1_drop_if_exists_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_1_drop_if_exists_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_2_create_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_2_create_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_3_insert_into_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_3_insert_into_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_4_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_4_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_5_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_5_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_6_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_6_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_7_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_7_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_8_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_8_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_9_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_9_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_a_select_from_table_with_arrays.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00013_a_select_from_table_with_arrays.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_1_drop_if_exists_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_1_drop_if_exists_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_2_create_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_2_create_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_3_insert_into_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_3_insert_into_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_4_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_4_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_5_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_5_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_6_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_6_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_7_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_7_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_9_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_9_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_a_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_a_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_b_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_b_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_c_select_from_table_with_nested.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00014_c_select_from_table_with_nested.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00015_totals_having_constants.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00015_totals_having_constants.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00016_totals_having_constants.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00016_totals_having_constants.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00017_in_subquery_with_empty_result.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00017_in_subquery_with_empty_result.sql (100%) rename dbms/tests/queries/{ => 0_stateless}/00018_distinct_in_subquery.reference (100%) rename dbms/tests/queries/{ => 0_stateless}/00018_distinct_in_subquery.sql (100%) diff --git a/dbms/tests/queries/00001_select_1.reference b/dbms/tests/queries/0_stateless/00001_select_1.reference similarity index 100% rename from dbms/tests/queries/00001_select_1.reference rename to dbms/tests/queries/0_stateless/00001_select_1.reference diff --git a/dbms/tests/queries/00001_select_1.sql b/dbms/tests/queries/0_stateless/00001_select_1.sql similarity index 100% rename from dbms/tests/queries/00001_select_1.sql rename to dbms/tests/queries/0_stateless/00001_select_1.sql diff --git a/dbms/tests/queries/00002_system_numbers.reference b/dbms/tests/queries/0_stateless/00002_system_numbers.reference similarity index 100% rename from dbms/tests/queries/00002_system_numbers.reference rename to dbms/tests/queries/0_stateless/00002_system_numbers.reference diff --git a/dbms/tests/queries/00002_system_numbers.sql b/dbms/tests/queries/0_stateless/00002_system_numbers.sql similarity index 100% rename from dbms/tests/queries/00002_system_numbers.sql rename to dbms/tests/queries/0_stateless/00002_system_numbers.sql diff --git a/dbms/tests/queries/00003_reinterpret_as_string.reference b/dbms/tests/queries/0_stateless/00003_reinterpret_as_string.reference similarity index 100% rename from dbms/tests/queries/00003_reinterpret_as_string.reference rename to dbms/tests/queries/0_stateless/00003_reinterpret_as_string.reference diff --git a/dbms/tests/queries/00003_reinterpret_as_string.sql b/dbms/tests/queries/0_stateless/00003_reinterpret_as_string.sql similarity index 100% rename from dbms/tests/queries/00003_reinterpret_as_string.sql rename to dbms/tests/queries/0_stateless/00003_reinterpret_as_string.sql diff --git a/dbms/tests/queries/00004_format_ast_and_remote_table.reference b/dbms/tests/queries/0_stateless/00004_format_ast_and_remote_table.reference similarity index 100% rename from dbms/tests/queries/00004_format_ast_and_remote_table.reference rename to dbms/tests/queries/0_stateless/00004_format_ast_and_remote_table.reference diff --git a/dbms/tests/queries/00004_format_ast_and_remote_table.sql b/dbms/tests/queries/0_stateless/00004_format_ast_and_remote_table.sql similarity index 100% rename from dbms/tests/queries/00004_format_ast_and_remote_table.sql rename to dbms/tests/queries/0_stateless/00004_format_ast_and_remote_table.sql diff --git a/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference b/dbms/tests/queries/0_stateless/00005_format_ast_and_remote_table_lambda.reference similarity index 100% rename from dbms/tests/queries/00005_format_ast_and_remote_table_lambda.reference rename to dbms/tests/queries/0_stateless/00005_format_ast_and_remote_table_lambda.reference diff --git a/dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql b/dbms/tests/queries/0_stateless/00005_format_ast_and_remote_table_lambda.sql similarity index 100% rename from dbms/tests/queries/00005_format_ast_and_remote_table_lambda.sql rename to dbms/tests/queries/0_stateless/00005_format_ast_and_remote_table_lambda.sql diff --git a/dbms/tests/queries/00006_1_set_extremes.reference b/dbms/tests/queries/0_stateless/00006_1_set_extremes.reference similarity index 100% rename from dbms/tests/queries/00006_1_set_extremes.reference rename to dbms/tests/queries/0_stateless/00006_1_set_extremes.reference diff --git a/dbms/tests/queries/00006_1_set_extremes.sql b/dbms/tests/queries/0_stateless/00006_1_set_extremes.sql similarity index 100% rename from dbms/tests/queries/00006_1_set_extremes.sql rename to dbms/tests/queries/0_stateless/00006_1_set_extremes.sql diff --git a/dbms/tests/queries/00006_2_extremes_and_subquery_from.reference b/dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.reference similarity index 100% rename from dbms/tests/queries/00006_2_extremes_and_subquery_from.reference rename to dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.reference diff --git a/dbms/tests/queries/00006_2_extremes_and_subquery_from.sql b/dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.sql similarity index 100% rename from dbms/tests/queries/00006_2_extremes_and_subquery_from.sql rename to dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.sql diff --git a/dbms/tests/queries/00006_3_unset_extremes.reference b/dbms/tests/queries/0_stateless/00006_3_unset_extremes.reference similarity index 100% rename from dbms/tests/queries/00006_3_unset_extremes.reference rename to dbms/tests/queries/0_stateless/00006_3_unset_extremes.reference diff --git a/dbms/tests/queries/00006_3_unset_extremes.sql b/dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql similarity index 100% rename from dbms/tests/queries/00006_3_unset_extremes.sql rename to dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql diff --git a/dbms/tests/queries/00007_array.reference b/dbms/tests/queries/0_stateless/00007_array.reference similarity index 100% rename from dbms/tests/queries/00007_array.reference rename to dbms/tests/queries/0_stateless/00007_array.reference diff --git a/dbms/tests/queries/00007_array.sql b/dbms/tests/queries/0_stateless/00007_array.sql similarity index 100% rename from dbms/tests/queries/00007_array.sql rename to dbms/tests/queries/0_stateless/00007_array.sql diff --git a/dbms/tests/queries/00008_array_join.reference b/dbms/tests/queries/0_stateless/00008_array_join.reference similarity index 100% rename from dbms/tests/queries/00008_array_join.reference rename to dbms/tests/queries/0_stateless/00008_array_join.reference diff --git a/dbms/tests/queries/00008_array_join.sql b/dbms/tests/queries/0_stateless/00008_array_join.sql similarity index 100% rename from dbms/tests/queries/00008_array_join.sql rename to dbms/tests/queries/0_stateless/00008_array_join.sql diff --git a/dbms/tests/queries/00009_array_join_subquery.reference b/dbms/tests/queries/0_stateless/00009_array_join_subquery.reference similarity index 100% rename from dbms/tests/queries/00009_array_join_subquery.reference rename to dbms/tests/queries/0_stateless/00009_array_join_subquery.reference diff --git a/dbms/tests/queries/00009_array_join_subquery.sql b/dbms/tests/queries/0_stateless/00009_array_join_subquery.sql similarity index 100% rename from dbms/tests/queries/00009_array_join_subquery.sql rename to dbms/tests/queries/0_stateless/00009_array_join_subquery.sql diff --git a/dbms/tests/queries/00010_big_array_join.reference b/dbms/tests/queries/0_stateless/00010_big_array_join.reference similarity index 100% rename from dbms/tests/queries/00010_big_array_join.reference rename to dbms/tests/queries/0_stateless/00010_big_array_join.reference diff --git a/dbms/tests/queries/00010_big_array_join.sql b/dbms/tests/queries/0_stateless/00010_big_array_join.sql similarity index 100% rename from dbms/tests/queries/00010_big_array_join.sql rename to dbms/tests/queries/0_stateless/00010_big_array_join.sql diff --git a/dbms/tests/queries/00011_array_join_alias.reference b/dbms/tests/queries/0_stateless/00011_array_join_alias.reference similarity index 100% rename from dbms/tests/queries/00011_array_join_alias.reference rename to dbms/tests/queries/0_stateless/00011_array_join_alias.reference diff --git a/dbms/tests/queries/00011_array_join_alias.sql b/dbms/tests/queries/0_stateless/00011_array_join_alias.sql similarity index 100% rename from dbms/tests/queries/00011_array_join_alias.sql rename to dbms/tests/queries/0_stateless/00011_array_join_alias.sql diff --git a/dbms/tests/queries/00012_array_join_alias_2.reference b/dbms/tests/queries/0_stateless/00012_array_join_alias_2.reference similarity index 100% rename from dbms/tests/queries/00012_array_join_alias_2.reference rename to dbms/tests/queries/0_stateless/00012_array_join_alias_2.reference diff --git a/dbms/tests/queries/00012_array_join_alias_2.sql b/dbms/tests/queries/0_stateless/00012_array_join_alias_2.sql similarity index 100% rename from dbms/tests/queries/00012_array_join_alias_2.sql rename to dbms/tests/queries/0_stateless/00012_array_join_alias_2.sql diff --git a/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_1_drop_if_exists_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_2_create_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_2_create_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_2_create_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_2_create_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_3_insert_into_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_3_insert_into_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_3_insert_into_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_4_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_4_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_4_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_4_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_4_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_4_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_4_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_4_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_5_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_5_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_5_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_5_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_5_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_5_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_5_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_5_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_6_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_6_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_6_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_6_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_6_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_6_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_6_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_6_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_7_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_7_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_7_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_7_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_7_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_7_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_7_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_7_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_8_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_8_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_8_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_8_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_8_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_8_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_8_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_8_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_9_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_9_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_9_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_9_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_9_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_9_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_9_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_9_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00013_a_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_a_select_from_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/00013_a_select_from_table_with_arrays.reference rename to dbms/tests/queries/0_stateless/00013_a_select_from_table_with_arrays.reference diff --git a/dbms/tests/queries/00013_a_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_a_select_from_table_with_arrays.sql similarity index 100% rename from dbms/tests/queries/00013_a_select_from_table_with_arrays.sql rename to dbms/tests/queries/0_stateless/00013_a_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.reference diff --git a/dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_1_drop_if_exists_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql diff --git a/dbms/tests/queries/00014_2_create_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_2_create_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.reference diff --git a/dbms/tests/queries/00014_2_create_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_2_create_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql diff --git a/dbms/tests/queries/00014_3_insert_into_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_3_insert_into_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.reference diff --git a/dbms/tests/queries/00014_3_insert_into_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_3_insert_into_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql diff --git a/dbms/tests/queries/00014_4_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_4_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_4_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_4_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_4_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_4_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_4_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_4_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_5_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_5_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_5_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_5_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_5_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_5_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_5_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_5_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_6_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_6_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_6_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_6_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_6_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_6_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_6_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_6_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_7_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_7_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_7_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_7_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_7_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_7_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_7_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_7_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_9_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_9_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_9_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_9_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_9_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_9_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_9_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_9_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_a_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_a_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_a_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_a_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_a_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_a_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_a_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_a_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_b_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_b_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_b_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_b_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_b_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_b_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_b_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_b_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00014_c_select_from_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_c_select_from_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/00014_c_select_from_table_with_nested.reference rename to dbms/tests/queries/0_stateless/00014_c_select_from_table_with_nested.reference diff --git a/dbms/tests/queries/00014_c_select_from_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_c_select_from_table_with_nested.sql similarity index 100% rename from dbms/tests/queries/00014_c_select_from_table_with_nested.sql rename to dbms/tests/queries/0_stateless/00014_c_select_from_table_with_nested.sql diff --git a/dbms/tests/queries/00015_totals_having_constants.reference b/dbms/tests/queries/0_stateless/00015_totals_having_constants.reference similarity index 100% rename from dbms/tests/queries/00015_totals_having_constants.reference rename to dbms/tests/queries/0_stateless/00015_totals_having_constants.reference diff --git a/dbms/tests/queries/00015_totals_having_constants.sql b/dbms/tests/queries/0_stateless/00015_totals_having_constants.sql similarity index 100% rename from dbms/tests/queries/00015_totals_having_constants.sql rename to dbms/tests/queries/0_stateless/00015_totals_having_constants.sql diff --git a/dbms/tests/queries/00016_totals_having_constants.reference b/dbms/tests/queries/0_stateless/00016_totals_having_constants.reference similarity index 100% rename from dbms/tests/queries/00016_totals_having_constants.reference rename to dbms/tests/queries/0_stateless/00016_totals_having_constants.reference diff --git a/dbms/tests/queries/00016_totals_having_constants.sql b/dbms/tests/queries/0_stateless/00016_totals_having_constants.sql similarity index 100% rename from dbms/tests/queries/00016_totals_having_constants.sql rename to dbms/tests/queries/0_stateless/00016_totals_having_constants.sql diff --git a/dbms/tests/queries/00017_in_subquery_with_empty_result.reference b/dbms/tests/queries/0_stateless/00017_in_subquery_with_empty_result.reference similarity index 100% rename from dbms/tests/queries/00017_in_subquery_with_empty_result.reference rename to dbms/tests/queries/0_stateless/00017_in_subquery_with_empty_result.reference diff --git a/dbms/tests/queries/00017_in_subquery_with_empty_result.sql b/dbms/tests/queries/0_stateless/00017_in_subquery_with_empty_result.sql similarity index 100% rename from dbms/tests/queries/00017_in_subquery_with_empty_result.sql rename to dbms/tests/queries/0_stateless/00017_in_subquery_with_empty_result.sql diff --git a/dbms/tests/queries/00018_distinct_in_subquery.reference b/dbms/tests/queries/0_stateless/00018_distinct_in_subquery.reference similarity index 100% rename from dbms/tests/queries/00018_distinct_in_subquery.reference rename to dbms/tests/queries/0_stateless/00018_distinct_in_subquery.reference diff --git a/dbms/tests/queries/00018_distinct_in_subquery.sql b/dbms/tests/queries/0_stateless/00018_distinct_in_subquery.sql similarity index 100% rename from dbms/tests/queries/00018_distinct_in_subquery.sql rename to dbms/tests/queries/0_stateless/00018_distinct_in_subquery.sql From 9abadce603210bc2295668e8bc1dff44a4c6f6e0 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 06:50:39 +0400 Subject: [PATCH 047/281] dbms: updated tests [#METR-2944]. --- dbms/tests/clickhouse-test | 111 +++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 4c918cf30a7..3300a944d0f 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -21,58 +21,71 @@ MSG_GENERATED="${COLOR_WHITE}[ ${COLOR_UNKNOWN}GENERATED${COLOR_WHITE} ]${COLOR_ ERRORS=0 - -for query_file in $(ls $QUERIES_DIR/*.sql) +for dir in $(ls $QUERIES_DIR) do - test_name=$(basename $query_file .sql) + tests_name=$(echo $dir | sed -E 's/^[0-9_]+//') - result_file=$QUERIES_DIR/$test_name.result - error_file=$QUERIES_DIR/$test_name.error - reference_file=$QUERIES_DIR/$test_name.reference - diff_file=$QUERIES_DIR/$test_name.diff + echo + echo "Running $tests_name tests." + echo - printf "%-60s" "$test_name: " - - $CLIENT_PROGRAM < $query_file > $result_file 2> $error_file - ret_code=$? - - if [ $ret_code -ne 0 ]; then - ERRORS=$(($ERRORS + 1)) - echo -e "$MSG_FAIL - return code $ret_code" - if [ -s "$error_file" ]; then - cat $error_file - fi - # разорвано соединение с сервером - if grep -q -E "Connection refused|Attempt to read after eof" $error_file; then - exit 1; - fi - elif [ -s "$error_file" ]; then - ERRORS=$(($ERRORS + 1)) - echo -e "$MSG_FAIL - having stderror:" - cat $error_file - elif grep -q "Exception" $result_file; then - ERRORS=$(($ERRORS + 1)) - echo -e "$MSG_FAIL - having exception:" - cat $result_file - elif [ ! -e "$reference_file" ]; then - # надо сгенерировать эталонный результат - if [ "$1" == "--generate" ]; then - cp $result_file $reference_file - echo -e "$MSG_GENERATED - no reference file" - else - echo -e "$MSG_UNKNOWN - no reference file (use --generate to create)" - fi - else - diff $reference_file $result_file > $diff_file - if [ -s "$diff_file" ]; then - ERRORS=$(($ERRORS + 1)) - echo -e "$MSG_FAIL - result differs with reference:" - cat $diff_file - else - echo -e "$MSG_OK" - rm $error_file $result_file $diff_file - fi + if [[ "$tests_name" =~ "stateful" && 0 -eq $(echo "EXISTS TABLE test.hits" | $CLIENT_PROGRAM) ]]; then + echo "Won't run stateful tests because test data wasn't loaded. See README.txt." + continue fi + + for query_file in $(ls $QUERIES_DIR/$dir/*.sql) + do + test_name=$(basename $query_file .sql) + + result_file=$QUERIES_DIR/$dir/$test_name.result + error_file=$QUERIES_DIR/$dir/$test_name.error + reference_file=$QUERIES_DIR/$dir/$test_name.reference + diff_file=$QUERIES_DIR/$dir/$test_name.diff + + printf "%-60s" "$test_name: " + + $CLIENT_PROGRAM < $query_file > $result_file 2> $error_file + ret_code=$? + + if [ $ret_code -ne 0 ]; then + ERRORS=$(($ERRORS + 1)) + echo -e "$MSG_FAIL - return code $ret_code" + if [ -s "$error_file" ]; then + cat $error_file + fi + # разорвано соединение с сервером + if grep -q -E "Connection refused|Attempt to read after eof" $error_file; then + exit 1; + fi + elif [ -s "$error_file" ]; then + ERRORS=$(($ERRORS + 1)) + echo -e "$MSG_FAIL - having stderror:" + cat $error_file + elif grep -q "Exception" $result_file; then + ERRORS=$(($ERRORS + 1)) + echo -e "$MSG_FAIL - having exception:" + cat $result_file + elif [ ! -e "$reference_file" ]; then + # надо сгенерировать эталонный результат + if [ "$1" == "--generate" ]; then + cp $result_file $reference_file + echo -e "$MSG_GENERATED - no reference file" + else + echo -e "$MSG_UNKNOWN - no reference file (use --generate to create)" + fi + else + diff $reference_file $result_file > $diff_file + if [ -s "$diff_file" ]; then + ERRORS=$(($ERRORS + 1)) + echo -e "$MSG_FAIL - result differs with reference:" + cat $diff_file + else + echo -e "$MSG_OK" + rm $error_file $result_file $diff_file + fi + fi + done done @@ -82,6 +95,6 @@ if [ $ERRORS -gt 0 ]; then echo -e "${COLOR_FAIL}Having $ERRORS errors!${COLOR_RESET}" exit 1 else - echo -e "${COLOR_OK}All tests succeeded.${COLOR_RESET}" + echo -e "${COLOR_OK}All tests passed.${COLOR_RESET}" exit 0 fi From 03a0e1e1ef79e54ca5d54915ac10793026c3d53f Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 24 Mar 2014 16:10:47 +0400 Subject: [PATCH 048/281] Fixed build. --- dbms/include/DB/IO/ReadHelpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/IO/ReadHelpers.h b/dbms/include/DB/IO/ReadHelpers.h index b5c482eba6f..6ca62c08280 100644 --- a/dbms/include/DB/IO/ReadHelpers.h +++ b/dbms/include/DB/IO/ReadHelpers.h @@ -115,7 +115,7 @@ inline void readChar(char & x, ReadBuffer & buf) void assertString(const char * s, ReadBuffer & buf); -void assertString(const String & s, ReadBuffer & buf) +inline void assertString(const String & s, ReadBuffer & buf) { assertString(s.c_str(), buf); } From 258b14b506bfbf3fd124154b446a24f79b30f7d0 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 24 Mar 2014 17:24:47 +0400 Subject: [PATCH 049/281] Fixed segfault with table functions. [#METR-10549] --- dbms/include/DB/Storages/IStorage.h | 18 ++++++++++++------ .../DB/TableFunctions/TableFunctionMerge.h | 2 +- .../DB/TableFunctions/TableFunctionRemote.h | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 90c068aa008..01d97dde8ad 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -27,6 +27,11 @@ typedef SharedPtr BlockInputStreamPtr; typedef std::vector BlockInputStreams; +class IStorage; + +typedef std::shared_ptr StoragePtr; + + /** Хранилище. Отвечает за: * - хранение данных таблицы; * - определение, в каком файле (или не файле) хранятся данные; @@ -59,7 +64,7 @@ public: /** Не дает изменять описание таблицы (в том числе переименовывать и удалять таблицу). * Если в течение какой-то операции структура таблицы должна оставаться неизменной, нужно держать такой лок на все ее время. * Например, нужно держать такой лок на время всего запроса SELECT или INSERT и на все время слияния набора кусков - * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). + * (но между выбором кусков для слияния и их слиянием структура таблицы может измениться). * NOTE: Это лок на "чтение" описания таблицы. Чтобы изменить описание таблицы, нужно взять TableStructureWriteLock. */ class TableStructureReadLock @@ -67,13 +72,15 @@ public: private: friend class IStorage; + StoragePtr storage; /// Порядок важен. Poco::SharedPtr data_lock; Poco::SharedPtr structure_lock; - TableStructureReadLock(IStorage & storage, bool lock_structure, bool lock_data) - : data_lock(lock_data ? new Poco::ScopedReadRWLock(storage. data_lock) : nullptr), - structure_lock(lock_structure ? new Poco::ScopedReadRWLock(storage.structure_lock) : nullptr) {} + TableStructureReadLock(StoragePtr storage_, bool lock_structure, bool lock_data) + : storage(storage_), + data_lock(lock_data ? new Poco::ScopedReadRWLock(storage-> data_lock) : nullptr), + structure_lock(lock_structure ? new Poco::ScopedReadRWLock(storage->structure_lock) : nullptr) {} }; typedef Poco::SharedPtr TableStructureReadLockPtr; @@ -88,7 +95,7 @@ public: */ TableStructureReadLockPtr lockStructure(bool will_modify_data) { - TableStructureReadLockPtr res = new TableStructureReadLock(*this, true, will_modify_data); + TableStructureReadLockPtr res = new TableStructureReadLock(thisPtr(), true, will_modify_data); if (is_dropped) throw Exception("Table is dropped", ErrorCodes::TABLE_IS_DROPPED); return res; @@ -277,7 +284,6 @@ private: mutable Poco::RWLock structure_lock; }; -typedef std::shared_ptr StoragePtr; typedef std::vector StorageVector; typedef IStorage::TableStructureReadLocks TableLocks; diff --git a/dbms/include/DB/TableFunctions/TableFunctionMerge.h b/dbms/include/DB/TableFunctions/TableFunctionMerge.h index b56ce106fbc..f08be8eef42 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionMerge.h +++ b/dbms/include/DB/TableFunctions/TableFunctionMerge.h @@ -44,7 +44,7 @@ public: String table_name_regexp = safeGet(dynamic_cast(*args[1]).value); /// В InterpreterSelectQuery будет создан ExpressionAnalzyer, который при обработке запроса наткнется на этот Identifier. - /// Нам необходимо его пометить как имя базы данных, посколку по умолчанию стоит значение column + /// Нам необходимо его пометить как имя базы данных, поскольку по умолчанию стоит значение column dynamic_cast(*args[0]).kind = ASTIdentifier::Database; return StorageMerge::create(getName(), chooseColumns(source_database, table_name_regexp, context), source_database, table_name_regexp, context); diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index b5edb9a8a21..0239c4800a2 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -50,7 +50,7 @@ public: String password = args.size() == 5 ? safeGet(dynamic_cast(*args[4]).value) : ""; /// В InterpreterSelectQuery будет создан ExpressionAnalzyer, который при обработке запроса наткнется на эти Identifier. - /// Нам необходимо их пометить как имя базы данных и таблицы посколку по умолчанию стоит значение column + /// Нам необходимо их пометить как имя базы данных и таблицы поскольку по умолчанию стоит значение column dynamic_cast(*args[1]).kind = ASTIdentifier::Database; dynamic_cast(*args[2]).kind = ASTIdentifier::Table; From 08f4af22e1e04b242f17c3156fac3491fe909b69 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 24 Mar 2014 17:59:04 +0400 Subject: [PATCH 050/281] Merge --- dbms/include/DB/Parsers/ASTInsertQuery.h | 2 ++ dbms/src/Storages/StorageFactory.cpp | 9 ++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dbms/include/DB/Parsers/ASTInsertQuery.h b/dbms/include/DB/Parsers/ASTInsertQuery.h index 4441ac3d4e6..d8f9d8565d6 100644 --- a/dbms/include/DB/Parsers/ASTInsertQuery.h +++ b/dbms/include/DB/Parsers/ASTInsertQuery.h @@ -18,6 +18,8 @@ public: ASTPtr columns; String format; ASTPtr select; + /// Идентификатор запроса INSERT. Используется при репликации. + String insert_id; /// Данные для вставки const char * data; const char * end; diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index 3a3959e417a..41f6fb191a1 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -217,11 +217,10 @@ StoragePtr StorageFactory::get( ASTs & args_func = dynamic_cast(*dynamic_cast(*query).storage).children; - if (args_func.size() != 1) - throw Exception("Unexpected AST structure.", - ErrorCodes::UNEXPECTED_AST_STRUCTURE); + ASTs args; - ASTs args = dynamic_cast(*args_func.at(0)).children; + if (args_func.size() == 1) + args = dynamic_cast(*args_func.at(0)).children; size_t additional_params = (replicated ? 2 : 0) + (mode == MergeTreeData::Collapsing ? 1 : 0); if (args.size() != additional_params + 3 && args.size() != additional_params + 4) @@ -232,7 +231,7 @@ StoragePtr StorageFactory::get( params += "name of column with date, [name of column for sampling], primary key expression, index granularity"; if (mode == MergeTreeData::Collapsing) params += "sign column"; - throw Exception("Storage CollapsingMergeTree requires " + toString(additional_params + 3) + " or " + throw Exception("Storage " + name + " requires " + toString(additional_params + 3) + " or " + toString(additional_params + 4) +" parameters: " + params, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); } From 4c92b5f2208e2d946af69efebd9b121b9c598e03 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 24 Mar 2014 20:10:14 +0400 Subject: [PATCH 051/281] Fixed segfault when some of the nested columns are missing. [#METR-10564] --- .../DB/Storages/MergeTree/MergeTreeReader.h | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index f6ee8257d0b..3f0f7173e4a 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -37,6 +37,8 @@ typedef std::vector MarkRanges; */ class MergeTreeReader { + typedef std::map OffsetColumns; + public: MergeTreeReader(const String & path_, /// Путь к куску const Names & columns_names_, bool use_uncompressed_cache_, MergeTreeData & storage_) @@ -60,7 +62,6 @@ public: /// Указатели на столбцы смещений, общие для столбцов из вложенных структур данных /// Если append, все значения NULL, и offset_columns используется только для проверки, что столбец смещений уже прочитан. - typedef std::map OffsetColumns; OffsetColumns offset_columns; for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) @@ -124,6 +125,24 @@ public: { try { + /** Для недостающих столбцов из вложенной структуры нужно создавать не столбец пустых массивов, а столбец массивов + * правильных длин. + * TODO: Если для какой-то вложенной структуры были запрошены только отсутствующие столбцы, для них вернутся пустые + * массивы, даже если в куске есть смещения для этой вложенной структуры. Это можно исправить. + */ + + /// Сначала запомним столбцы смещений для всех массивов в блоке. + OffsetColumns offset_columns; + for (size_t i = 0; i < res.columns(); ++i) + { + const ColumnWithNameAndType & column = res.getByPosition(i); + if (const ColumnArray * array = dynamic_cast(&*column.column)) + { + String offsets_name = DataTypeNested::extractNestedTableName(column.name); + offset_columns[offsets_name] = array->getOffsetsColumn(); + } + } + size_t pos = 0; /// Позиция, куда надо вставить недостающий столбец. for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it, ++pos) { @@ -133,11 +152,27 @@ public: column.name = *it; column.type = storage.getDataTypeByName(*it); - /** Нужно превратить константный столбец в полноценный, так как в части блоков (из других кусков), - * он может быть полноценным (а то интерпретатор может посчитать, что он константный везде). - */ - column.column = dynamic_cast(*column.type->createConstColumn( - res.rows(), column.type->getDefault())).convertToFullColumn(); + String offsets_name = DataTypeNested::extractNestedTableName(column.name); + if (offset_columns.count(offsets_name)) + { + ColumnPtr offsets_column = offset_columns[offsets_name]; + DataTypePtr nested_type = dynamic_cast(*column.type).getNestedType(); + size_t nested_rows = offsets_column->empty() ? 0 + : dynamic_cast(*offsets_column).getData().back(); + + ColumnPtr nested_column = dynamic_cast(*nested_type->createConstColumn( + nested_rows, nested_type->getDefault())).convertToFullColumn(); + + column.column = new ColumnArray(nested_column, offsets_column); + } + else + { + /** Нужно превратить константный столбец в полноценный, так как в части блоков (из других кусков), + * он может быть полноценным (а то интерпретатор может посчитать, что он константный везде). + */ + column.column = dynamic_cast(*column.type->createConstColumn( + res.rows(), column.type->getDefault())).convertToFullColumn(); + } res.insert(pos, column); } @@ -266,17 +301,6 @@ private: addStream(name, *type_arr->getNestedType(), level + 1); } - else if (const DataTypeNested * type_nested = dynamic_cast(&type)) - { - String size_name = name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - String escaped_size_name = escaped_column_name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - - streams[size_name] = new Stream(path + escaped_size_name, uncompressed_cache, mark_cache); - - const NamesAndTypesList & columns = *type_nested->getNestedTypesList(); - for (NamesAndTypesList::const_iterator it = columns.begin(); it != columns.end(); ++it) - addStream(DataTypeNested::concatenateNestedName(name, it->first), *it->second, level + 1); - } else streams[name] = new Stream(path + escaped_column_name, uncompressed_cache, mark_cache); } From 2bc8beff045c3e74e732b9981a745b7ad221898a Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 24 Mar 2014 23:32:31 +0400 Subject: [PATCH 052/281] Added tests [#METR-10566]. --- .../queries/0_stateless/00019_quantiles_totals_distributed.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.sql diff --git a/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.sql b/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.sql new file mode 100644 index 00000000000..c075aeecc08 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.sql @@ -0,0 +1 @@ +SELECT quantilesTiming(0.1, 0.5, 0.9)(materialize(dummy)) FROM remote('127.0.0.{1,2}', system, one) GROUP BY 1 WITH TOTALS From 7cde6bb6a6617663dc98dc766227f795d1cf8598 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 25 Mar 2014 04:28:25 +0400 Subject: [PATCH 053/281] dbms: fixed error with hash tables [#METR-10571]. --- dbms/include/DB/Interpreters/ClearableHashMap.h | 14 +++++++++++++- dbms/include/DB/Interpreters/HashMap.h | 14 +++++++++++++- dbms/include/DB/Interpreters/HashSet.h | 14 +++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/dbms/include/DB/Interpreters/ClearableHashMap.h b/dbms/include/DB/Interpreters/ClearableHashMap.h index c3279ed5a5a..963511a7cc8 100644 --- a/dbms/include/DB/Interpreters/ClearableHashMap.h +++ b/dbms/include/DB/Interpreters/ClearableHashMap.h @@ -86,10 +86,22 @@ private: * Элемент может остаться на месте, или переместиться в новое место "справа", * или переместиться левее по цепочке разрешения коллизий, из-за того, что элементы левее него были перемещены в новое место "справа". */ - for (size_t i = 0; i < old_size; ++i) + size_t i = 0; + for (; i < old_size; ++i) if (buf[i].version == version) reinsert(buf[i]); + /** Также имеется особый случай: + * если элемент должен был быть в конце старого буфера, [ x] + * но находится в начале из-за цепочки разрешения коллизий, [o x] + * то после ресайза, он сначала снова окажется не на своём месте, [ xo ] + * и для того, чтобы перенести его куда надо, + * надо будет после переноса всех элементов из старой половинки [ o x ] + * обработать ещё хвостик из цепочки разрешения коллизий сразу после неё [ o x ] + */ + for (; buf[i].version == version; ++i) + reinsert(buf[i]); + #ifdef DBMS_HASH_MAP_DEBUG_RESIZES watch.stop(); std::cerr << std::fixed << std::setprecision(3) diff --git a/dbms/include/DB/Interpreters/HashMap.h b/dbms/include/DB/Interpreters/HashMap.h index fe0d6db5b5c..fa04d48f435 100644 --- a/dbms/include/DB/Interpreters/HashMap.h +++ b/dbms/include/DB/Interpreters/HashMap.h @@ -181,10 +181,22 @@ private: * Элемент может остаться на месте, или переместиться в новое место "справа", * или переместиться левее по цепочке разрешения коллизий, из-за того, что элементы левее него были перемещены в новое место "справа". */ - for (size_t i = 0; i < old_size; ++i) + size_t i = 0; + for (; i < old_size; ++i) if (!ZeroTraits::check(buf[i].first)) reinsert(buf[i]); + /** Также имеется особый случай: + * если элемент должен был быть в конце старого буфера, [ x] + * но находится в начале из-за цепочки разрешения коллизий, [o x] + * то после ресайза, он сначала снова окажется не на своём месте, [ xo ] + * и для того, чтобы перенести его куда надо, + * надо будет после переноса всех элементов из старой половинки [ o x ] + * обработать ещё хвостик из цепочки разрешения коллизий сразу после неё [ o x ] + */ + for (; !ZeroTraits::check(buf[i].first); ++i) + reinsert(buf[i]); + #ifdef DBMS_HASH_MAP_DEBUG_RESIZES watch.stop(); std::cerr << std::fixed << std::setprecision(3) diff --git a/dbms/include/DB/Interpreters/HashSet.h b/dbms/include/DB/Interpreters/HashSet.h index 67f4c52d494..7856eff6072 100644 --- a/dbms/include/DB/Interpreters/HashSet.h +++ b/dbms/include/DB/Interpreters/HashSet.h @@ -87,10 +87,22 @@ private: * Элемент может остаться на месте, или переместиться в новое место "справа", * или переместиться левее по цепочке разрешения коллизий, из-за того, что элементы левее него были перемещены в новое место "справа". */ - for (size_t i = 0; i < old_size; ++i) + size_t i = 0; + for (; i < old_size; ++i) if (!ZeroTraits::check(buf[i])) reinsert(buf[i]); + /** Также имеется особый случай: + * если элемент должен был быть в конце старого буфера, [ x] + * но находится в начале из-за цепочки разрешения коллизий, [o x] + * то после ресайза, он сначала снова окажется не на своём месте, [ xo ] + * и для того, чтобы перенести его куда надо, + * надо будет после переноса всех элементов из старой половинки [ o x ] + * обработать ещё хвостик из цепочки разрешения коллизий сразу после неё [ o x ] + */ + for (; !ZeroTraits::check(buf[i]); ++i) + reinsert(buf[i]); + #ifdef DBMS_HASH_MAP_DEBUG_RESIZES watch.stop(); std::cerr << std::fixed << std::setprecision(3) From a202cb4eaf40adda099ee86188d72d72b03c934c Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Tue, 25 Mar 2014 18:06:40 +0400 Subject: [PATCH 054/281] Merge --- .../ReplicatedMergeTreeBlockOutputStream.h | 0 dbms/src/Parsers/ParserInsertQuery.cpp | 19 +++++++++++++++++++ dbms/src/Parsers/formatAST.cpp | 4 ++++ .../Storages/StorageReplicatedMergeTree.cpp | 2 +- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/src/Parsers/ParserInsertQuery.cpp b/dbms/src/Parsers/ParserInsertQuery.cpp index e33d3902345..a7da264f980 100644 --- a/dbms/src/Parsers/ParserInsertQuery.cpp +++ b/dbms/src/Parsers/ParserInsertQuery.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -21,6 +22,9 @@ bool ParserInsertQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ParserString s_insert("INSERT", true, true); ParserString s_into("INTO", true, true); ParserString s_dot("."); + ParserString s_id("ID"); + ParserString s_eq("="); + ParserStringLiteral id_p; ParserString s_values("VALUES", true, true); ParserString s_format("FORMAT", true, true); ParserString s_select("SELECT", true, true); @@ -34,6 +38,7 @@ bool ParserInsertQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ASTPtr columns; ASTPtr format; ASTPtr select; + ASTPtr id; /// Данные для вставки const char * data = NULL; @@ -63,6 +68,17 @@ bool ParserInsertQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ws.ignore(pos, end); + if (s_id.ignore(pos, end, expected)) + { + if (!s_eq.ignore(pos, end, expected)) + return false; + + if (!id_p.parse(pos, end, id, expected)) + return false; + } + + ws.ignore(pos, end); + /// Есть ли список столбцов if (s_lparen.ignore(pos, end, expected)) { @@ -121,6 +137,9 @@ bool ParserInsertQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char query->table = dynamic_cast(*table).name; + if (id) + query->insert_id = safeGet(dynamic_cast(*id).value); + if (format) query->format = dynamic_cast(*format).name; diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index dff56b9c087..c8c479b2eac 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -409,6 +409,10 @@ void formatAST(const ASTInsertQuery & ast, std::ostream & s, size_t indent, bo s << (hilite ? hilite_keyword : "") << "INSERT INTO " << (hilite ? hilite_none : "") << (!ast.database.empty() ? backQuoteIfNeed(ast.database) + "." : "") << backQuoteIfNeed(ast.table); + if (!ast.insert_id.empty()) + s << (hilite ? hilite_keyword : "") << " ID = " << (hilite ? hilite_none : "") + << mysqlxx::quote << ast.insert_id; + if (ast.columns) { s << " ("; diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 0a39e3ea2fd..d1feb2fdae3 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -55,7 +55,7 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( else { checkTableStructure(); - checkParts(); + //checkParts(); } String endpoint_name = "ReplicatedMergeTree:" + replica_path; From 262ffcd74b85bccdc13bd18c340fc41ed81e99ca Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Tue, 25 Mar 2014 22:16:26 +0400 Subject: [PATCH 055/281] Fixed parametric aggregate functions in totals. [#METR-10566] --- .../AggregateFunctions/AggregateFunctionIf.h | 2 +- .../AggregateFunctionQuantile.h | 4 +- .../AggregateFunctionQuantileTiming.h | 4 +- .../AggregateFunctions/IAggregateFunction.h | 5 ++- dbms/include/DB/Common/AutoArray.h | 20 +++++----- .../DB/DataTypes/DataTypeAggregateFunction.h | 22 +++++++--- dbms/include/DB/Interpreters/Aggregator.h | 1 + .../CollapsingSortedBlockInputStream.cpp | 2 +- .../SummingSortedBlockInputStream.cpp | 2 +- dbms/src/DataTypes/DataTypeFactory.cpp | 40 +++++++++++++++---- dbms/src/Interpreters/Aggregator.cpp | 2 +- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 3 +- .../Interpreters/InterpreterSelectQuery.cpp | 2 +- 13 files changed, 75 insertions(+), 34 deletions(-) diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionIf.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionIf.h index 296331f56bb..c9936106426 100644 --- a/dbms/include/DB/AggregateFunctions/AggregateFunctionIf.h +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionIf.h @@ -47,7 +47,7 @@ public: nested_func->setArguments(nested_arguments); } - void setParameters(const Row & params) + void setParameters(const Array & params) { nested_func->setParameters(params); } diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantile.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantile.h index f9eb53d4afa..1c12f2d48bb 100644 --- a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantile.h +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantile.h @@ -56,7 +56,7 @@ public: type = argument; } - void setParameters(const Row & params) + void setParameters(const Array & params) { if (params.size() != 1) throw Exception("Aggregate function " + getName() + " requires exactly one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); @@ -130,7 +130,7 @@ public: type = argument; } - void setParameters(const Row & params) + void setParameters(const Array & params) { if (params.empty()) throw Exception("Aggregate function " + getName() + " requires at least one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h index 51107b85f83..06ae5222c41 100644 --- a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h @@ -511,7 +511,7 @@ public: { } - void setParameters(const Row & params) + void setParameters(const Array & params) { if (params.size() != 1) throw Exception("Aggregate function " + getName() + " requires exactly one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); @@ -570,7 +570,7 @@ public: { } - void setParameters(const Row & params) + void setParameters(const Array & params) { if (params.empty()) throw Exception("Aggregate function " + getName() + " requires at least one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); diff --git a/dbms/include/DB/AggregateFunctions/IAggregateFunction.h b/dbms/include/DB/AggregateFunctions/IAggregateFunction.h index d0c3653ad83..b6063508d7a 100644 --- a/dbms/include/DB/AggregateFunctions/IAggregateFunction.h +++ b/dbms/include/DB/AggregateFunctions/IAggregateFunction.h @@ -39,9 +39,10 @@ public: * Если параметры не предусмотрены или переданные параметры недопустимы - кинуть исключение. * Если параметры есть - необходимо вызывать перед остальными вызовами, иначе - не вызывать. */ - virtual void setParameters(const Row & params) + virtual void setParameters(const Array & params) { - throw Exception("Aggregate function " + getName() + " doesn't allow parameters.", ErrorCodes::AGGREGATE_FUNCTION_DOESNT_ALLOW_PARAMETERS); + throw Exception("Aggregate function " + getName() + " doesn't allow parameters.", + ErrorCodes::AGGREGATE_FUNCTION_DOESNT_ALLOW_PARAMETERS); } /// Получить тип результата. diff --git a/dbms/include/DB/Common/AutoArray.h b/dbms/include/DB/Common/AutoArray.h index 43f31ad2309..454e430efdd 100644 --- a/dbms/include/DB/Common/AutoArray.h +++ b/dbms/include/DB/Common/AutoArray.h @@ -20,7 +20,7 @@ namespace DB * sizeof равен размеру одного указателя. * * Не exception-safe. - * Копирование и присваивание разрушающее: исходный объект становится пустым. + * Копирование не поддерживается. Перемещение опустошает исходный объект. * То есть, использовать этот массив во многих случаях неудобно. * * Предназначен для ситуаций, в которых создаётся много массивов одинакового небольшого размера, @@ -82,24 +82,24 @@ public: init(size_, dont_init_elems); } - /** Разрушающее копирование. + /** Премещение. */ - AutoArray(const AutoArray & src) + AutoArray(AutoArray && src) { - //std::cerr << this << " AutoArray(const AutoArray & src)" << std::endl; - + if (this == &src) + return; setEmpty(); data = src.data; - const_cast &>(src).setEmpty(); + src.setEmpty(); } - AutoArray & operator= (const AutoArray & src) + AutoArray & operator= (AutoArray && src) { - //std::cerr << this << " operator=(const AutoArray & src)" << std::endl; - + if (this == &src) + return; uninit(); data = src.data; - const_cast &>(src).setEmpty(); + src.setEmpty(); return *this; } diff --git a/dbms/include/DB/DataTypes/DataTypeAggregateFunction.h b/dbms/include/DB/DataTypes/DataTypeAggregateFunction.h index bc7ffcf8d28..e3d87a4c65c 100644 --- a/dbms/include/DB/DataTypes/DataTypeAggregateFunction.h +++ b/dbms/include/DB/DataTypes/DataTypeAggregateFunction.h @@ -11,19 +11,19 @@ namespace DB using Poco::SharedPtr; /** Тип - состояние агрегатной функции. - * Параметры типа - это агрегатная функция и типы её аргументов. + * Параметры типа - это агрегатная функция, типы её аргументов и её параметры (для параметрических агрегатных функций). */ class DataTypeAggregateFunction : public IDataType { private: AggregateFunctionPtr function; DataTypes argument_types; + Array parameters; public: - DataTypeAggregateFunction(const AggregateFunctionPtr & function_, const DataTypes & argument_types_) - : function(function_), argument_types(argument_types_) + DataTypeAggregateFunction(const AggregateFunctionPtr & function_, const DataTypes & argument_types_, const Array & parameters_) + : function(function_), argument_types(argument_types_), parameters(parameters_) { - function->setArguments(argument_types); } std::string getName() const @@ -31,6 +31,18 @@ public: std::stringstream stream; stream << "AggregateFunction(" << function->getName(); + if (!parameters.empty()) + { + stream << "("; + for (size_t i = 0; i < parameters.size(); ++i) + { + if (i) + stream << ", "; + stream << apply_visitor(DB::FieldVisitorToString(), parameters[i]); + } + stream << ")"; + } + for (DataTypes::const_iterator it = argument_types.begin(); it != argument_types.end(); ++it) stream << ", " << (*it)->getName(); @@ -38,7 +50,7 @@ public: return stream.str(); } - DataTypePtr clone() const { return new DataTypeAggregateFunction(function, argument_types); } + DataTypePtr clone() const { return new DataTypeAggregateFunction(function, argument_types, parameters); } void serializeBinary(const Field & field, WriteBuffer & ostr) const; void deserializeBinary(Field & field, ReadBuffer & istr) const; diff --git a/dbms/include/DB/Interpreters/Aggregator.h b/dbms/include/DB/Interpreters/Aggregator.h index 3cd9464fbab..b324bfbbbb3 100644 --- a/dbms/include/DB/Interpreters/Aggregator.h +++ b/dbms/include/DB/Interpreters/Aggregator.h @@ -27,6 +27,7 @@ namespace DB struct AggregateDescription { AggregateFunctionPtr function; + Array parameters; /// Параметры (параметрической) агрегатной функции. ColumnNumbers arguments; Names argument_names; /// Используются, если arguments не заданы. String column_name; /// Какое имя использовать для столбца со значениями агрегатной функции diff --git a/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp b/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp index 028da3692e4..2fbdeddf913 100644 --- a/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp +++ b/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp @@ -129,7 +129,7 @@ void CollapsingSortedBlockInputStream::merge(Block & merged_block, ColumnPlainPt /// Запишем данные для предыдущего визита. insertRows(merged_columns, merged_rows); - current_key = next_key; + current_key = std::move(next_key); next_key.resize(description.size()); count_negative = 0; diff --git a/dbms/src/DataStreams/SummingSortedBlockInputStream.cpp b/dbms/src/DataStreams/SummingSortedBlockInputStream.cpp index c49f05349f4..48b100049e0 100644 --- a/dbms/src/DataStreams/SummingSortedBlockInputStream.cpp +++ b/dbms/src/DataStreams/SummingSortedBlockInputStream.cpp @@ -90,7 +90,7 @@ void SummingSortedBlockInputStream::merge(Block & merged_block, ColumnPlainPtrs insertCurrentRow(merged_columns); } - current_key = next_key; + current_key = std::move(next_key); next_key.resize(description.size()); setRow(current_row, current); diff --git a/dbms/src/DataTypes/DataTypeFactory.cpp b/dbms/src/DataTypes/DataTypeFactory.cpp index b5b812426f5..6fe29be3efe 100644 --- a/dbms/src/DataTypes/DataTypeFactory.cpp +++ b/dbms/src/DataTypes/DataTypeFactory.cpp @@ -21,14 +21,15 @@ #include #include #include +#include namespace DB { DataTypeFactory::DataTypeFactory() - : fixed_string_regexp("^FixedString\\s*\\(\\s*(\\d+)\\s*\\)$"), - nested_regexp("^(\\w+)\\s*\\(\\s*(.+)\\s*\\)$", Poco::RegularExpression::RE_MULTILINE | Poco::RegularExpression::RE_DOTALL) + : fixed_string_regexp(R"--(^FixedString\s*\(\s*(\d+)\s*\)$)--"), + nested_regexp(R"--(^(\w+)\s*\(\s*(.+)\s*\)$)--", Poco::RegularExpression::RE_MULTILINE | Poco::RegularExpression::RE_DOTALL) { boost::assign::insert(non_parametric_data_types) ("UInt8", new DataTypeUInt8) @@ -71,6 +72,7 @@ DataTypePtr DataTypeFactory::get(const String & name) const String function_name; AggregateFunctionPtr function; DataTypes argument_types; + Array params_row; ParserExpressionList args_parser; ASTPtr args_ast; @@ -87,14 +89,38 @@ DataTypePtr DataTypeFactory::get(const String & name) const throw Exception("Data type AggregateFunction requires parameters: " "name of aggregate function and list of data types for arguments", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - ASTs::iterator it = args_list.children.begin(); - function_name = (*it)->getColumnName(); + if (ASTFunction * parametric = dynamic_cast(&*args_list.children[0])) + { + if (parametric->parameters) + throw Exception("Unexpected level of parameters to aggregate function", ErrorCodes::SYNTAX_ERROR); + function_name = parametric->name; - for (++it; it != args_list.children.end(); ++it) - argument_types.push_back(get((*it)->getColumnName())); + ASTs & parameters = dynamic_cast(*parametric->arguments).children; + params_row.resize(parameters.size()); + + for (size_t i = 0; i < parameters.size(); ++i) + { + ASTLiteral * lit = dynamic_cast(&*parameters[i]); + if (!lit) + throw Exception("Parameters to aggregate functions must be literals", + ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS); + + params_row[i] = lit->value; + } + } + else + { + function_name = args_list.children[0]->getColumnName(); + } + + for (size_t i = 1; i < args_list.children.size(); ++i) + argument_types.push_back(get(args_list.children[i]->getColumnName())); function = AggregateFunctionFactory().get(function_name, argument_types); - return new DataTypeAggregateFunction(function, argument_types); + if (!params_row.empty()) + function->setParameters(params_row); + function->setArguments(argument_types); + return new DataTypeAggregateFunction(function, argument_types, params_row); } if (base_name == "Nested") diff --git a/dbms/src/Interpreters/Aggregator.cpp b/dbms/src/Interpreters/Aggregator.cpp index 71a7b14e060..df5c9f58e34 100644 --- a/dbms/src/Interpreters/Aggregator.cpp +++ b/dbms/src/Interpreters/Aggregator.cpp @@ -86,7 +86,7 @@ void Aggregator::initialize(Block & block) for (size_t j = 0; j < arguments_size; ++j) argument_types[j] = block.getByPosition(aggregates[i].arguments[j]).type; - col.type = new DataTypeAggregateFunction(aggregates[i].function, argument_types); + col.type = new DataTypeAggregateFunction(aggregates[i].function, argument_types, aggregates[i].parameters); col.column = new ColumnAggregateFunction(aggregates[i].function); sample.insert(col); diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 4d06a422031..38aaeabd661 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -1004,7 +1004,7 @@ void ExpressionAnalyzer::getAggregatesImpl(ASTPtr ast, ExpressionActions & actio if (node->parameters) { ASTs & parameters = dynamic_cast(*node->parameters).children; - Row params_row(parameters.size()); + Array params_row(parameters.size()); for (size_t i = 0; i < parameters.size(); ++i) { @@ -1015,6 +1015,7 @@ void ExpressionAnalyzer::getAggregatesImpl(ASTPtr ast, ExpressionActions & actio params_row[i] = lit->value; } + aggregate.parameters = params_row; aggregate.function->setParameters(params_row); } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 518a42c2f39..009ca1ca9a3 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -293,7 +293,7 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() settings.limits.max_rows_to_group_by && settings.limits.group_by_overflow_mode == OverflowMode::ANY && settings.totals_mode != TotalsMode::AFTER_HAVING_EXCLUSIVE; - /// Нужно ли после агрегации сразу финализироыать агрегатные функции. + /// Нужно ли после агрегации сразу финализировать агрегатные функции. bool aggregate_final = need_aggregate && to_stage > QueryProcessingStage::WithMergeableState && From 755fa64369701dc61da626ab0a41526843353897 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Tue, 25 Mar 2014 22:19:08 +0400 Subject: [PATCH 056/281] Fixed build. [#METR-10566] --- dbms/include/DB/Common/AutoArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Common/AutoArray.h b/dbms/include/DB/Common/AutoArray.h index 454e430efdd..350fdb4f50a 100644 --- a/dbms/include/DB/Common/AutoArray.h +++ b/dbms/include/DB/Common/AutoArray.h @@ -96,7 +96,7 @@ public: AutoArray & operator= (AutoArray && src) { if (this == &src) - return; + return *this; uninit(); data = src.data; src.setEmpty(); From 42be4d6ec5102b021bbc02e71e94997faa8485e5 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Tue, 25 Mar 2014 23:19:22 +0400 Subject: [PATCH 057/281] Added an option for clickhouse-test to generate reference for just one test. Added reference for one test query. [#METR-10566] --- dbms/tests/clickhouse-test | 4 ++-- .../0_stateless/00019_quantiles_totals_distributed.reference | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.reference diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 3300a944d0f..6911441e468 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -68,11 +68,11 @@ do cat $result_file elif [ ! -e "$reference_file" ]; then # надо сгенерировать эталонный результат - if [ "$1" == "--generate" ]; then + if [ "$1" == "--generate" ] && ( [ "$2" == "" ] || [ "$2" == "$test_name" ] ); then cp $result_file $reference_file echo -e "$MSG_GENERATED - no reference file" else - echo -e "$MSG_UNKNOWN - no reference file (use --generate to create)" + echo -e "$MSG_UNKNOWN - no reference file (use --generate [test_name] to create)" fi else diff $reference_file $result_file > $diff_file diff --git a/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.reference b/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.reference new file mode 100644 index 00000000000..7575632704a --- /dev/null +++ b/dbms/tests/queries/0_stateless/00019_quantiles_totals_distributed.reference @@ -0,0 +1,3 @@ +[0,0,0] + +[0,0,0] From 82f6b4a73d46f98411829374efc2c277fb06e781 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 02:53:48 +0400 Subject: [PATCH 058/281] dbms: removed old test script [#METR-2944]. --- dbms/tests/test_runner.sh | 91 --------------------------------------- 1 file changed, 91 deletions(-) delete mode 100755 dbms/tests/test_runner.sh diff --git a/dbms/tests/test_runner.sh b/dbms/tests/test_runner.sh deleted file mode 100755 index 0418251a98d..00000000000 --- a/dbms/tests/test_runner.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -# script to run query to databases - -function usage() -{ - cat < $log - queries=("${@}") - queries_count=${#queries[@]} - - index=0 - while [ "$index" -lt "$queries_count" ]; do - query=${queries[$index]} - - if [[ $query == "" ]]; then - let "index = $index + 1" - continue - fi - - comment_re='--.*' - if [[ $query =~ $comment_re ]]; then - echo "$query" - echo - else - echo "query:" "$query" - expect -c "#!/bin/bash -#!/bin/expect - -# Set timeout -set timeout 600 - -# Get arguments -set query [lindex $argv 0] - -spawn clickhouse-client --multiline; -expect \":) \" -send \"$query;\r\"; -expect \":) \" -send \"quit\";" >> "$log" - fi - let "index = $index + 1" - done - - echo "stop time: $(date)" >> $log -} - -mapfile -t test_queries < $test_file - -execute "${test_queries[@]}" - -echo "Error list" -cat $log - -echo -echo Error list\: -cat $log | grep -iP 'error|exception' From 6aa304ba4a0ad37127e674c46875b19cd407251b Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 03:14:51 +0400 Subject: [PATCH 059/281] dbms: added tests [#METR-10572]. --- dbms/tests/queries/0_stateless/00020_sorting_arrays.reference | 4 ++++ dbms/tests/queries/0_stateless/00020_sorting_arrays.sql | 1 + dbms/tests/queries/0_stateless/00021_sorting_arrays.reference | 4 ++++ dbms/tests/queries/0_stateless/00021_sorting_arrays.sql | 1 + 4 files changed, 10 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00020_sorting_arrays.reference create mode 100644 dbms/tests/queries/0_stateless/00020_sorting_arrays.sql create mode 100644 dbms/tests/queries/0_stateless/00021_sorting_arrays.reference create mode 100644 dbms/tests/queries/0_stateless/00021_sorting_arrays.sql diff --git a/dbms/tests/queries/0_stateless/00020_sorting_arrays.reference b/dbms/tests/queries/0_stateless/00020_sorting_arrays.reference new file mode 100644 index 00000000000..95c9078cae1 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00020_sorting_arrays.reference @@ -0,0 +1,4 @@ +[1,1] +[2] +[3,4,5] +[6,7] diff --git a/dbms/tests/queries/0_stateless/00020_sorting_arrays.sql b/dbms/tests/queries/0_stateless/00020_sorting_arrays.sql new file mode 100644 index 00000000000..69ddf6c2b0d --- /dev/null +++ b/dbms/tests/queries/0_stateless/00020_sorting_arrays.sql @@ -0,0 +1 @@ +SELECT arrayJoin([[3,4,5], [6,7], [2], [1,1]]) AS x ORDER BY x diff --git a/dbms/tests/queries/0_stateless/00021_sorting_arrays.reference b/dbms/tests/queries/0_stateless/00021_sorting_arrays.reference new file mode 100644 index 00000000000..9ff07694305 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00021_sorting_arrays.reference @@ -0,0 +1,4 @@ +[6,7] +[3,4,5] +[2] +[1,1] diff --git a/dbms/tests/queries/0_stateless/00021_sorting_arrays.sql b/dbms/tests/queries/0_stateless/00021_sorting_arrays.sql new file mode 100644 index 00000000000..e2034d94fa2 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00021_sorting_arrays.sql @@ -0,0 +1 @@ +SELECT arrayJoin([[3,4,5], [6,7], [2], [1,1]]) AS x ORDER BY x DESC From 369767dde935ae5e606a1cf9e00a129eb2f7b887 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 03:15:14 +0400 Subject: [PATCH 060/281] dbms: test: allowed to specify many test names to generate reference result [#METR-10572]. --- dbms/tests/clickhouse-test | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 6911441e468..3fcc18f5c5a 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -21,6 +21,15 @@ MSG_GENERATED="${COLOR_WHITE}[ ${COLOR_UNKNOWN}GENERATED${COLOR_WHITE} ]${COLOR_ ERRORS=0 + +if [ "$1" == "--generate" ]; then + GENERATE=1 + shift +else + GENERATE=0 +fi + + for dir in $(ls $QUERIES_DIR) do tests_name=$(echo $dir | sed -E 's/^[0-9_]+//') @@ -68,11 +77,11 @@ do cat $result_file elif [ ! -e "$reference_file" ]; then # надо сгенерировать эталонный результат - if [ "$1" == "--generate" ] && ( [ "$2" == "" ] || [ "$2" == "$test_name" ] ); then + if [[ $GENERATE -eq 1 && ( -z "$1" || "$@" =~ "$test_name") ]]; then cp $result_file $reference_file echo -e "$MSG_GENERATED - no reference file" else - echo -e "$MSG_UNKNOWN - no reference file (use --generate [test_name] to create)" + echo -e "$MSG_UNKNOWN - no reference file (use --generate [test_name]... to create)" fi else diff $reference_file $result_file > $diff_file From 78e59c0394a291c287ecdd26f27650252939c78b Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 03:16:20 +0400 Subject: [PATCH 061/281] dbms: fixed sorting of arrays [#METR-10572]. --- dbms/include/DB/Columns/ColumnArray.h | 39 +++++++++++---------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/dbms/include/DB/Columns/ColumnArray.h b/dbms/include/DB/Columns/ColumnArray.h index 3b697510e22..97901d5daad 100644 --- a/dbms/include/DB/Columns/ColumnArray.h +++ b/dbms/include/DB/Columns/ColumnArray.h @@ -229,7 +229,7 @@ public: return res; } - int compareAt(size_t n, size_t m, const IColumn & rhs_, int nan_direction_hint) const + int compareAt(size_t n, size_t m, const IColumn & rhs_, int nan_direction_hint) const final { const ColumnArray & rhs = static_cast(rhs_); @@ -238,7 +238,7 @@ public: size_t rhs_size = rhs.sizeAt(m); size_t min_size = std::min(lhs_size, rhs_size); for (size_t i = 0; i < min_size; ++i) - if (int res = data->compareAt(offsetAt(n) + i, rhs.offsetAt(m) + i, *rhs.data, nan_direction_hint)) + if (int res = data.get()->compareAt(offsetAt(n) + i, rhs.offsetAt(m) + i, *rhs.data.get(), nan_direction_hint)) return res; return lhs_size < rhs_size @@ -252,50 +252,41 @@ public: struct less { const ColumnArray & parent; - const Permutation & nested_perm; - less(const ColumnArray & parent_, const Permutation & nested_perm_) : parent(parent_), nested_perm(nested_perm_) {} + less(const ColumnArray & parent_) : parent(parent_) {} + bool operator()(size_t lhs, size_t rhs) const { - size_t lhs_size = parent.sizeAt(lhs); - size_t rhs_size = parent.sizeAt(rhs); - size_t min_size = std::min(lhs_size, rhs_size); - for (size_t i = 0; i < min_size; ++i) - { - if (nested_perm[parent.offsetAt(lhs) + i] < nested_perm[parent.offsetAt(rhs) + i]) - return positive; - else if (nested_perm[parent.offsetAt(lhs) + i] > nested_perm[parent.offsetAt(rhs) + i]) - return !positive; - } - return positive == (lhs_size < rhs_size); + if (positive) + return parent.compareAt(lhs, rhs, parent, 1) < 0; + else + return parent.compareAt(lhs, rhs, parent, -1) > 0; } }; void getPermutation(bool reverse, size_t limit, Permutation & res) const { - Permutation nested_perm; - data->getPermutation(reverse, limit, nested_perm); size_t s = size(); + if (limit > s) + limit = 0; + res.resize(s); for (size_t i = 0; i < s; ++i) res[i] = i; - if (limit > s) - limit = 0; - if (limit) { if (reverse) - std::partial_sort(res.begin(), res.begin() + limit, res.end(), less(*this, nested_perm)); + std::partial_sort(res.begin(), res.begin() + limit, res.end(), less(*this)); else - std::partial_sort(res.begin(), res.begin() + limit, res.end(), less(*this, nested_perm)); + std::partial_sort(res.begin(), res.begin() + limit, res.end(), less(*this)); } else { if (reverse) - std::sort(res.begin(), res.end(), less(*this, nested_perm)); + std::sort(res.begin(), res.end(), less(*this)); else - std::sort(res.begin(), res.end(), less(*this, nested_perm)); + std::sort(res.begin(), res.end(), less(*this)); } } From f0f09d00fd5229f06d0abcbe7c6e60083cb81160 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 04:17:31 +0400 Subject: [PATCH 062/281] dbms: fixed unitialized memory read [#METR-10597]. --- dbms/include/DB/IO/CompressedReadBufferBase.h | 1 - dbms/include/DB/IO/CompressedReadBufferFromFile.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dbms/include/DB/IO/CompressedReadBufferBase.h b/dbms/include/DB/IO/CompressedReadBufferBase.h index d6eb20ccf7d..b2bd8a7b60f 100644 --- a/dbms/include/DB/IO/CompressedReadBufferBase.h +++ b/dbms/include/DB/IO/CompressedReadBufferBase.h @@ -26,7 +26,6 @@ protected: /// Если в буфере compressed_in помещается целый сжатый блок - используем его. Иначе - копируем данные по кусочкам в own_compressed_buffer. PODArray own_compressed_buffer; char * compressed_buffer; - size_t size_compressed; qlz_state_decompress * qlz_state; diff --git a/dbms/include/DB/IO/CompressedReadBufferFromFile.h b/dbms/include/DB/IO/CompressedReadBufferFromFile.h index 47cd9d7bcb1..fa9e0552a57 100644 --- a/dbms/include/DB/IO/CompressedReadBufferFromFile.h +++ b/dbms/include/DB/IO/CompressedReadBufferFromFile.h @@ -19,7 +19,7 @@ private: * - size_compressed содержит сжатый размер этого блока. */ ReadBufferFromFile file_in; - size_t size_compressed; + size_t size_compressed = 0; bool nextImpl() { From 651e593bb2f1d6b2593a50bd9145f6277d591f48 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 04:34:00 +0400 Subject: [PATCH 063/281] dbms: fixed error with aggregate function quantileTiming [#METR-10597]. --- .../DB/AggregateFunctions/AggregateFunctionQuantileTiming.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h index 06ae5222c41..86b8cd78389 100644 --- a/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionQuantileTiming.h @@ -324,11 +324,13 @@ private: void toLarge() { - large = new detail::QuantileTimingLarge; + /// На время копирования данных из tiny, устанавливать значение large ещё нельзя (иначе оно перезатрёт часть данных). + detail::QuantileTimingLarge * tmp_large = new detail::QuantileTimingLarge; for (size_t i = 0; i < tiny.count; ++i) - large->insert(tiny.elems[i]); + tmp_large->insert(tiny.elems[i]); + large = tmp_large; tiny.count = TINY_MAX_ELEMS + 1; } From 4042069b3eb35dbfa51a4524a9feb800c946bacf Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 26 Mar 2014 18:08:00 +0400 Subject: [PATCH 064/281] fixed build --- dbms/src/Common/tests/auto_array.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Common/tests/auto_array.cpp b/dbms/src/Common/tests/auto_array.cpp index fd0aff7ea76..3d8ae6fa9a0 100644 --- a/dbms/src/Common/tests/auto_array.cpp +++ b/dbms/src/Common/tests/auto_array.cpp @@ -55,7 +55,7 @@ int main(int argc, char ** argv) std::cerr << std::endl; - Arr arr2 = arr; + Arr arr2 = std::move(arr); std::cerr << arr.size() << ", " << arr2.size() << std::endl; From e060784ecca1120396f5d4aea2888ec01ae69e72 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 26 Mar 2014 18:45:50 +0400 Subject: [PATCH 065/281] fixed build --- dbms/src/Common/tests/auto_array.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dbms/src/Common/tests/auto_array.cpp b/dbms/src/Common/tests/auto_array.cpp index 3d8ae6fa9a0..43c8c359f9f 100644 --- a/dbms/src/Common/tests/auto_array.cpp +++ b/dbms/src/Common/tests/auto_array.cpp @@ -79,7 +79,7 @@ int main(int argc, char ** argv) for (size_t j = 0; j < n; ++j) key[j] = DB::toString(rand()); - map[key] = "Hello, world! " + DB::toString(i); + map[std::move(key)] = "Hello, world! " + DB::toString(i); } for (Map::const_iterator it = map.begin(); it != map.end(); ++it) @@ -94,7 +94,7 @@ int main(int argc, char ** argv) std::cerr << std::endl; - Map map2 = map; + Map map2 = std::move(map); for (Map::const_iterator it = map2.begin(); it != map2.end(); ++it) { @@ -123,7 +123,7 @@ int main(int argc, char ** argv) for (size_t j = 0; j < n; ++j) key[j] = DB::toString(rand()); - vec.push_back(key); + vec.push_back(std::move(key)); } for (Vec::const_iterator it = vec.begin(); it != vec.end(); ++it) @@ -136,7 +136,7 @@ int main(int argc, char ** argv) std::cerr << std::endl; - Vec vec2 = vec; + Vec vec2 = std::move(vec); for (Vec::const_iterator it = vec2.begin(); it != vec2.end(); ++it) { @@ -224,7 +224,7 @@ int main(int argc, char ** argv) arr2[i] = "Goodbye, world! " + DB::toString(i); } - arr2 = arr1; + arr2 = std::move(arr1); arr1.resize(n); std::cerr From 966995b011e3112f23190f7f5d2f15ef1642e194 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 26 Mar 2014 22:29:30 +0400 Subject: [PATCH 066/281] Fixed build [#METR-2807]. --- dbms/src/Interpreters/tests/hash_map.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dbms/src/Interpreters/tests/hash_map.cpp b/dbms/src/Interpreters/tests/hash_map.cpp index 1bac5fb5f3a..684df7bc3fd 100644 --- a/dbms/src/Interpreters/tests/hash_map.cpp +++ b/dbms/src/Interpreters/tests/hash_map.cpp @@ -48,7 +48,7 @@ * Но в этом тесте осталось нечто похожее на старый сценарий использования хэш-таблиц при агрегации. */ -#define USE_AUTO_ARRAY 1 +#define USE_AUTO_ARRAY 0 int main(int argc, char ** argv) @@ -131,7 +131,7 @@ int main(int argc, char ** argv) map.emplace(data[i], it, inserted); if (inserted) { - new(&it->second) Value(value); + new(&it->second) Value(std::move(value)); INIT; } } @@ -155,7 +155,7 @@ int main(int argc, char ** argv) std::unordered_map >::iterator it; for (size_t i = 0; i < n; ++i) { - it = map.insert(std::make_pair(data[i], value)).first; + it = map.insert(std::make_pair(data[i], std::move(value))).first; INIT; } @@ -176,7 +176,7 @@ int main(int argc, char ** argv) map.set_empty_key(-1ULL); for (size_t i = 0; i < n; ++i) { - it = map.insert(std::make_pair(data[i], value)).first; + it = map.insert(std::make_pair(data[i], std::move(value))).first; INIT; } @@ -196,7 +196,7 @@ int main(int argc, char ** argv) google::sparse_hash_map >::iterator it; for (size_t i = 0; i < n; ++i) { - map.insert(std::make_pair(data[i], value)); + map.insert(std::make_pair(data[i], std::move(value))); INIT; } From 64251e04d1545c439ea6c1c9dcc04b9ddea9d48e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 27 Mar 2014 02:23:54 +0400 Subject: [PATCH 067/281] dbms: added tests [#METR-8757] [#METR-9119]. --- .../0_stateless/00022_func_higher_order_and_constants.reference | 1 + .../0_stateless/00022_func_higher_order_and_constants.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.reference create mode 100644 dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.sql diff --git a/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.reference b/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.reference @@ -0,0 +1 @@ +1 diff --git a/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.sql b/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.sql new file mode 100644 index 00000000000..c2831e52763 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00022_func_higher_order_and_constants.sql @@ -0,0 +1 @@ +select arrayExists(x -> position(x, 'a') > 0, ['a']) From 8bbee1f1c18ec0818226fdb5c9260cf2cc6bc933 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 26 Mar 2014 23:44:51 +0400 Subject: [PATCH 068/281] dbms: Added HashingWriteBuffer and a unit test. [#METR-10202] --- dbms/include/DB/IO/HashingWriteBuffer.h | 97 ++++++++++++++++++++ dbms/src/IO/tests/hashing_write_buffer.cpp | 102 +++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 dbms/include/DB/IO/HashingWriteBuffer.h create mode 100644 dbms/src/IO/tests/hashing_write_buffer.cpp diff --git a/dbms/include/DB/IO/HashingWriteBuffer.h b/dbms/include/DB/IO/HashingWriteBuffer.h new file mode 100644 index 00000000000..b25d50aeaf9 --- /dev/null +++ b/dbms/include/DB/IO/HashingWriteBuffer.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include + +#define DBMS_DEFAULT_HASHING_BLOCK_SIZE 2048ULL + + +namespace DB +{ + +/** Вычисляет хеш от записываемых данных и передает их в указанный WriteBuffer. + * В качестве основного буфера используется буфер вложенного WriteBuffer. + */ +class HashingWriteBuffer : public BufferWithOwnMemory +{ +private: + WriteBuffer & out; + + size_t block_size; + size_t block_pos; + uint128 state; + + void append(Position data) + { + state = CityHash128WithSeed(data, block_size, state); + } + + void nextImpl() override + { + size_t len = offset(); + + if (len) + { + Position data = working_buffer.begin(); + + if (block_pos + len < block_size) + { + memcpy(&memory[block_pos], data, len); + block_pos += len; + } + else + { + if (block_pos) + { + size_t n = block_size - block_pos; + memcpy(&memory[block_pos], data, n); + append(&memory[0]); + len -= n; + data += n; + block_pos = 0; + } + + while (len >= block_size) + { + append(data); + len -= block_size; + data += block_size; + } + + if (len) + { + memcpy(&memory[0], data, len); + block_pos = len; + } + } + } + + out.position() = pos; + out.next(); + working_buffer = out.buffer(); + } + +public: + HashingWriteBuffer( + WriteBuffer & out_, + size_t block_size_ = DBMS_DEFAULT_HASHING_BLOCK_SIZE) + : BufferWithOwnMemory(block_size_), out(out_), block_size(block_size_), block_pos(0) + { + out.next(); /// Если до нас в out что-то уже писали, не дадим остаткам этих данных повлиять на хеш. + working_buffer = out.buffer(); + pos = working_buffer.begin(); + state = uint128(0, 0); + } + + uint128 getHash() + { + next(); + if (block_pos) + return CityHash128WithSeed(&memory[0], block_pos, state); + else + return state; + } +}; + +} diff --git a/dbms/src/IO/tests/hashing_write_buffer.cpp b/dbms/src/IO/tests/hashing_write_buffer.cpp new file mode 100644 index 00000000000..1143ae759af --- /dev/null +++ b/dbms/src/IO/tests/hashing_write_buffer.cpp @@ -0,0 +1,102 @@ +#include +#include + +#define FAIL(msg) { std::cout << msg; exit(1); } + + +uint128 referenceHash(char * data, size_t len) +{ + const size_t block_size = DBMS_DEFAULT_HASHING_BLOCK_SIZE; + uint128 state(0, 0); + size_t pos; + + for (pos = 0; pos + block_size <= len; pos += block_size) + { + state = CityHash128WithSeed(data + pos, block_size, state); + } + + if (pos < len) + state = CityHash128WithSeed(data + pos, len - pos, state); + + return state; +} + + +void test(size_t data_size) +{ + std::vector vec(data_size); + char * data = &vec[0]; + + for (size_t i = 0; i < data_size; ++i) + data[i] = rand() & 255; + + uint128 reference = referenceHash(data, data_size); + + DB::WriteBufferFromFile sink("/dev/null", 1 << 16); + + { + DB::HashingWriteBuffer buf(sink); + + for (size_t pos = 0; pos < data_size;) + { + size_t len = std::min(static_cast(rand() % 10000 + 1), data_size - pos); + buf.write(data + pos, len); + buf.next(); + pos += len; + } + + if (buf.getHash() != reference) + FAIL("failed on data size " << data_size << " writing random chunks of up to 10000 bytes"); + } + + { + DB::HashingWriteBuffer buf(sink); + + for (size_t pos = 0; pos < data_size;) + { + size_t len = std::min(static_cast(rand() % 5 + 1), data_size - pos); + buf.write(data + pos, len); + buf.next(); + pos += len; + } + + if (buf.getHash() != reference) + FAIL("failed on data size " << data_size << " writing random chunks of up to 5 bytes"); + } + + { + DB::HashingWriteBuffer buf(sink); + + for (size_t pos = 0; pos < data_size;) + { + size_t len = std::min(static_cast(2048 + rand() % 3 - 1), data_size - pos); + buf.write(data + pos, len); + buf.next(); + pos += len; + } + + if (buf.getHash() != reference) + FAIL("failed on data size " << data_size << " writing random chunks of 2048 +-1 bytes"); + } + + { + DB::HashingWriteBuffer buf(sink); + + buf.write(data, data_size); + + if (buf.getHash() != reference) + FAIL("failed on data size " << data_size << " writing all at once"); + } +} + +int main() +{ + test(5); + test(100); + test(2048); + test(2049); + test(100000); + test(1 << 17); + + return 0; +} From 45dcf66dd8b9fc259c3849c66e7440ebce8b56e7 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 12:40:35 +0400 Subject: [PATCH 069/281] Added test query. [#METR-10615] --- .../queries/0_stateless/00023_agg_select_agg_subquery.reference | 1 + dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.reference create mode 100644 dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql diff --git a/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.reference b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.reference @@ -0,0 +1 @@ +1 diff --git a/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql new file mode 100644 index 00000000000..398b4ff8b89 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql @@ -0,0 +1 @@ +SELECT count() FROM (SELECT sum(1), sum(2)) From db3c061396bb1ee8a06df8c13f90d43cba5eb595 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 15:29:40 +0400 Subject: [PATCH 070/281] Added checksums to data parts. Not writing them yet. [#METR-10202] --- dbms/include/DB/Core/ErrorCodes.h | 3 + dbms/include/DB/IO/ReadHelpers.h | 1 + .../DB/Storages/MergeTree/MergeTreeData.h | 37 +++++++++ dbms/src/IO/ReadHelpers.cpp | 6 ++ dbms/src/Storages/MergeTree/MergeTreeData.cpp | 77 +++++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index b875e465ba4..a5a36a09f18 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -227,6 +227,9 @@ namespace ErrorCodes CLIENT_HAS_CONNECTED_TO_WRONG_PORT, TABLE_IS_DROPPED, DATABASE_NOT_EMPTY, + NO_FILE_IN_DATA_PART, + UNEXPECTED_FILE_IN_DATA_PART, + BAD_SIZE_OF_FILE_IN_DATA_PART, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/IO/ReadHelpers.h b/dbms/include/DB/IO/ReadHelpers.h index cb336d7194b..d45fadb78ff 100644 --- a/dbms/include/DB/IO/ReadHelpers.h +++ b/dbms/include/DB/IO/ReadHelpers.h @@ -114,6 +114,7 @@ inline void readChar(char & x, ReadBuffer & buf) } void assertString(const char * s, ReadBuffer & buf); +void assertEOF(ReadBuffer & buf); inline void readBoolText(bool & x, ReadBuffer & buf) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 593eef78765..dc7af53d186 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -33,6 +33,7 @@ namespace DB * Структура файлов: * / min-date _ max-date _ min-id _ max-id _ level / - директория с куском. * Внутри директории с куском: + * checksums.txt - список файлов с их размерами и контрольными суммами. * primary.idx - индексный файл. * Column.bin - данные столбца * Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк. @@ -99,6 +100,28 @@ public: /// Описание куска с данными. struct DataPart { + /** Контрольные суммы всех не временных файлов. + * Для сжатых файлов хранятся чексумма и размер разжатых данных, чтобы не зависеть от способа сжатия. + */ + struct Checksums + { + struct Checksum + { + size_t size; + uint128 hash; + }; + + typedef std::map FileChecksums; + FileChecksums file_checksums; + + /// Проверяет, что множество столбцов и их контрольные суммы совпадают. Если нет - бросает исключение. + void check(const Checksums & rhs) const; + + /// Сериализует и десериализует в человекочитаемом виде. + void readText(ReadBuffer & in); + void writeText(WriteBuffer & out) const; + }; + DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} MergeTreeData & storage; @@ -121,6 +144,8 @@ public: typedef std::vector Index; Index index; + Checksums checksums; + /// NOTE можно загружать засечки тоже в оперативку /// Вычисляем сумарный размер всей директории со всеми файлами @@ -204,6 +229,18 @@ public: size_in_bytes = calcTotalSize(storage.full_path + name + "/"); } + + /// Прочитать контрольные суммы, если есть. + bool loadChecksums() + { + String path = storage.full_path + name + "/checksums.txt"; + if (!Poco::File(path).exists()) + return false; + ReadBufferFromFile file(path, std::min(static_cast(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(path).getSize())); + checksums.readText(file); + assertEOF(file); + return true; + } }; typedef std::shared_ptr MutableDataPartPtr; diff --git a/dbms/src/IO/ReadHelpers.cpp b/dbms/src/IO/ReadHelpers.cpp index dcb42e86d54..dac6048f6d7 100644 --- a/dbms/src/IO/ReadHelpers.cpp +++ b/dbms/src/IO/ReadHelpers.cpp @@ -36,6 +36,12 @@ void assertString(const char * s, ReadBuffer & buf) } } +void assertEOF(ReadBuffer & buf) +{ + if (!buf.eof()) + throwAtAssertionFailed("eof", buf); +} + void readString(String & s, ReadBuffer & buf) { s = ""; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 580504feb0e..4199283126f 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -212,6 +212,7 @@ void MergeTreeData::loadDataParts() try { part->loadIndex(); + part->loadChecksums(); } catch (...) { @@ -687,4 +688,80 @@ MergeTreeData::DataParts MergeTreeData::getDataParts() return data_parts; } + +void MergeTreeData::DataPart::Checksums::check(const Checksums & rhs) const +{ + for (const auto & it : rhs.file_checksums) + { + const String & name = it.first; + + if (!file_checksums.count(name)) + throw Exception("Unexpected file " + name + " in data part", ErrorCodes::UNEXPECTED_FILE_IN_DATA_PART); + } + + for (const auto & it : file_checksums) + { + const String & name = it.first; + + auto jt = rhs.file_checksums.find(name); + if (jt == rhs.file_checksums.end()) + throw Exception("No file " + name + " in data part", ErrorCodes::NO_FILE_IN_DATA_PART); + + const Checksum & expected = it.second; + const Checksum & found = jt->second; + + if (expected.size != found.size) + throw Exception("Unexpected size of file " + name + " in data part", ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART); + + if (expected.hash != found.hash) + throw Exception("Checksum mismatch for file " + name + " in data part", ErrorCodes::CHECKSUM_DOESNT_MATCH); + } +} + +void MergeTreeData::DataPart::Checksums::readText(ReadBuffer & in) +{ + file_checksums.clear(); + size_t count; + + DB::assertString("checksums format version: 1\n", in); + DB::readText(count, in); + DB::assertString(" files:\n", in); + + for (size_t i = 0; i < count; ++i) + { + String name; + Checksum sum; + + DB::readString(name, in); + DB::assertString("\n\tsize: ", in); + DB::readText(sum.size, in); + DB::assertString("\n\thash: ", in); + DB::readText(sum.hash.first, in); + DB::assertString(" ", in); + DB::readText(sum.hash.second, in); + DB::assertString("\n", in); + + file_checksums.insert(std::make_pair(name, sum)); + } +} + +void MergeTreeData::DataPart::Checksums::writeText(WriteBuffer & out) const +{ + DB::writeString("checksums format version: 1\n", out); + DB::writeText(file_checksums.size(), out); + DB::writeString(" files:\n", out); + + for (const auto & it : file_checksums) + { + DB::writeString(it.first, out); + DB::writeString("\n\tsize: ", out); + DB::writeText(it.second.size, out); + DB::writeString("\n\thash: ", out); + DB::writeText(it.second.hash.first, out); + DB::writeString(" ", out); + DB::writeText(it.second.hash.second, out); + DB::writeString("\n", out); + } +} + } From 2b7cdfd950dcfc741264910dada662f745aabfc8 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 15:30:54 +0400 Subject: [PATCH 071/281] Merge --- dbms/src/Storages/StorageMergeTree.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 5e2b2dfd549..fe3d5be0ffe 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -134,6 +134,8 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) { while (!shutdown_called) { + auto structure_lock = lockStructure(false); + /// Удаляем старые куски. На случай, если в слиянии что-то сломано, и из следующего блока вылетит исключение. data.clearOldParts(); @@ -161,17 +163,13 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) } } - { - auto structure_lock = lockStructure(false); - if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && - !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) - break; - } + if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && + !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) + break; merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); } - auto structure_lock = lockStructure(true); merger.mergeParts(merging_tagger->parts); } From b48bc12739301210862fe31c16529ed8187276cc Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 15:32:41 +0400 Subject: [PATCH 072/281] Merge --- .../Storages/MergeTree/MergeTreeDataWriter.h | 6 +- .../MergeTree/MergeTreeDataWriter.cpp | 81 ++++++++++--------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index 6e720bfd791..2220456ac33 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -34,12 +34,12 @@ public: MergeTreeDataWriter(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataWriter")), flags(O_TRUNC | O_CREAT | O_WRONLY) {} /** Разбивает блок на блоки, каждый из которых нужно записать в отдельный кусок. - * (читай: разбивает строки по месяцам) + * (читай: разбивает строки по месяцам) * Работает детерминированно: если отдать на вход такой же блок, на выходе получатся такие же блоки в таком же порядке. */ BlocksWithDateIntervals splitBlockIntoParts(const Block & block); - /** Все строки должны относиться к одному месяцу. Возвращает название временного куска. + /** Все строки должны относиться к одному месяцу. * temp_index - значение left и right для нового куска. Можно будет изменить при переименовании. * Возвращает кусок с именем, начинающимся с tmp_, еще не добавленный в MergeTreeData. */ @@ -56,7 +56,7 @@ private: /// Записать данные одного столбца. void writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, size_t level = 0); + OffsetColumns & offset_columns, MergeTreeData::DataPart::Checksums & checksums, size_t level = 0); }; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index a7c1550c7ee..e2ebfaab235 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace DB { @@ -104,12 +105,15 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa /// Наконец-то можно писать данные на диск. LOG_TRACE(log, "Writing index."); - /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. + MergeTreeData::DataPart::Checksums checksums; MergeTreeData::DataPart::Index index_vec; + + /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. index_vec.reserve(part_size * sort_descr.size()); { - WriteBufferFromFile index(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile index_file(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); + HashingWriteBuffer index(index_file); typedef std::vector PrimaryColumns; PrimaryColumns primary_columns; @@ -130,6 +134,8 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa } index.next(); + checksums.file_checksums["primary.idx"].size = index.count(); + checksums.file_checksums["primary.idx"].hash = index.getHash(); } LOG_TRACE(log, "Writing data."); @@ -140,7 +146,14 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa for (size_t i = 0; i < columns; ++i) { const ColumnWithNameAndType & column = block.getByPosition(i); - writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns); + writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns, checksums); + } + + /// Запишем файл с чексуммами. + { + WriteBufferFromFile checksums_file(part_tmp_path + "checksums.txt", 1024, flags); + checksums.writeText(checksums_file); + checksums_file.next(); } MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); @@ -155,12 +168,13 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); new_data_part->index.swap(index_vec); + new_data_part->checksums = checksums; return new_data_part; } void MergeTreeDataWriter::writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, size_t level) + OffsetColumns & offset_columns, MergeTreeData::DataPart::Checksums & checksums, size_t level) { String escaped_column_name = escapeForFileName(name); size_t size = column.size(); @@ -174,15 +188,17 @@ void MergeTreeDataWriter::writeData(const String & path, const String & name, co { offset_columns.insert(size_name); - WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); + WriteBufferFromFile plain_file(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile marks_file(path + size_name + ".mrk", 4096, flags); + CompressedWriteBuffer compressed_file(plain_file); + HashingWriteBuffer marks(marks_file); + HashingWriteBuffer compressed(compressed_file); size_t prev_mark = 0; while (prev_mark < size) { /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) - writeIntBinary(plain.count(), marks); + writeIntBinary(plain_file.count(), marks); writeIntBinary(compressed.offset(), marks); type_arr->serializeOffsets(column, compressed, prev_mark, data.index_granularity); @@ -192,45 +208,26 @@ void MergeTreeDataWriter::writeData(const String & path, const String & name, co } compressed.next(); - plain.next(); + plain_file.next(); marks.next(); + checksums.file_checksums[size_name + ".bin"].size = compressed.count(); + checksums.file_checksums[size_name + ".bin"].hash = compressed.getHash(); + checksums.file_checksums[size_name + ".mrk"].size = marks.count(); + checksums.file_checksums[size_name + ".mrk"].hash = marks.getHash(); } } - if (const DataTypeNested * type_nested = dynamic_cast(&type)) - { - String size_name = escaped_column_name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - WriteBufferFromFile plain(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + size_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); + { + WriteBufferFromFile plain_file(path + escaped_column_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); + WriteBufferFromFile marks_file(path + escaped_column_name + ".mrk", 4096, flags); + CompressedWriteBuffer compressed_file(plain_file); + HashingWriteBuffer marks(marks_file); + HashingWriteBuffer compressed(compressed_file); size_t prev_mark = 0; while (prev_mark < size) { - /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) - writeIntBinary(plain.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type_nested->serializeOffsets(column, compressed, prev_mark, data.index_granularity); - prev_mark += data.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain.next(); - marks.next(); - } - - { - WriteBufferFromFile plain(path + escaped_column_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks(path + escaped_column_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed(plain); - - size_t prev_mark = 0; - while (prev_mark < size) - { - writeIntBinary(plain.count(), marks); + writeIntBinary(plain_file.count(), marks); writeIntBinary(compressed.offset(), marks); type.serializeBinary(column, compressed, prev_mark, data.index_granularity); @@ -240,8 +237,12 @@ void MergeTreeDataWriter::writeData(const String & path, const String & name, co } compressed.next(); - plain.next(); + plain_file.next(); marks.next(); + checksums.file_checksums[escaped_column_name + ".bin"].size = compressed.count(); + checksums.file_checksums[escaped_column_name + ".bin"].hash = compressed.getHash(); + checksums.file_checksums[escaped_column_name + ".mrk"].size = marks.count(); + checksums.file_checksums[escaped_column_name + ".mrk"].hash = marks.getHash(); } } From 0c59630ddc07d3cf797e74a08eb4639293d98de6 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 4 Mar 2014 19:31:56 +0400 Subject: [PATCH 073/281] temporary tables: support of sending temporary talbes from client to server via tcp client, all previous versions should work fine with new one [METR-10071] --- dbms/include/DB/Client/Connection.h | 1 + dbms/include/DB/Core/Defines.h | 1 + dbms/include/DB/Interpreters/Context.h | 4 +- dbms/src/Client/Client.cpp | 3 ++ dbms/src/Client/Connection.cpp | 20 +++++++++ dbms/src/Interpreters/Context.cpp | 36 ++++++++++++++- dbms/src/Server/TCPHandler.cpp | 62 +++++++++++++++++++++----- dbms/src/Server/TCPHandler.h | 5 ++- 8 files changed, 116 insertions(+), 16 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index 2793e0b2e7c..a2f542eb0d5 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -84,6 +84,7 @@ public: void sendCancel(); void sendData(const Block & block); + void sendTemporaryTables(); /// Проверить, если ли данные, которые можно прочитать. bool poll(size_t timeout_microseconds = 0); diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index 487c322a726..b4908d5f7a6 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -40,3 +40,4 @@ #define DBMS_MIN_REVISION_WITH_USER_PASSWORD 34482 #define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265 #define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002 +#define DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES 50092 diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index cc71891d2d3..91aadb4c27f 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -172,7 +172,7 @@ private: String default_format; /// Формат, используемый, если сервер сам форматирует данные, и если в запросе не задан FORMAT. /// То есть, используется в HTTP-интерфейсе. Может быть не задан - тогда используется некоторый глобальный формат по-умолчанию. - + Tables temporary_tables; /// Временные таблицы. Context * session_context; /// Контекст сессии или NULL, если его нет. (Возможно, равен this.) Context * global_context; /// Глобальный контекст или NULL, если его нет. (Возможно, равен this.) @@ -209,8 +209,10 @@ public: void assertDatabaseExists(const String & database_name) const; void assertDatabaseDoesntExist(const String & database_name) const; + StoragePtr tryGetTemporaryTable(const String & table_name) const; StoragePtr getTable(const String & database_name, const String & table_name) const; StoragePtr tryGetTable(const String & database_name, const String & table_name) const; + void addTemporaryTable(const String & table_name, StoragePtr storage); void addTable(const String & database_name, const String & table_name, StoragePtr table); void addDatabase(const String & database_name); diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 9f42db41403..cb4e89912d0 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -432,6 +432,7 @@ private: void processOrdinaryQuery() { connection->sendQuery(query, "", QueryProcessingStage::Complete); + connection->sendTemporaryTables(); receiveResult(); } @@ -449,6 +450,7 @@ private: throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete); + connection->sendTemporaryTables(); /// Получим структуру таблицы Block sample = receiveSampleBlock(); @@ -857,6 +859,7 @@ private: int main(int argc, char ** argv) { DB::Client client; +// client.stopOptionsProcessing(); client.init(argc, argv); return client.run(); } diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 30c492cf068..f84b9204073 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -258,6 +258,10 @@ void Connection::sendData(const Block & block) } writeVarUInt(Protocol::Client::Data, *out); + + if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + writeStringBinary("", *out); + block.checkNestedArraysOffsets(); block_out->write(block); maybe_compressed_out->next(); @@ -265,6 +269,17 @@ void Connection::sendData(const Block & block) } +void Connection::sendTemporaryTables() +{ + /// Если работаем со старым сервером, то никакой информации не отправляем + if (server_revision < DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + return; + + /// Отправляем пустой блок, символизируя конец передачи данных + sendData(Block()); +} + + bool Connection::poll(size_t timeout_microseconds) { return static_cast(*in).poll(timeout_microseconds); @@ -336,6 +351,11 @@ Block Connection::receiveData() initBlockInput(); + String temporary_table_name; + + if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + readStringBinary(temporary_table_name, *in); + /// Прочитать из сети один блок return block_in->read(); } diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 1fcc148ff9d..cb88a2eaf96 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -161,23 +161,47 @@ void Context::assertDatabaseDoesntExist(const String & database_name) const } +StoragePtr Context::tryGetTemporaryTable(const String & table_name) const +{ + Poco::ScopedLock lock(shared->mutex); + + Tables::const_iterator jt; + if (temporary_tables.end() == (jt = temporary_tables.find(table_name))) + return StoragePtr(); + + return jt->second; +} + + StoragePtr Context::getTable(const String & database_name, const String & table_name) const { Poco::ScopedLock lock(shared->mutex); + Databases::const_iterator it; + Tables::const_iterator jt; + + if (database_name.empty()) + { + StoragePtr res; + if (res = tryGetTemporaryTable(table_name)) + return res; + if (res = session_context->tryGetTemporaryTable(table_name)) + return res; + if (res = global_context->tryGetTemporaryTable(table_name)) + return res; + } String db = database_name.empty() ? current_database : database_name; - Databases::const_iterator it; if (shared->databases.end() == (it = shared->databases.find(db))) throw Exception("Database " + db + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE); - Tables::const_iterator jt; if (it->second.end() == (jt = it->second.find(table_name))) throw Exception("Table " + db + "." + table_name + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE); return jt->second; } + StoragePtr Context::tryGetTable(const String & database_name, const String & table_name) const { Poco::ScopedLock lock(shared->mutex); @@ -196,6 +220,14 @@ StoragePtr Context::tryGetTable(const String & database_name, const String & tab } +void Context::addTemporaryTable(const String & table_name, StoragePtr storage) +{ + if (temporary_tables.end() != temporary_tables.find(table_name)) + throw Exception("Temporary table " + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); + temporary_tables[table_name] = storage; +} + + void Context::addTable(const String & database_name, const String & table_name, StoragePtr table) { Poco::ScopedLock lock(shared->mutex); diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 4aeed0f31a4..7f1234ffe7a 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "TCPHandler.h" @@ -112,13 +114,21 @@ void TCPHandler::runImpl() if (!receivePacket()) continue; + /// Получить блоки временных таблиц + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + readData(global_settings); + /// Обрабатываем Query - + state.io = executeQuery(state.query, query_context, false, state.stage); + + if (state.io.out) + state.is_insert = true; + after_check_cancelled.restart(); after_send_progress.restart(); /// Запрос требует приёма данных от клиента? - if (state.io.out) + if (state.is_insert) processInsertQuery(global_settings); else processOrdinaryQuery(); @@ -203,13 +213,8 @@ void TCPHandler::runImpl() } -void TCPHandler::processInsertQuery(const Settings & global_settings) +void TCPHandler::readData(const Settings & global_settings) { - /// Отправляем клиенту блок - структура таблицы. - Block block = state.io.out_sample; - sendData(block); - - state.io.out->writePrefix(); while (1) { /// Ждём пакета от клиента. При этом, каждые POLL_INTERVAL сек. проверяем, не требуется ли завершить работу. @@ -220,9 +225,22 @@ void TCPHandler::processInsertQuery(const Settings & global_settings) if (Daemon::instance().isCancelled() || in->eof()) return; + std::cerr << "Receiving packet" << std::endl; + if (!receivePacket()) break; } +} + + +void TCPHandler::processInsertQuery(const Settings & global_settings) +{ + /// Отправляем клиенту блок - структура таблицы. + Block block = state.io.out_sample; + sendData(block); + + state.io.out->writePrefix(); + readData(global_settings); state.io.out->writeSuffix(); } @@ -518,19 +536,37 @@ void TCPHandler::receiveQuery() LOG_DEBUG(log, "Query ID: " << state.query_id); LOG_DEBUG(log, "Query: " << state.query); LOG_DEBUG(log, "Requested stage: " << QueryProcessingStage::toString(stage)); - - state.io = executeQuery(state.query, query_context, false, state.stage); } bool TCPHandler::receiveData() { initBlockInput(); - - /// Прочитать из сети один блок и засунуть его в state.io.out (данные для INSERT-а) + + /// Имя временной таблицы для записи данных, по умолчанию пустая строка + String temporary_table_name; + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + readStringBinary(temporary_table_name, *in); + + /// Прочитать из сети один блок и записать его Block block = state.block_in->read(); if (block) { + /// Если запрос на вставку, то данные нужно писать напрямую в state.io.out. + /// Иначе пишем блоки во временную таблицу temporary_table_name. + if (!state.is_insert) + { + StoragePtr storage; + /// Если такой таблицы не существовало, создаем ее. + if (!(storage = query_context.tryGetTemporaryTable(temporary_table_name))) + { + NamesAndTypesListPtr columns = new NamesAndTypesList(block.getColumnsList()); + storage = StorageMemory::create(temporary_table_name, columns); + query_context.addTemporaryTable(temporary_table_name, storage); + } + /// Данные будем писать напрямую в таблицу. + state.io.out = storage->write(ASTPtr()); + } state.io.out->write(block); return true; } @@ -614,6 +650,8 @@ void TCPHandler::sendData(Block & block) initBlockOutput(); writeVarUInt(Protocol::Server::Data, *out); + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + writeStringBinary("", *out); state.block_out->write(block); state.maybe_compressed_out->next(); diff --git a/dbms/src/Server/TCPHandler.h b/dbms/src/Server/TCPHandler.h index 5aeedd29719..83928aed0bf 100644 --- a/dbms/src/Server/TCPHandler.h +++ b/dbms/src/Server/TCPHandler.h @@ -45,6 +45,8 @@ struct QueryState bool is_empty; /// Данные были отправлены. bool sent_all_data; + /// Запрос на вставку или нет. + bool is_insert; /// Для вывода прогресса - разница после предыдущей отправки прогресса. volatile size_t rows_processed; @@ -52,7 +54,7 @@ struct QueryState QueryState() : query_id(""), stage(QueryProcessingStage::Complete), compression(Protocol::Compression::Disable), - is_cancelled(false), is_empty(true), sent_all_data(false), rows_processed(0), bytes_processed(0) {} + is_cancelled(false), is_empty(true), sent_all_data(false), is_insert(false), rows_processed(0), bytes_processed(0) {} void reset() { @@ -107,6 +109,7 @@ private: bool receivePacket(); void receiveQuery(); bool receiveData(); + void readData(const Settings & global_settings); /// Обработать запрос INSERT void processInsertQuery(const Settings & global_settings); From 92658ff05a7f8099fd2789dd72b2de3b39b08e18 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 5 Mar 2014 17:48:45 +0400 Subject: [PATCH 074/281] client: comand line parsing changed from poco to boost::program_options [METR-10071] --- dbms/src/Client/Client.cpp | 91 ++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 54 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index cb4e89912d0..8b054b0dc45 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -790,66 +791,49 @@ private: if (is_interactive && !written_first_block) std::cout << "Ok." << std::endl; } - - void defineOptions(Poco::Util::OptionSet & options) +public: + void init(int argc, char ** argv) { - Poco::Util::Application::defineOptions(options); + /// Останавливаем внутреннюю обработку командной строки + stopOptionsProcessing(); - options.addOption( - Poco::Util::Option("config-file", "c") - .required(false) - .repeatable(false) - .argument("") - .binding("config-file")); + /// Перечисляем опции командной строки + boost::program_options::options_description desc("Allowed options"); + desc.add_options() + ("config-file,c", boost::program_options::value (), "config-file") + ("host,h", boost::program_options::value ()->default_value("localhost"), "host") + ("port,p", boost::program_options::value ()->default_value(9000), "port") + ("user,u", boost::program_options::value (), "user") + ("password,p", boost::program_options::value (), "password") + ("query,q", boost::program_options::value (), "query") + ("database,d", boost::program_options::value (), "database") + ("multiline,m", "multiline") + ; - options.addOption( - Poco::Util::Option("host", "h") - .required(false) - .repeatable(false) - .argument("") - .binding("host")); + /// Парсим командную строку + boost::program_options::variables_map options; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), options); - options.addOption( - Poco::Util::Option("port", "") - .required(false) - .repeatable(false) - .argument("") - .binding("port")); + /// Сохраняем полученные значение во внутренний конфиг + if (options.count("config-file")) + config().setString("config-file", options["config-file"].as()); + if (options.count("host")) + config().setString("host", options["host"].as()); + if (options.count("query")) + config().setString("query", options["query"].as()); + if (options.count("database")) + config().setString("database", options["database"].as()); - options.addOption( - Poco::Util::Option("user", "u") - .required(false) - .repeatable(false) - .argument("") - .binding("user")); + if (options.count("port")) + config().setInt("port", options["port"].as()); + if (options.count("user")) + config().setInt("user", options["user"].as()); + if (options.count("password")) + config().setInt("password", options["password"].as()); - options.addOption( - Poco::Util::Option("password", "") - .required(false) - .repeatable(false) - .argument("") - .binding("password")); - - options.addOption( - Poco::Util::Option("query", "e") - .required(false) - .repeatable(false) - .argument("") - .binding("query")); - - options.addOption( - Poco::Util::Option("database", "d") - .required(false) - .repeatable(false) - .argument("") - .binding("database")); - - options.addOption( - Poco::Util::Option("multiline", "m") - .required(false) - .repeatable(false) - .binding("multiline")); + if (options.count("multiline")) + config().setBool("multiline", true); } }; @@ -859,7 +843,6 @@ private: int main(int argc, char ** argv) { DB::Client client; -// client.stopOptionsProcessing(); client.init(argc, argv); return client.run(); } From 3fb930ef53d510dd73d3d042ed35520ed854aba5 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 5 Mar 2014 19:26:43 +0400 Subject: [PATCH 075/281] client: support external tables descriptions in command line arguments [METR-10071] --- dbms/src/Client/Client.cpp | 131 +++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 7 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 8b054b0dc45..f6609eb3360 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -58,6 +58,93 @@ namespace DB using Poco::SharedPtr; +class ExternalTable +{ +public: + std::string file; + std::string name; + std::string format; + std::vector > structure; + + void write() + { + std::cerr << "file " << file << std::endl; + std::cerr << "name " << name << std::endl; + std::cerr << "format " << format << std::endl; + std::cerr << "structure: \n"; + for (size_t i = 0; i < structure.size(); ++i) + std::cerr << "\t" << structure[i].first << " " << structure[i].second << std::endl; + } + + ExternalTable(const boost::program_options::variables_map & external_options) + { + if (external_options.count("file")) + file = external_options["file"].as(); + else + throw Exception("File field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + + if (external_options.count("name")) + name = external_options["name"].as(); + else + throw Exception("Name field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + + if (external_options.count("format")) + format = external_options["format"].as(); + else + throw Exception("format field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + + if (external_options.count("structure")) + { + std::vector temp = external_options["structure"].as>(); + + std::string argument; + for (size_t i = 0; i < temp.size(); ++i) + argument = argument + temp[i] + " "; + std::vector vals = split(argument, " ,"); + + if (vals.size() & 1) + throw Exception("Odd number of attributes in section structure", ErrorCodes::BAD_ARGUMENTS); + + for (size_t i = 0; i < vals.size(); i += 2) + structure.push_back(std::make_pair(vals[i], vals[i+1])); + } + else if (external_options.count("types")) + { + std::vector temp = external_options["types"].as>(); + std::string argument; + for (size_t i = 0; i < temp.size(); ++i) + argument = argument + temp[i] + " "; + std::vector vals = split(argument, " ,"); + + for (size_t i = 0; i < vals.size(); ++i) + structure.push_back(std::make_pair("_" + toString(i + 1), vals[i])); + } + else + throw Exception("Neither structure nor types have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + } + + static std::vector split(const std::string & s, const std::string &d) + { + std::vector res; + std::string now; + for (size_t i = 0; i < s.size(); ++i) + { + if (d.find(s[i]) != std::string::npos) + { + if (!now.empty()) + res.push_back(now); + now = ""; + continue; + } + now += s[i]; + } + if (!now.empty()) + res.push_back(now); + return res; + } +}; + + class Client : public Poco::Util::Application { public: @@ -795,12 +882,13 @@ private: public: void init(int argc, char ** argv) { + /// Останавливаем внутреннюю обработку командной строки stopOptionsProcessing(); - /// Перечисляем опции командной строки - boost::program_options::options_description desc("Allowed options"); - desc.add_options() + /// Перечисляем основные опции командной строки относящиеся к функциональности клиента + boost::program_options::options_description main_description("Main options"); + main_description.add_options() ("config-file,c", boost::program_options::value (), "config-file") ("host,h", boost::program_options::value ()->default_value("localhost"), "host") ("port,p", boost::program_options::value ()->default_value(9000), "port") @@ -811,11 +899,40 @@ public: ("multiline,m", "multiline") ; - /// Парсим командную строку - boost::program_options::variables_map options; - boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), options); + /// Перечисляем опции командной строки относящиеся к внешним таблицам + boost::program_options::options_description external_description("Main options"); + external_description.add_options() + ("file", boost::program_options::value (), "data file or - for stdin") + ("name", boost::program_options::value ()->default_value("_data"), "name of the table") + ("format", boost::program_options::value ()->default_value("TabSeparated"), "data format") + ("structure", boost::program_options::value> ()->multitoken(), "structure") + ("types", boost::program_options::value> ()->multitoken(), "types") + ; - /// Сохраняем полученные значение во внутренний конфиг + std::vector positions; + + positions.push_back(0); + for (int i = 0; i < argc; ++i) + if (std::string(argv[i]) == "--external") + positions.push_back(i); + positions.push_back(argc); + + /// Парсим основные опции командной строки + boost::program_options::variables_map options; + boost::program_options::store(boost::program_options::parse_command_line(positions[1] - positions[0], argv, main_description), options); + + std::vector external_tables; + + for (size_t i = 1; i + 1 < positions.size(); ++i) + { + boost::program_options::variables_map external_options; + boost::program_options::store(boost::program_options::parse_command_line( + positions[i+1] - positions[i], &argv[positions[i]], external_description), external_options); + external_tables.push_back(ExternalTable(external_options)); +// external_tables.back().write(); + } + + /// Сохраняем полученные данные во внутренний конфиг if (options.count("config-file")) config().setString("config-file", options["config-file"].as()); if (options.count("host")) From 4ffc3fbdf7e8b320b96e937d46fe79c0918e281e Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 6 Mar 2014 15:37:30 +0400 Subject: [PATCH 076/281] client: try catch for External table exceptions, couple of fixes [METR-10071] --- dbms/src/Client/Client.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index f6609eb3360..64527a1fac3 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -81,17 +81,17 @@ public: if (external_options.count("file")) file = external_options["file"].as(); else - throw Exception("File field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + throw Exception("--file field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); if (external_options.count("name")) name = external_options["name"].as(); else - throw Exception("Name field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + throw Exception("--name field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); if (external_options.count("format")) format = external_options["format"].as(); else - throw Exception("format field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + throw Exception("--format field have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); if (external_options.count("structure")) { @@ -120,7 +120,7 @@ public: structure.push_back(std::make_pair("_" + toString(i + 1), vals[i])); } else - throw Exception("Neither structure nor types have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); + throw Exception("Neither --structure nor --types have not been provided for external table", ErrorCodes::BAD_ARGUMENTS); } static std::vector split(const std::string & s, const std::string &d) @@ -199,6 +199,9 @@ private: size_t written_progress_chars; bool written_first_block; + /// Информация о внешних таблицах + std::vector external_tables; + void initialize(Poco::Util::Application & self) { @@ -912,8 +915,8 @@ public: std::vector positions; positions.push_back(0); - for (int i = 0; i < argc; ++i) - if (std::string(argv[i]) == "--external") + for (int i = 1; i < argc; ++i) + if (strcmp(argv[i], "--external") == 0) positions.push_back(i); positions.push_back(argc); @@ -921,15 +924,22 @@ public: boost::program_options::variables_map options; boost::program_options::store(boost::program_options::parse_command_line(positions[1] - positions[0], argv, main_description), options); - std::vector external_tables; - for (size_t i = 1; i + 1 < positions.size(); ++i) { boost::program_options::variables_map external_options; boost::program_options::store(boost::program_options::parse_command_line( positions[i+1] - positions[i], &argv[positions[i]], external_description), external_options); - external_tables.push_back(ExternalTable(external_options)); -// external_tables.back().write(); + try + { + external_tables.push_back(ExternalTable(external_options)); + } + catch (const Exception & e) + { + std::string text = e.displayText(); + std::cerr << "Code: " << e.code() << ". " << text << std::endl << std::endl; + std::cerr << "Table #" << i << std::endl; + exit(e.code()); + } } /// Сохраняем полученные данные во внутренний конфиг From a17a57c4253a32d848cc53f874d45a1b612bc67a Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 6 Mar 2014 18:02:20 +0400 Subject: [PATCH 077/281] client: external data sending from client to server [METR-10071] --- dbms/include/DB/Client/Connection.h | 5 ++- dbms/src/Client/Client.cpp | 44 ++++++++++++++++++- dbms/src/Client/Connection.cpp | 15 +++++-- dbms/src/Interpreters/Context.cpp | 11 +++++ .../Interpreters/InterpreterSelectQuery.cpp | 17 ++++--- 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index a2f542eb0d5..648df36b904 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -24,6 +24,7 @@ namespace DB using Poco::SharedPtr; +typedef std::pair ExternalTableData; /** Соединение с сервером БД для использования в клиенте. * Как использовать - см. Core/Protocol.h @@ -83,8 +84,8 @@ public: const Settings * settings = NULL); void sendCancel(); - void sendData(const Block & block); - void sendTemporaryTables(); + void sendData(const Block & block, const String & name = ""); + void sendExternalTables(std::vector & data); /// Проверить, если ли данные, которые можно прочитать. bool poll(size_t timeout_microseconds = 0); diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 64527a1fac3..ad3fa6bd413 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -65,6 +65,37 @@ public: std::string name; std::string format; std::vector > structure; + ReadBuffer *read_buffer; + Block sample_block; + + void initReadBuffer() + { + /// stdin + if (file == "-") + throw Exception("stdin as file is not supported yet", ErrorCodes::BAD_ARGUMENTS); + read_buffer = new ReadBufferFromFile(file); + } + + void initSampleBlock(const Context &context) + { + for (size_t i = 0; i < structure.size(); ++i) + { + ColumnWithNameAndType column; + column.name = structure[i].first; + column.type = context.getDataTypeFactory().get(structure[i].second); + column.column = column.type->createColumn(); + sample_block.insert(column); + } + } + + ExternalTableData getData(const Context &context) + { + initReadBuffer(); + initSampleBlock(context); + ExternalTableData res = std::make_pair(new AsynchronousBlockInputStream(context.getFormatFactory().getInput( + format, *read_buffer, sample_block, DEFAULT_BLOCK_SIZE, context.getDataTypeFactory())), name); + return res; + } void write() { @@ -519,11 +550,20 @@ private: } + void sendExternalTables() + { + std::vector data; + for (size_t i = 0; i < external_tables.size(); ++i) + data.push_back(external_tables[i].getData(context)); + connection->sendExternalTables(data); + } + + /// Обработать запрос, который не требует передачи блоков данных на сервер. void processOrdinaryQuery() { connection->sendQuery(query, "", QueryProcessingStage::Complete); - connection->sendTemporaryTables(); + sendExternalTables(); receiveResult(); } @@ -541,7 +581,7 @@ private: throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete); - connection->sendTemporaryTables(); + sendExternalTables(); /// Получим структуру таблицы Block sample = receiveSampleBlock(); diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index f84b9204073..0e9692cff00 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -243,7 +243,7 @@ void Connection::sendCancel() } -void Connection::sendData(const Block & block) +void Connection::sendData(const Block & block, const String & name) { //LOG_TRACE(log, "Sending data (" << getServerAddress() << ")"); @@ -260,7 +260,7 @@ void Connection::sendData(const Block & block) writeVarUInt(Protocol::Client::Data, *out); if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) - writeStringBinary("", *out); + writeStringBinary(name, *out); block.checkNestedArraysOffsets(); block_out->write(block); @@ -268,13 +268,20 @@ void Connection::sendData(const Block & block) out->next(); } - -void Connection::sendTemporaryTables() +void Connection::sendExternalTables(std::vector & data) { /// Если работаем со старым сервером, то никакой информации не отправляем if (server_revision < DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) return; + for (size_t i = 0; i < data.size(); ++i) + { + data[i].first->readPrefix(); + while(Block block = data[i].first->read()) + sendData(block, data[i].second); + data[i].first->readSuffix(); + } + /// Отправляем пустой блок, символизируя конец передачи данных sendData(Block()); } diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index cb88a2eaf96..43473d8c7e3 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -164,6 +164,7 @@ void Context::assertDatabaseDoesntExist(const String & database_name) const StoragePtr Context::tryGetTemporaryTable(const String & table_name) const { Poco::ScopedLock lock(shared->mutex); + std::cerr << temporary_tables.size() << " " << table_name << std::endl; Tables::const_iterator jt; if (temporary_tables.end() == (jt = temporary_tables.find(table_name))) @@ -206,6 +207,16 @@ StoragePtr Context::tryGetTable(const String & database_name, const String & tab { Poco::ScopedLock lock(shared->mutex); + if (database_name.empty()) + { + StoragePtr res; + if (res = tryGetTemporaryTable(table_name)) + return res; + if (res = session_context->tryGetTemporaryTable(table_name)) + return res; + if (res = global_context->tryGetTemporaryTable(table_name)) + return res; + } String db = database_name.empty() ? current_database : database_name; Databases::const_iterator it; diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 009ca1ca9a3..0284f4538d0 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -126,18 +126,23 @@ void InterpreterSelectQuery::getDatabaseAndTableNames(String & database_name, St /** Если таблица не указана - используем таблицу system.one. * Если база данных не указана - используем текущую базу данных. */ + if (query.database) + database_name = dynamic_cast(*query.database).name; + if (query.table) + table_name = dynamic_cast(*query.table).name; + if (!query.table) { database_name = "system"; table_name = "one"; } else if (!query.database) - database_name = context.getCurrentDatabase(); - - if (query.database) - database_name = dynamic_cast(*query.database).name; - if (query.table) - table_name = dynamic_cast(*query.table).name; + { + if (context.tryGetTable("", table_name)) + database_name = ""; + else + database_name = context.getCurrentDatabase(); + } } From 27f081a382e80ed56ec0b86dcd040df36505a53f Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 12 Mar 2014 17:14:16 +0400 Subject: [PATCH 078/281] dbms: create temporary table query, external tables from stdin [METR-10071] --- dbms/include/DB/Parsers/ASTCreateQuery.h | 5 +-- dbms/src/Client/Client.cpp | 19 ++++++++--- dbms/src/Interpreters/Context.cpp | 11 +++--- .../Interpreters/InterpreterCreateQuery.cpp | 34 ++++++++++++++----- dbms/src/Parsers/ParserCreateQuery.cpp | 9 +++++ 5 files changed, 56 insertions(+), 22 deletions(-) diff --git a/dbms/include/DB/Parsers/ASTCreateQuery.h b/dbms/include/DB/Parsers/ASTCreateQuery.h index bedd7f89d02..7f35bd5ad0a 100644 --- a/dbms/include/DB/Parsers/ASTCreateQuery.h +++ b/dbms/include/DB/Parsers/ASTCreateQuery.h @@ -18,6 +18,7 @@ public: bool is_view; bool is_materialized_view; bool is_populate; + bool is_temporary; String database; String table; ASTPtr columns; @@ -27,8 +28,8 @@ public: String as_table; ASTPtr select; - ASTCreateQuery() : attach(false), if_not_exists(false), is_view(false), is_materialized_view(false), is_populate(false) {} - ASTCreateQuery(StringRange range_) : IAST(range_), attach(false), if_not_exists(false), is_view(false), is_materialized_view(false),is_populate(false) {} + ASTCreateQuery() : attach(false), if_not_exists(false), is_view(false), is_materialized_view(false), is_populate(false), is_temporary(false) {} + ASTCreateQuery(StringRange range_) : IAST(range_), attach(false), if_not_exists(false), is_view(false), is_materialized_view(false), is_populate(false), is_temporary(false) {} /** Получить текст, который идентифицирует этот элемент. */ String getID() const { return (attach ? "AttachQuery_" : "CreateQuery_") + database + "_" + table; }; diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index ad3fa6bd413..ef22e193bf1 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -70,10 +71,10 @@ public: void initReadBuffer() { - /// stdin if (file == "-") - throw Exception("stdin as file is not supported yet", ErrorCodes::BAD_ARGUMENTS); - read_buffer = new ReadBufferFromFile(file); + read_buffer = new ReadBufferFromIStream(std::cin); + else + read_buffer = new ReadBufferFromFile(file); } void initSampleBlock(const Context &context) @@ -552,6 +553,9 @@ private: void sendExternalTables() { + const ASTSelectQuery * select = dynamic_cast(&*parsed_query); + if (!select && !external_tables.empty()) + throw Exception("External tables could be sent only with select query", ErrorCodes::BAD_ARGUMENTS); std::vector data; for (size_t i = 0; i < external_tables.size(); ++i) data.push_back(external_tables[i].getData(context)); @@ -964,6 +968,7 @@ public: boost::program_options::variables_map options; boost::program_options::store(boost::program_options::parse_command_line(positions[1] - positions[0], argv, main_description), options); + size_t stdin_count = 0; for (size_t i = 1; i + 1 < positions.size(); ++i) { boost::program_options::variables_map external_options; @@ -972,12 +977,16 @@ public: try { external_tables.push_back(ExternalTable(external_options)); + if (external_tables.back().file == "-") + stdin_count ++; + if (stdin_count > 1) + throw Exception("Two or more external tables has stdin (-) set as --file field", ErrorCodes::BAD_ARGUMENTS); } catch (const Exception & e) { std::string text = e.displayText(); - std::cerr << "Code: " << e.code() << ". " << text << std::endl << std::endl; - std::cerr << "Table #" << i << std::endl; + std::cerr << "Code: " << e.code() << ". " << text << std::endl; + std::cerr << "Table #" << i << std::endl << std::endl; exit(e.code()); } } diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 43473d8c7e3..50a2e81972a 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -164,7 +164,6 @@ void Context::assertDatabaseDoesntExist(const String & database_name) const StoragePtr Context::tryGetTemporaryTable(const String & table_name) const { Poco::ScopedLock lock(shared->mutex); - std::cerr << temporary_tables.size() << " " << table_name << std::endl; Tables::const_iterator jt; if (temporary_tables.end() == (jt = temporary_tables.find(table_name))) @@ -186,9 +185,9 @@ StoragePtr Context::getTable(const String & database_name, const String & table_ StoragePtr res; if (res = tryGetTemporaryTable(table_name)) return res; - if (res = session_context->tryGetTemporaryTable(table_name)) + if (session_context && (res = session_context->tryGetTemporaryTable(table_name))) return res; - if (res = global_context->tryGetTemporaryTable(table_name)) + if (global_context && (res = global_context->tryGetTemporaryTable(table_name))) return res; } String db = database_name.empty() ? current_database : database_name; @@ -206,15 +205,15 @@ StoragePtr Context::getTable(const String & database_name, const String & table_ StoragePtr Context::tryGetTable(const String & database_name, const String & table_name) const { Poco::ScopedLock lock(shared->mutex); - + if (database_name.empty()) { StoragePtr res; if (res = tryGetTemporaryTable(table_name)) return res; - if (res = session_context->tryGetTemporaryTable(table_name)) + if (session_context && (res = session_context->tryGetTemporaryTable(table_name))) return res; - if (res = global_context->tryGetTemporaryTable(table_name)) + if (global_context && (res = global_context->tryGetTemporaryTable(table_name))) return res; } String db = database_name.empty() ? current_database : database_name; diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index 07913765133..9fa064b2631 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -97,14 +97,17 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) { Poco::ScopedLock lock(context.getMutex()); - context.assertDatabaseExists(database_name); - - if (context.isTableExist(database_name, table_name)) + if (!create.is_temporary) { - if (create.if_not_exists) - return context.getTable(database_name, table_name); - else - throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); + context.assertDatabaseExists(database_name); + + if (context.isTableExist(database_name, table_name)) + { + if (create.if_not_exists) + return context.getTable(database_name, table_name); + else + throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); + } } /// Получаем список столбцов @@ -170,6 +173,13 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) storage_name = as_storage->getName(); create.storage = dynamic_cast(*context.getCreateQuery(as_database_name, as_table_name)).storage; } + else if (create.is_temporary) + { + storage_name = "Memory"; + ASTFunction * func = new ASTFunction(); + func->name = storage_name; + create.storage = func; + } else if (create.is_view) { storage_name = "View"; @@ -191,7 +201,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) storage_name, data_path, table_name, database_name, context.getGlobalContext(), query_ptr, columns, create.attach); /// Проверка наличия метаданных таблицы на диске и создание метаданных - if (!assume_metadata_exists) + if (!assume_metadata_exists && !create.is_temporary) { if (Poco::File(metadata_path).exists()) { @@ -225,7 +235,13 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) } } - context.addTable(database_name, table_name, res); + if (create.is_temporary) + { + res->drop_on_destroy = true; + context.getSessionContext().addTemporaryTable(table_name, res); + } + else + context.addTable(database_name, table_name, res); } /// Если запрос CREATE SELECT, то вставим в таблицу данные diff --git a/dbms/src/Parsers/ParserCreateQuery.cpp b/dbms/src/Parsers/ParserCreateQuery.cpp index 23b95fd6f28..19fa637e666 100644 --- a/dbms/src/Parsers/ParserCreateQuery.cpp +++ b/dbms/src/Parsers/ParserCreateQuery.cpp @@ -168,6 +168,7 @@ bool ParserCreateQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ParserWhiteSpaceOrComments ws; ParserString s_create("CREATE", true, true); + ParserString s_temporary("TEMPORARY", true, true); ParserString s_attach("ATTACH", true, true); ParserString s_table("TABLE", true, true); ParserString s_database("DATABASE", true, true); @@ -199,6 +200,7 @@ bool ParserCreateQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char bool is_view = false; bool is_materialized_view = false; bool is_populate = false; + bool is_temporary = false; ws.ignore(pos, end); @@ -212,6 +214,12 @@ bool ParserCreateQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ws.ignore(pos, end); + if (s_temporary.ignore(pos, end, expected)) + { + is_temporary = true; + ws.ignore(pos, end); + } + if (s_database.ignore(pos, end, expected)) { ws.ignore(pos, end); @@ -403,6 +411,7 @@ bool ParserCreateQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char query->is_view = is_view; query->is_materialized_view = is_materialized_view; query->is_populate = is_populate; + query->is_temporary = is_temporary; if (database) query->database = dynamic_cast(*database).name; From cf256736062b18b2a3f40b61351f805079f57e5d Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 12 Mar 2014 18:15:35 +0400 Subject: [PATCH 079/281] expression analyzer: support "IN t" = "in select * from t" [METR-10071] --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 30 ++++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 38aaeabd661..793bb377357 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -397,6 +398,9 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as current_asts.insert(ast); replaced = true; } + if (node->name == "in" || node->name == "notIn") + if (ASTIdentifier * right = dynamic_cast(&*node->arguments->children[1])) + right->kind = ASTIdentifier::Table; } else if (ASTIdentifier * node = dynamic_cast(&*ast)) { @@ -508,7 +512,7 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) { /** Нужно преобразовать правый аргумент в множество. - * Это может быть значение, перечисление значений или подзапрос. + * Это может быть имя таблицы, значение, перечисление значений или подзапрос. * Перечисление значений парсится как функция tuple. */ IAST & args = *node->arguments; @@ -517,7 +521,8 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) if (dynamic_cast(&*arg)) return; - if (dynamic_cast(&*arg)) + /// Если подзапрос или имя таблицы для селекта + if (dynamic_cast(&*arg) || dynamic_cast(&*arg)) { /// Получаем поток блоков для подзапроса, отдаем его множеству, и кладём это множество на место подзапроса. ASTSet * ast_set = new ASTSet(arg->getColumnName()); @@ -541,7 +546,26 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) subquery_settings.extremes = 0; subquery_context.setSettings(subquery_settings); - InterpreterSelectQuery interpreter(arg->children[0], subquery_context, QueryProcessingStage::Complete, subquery_depth + 1); + ASTPtr subquery; + if (ASTIdentifier * table = dynamic_cast(&*arg)) + { + ParserSelectQuery parser; + + String query = "SELECT * FROM " + table->name; + const char * begin = query.data(); + const char * end = begin + query.size(); + const char * pos = begin; + const char * expected = ""; + + bool parse_res = parser.parse(pos, end, subquery, expected); + if (!parse_res) + throw Exception("Error in parsing select query while creating set for table " + table->name + ".", + ErrorCodes::LOGICAL_ERROR); + } + else + subquery = arg->children[0]; + + InterpreterSelectQuery interpreter(subquery, subquery_context, QueryProcessingStage::Complete, subquery_depth + 1); ast_set->set = new Set(settings.limits); ast_set->set->setSource(interpreter.execute()); sets_with_subqueries[ast_set->getColumnName()] = ast_set->set; From 386f6d0da5dd677b779b4c43aa7c12b3ada4fcd5 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 12 Mar 2014 19:37:44 +0400 Subject: [PATCH 080/281] connection: bug-fix: sending query with pending data [METR-10071] --- dbms/include/DB/Client/Connection.h | 2 +- dbms/src/Client/Client.cpp | 4 ++-- dbms/src/Client/Connection.cpp | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index 648df36b904..bb130f42b35 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -81,7 +81,7 @@ public: String getServerAddress() const; void sendQuery(const String & query, const String & query_id_ = "", UInt64 stage = QueryProcessingStage::Complete, - const Settings * settings = NULL); + const Settings * settings = NULL, bool with_pending_data = false); void sendCancel(); void sendData(const Block & block, const String & name = ""); diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index ef22e193bf1..9719dc17adf 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -566,7 +566,7 @@ private: /// Обработать запрос, который не требует передачи блоков данных на сервер. void processOrdinaryQuery() { - connection->sendQuery(query, "", QueryProcessingStage::Complete); + connection->sendQuery(query, "", QueryProcessingStage::Complete, NULL, true); sendExternalTables(); receiveResult(); } @@ -584,7 +584,7 @@ private: if ((is_interactive && !parsed_insert_query.data) || (stdin_is_not_tty && std_in.eof())) throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); - connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete); + connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete, NULL, true); sendExternalTables(); /// Получим структуру таблицы diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 0e9692cff00..3e5136294aa 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -196,7 +196,7 @@ bool Connection::ping() } -void Connection::sendQuery(const String & query, const String & query_id_, UInt64 stage, const Settings * settings) +void Connection::sendQuery(const String & query, const String & query_id_, UInt64 stage, const Settings * settings, bool with_pending_data) { forceConnected(); @@ -231,6 +231,10 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 maybe_compressed_out = NULL; block_in = NULL; block_out = NULL; + + /// Отправляем пустой блок, символизируя конец передачи данных + if (!with_pending_data) + sendData(Block()); } From 6f85a1269285c9c8b6e709eb65e3270c4b2c7518 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 13 Mar 2014 19:00:06 +0400 Subject: [PATCH 081/281] dbms: fixed variable names, temporary tables are now external tables, in distributed query external table now always will be send to remote servers [METR-10071] --- dbms/include/DB/Client/Connection.h | 7 +++- dbms/include/DB/Core/Defines.h | 2 +- .../DB/DataStreams/RemoteBlockInputStream.h | 22 +++++++++-- dbms/include/DB/Interpreters/Context.h | 8 ++-- dbms/include/DB/Storages/IStorage.h | 5 +++ dbms/include/DB/Storages/ITableDeclaration.h | 4 ++ dbms/include/DB/Storages/StorageDistributed.h | 3 ++ dbms/src/Client/Connection.cpp | 12 +++--- dbms/src/Interpreters/Context.cpp | 39 +++++++++++++------ .../Interpreters/InterpreterCreateQuery.cpp | 2 +- .../Interpreters/InterpreterSelectQuery.cpp | 2 + dbms/src/Server/TCPHandler.cpp | 18 ++++----- dbms/src/Storages/ITableDeclaration.cpp | 12 +++++- dbms/src/Storages/StorageDistributed.cpp | 4 ++ 14 files changed, 102 insertions(+), 38 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index bb130f42b35..5c52a14f141 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -4,8 +4,8 @@ #include -#include #include +#include #include #include #include @@ -24,7 +24,10 @@ namespace DB using Poco::SharedPtr; +/// Поток блоков читающих из таблицы и ее имя typedef std::pair ExternalTableData; +/// Вектор пар, описывающих таблицы +typedef std::vector ExternalTablesData; /** Соединение с сервером БД для использования в клиенте. * Как использовать - см. Core/Protocol.h @@ -85,7 +88,7 @@ public: void sendCancel(); void sendData(const Block & block, const String & name = ""); - void sendExternalTables(std::vector & data); + void sendExternalTables(ExternalTablesData & data); /// Проверить, если ли данные, которые можно прочитать. bool poll(size_t timeout_microseconds = 0); diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index b4908d5f7a6..a87875c0ee8 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -40,4 +40,4 @@ #define DBMS_MIN_REVISION_WITH_USER_PASSWORD 34482 #define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265 #define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002 -#define DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES 50092 +#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50092 diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index 587222d6cc5..753685e6607 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -48,9 +48,10 @@ public: } RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, - const String & _host_column_, const String & _port_column_, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + const String & _host_column_, const String & _port_column_, const Tables & external_tables_, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) : pool_entry(pool_entry_), connection(*pool_entry), query(query_), _host_column(_host_column_), - _port_column(_port_column_), stage(stage_), sent_query(false), finished(false), was_cancelled(false), + _port_column(_port_column_), external_tables(external_tables_), stage(stage_), sent_query(false), finished(false), + was_cancelled(false), got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection.getServerAddress() + ")")) { if (settings_) @@ -122,11 +123,25 @@ protected: } } + void sendExternalTables() + { + ExternalTablesData res; + Tables::const_iterator it; + for (it = external_tables.begin(); it != external_tables.end(); it ++) + { + StoragePtr cur = it->second; + QueryProcessingStage::Enum stage = QueryProcessingStage::Complete; + res.push_back(std::make_pair(cur->read(cur->getColumnNamesList(), ASTPtr(), settings, stage, DEFAULT_BLOCK_SIZE, 1)[0], it->first)); + } + connection.sendExternalTables(res); + } + Block readImpl() { if (!sent_query) { - connection.sendQuery(query, "", stage, send_settings ? &settings : NULL); + connection.sendQuery(query, "", stage, send_settings ? &settings : NULL, true); + sendExternalTables(); sent_query = true; } @@ -253,6 +268,7 @@ private: String _host_column; /// Имя столбца, куда записать номер порта (Например "_port"). Пустая строка, если записывать не надо. String _port_column; + Tables external_tables; QueryProcessingStage::Enum stage; /// Отправили запрос (это делается перед получением первого блока). diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 91aadb4c27f..0bf42ab6f3b 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -48,7 +48,6 @@ typedef std::pair DatabaseAndTableName; typedef std::map > ViewDependencies; typedef std::vector Dependencies; - /** Набор известных объектов, которые могут быть использованы в запросе. * Разделяемая часть. Порядок членов (порядок их уничтожения) очень важен. */ @@ -172,7 +171,7 @@ private: String default_format; /// Формат, используемый, если сервер сам форматирует данные, и если в запросе не задан FORMAT. /// То есть, используется в HTTP-интерфейсе. Может быть не задан - тогда используется некоторый глобальный формат по-умолчанию. - Tables temporary_tables; /// Временные таблицы. + Tables external_tables; /// Временные таблицы. Context * session_context; /// Контекст сессии или NULL, если его нет. (Возможно, равен this.) Context * global_context; /// Глобальный контекст или NULL, если его нет. (Возможно, равен this.) @@ -209,10 +208,11 @@ public: void assertDatabaseExists(const String & database_name) const; void assertDatabaseDoesntExist(const String & database_name) const; - StoragePtr tryGetTemporaryTable(const String & table_name) const; + Tables getExternalTables() const; + StoragePtr tryGetExternalTable(const String & table_name) const; StoragePtr getTable(const String & database_name, const String & table_name) const; StoragePtr tryGetTable(const String & database_name, const String & table_name) const; - void addTemporaryTable(const String & table_name, StoragePtr storage); + void addExternalTable(const String & table_name, StoragePtr storage); void addTable(const String & database_name, const String & table_name, StoragePtr table); void addDatabase(const String & database_name); diff --git a/dbms/include/DB/Storages/IStorage.h b/dbms/include/DB/Storages/IStorage.h index 01d97dde8ad..8b1f50065cf 100644 --- a/dbms/include/DB/Storages/IStorage.h +++ b/dbms/include/DB/Storages/IStorage.h @@ -49,6 +49,11 @@ public: */ virtual bool isRemote() const { return false; } + virtual void storeExternalTables(const std::map & tables_) + { + throw Exception("Method storeExternalTables is not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED); + } + /** Возвращает true, если хранилище поддерживает запросы с секцией SAMPLE. */ virtual bool supportsSampling() const { return false; } diff --git a/dbms/include/DB/Storages/ITableDeclaration.h b/dbms/include/DB/Storages/ITableDeclaration.h index 1307d1cc5e0..cdc6d1539bd 100644 --- a/dbms/include/DB/Storages/ITableDeclaration.h +++ b/dbms/include/DB/Storages/ITableDeclaration.h @@ -25,6 +25,10 @@ public: */ virtual const NamesAndTypesList & getColumnsList() const = 0; + /** Получить список имён столбцов таблицы, только невиртуальные. + */ + virtual Names getColumnNamesList() const; + /** Получить описание реального (невиртуального) столбца по его имени. */ virtual NameAndTypePair getRealColumn(const String & column_name) const; diff --git a/dbms/include/DB/Storages/StorageDistributed.h b/dbms/include/DB/Storages/StorageDistributed.h index a856c8c0412..443629370b5 100644 --- a/dbms/include/DB/Storages/StorageDistributed.h +++ b/dbms/include/DB/Storages/StorageDistributed.h @@ -49,6 +49,7 @@ public: bool hasColumn(const String &column_name) const; bool isRemote() const { return true; } + void storeExternalTables(const Tables & tables_) { external_tables = tables_; } BlockInputStreams read( const Names & column_names, @@ -92,6 +93,8 @@ private: const Context & context; + Tables external_tables; + /// Используется только, если таблица должна владеть объектом Cluster, которым больше никто не владеет - для реализации TableFunctionRemote. SharedPtr owned_cluster; diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 3e5136294aa..8a57ea33fb6 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -263,7 +263,7 @@ void Connection::sendData(const Block & block, const String & name) writeVarUInt(Protocol::Client::Data, *out); - if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) writeStringBinary(name, *out); block.checkNestedArraysOffsets(); @@ -272,10 +272,10 @@ void Connection::sendData(const Block & block, const String & name) out->next(); } -void Connection::sendExternalTables(std::vector & data) +void Connection::sendExternalTables(ExternalTablesData & data) { /// Если работаем со старым сервером, то никакой информации не отправляем - if (server_revision < DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + if (server_revision < DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) return; for (size_t i = 0; i < data.size(); ++i) @@ -362,10 +362,10 @@ Block Connection::receiveData() initBlockInput(); - String temporary_table_name; + String external_table_name; - if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) - readStringBinary(temporary_table_name, *in); + if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) + readStringBinary(external_table_name, *in); /// Прочитать из сети один блок return block_in->read(); diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 50a2e81972a..b39263188fa 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -161,12 +161,29 @@ void Context::assertDatabaseDoesntExist(const String & database_name) const } -StoragePtr Context::tryGetTemporaryTable(const String & table_name) const +Tables Context::getExternalTables() const +{ + Tables res = external_tables; + if (session_context && session_context != this) + { + Tables buf = session_context->getExternalTables(); + res.insert(buf.begin(), buf.end()); + } + else if (global_context && global_context != this) + { + Tables buf = global_context->getExternalTables(); + res.insert(buf.begin(), buf.end()); + } + return res; +} + + +StoragePtr Context::tryGetExternalTable(const String & table_name) const { Poco::ScopedLock lock(shared->mutex); Tables::const_iterator jt; - if (temporary_tables.end() == (jt = temporary_tables.find(table_name))) + if (external_tables.end() == (jt = external_tables.find(table_name))) return StoragePtr(); return jt->second; @@ -183,11 +200,11 @@ StoragePtr Context::getTable(const String & database_name, const String & table_ if (database_name.empty()) { StoragePtr res; - if (res = tryGetTemporaryTable(table_name)) + if (res = tryGetExternalTable(table_name)) return res; - if (session_context && (res = session_context->tryGetTemporaryTable(table_name))) + if (session_context && (res = session_context->tryGetExternalTable(table_name))) return res; - if (global_context && (res = global_context->tryGetTemporaryTable(table_name))) + if (global_context && (res = global_context->tryGetExternalTable(table_name))) return res; } String db = database_name.empty() ? current_database : database_name; @@ -209,11 +226,11 @@ StoragePtr Context::tryGetTable(const String & database_name, const String & tab if (database_name.empty()) { StoragePtr res; - if (res = tryGetTemporaryTable(table_name)) + if (res = tryGetExternalTable(table_name)) return res; - if (session_context && (res = session_context->tryGetTemporaryTable(table_name))) + if (session_context && (res = session_context->tryGetExternalTable(table_name))) return res; - if (global_context && (res = global_context->tryGetTemporaryTable(table_name))) + if (global_context && (res = global_context->tryGetExternalTable(table_name))) return res; } String db = database_name.empty() ? current_database : database_name; @@ -230,11 +247,11 @@ StoragePtr Context::tryGetTable(const String & database_name, const String & tab } -void Context::addTemporaryTable(const String & table_name, StoragePtr storage) +void Context::addExternalTable(const String & table_name, StoragePtr storage) { - if (temporary_tables.end() != temporary_tables.find(table_name)) + if (external_tables.end() != external_tables.find(table_name)) throw Exception("Temporary table " + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); - temporary_tables[table_name] = storage; + external_tables[table_name] = storage; } diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index 9fa064b2631..6a05b4127e2 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -238,7 +238,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) if (create.is_temporary) { res->drop_on_destroy = true; - context.getSessionContext().addTemporaryTable(table_name, res); + context.getSessionContext().addExternalTable(table_name, res); } else context.addTable(database_name, table_name, res); diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 0284f4538d0..ca0f8741d78 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -516,6 +516,8 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu /// Инициализируем изначальные потоки данных, на которые накладываются преобразования запроса. Таблица или подзапрос? if (!interpreter_subquery) { + if (storage->isRemote()) + storage->storeExternalTables(context.getExternalTables()); streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); for (auto stream : streams) { diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 7f1234ffe7a..b14dee0605b 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -115,7 +115,7 @@ void TCPHandler::runImpl() continue; /// Получить блоки временных таблиц - if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) readData(global_settings); /// Обрабатываем Query @@ -544,25 +544,25 @@ bool TCPHandler::receiveData() initBlockInput(); /// Имя временной таблицы для записи данных, по умолчанию пустая строка - String temporary_table_name; - if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) - readStringBinary(temporary_table_name, *in); + String external_table_name; + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) + readStringBinary(external_table_name, *in); /// Прочитать из сети один блок и записать его Block block = state.block_in->read(); if (block) { /// Если запрос на вставку, то данные нужно писать напрямую в state.io.out. - /// Иначе пишем блоки во временную таблицу temporary_table_name. + /// Иначе пишем блоки во временную таблицу external_table_name. if (!state.is_insert) { StoragePtr storage; /// Если такой таблицы не существовало, создаем ее. - if (!(storage = query_context.tryGetTemporaryTable(temporary_table_name))) + if (!(storage = query_context.tryGetExternalTable(external_table_name))) { NamesAndTypesListPtr columns = new NamesAndTypesList(block.getColumnsList()); - storage = StorageMemory::create(temporary_table_name, columns); - query_context.addTemporaryTable(temporary_table_name, storage); + storage = StorageMemory::create(external_table_name, columns); + query_context.addExternalTable(external_table_name, storage); } /// Данные будем писать напрямую в таблицу. state.io.out = storage->write(ASTPtr()); @@ -650,7 +650,7 @@ void TCPHandler::sendData(Block & block) initBlockOutput(); writeVarUInt(Protocol::Server::Data, *out); - if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPRORY_TABLES) + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) writeStringBinary("", *out); state.block_out->write(block); diff --git a/dbms/src/Storages/ITableDeclaration.cpp b/dbms/src/Storages/ITableDeclaration.cpp index 3de3049fa62..9145faced2f 100644 --- a/dbms/src/Storages/ITableDeclaration.cpp +++ b/dbms/src/Storages/ITableDeclaration.cpp @@ -10,7 +10,7 @@ namespace DB { -bool ITableDeclaration::hasRealColumn(const String &column_name) const +bool IStorage::hasRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) @@ -20,6 +20,16 @@ bool ITableDeclaration::hasRealColumn(const String &column_name) const } +Names ITableDeclaration::getColumnNamesList() const +{ + const NamesAndTypesList & real_columns = getColumnsList(); + Names res; + for (auto & it : real_columns) + res.push_back(it.first); + return res; +} + + NameAndTypePair ITableDeclaration::getRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index 51a7646276b..9c79a68e332 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -210,6 +210,7 @@ BlockInputStreams StorageDistributed::read( &new_settings, need_host_column ? _host_column_name : "", need_port_column ? _port_column_name : "", + external_tables, processed_stage); if (processed_stage == QueryProcessingStage::WithMergeableState || columns_to_remove.empty()) @@ -228,6 +229,8 @@ BlockInputStreams StorageDistributed::read( /// Добавляем запросы к локальному ClickHouse DB::Context new_context = context; new_context.setSettings(new_settings); + for (auto & it : external_tables) + new_context.addExternalTable(it.first, it.second); for(size_t i = 0; i < cluster.getLocalNodesNum(); ++i) { @@ -238,6 +241,7 @@ BlockInputStreams StorageDistributed::read( res.push_back(new RemoveColumnsBlockInputStream(interpreter.execute(), columns_to_remove)); } } + external_tables.clear(); return res; } From f0aae9604ab89554d7b4eff62fb531009287ae55 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 14 Mar 2014 18:52:48 +0400 Subject: [PATCH 082/281] dbms: Global in function, sexternal tables transfers to remote servers [METR-10071] --- .../DB/Functions/FunctionsMiscellaneous.h | 8 +- .../DB/Interpreters/ExpressionAnalyzer.h | 11 +- dbms/src/Functions/FunctionFactory.cpp | 6 +- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 104 +++++++++++++++++- .../Interpreters/InterpreterSelectQuery.cpp | 5 + dbms/src/Parsers/ExpressionListParsers.cpp | 2 + dbms/src/Server/TCPHandler.cpp | 2 - 7 files changed, 129 insertions(+), 9 deletions(-) diff --git a/dbms/include/DB/Functions/FunctionsMiscellaneous.h b/dbms/include/DB/Functions/FunctionsMiscellaneous.h index 0fcfc72bd76..438819c2d5e 100644 --- a/dbms/include/DB/Functions/FunctionsMiscellaneous.h +++ b/dbms/include/DB/Functions/FunctionsMiscellaneous.h @@ -285,14 +285,18 @@ class FunctionIn : public IFunction { private: bool negative; + bool global; public: - FunctionIn(bool negative_ = false) : negative(negative_) {} + FunctionIn(bool negative_ = false, bool global_ = false) : negative(negative_), global(global_) {} /// Получить имя функции. String getName() const { - return negative ? "notIn" : "in"; + if (global) + return negative ? "globalNotIn" : "globalIn"; + else + return negative ? "notIn" : "in"; } /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index a16810e0a8f..0923bd6776d 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -64,6 +64,8 @@ public: * chain.finalize(); */ + void processGlobalOperations(); + /// До агрегации: bool appendArrayJoin(ExpressionActionsChain & chain); bool appendWhere(ExpressionActionsChain & chain); @@ -95,6 +97,8 @@ public: /// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT. Block getSelectSampleBlock(); + std::vector external_tables; + private: typedef std::set NamesSet; @@ -124,6 +128,8 @@ private: AggregateDescriptions aggregate_descriptions; std::map sets_with_subqueries; + + std::vector global_nodes; typedef std::map Aliases; Aliases aliases; @@ -257,10 +263,13 @@ private: */ void normalizeTree(); void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias, bool in_sign_rewritten); + + void findGlobalFunctions(ASTPtr & ast); /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. void makeSet(ASTFunction * node, const Block & sample_block); - + void addExternalStorage(ASTFunction * node, size_t & name_id); + void getArrayJoinedColumns(); void getArrayJoinedColumnsImpl(ASTPtr ast); void addMultipleArrayJoinAction(ExpressionActions & actions); diff --git a/dbms/src/Functions/FunctionFactory.cpp b/dbms/src/Functions/FunctionFactory.cpp index eb48b6efa45..1ed0f4cabd7 100644 --- a/dbms/src/Functions/FunctionFactory.cpp +++ b/dbms/src/Functions/FunctionFactory.cpp @@ -191,8 +191,10 @@ FunctionPtr FunctionFactory::get( else if (name == "tuple") return new FunctionTuple; else if (name == "tupleElement") return new FunctionTupleElement; - else if (name == "in") return new FunctionIn; - else if (name == "notIn") return new FunctionIn(true); + else if (name == "in") return new FunctionIn(false, false); + else if (name == "notIn") return new FunctionIn(true, false); + else if (name == "globalIn") return new FunctionIn(false, true); + else if (name == "globalNotIn") return new FunctionIn(true, true); else if (name == "array") return new FunctionArray; else if (name == "arrayElement") return new FunctionArrayElement; diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 793bb377357..8a96268adfb 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -23,6 +23,11 @@ #include #include +#include + +#include + +#include namespace DB @@ -398,7 +403,7 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as current_asts.insert(ast); replaced = true; } - if (node->name == "in" || node->name == "notIn") + if (node->name == "in" || node->name == "notIn" || node->name == "globalIn" || node->name == "globalNotIn") if (ASTIdentifier * right = dynamic_cast(&*node->arguments->children[1])) right->kind = ASTIdentifier::Table; } @@ -509,6 +514,87 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as } +void ExpressionAnalyzer::findGlobalFunctions(ASTPtr & ast) +{ + /// Рекурсивные вызовы. Не опускаемся в подзапросы. + for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) + if (!dynamic_cast(&**it)) + findGlobalFunctions(*it); + + if (ASTFunction * node = dynamic_cast(&*ast)) + { + if (node->name == "globalIn" || node->name == "globalNotIn") + { + global_nodes.push_back(ast); + } + } +} + + +void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id) +{ + IAST & args = *node->arguments; + ASTPtr & arg = args.children[1]; + StoragePtr external_storage = StoragePtr(); + + /// Если подзапрос или имя таблицы для селекта + if (dynamic_cast(&*arg) || dynamic_cast(&*arg)) + { + /** Для подзапроса в секции IN не действуют ограничения на максимальный размер результата. + * Так как результат этого поздапроса - ещё не результат всего запроса. + * Вместо этого работают ограничения max_rows_in_set, max_bytes_in_set, set_overflow_mode. + */ + Context subquery_context = context; + Settings subquery_settings = context.getSettings(); + subquery_settings.limits.max_result_rows = 0; + subquery_settings.limits.max_result_bytes = 0; + /// Вычисление extremes не имеет смысла и не нужно (если его делать, то в результате всего запроса могут взяться extremes подзапроса). + subquery_settings.extremes = 0; + subquery_context.setSettings(subquery_settings); + + ASTPtr subquery; + if (ASTIdentifier * table = dynamic_cast(&*arg)) + { + ParserSelectQuery parser; + + if (context.tryGetExternalTable(table->name)) + return; + + String query = "SELECT * FROM " + table->name; + const char * begin = query.data(); + const char * end = begin + query.size(); + const char * pos = begin; + const char * expected = ""; + + bool parse_res = parser.parse(pos, end, subquery, expected); + if (!parse_res) + throw Exception("Error in parsing select query while creating set for table " + table->name + ".", + ErrorCodes::LOGICAL_ERROR); + } + else + subquery = arg->children[0]; + + InterpreterSelectQuery interpreter(subquery, subquery_context, QueryProcessingStage::Complete, subquery_depth + 1); + + Block sample = interpreter.getSampleBlock(); + NamesAndTypesListPtr columns = new NamesAndTypesList(sample.getColumnsList()); + + String external_table_name = "_table" + toString(name_id++); + external_storage = StorageMemory::create(external_table_name, columns); + BlockOutputStreamPtr output = external_storage->write(ASTPtr()); + copyData(*interpreter.execute(), *output); + + ASTIdentifier * ast_ident = new ASTIdentifier(); + ast_ident->kind = ASTIdentifier::Table; + ast_ident->name = external_storage->getTableName(); + arg = ast_ident; + external_tables.push_back(external_storage); + } + else + throw Exception("Global in (notIn) supports only select data.", ErrorCodes::BAD_ARGUMENTS); +} + + void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) { /** Нужно преобразовать правый аргумент в множество. @@ -566,6 +652,7 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) subquery = arg->children[0]; InterpreterSelectQuery interpreter(subquery, subquery_context, QueryProcessingStage::Complete, subquery_depth + 1); + ast_set->set = new Set(settings.limits); ast_set->set->setSource(interpreter.execute()); sets_with_subqueries[ast_set->getColumnName()] = ast_set->set; @@ -797,7 +884,7 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl if (node->kind == ASTFunction::FUNCTION) { - if (node->name == "in" || node->name == "notIn") + if (node->name == "in" || node->name == "notIn" || node->name == "globalIn" || node->name == "globalNotIn") { if (!no_subqueries) { @@ -1092,6 +1179,19 @@ void ExpressionAnalyzer::addMultipleArrayJoinAction(ExpressionActions & actions) actions.add(ExpressionActions::Action::arrayJoin(result_columns)); } +void ExpressionAnalyzer::processGlobalOperations() +{ + findGlobalFunctions(ast); + + size_t id = 1; + for (size_t i = 0; i < global_nodes.size(); ++i) + { + String external_table_name = "_data"; + while (context.tryGetExternalTable(external_table_name + toString(id))) + id ++; + addExternalStorage(dynamic_cast(&*global_nodes[i]), id); + } +} bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain) { diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index ca0f8741d78..b410896ce4c 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -78,6 +78,11 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth); + query_analyzer->processGlobalOperations(); + + for (auto & it : query_analyzer->external_tables) + context.addExternalTable(it->getTableName(), it); + if (input_) streams.push_back(input_); } diff --git a/dbms/src/Parsers/ExpressionListParsers.cpp b/dbms/src/Parsers/ExpressionListParsers.cpp index ee71f89a92b..60b78c56846 100644 --- a/dbms/src/Parsers/ExpressionListParsers.cpp +++ b/dbms/src/Parsers/ExpressionListParsers.cpp @@ -47,6 +47,8 @@ const char * ParserComparisonExpression::operators[] = "NOT LIKE", "notLike", "IN", "in", "NOT IN", "notIn", + "GLOBAL IN", "globalIn", + "GLOBAL NOT IN","globalNotIn", nullptr }; diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index b14dee0605b..a67d0fe8c36 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -225,8 +225,6 @@ void TCPHandler::readData(const Settings & global_settings) if (Daemon::instance().isCancelled() || in->eof()) return; - std::cerr << "Receiving packet" << std::endl; - if (!receivePacket()) break; } From 003c7b30cdf118e4b079134273c4762929bb7274 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 14 Mar 2014 19:42:30 +0400 Subject: [PATCH 083/281] dbms: fixes and more comments on 'Global in' and external tables behavior [METR-10071] --- dbms/include/DB/Client/Connection.h | 5 ++++- .../DB/DataStreams/RemoteBlockInputStream.h | 4 +++- .../DB/Interpreters/ExpressionAnalyzer.h | 10 +++++----- dbms/include/DB/Storages/StorageDistributed.h | 3 +++ dbms/src/Client/Client.cpp | 17 ++++++++++++----- dbms/src/Client/Connection.cpp | 2 +- dbms/src/Interpreters/Context.cpp | 2 ++ dbms/src/Interpreters/ExpressionAnalyzer.cpp | 8 +++++--- .../src/Interpreters/InterpreterSelectQuery.cpp | 2 ++ 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index 5c52a14f141..d5ab6afe10a 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -83,12 +83,15 @@ public: /// Адрес сервера - для сообщений в логе и в эксепшенах. String getServerAddress() const; + /// Если последний флаг true, то затем необходимо вызвать sendExternalTablesData void sendQuery(const String & query, const String & query_id_ = "", UInt64 stage = QueryProcessingStage::Complete, const Settings * settings = NULL, bool with_pending_data = false); void sendCancel(); + /// Отправить блок данных, на сервере сохранить во временную таблицу name void sendData(const Block & block, const String & name = ""); - void sendExternalTables(ExternalTablesData & data); + /// Отправить все содержимое внешних таблиц + void sendExternalTablesData(ExternalTablesData & data); /// Проверить, если ли данные, которые можно прочитать. bool poll(size_t timeout_microseconds = 0); diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index 753685e6607..e1a816acc82 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -123,6 +123,7 @@ protected: } } + /// Отправить на удаленные сервера все временные таблицы void sendExternalTables() { ExternalTablesData res; @@ -133,7 +134,7 @@ protected: QueryProcessingStage::Enum stage = QueryProcessingStage::Complete; res.push_back(std::make_pair(cur->read(cur->getColumnNamesList(), ASTPtr(), settings, stage, DEFAULT_BLOCK_SIZE, 1)[0], it->first)); } - connection.sendExternalTables(res); + connection.sendExternalTablesData(res); } Block readImpl() @@ -268,6 +269,7 @@ private: String _host_column; /// Имя столбца, куда записать номер порта (Например "_port"). Пустая строка, если записывать не надо. String _port_column; + /// Временные таблицы, которые необходимо переслать на удаленные сервера. Tables external_tables; QueryProcessingStage::Enum stage; diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 0923bd6776d..41008e12c75 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -97,8 +97,8 @@ public: /// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT. Block getSelectSampleBlock(); + /// Все новые временные таблицы, полученные при выполнении подзапросов Global In std::vector external_tables; - private: typedef std::set NamesSet; @@ -128,9 +128,6 @@ private: AggregateDescriptions aggregate_descriptions; std::map sets_with_subqueries; - - std::vector global_nodes; - typedef std::map Aliases; Aliases aliases; @@ -264,10 +261,13 @@ private: void normalizeTree(); void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias, bool in_sign_rewritten); - void findGlobalFunctions(ASTPtr & ast); + /// Обходит запрос и сохраняет найденные глобальные функции (например Global in) + void findGlobalFunctions(ASTPtr & ast, std::vector & global_nodes); /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. void makeSet(ASTFunction * node, const Block & sample_block); + /// Выполнить подзапрос в секции global in и запомнить результат во временную таблицу типа memory + /// Все новые временные таблицы хранятся в переменной external_tables void addExternalStorage(ASTFunction * node, size_t & name_id); void getArrayJoinedColumns(); diff --git a/dbms/include/DB/Storages/StorageDistributed.h b/dbms/include/DB/Storages/StorageDistributed.h index 443629370b5..5640bb532f0 100644 --- a/dbms/include/DB/Storages/StorageDistributed.h +++ b/dbms/include/DB/Storages/StorageDistributed.h @@ -49,6 +49,7 @@ public: bool hasColumn(const String &column_name) const; bool isRemote() const { return true; } + /// Сохранить временные таблицы, чтобы при следующем вызове метода read переслать их на удаленные сервера void storeExternalTables(const Tables & tables_) { external_tables = tables_; } BlockInputStreams read( @@ -93,6 +94,8 @@ private: const Context & context; + /// Временные таблицы, которые необходимо отправить на сервер. Переменная очищается после каждого вызова метода read + /// Для подготовки к отправке нужно использовтаь метод storeExternalTables Tables external_tables; /// Используется только, если таблица должна владеть объектом Cluster, которым больше никто не владеет - для реализации TableFunctionRemote. diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 9719dc17adf..7081141ad4f 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -58,14 +58,17 @@ namespace DB using Poco::SharedPtr; - +/// Описание внешней таблицы class ExternalTable { public: - std::string file; - std::string name; - std::string format; + std::string file; /// Файл с данными или '-' если stdin + std::string name; /// Имя таблицы + std::string format; /// Название формата хранения данных + + /// Описание структуры таблицы: (имя столбца, имя типа данных) std::vector > structure; + ReadBuffer *read_buffer; Block sample_block; @@ -98,6 +101,7 @@ public: return res; } + /// Функция для отладочного вывода информации void write() { std::cerr << "file " << file << std::endl; @@ -108,6 +112,7 @@ public: std::cerr << "\t" << structure[i].first << " " << structure[i].second << std::endl; } + /// Извлечение параметров из variables_map, которая строится по командной строке ExternalTable(const boost::program_options::variables_map & external_options) { if (external_options.count("file")) @@ -551,15 +556,17 @@ private: } + /// Преобразовать внешние таблицы к ExternalTableData и переслать через connection void sendExternalTables() { const ASTSelectQuery * select = dynamic_cast(&*parsed_query); if (!select && !external_tables.empty()) throw Exception("External tables could be sent only with select query", ErrorCodes::BAD_ARGUMENTS); + std::vector data; for (size_t i = 0; i < external_tables.size(); ++i) data.push_back(external_tables[i].getData(context)); - connection->sendExternalTables(data); + connection->sendExternalTablesData(data); } diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 8a57ea33fb6..f7e5af1db9d 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -272,7 +272,7 @@ void Connection::sendData(const Block & block, const String & name) out->next(); } -void Connection::sendExternalTables(ExternalTablesData & data) +void Connection::sendExternalTablesData(ExternalTablesData & data) { /// Если работаем со старым сервером, то никакой информации не отправляем if (server_revision < DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index b39263188fa..67aa10b9f62 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -163,6 +163,8 @@ void Context::assertDatabaseDoesntExist(const String & database_name) const Tables Context::getExternalTables() const { + Poco::ScopedLock lock(shared->mutex); + Tables res = external_tables; if (session_context && session_context != this) { diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 8a96268adfb..32b4543048f 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -403,6 +403,7 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as current_asts.insert(ast); replaced = true; } + /// может быть указано in t, где t - таблица, что равносильно select * from t. if (node->name == "in" || node->name == "notIn" || node->name == "globalIn" || node->name == "globalNotIn") if (ASTIdentifier * right = dynamic_cast(&*node->arguments->children[1])) right->kind = ASTIdentifier::Table; @@ -514,12 +515,12 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as } -void ExpressionAnalyzer::findGlobalFunctions(ASTPtr & ast) +void ExpressionAnalyzer::findGlobalFunctions(ASTPtr & ast, std::vector & global_nodes) { /// Рекурсивные вызовы. Не опускаемся в подзапросы. for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) if (!dynamic_cast(&**it)) - findGlobalFunctions(*it); + findGlobalFunctions(*it, global_nodes); if (ASTFunction * node = dynamic_cast(&*ast)) { @@ -1181,7 +1182,8 @@ void ExpressionAnalyzer::addMultipleArrayJoinAction(ExpressionActions & actions) void ExpressionAnalyzer::processGlobalOperations() { - findGlobalFunctions(ast); + std::vector global_nodes; + findGlobalFunctions(ast, global_nodes); size_t id = 1; for (size_t i = 0; i < global_nodes.size(); ++i) diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index b410896ce4c..78caf91142b 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -78,8 +78,10 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth); + /// Выполняем все Global in подзапросы, результаты будут сохранены в query_analyzer->external_tables query_analyzer->processGlobalOperations(); + /// Сохраняем в query context новые временные таблицы for (auto & it : query_analyzer->external_tables) context.addExternalTable(it->getTableName(), it); From 9ecf7b3f795647c02f5983e29b7b359ed9a61693 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 19 Mar 2014 16:52:14 +0400 Subject: [PATCH 084/281] defines: fixed DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES to correct value [METR-10071] --- dbms/include/DB/Core/Defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index a87875c0ee8..2212b58811d 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -40,4 +40,4 @@ #define DBMS_MIN_REVISION_WITH_USER_PASSWORD 34482 #define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265 #define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002 -#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50092 +#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50237 From c9c2f671f23be856284dd4803bb873b154fc843a Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 19 Mar 2014 17:03:29 +0400 Subject: [PATCH 085/281] connection: fixed bug with sending extra block to old-versioned server [METR-10071] --- dbms/src/Client/Connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index f7e5af1db9d..2887af93e72 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -232,8 +232,8 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 block_in = NULL; block_out = NULL; - /// Отправляем пустой блок, символизируя конец передачи данных - if (!with_pending_data) + /// Если версия сервера достаточно новая и стоит флаг, отправляем пустой блок, символизируя конец передачи данных. + if (server_revision > DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES && !with_pending_data) sendData(Block()); } From a4d499086000e45fc2b64f5eab1a88e53d7d65d4 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 19 Mar 2014 19:07:29 +0400 Subject: [PATCH 086/281] dbms: fixed problem with sending empty tables, few more fixes [METR-10071] --- dbms/include/DB/DataStreams/RemoteBlockInputStream.h | 6 +++++- dbms/include/DB/Storages/StorageMemory.h | 2 ++ dbms/src/Server/TCPHandler.cpp | 5 +++-- dbms/src/Storages/StorageDistributed.cpp | 5 +++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index e1a816acc82..5828d2f64f6 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -132,7 +132,11 @@ protected: { StoragePtr cur = it->second; QueryProcessingStage::Enum stage = QueryProcessingStage::Complete; - res.push_back(std::make_pair(cur->read(cur->getColumnNamesList(), ASTPtr(), settings, stage, DEFAULT_BLOCK_SIZE, 1)[0], it->first)); + DB::BlockInputStreams input = cur->read(cur->getColumnNamesList(), ASTPtr(), settings, stage, DEFAULT_BLOCK_SIZE, 1); + if (input.size() == 0) + res.push_back(std::make_pair(new OneBlockInputStream(cur->getSampleBlock()), it->first)); + else + res.push_back(std::make_pair(input[0], it->first)); } connection.sendExternalTablesData(res); } diff --git a/dbms/include/DB/Storages/StorageMemory.h b/dbms/include/DB/Storages/StorageMemory.h index e6ab8d97b4b..10f46a4bec1 100644 --- a/dbms/include/DB/Storages/StorageMemory.h +++ b/dbms/include/DB/Storages/StorageMemory.h @@ -69,6 +69,8 @@ public: const NamesAndTypesList & getColumnsList() const { return *columns; } + size_t getSize() const { return data.size(); } + BlockInputStreams read( const Names & column_names, ASTPtr query, diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index a67d0fe8c36..3a566864985 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -548,7 +548,7 @@ bool TCPHandler::receiveData() /// Прочитать из сети один блок и записать его Block block = state.block_in->read(); - if (block) + if (block || !external_table_name.empty()) { /// Если запрос на вставку, то данные нужно писать напрямую в state.io.out. /// Иначе пишем блоки во временную таблицу external_table_name. @@ -565,7 +565,8 @@ bool TCPHandler::receiveData() /// Данные будем писать напрямую в таблицу. state.io.out = storage->write(ASTPtr()); } - state.io.out->write(block); + if (block) + state.io.out->write(block); return true; } else diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index 9c79a68e332..d60c2f80b64 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -219,7 +219,7 @@ BlockInputStreams StorageDistributed::read( res.push_back(new RemoveColumnsBlockInputStream(temp, columns_to_remove)); } - if (all_inclusive || values.find(std::make_pair("localhost", clickhouse_port)) != values.end()) + if (cluster.getLocalNodesNum() > 0 && (all_inclusive || values.find(std::make_pair("localhost", clickhouse_port)) != values.end())) { ASTPtr modified_query_ast = remakeQuery( query, @@ -230,7 +230,8 @@ BlockInputStreams StorageDistributed::read( DB::Context new_context = context; new_context.setSettings(new_settings); for (auto & it : external_tables) - new_context.addExternalTable(it.first, it.second); + if (!new_context.tryGetExternalTable(it.first)) + new_context.addExternalTable(it.first, it.second); for(size_t i = 0; i < cluster.getLocalNodesNum(); ++i) { From eda2a1590bd8fec37081c215b760e50238dd7768 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 19 Mar 2014 19:50:04 +0400 Subject: [PATCH 087/281] dbms: fixed different names of new external storages in expression analyzer [METR-10071] --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 32b4543048f..1b1f0774af5 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -580,7 +580,7 @@ void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id Block sample = interpreter.getSampleBlock(); NamesAndTypesListPtr columns = new NamesAndTypesList(sample.getColumnsList()); - String external_table_name = "_table" + toString(name_id++); + String external_table_name = "_data" + toString(name_id++); external_storage = StorageMemory::create(external_table_name, columns); BlockOutputStreamPtr output = external_storage->write(ASTPtr()); copyData(*interpreter.execute(), *output); From 0898ccfeb4cb8a4b41ca4cd972b28d4ab3412d35 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 26 Mar 2014 17:06:10 +0400 Subject: [PATCH 088/281] dbms: fixed merging errors [METR-10071] --- dbms/src/Interpreters/InterpreterCreateQuery.cpp | 2 +- dbms/src/Storages/ITableDeclaration.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index 6a05b4127e2..ee2cd8d004a 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -237,7 +237,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) if (create.is_temporary) { - res->drop_on_destroy = true; + res->is_dropped = true; context.getSessionContext().addExternalTable(table_name, res); } else diff --git a/dbms/src/Storages/ITableDeclaration.cpp b/dbms/src/Storages/ITableDeclaration.cpp index 9145faced2f..abf21a3ccbf 100644 --- a/dbms/src/Storages/ITableDeclaration.cpp +++ b/dbms/src/Storages/ITableDeclaration.cpp @@ -10,7 +10,7 @@ namespace DB { -bool IStorage::hasRealColumn(const String &column_name) const +bool ITableDeclaration::hasRealColumn(const String &column_name) const { const NamesAndTypesList & real_columns = getColumnsList(); for (auto & it : real_columns) From 5237f8b6ca9318516723af4668c5679c7e7ee40e Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 27 Mar 2014 15:30:08 +0400 Subject: [PATCH 089/281] dbms: fixed cancelling input stream in tcphandler [METR-10071] --- dbms/include/DB/Core/Defines.h | 2 +- dbms/src/Client/Connection.cpp | 2 +- dbms/src/Server/TCPHandler.cpp | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index 2212b58811d..025bb85e44c 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -40,4 +40,4 @@ #define DBMS_MIN_REVISION_WITH_USER_PASSWORD 34482 #define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265 #define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002 -#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50237 +#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50263 diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 2887af93e72..df4b933769e 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -233,7 +233,7 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 block_out = NULL; /// Если версия сервера достаточно новая и стоит флаг, отправляем пустой блок, символизируя конец передачи данных. - if (server_revision > DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES && !with_pending_data) + if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES && !with_pending_data) sendData(Block()); } diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 3a566864985..06e4c2d910d 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -118,6 +118,10 @@ void TCPHandler::runImpl() if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) readData(global_settings); + /// Пересоздаем, поскольку получая данные внешних таблиц, мы получили пустой блок. + /// Из-за этого весь stream помечен как cancelled + state.block_in = BlockInputStreamPtr(); + /// Обрабатываем Query state.io = executeQuery(state.query, query_context, false, state.stage); @@ -548,7 +552,8 @@ bool TCPHandler::receiveData() /// Прочитать из сети один блок и записать его Block block = state.block_in->read(); - if (block || !external_table_name.empty()) + + if (block) { /// Если запрос на вставку, то данные нужно писать напрямую в state.io.out. /// Иначе пишем блоки во временную таблицу external_table_name. From 6a3043e74978e171acce2a8b1d4e7c56c4b74f69 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 16:32:37 +0400 Subject: [PATCH 090/281] Deduplicated code for merge tree writing; previous commit is useless. [#METR-10202] --- .../Storages/MergeTree/MergeTreeDataWriter.h | 10 +- .../MergeTree/MergedBlockOutputStream.h | 95 +++--------- .../MergeTree/MergeTreeDataMerger.cpp | 12 +- .../MergeTree/MergeTreeDataWriter.cpp | 138 +----------------- 4 files changed, 40 insertions(+), 215 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h index 2220456ac33..ebeb19f374f 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataWriter.h @@ -31,7 +31,7 @@ typedef std::list BlocksWithDateIntervals; class MergeTreeDataWriter { public: - MergeTreeDataWriter(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataWriter")), flags(O_TRUNC | O_CREAT | O_WRONLY) {} + MergeTreeDataWriter(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataWriter")) {} /** Разбивает блок на блоки, каждый из которых нужно записать в отдельный кусок. * (читай: разбивает строки по месяцам) @@ -49,14 +49,6 @@ private: MergeTreeData & data; Logger * log; - - const int flags; - - typedef std::set OffsetColumns; - - /// Записать данные одного столбца. - void writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, MergeTreeData::DataPart::Checksums & checksums, size_t level = 0); }; } diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index 6a745460985..a243813778e 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -4,6 +4,9 @@ #include #include +#include +#include +#include namespace DB @@ -20,9 +23,9 @@ protected: struct ColumnStream { ColumnStream(const String & data_path, const std::string & marks_path) : - plain(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY), - compressed(plain), - marks(marks_path, 4096, O_TRUNC | O_CREAT | O_WRONLY) {} + plain(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY), + compressed(plain), + marks(marks_path, 4096, O_TRUNC | O_CREAT | O_WRONLY) {} WriteBufferFromFile plain; CompressedWriteBuffer compressed; @@ -66,19 +69,6 @@ protected: addStream(path, name, *type_arr->getNestedType(), level + 1); } - else if (const DataTypeNested * type_nested = dynamic_cast(&type)) - { - String size_name = name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - String escaped_size_name = escaped_column_name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - - column_streams[size_name] = new ColumnStream( - path + escaped_size_name + ".bin", - path + escaped_size_name + ".mrk"); - - const NamesAndTypesList & columns = *type_nested->getNestedTypesList(); - for (NamesAndTypesList::const_iterator it = columns.begin(); it != columns.end(); ++it) - addStream(path, DataTypeNested::concatenateNestedName(name, it->first), *it->second, level + 1); - } else column_streams[name] = new ColumnStream( path + escaped_column_name + ".bin", @@ -126,34 +116,6 @@ protected: } } } - else if (const DataTypeNested * type_nested = dynamic_cast(&type)) - { - String size_name = name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - - ColumnStream & stream = *column_streams[size_name]; - - size_t prev_mark = 0; - while (prev_mark < size) - { - size_t limit = 0; - - /// Если есть index_offset, то первая засечка идёт не сразу, а после этого количества строк. - if (prev_mark == 0 && index_offset != 0) - { - limit = index_offset; - } - else - { - limit = storage.index_granularity; - writeIntBinary(stream.plain.count(), stream.marks); - writeIntBinary(stream.compressed.offset(), stream.marks); - } - - type_nested->serializeOffsets(column, stream.compressed, prev_mark, limit); - stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - prev_mark += limit; - } - } { ColumnStream & stream = *column_streams[name]; @@ -190,30 +152,21 @@ protected: size_t index_offset; }; -/** Для записи куска, полученного слиянием нескольких других. - * Данные уже отсортированы, относятся к одному месяцу, и пишутся в один кускок. +/** Для записи одного куска. Данные уже отсортированы, относятся к одному месяцу, и пишутся в один кускок. */ class MergedBlockOutputStream : public IMergedBlockOutputStream { public: - MergedBlockOutputStream(MergeTreeData & storage_, - UInt16 min_date, UInt16 max_date, UInt64 min_part_id, UInt64 max_part_id, UInt32 level) - : IMergedBlockOutputStream(storage_), marks_count(0) + MergedBlockOutputStream(MergeTreeData & storage_, String part_path_, const NamesAndTypesList & columns_list_) + : IMergedBlockOutputStream(storage_), columns_list(columns_list_), part_path(part_path_), marks_count(0) { - part_name = storage.getPartName( - DayNum_t(min_date), DayNum_t(max_date), - min_part_id, max_part_id, level); + Poco::File(part_path).createDirectories(); - part_tmp_path = storage.getFullPath() + "tmp_" + part_name + "/"; - part_res_path = storage.getFullPath() + part_name + "/"; - - Poco::File(part_tmp_path).createDirectories(); - - index_stream = new WriteBufferFromFile(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); + index_stream = new WriteBufferFromFile(part_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); columns_list = storage.getColumnsList(); for (const auto & it : columns_list) - addStream(part_tmp_path, it.first, *it.second); + addStream(part_path, it.first, *it.second); } void write(const Block & block) @@ -234,7 +187,8 @@ public: { for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it) { - (*it)->type->serializeBinary((*(*it)->column)[i], *index_stream); + index_vec.push_back((*(*it)->column)[i]); + (*it)->type->serializeBinary(index_vec.back(), *index_stream); } ++marks_count; @@ -268,15 +222,13 @@ public: if (marks_count == 0) { /// Кусок пустой - все записи удалились. - Poco::File(part_tmp_path).remove(true); + Poco::File(part_path).remove(true); } - else - { - /// Переименовываем кусок. - Poco::File(part_tmp_path).renameTo(part_res_path); + } - /// А добавление нового куска в набор (и удаление исходных кусков) сделает вызывающая сторона. - } + MergeTreeData::DataPart::Index & getIndex() + { + return index_vec; } /// Сколько засечек уже записано. @@ -287,13 +239,12 @@ public: private: NamesAndTypesList columns_list; + String part_path; - String part_name; - String part_tmp_path; - String part_res_path; size_t marks_count; - + SharedPtr index_stream; + MergeTreeData::DataPart::Index index_vec; }; typedef Poco::SharedPtr MergedBlockOutputStreamPtr; @@ -315,7 +266,7 @@ public: for (size_t i = 0; i < block.columns(); ++i) { addStream(part_path, block.getByPosition(i).name, - *block.getByPosition(i).type, 0, prefix + block.getByPosition(i).name); + *block.getByPosition(i).type, 0, prefix + block.getByPosition(i).name); } initialized = true; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 160b4dc2b95..f10faef52ca 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -292,8 +292,10 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa throw Exception("Unknown mode of operation for MergeTreeData: " + toString(data.mode), ErrorCodes::LOGICAL_ERROR); } - MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); + String new_part_tmp_path = data.getFullPath() + "tmp_" + new_data_part->name + "/"; + String new_part_res_path = data.getFullPath() + new_data_part->name + "/"; + + MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, new_part_tmp_path, data.getColumnsList()); merged_stream->readPrefix(); to->writePrefix(); @@ -311,6 +313,8 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa merged_stream->readSuffix(); to->writeSuffix(); + new_data_part->index.swap(to->getIndex()); + /// В обычном режиме строчки не могут удалиться при мердже. if (0 == to->marksCount() && data.mode == MergeTreeData::Ordinary) throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); @@ -324,8 +328,8 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa return ""; } - /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи. - new_data_part->loadIndex(); + /// Переименовываем кусок. + Poco::File(new_part_tmp_path).renameTo(new_part_res_path); /// Добавляем новый кусок в набор. data.replaceParts(parts, new_data_part); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index e2ebfaab235..c1e6051e7bc 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -78,9 +79,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - size_t rows = block.rows(); - size_t columns = block.columns(); - size_t part_size = (rows + data.index_granularity - 1) / data.index_granularity; + size_t part_size = (block.rows() + data.index_granularity - 1) / data.index_granularity; String tmp_part_name = "tmp_" + data.getPartName( DayNum_t(min_date), DayNum_t(max_date), @@ -102,59 +101,12 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa /// Сортируем. stableSortBlock(block, sort_descr); - /// Наконец-то можно писать данные на диск. - LOG_TRACE(log, "Writing index."); + MergedBlockOutputStream out(data, part_tmp_path, block.getColumnsList()); + out.getIndex().reserve(part_size * sort_descr.size()); - MergeTreeData::DataPart::Checksums checksums; - MergeTreeData::DataPart::Index index_vec; - - /// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки. - index_vec.reserve(part_size * sort_descr.size()); - - { - WriteBufferFromFile index_file(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags); - HashingWriteBuffer index(index_file); - - typedef std::vector PrimaryColumns; - PrimaryColumns primary_columns; - - for (size_t i = 0, size = sort_descr.size(); i < size; ++i) - primary_columns.push_back( - !sort_descr[i].column_name.empty() - ? &block.getByName(sort_descr[i].column_name) - : &block.getByPosition(sort_descr[i].column_number)); - - for (size_t i = 0; i < rows; i += data.index_granularity) - { - for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it) - { - index_vec.push_back((*(*it)->column)[i]); - (*it)->type->serializeBinary(index_vec.back(), index); - } - } - - index.next(); - checksums.file_checksums["primary.idx"].size = index.count(); - checksums.file_checksums["primary.idx"].hash = index.getHash(); - } - - LOG_TRACE(log, "Writing data."); - - /// Множество записанных столбцов со смещениями, чтобы не писать общие для вложенных структур столбцы несколько раз - OffsetColumns offset_columns; - - for (size_t i = 0; i < columns; ++i) - { - const ColumnWithNameAndType & column = block.getByPosition(i); - writeData(part_tmp_path, column.name, *column.type, *column.column, offset_columns, checksums); - } - - /// Запишем файл с чексуммами. - { - WriteBufferFromFile checksums_file(part_tmp_path + "checksums.txt", 1024, flags); - checksums.writeText(checksums_file); - checksums_file.next(); - } + out.writePrefix(); + out.write(block); + out.writeSuffix(); MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); new_data_part->left_date = DayNum_t(min_date); @@ -167,83 +119,9 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa new_data_part->modification_time = time(0); new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); - new_data_part->index.swap(index_vec); - new_data_part->checksums = checksums; + new_data_part->index.swap(out.getIndex()); return new_data_part; } -void MergeTreeDataWriter::writeData(const String & path, const String & name, const IDataType & type, const IColumn & column, - OffsetColumns & offset_columns, MergeTreeData::DataPart::Checksums & checksums, size_t level) -{ - String escaped_column_name = escapeForFileName(name); - size_t size = column.size(); - - /// Для массивов требуется сначала сериализовать размеры, а потом значения. - if (const DataTypeArray * type_arr = dynamic_cast(&type)) - { - String size_name = escapeForFileName(DataTypeNested::extractNestedTableName(name)) - + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - if (offset_columns.count(size_name) == 0) - { - offset_columns.insert(size_name); - - WriteBufferFromFile plain_file(path + size_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks_file(path + size_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed_file(plain_file); - HashingWriteBuffer marks(marks_file); - HashingWriteBuffer compressed(compressed_file); - - size_t prev_mark = 0; - while (prev_mark < size) - { - /// Каждая засечка - это: (смещение в файле до начала сжатого блока, смещение внутри блока) - writeIntBinary(plain_file.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type_arr->serializeOffsets(column, compressed, prev_mark, data.index_granularity); - prev_mark += data.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain_file.next(); - marks.next(); - checksums.file_checksums[size_name + ".bin"].size = compressed.count(); - checksums.file_checksums[size_name + ".bin"].hash = compressed.getHash(); - checksums.file_checksums[size_name + ".mrk"].size = marks.count(); - checksums.file_checksums[size_name + ".mrk"].hash = marks.getHash(); - } - } - - { - WriteBufferFromFile plain_file(path + escaped_column_name + ".bin", DBMS_DEFAULT_BUFFER_SIZE, flags); - WriteBufferFromFile marks_file(path + escaped_column_name + ".mrk", 4096, flags); - CompressedWriteBuffer compressed_file(plain_file); - HashingWriteBuffer marks(marks_file); - HashingWriteBuffer compressed(compressed_file); - - size_t prev_mark = 0; - while (prev_mark < size) - { - writeIntBinary(plain_file.count(), marks); - writeIntBinary(compressed.offset(), marks); - - type.serializeBinary(column, compressed, prev_mark, data.index_granularity); - prev_mark += data.index_granularity; - - compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. - } - - compressed.next(); - plain_file.next(); - marks.next(); - checksums.file_checksums[escaped_column_name + ".bin"].size = compressed.count(); - checksums.file_checksums[escaped_column_name + ".bin"].hash = compressed.getHash(); - checksums.file_checksums[escaped_column_name + ".mrk"].size = marks.count(); - checksums.file_checksums[escaped_column_name + ".mrk"].hash = marks.getHash(); - } -} - } From e735568723972b9fff49dd9122dbde707c84c154 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 27 Mar 2014 16:48:09 +0400 Subject: [PATCH 091/281] dbms: aggregate function array [METR-10366] --- .../AggregateFunctionArray.h | 120 ++++++++++++++++++ .../AggregateFunctionFactory.cpp | 22 +++- 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h new file mode 100644 index 00000000000..6e40bd22306 --- /dev/null +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include + + +namespace DB +{ + + +/** Не агрегатная функция, а адаптер агрегатных функций, + * который любую агрегатную функцию agg(x) делает агрегатной функцией вида aggArray(x). + * Адаптированная агрегатная функция вычисляет вложенную агрегатную функцию для каждого элемента массива. + */ +class AggregateFunctionArray : public IAggregateFunction +{ +private: + AggregateFunctionPtr nested_func_owner; + IAggregateFunction * nested_func; + int num_agruments; + +public: + AggregateFunctionArray(AggregateFunctionPtr nested_) : nested_func_owner(nested_), nested_func(nested_func_owner.get()) {} + + String getName() const + { + return nested_func->getName() + "Array"; + } + + DataTypePtr getReturnType() const + { + return nested_func->getReturnType(); + } + + void setArguments(const DataTypes & arguments) + { + num_agruments = arguments.size(); + + DataTypes nested_arguments; + for (int i = 0; i < num_agruments; ++i) + { + if (const DataTypeArray * array = dynamic_cast(&*arguments[i])) + nested_arguments.push_back(array->getNestedType()); + else + throw Exception("Illegal type " + arguments[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + getName() + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + nested_func->setArguments(nested_arguments); + } + + void setParameters(const Array & params) + { + nested_func->setParameters(params); + } + + void create(AggregateDataPtr place) const + { + nested_func->create(place); + } + + void destroy(AggregateDataPtr place) const + { + nested_func->destroy(place); + } + + bool hasTrivialDestructor() const + { + return nested_func->hasTrivialDestructor(); + } + + size_t sizeOfData() const + { + return nested_func->sizeOfData(); + } + + size_t alignOfData() const + { + return nested_func->alignOfData(); + } + + void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num) const + { + const IColumn ** nested = new const IColumn*[num_agruments]; + std::vector column_ptrs; + for (int i = 0; i < num_agruments; ++i) + { + ColumnPtr single_value_column = dynamic_cast(*columns[i]).cut(row_num, 1); + column_ptrs.push_back(single_value_column); + nested[i] = dynamic_cast(*single_value_column).getDataPtr().get(); + } + for (int i = 0; i < num_agruments; ++i) + if (nested[i]->size() != nested[0]->size()) + throw Exception("All arrays must be of the same size. Aggregate function " + getName(), ErrorCodes::BAD_ARGUMENTS); + for (size_t i = 0; i < nested[0]->size(); ++i) + nested_func->add(place, nested, i); + } + + void merge(AggregateDataPtr place, ConstAggregateDataPtr rhs) const + { + nested_func->merge(place, rhs); + } + + void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const + { + nested_func->serialize(place, buf); + } + + void deserializeMerge(AggregateDataPtr place, ReadBuffer & buf) const + { + nested_func->deserializeMerge(place, buf); + } + + void insertResultInto(ConstAggregateDataPtr place, IColumn & to) const + { + nested_func->insertResultInto(place, to); + } +}; + +} diff --git a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp index f7dc1c31bb5..9efa8b65fad 100644 --- a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp +++ b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -317,9 +318,25 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da /// Для агрегатных функций вида aggIf, где agg - имя другой агрегатной функции. DataTypes nested_dt = argument_types; nested_dt.pop_back(); - AggregateFunctionPtr nested = get(String(name.data(), name.size() - 2), nested_dt); + AggregateFunctionPtr nested = get(String(name.data(), name.size() - 2), nested_dt, recursion_level + 1); return new AggregateFunctionIf(nested); } + else if (recursion_level <= 1 && name.size() >= 6 && name.substr(name.size()-5) == "Array") + { + /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. + size_t num_agruments = argument_types.size(); + + DataTypes nested_arguments; + for (size_t i = 0; i < num_agruments; ++i) + { + if (const DataTypeArray * array = dynamic_cast(&*argument_types[i])) + nested_arguments.push_back(array->getNestedType()); + else + throw Exception("Illegal type " + argument_types[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + name + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + } + AggregateFunctionPtr nested = get(String(name.data(), name.size() - 5), nested_arguments, recursion_level + 1); + return new AggregateFunctionArray(nested); + } else throw Exception("Unknown aggregate function " + name, ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); } @@ -369,6 +386,9 @@ bool AggregateFunctionFactory::isAggregateFunctionName(const String & name, int /// Для агрегатных функций вида aggIf, где agg - имя другой агрегатной функции. if (recursion_level == 0 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f') return isAggregateFunctionName(String(name.data(), name.size() - 2), 1); + /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. + if (recursion_level <= 1 && name.size() >= 6 && name.substr(name.size()-5) == "Array") + return isAggregateFunctionName(String(name.data(), name.size() - 5), 1); return false; } From a8f4ed62788d03ffb146db1e66b793cfb8d1faf2 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 21:30:04 +0400 Subject: [PATCH 092/281] Merge --- .../DB/Storages/MergeTree/MergeTreeData.h | 7 +- .../MergeTree/MergedBlockOutputStream.h | 90 +++++++++++++---- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 96 ++++++++++++------- .../MergeTree/MergeTreeDataMerger.cpp | 2 +- .../MergeTree/MergeTreeDataWriter.cpp | 3 +- 5 files changed, 140 insertions(+), 58 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index dc7af53d186..616d7392697 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -112,7 +112,7 @@ public: }; typedef std::map FileChecksums; - FileChecksums file_checksums; + FileChecksums files; /// Проверяет, что множество столбцов и их контрольные суммы совпадают. Если нет - бросает исключение. void check(const Checksums & rhs) const; @@ -120,6 +120,11 @@ public: /// Сериализует и десериализует в человекочитаемом виде. void readText(ReadBuffer & in); void writeText(WriteBuffer & out) const; + + bool empty() const + { + return files.empty(); + } }; DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index a243813778e..50756c2c2c5 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -22,26 +23,41 @@ protected: typedef std::set OffsetColumns; struct ColumnStream { - ColumnStream(const String & data_path, const std::string & marks_path) : - plain(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY), - compressed(plain), - marks(marks_path, 4096, O_TRUNC | O_CREAT | O_WRONLY) {} + ColumnStream(const String & escaped_column_name_, const String & data_path, const std::string & marks_path) : + escaped_column_name(escaped_column_name_), + plain_file(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY), + compressed_buf(plain_file), + marks_file(marks_path, 4096, O_TRUNC | O_CREAT | O_WRONLY), + compressed(compressed_buf), marks(marks_file) {} - WriteBufferFromFile plain; - CompressedWriteBuffer compressed; - WriteBufferFromFile marks; + String escaped_column_name; + WriteBufferFromFile plain_file; + CompressedWriteBuffer compressed_buf; + WriteBufferFromFile marks_file; + HashingWriteBuffer compressed; + HashingWriteBuffer marks; void finalize() { compressed.next(); - plain.next(); + plain_file.next(); marks.next(); } void sync() { - plain.sync(); - marks.sync(); + plain_file.sync(); + marks_file.sync(); + } + + void addToChecksums(MergeTreeData::DataPart::Checksums & checksums, String name = "") + { + if (name == "") + name = escaped_column_name; + checksums.files[name + ".bin"].size = compressed.count(); + checksums.files[name + ".bin"].hash = compressed.getHash(); + checksums.files[name + ".mrk"].size = marks.count(); + checksums.files[name + ".mrk"].hash = marks.getHash(); } }; @@ -64,6 +80,7 @@ protected: + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); column_streams[size_name] = new ColumnStream( + escaped_size_name, path + escaped_size_name + ".bin", path + escaped_size_name + ".mrk"); @@ -71,6 +88,7 @@ protected: } else column_streams[name] = new ColumnStream( + escaped_column_name, path + escaped_column_name + ".bin", path + escaped_column_name + ".mrk"); } @@ -106,7 +124,7 @@ protected: else { limit = storage.index_granularity; - writeIntBinary(stream.plain.count(), stream.marks); + writeIntBinary(stream.plain_file.count(), stream.marks); writeIntBinary(stream.compressed.offset(), stream.marks); } @@ -133,7 +151,7 @@ protected: else { limit = storage.index_granularity; - writeIntBinary(stream.plain.count(), stream.marks); + writeIntBinary(stream.plain_file.count(), stream.marks); writeIntBinary(stream.compressed.offset(), stream.marks); } @@ -162,7 +180,8 @@ public: { Poco::File(part_path).createDirectories(); - index_stream = new WriteBufferFromFile(part_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); + index_file_stream = new WriteBufferFromFile(part_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY); + index_stream = new HashingWriteBuffer(*index_file_stream); columns_list = storage.getColumnsList(); for (const auto & it : columns_list) @@ -207,23 +226,44 @@ public: size_t written_for_last_mark = (storage.index_granularity - index_offset + rows) % storage.index_granularity; index_offset = (storage.index_granularity - written_for_last_mark) % storage.index_granularity; } - - void writeSuffix() + + void writeSuffix() override { - /// Заканчиваем запись. + throw Exception("Method writeSuffix is not supported by MergedBlockOutputStream", ErrorCodes::NOT_IMPLEMENTED); + } + + MergeTreeData::DataPart::Checksums writeSuffixAndGetChecksums() + { + /// Заканчиваем запись и достаем чексуммы. + MergeTreeData::DataPart::Checksums checksums; + index_stream->next(); - index_stream = NULL; + checksums.files["primary.idx"].size = index_stream->count(); + checksums.files["primary.idx"].hash = index_stream->getHash(); for (ColumnStreams::iterator it = column_streams.begin(); it != column_streams.end(); ++it) + { it->second->finalize(); + it->second->addToChecksums(checksums); + } + index_stream = NULL; column_streams.clear(); if (marks_count == 0) { /// Кусок пустой - все записи удалились. Poco::File(part_path).remove(true); + checksums.files.clear(); } + else + { + /// Записываем файл с чексуммами. + WriteBufferFromFile out(part_path + "checksums.txt", 1024); + checksums.writeText(out); + } + + return checksums; } MergeTreeData::DataPart::Index & getIndex() @@ -243,7 +283,8 @@ private: size_t marks_count; - SharedPtr index_stream; + SharedPtr index_file_stream; + SharedPtr index_stream; MergeTreeData::DataPart::Index index_vec; }; @@ -284,19 +325,30 @@ public: index_offset = (storage.index_granularity - written_for_last_mark) % storage.index_granularity; } - void writeSuffix() + void writeSuffix() override { + throw Exception("Method writeSuffix is not supported by MergedColumnOnlyOutputStream", ErrorCodes::NOT_IMPLEMENTED); + } + + MergeTreeData::DataPart::Checksums writeSuffixAndGetChecksums() + { + MergeTreeData::DataPart::Checksums checksums; + for (auto & column_stream : column_streams) { column_stream.second->finalize(); if (sync) column_stream.second->sync(); std::string column = escapeForFileName(column_stream.first); + column_stream.second->addToChecksums(checksums, column); Poco::File(part_path + prefix + column + ".bin").renameTo(part_path + column + ".bin"); Poco::File(part_path + prefix + column + ".mrk").renameTo(part_path + column + ".mrk"); } + column_streams.clear(); initialized = false; + + return checksums; } private: diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 4199283126f..9c8fc38fc44 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -9,6 +9,7 @@ #include #include #include +#include @@ -366,26 +367,15 @@ void MergeTreeData::removeColumnFiles(String column_name) void MergeTreeData::createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column) { - ASTFunction * function = new ASTFunction; - ASTPtr function_ptr = function; + Names out_names; + out_expression = new ExpressionActions( + NamesAndTypesList(1, NameAndTypePair(in_column_name, getDataTypeByName(in_column_name))), context.getSettingsRef()); - ASTExpressionList * arguments = new ASTExpressionList; - ASTPtr arguments_ptr = arguments; + FunctionPtr function = context.getFunctionFactory().get("to" + out_type, context); + out_expression->add(ExpressionActions::Action::applyFunction(function, Names(1, in_column_name)), out_names); + out_expression->add(ExpressionActions::Action::removeColumn(in_column_name)); - function->name = "to" + out_type; - function->arguments = arguments_ptr; - function->children.push_back(arguments_ptr); - - ASTIdentifier * in_column = new ASTIdentifier; - ASTPtr in_column_ptr = in_column; - - arguments->children.push_back(in_column_ptr); - - in_column->name = in_column_name; - in_column->kind = ASTIdentifier::Column; - - out_expression = ExpressionAnalyzer(function_ptr, context, *columns).getActions(false); - out_column = function->getColumnName(); + out_column = out_names[0]; } static DataTypePtr getDataTypeByName(const String & name, const NamesAndTypesList & columns) @@ -445,18 +435,29 @@ void MergeTreeData::prepareAlterModify(const ASTAlterQuery::Parameters & params) ExpressionBlockInputStream in(new MergeTreeBlockInputStream(full_path + part->name + '/', DEFAULT_MERGE_BLOCK_SIZE, column_name, *this, part, ranges, false, NULL, ""), expr); MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true); + in.readPrefix(); out.writePrefix(); try { while(DB::Block b = in.read()) - { - /// оставляем только столбец с результатом - b.erase(0); out.write(b); + + in.readSuffix(); + DataPart::Checksums add_checksums = out.writeSuffixAndGetChecksums(); + + /// Запишем обновленные контрольные суммы во временный файл. + if (!part->checksums.empty()) + { + DataPart::Checksums new_checksums = part->checksums; + std::string escaped_name = escapeForFileName(name_type.name); + std::string escaped_out_column = escapeForFileName(out_column); + new_checksums.files[escaped_name + ".bin"] = add_checksums.files[escaped_out_column + ".bin"]; + new_checksums.files[escaped_name + ".mrk"] = add_checksums.files[escaped_out_column + ".mrk"]; + + WriteBufferFromFile checksums_file(full_path + part->name + '/' + escaped_out_column + ".checksums.txt", 1024); + new_checksums.writeText(checksums_file); } - LOG_TRACE(log, "Write Suffix"); - out.writeSuffix(); } catch (const Exception & e) { @@ -486,40 +487,63 @@ void MergeTreeData::commitAlterModify(const ASTAlterQuery::Parameters & params) /// переименовываем старые столбцы, добавляя расширение .old for (DataPartPtr & part : parts) { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + std::string part_path = full_path + part->name + '/'; + std::string path = part_path + escapeForFileName(name_type.name); if (Poco::File(path + ".bin").exists()) { LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << path + ".bin" + ".old"); Poco::File(path + ".bin").renameTo(path + ".bin" + ".old"); LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << path + ".mrk" + ".old"); Poco::File(path + ".mrk").renameTo(path + ".mrk" + ".old"); + + if (Poco::File(part_path + "checksums.txt").exists()) + { + LOG_TRACE(log, "Renaming " << part_path + "checksums.txt" << " to " << part_path + "checksums.txt" + ".old"); + Poco::File(part_path + "checksums.txt").renameTo(part_path + "checksums.txt" + ".old"); + } } } /// переименовываем временные столбцы for (DataPartPtr & part : parts) { - std::string path = full_path + part->name + '/' + escapeForFileName(out_column); - std::string new_path = full_path + part->name + '/' + escapeForFileName(name_type.name); + std::string part_path = full_path + part->name + '/'; + std::string name = escapeForFileName(out_column); + std::string new_name = escapeForFileName(name_type.name); + std::string path = part_path + name; + std::string new_path = part_path + new_name; if (Poco::File(path + ".bin").exists()) { LOG_TRACE(log, "Renaming " << path + ".bin" << " to " << new_path + ".bin"); Poco::File(path + ".bin").renameTo(new_path + ".bin"); LOG_TRACE(log, "Renaming " << path + ".mrk" << " to " << new_path + ".mrk"); Poco::File(path + ".mrk").renameTo(new_path + ".mrk"); + + if (Poco::File(path + ".checksums.txt").exists()) + { + LOG_TRACE(log, "Renaming " << path + ".checksums.txt" << " to " << part_path + ".checksums.txt"); + Poco::File(path + ".checksums.txt").renameTo(part_path + "checksums.txt"); + } } } // удаляем старые столбцы for (DataPartPtr & part : parts) { - std::string path = full_path + part->name + '/' + escapeForFileName(name_type.name); + std::string part_path = full_path + part->name + '/'; + std::string path = part_path + escapeForFileName(name_type.name); if (Poco::File(path + ".bin" + ".old").exists()) { LOG_TRACE(log, "Removing old column " << path + ".bin" + ".old"); Poco::File(path + ".bin" + ".old").remove(); LOG_TRACE(log, "Removing old column " << path + ".mrk" + ".old"); Poco::File(path + ".mrk" + ".old").remove(); + + if (Poco::File(part_path + "checksums.txt" + ".old").exists()) + { + LOG_TRACE(log, "Removing old checksums " << part_path + "checksums.txt" + ".old"); + Poco::File(part_path + "checksums.txt" + ".old").remove(); + } } } @@ -691,20 +715,20 @@ MergeTreeData::DataParts MergeTreeData::getDataParts() void MergeTreeData::DataPart::Checksums::check(const Checksums & rhs) const { - for (const auto & it : rhs.file_checksums) + for (const auto & it : rhs.files) { const String & name = it.first; - if (!file_checksums.count(name)) + if (!files.count(name)) throw Exception("Unexpected file " + name + " in data part", ErrorCodes::UNEXPECTED_FILE_IN_DATA_PART); } - for (const auto & it : file_checksums) + for (const auto & it : files) { const String & name = it.first; - auto jt = rhs.file_checksums.find(name); - if (jt == rhs.file_checksums.end()) + auto jt = rhs.files.find(name); + if (jt == rhs.files.end()) throw Exception("No file " + name + " in data part", ErrorCodes::NO_FILE_IN_DATA_PART); const Checksum & expected = it.second; @@ -720,7 +744,7 @@ void MergeTreeData::DataPart::Checksums::check(const Checksums & rhs) const void MergeTreeData::DataPart::Checksums::readText(ReadBuffer & in) { - file_checksums.clear(); + files.clear(); size_t count; DB::assertString("checksums format version: 1\n", in); @@ -741,17 +765,17 @@ void MergeTreeData::DataPart::Checksums::readText(ReadBuffer & in) DB::readText(sum.hash.second, in); DB::assertString("\n", in); - file_checksums.insert(std::make_pair(name, sum)); + files.insert(std::make_pair(name, sum)); } } void MergeTreeData::DataPart::Checksums::writeText(WriteBuffer & out) const { DB::writeString("checksums format version: 1\n", out); - DB::writeText(file_checksums.size(), out); + DB::writeText(files.size(), out); DB::writeString(" files:\n", out); - for (const auto & it : file_checksums) + for (const auto & it : files) { DB::writeString(it.first, out); DB::writeString("\n\tsize: ", out); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index f10faef52ca..9477d64c280 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -311,7 +311,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa } merged_stream->readSuffix(); - to->writeSuffix(); + new_data_part->checksums = to->writeSuffixAndGetChecksums(); new_data_part->index.swap(to->getIndex()); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index c1e6051e7bc..6d6cbbe6a12 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -106,7 +106,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa out.writePrefix(); out.write(block); - out.writeSuffix(); + MergeTreeData::DataPart::Checksums checksums = out.writeSuffixAndGetChecksums(); MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); new_data_part->left_date = DayNum_t(min_date); @@ -120,6 +120,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); new_data_part->index.swap(out.getIndex()); + new_data_part->checksums = checksums; return new_data_part; } From 26848b680333fd0fc09b148d7646228040432ec5 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 27 Mar 2014 21:58:25 +0400 Subject: [PATCH 093/281] dbms: added pretty-formatting for GLOBAL IN; pedantic modifications in comments [#METR-10071]. --- dbms/include/DB/Interpreters/ExpressionAnalyzer.h | 6 +++--- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 4 ++-- dbms/src/Interpreters/InterpreterSelectQuery.cpp | 2 +- dbms/src/Parsers/formatAST.cpp | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 41008e12c75..d84bae25f39 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -97,7 +97,7 @@ public: /// Если ast - запрос SELECT, получает имена (алиасы) и типы столбцов из секции SELECT. Block getSelectSampleBlock(); - /// Все новые временные таблицы, полученные при выполнении подзапросов Global In + /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN. std::vector external_tables; private: typedef std::set NamesSet; @@ -261,12 +261,12 @@ private: void normalizeTree(); void normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_asts, SetOfASTs & current_asts, std::string current_alias, bool in_sign_rewritten); - /// Обходит запрос и сохраняет найденные глобальные функции (например Global in) + /// Обходит запрос и сохраняет найденные глобальные функции (например GLOBAL IN) void findGlobalFunctions(ASTPtr & ast, std::vector & global_nodes); /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. void makeSet(ASTFunction * node, const Block & sample_block); - /// Выполнить подзапрос в секции global in и запомнить результат во временную таблицу типа memory + /// Выполнить подзапрос в секции GLOBAL IN и запомнить результат во временную таблицу типа memory /// Все новые временные таблицы хранятся в переменной external_tables void addExternalStorage(ASTFunction * node, size_t & name_id); diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 1b1f0774af5..a592d50a221 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -569,7 +569,7 @@ void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id bool parse_res = parser.parse(pos, end, subquery, expected); if (!parse_res) - throw Exception("Error in parsing select query while creating set for table " + table->name + ".", + throw Exception("Error in parsing SELECT query while creating set for table " + table->name + ".", ErrorCodes::LOGICAL_ERROR); } else @@ -592,7 +592,7 @@ void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id external_tables.push_back(external_storage); } else - throw Exception("Global in (notIn) supports only select data.", ErrorCodes::BAD_ARGUMENTS); + throw Exception("GLOBAL [NOT] IN supports only SELECT data.", ErrorCodes::BAD_ARGUMENTS); } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 78caf91142b..dbf8c7c2829 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -78,7 +78,7 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType query_analyzer = new ExpressionAnalyzer(query_ptr, context, storage, subquery_depth); - /// Выполняем все Global in подзапросы, результаты будут сохранены в query_analyzer->external_tables + /// Выполняем все GLOBAL IN подзапросы, результаты будут сохранены в query_analyzer->external_tables query_analyzer->processGlobalOperations(); /// Сохраняем в query context новые временные таблицы diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index dff56b9c087..8f55c9866cd 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -498,6 +498,8 @@ void formatAST(const ASTFunction & ast, std::ostream & s, size_t indent, bool "notLike", " NOT LIKE ", "in", " IN ", "notIn", " NOT IN ", + "globalIn", " GLOBAL IN ", + "globalNotIn", " GLOBAL NOT IN ", nullptr }; From eba31260772a74ce70ebde1b46e7acc7e1b86dd9 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 27 Mar 2014 23:09:23 +0400 Subject: [PATCH 094/281] dbms: style [#METR-10071]. --- dbms/include/DB/Functions/FunctionsStringSearch.h | 2 +- dbms/src/Client/Client.cpp | 2 +- dbms/src/Common/VirtualColumnUtils.cpp | 2 +- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 2 +- dbms/src/Storages/StorageLog.cpp | 2 +- dbms/src/Storages/tests/MergeLogicTester.cpp | 8 ++++---- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dbms/include/DB/Functions/FunctionsStringSearch.h b/dbms/include/DB/Functions/FunctionsStringSearch.h index 3ce80c24dc7..c4137d0494a 100644 --- a/dbms/include/DB/Functions/FunctionsStringSearch.h +++ b/dbms/include/DB/Functions/FunctionsStringSearch.h @@ -810,7 +810,7 @@ struct ReplaceStringImpl match = false; if (match) { - replace_cnt ++; + ++replace_cnt; res_data += replacement; i = i + needle.size() - 1; } else diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 7081141ad4f..c7c095ece6a 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -985,7 +985,7 @@ public: { external_tables.push_back(ExternalTable(external_options)); if (external_tables.back().file == "-") - stdin_count ++; + ++stdin_count; if (stdin_count > 1) throw Exception("Two or more external tables has stdin (-) set as --file field", ErrorCodes::BAD_ARGUMENTS); } diff --git a/dbms/src/Common/VirtualColumnUtils.cpp b/dbms/src/Common/VirtualColumnUtils.cpp index a287317a92e..e247a796ccd 100644 --- a/dbms/src/Common/VirtualColumnUtils.cpp +++ b/dbms/src/Common/VirtualColumnUtils.cpp @@ -60,7 +60,7 @@ String chooseSuffixForSet(const NamesAndTypesList & columns, const std::vector(id); } return current_suffix; diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index a592d50a221..68ace36e27e 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -1190,7 +1190,7 @@ void ExpressionAnalyzer::processGlobalOperations() { String external_table_name = "_data"; while (context.tryGetExternalTable(external_table_name + toString(id))) - id ++; + ++id; addExternalStorage(dynamic_cast(&*global_nodes[i]), id); } } diff --git a/dbms/src/Storages/StorageLog.cpp b/dbms/src/Storages/StorageLog.cpp index 24ea5ce5b2b..a8e77cb79f7 100644 --- a/dbms/src/Storages/StorageLog.cpp +++ b/dbms/src/Storages/StorageLog.cpp @@ -85,7 +85,7 @@ Block LogBlockInputStream::readImpl() if (mark_number > 0) current_row += marks[mark_number-1].rows; while (current_mark < marks.size() && marks[current_mark].rows <= current_row) - current_mark ++; + ++current_mark; current_table = storage.getTableFromMark(current_mark); current_table.second = std::min(current_table.second, marks.size() - 1); diff --git a/dbms/src/Storages/tests/MergeLogicTester.cpp b/dbms/src/Storages/tests/MergeLogicTester.cpp index e03b503c07d..b08298e3574 100644 --- a/dbms/src/Storages/tests/MergeLogicTester.cpp +++ b/dbms/src/Storages/tests/MergeLogicTester.cpp @@ -256,7 +256,7 @@ bool makeMerge(int cur_time) { mergeScheduled --; std::vector e; if (!selectPartsToMerge(e)) return 1; - int curId = uniqId ++; + int curId = ++uniqId; size_t size = 0; for (size_t i = 0; i < e.size(); ++i) { @@ -300,11 +300,11 @@ void process(pair > ev) size += (*it)->size; st = min(st, (*it)->time); DataParts::iterator nxt = it; - nxt ++; + ++nxt; data_parts.erase(it); it = nxt; } else - it ++; + ++it; } data_parts.insert(new DataPart(st, size, 0, cur_time)); } else if (type == 3) /// do merge @@ -342,7 +342,7 @@ int main() averageNumberOfParts += 1.0 * (cur_time - last_time) * data_parts.size(); if (cur_time > total_time) break; updateStat(cur_time); - iter ++; + ++iter; if (iter % 3000 == 0) { printf("Current time: %d\n", cur_time); From 852d35615c1581e8867cf73a70ae20f34c7f5664 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 23:47:51 +0400 Subject: [PATCH 095/281] dbms: Fixed a bug. [#METR-10615] --- dbms/include/DB/Parsers/ASTSelectQuery.h | 11 ++++++++++- dbms/src/Interpreters/InterpreterSelectQuery.cpp | 8 ++++---- .../0_stateless/00023_agg_select_agg_subquery.sql | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/dbms/include/DB/Parsers/ASTSelectQuery.h b/dbms/include/DB/Parsers/ASTSelectQuery.h index 9b1bc0bed99..e9e65e3d714 100644 --- a/dbms/include/DB/Parsers/ASTSelectQuery.h +++ b/dbms/include/DB/Parsers/ASTSelectQuery.h @@ -60,7 +60,16 @@ public: DB::ErrorCodes::UNKNOWN_IDENTIFIER); } - select_expression_list.swap(result); + + for (auto & child : children) + { + if (child == select_expression_list) + { + child = result; + break; + } + } + select_expression_list = result; } ASTPtr clone() const diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index dbf8c7c2829..53bdb5308d7 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -104,13 +104,13 @@ InterpreterSelectQuery::InterpreterSelectQuery(ASTPtr query_ptr_, const Context context(context_), settings(context.getSettings()), to_stage(to_stage_), subquery_depth(subquery_depth_), log(&Logger::get("InterpreterSelectQuery")) { - init(input_); - /** Оставляем в запросе в секции SELECT только нужные столбцы. * Но если используется DISTINCT, то все столбцы считаются нужными, так как иначе DISTINCT работал бы по-другому. */ if (!query.distinct) query.rewriteSelectExpressionList(required_column_names_); + + init(input_); } InterpreterSelectQuery::InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, const Names & required_column_names_, @@ -119,13 +119,13 @@ InterpreterSelectQuery::InterpreterSelectQuery(ASTPtr query_ptr_, const Context context(context_), settings(context.getSettings()), to_stage(to_stage_), subquery_depth(subquery_depth_), log(&Logger::get("InterpreterSelectQuery")) { - init(input_, table_column_names); - /** Оставляем в запросе в секции SELECT только нужные столбцы. * Но если используется DISTINCT, то все столбцы считаются нужными, так как иначе DISTINCT работал бы по-другому. */ if (!query.distinct) query.rewriteSelectExpressionList(required_column_names_); + + init(input_, table_column_names); } void InterpreterSelectQuery::getDatabaseAndTableNames(String & database_name, String & table_name) diff --git a/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql index 398b4ff8b89..bb63f3b9d71 100644 --- a/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql +++ b/dbms/tests/queries/0_stateless/00023_agg_select_agg_subquery.sql @@ -1 +1 @@ -SELECT count() FROM (SELECT sum(1), sum(2)) +SELECT count() FROM (SELECT sum(materialize(1)), sum(materialize(2))) From 784014f79a2bd69da744d6c3ed8fe7d2eabbebce Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 27 Mar 2014 23:49:52 +0400 Subject: [PATCH 096/281] dbms: fixed removing arrayJoin from subquery. [#METR-9549] --- dbms/include/DB/Parsers/ASTSelectQuery.h | 32 ++++++++++++++++++- ...24_unused_array_join_in_subquery.reference | 1 + .../00024_unused_array_join_in_subquery.sql | 1 + ..._implicitly_used_subquery_column.reference | 1 + .../00025_implicitly_used_subquery_column.sql | 1 + 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.reference create mode 100644 dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.sql create mode 100644 dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.reference create mode 100644 dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.sql diff --git a/dbms/include/DB/Parsers/ASTSelectQuery.h b/dbms/include/DB/Parsers/ASTSelectQuery.h index e9e65e3d714..b357b414e33 100644 --- a/dbms/include/DB/Parsers/ASTSelectQuery.h +++ b/dbms/include/DB/Parsers/ASTSelectQuery.h @@ -37,12 +37,36 @@ public: String getID() const { return "SelectQuery"; }; + static bool hasArrayJoin(const ASTPtr & ast) + { + if (const ASTFunction * function = dynamic_cast(&*ast)) + { + if (function->kind == ASTFunction::ARRAY_JOIN) + return true; + } + for (const auto & child : ast->children) + if (hasArrayJoin(child)) + return true; + return false; + } + /// Переписывает select_expression_list, чтобы вернуть только необходимые столбцы в правильном порядке. void rewriteSelectExpressionList(const Names & column_names) { ASTPtr result = new ASTExpressionList; ASTs asts = select_expression_list->children; + /// Не будем выбрасывать выражения, содержащие функцию arrayJoin. + std::set unremovable_asts; + for (size_t j = 0; j < asts.size(); ++j) + { + if (hasArrayJoin(asts[j])) + { + result->children.push_back(asts[j]->clone()); + unremovable_asts.insert(asts[j]); + } + } + for (size_t i = 0; i < column_names.size(); ++i) { bool done = 0; @@ -50,7 +74,8 @@ public: { if (asts[j]->getAlias() == column_names[i]) { - result->children.push_back(asts[j]->clone()); + if (!unremovable_asts.count(asts[j])) + result->children.push_back(asts[j]->clone()); done = 1; } } @@ -70,6 +95,11 @@ public: } } select_expression_list = result; + + /** NOTE: Может показаться, что мы могли испортить запрос, выбросив выражение с алиасом, который используется где-то еще. + * Такого произойти не может, потому что этот метод вызывается всегда для запроса, на котором хоть раз создавали + * ExpressionAnalyzer, что гарантирует, что в нем все алиасы уже подставлены. Не совсем очевидная логика :) + */ } ASTPtr clone() const diff --git a/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.reference b/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.reference new file mode 100644 index 00000000000..00750edc07d --- /dev/null +++ b/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.reference @@ -0,0 +1 @@ +3 diff --git a/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.sql b/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.sql new file mode 100644 index 00000000000..05ed9bdd3e7 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00024_unused_array_join_in_subquery.sql @@ -0,0 +1 @@ +SELECT count() FROM (SELECT 1, arrayJoin([1,2,3])) diff --git a/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.reference b/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.reference @@ -0,0 +1 @@ +1 diff --git a/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.sql b/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.sql new file mode 100644 index 00000000000..1954590ec94 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00025_implicitly_used_subquery_column.sql @@ -0,0 +1 @@ +SELECT y FROM (SELECT materialize(1) AS x, x AS y) From 95a3cd2e9e26846aac5e9edcc7c7c36aa1aea58d Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 28 Mar 2014 00:32:12 +0400 Subject: [PATCH 097/281] dbms: AggregateFunctionArray: fixed performance issue [#METR-10366]. --- .../AggregateFunctionArray.h | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h b/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h index 6e40bd22306..6b1f03dff60 100644 --- a/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h +++ b/dbms/include/DB/AggregateFunctions/AggregateFunctionArray.h @@ -81,18 +81,18 @@ public: void add(AggregateDataPtr place, const IColumn ** columns, size_t row_num) const { - const IColumn ** nested = new const IColumn*[num_agruments]; - std::vector column_ptrs; + const IColumn * nested[num_agruments]; + for (int i = 0; i < num_agruments; ++i) - { - ColumnPtr single_value_column = dynamic_cast(*columns[i]).cut(row_num, 1); - column_ptrs.push_back(single_value_column); - nested[i] = dynamic_cast(*single_value_column).getDataPtr().get(); - } - for (int i = 0; i < num_agruments; ++i) - if (nested[i]->size() != nested[0]->size()) - throw Exception("All arrays must be of the same size. Aggregate function " + getName(), ErrorCodes::BAD_ARGUMENTS); - for (size_t i = 0; i < nested[0]->size(); ++i) + nested[i] = &static_cast(*columns[i]).getData(); + + const ColumnArray & first_array_column = static_cast(*columns[0]); + const IColumn::Offsets_t & offsets = first_array_column.getOffsets(); + + size_t begin = row_num == 0 ? 0 : offsets[row_num - 1]; + size_t end = offsets[row_num]; + + for (size_t i = begin; i < end; ++i) nested_func->add(place, nested, i); } From 3e4238c9a544a60671ebc8361961b1a2190567e8 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 14:23:37 +0400 Subject: [PATCH 098/281] dbms: Added some test. [#METR-10616] --- .../queries/0_stateless/00026_something_distributed.reference | 2 ++ dbms/tests/queries/0_stateless/00026_something_distributed.sql | 1 + 2 files changed, 3 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00026_something_distributed.reference create mode 100644 dbms/tests/queries/0_stateless/00026_something_distributed.sql diff --git a/dbms/tests/queries/0_stateless/00026_something_distributed.reference b/dbms/tests/queries/0_stateless/00026_something_distributed.reference new file mode 100644 index 00000000000..6ed281c757a --- /dev/null +++ b/dbms/tests/queries/0_stateless/00026_something_distributed.reference @@ -0,0 +1,2 @@ +1 +1 diff --git a/dbms/tests/queries/0_stateless/00026_something_distributed.sql b/dbms/tests/queries/0_stateless/00026_something_distributed.sql new file mode 100644 index 00000000000..0ab06a36497 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00026_something_distributed.sql @@ -0,0 +1 @@ +SELECT NOT dummy FROM remote('127.0.0.{1,2}', system, one) WHERE NOT dummy From 25f5d03f77ac88b701c9e3b2c9b04dac6faf7802 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 16:13:58 +0400 Subject: [PATCH 099/281] dbms: (Warning: this commit makes clickhouse distributed queries incompatible with previous versions) Improved distributed query processing: more stages and optimizations are now made on remote side. Significantly reduced spaghettiness of InterpreterSelectQuery. [#METR-10616] --- .../DB/Interpreters/ExpressionAnalyzer.h | 26 ++-- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 72 ++++----- .../Interpreters/InterpreterSelectQuery.cpp | 144 ++++++++---------- 3 files changed, 101 insertions(+), 141 deletions(-) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index d84bae25f39..6b22ed29808 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -53,7 +53,6 @@ public: Names getRequiredColumns(); /** Эти методы позволяют собрать цепочку преобразований над блоком, получающую значения в нужных секциях запроса. - * Выполняют подзапросы в соответствующих частях запроса. * * Пример использования: * ExpressionActionsChain chain; @@ -62,22 +61,25 @@ public: * analyzer.appendSelect(chain); * analyzer.appendOrderBy(chain); * chain.finalize(); + * + * Если указано only_types=true, не выполняет подзапросы в соответствующих частях запроса. Полученные таким + * образом действия не следует выполнять, они нужны только чтобы получить список столбцов с их типами. */ void processGlobalOperations(); /// До агрегации: - bool appendArrayJoin(ExpressionActionsChain & chain); - bool appendWhere(ExpressionActionsChain & chain); - bool appendGroupBy(ExpressionActionsChain & chain); - void appendAggregateFunctionsArguments(ExpressionActionsChain & chain); + bool appendArrayJoin(ExpressionActionsChain & chain, bool only_types); + bool appendWhere(ExpressionActionsChain & chain, bool only_types); + bool appendGroupBy(ExpressionActionsChain & chain, bool only_types); + void appendAggregateFunctionsArguments(ExpressionActionsChain & chain, bool only_types); /// После агрегации: - bool appendHaving(ExpressionActionsChain & chain); - void appendSelect(ExpressionActionsChain & chain); - bool appendOrderBy(ExpressionActionsChain & chain); + bool appendHaving(ExpressionActionsChain & chain, bool only_types); + void appendSelect(ExpressionActionsChain & chain, bool only_types); + bool appendOrderBy(ExpressionActionsChain & chain, bool only_types); /// Удаляет все столбцы кроме выбираемых SELECT, упорядочивает оставшиеся столбцы и переименовывает их в алиасы. - void appendProjectResult(ExpressionActionsChain & chain); + void appendProjectResult(ExpressionActionsChain & chain, bool only_types); /// Если ast не запрос SELECT, просто получает все действия для вычисления выражения. /// Если project_result, в выходном блоке останутся только вычисленные значения в нужном порядке, переименованные в алиасы. @@ -113,9 +115,7 @@ private: /// Исходные столбцы. NamesAndTypesList columns; - /// Столбцы после ARRAY JOIN. Если нет ARRAY JOIN, совпадает с columns. - NamesAndTypesList columns_after_array_join; - /// Столбцы после агрегации. Если нет агрегации, совпадает с columns_after_array_join. + /// Столбцы после ARRAY JOIN и/или агрегации. NamesAndTypesList aggregated_columns; /// Таблица, из которой делается запрос. Используется для sign-rewrite'а @@ -278,7 +278,7 @@ private: void getRootActionsImpl(ASTPtr ast, bool no_subqueries, bool only_consts, ExpressionActions & actions); - void getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionActions & actions); + void getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionActions & actions, bool no_subqueries); /// Добавить агрегатные функции в aggregate_descriptions. /// Установить has_aggregation=true, если есть хоть одна агрегатная функция. diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 68ace36e27e..5c32f4053a4 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -95,26 +95,10 @@ void ExpressionAnalyzer::init() if (select_query && select_query->array_join_expression_list) { - const ASTs & array_join_asts = select_query->array_join_expression_list->children; - for (size_t i = 0; i < array_join_asts.size(); ++i) - { - ASTPtr ast = array_join_asts[i]; - getRootActionsImpl(ast, true, false, temp_actions); - } - + getRootActionsImpl(select_query->array_join_expression_list, true, false, temp_actions); addMultipleArrayJoinAction(temp_actions); + } - const Block & temp_sample = temp_actions.getSampleBlock(); - for (NameToNameMap::iterator it = array_join_result_to_source.begin(); it != array_join_result_to_source.end(); ++it) - { - columns_after_array_join.push_back(NameAndTypePair(it->first, temp_sample.getByName(it->first).type)); - } - } - for (NamesAndTypesList::iterator it = columns.begin(); it != columns.end(); ++it) - { - if (!array_join_result_to_source.count(it->first)) - columns_after_array_join.push_back(*it); - } getAggregatesImpl(ast, temp_actions); if (has_aggregation) @@ -150,7 +134,7 @@ void ExpressionAnalyzer::init() } else { - aggregated_columns = columns_after_array_join; + aggregated_columns = temp_actions.getSampleBlock().getColumnsList(); } } @@ -847,11 +831,11 @@ void ExpressionAnalyzer::getActionsImpl(ASTPtr ast, bool no_subqueries, bool onl if (!only_consts && !actions_stack.getSampleBlock().has(name)) { /// Запрошенного столбца нет в блоке. - /// Если такой столбец есть до агрегации, значит пользователь наверно забыл окружить его агрегатной функцией или добавить в GROUP BY. + /// Если такой столбец есть в таблице, значит пользователь наверно забыл окружить его агрегатной функцией или добавить в GROUP BY. bool found = false; - for (NamesAndTypesList::const_iterator it = columns_after_array_join.begin(); - it != columns_after_array_join.end(); ++it) + for (NamesAndTypesList::const_iterator it = columns.begin(); + it != columns.end(); ++it) if (it->first == name) found = true; @@ -1195,7 +1179,7 @@ void ExpressionAnalyzer::processGlobalOperations() } } -bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain) +bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain, bool only_types) { assertSelect(); @@ -1205,7 +1189,7 @@ bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain) initChain(chain, columns); ExpressionActionsChain::Step & step = chain.steps.back(); - getRootActionsImpl(select_query->array_join_expression_list, false, false, *step.actions); + getRootActionsImpl(select_query->array_join_expression_list, only_types, false, *step.actions); addMultipleArrayJoinAction(*step.actions); @@ -1215,23 +1199,23 @@ bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain) return true; } -bool ExpressionAnalyzer::appendWhere(ExpressionActionsChain & chain) +bool ExpressionAnalyzer::appendWhere(ExpressionActionsChain & chain, bool only_types) { assertSelect(); if (!select_query->where_expression) return false; - initChain(chain, columns_after_array_join); + initChain(chain, columns); ExpressionActionsChain::Step & step = chain.steps.back(); step.required_output.push_back(select_query->where_expression->getColumnName()); - getRootActionsImpl(select_query->where_expression, false, false, *step.actions); + getRootActionsImpl(select_query->where_expression, only_types, false, *step.actions); return true; } -bool ExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain) +bool ExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain, bool only_types) { assertAggregation(); @@ -1245,17 +1229,17 @@ bool ExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain) for (size_t i = 0; i < asts.size(); ++i) { step.required_output.push_back(asts[i]->getColumnName()); - getRootActionsImpl(asts[i], false, false, *step.actions); + getRootActionsImpl(asts[i], only_types, false, *step.actions); } return true; } -void ExpressionAnalyzer::appendAggregateFunctionsArguments(ExpressionActionsChain & chain) +void ExpressionAnalyzer::appendAggregateFunctionsArguments(ExpressionActionsChain & chain, bool only_types) { assertAggregation(); - initChain(chain, columns_after_array_join); + initChain(chain, columns); ExpressionActionsChain::Step & step = chain.steps.back(); for (size_t i = 0; i < aggregate_descriptions.size(); ++i) @@ -1266,16 +1250,16 @@ void ExpressionAnalyzer::appendAggregateFunctionsArguments(ExpressionActionsChai } } - getActionsBeforeAggregationImpl(select_query->select_expression_list, *step.actions); + getActionsBeforeAggregationImpl(select_query->select_expression_list, *step.actions, only_types); if (select_query->having_expression) - getActionsBeforeAggregationImpl(select_query->having_expression, *step.actions); + getActionsBeforeAggregationImpl(select_query->having_expression, *step.actions, only_types); if (select_query->order_expression_list) - getActionsBeforeAggregationImpl(select_query->order_expression_list, *step.actions); + getActionsBeforeAggregationImpl(select_query->order_expression_list, *step.actions, only_types); } -bool ExpressionAnalyzer::appendHaving(ExpressionActionsChain & chain) +bool ExpressionAnalyzer::appendHaving(ExpressionActionsChain & chain, bool only_types) { assertAggregation(); @@ -1286,19 +1270,19 @@ bool ExpressionAnalyzer::appendHaving(ExpressionActionsChain & chain) ExpressionActionsChain::Step & step = chain.steps.back(); step.required_output.push_back(select_query->having_expression->getColumnName()); - getRootActionsImpl(select_query->having_expression, false, false, *step.actions); + getRootActionsImpl(select_query->having_expression, only_types, false, *step.actions); return true; } -void ExpressionAnalyzer::appendSelect(ExpressionActionsChain & chain) +void ExpressionAnalyzer::appendSelect(ExpressionActionsChain & chain, bool only_types) { assertSelect(); initChain(chain, aggregated_columns); ExpressionActionsChain::Step & step = chain.steps.back(); - getRootActionsImpl(select_query->select_expression_list, false, false, *step.actions); + getRootActionsImpl(select_query->select_expression_list, only_types, false, *step.actions); ASTs asts = select_query->select_expression_list->children; for (size_t i = 0; i < asts.size(); ++i) @@ -1307,7 +1291,7 @@ void ExpressionAnalyzer::appendSelect(ExpressionActionsChain & chain) } } -bool ExpressionAnalyzer::appendOrderBy(ExpressionActionsChain & chain) +bool ExpressionAnalyzer::appendOrderBy(ExpressionActionsChain & chain, bool only_types) { assertSelect(); @@ -1317,7 +1301,7 @@ bool ExpressionAnalyzer::appendOrderBy(ExpressionActionsChain & chain) initChain(chain, aggregated_columns); ExpressionActionsChain::Step & step = chain.steps.back(); - getRootActionsImpl(select_query->order_expression_list, false, false, *step.actions); + getRootActionsImpl(select_query->order_expression_list, only_types, false, *step.actions); ASTs asts = select_query->order_expression_list->children; for (size_t i = 0; i < asts.size(); ++i) @@ -1332,7 +1316,7 @@ bool ExpressionAnalyzer::appendOrderBy(ExpressionActionsChain & chain) return true; } -void ExpressionAnalyzer::appendProjectResult(DB::ExpressionActionsChain & chain) +void ExpressionAnalyzer::appendProjectResult(DB::ExpressionActionsChain & chain, bool only_types) { assertSelect(); @@ -1380,7 +1364,7 @@ Block ExpressionAnalyzer::getSelectSampleBlock() return temp_actions.getSampleBlock(); } -void ExpressionAnalyzer::getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionActions & actions) +void ExpressionAnalyzer::getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionActions & actions, bool no_subqueries) { ASTFunction * node = dynamic_cast(&*ast); if (node && node->kind == ASTFunction::AGGREGATE_FUNCTION) @@ -1389,14 +1373,14 @@ void ExpressionAnalyzer::getActionsBeforeAggregationImpl(ASTPtr ast, ExpressionA for (size_t i = 0; i < arguments.size(); ++i) { - getRootActionsImpl(arguments[i], false, false, actions); + getRootActionsImpl(arguments[i], no_subqueries, false, actions); } } else { for (size_t i = 0; i < ast->children.size(); ++i) { - getActionsBeforeAggregationImpl(ast->children[i], actions); + getActionsBeforeAggregationImpl(ast->children[i], actions, no_subqueries); } } } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 53bdb5308d7..e79a3f0bca5 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -214,7 +214,7 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() bool has_where = false; bool need_aggregate = false; bool has_having = false; - bool has_order_by = !query.order_expression_list.isNull(); + bool has_order_by = false; ExpressionActionsPtr array_join; ExpressionActionsPtr before_where; @@ -223,78 +223,64 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() ExpressionActionsPtr before_order_and_select; ExpressionActionsPtr final_projection; - /// Сначала составим цепочку действий и запомним нужные шаги из нее. - - ExpressionActionsChain chain; - - need_aggregate = query_analyzer->hasAggregation(); - - if (from_stage < QueryProcessingStage::WithMergeableState - && to_stage >= QueryProcessingStage::WithMergeableState) - { - if (query_analyzer->appendArrayJoin(chain)) - array_join = chain.getLastActions(); + /// Нужно ли выполнять первую часть конвейера - выполняемую на удаленных серверах при распределенной обработке. + bool first_stage = from_stage < QueryProcessingStage::WithMergeableState + && to_stage >= QueryProcessingStage::WithMergeableState; + /// Нужно ли выполнять вторую часть конвейера - выполняемую на сервере-инициаторе при распределенной обработке. + bool second_stage = from_stage <= QueryProcessingStage::WithMergeableState + && to_stage > QueryProcessingStage::WithMergeableState; - if (query_analyzer->appendWhere(chain)) - { - has_where = true; - before_where = chain.getLastActions(); - - /// Если кроме WHERE ничего выполнять не нужно, пометим все исходные столбцы как нужные, чтобы finalize их не выбросил. - if (!need_aggregate && to_stage == QueryProcessingStage::WithMergeableState) - { - Names columns = query_analyzer->getRequiredColumns(); - chain.getLastStep().required_output.insert(chain.getLastStep().required_output.end(), - columns.begin(), columns.end()); - - chain.finalize(); - } - else - { - chain.addStep(); - } - } - - if (need_aggregate) - { - query_analyzer->appendGroupBy(chain); - query_analyzer->appendAggregateFunctionsArguments(chain); - before_aggregation = chain.getLastActions(); - - chain.finalize(); - - chain.clear(); - } - } - - if (from_stage <= QueryProcessingStage::WithMergeableState - && to_stage > QueryProcessingStage::WithMergeableState) + /** Сначала составим цепочку действий и запомним нужные шаги из нее. + * Независимо от from_stage и to_stage составим полную последовательность действий, чтобы выполнять оптимизации и + * выбрасывать ненужные столбцы с учетом всего запроса. В ненужных частях запроса не будем выполнять подзапросы. + */ + + ExpressionActionsChain chain; + + need_aggregate = query_analyzer->hasAggregation(); + + if (query_analyzer->appendArrayJoin(chain, !first_stage)) + array_join = chain.getLastActions(); + + if (query_analyzer->appendWhere(chain, !first_stage)) { - if (need_aggregate && query_analyzer->appendHaving(chain)) + has_where = true; + before_where = chain.getLastActions(); + chain.addStep(); + } + + if (need_aggregate) + { + query_analyzer->appendGroupBy(chain, !first_stage); + query_analyzer->appendAggregateFunctionsArguments(chain, !first_stage); + before_aggregation = chain.getLastActions(); + + chain.finalize(); + chain.clear(); + + if (query_analyzer->appendHaving(chain, !second_stage)) { has_having = true; before_having = chain.getLastActions(); chain.addStep(); } - - query_analyzer->appendSelect(chain); - query_analyzer->appendOrderBy(chain); - before_order_and_select = chain.getLastActions(); - chain.addStep(); - - query_analyzer->appendProjectResult(chain); - final_projection = chain.getLastActions(); - chain.finalize(); - - /// Если предыдущая стадия запроса выполнялась отдельно, нам могли дать лишних столбцов (например, используемых только в секции WHERE). - /// Уберем их. Они могут существенно мешать, например, при arrayJoin. - if (from_stage == QueryProcessingStage::WithMergeableState) - before_order_and_select->prependProjectInput(); - - /// Перед выполнением HAVING уберем из блока лишние столбцы (в основном, ключи агрегации). - if (has_having) - before_having->prependProjectInput(); } + + /// Если есть агрегация, выполняем выражения в SELECT и ORDER BY на инициировавшем сервере, иначе - на серверах-источниках. + query_analyzer->appendSelect(chain, need_aggregate ? !second_stage : !first_stage); + has_order_by = query_analyzer->appendOrderBy(chain, need_aggregate ? !second_stage : !first_stage); + before_order_and_select = chain.getLastActions(); + chain.addStep(); + + query_analyzer->appendProjectResult(chain, !second_stage); + final_projection = chain.getLastActions(); + + chain.finalize(); + chain.clear(); + + /// Перед выполнением HAVING уберем из блока лишние столбцы (в основном, ключи агрегации). + if (has_having) + before_having->prependProjectInput(); /// Теперь составим потоки блоков, выполняющие нужные действия. @@ -311,30 +297,21 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() to_stage > QueryProcessingStage::WithMergeableState && !query.group_by_with_totals; - if (from_stage < QueryProcessingStage::WithMergeableState - && to_stage >= QueryProcessingStage::WithMergeableState) + if (first_stage) { if (has_where) executeWhere(streams, before_where); - + if (need_aggregate) executeAggregation(streams, before_aggregation, aggregate_overflow_row, aggregate_final); - - if (array_join && !has_where && !need_aggregate && to_stage == QueryProcessingStage::WithMergeableState) - { - /** Если есть ARRAY JOIN, его действие сначала старается оказаться в - * before_where, before_aggregation или before_order_and_select. - * Если ни одного из них нет, array_join нужно выполнить отдельно. - */ - - executeExpression(streams, array_join); - } + else + executeExpression(streams, before_order_and_select); /** Оптимизация - при распределённой обработке запроса, * если не указаны DISTINCT, GROUP, HAVING, ORDER, но указан LIMIT, * то выполним предварительный LIMIT на удалёном сервере. */ - if (to_stage == QueryProcessingStage::WithMergeableState + if (!second_stage && !query.distinct && !need_aggregate && !has_having && !has_order_by && query.limit_length) { @@ -342,22 +319,21 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() } } - if (from_stage <= QueryProcessingStage::WithMergeableState - && to_stage > QueryProcessingStage::WithMergeableState) + if (second_stage) { if (need_aggregate) { /// Если нужно объединить агрегированные результаты с нескольких серверов - if (from_stage == QueryProcessingStage::WithMergeableState) + if (!first_stage) executeMergeAggregated(streams, aggregate_overflow_row, aggregate_final); if (!aggregate_final) executeTotalsAndHaving(streams, has_having, before_having, aggregate_overflow_row); else if (has_having) executeHaving(streams, before_having); - } - executeExpression(streams, before_order_and_select); + executeExpression(streams, before_order_and_select); + } if (has_order_by) executeOrder(streams); From 9d3da474285db759cbb4c062bb9d49a3b7242735 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 15:50:54 +0400 Subject: [PATCH 100/281] protocol: commets on sending external tables [METR-10071] --- dbms/include/DB/Core/Protocol.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dbms/include/DB/Core/Protocol.h b/dbms/include/DB/Core/Protocol.h index 7dadb17f953..61eaee02d4d 100644 --- a/dbms/include/DB/Core/Protocol.h +++ b/dbms/include/DB/Core/Protocol.h @@ -17,6 +17,11 @@ namespace DB * * 1. Клиент отправляет на сервер пакет Query. * + * Начиная с версии 50263, сразу после отправки пакета Query клиент начинает передачу + * внешних (временных) таблиц (external storages) - один или несколько пакетов Data. + * Конец передачи данных определается по отправленному пустому блоку. + * В данный момент, не пустое множество таблиц может быть передано только вместе с запросом SELECT. + * * Если запрос типа INSERT (требует передачи данных от клиента), то сервер передаёт * пакет Data, содержащий пустой блок, который описывает структуру таблицы. * Затем клиент отправляет данные для вставки From 535fadf131741873dbcee57d4a23234f54751efa Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 15:53:13 +0400 Subject: [PATCH 101/281] dbms: fixed checking aggregate function names with "Array" suffix [METR-10366] --- dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp index 9efa8b65fad..c037a48a252 100644 --- a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp +++ b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp @@ -321,7 +321,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da AggregateFunctionPtr nested = get(String(name.data(), name.size() - 2), nested_dt, recursion_level + 1); return new AggregateFunctionIf(nested); } - else if (recursion_level <= 1 && name.size() >= 6 && name.substr(name.size()-5) == "Array") + else if (recursion_level <= 1 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array"))) { /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. size_t num_agruments = argument_types.size(); @@ -334,7 +334,7 @@ AggregateFunctionPtr AggregateFunctionFactory::get(const String & name, const Da else throw Exception("Illegal type " + argument_types[i]->getName() + " of argument #" + toString(i + 1) + " for aggregate function " + name + ". Must be array.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } - AggregateFunctionPtr nested = get(String(name.data(), name.size() - 5), nested_arguments, recursion_level + 1); + AggregateFunctionPtr nested = get(String(name.data(), name.size() - strlen("Array")), nested_arguments, recursion_level + 1); return new AggregateFunctionArray(nested); } else @@ -387,8 +387,8 @@ bool AggregateFunctionFactory::isAggregateFunctionName(const String & name, int if (recursion_level == 0 && name.size() >= 3 && name[name.size() - 2] == 'I' && name[name.size() - 1] == 'f') return isAggregateFunctionName(String(name.data(), name.size() - 2), 1); /// Для агрегатных функций вида aggArray, где agg - имя другой агрегатной функции. - if (recursion_level <= 1 && name.size() >= 6 && name.substr(name.size()-5) == "Array") - return isAggregateFunctionName(String(name.data(), name.size() - 5), 1); + if (recursion_level <= 1 && name.size() > strlen("Array") && !(strcmp(name.data() + name.size() - strlen("Array"), "Array"))) + return isAggregateFunctionName(String(name.data(), name.size() - strlen("Array")), 1); return false; } From a6c110d03c41b3e1112c561d7e1d1a8f4165e5de Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 15:57:03 +0400 Subject: [PATCH 102/281] dbms: fixed comments on clearing block input stream after receiving external tables [METR-10071] --- dbms/src/Server/TCPHandler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 06e4c2d910d..6a459019044 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -118,9 +118,9 @@ void TCPHandler::runImpl() if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) readData(global_settings); - /// Пересоздаем, поскольку получая данные внешних таблиц, мы получили пустой блок. - /// Из-за этого весь stream помечен как cancelled - state.block_in = BlockInputStreamPtr(); + /// Очищаем, так как, получая данные внешних таблиц, мы получили пустой блок. + /// А значит, stream помечен как cancelled и читать из него нельзя. + state.block_in = NULL; /// Обрабатываем Query state.io = executeQuery(state.query, query_context, false, state.stage); From 43b2fe717f8e4b4b3dab3791e7b03eec28706040 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 16:43:41 +0400 Subject: [PATCH 103/281] clickhouse-client: fixed inserting data from stdin query. [#METR-2807] --- dbms/src/Client/Client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index c7c095ece6a..0198b2eaedb 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -588,7 +588,7 @@ private: ? query.substr(0, parsed_insert_query.data - query.data()) : query; - if ((is_interactive && !parsed_insert_query.data) || (stdin_is_not_tty && std_in.eof())) + if (!parsed_insert_query.data && (is_interactive || (stdin_is_not_tty && std_in.eof()))) throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete, NULL, true); From fd6cf4974d54c36ea0032c730076db0c40010610 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 17:45:24 +0400 Subject: [PATCH 104/281] clickhouse-client: multiquery support. [#METR-10639] --- dbms/src/Client/Client.cpp | 81 +++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 0198b2eaedb..1a723213ab4 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -448,7 +448,7 @@ private: query += line; - if (!ends_with_backslash && (ends_with_semicolon || !config().hasOption("multiline"))) + if (!ends_with_backslash && (ends_with_semicolon || !config().has("multiline"))) { if (query != prev_query) { @@ -490,8 +490,9 @@ private: void nonInteractive() { + String line; if (config().has("query")) - process(config().getString("query")); + line = config().getString("query"); else { /** В случае, если параметр query не задан, то запрос будет читаться из stdin. @@ -499,20 +500,52 @@ private: * Поддерживается только один запрос в stdin. */ - String stdin_str; + ReadBufferFromFileDescriptor in(STDIN_FILENO); + WriteBufferFromString out(line); + copyData(in, out); + } + if (config().has("multiquery")) + { + /// Несколько запросов, разделенных ';'. + /// Данные для INSERT заканчиваются переводом строки, а не ';'. + + String query; + + const char * begin = line.data(); + const char * end = begin + line.size(); + + while (begin < end) { - ReadBufferFromFileDescriptor in(STDIN_FILENO); - WriteBufferFromString out(stdin_str); - copyData(in, out); - } + const char * pos = begin; + ASTPtr ast = parseQuery(pos, end); + ASTInsertQuery * insert = dynamic_cast(&*ast); - process(stdin_str); + if (insert && insert->data) + { + pos = insert->data; + while (*pos && *pos != '\n') + ++pos; + insert->end = pos; + } + + query = line.substr(begin - line.data(), pos - begin); + + begin = pos; + while (isWhitespace(*begin) || *begin == ';') + ++begin; + + process(query, ast); + } + } + else + { + process(line); } } - bool process(const String & line) + bool process(const String & line, ASTPtr parsed_query_ = nullptr) { if (exit_strings.end() != exit_strings.find(line)) return false; @@ -524,7 +557,15 @@ private: query = line; /// Некоторые части запроса выполняются на стороне клиента (форматирование результата). Поэтому, распарсим запрос. - if (!parseQuery()) + parsed_query = parsed_query_; + + if (!parsed_query) + { + const char * begin = query.data(); + parsed_query = parseQuery(begin, begin + query.size()); + } + + if (!parsed_query) return true; processed_rows = 0; @@ -602,16 +643,15 @@ private: } - bool parseQuery() + ASTPtr parseQuery(const char *& pos, const char * end) { ParserQuery parser; const char * expected = ""; - const char * begin = query.data(); - const char * end = begin + query.size(); - const char * pos = begin; + const char * begin = pos; - bool parse_res = parser.parse(pos, end, parsed_query, expected); + ASTPtr res; + bool parse_res = parser.parse(pos, end, res, expected); /// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой. if (!parse_res || (pos != end && *pos != ';')) @@ -629,17 +669,17 @@ private: else throw Exception(message.str(), ErrorCodes::SYNTAX_ERROR); - return false; + return nullptr; } if (is_interactive) { std::cout << std::endl; - formatAST(*parsed_query, std::cout); + formatAST(*res, std::cout); std::cout << std::endl << std::endl; } - return true; + return res; } @@ -950,7 +990,8 @@ public: ("password,p", boost::program_options::value (), "password") ("query,q", boost::program_options::value (), "query") ("database,d", boost::program_options::value (), "database") - ("multiline,m", "multiline") + ("multiline,m", "multiline") + ("multiquery,n", "multiquery") ; /// Перечисляем опции командной строки относящиеся к внешним таблицам @@ -1017,6 +1058,8 @@ public: if (options.count("multiline")) config().setBool("multiline", true); + if (options.count("multiquery")) + config().setBool("multiquery", true); } }; From 6395e841f9155e66c4aff1d49020f71108247d50 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 18:36:24 +0400 Subject: [PATCH 105/281] Merge --- dbms/include/DB/Core/Defines.h | 2 ++ dbms/include/DB/IO/UncompressedCache.h | 16 ++++++++++---- dbms/include/DB/Interpreters/Context.h | 2 +- dbms/include/DB/Interpreters/Settings.h | 4 ++++ .../MergeTree/MergedBlockOutputStream.h | 22 ++++++++++++++----- dbms/include/DB/Storages/StorageLog.h | 10 +++++---- dbms/include/DB/Storages/StorageTinyLog.h | 10 +++++---- dbms/src/Interpreters/Context.cpp | 4 ++-- dbms/src/Storages/StorageChunks.cpp | 2 +- dbms/src/Storages/StorageFactory.cpp | 4 ++-- dbms/src/Storages/StorageLog.cpp | 14 ++++++------ dbms/src/Storages/StorageTinyLog.cpp | 14 ++++++------ 12 files changed, 66 insertions(+), 38 deletions(-) diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index 025bb85e44c..e492db12a58 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -13,6 +13,8 @@ #define DBMS_DEFAULT_RECEIVE_TIMEOUT_SEC 300 #define DBMS_DEFAULT_POLL_INTERVAL 10 +#define DEFAULT_MIN_COMPRESS_BLOCK_SIZE 65536 +#define DEFAULT_MAX_COMPRESS_BLOCK_SIZE 1048576 /// Какими блоками по-умолчанию читаются и пишутся данные (в числе строк). #define DEFAULT_BLOCK_SIZE 1048576 /// То же самое, но для операций слияния. Меньше DEFAULT_BLOCK_SIZE для экономии оперативки (так как читаются все столбцы). diff --git a/dbms/include/DB/IO/UncompressedCache.h b/dbms/include/DB/IO/UncompressedCache.h index aebeaa9bdf8..4463d2726cb 100644 --- a/dbms/include/DB/IO/UncompressedCache.h +++ b/dbms/include/DB/IO/UncompressedCache.h @@ -17,17 +17,25 @@ struct UncompressedCacheCell size_t compressed_size; }; +struct UncompressedSizeWeightFunction +{ + size_t operator()(const UncompressedCacheCell & x) const + { + return x.data.size(); + } +}; + /** Кэш разжатых блоков для CachedCompressedReadBuffer. thread-safe. */ -class UncompressedCache : public LRUCache +class UncompressedCache : public LRUCache { private: - typedef LRUCache Base; + typedef LRUCache Base; public: - UncompressedCache(size_t max_size_in_cells) - : Base(max_size_in_cells) {} + UncompressedCache(size_t max_size_in_bytes) + : Base(max_size_in_bytes) {} /// Посчитать ключ от пути к файлу и смещения. static UInt128 hash(const String & path_to_file, size_t offset) diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 0bf42ab6f3b..a57839ad699 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -288,7 +288,7 @@ public: const ProcessList & getProcessList() const { return shared->process_list; } /// Создать кэш разжатых блоков указанного размера. Это можно сделать только один раз. - void setUncompressedCache(size_t cache_size_in_cells); + void setUncompressedCache(size_t max_size_in_bytes); UncompressedCachePtr getUncompressedCache() const; /// Создать кэш засечек указанного размера. Это можно сделать только один раз. diff --git a/dbms/include/DB/Interpreters/Settings.h b/dbms/include/DB/Interpreters/Settings.h index 63230726cd2..e7a57de3ec1 100644 --- a/dbms/include/DB/Interpreters/Settings.h +++ b/dbms/include/DB/Interpreters/Settings.h @@ -24,6 +24,10 @@ struct Settings */ #define APPLY_FOR_SETTINGS(M) \ + /** Минимальный размер блока, готового для сжатия */ \ + M(SettingUInt64, min_compress_block_size, DEFAULT_MIN_COMPRESS_BLOCK_SIZE) \ + /** Максимальный размер блока, пригодного для сжатия */ \ + M(SettingUInt64, max_compress_block_size, DEFAULT_MAX_COMPRESS_BLOCK_SIZE) \ /** Максимальный размер блока для чтения */ \ M(SettingUInt64, max_block_size, DEFAULT_BLOCK_SIZE) \ /** Максимальное количество потоков выполнения запроса */ \ diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index 50756c2c2c5..fac3b5166e2 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -23,9 +23,9 @@ protected: typedef std::set OffsetColumns; struct ColumnStream { - ColumnStream(const String & escaped_column_name_, const String & data_path, const std::string & marks_path) : + ColumnStream(const String & escaped_column_name_, const String & data_path, const std::string & marks_path, size_t max_compress_block_size = DBMS_DEFAULT_BUFFER_SIZE) : escaped_column_name(escaped_column_name_), - plain_file(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_TRUNC | O_CREAT | O_WRONLY), + plain_file(data_path, max_compress_block_size, O_TRUNC | O_CREAT | O_WRONLY), compressed_buf(plain_file), marks_file(marks_path, 4096, O_TRUNC | O_CREAT | O_WRONLY), compressed(compressed_buf), marks(marks_file) {} @@ -82,7 +82,8 @@ protected: column_streams[size_name] = new ColumnStream( escaped_size_name, path + escaped_size_name + ".bin", - path + escaped_size_name + ".mrk"); + path + escaped_size_name + ".mrk", + storage.context.getSettings().max_compress_block_size); addStream(path, name, *type_arr->getNestedType(), level + 1); } @@ -90,7 +91,8 @@ protected: column_streams[name] = new ColumnStream( escaped_column_name, path + escaped_column_name + ".bin", - path + escaped_column_name + ".mrk"); + path + escaped_column_name + ".mrk", + storage.context.getSettings().max_compress_block_size); } @@ -129,7 +131,11 @@ protected: } type_arr->serializeOffsets(column, stream.compressed, prev_mark, limit); - stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. + /// Уже могло накопиться достаточно данных для сжатия в новый блок. + if (stream.compressed.offset() > storage.context.getSettings().min_compress_block_size) + stream.compressed.next(); + else + stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. prev_mark += limit; } } @@ -156,7 +162,11 @@ protected: } type.serializeBinary(column, stream.compressed, prev_mark, limit); - stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. + /// Уже могло накопиться достаточно данных для сжатия в новый блок. + if (stream.compressed.offset() > storage.context.getSettings().min_compress_block_size) + stream.compressed.next(); + else + stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. prev_mark += limit; } } diff --git a/dbms/include/DB/Storages/StorageLog.h b/dbms/include/DB/Storages/StorageLog.h index 0c74c712a92..c0520ff4cc6 100644 --- a/dbms/include/DB/Storages/StorageLog.h +++ b/dbms/include/DB/Storages/StorageLog.h @@ -88,8 +88,8 @@ private: struct Stream { - Stream(const std::string & data_path) : - plain(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY), + Stream(const std::string & data_path, size_t max_compress_block_size) : + plain(data_path, max_compress_block_size, O_APPEND | O_CREAT | O_WRONLY), compressed(plain) { plain_offset = Poco::File(data_path).getSize(); @@ -136,7 +136,7 @@ public: * (корректность имён и путей не проверяется) * состоящую из указанных столбцов; создать файлы, если их нет. */ - static StoragePtr create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_); + static StoragePtr create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, size_t max_compress_block_size_ = DEFAULT_MAX_COMPRESS_BLOCK_SIZE); std::string getName() const { return "Log"; } std::string getTableName() const { return name; } @@ -174,7 +174,7 @@ protected: throw Exception("There is no column " + _table_column_name + " in table " + getTableName(), ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); } - StorageLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_); + StorageLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, size_t max_compress_block_size_); /// Прочитать файлы с засечками, если они ещё не прочитаны. /// Делается лениво, чтобы при большом количестве таблиц, сервер быстро стартовал. @@ -216,6 +216,8 @@ private: bool loaded_marks; + size_t max_compress_block_size; + /** Для обычных столбцов, в засечках указано количество строчек в блоке. * Для столбцов-массивов и вложенных структур, есть более одной группы засечек, соответствующих разным файлам: * - для внутренностей (файла name.bin) - указано суммарное количество элементов массивов в блоке, diff --git a/dbms/include/DB/Storages/StorageTinyLog.h b/dbms/include/DB/Storages/StorageTinyLog.h index 682efb70215..8c6ed56d37b 100644 --- a/dbms/include/DB/Storages/StorageTinyLog.h +++ b/dbms/include/DB/Storages/StorageTinyLog.h @@ -67,8 +67,8 @@ private: struct Stream { - Stream(const std::string & data_path) : - plain(data_path, DBMS_DEFAULT_BUFFER_SIZE, O_APPEND | O_CREAT | O_WRONLY), + Stream(const std::string & data_path, size_t max_compress_block_size) : + plain(data_path, max_compress_block_size, O_APPEND | O_CREAT | O_WRONLY), compressed(plain) { } @@ -107,7 +107,7 @@ public: * состоящую из указанных столбцов. * Если не указано attach - создать директорию, если её нет. */ - static StoragePtr create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach); + static StoragePtr create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach, size_t max_compress_block_size_ = DEFAULT_MAX_COMPRESS_BLOCK_SIZE); std::string getName() const { return "TinyLog"; } std::string getTableName() const { return name; } @@ -134,6 +134,8 @@ private: String name; NamesAndTypesListPtr columns; + size_t max_compress_block_size; + /// Данные столбца struct ColumnData { @@ -142,7 +144,7 @@ private: typedef std::map Files_t; Files_t files; - StorageTinyLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach); + StorageTinyLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach, size_t max_compress_block_size_); void addFile(const String & column_name, const IDataType & type, size_t level = 0); }; diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 67aa10b9f62..5712e750131 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -503,14 +503,14 @@ ProcessList::Element * Context::getProcessListElement() } -void Context::setUncompressedCache(size_t cache_size_in_cells) +void Context::setUncompressedCache(size_t max_size_in_bytes) { Poco::ScopedLock lock(shared->mutex); if (shared->uncompressed_cache) throw Exception("Uncompressed cache has been already created.", ErrorCodes::LOGICAL_ERROR); - shared->uncompressed_cache = new UncompressedCache(cache_size_in_cells); + shared->uncompressed_cache = new UncompressedCache(max_size_in_bytes); } diff --git a/dbms/src/Storages/StorageChunks.cpp b/dbms/src/Storages/StorageChunks.cpp index 7ff9741a3f6..c3579bbff2a 100644 --- a/dbms/src/Storages/StorageChunks.cpp +++ b/dbms/src/Storages/StorageChunks.cpp @@ -137,7 +137,7 @@ StorageChunks::StorageChunks( Context & context_, bool attach) : - StorageLog(path_, name_, columns_), + StorageLog(path_, name_, columns_, context_.getSettings().max_compress_block_size), database_name(database_name_), reference_counter(path_ + escapeForFileName(name_) + "/refcount.txt"), context(context_), diff --git a/dbms/src/Storages/StorageFactory.cpp b/dbms/src/Storages/StorageFactory.cpp index 315390328b5..8e74418ddaa 100644 --- a/dbms/src/Storages/StorageFactory.cpp +++ b/dbms/src/Storages/StorageFactory.cpp @@ -68,7 +68,7 @@ StoragePtr StorageFactory::get( if (name == "Log") { - return StorageLog::create(data_path, table_name, columns); + return StorageLog::create(data_path, table_name, columns, context.getSettings().max_compress_block_size); } else if (name == "Chunks") { @@ -119,7 +119,7 @@ StoragePtr StorageFactory::get( } else if (name == "TinyLog") { - return StorageTinyLog::create(data_path, table_name, columns, attach); + return StorageTinyLog::create(data_path, table_name, columns, attach, context.getSettings().max_compress_block_size); } else if (name == "Memory") { diff --git a/dbms/src/Storages/StorageLog.cpp b/dbms/src/Storages/StorageLog.cpp index a8e77cb79f7..b1dd7ee1479 100644 --- a/dbms/src/Storages/StorageLog.cpp +++ b/dbms/src/Storages/StorageLog.cpp @@ -293,21 +293,21 @@ void LogBlockOutputStream::addStream(const String & name, const IDataType & type String size_name = DataTypeNested::extractNestedTableName(name) + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); if (!streams.count(size_name)) streams.insert(std::make_pair(size_name, new Stream( - storage.files[size_name].data_file.path()))); + storage.files[size_name].data_file.path(), storage.max_compress_block_size))); addStream(name, *type_arr->getNestedType(), level + 1); } else if (const DataTypeNested * type_nested = dynamic_cast(&type)) { String size_name = name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - streams[size_name] = new Stream(storage.files[size_name].data_file.path()); + streams[size_name] = new Stream(storage.files[size_name].data_file.path(), storage.max_compress_block_size); const NamesAndTypesList & columns = *type_nested->getNestedTypesList(); for (NamesAndTypesList::const_iterator it = columns.begin(); it != columns.end(); ++it) addStream(DataTypeNested::concatenateNestedName(name, it->first), *it->second, level + 1); } else - streams[name] = new Stream(storage.files[name].data_file.path()); + streams[name] = new Stream(storage.files[name].data_file.path(), storage.max_compress_block_size); } @@ -402,8 +402,8 @@ void LogBlockOutputStream::writeMarks(MarksForColumns marks) } -StorageLog::StorageLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_) - : path(path_), name(name_), columns(columns_), loaded_marks(false) +StorageLog::StorageLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, size_t max_compress_block_size_) + : path(path_), name(name_), columns(columns_), loaded_marks(false), max_compress_block_size(max_compress_block_size_) { if (columns->empty()) throw Exception("Empty list of columns passed to StorageLog constructor", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED); @@ -417,9 +417,9 @@ StorageLog::StorageLog(const std::string & path_, const std::string & name_, Nam marks_file = Poco::File(path + escapeForFileName(name) + '/' + DBMS_STORAGE_LOG_MARKS_FILE_NAME); } -StoragePtr StorageLog::create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_) +StoragePtr StorageLog::create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, size_t max_compress_block_size_) { - return (new StorageLog(path_, name_, columns_))->thisPtr(); + return (new StorageLog(path_, name_, columns_, max_compress_block_size_))->thisPtr(); } diff --git a/dbms/src/Storages/StorageTinyLog.cpp b/dbms/src/Storages/StorageTinyLog.cpp index ab179fb5492..73cf65987e0 100644 --- a/dbms/src/Storages/StorageTinyLog.cpp +++ b/dbms/src/Storages/StorageTinyLog.cpp @@ -200,21 +200,21 @@ void TinyLogBlockOutputStream::addStream(const String & name, const IDataType & { String size_name = DataTypeNested::extractNestedTableName(name) + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); if (!streams.count(size_name)) - streams.insert(std::make_pair(size_name, new Stream(storage.files[size_name].data_file.path()))); + streams.insert(std::make_pair(size_name, new Stream(storage.files[size_name].data_file.path(), storage.max_compress_block_size))); addStream(name, *type_arr->getNestedType(), level + 1); } else if (const DataTypeNested * type_nested = dynamic_cast(&type)) { String size_name = name + ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level); - streams[size_name] = new Stream(storage.files[size_name].data_file.path()); + streams[size_name] = new Stream(storage.files[size_name].data_file.path(), storage.max_compress_block_size); const NamesAndTypesList & columns = *type_nested->getNestedTypesList(); for (NamesAndTypesList::const_iterator it = columns.begin(); it != columns.end(); ++it) addStream(DataTypeNested::concatenateNestedName(name, it->first), *it->second, level + 1); } else - streams[name] = new Stream(storage.files[name].data_file.path()); + streams[name] = new Stream(storage.files[name].data_file.path(), storage.max_compress_block_size); } @@ -285,8 +285,8 @@ void TinyLogBlockOutputStream::write(const Block & block) } -StorageTinyLog::StorageTinyLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach) - : path(path_), name(name_), columns(columns_) +StorageTinyLog::StorageTinyLog(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach, size_t max_compress_block_size_) + : path(path_), name(name_), columns(columns_), max_compress_block_size(max_compress_block_size_) { if (columns->empty()) throw Exception("Empty list of columns passed to StorageTinyLog constructor", ErrorCodes::EMPTY_LIST_OF_COLUMNS_PASSED); @@ -303,9 +303,9 @@ StorageTinyLog::StorageTinyLog(const std::string & path_, const std::string & na addFile(it->first, *it->second); } -StoragePtr StorageTinyLog::create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach) +StoragePtr StorageTinyLog::create(const std::string & path_, const std::string & name_, NamesAndTypesListPtr columns_, bool attach, size_t max_compress_block_size_) { - return (new StorageTinyLog(path_, name_, columns_, attach))->thisPtr(); + return (new StorageTinyLog(path_, name_, columns_, attach, max_compress_block_size_))->thisPtr(); } From c4f8176d14dddc46f01f5f229e2c5be9bafaf903 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 18:39:00 +0400 Subject: [PATCH 106/281] dbms: simple test on sumArray and sumArrayIf [METR-10366] --- .../0_stateless/00013_b_select_from_table_with_arrays.reference | 1 + .../0_stateless/00013_b_select_from_table_with_arrays.sql | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.reference create mode 100644 dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.sql diff --git a/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.reference new file mode 100644 index 00000000000..725d1e7afd5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.reference @@ -0,0 +1 @@ +15 15 3 diff --git a/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.sql new file mode 100644 index 00000000000..95106c9748a --- /dev/null +++ b/dbms/tests/queries/0_stateless/00013_b_select_from_table_with_arrays.sql @@ -0,0 +1,2 @@ +SELECT sumArray(arr), sumArrayIf(arr, s LIKE '%l%'), sumArrayIf(arr, s LIKE '%e%') FROM arrays_test + From 14992512e9d60523b523c48501867a4bd345257b Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 19:33:30 +0400 Subject: [PATCH 107/281] dbms: fixed bug with sending totals and extreams in clickhouse server [METR-10071] --- dbms/src/Server/TCPHandler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 6a459019044..9c99f035005 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -346,6 +346,8 @@ void TCPHandler::sendTotals() initBlockOutput(); writeVarUInt(Protocol::Server::Totals, *out); + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) + writeStringBinary("", *out); state.block_out->write(totals); state.maybe_compressed_out->next(); @@ -369,6 +371,8 @@ void TCPHandler::sendExtremes() initBlockOutput(); writeVarUInt(Protocol::Server::Extremes, *out); + if (client_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) + writeStringBinary("", *out); state.block_out->write(extremes); state.maybe_compressed_out->next(); From 9f208de753db6087a1a4fe6b489d421a998bc279 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 19:52:11 +0400 Subject: [PATCH 108/281] tests: simple test on argMinArray and argMaxArray using arrayJoin [METR-10366] --- .../tests/queries/0_stateless/00027_simple_argMinArray.reference | 1 + dbms/tests/queries/0_stateless/00027_simple_argMinArray.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00027_simple_argMinArray.reference create mode 100644 dbms/tests/queries/0_stateless/00027_simple_argMinArray.sql diff --git a/dbms/tests/queries/0_stateless/00027_simple_argMinArray.reference b/dbms/tests/queries/0_stateless/00027_simple_argMinArray.reference new file mode 100644 index 00000000000..4482956b706 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00027_simple_argMinArray.reference @@ -0,0 +1 @@ +4 1 diff --git a/dbms/tests/queries/0_stateless/00027_simple_argMinArray.sql b/dbms/tests/queries/0_stateless/00027_simple_argMinArray.sql new file mode 100644 index 00000000000..b681a2c53cf --- /dev/null +++ b/dbms/tests/queries/0_stateless/00027_simple_argMinArray.sql @@ -0,0 +1 @@ +SELECT argMinArray(id, num), argMaxArray(id, num) FROM (SELECT arrayJoin([[10, 4, 3], [7, 5, 6], [8, 8, 2]]) AS num, arrayJoin([[1, 2, 4], [2, 3, 3]]) AS id) From a138e8efdf96f785e15a1e413262ae571f96f04e Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 28 Mar 2014 20:01:36 +0400 Subject: [PATCH 109/281] dbms: insignificant fix in checking if block is ready for compression [METR-10570] --- dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index fac3b5166e2..47e7f0ccf1c 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -132,7 +132,7 @@ protected: type_arr->serializeOffsets(column, stream.compressed, prev_mark, limit); /// Уже могло накопиться достаточно данных для сжатия в новый блок. - if (stream.compressed.offset() > storage.context.getSettings().min_compress_block_size) + if (stream.compressed.offset() >= storage.context.getSettings().min_compress_block_size) stream.compressed.next(); else stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. @@ -163,7 +163,7 @@ protected: type.serializeBinary(column, stream.compressed, prev_mark, limit); /// Уже могло накопиться достаточно данных для сжатия в новый блок. - if (stream.compressed.offset() > storage.context.getSettings().min_compress_block_size) + if (stream.compressed.offset() >= storage.context.getSettings().min_compress_block_size) stream.compressed.next(); else stream.compressed.nextIfAtEnd(); /// Чтобы вместо засечек, указывающих на конец сжатого блока, были засечки, указывающие на начало следующего. From d42352149d0e4eea086b04ad2ec68d1c2ac9c20a Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 19:57:06 +0400 Subject: [PATCH 110/281] dbms: DISTINCT before ORDER BY. [#METR-9627] --- .../DB/DataStreams/DistinctBlockInputStream.h | 21 ++++++++--- .../DB/Interpreters/InterpreterSelectQuery.h | 2 +- .../Interpreters/InterpreterSelectQuery.cpp | 35 +++++++++++-------- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/dbms/include/DB/DataStreams/DistinctBlockInputStream.h b/dbms/include/DB/DataStreams/DistinctBlockInputStream.h index 85d36502945..ff4f80d2264 100644 --- a/dbms/include/DB/DataStreams/DistinctBlockInputStream.h +++ b/dbms/include/DB/DataStreams/DistinctBlockInputStream.h @@ -17,8 +17,10 @@ namespace DB class DistinctBlockInputStream : public IProfilingBlockInputStream { public: - DistinctBlockInputStream(BlockInputStreamPtr input_, const Limits & limits, size_t limit_ = 0) - : limit(limit_), + /// Пустой columns_ значит все столбцы. + DistinctBlockInputStream(BlockInputStreamPtr input_, const Limits & limits, size_t limit_, Names columns_) + : columns_names(columns_), + limit(limit_), max_rows(limits.max_rows_in_distinct), max_bytes(limits.max_bytes_in_distinct), overflow_mode(limits.distinct_overflow_mode) @@ -51,11 +53,17 @@ protected: return Block(); size_t rows = block.rows(); - size_t columns = block.columns(); + size_t columns = columns_names.empty() ? block.columns() : columns_names.size(); ConstColumnPlainPtrs column_ptrs(columns); + for (size_t i = 0; i < columns; ++i) - column_ptrs[i] = block.getByPosition(i).column; + { + if (columns_names.empty()) + column_ptrs[i] = block.getByPosition(i).column; + else + column_ptrs[i] = block.getByName(columns_names[i]).column; + } /// Будем фильтровать блок, оставляя там только строки, которых мы ещё не видели. IColumn::Filter filter(rows); @@ -111,7 +119,8 @@ protected: throw Exception("Logical error: unknown overflow mode", ErrorCodes::LOGICAL_ERROR); } - for (size_t i = 0; i < columns; ++i) + size_t all_columns = block.columns(); + for (size_t i = 0; i < all_columns; ++i) block.getByPosition(i).column = block.getByPosition(i).column->filter(filter); return block; @@ -129,6 +138,8 @@ private: return true; } + Names columns_names; + size_t limit; /// Ограничения на максимальный размер множества diff --git a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h index 55991248a49..f190ca54035 100644 --- a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h @@ -65,7 +65,7 @@ private: void executeUnion( BlockInputStreams & streams); void executeLimit( BlockInputStreams & streams); void executeProjection( BlockInputStreams & streams, ExpressionActionsPtr expression); - void executeDistinct( BlockInputStreams & streams, bool before_order); + void executeDistinct( BlockInputStreams & streams, bool before_order, Names columns); void executeSubqueriesInSets( BlockInputStreams & streams, const Sets & sets); diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index e79a3f0bca5..d10d1a6f357 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -222,6 +222,9 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() ExpressionActionsPtr before_having; ExpressionActionsPtr before_order_and_select; ExpressionActionsPtr final_projection; + + /// Столбцы из списка SELECT, до переименования в алиасы. + Names selected_columns; /// Нужно ли выполнять первую часть конвейера - выполняемую на удаленных серверах при распределенной обработке. bool first_stage = from_stage < QueryProcessingStage::WithMergeableState @@ -268,6 +271,7 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() /// Если есть агрегация, выполняем выражения в SELECT и ORDER BY на инициировавшем сервере, иначе - на серверах-источниках. query_analyzer->appendSelect(chain, need_aggregate ? !second_stage : !first_stage); + selected_columns = chain.getLastStep().required_output; has_order_by = query_analyzer->appendOrderBy(chain, need_aggregate ? !second_stage : !first_stage); before_order_and_select = chain.getLastActions(); chain.addStep(); @@ -305,14 +309,17 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() if (need_aggregate) executeAggregation(streams, before_aggregation, aggregate_overflow_row, aggregate_final); else + { executeExpression(streams, before_order_and_select); + executeDistinct(streams, true, selected_columns); + } /** Оптимизация - при распределённой обработке запроса, - * если не указаны DISTINCT, GROUP, HAVING, ORDER, но указан LIMIT, + * если не указаны GROUP, HAVING, ORDER, но указан LIMIT, * то выполним предварительный LIMIT на удалёном сервере. */ if (!second_stage - && !query.distinct && !need_aggregate && !has_having && !has_order_by + && !need_aggregate && !has_having && !has_order_by && query.limit_length) { executePreLimit(streams); @@ -321,6 +328,8 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() if (second_stage) { + bool need_second_distinct_pass = true; + if (need_aggregate) { /// Если нужно объединить агрегированные результаты с нескольких серверов @@ -333,15 +342,16 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() executeHaving(streams, before_having); executeExpression(streams, before_order_and_select); + + executeDistinct(streams, true, selected_columns); + + need_second_distinct_pass = streams.size() > 1; } if (has_order_by) executeOrder(streams); executeProjection(streams, final_projection); - - /// Сначала выполняем DISTINCT во всех источниках. - executeDistinct(streams, true); /// На этой стадии можно считать минимумы и максимумы, если надо. if (settings.extremes) @@ -352,20 +362,14 @@ BlockInputStreamPtr InterpreterSelectQuery::execute() /** Оптимизация - если источников несколько и есть LIMIT, то сначала применим предварительный LIMIT, * ограничивающий число записей в каждом до offset + limit. */ - if (query.limit_length && streams.size() > 1) + if (query.limit_length && streams.size() > 1 && !query.distinct) executePreLimit(streams); - bool need_second_distinct_pass = streams.size() > 1; - executeUnion(streams); /// Если было более одного источника - то нужно выполнить DISTINCT ещё раз после их слияния. if (need_second_distinct_pass) - executeDistinct(streams, false); - - /** NOTE: В некоторых случаях, DISTINCT можно было бы применять раньше - * - до сортировки и, возможно, на удалённых серверах. - */ + executeDistinct(streams, false, Names()); executeLimit(streams); } @@ -734,7 +738,7 @@ void InterpreterSelectQuery::executeProjection(BlockInputStreams & streams, Expr } -void InterpreterSelectQuery::executeDistinct(BlockInputStreams & streams, bool before_order) +void InterpreterSelectQuery::executeDistinct(BlockInputStreams & streams, bool before_order, Names columns) { if (query.distinct) { @@ -752,7 +756,8 @@ void InterpreterSelectQuery::executeDistinct(BlockInputStreams & streams, bool b for (BlockInputStreams::iterator it = streams.begin(); it != streams.end(); ++it) { BlockInputStreamPtr & stream = *it; - stream = maybeAsynchronous(new DistinctBlockInputStream(stream, settings.limits, limit_for_distinct), is_async); + stream = maybeAsynchronous(new DistinctBlockInputStream( + stream, settings.limits, limit_for_distinct, columns), is_async); } } } From 6f61446152461aa0cc31edcb0f1c0a9137a475b8 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 19:58:59 +0400 Subject: [PATCH 111/281] clickhouse-test: switched to clickhouse-client; added a couple of multiquery tests. [#METR-10639] --- dbms/tests/clickhouse-test | 2 +- .../0_stateless/00027_distinct_and_order_by.reference | 10 ++++++++++ .../0_stateless/00027_distinct_and_order_by.sql | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 dbms/tests/queries/0_stateless/00027_distinct_and_order_by.reference create mode 100644 dbms/tests/queries/0_stateless/00027_distinct_and_order_by.sql diff --git a/dbms/tests/clickhouse-test b/dbms/tests/clickhouse-test index 3fcc18f5c5a..9270d99bc80 100755 --- a/dbms/tests/clickhouse-test +++ b/dbms/tests/clickhouse-test @@ -5,7 +5,7 @@ # Результаты сравниваются с эталонами. QUERIES_DIR="./queries" -CLIENT_PROGRAM="curl -sS http://localhost:8123/ --data-binary @-" +CLIENT_PROGRAM="clickhouse-client -n" COLOR_RESET="\033[0m" diff --git a/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.reference b/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.reference new file mode 100644 index 00000000000..8b1acc12b63 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.reference @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.sql b/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.sql new file mode 100644 index 00000000000..e794605febc --- /dev/null +++ b/dbms/tests/queries/0_stateless/00027_distinct_and_order_by.sql @@ -0,0 +1,2 @@ +SET max_rows_to_sort = 100; +SELECT DISTINCT x FROM (SELECT number % 10 AS x FROM system.numbers LIMIT 100000) ORDER BY x; From b0558796aa278070f98ba5d5b63441addb00e663 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 20:02:12 +0400 Subject: [PATCH 112/281] dbms: remote() table function can be used with dot: remote('127.0.0.1', system.one). [#METR-2807] --- .../DB/TableFunctions/TableFunctionRemote.h | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index 0239c4800a2..621a7278abb 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -29,30 +29,40 @@ public: { ASTs & args_func = dynamic_cast(*ast_function).children; + const char * err = "Storage Distributed requires 2 to 5 parameters" + " - name of configuration section with list of remote servers, name of remote database {,|.} name of remote table, " + "[username, password]."; + if (args_func.size() != 1) - throw Exception("Storage Distributed requires 3 or 5 parameters" - " - name of configuration section with list of remote servers, name of remote database, name of remote table, " - "[username, password].", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + throw Exception(err, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); ASTs & args = dynamic_cast(*args_func.at(0)).children; - if (args.size() != 3 && args.size() != 5) - throw Exception("Storage Distributed requires 3 or 5 parameters" - " - name of configuration section with list of remote servers, name of remote database, name of remote table, " - "[username, password].", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + if (args.size() < 2 || args.size() > 5) + throw Exception(err, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); String descripton = safeGet(dynamic_cast(*args[0]).value); String remote_database = dynamic_cast(*args[1]).name; - String remote_table = dynamic_cast(*args[2]).name; - String username = args.size() == 5 ? safeGet(dynamic_cast(*args[3]).value) : "default"; - String password = args.size() == 5 ? safeGet(dynamic_cast(*args[4]).value) : ""; + String remote_table = args.size() % 2 ? dynamic_cast(*args[2]).name : ""; + String username = args.size() >= 4 + ? safeGet(dynamic_cast(*args[args.size() - 2]).value) : "default"; + String password = args.size() >= 4 + ? safeGet(dynamic_cast(*args[args.size() - 1]).value) : ""; + + if (remote_table.empty()) + { + size_t dot = remote_database.find('.'); + if (dot == String::npos) + throw Exception(err, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + remote_table = remote_database.substr(dot + 1); + remote_database = remote_database.substr(0, dot); + } /// В InterpreterSelectQuery будет создан ExpressionAnalzyer, который при обработке запроса наткнется на эти Identifier. /// Нам необходимо их пометить как имя базы данных и таблицы поскольку по умолчанию стоит значение column dynamic_cast(*args[1]).kind = ASTIdentifier::Database; - dynamic_cast(*args[2]).kind = ASTIdentifier::Table; + if (args.size() % 2) + dynamic_cast(*args[2]).kind = ASTIdentifier::Table; std::vector > names; std::vector shards = parseDescription(descripton, 0, descripton.size(), ','); From 31093464dd7579828c738e4fafb92da2792e4700 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 28 Mar 2014 20:08:09 +0400 Subject: [PATCH 113/281] clickhouse-test: merged some tests into multiline tests. [#METR-10639] --- dbms/tests/queries/0_stateless/00006_1_set_extremes.sql | 1 - dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql | 1 - ...om.reference => 00006_extremes_and_subquery_from.reference} | 0 ..._subquery_from.sql => 00006_extremes_and_subquery_from.sql} | 3 ++- ...es.reference => 00013_1_create_table_with_arrays.reference} | 0 .../queries/0_stateless/00013_1_create_table_with_arrays.sql | 3 +++ .../00013_1_drop_if_exists_table_with_arrays.reference | 0 .../0_stateless/00013_1_drop_if_exists_table_with_arrays.sql | 1 - .../0_stateless/00013_2_create_table_with_arrays.reference | 0 .../queries/0_stateless/00013_2_create_table_with_arrays.sql | 1 - .../00013_3_insert_into_table_with_arrays.reference | 0 .../0_stateless/00013_3_insert_into_table_with_arrays.sql | 1 - ...es.reference => 00014_1_create_table_with_nested.reference} | 0 .../queries/0_stateless/00014_1_create_table_with_nested.sql | 3 +++ .../00014_1_drop_if_exists_table_with_nested.reference | 0 .../0_stateless/00014_1_drop_if_exists_table_with_nested.sql | 1 - .../0_stateless/00014_2_create_table_with_nested.reference | 0 .../queries/0_stateless/00014_2_create_table_with_nested.sql | 1 - .../00014_3_insert_into_table_with_nested.reference | 0 .../0_stateless/00014_3_insert_into_table_with_nested.sql | 1 - 20 files changed, 8 insertions(+), 9 deletions(-) delete mode 100644 dbms/tests/queries/0_stateless/00006_1_set_extremes.sql delete mode 100644 dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql rename dbms/tests/queries/0_stateless/{00006_2_extremes_and_subquery_from.reference => 00006_extremes_and_subquery_from.reference} (100%) rename dbms/tests/queries/0_stateless/{00006_2_extremes_and_subquery_from.sql => 00006_extremes_and_subquery_from.sql} (70%) rename dbms/tests/queries/0_stateless/{00006_1_set_extremes.reference => 00013_1_create_table_with_arrays.reference} (100%) create mode 100644 dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.sql delete mode 100644 dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.reference delete mode 100644 dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql delete mode 100644 dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.reference delete mode 100644 dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql delete mode 100644 dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.reference delete mode 100644 dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql rename dbms/tests/queries/0_stateless/{00006_3_unset_extremes.reference => 00014_1_create_table_with_nested.reference} (100%) create mode 100644 dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.sql delete mode 100644 dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.reference delete mode 100644 dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql delete mode 100644 dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.reference delete mode 100644 dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql delete mode 100644 dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.reference delete mode 100644 dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql diff --git a/dbms/tests/queries/0_stateless/00006_1_set_extremes.sql b/dbms/tests/queries/0_stateless/00006_1_set_extremes.sql deleted file mode 100644 index 53bd0c96ce2..00000000000 --- a/dbms/tests/queries/0_stateless/00006_1_set_extremes.sql +++ /dev/null @@ -1 +0,0 @@ -SET GLOBAL extremes = 1 diff --git a/dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql b/dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql deleted file mode 100644 index 31e8a97da82..00000000000 --- a/dbms/tests/queries/0_stateless/00006_3_unset_extremes.sql +++ /dev/null @@ -1 +0,0 @@ -SET GLOBAL extremes = 0 diff --git a/dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.reference b/dbms/tests/queries/0_stateless/00006_extremes_and_subquery_from.reference similarity index 100% rename from dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.reference rename to dbms/tests/queries/0_stateless/00006_extremes_and_subquery_from.reference diff --git a/dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.sql b/dbms/tests/queries/0_stateless/00006_extremes_and_subquery_from.sql similarity index 70% rename from dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.sql rename to dbms/tests/queries/0_stateless/00006_extremes_and_subquery_from.sql index c8e1e1bf66f..99bc0c63c22 100644 --- a/dbms/tests/queries/0_stateless/00006_2_extremes_and_subquery_from.sql +++ b/dbms/tests/queries/0_stateless/00006_extremes_and_subquery_from.sql @@ -1,2 +1,3 @@ +SET extremes = 1; SELECT 'Hello, world' FROM (SELECT number FROM system.numbers LIMIT 10) WHERE number < 0 -FORMAT JSONCompact \ No newline at end of file +FORMAT JSONCompact; diff --git a/dbms/tests/queries/0_stateless/00006_1_set_extremes.reference b/dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.reference similarity index 100% rename from dbms/tests/queries/0_stateless/00006_1_set_extremes.reference rename to dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.reference diff --git a/dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.sql new file mode 100644 index 00000000000..5a728830a48 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00013_1_create_table_with_arrays.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS arrays_test; +CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory; +INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []); diff --git a/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql deleted file mode 100644 index 8dbef3a0abb..00000000000 --- a/dbms/tests/queries/0_stateless/00013_1_drop_if_exists_table_with_arrays.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS arrays_test diff --git a/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql deleted file mode 100644 index 8585ba14140..00000000000 --- a/dbms/tests/queries/0_stateless/00013_2_create_table_with_arrays.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE TABLE arrays_test (s String, arr Array(UInt8)) ENGINE = Memory diff --git a/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.reference b/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql b/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql deleted file mode 100644 index 5b41e56db66..00000000000 --- a/dbms/tests/queries/0_stateless/00013_3_insert_into_table_with_arrays.sql +++ /dev/null @@ -1 +0,0 @@ -INSERT INTO arrays_test VALUES ('Hello', [1,2]), ('World', [3,4,5]), ('Goodbye', []) diff --git a/dbms/tests/queries/0_stateless/00006_3_unset_extremes.reference b/dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.reference similarity index 100% rename from dbms/tests/queries/0_stateless/00006_3_unset_extremes.reference rename to dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.reference diff --git a/dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.sql new file mode 100644 index 00000000000..56722dee7ac --- /dev/null +++ b/dbms/tests/queries/0_stateless/00014_1_create_table_with_nested.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS nested_test; +CREATE TABLE nested_test (s String, nest Nested(x UInt8, y UInt32)) ENGINE = Memory; +INSERT INTO nested_test VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], []); diff --git a/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql deleted file mode 100644 index 257afc58919..00000000000 --- a/dbms/tests/queries/0_stateless/00014_1_drop_if_exists_table_with_nested.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS nested_test diff --git a/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql deleted file mode 100644 index 49f015ce1aa..00000000000 --- a/dbms/tests/queries/0_stateless/00014_2_create_table_with_nested.sql +++ /dev/null @@ -1 +0,0 @@ -CREATE TABLE nested_test (s String, nest Nested(x UInt8, y UInt32)) ENGINE = Memory diff --git a/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.reference b/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.reference deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql b/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql deleted file mode 100644 index 44fdbc820ff..00000000000 --- a/dbms/tests/queries/0_stateless/00014_3_insert_into_table_with_nested.sql +++ /dev/null @@ -1 +0,0 @@ -INSERT INTO nested_test VALUES ('Hello', [1,2], [10,20]), ('World', [3,4,5], [30,40,50]), ('Goodbye', [], []) From ad52e7dd9d19d67209aa4a6f52a71828ab117e13 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 28 Mar 2014 23:59:23 +0400 Subject: [PATCH 114/281] dbms: improved tests [#METR-2944]. --- .../0_stateless/00028_big_agg_aj_distributed.reference | 1 + .../queries/0_stateless/00028_big_agg_aj_distributed.sql | 4 ++++ .../0_stateless/00029_big_non_agg_aj_distributed.reference | 1 + .../queries/0_stateless/00029_big_non_agg_aj_distributed.sql | 1 + 4 files changed, 7 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.reference create mode 100644 dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.sql create mode 100644 dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.reference create mode 100644 dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.sql diff --git a/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.reference b/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.reference new file mode 100644 index 00000000000..c2a650b8fa6 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.reference @@ -0,0 +1 @@ +253984050 diff --git a/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.sql b/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.sql new file mode 100644 index 00000000000..76892277a95 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00028_big_agg_aj_distributed.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS test.big_array; +CREATE TABLE IF NOT EXISTS test.big_array (x Array(UInt8)) ENGINE=TinyLog; +INSERT INTO test.big_array SELECT groupArray(number % 255) AS x FROM (SELECT * FROM system.numbers LIMIT 1000000); +SELECT sum(y) AS s FROM remote('127.0.0.{1,2}', test, big_array) ARRAY JOIN x AS y; diff --git a/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.reference b/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.reference new file mode 100644 index 00000000000..c2a650b8fa6 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.reference @@ -0,0 +1 @@ +253984050 diff --git a/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.sql b/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.sql new file mode 100644 index 00000000000..041c52986fc --- /dev/null +++ b/dbms/tests/queries/0_stateless/00029_big_non_agg_aj_distributed.sql @@ -0,0 +1 @@ +SELECT sum(s) FROM (SELECT y AS s FROM remote('127.0.0.{1,2}', test, big_array) ARRAY JOIN x AS y) From c12d5231dd48ad2e82d4e5bfa62987b32dae8a51 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 29 Mar 2014 00:32:38 +0400 Subject: [PATCH 115/281] dbms: fixed build [#METR-10616]. --- .../DataStreams/tests/expression_stream.cpp | 4 ++-- dbms/src/DataStreams/tests/filter_stream.cpp | 4 ++-- .../tests/filter_stream_hitlog.cpp | 4 ++-- dbms/src/DataStreams/tests/fork_streams.cpp | 4 ++-- dbms/src/Interpreters/tests/expression.cpp | 4 ++-- .../tests/expression_analyzer.cpp | 22 +++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/dbms/src/DataStreams/tests/expression_stream.cpp b/dbms/src/DataStreams/tests/expression_stream.cpp index f38c19e47a2..031e39d046f 100644 --- a/dbms/src/DataStreams/tests/expression_stream.cpp +++ b/dbms/src/DataStreams/tests/expression_stream.cpp @@ -50,8 +50,8 @@ int main(int argc, char ** argv) DB::ExpressionAnalyzer analyzer(ast, context); DB::ExpressionActionsChain chain; - analyzer.appendSelect(chain); - analyzer.appendProjectResult(chain); + analyzer.appendSelect(chain, false); + analyzer.appendProjectResult(chain, false); chain.finalize(); DB::ExpressionActionsPtr expression = chain.getLastActions(); diff --git a/dbms/src/DataStreams/tests/filter_stream.cpp b/dbms/src/DataStreams/tests/filter_stream.cpp index e6d121c27ea..eb7d7c06cbd 100644 --- a/dbms/src/DataStreams/tests/filter_stream.cpp +++ b/dbms/src/DataStreams/tests/filter_stream.cpp @@ -56,8 +56,8 @@ int main(int argc, char ** argv) DB::ExpressionAnalyzer analyzer(ast, context); DB::ExpressionActionsChain chain; - analyzer.appendSelect(chain); - analyzer.appendProjectResult(chain); + analyzer.appendSelect(chain, false); + analyzer.appendProjectResult(chain, false); chain.finalize(); DB::ExpressionActionsPtr expression = chain.getLastActions(); diff --git a/dbms/src/DataStreams/tests/filter_stream_hitlog.cpp b/dbms/src/DataStreams/tests/filter_stream_hitlog.cpp index 3502b798470..f5504d642f4 100644 --- a/dbms/src/DataStreams/tests/filter_stream_hitlog.cpp +++ b/dbms/src/DataStreams/tests/filter_stream_hitlog.cpp @@ -130,8 +130,8 @@ int main(int argc, char ** argv) DB::ExpressionAnalyzer analyzer(ast, context); DB::ExpressionActionsChain chain; - analyzer.appendSelect(chain); - analyzer.appendWhere(chain); + analyzer.appendSelect(chain, false); + analyzer.appendWhere(chain, false); chain.finalize(); DB::ExpressionActionsPtr expression = chain.getLastActions(); diff --git a/dbms/src/DataStreams/tests/fork_streams.cpp b/dbms/src/DataStreams/tests/fork_streams.cpp index 1a806b7b0e4..4fcc3a2bdd6 100644 --- a/dbms/src/DataStreams/tests/fork_streams.cpp +++ b/dbms/src/DataStreams/tests/fork_streams.cpp @@ -71,8 +71,8 @@ int main(int argc, char ** argv) DB::ExpressionAnalyzer analyzer(ast, context); DB::ExpressionActionsChain chain; - analyzer.appendSelect(chain); - analyzer.appendProjectResult(chain); + analyzer.appendSelect(chain, false); + analyzer.appendProjectResult(chain, false); chain.finalize(); DB::ExpressionActionsPtr expression = chain.getLastActions(); diff --git a/dbms/src/Interpreters/tests/expression.cpp b/dbms/src/Interpreters/tests/expression.cpp index 4cbb0d51e13..23582bd2780 100644 --- a/dbms/src/Interpreters/tests/expression.cpp +++ b/dbms/src/Interpreters/tests/expression.cpp @@ -65,8 +65,8 @@ int main(int argc, char ** argv) DB::ExpressionAnalyzer analyzer(ast, context); DB::ExpressionActionsChain chain; - analyzer.appendSelect(chain); - analyzer.appendProjectResult(chain); + analyzer.appendSelect(chain, false); + analyzer.appendProjectResult(chain, false); chain.finalize(); DB::ExpressionActionsPtr expression = chain.getLastActions(); diff --git a/dbms/src/Interpreters/tests/expression_analyzer.cpp b/dbms/src/Interpreters/tests/expression_analyzer.cpp index abe55518ba6..71c211d2211 100644 --- a/dbms/src/Interpreters/tests/expression_analyzer.cpp +++ b/dbms/src/Interpreters/tests/expression_analyzer.cpp @@ -86,19 +86,19 @@ int main(int argc, char ** argv) std::cout << "\n"; ExpressionActionsChain before; - if (analyzer.appendWhere(before)) + if (analyzer.appendWhere(before, false)) before.addStep(); - analyzer.appendAggregateFunctionsArguments(before); - analyzer.appendGroupBy(before); + analyzer.appendAggregateFunctionsArguments(before, false); + analyzer.appendGroupBy(before, false); before.finalize(); ExpressionActionsChain after; - if (analyzer.appendHaving(after)) + if (analyzer.appendHaving(after, false)) after.addStep(); - analyzer.appendSelect(after); - analyzer.appendOrderBy(after); + analyzer.appendSelect(after, false); + analyzer.appendOrderBy(after, false); after.addStep(); - analyzer.appendProjectResult(after); + analyzer.appendProjectResult(after, false); after.finalize(); std::cout << "before aggregation:\n\n"; @@ -120,12 +120,12 @@ int main(int argc, char ** argv) if (dynamic_cast(&*root)) { ExpressionActionsChain chain; - if (analyzer.appendWhere(chain)) + if (analyzer.appendWhere(chain, false)) chain.addStep(); - analyzer.appendSelect(chain); - analyzer.appendOrderBy(chain); + analyzer.appendSelect(chain, false); + analyzer.appendOrderBy(chain, false); chain.addStep(); - analyzer.appendProjectResult(chain); + analyzer.appendProjectResult(chain, false); chain.finalize(); for (size_t i = 0; i < chain.steps.size(); ++i) From e426667931dfc00e02a1c2e92adbfc601f713fe2 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 1 Apr 2014 17:41:03 +0400 Subject: [PATCH 116/281] dbms: fixes in create temporary table [METR-10071] --- dbms/src/Interpreters/InterpreterCreateQuery.cpp | 2 +- dbms/src/Parsers/formatAST.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index ee2cd8d004a..b10d07689d9 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -237,7 +237,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) if (create.is_temporary) { - res->is_dropped = true; +// res->is_dropped = true; context.getSessionContext().addExternalTable(table_name, res); } else diff --git a/dbms/src/Parsers/formatAST.cpp b/dbms/src/Parsers/formatAST.cpp index 8f55c9866cd..c6cedf62c90 100644 --- a/dbms/src/Parsers/formatAST.cpp +++ b/dbms/src/Parsers/formatAST.cpp @@ -248,7 +248,7 @@ void formatAST(const ASTCreateQuery & ast, std::ostream & s, size_t indent, bo if (ast.is_materialized_view) what = "MATERIALIZED VIEW"; - s << (hilite ? hilite_keyword : "") << (ast.attach ? "ATTACH " : "CREATE ") << what << " " << (ast.if_not_exists ? "IF NOT EXISTS " : "") << (hilite ? hilite_none : "") + s << (hilite ? hilite_keyword : "") << (ast.attach ? "ATTACH " : "CREATE ") << (ast.is_temporary ? "TEMPORARY " : "") << what << " " << (ast.if_not_exists ? "IF NOT EXISTS " : "") << (hilite ? hilite_none : "") << (!ast.database.empty() ? backQuoteIfNeed(ast.database) + "." : "") << backQuoteIfNeed(ast.table); } From fff1593bd980bd7e3ced1f29d556434408d54856 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Tue, 1 Apr 2014 18:06:58 +0400 Subject: [PATCH 117/281] dbms: Sending only required external tables to remote servers [METR-10071] --- .../DB/Interpreters/ExpressionAnalyzer.h | 5 ++- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 39 ++++++++++++++++++- .../Interpreters/InterpreterSelectQuery.cpp | 5 ++- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 6b22ed29808..c3418bc12d8 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -100,7 +100,7 @@ public: Block getSelectSampleBlock(); /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN. - std::vector external_tables; + Tables external_tables; private: typedef std::set NamesSet; @@ -263,7 +263,8 @@ private: /// Обходит запрос и сохраняет найденные глобальные функции (например GLOBAL IN) void findGlobalFunctions(ASTPtr & ast, std::vector & global_nodes); - + void findExternalTables(ASTPtr & ast); + /// Превратить перечисление значений или подзапрос в ASTSet. node - функция in или notIn. void makeSet(ASTFunction * node, const Block & sample_block); /// Выполнить подзапрос в секции GLOBAL IN и запомнить результат во временную таблицу типа memory diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 5c32f4053a4..208bc0c1e9f 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -516,6 +516,34 @@ void ExpressionAnalyzer::findGlobalFunctions(ASTPtr & ast, std::vector & } +void ExpressionAnalyzer::findExternalTables(ASTPtr & ast) +{ + /// Рекурсивные вызовы. Намеренно опускаемся в подзапросы. + for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it) + findExternalTables(*it); + + /// Если идентификатор типа таблица + StoragePtr external_storage; + if (ASTIdentifier * node = dynamic_cast(&*ast)) + if (node->kind == ASTIdentifier::Kind::Table) + if (external_storage = context.tryGetExternalTable(node->name)) + external_tables[node->name] = external_storage; + + if (ASTFunction * node = dynamic_cast(&*ast)) + { + if (node->name == "globalIn" || node->name == "globalNotIn" || node->name == "In" || node->name == "NotIn") + { + IAST & args = *node->arguments; + ASTPtr & arg = args.children[1]; + /// Если имя таблицы для селекта + if (ASTIdentifier * id = dynamic_cast(&*arg)) + if (external_storage = context.tryGetExternalTable(id->name)) + external_tables[id->name] = external_storage; + } + } +} + + void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id) { IAST & args = *node->arguments; @@ -542,8 +570,13 @@ void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id { ParserSelectQuery parser; - if (context.tryGetExternalTable(table->name)) + StoragePtr existing_storage; + + if (existing_storage = context.tryGetExternalTable(table->name)) + { + external_tables[table->name] = existing_storage; return; + } String query = "SELECT * FROM " + table->name; const char * begin = query.data(); @@ -573,7 +606,7 @@ void ExpressionAnalyzer::addExternalStorage(ASTFunction * node, size_t & name_id ast_ident->kind = ASTIdentifier::Table; ast_ident->name = external_storage->getTableName(); arg = ast_ident; - external_tables.push_back(external_storage); + external_tables[external_table_name] = external_storage; } else throw Exception("GLOBAL [NOT] IN supports only SELECT data.", ErrorCodes::BAD_ARGUMENTS); @@ -1177,6 +1210,8 @@ void ExpressionAnalyzer::processGlobalOperations() ++id; addExternalStorage(dynamic_cast(&*global_nodes[i]), id); } + + findExternalTables(ast); } bool ExpressionAnalyzer::appendArrayJoin(ExpressionActionsChain & chain, bool only_types) diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index d10d1a6f357..9e8a4bcc61f 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -83,7 +83,8 @@ void InterpreterSelectQuery::init(BlockInputStreamPtr input_, const NamesAndType /// Сохраняем в query context новые временные таблицы for (auto & it : query_analyzer->external_tables) - context.addExternalTable(it->getTableName(), it); + if (!(context.tryGetExternalTable(it.first))) + context.addExternalTable(it.first, it.second); if (input_) streams.push_back(input_); @@ -504,7 +505,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu if (!interpreter_subquery) { if (storage->isRemote()) - storage->storeExternalTables(context.getExternalTables()); + storage->storeExternalTables(query_analyzer->external_tables); streams = storage->read(required_columns, query_ptr, settings_for_storage, from_stage, settings.max_block_size, settings.max_threads); for (auto stream : streams) { From 33338e0d2e992873cbc1c0de5b44c3feeea73db2 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 2 Apr 2014 03:42:26 +0400 Subject: [PATCH 118/281] dbms: fixed compatibility [#METR-10667]. --- dbms/include/DB/Core/Defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index e492db12a58..bd47cbcdf1c 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -42,4 +42,4 @@ #define DBMS_MIN_REVISION_WITH_USER_PASSWORD 34482 #define DBMS_MIN_REVISION_WITH_TOTALS_EXTREMES 35265 #define DBMS_MIN_REVISION_WITH_STRING_QUERY_ID 39002 -#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50263 +#define DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES 50264 From 0b8867ca55e6c418a52e92b042ff35bb744c8cf4 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 2 Apr 2014 11:14:40 +0400 Subject: [PATCH 119/281] dbms: updated test [#METR-8766]. --- .../src/IO/tests/o_direct_and_dirty_pages.cpp | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/dbms/src/IO/tests/o_direct_and_dirty_pages.cpp b/dbms/src/IO/tests/o_direct_and_dirty_pages.cpp index f372178f797..9ad09ee48a2 100644 --- a/dbms/src/IO/tests/o_direct_and_dirty_pages.cpp +++ b/dbms/src/IO/tests/o_direct_and_dirty_pages.cpp @@ -15,24 +15,58 @@ int main(int argc, char ** argv) try { + const size_t N = 100000; + const size_t BUF_SIZE = 1048576; + ReadBufferFromFile rand_in("/dev/urandom"); unsigned rand = 0; readBinary(rand, rand_in); String test = "Hello, world! " + toString(rand); + /// Пишем в файл как обычно, читаем с O_DIRECT. + { - WriteBufferFromFile wb("test", 4096); - writeStringBinary(test, wb); + WriteBufferFromFile wb("test1", BUF_SIZE); + for (size_t i = 0; i < N; ++i) + writeStringBinary(test, wb); wb.next(); } { - ReadBufferFromFile rb("test", 4096, O_RDONLY | O_DIRECT, nullptr, 4096); + ReadBufferFromFile rb("test1", BUF_SIZE, O_RDONLY | O_DIRECT, nullptr, 4096); String res; - readStringBinary(res, rb); + for (size_t i = 0; i < N; ++i) + readStringBinary(res, rb); - std::cerr << "test: " << test << ", res: " << res << std::endl; + std::cerr << "test: " << test << ", res: " << res << ", bytes: " << rb.count() << std::endl; + } + + /// Пишем в файл с O_DIRECT, читаем как обычно. + + { + WriteBufferFromFile wb("test2", BUF_SIZE, O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, 0666, nullptr, 4096); + + for (size_t i = 0; i < N; ++i) + writeStringBinary(test, wb); + + if (wb.offset() % 4096 != 0) + { + size_t pad = 4096 - wb.offset() % 4096; + memset(wb.position(), 0, pad); + wb.position() += pad; + } + + wb.next(); + } + + { + ReadBufferFromFile rb("test2", BUF_SIZE); + String res; + for (size_t i = 0; i < N; ++i) + readStringBinary(res, rb); + + std::cerr << "test: " << test << ", res: " << res << ", bytes: " << rb.count() << std::endl; } } catch (const Exception & e) From 8fc53a70206e4ccc36c9a0b3df4061e339a9c6d7 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 11:59:43 +0400 Subject: [PATCH 120/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 3 + .../DB/Interpreters/InterserverIOHandler.h | 10 +- .../MergeTree/AbandonableLockInZooKeeper.h | 99 +++++++++++++++++++ .../DB/Storages/MergeTree/MergeTreeData.h | 21 +++- .../ReplicatedMergeTreeBlockOutputStream.h | 80 +++++++++++++++ .../DB/Storages/StorageReplicatedMergeTree.h | 9 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 10 +- .../MergeTree/MergeTreeDataWriter.cpp | 2 +- .../Storages/StorageReplicatedMergeTree.cpp | 51 +++++++++- 9 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index 604c9807f55..6213ece69cf 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -236,6 +236,9 @@ namespace ErrorCodes NO_FILE_IN_DATA_PART, UNEXPECTED_FILE_IN_DATA_PART, BAD_SIZE_OF_FILE_IN_DATA_PART, + NOT_FOUND_EXPECTED_DATA_PART, + TOO_MANY_UNEXPECTED_DATA_PARTS, + NO_SUCH_DATA_PART, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/Interpreters/InterserverIOHandler.h b/dbms/include/DB/Interpreters/InterserverIOHandler.h index 16248887918..9a7bfef7291 100644 --- a/dbms/include/DB/Interpreters/InterserverIOHandler.h +++ b/dbms/include/DB/Interpreters/InterserverIOHandler.h @@ -63,12 +63,17 @@ private: class InterserverIOEndpointHolder { public: - InterserverIOEndpointHolder(const String & name_, InterserverIOEndpointPtr endpoint, InterserverIOHandler & handler_) - : name(name_), handler(handler_) + InterserverIOEndpointHolder(const String & name_, InterserverIOEndpointPtr endpoint_, InterserverIOHandler & handler_) + : name(name_), endpoint(endpoint_), handler(handler_) { handler.addEndpoint(name, endpoint); } + InterserverIOEndpointPtr getEndpoint() + { + return endpoint; + } + ~InterserverIOEndpointHolder() { try @@ -83,6 +88,7 @@ public: private: String name; + InterserverIOEndpointPtr endpoint; InterserverIOHandler & handler; }; diff --git a/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h new file mode 100644 index 00000000000..6a65d33bb73 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +/** Примитив синхронизации. Работает следующим образом: + * При создании создает неэфемерную инкрементную ноду и помечает ее как заблокированную (LOCKED). + * unlock() разблокирует ее (UNLOCKED). + * При вызове деструктора или завершении сессии в ZooKeeper, переходит в состояние ABANDONED. + * (В том числе при падении программы) + */ +class AbandonableLockInZooKeeper +{ +public: + enum State + { + UNLOCKED, + LOCKED, + ABANDONED, + }; + + AbandonableLockInZookeeper( + const String & path_prefix_, const String & temp_path, zkutil::ZooKeeper & zookeeper_) + : zookeeper(zookeeper_), path_prefix(path_prefix_) + { + /// Создадим вспомогательную эфемерную ноду. + holder_path = zookeeper.create(temp_path + "/abandonable-lock-", "", zkutil::CreateMode::EphemeralSequential); + + /// Запишем в основную ноду путь к вспомогательной. + path = zookeeper.create(path_prefix, holder_path, zkutil::CreateMode::PersistentSequential); + } + + String getPath() + { + return path; + } + + /// Распарсить число в конце пути. + UInt64 getNumber() + { + return static_cast(atol(path.substr(path_prefix.size()))); + } + + void unlock() + { + zookeeper.remove(path); + zookeeper.remove(holder_path); + } + + ~AbandonableLockInZookeeper() + { + try + { + zookeeper.remove(holder_path); + zookeeper.set(path, ""); /// Это не обязательно. + } + catch (...) + { + tryLogCurrentException(); + } + } + + static State check(const String & path, zkutil::ZooKeeper & zookeeper) + { + String holder_path; + + /// Если нет основной ноды, UNLOCKED. + if (!zookeeper.tryGet(path, holder_path)) + return UNLOCKED; + + /// Если в основной ноде нет пути к вспомогательной, ABANDONED. + if (holder_path.empty()) + return ABANDONED; + + /// Если вспомогательная нода жива, LOCKED. + if (zookeeper.exists(holder_path)) + return LOCKED; + + /// Если вспомогательной ноды нет, нужно еще раз проверить существование основной ноды, + /// потому что за это время могли успеть вызвать unlock(). + /// Заодно уберем оттуда путь к вспомогательной ноде. + if (zookeeper.trySet(path, "") == zkutil::ReturnCode::Ok) + return ABANDONED; + + return UNLOCKED; + } + +private: + zkutil::ZooKeeper & zookeeper; + String path_prefix; + String path; + String holder_path; +}; + +} diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 9ec69ea1952..044331d7cdb 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace DB @@ -125,6 +126,15 @@ public: { return files.empty(); } + + static Checksums parse(const String & s) + { + ReadBufferFromString in(s); + Checksums res; + res.readText(in); + assertEOF(in); + return res; + } }; DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} @@ -176,10 +186,11 @@ public: Poco::File(to).remove(true); } - void renameToOld() const + /// Переименовывает кусок, дописав к имени префикс. + void renameAddPrefix(const String & prefix) const { String from = storage.full_path + name + "/"; - String to = storage.full_path + "old_" + name + "/"; + String to = storage.full_path + prefix + name + "/"; Poco::File f(from); f.setLastModified(Poco::Timestamp::fromEpochTime(time(0))); @@ -318,6 +329,12 @@ public: */ void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment); + /** Переименовывает кусок в prefix_кусок и убирает его из рабочего набора. + * Лучше использовать только когда никто не может читать или писать этот кусок + * (например, при инициализации таблицы). + */ + void renameAndRemovePart(DataPartPtr part, const String & prefix); + /** Удалить неактуальные куски. */ void clearOldParts(); diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index e69de29bb2d..52899cfc496 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +class ReplicatedMergeTreeBlockOutputStream : public IBlockOutputStream +{ +public: + ReplicatedMergeTreeBlockOutputStream(StorageReplicatedMergeTree & storage_, const String & insert_id_) + : storage(storage_), insert_id(insert_id_), block_index(0) {} + + void write(const Block & block) override + { + auto part_blocks = storage.writer.splitBlockIntoParts(block); + for (auto & current_block : part_blocks) + { + ++block_index; + String block_id = insert_id.empty() ? "" : insert_id + "__" + toString(block_index); + + AbandonableLockInZooKeeper block_number_lock( + storage.zookeeper_path + "/block-numbers/block-", + storage.zookeeper_path + "/temp", storage.zookeeper); + + UInt64 part_number = block_number_lock.getNumber(); + + MergeTreeData::MutableDataPartPtr part = storage.writer.writeTempPart(current_block, part_number); + + String expected_checksums_str; + if (!block_id.empty() && storage.zookeeper.tryGet( + storage.zookeeper_path + "/blocks/" + block_id + "/checksums", expected_checksums_str)) + { + /// Блок с таким ID уже когда-то вставляли. Проверим чексуммы и не будем его вставлять. + auto expected_checksums = MergeTreeData::DataPart::Checksums.parse(expected_checksums_str); + expected_checksums.check(part->checksums); + + /// Бросаем block_number_lock. + continue; + } + + storage.data.renameTempPartAndAdd(part); + + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id, + "", + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id + "/checksums", + part->checksums.toString(), + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id + "/number", + toString(part_numbre), + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.replica_path + "/parts/" + block_id + "/number", + toString(part_numbre), + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + + const std::vector& acl, CreateMode::type mode); + + block_number_lock.unlock(); + } + } + +private: + StorageReplicatedMergeTree & storage; + String insert_id; + size_t block_index; +}; + +} diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index aad819a6d29..e71da14cb93 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -62,6 +62,8 @@ public: void drop() override; private: + friend class ReplicatedMergeTreeBlockOutputStream; + struct LogEntry { enum Type @@ -82,7 +84,12 @@ private: class MyInterserverIOEndpoint : public InterserverIOEndpoint { public: - MyInterserverIOEndpoint(StorageReplicatedMergeTree & storage_) : storage(storage_), owned_storage(storage.thisPtr()) {} + MyInterserverIOEndpoint(StorageReplicatedMergeTree & storage_) : storage(storage_) {} + + void setOwnedStorage(StoragePtr owned_storage_) + { + owned_storage = owned_storage_; + } void processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) override; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index ea0e81fbb9f..509e57a1e72 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -290,7 +290,7 @@ void MergeTreeData::clearOldParts() { LOG_DEBUG(log, "'Removing' part " << (*it)->name << " (prepending old_ to its name)"); - (*it)->renameToOld(); + (*it)->renameAddPrefix("old_"); all_data_parts.erase(it++); } else @@ -703,6 +703,14 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in all_data_parts.insert(part); } +void MergeTreeData::renameAndRemovePart(DataPartPtr part, const String & prefix) +{ + Poco::ScopedLock lock(data_parts_mutex); + if (!data_parts.erase(part)) + throw Exception("No such data part", ErrorCodes::NO_SUCH_DATA_PART); + part->renameAddPrefix(prefix); +} + MergeTreeData::DataParts MergeTreeData::getDataParts() { Poco::ScopedLock lock(data_parts_mutex); diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 6d6cbbe6a12..edf9e3b0dfa 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -114,7 +114,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa new_data_part->left = temp_index; new_data_part->right = temp_index; new_data_part->level = 0; - new_data_part->name = tmp_part_name; + new_data_part->name = tmp_part_name; (no tmp_ here?) new_data_part->size = part_size; new_data_part->modification_time = time(0); new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index d1feb2fdae3..58153c2f18c 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -55,7 +55,7 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( else { checkTableStructure(); - //checkParts(); + checkParts(); } String endpoint_name = "ReplicatedMergeTree:" + replica_path; @@ -79,8 +79,12 @@ StoragePtr StorageReplicatedMergeTree::create( const String & sign_column_, const MergeTreeSettings & settings_) { - return (new StorageReplicatedMergeTree(zookeeper_path_, replica_name_, attach, path_, name_, columns_, context_, primary_expr_ast_, - date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_))->thisPtr(); + StorageReplicatedMergeTree * res = new StorageReplicatedMergeTree(zookeeper_path_, replica_name_, attach, + path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, + index_granularity_, mode_, sign_column_, settings_); + StoragePtr res_ptr = res->thisPtr(); + dynamic_cast(*res->endpoint_holder->getEndpoint()).setOwnedStorage(res_ptr); + return res_ptr; } static String formattedAST(const ASTPtr & ast) @@ -156,6 +160,7 @@ void StorageReplicatedMergeTree::checkTableStructure() assertString(it.second->getName(), buf); assertString("\n", buf); } + assertEOF(buf); } void StorageReplicatedMergeTree::createReplica() @@ -170,6 +175,8 @@ void StorageReplicatedMergeTree::createReplica() void StorageReplicatedMergeTree::activateReplica() { + throw Exception("test"); + std::stringstream host; host << "host: " << context.getInterserverIOHost() << std::endl; host << "port: " << context.getInterserverIOPort() << std::endl; @@ -194,7 +201,42 @@ bool StorageReplicatedMergeTree::isTableEmpty() return true; } -void StorageReplicatedMergeTree::checkParts() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::checkParts() +{ + Strings expected_parts_vec = zookeeper.getChildren(replica_path + "/parts"); + NameSet expected_parts(expected_parts_vec.begin(), expected_parts_vec.end()); + + MergeTreeData::DataParts parts = data.getDataParts(); + + MergeTreeData::DataPartsVector unexpected_parts; + for (const auto & part : parts) + { + if (expected_parts.count(part->name)) + { + expected_parts.erase(part->name); + } + else + { + unexpected_parts.push_back(part); + } + } + + if (!expected_parts.empty()) + throw Exception("Not found " + toString(expected_parts.size()) + + " (including " + *expected_parts.begin() + ") parts in table " + data.getTableName(), + ErrorCodes::NOT_FOUND_EXPECTED_DATA_PART); + + if (unexpected_parts.size() > 1) + throw Exception("More than one unexpected part (including " + unexpected_parts[0]->name + + ") in table " + data.getTableName(), + ErrorCodes::TOO_MANY_UNEXPECTED_DATA_PARTS); + + for (MergeTreeData::DataPartPtr part : unexpected_parts) + { + LOG_ERROR(log, "Unexpected part " << part->name << ". Renaming it to ignored_" + part->name); + data.renameAndRemovePart(part, "ignored_"); + } +} void StorageReplicatedMergeTree::loadQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } @@ -207,6 +249,7 @@ void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not bool StorageReplicatedMergeTree::tryExecute(const LogEntry & entry) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + void StorageReplicatedMergeTree::getPart(const String & name, const String & replica_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } void StorageReplicatedMergeTree::shutdown() From 997875d7d1f3baa02259d13bd26f83d8c1805057 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:10:37 +0400 Subject: [PATCH 121/281] Merge --- .../MergeTree/AbandonableLockInZooKeeper.h | 19 +++-- .../DB/Storages/MergeTree/MergeTreeData.h | 16 ++++- .../ReplicatedMergeTreeBlockOutputStream.h | 27 +++++--- .../DB/Storages/StorageReplicatedMergeTree.h | 24 ++++++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 2 +- .../MergeTree/MergeTreeDataWriter.cpp | 2 +- .../Storages/StorageReplicatedMergeTree.cpp | 69 +++++++++++++++++-- 7 files changed, 136 insertions(+), 23 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h index 6a65d33bb73..0b429de68c0 100644 --- a/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h +++ b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h @@ -23,7 +23,7 @@ public: ABANDONED, }; - AbandonableLockInZookeeper( + AbandonableLockInZooKeeper( const String & path_prefix_, const String & temp_path, zkutil::ZooKeeper & zookeeper_) : zookeeper(zookeeper_), path_prefix(path_prefix_) { @@ -42,7 +42,7 @@ public: /// Распарсить число в конце пути. UInt64 getNumber() { - return static_cast(atol(path.substr(path_prefix.size()))); + return static_cast(atol(path.substr(path_prefix.size()).c_str())); } void unlock() @@ -51,16 +51,23 @@ public: zookeeper.remove(holder_path); } - ~AbandonableLockInZookeeper() + /// Добавляет в список действия, эквивалентные unlock(). + void getUnlockOps(zkutil::Ops & ops) + { + ops.push_back(new zkutil::Op::Remove(path, -1)); + ops.push_back(new zkutil::Op::Remove(holder_path, -1)); + } + + ~AbandonableLockInZooKeeper() { try { - zookeeper.remove(holder_path); - zookeeper.set(path, ""); /// Это не обязательно. + zookeeper.tryRemove(holder_path); + zookeeper.trySet(path, ""); /// Это не обязательно. } catch (...) { - tryLogCurrentException(); + tryLogCurrentException("~AbandonableLockInZooKeeper"); } } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 044331d7cdb..9aba07f95dd 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -127,6 +127,16 @@ public: return files.empty(); } + String toString() const + { + String s; + { + WriteBufferFromString out(s); + writeText(out); + } + return s; + } + static Checksums parse(const String & s) { ReadBufferFromString in(s); @@ -333,7 +343,11 @@ public: * Лучше использовать только когда никто не может читать или писать этот кусок * (например, при инициализации таблицы). */ - void renameAndRemovePart(DataPartPtr part, const String & prefix); + void renameAndDetachPart(DataPartPtr part, const String & prefix); + + /** Удаляет кусок из рабочего набора. clearOldParts удалит его файлы, если на него никто не ссылается. + */ + void removePart(DataPartPtr part); /** Удалить неактуальные куски. */ diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index 52899cfc496..3fb706c25ae 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -34,15 +34,22 @@ public: storage.zookeeper_path + "/blocks/" + block_id + "/checksums", expected_checksums_str)) { /// Блок с таким ID уже когда-то вставляли. Проверим чексуммы и не будем его вставлять. - auto expected_checksums = MergeTreeData::DataPart::Checksums.parse(expected_checksums_str); + auto expected_checksums = MergeTreeData::DataPart::Checksums::parse(expected_checksums_str); expected_checksums.check(part->checksums); + part->remove(); + /// Бросаем block_number_lock. continue; } - storage.data.renameTempPartAndAdd(part); + storage.data.renameTempPartAndAdd(part, nullptr); + StorageReplicatedMergeTree::LogEntry log_entry; + log_entry.type = StorageReplicatedMergeTree::LogEntry::GET_PART; + log_entry.new_part_name = part->name; + + /// Одновременно добавим информацию о куске во все нужные места в ZooKeeper и снимем block_number_lock. zkutil::Ops ops; ops.push_back(new zkutil::Op::Create( storage.zookeeper_path + "/blocks/" + block_id, @@ -56,18 +63,22 @@ public: zkutil::CreateMode::Persistent)); ops.push_back(new zkutil::Op::Create( storage.zookeeper_path + "/blocks/" + block_id + "/number", - toString(part_numbre), + toString(part_number), storage.zookeeper.getDefaultACL(), zkutil::CreateMode::Persistent)); ops.push_back(new zkutil::Op::Create( - storage.replica_path + "/parts/" + block_id + "/number", - toString(part_numbre), + storage.replica_path + "/parts/" + part->name, + "", storage.zookeeper.getDefaultACL(), zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.replica_path + "/log/log-", + log_entry.toString(), + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::PersistentSequential)); + block_number_lock.getUnlockOps(ops); - const std::vector& acl, CreateMode::type mode); - - block_number_lock.unlock(); + storage.zookeeper.multi(ops); } } diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index e71da14cb93..018386ca0dc 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -72,11 +72,33 @@ private: MERGE_PARTS, }; - Type type; String znode_name; + Type type; String new_part_name; Strings parts_to_merge; + + void writeText(WriteBuffer & out) const; + void readText(ReadBuffer & in); + + String toString() const + { + String s; + { + WriteBufferFromString out(s); + writeText(out); + } + return s; + } + + static LogEntry parse(const String & s) + { + ReadBufferFromString in(s); + LogEntry res; + res.readText(in); + assertEOF(in); + return res; + } }; typedef std::list LogEntries; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 509e57a1e72..a5d5d081179 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -703,7 +703,7 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in all_data_parts.insert(part); } -void MergeTreeData::renameAndRemovePart(DataPartPtr part, const String & prefix) +void MergeTreeData::renameAndDetachPart(DataPartPtr part, const String & prefix) { Poco::ScopedLock lock(data_parts_mutex); if (!data_parts.erase(part)) diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp index edf9e3b0dfa..6d6cbbe6a12 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -114,7 +114,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithDa new_data_part->left = temp_index; new_data_part->right = temp_index; new_data_part->level = 0; - new_data_part->name = tmp_part_name; (no tmp_ here?) + new_data_part->name = tmp_part_name; new_data_part->size = part_size; new_data_part->modification_time = time(0); new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 58153c2f18c..d0772fb0c21 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -175,8 +176,6 @@ void StorageReplicatedMergeTree::createReplica() void StorageReplicatedMergeTree::activateReplica() { - throw Exception("test"); - std::stringstream host; host << "host: " << context.getInterserverIOHost() << std::endl; host << "port: " << context.getInterserverIOPort() << std::endl; @@ -223,7 +222,7 @@ void StorageReplicatedMergeTree::checkParts() if (!expected_parts.empty()) throw Exception("Not found " + toString(expected_parts.size()) - + " (including " + *expected_parts.begin() + ") parts in table " + data.getTableName(), + + " parts (including " + *expected_parts.begin() + ") in table " + data.getTableName(), ErrorCodes::NOT_FOUND_EXPECTED_DATA_PART); if (unexpected_parts.size() > 1) @@ -234,7 +233,7 @@ void StorageReplicatedMergeTree::checkParts() for (MergeTreeData::DataPartPtr part : unexpected_parts) { LOG_ERROR(log, "Unexpected part " << part->name << ". Renaming it to ignored_" + part->name); - data.renameAndRemovePart(part, "ignored_"); + data.renameAndDetachPart(part, "ignored_"); } } @@ -286,7 +285,14 @@ BlockInputStreams StorageReplicatedMergeTree::read( return reader.read(column_names, query, settings, processed_stage, max_block_size, threads); } -BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query) +{ + String insert_id; + if (ASTInsertQuery * insert = dynamic_cast(&*query)) + insert_id = insert->insert_id; + + return new ReplicatedMergeTreeBlockOutputStream(*this, insert_id); +} void StorageReplicatedMergeTree::drop() { @@ -296,4 +302,57 @@ void StorageReplicatedMergeTree::drop() zookeeper.removeRecursive(zookeeper_path); } +void StorageReplicatedMergeTree::LogEntry::writeText(WriteBuffer & out) const +{ + writeString("format version: 1\n", out); + switch (type) + { + case GET_PART: + writeString("get\n", out); + writeString(new_part_name, out); + break; + case MERGE_PARTS: + writeString("merge\n", out); + for (const String & s : parts_to_merge) + { + writeString(s, out); + writeString("\n", out); + } + writeString("into\n", out); + writeString(new_part_name, out); + break; + } + writeString("\n", out); +} + +void StorageReplicatedMergeTree::LogEntry::readText(ReadBuffer & in) +{ + String type_str; + + assertString("format version: 1\n", in); + readString(type_str, in); + assertString("\n", in); + + if (type_str == "get") + { + type = GET_PART; + readString(new_part_name, in); + } + else if (type_str == "merge") + { + type = MERGE_PARTS; + while (true) + { + String s; + readString(s, in); + assertString("\n", in); + if (s == "into") + break; + parts_to_merge.push_back(s); + } + readString(new_part_name, in); + } + assertString("\n", in); +} + } From f885e8680b62f5601da6f7bd25d5ebee4e4e0325 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:29:54 +0400 Subject: [PATCH 122/281] Moved DB::Exception and StackTrace to statdaemons (to allow zkutil to have stack trace in exceptions without depending on dbms). [#METR-10202] --- dbms/include/DB/Core/Exception.h | 29 +--------- dbms/include/DB/Core/StackTrace.h | 28 ---------- dbms/src/Core/Exception.cpp | 41 --------------- dbms/src/Core/StackTrace.cpp | 82 ----------------------------- dbms/src/Core/tests/stack_trace.cpp | 12 ----- 5 files changed, 1 insertion(+), 191 deletions(-) delete mode 100644 dbms/include/DB/Core/StackTrace.h delete mode 100644 dbms/src/Core/StackTrace.cpp delete mode 100644 dbms/src/Core/tests/stack_trace.cpp diff --git a/dbms/include/DB/Core/Exception.h b/dbms/include/DB/Core/Exception.h index b746b4f9c0f..047c156e491 100644 --- a/dbms/include/DB/Core/Exception.h +++ b/dbms/include/DB/Core/Exception.h @@ -3,40 +3,13 @@ #include #include -#include +#include #include -#include - namespace DB { -class Exception : public Poco::Exception -{ -public: - Exception(int code = 0); - Exception(const std::string & msg, int code = 0); - Exception(const std::string & msg, const std::string & arg, int code = 0); - Exception(const std::string & msg, const Exception & exc, int code = 0); - Exception(const Exception & exc); - explicit Exception(const Poco::Exception & exc); - ~Exception() throw(); - Exception & operator = (const Exception & exc); - const char * name() const throw(); - const char * className() const throw(); - Exception * clone() const; - void rethrow() const; - - /// Дописать к существующему сообщению что-нибудь ещё. - void addMessage(const std::string & arg); - - const StackTrace & getStackTrace() const { return trace; } - -private: - StackTrace trace; -}; - using Poco::SharedPtr; typedef SharedPtr ExceptionPtr; diff --git a/dbms/include/DB/Core/StackTrace.h b/dbms/include/DB/Core/StackTrace.h deleted file mode 100644 index 857f4ecd3f6..00000000000 --- a/dbms/include/DB/Core/StackTrace.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#define DBMS_STACK_TRACE_MAX_DEPTH 32 - - -namespace DB -{ - -/// Позволяет получить стек-трейс -class StackTrace -{ -public: - /// Стектрейс снимается в момент создания объекта - StackTrace(); - - /// Вывести в строку - std::string toString() const; - -private: - typedef void* Frame; - Frame frames[DBMS_STACK_TRACE_MAX_DEPTH]; - size_t frames_size; -}; - -} diff --git a/dbms/src/Core/Exception.cpp b/dbms/src/Core/Exception.cpp index 15aa3266242..1194d913d46 100644 --- a/dbms/src/Core/Exception.cpp +++ b/dbms/src/Core/Exception.cpp @@ -11,47 +11,6 @@ namespace DB { -Exception::Exception(int code): Poco::Exception(code) {} -Exception::Exception(const std::string & msg, int code) : Poco::Exception(msg, code) {} -Exception::Exception(const std::string & msg, const std::string & arg, int code): Poco::Exception(msg, arg, code) {} -Exception::Exception(const std::string & msg, const Exception & exc, int code): Poco::Exception(msg, exc, code), trace(exc.trace) {} -Exception::Exception(const Exception & exc) : Poco::Exception(exc), trace(exc.trace) {} -Exception::Exception(const Poco::Exception & exc) : Poco::Exception(exc.displayText()) {} -Exception::~Exception() throw() {} - -Exception & Exception::operator=(const Exception& exc) -{ - Poco::Exception::operator=(exc); - trace = exc.trace; - return *this; -} - -const char* Exception::name() const throw() -{ - return "DB::Exception"; -} - -const char* Exception::className() const throw() -{ - return "DB::Exception"; -} - -Exception * Exception::clone() const -{ - return new Exception(*this); -} - -void Exception::rethrow() const -{ - throw *this; -} - -void Exception::addMessage(const std::string & arg) -{ - extendedMessage(arg); -} - - void throwFromErrno(const std::string & s, int code, int e) { char buf[128]; diff --git a/dbms/src/Core/StackTrace.cpp b/dbms/src/Core/StackTrace.cpp deleted file mode 100644 index 5b5be938aa8..00000000000 --- a/dbms/src/Core/StackTrace.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - - -#define DBMS_STACK_TRACE_MAX_DEPTH 32 - - -namespace DB -{ - -StackTrace::StackTrace() -{ - frames_size = backtrace(frames, DBMS_STACK_TRACE_MAX_DEPTH); -} - -std::string StackTrace::toString() const -{ - char ** symbols = backtrace_symbols(frames, frames_size); - std::stringstream res; - - if (!symbols) - return "Cannot get symbols for stack trace.\n"; - - try - { - for (size_t i = 0, size = frames_size; i < size; ++i) - { - /// Делаем demangling имён. Имя находится в скобках, до символа '+'. - - char * name_start = NULL; - char * name_end = NULL; - char * demangled_name = NULL; - int status = 0; - - if (NULL != (name_start = strchr(symbols[i], '(')) - && NULL != (name_end = strchr(name_start, '+'))) - { - ++name_start; - *name_end = '\0'; - demangled_name = abi::__cxa_demangle(name_start, 0, 0, &status); - *name_end = '+'; - } - - try - { - res << i << ". "; - - if (NULL != demangled_name && 0 == status) - { - res.write(symbols[i], name_start - symbols[i]); - res << demangled_name << name_end; - } - else - res << symbols[i]; - - res << std::endl; - } - catch (...) - { - free(demangled_name); - throw; - } - free(demangled_name); - } - } - catch (...) - { - free(symbols); - throw; - } - - free(symbols); - return res.str(); -} - -} diff --git a/dbms/src/Core/tests/stack_trace.cpp b/dbms/src/Core/tests/stack_trace.cpp deleted file mode 100644 index 061fe0fecb4..00000000000 --- a/dbms/src/Core/tests/stack_trace.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include - - -int main(int argc, char ** argv) -{ - DB::StackTrace trace; - std::cerr << trace.toString(); - - return 0; -} From 91f6a736179512a956e4f2115917a8077f1b057b Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:38:27 +0400 Subject: [PATCH 123/281] zkutil: Inherited KeeperException from DB::Exception. [#METR-10202] --- libs/libzkutil/include/zkutil/KeeperException.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index f4ec86fe958..b2375a3d282 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -1,15 +1,15 @@ #pragma once -#include +#include #include namespace zkutil { -class KeeperException : public Poco::Exception +class KeeperException : public DB::Exception { public: - KeeperException(const std::string & msg) : Poco::Exception(msg), code(ReturnCode::Ok) {} + KeeperException(const std::string & msg) : DB::Exception(msg), code(ReturnCode::Ok) {} KeeperException(const std::string & msg, ReturnCode::type code_) : Poco::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} KeeperException(ReturnCode::type code_) From 83fce6973ace5a1e14087207482df865e53c3406 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:44:55 +0400 Subject: [PATCH 124/281] Fixed build. [#METR-10202] --- libs/libzkutil/include/zkutil/KeeperException.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index b2375a3d282..ff5d96f6278 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -11,9 +11,9 @@ class KeeperException : public DB::Exception public: KeeperException(const std::string & msg) : DB::Exception(msg), code(ReturnCode::Ok) {} KeeperException(const std::string & msg, ReturnCode::type code_) - : Poco::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} + : DB::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} KeeperException(ReturnCode::type code_) - : Poco::Exception(ReturnCode::toString(code_)), code(code_) {} + : DB::Exception(ReturnCode::toString(code_)), code(code_) {} ReturnCode::type code; }; From b92f1ff48046a736cf5721b7d600ad2dc0dd7d8a Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 17:45:39 +0400 Subject: [PATCH 125/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 1 + dbms/include/DB/IO/ReadBufferFromHTTP.h | 26 ++-- dbms/include/DB/IO/ReadHelpers.h | 2 + dbms/include/DB/IO/RemoteReadBuffer.h | 21 ++- dbms/include/DB/IO/WriteHelpers.h | 2 + dbms/include/DB/IO/copyData.h | 4 + .../DB/Storages/MergeTree/MergeTreeData.h | 19 ++- .../DB/Storages/MergeTree/MergeTreeReader.h | 3 - .../ReplicatedMergeTreeBlockOutputStream.h | 46 +++--- .../ReplicatedMergeTreePartsExchange.h | 131 ++++++++++++++++++ .../DB/Storages/StorageReplicatedMergeTree.h | 17 --- dbms/src/IO/copyData.cpp | 14 ++ dbms/src/Storages/MergeTree/MergeTreeData.cpp | 24 ++-- .../Storages/StorageReplicatedMergeTree.cpp | 18 +-- .../include/zkutil/KeeperException.h | 4 + 15 files changed, 249 insertions(+), 83 deletions(-) create mode 100644 dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index 6213ece69cf..f016e90b3fb 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -239,6 +239,7 @@ namespace ErrorCodes NOT_FOUND_EXPECTED_DATA_PART, TOO_MANY_UNEXPECTED_DATA_PARTS, NO_SUCH_DATA_PART, + BAD_DATA_PART_NAME, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/IO/ReadBufferFromHTTP.h b/dbms/include/DB/IO/ReadBufferFromHTTP.h index d94f8d53bbc..9e23cffc413 100644 --- a/dbms/include/DB/IO/ReadBufferFromHTTP.h +++ b/dbms/include/DB/IO/ReadBufferFromHTTP.h @@ -24,32 +24,42 @@ class ReadBufferFromHTTP : public ReadBuffer private: std::string host; int port; - std::string params; Poco::Net::HTTPClientSession session; std::istream * istr; /// этим владеет session Poco::SharedPtr impl; public: + typedef std::vector > Params; + ReadBufferFromHTTP( const std::string & host_, int port_, - const std::string & params_, + const Params & params, size_t timeout_ = 0, size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE) - : ReadBuffer(NULL, 0), host(host_), port(port_), params(params_) + : ReadBuffer(NULL, 0), host(host_), port(port_) { - std::string encoded_path; - Poco::URI::encode(path, "&#", encoded_path); - std::stringstream uri; - uri << "http://" << host << ":" << port << "/?" << params; + uri << "http://" << host << ":" << port << "/"; + + bool first = true; + for (const auto & it : params) + { + uri << (first ? "?" : "&"); + first = false; + String encoded_key; + String encoded_value; + Poco::URI::encode(it.first, "=&#", encoded_key); + Poco::URI::encode(it.second, "&#", encoded_value); + uri << encoded_key << "=" << encoded_value; + } session.setHost(host); session.setPort(port); /// устанавливаем таймаут - session.setTimeout(Poco::Timespan(timeout_ ? timeout_ : DEFAULT_REMOTE_READ_BUFFER_TIMEOUT, 0)); + session.setTimeout(Poco::Timespan(timeout_ ? timeout_ : DEFAULT_HTTP_READ_BUFFER_TIMEOUT, 0)); Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uri.str()); Poco::Net::HTTPResponse response; diff --git a/dbms/include/DB/IO/ReadHelpers.h b/dbms/include/DB/IO/ReadHelpers.h index 724246d6d83..0f364c01c29 100644 --- a/dbms/include/DB/IO/ReadHelpers.h +++ b/dbms/include/DB/IO/ReadHelpers.h @@ -18,6 +18,7 @@ #include #include +#include #define DEFAULT_MAX_STRING_SIZE 0x00FFFFFFULL @@ -417,6 +418,7 @@ inline void readBinary(Float32 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(Float64 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(String & x, ReadBuffer & buf) { readStringBinary(x, buf); } inline void readBinary(bool & x, ReadBuffer & buf) { readPODBinary(x, buf); } +inline void readBinary(uint128 & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(VisitID_t & x, ReadBuffer & buf) { readPODBinary(x, buf); } inline void readBinary(mysqlxx::Date & x, ReadBuffer & buf) { readPODBinary(x, buf); } diff --git a/dbms/include/DB/IO/RemoteReadBuffer.h b/dbms/include/DB/IO/RemoteReadBuffer.h index 0366da65a18..ef80b364634 100644 --- a/dbms/include/DB/IO/RemoteReadBuffer.h +++ b/dbms/include/DB/IO/RemoteReadBuffer.h @@ -23,13 +23,12 @@ public: size_t timeout = 0, size_t buffer_size = DBMS_DEFAULT_BUFFER_SIZE) { - std::string encoded_path; - Poco::URI::encode(path, "&#", encoded_path); - - std::stringstream params; - params << "action=read&path=" << encoded_path << "&compress=" << (compress ? "true" : "false"); + ReadBufferFromHTTP::Params params = { + std::make_pair("action", "read"), + std::make_pair("path", path), + std::make_pair("compress", (compress ? "true" : "false"))}; - impl = new ReadBufferFromHTTP(host, port, params.str, timeout, buffer_size); + impl = new ReadBufferFromHTTP(host, port, params, timeout, buffer_size); } bool nextImpl() @@ -48,13 +47,11 @@ public: const std::string & path, size_t timeout = 0) { - std::string encoded_path; - Poco::URI::encode(path, "&#", encoded_path); + ReadBufferFromHTTP::Params params = { + std::make_pair("action", "list"), + std::make_pair("path", path)}; - std::stringstream params; - params << "action=list&path=" << encoded_path; - - ReadBufferFromHTTP in(host, port, params.str(), timeout); + ReadBufferFromHTTP in(host, port, params, timeout); std::vector files; while (!in.eof()) diff --git a/dbms/include/DB/IO/WriteHelpers.h b/dbms/include/DB/IO/WriteHelpers.h index 9d9cd563e89..06bad60e829 100644 --- a/dbms/include/DB/IO/WriteHelpers.h +++ b/dbms/include/DB/IO/WriteHelpers.h @@ -18,6 +18,7 @@ #include #include #include +#include #define WRITE_HELPERS_DEFAULT_FLOAT_PRECISION 6U @@ -448,6 +449,7 @@ inline void writeBinary(const Float32 & x, WriteBuffer & buf) { writePODBinary( inline void writeBinary(const Float64 & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const String & x, WriteBuffer & buf) { writeStringBinary(x, buf); } inline void writeBinary(const bool & x, WriteBuffer & buf) { writePODBinary(x, buf); } +inline void writeBinary(const uint128 & x, WriteBuffer & buf) { writePODBinary(x, buf); } inline void writeBinary(const VisitID_t & x, WriteBuffer & buf) { writePODBinary(static_cast(x), buf); } inline void writeBinary(const mysqlxx::Date & x, WriteBuffer & buf) { writePODBinary(x, buf); } diff --git a/dbms/include/DB/IO/copyData.h b/dbms/include/DB/IO/copyData.h index c4e50bdff47..c44698954c4 100644 --- a/dbms/include/DB/IO/copyData.h +++ b/dbms/include/DB/IO/copyData.h @@ -12,6 +12,10 @@ namespace DB */ void copyData(ReadBuffer & from, WriteBuffer & to); +/** Копирует bytes байт из ReadBuffer в WriteBuffer + */ +void copyData(ReadBuffer & from, WriteBuffer & to, size_t bytes); + } #endif diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 9aba07f95dd..6957902fbd8 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -8,8 +8,13 @@ #include #include #include +#include #include + +#define MERGE_TREE_MARK_SIZE (2 * sizeof(size_t)) + + namespace DB { @@ -147,7 +152,7 @@ public: } }; - DataPart(MergeTreeData & storage_) : storage(storage_), size_in_bytes(0) {} + DataPart(MergeTreeData & storage_) : storage(storage_), size(0), size_in_bytes(0) {} MergeTreeData & storage; DayNum_t left_date; @@ -237,14 +242,20 @@ public: && right >= rhs.right; } - /// Загрузить индекс и вычислить размер. + /// Загрузить индекс и вычислить размер. Если size=0, вычислить его тоже. void loadIndex() { + /// Размер - в количестве засечек. + if (!size) + size = Poco::File(storage.full_path + name + "/" + escapeForFileName(storage.columns->front().first) + ".mrk") + .getSize() / MERGE_TREE_MARK_SIZE; + size_t key_size = storage.sort_descr.size(); index.resize(key_size * size); String index_path = storage.full_path + name + "/primary.idx"; - ReadBufferFromFile index_file(index_path, std::min(static_cast(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize())); + ReadBufferFromFile index_file(index_path, + std::min(static_cast(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize())); for (size_t i = 0; i < size; ++i) for (size_t j = 0; j < key_size; ++j) @@ -318,7 +329,7 @@ public: bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const; /// Кладет в DataPart данные из имени кусочка. - void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part); + void parsePartName(const String & file_name, DataPart & part, const Poco::RegularExpression::MatchVec * matches = nullptr); std::string getTableName() { return ""; } diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index 3f0f7173e4a..041edc68989 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -12,9 +12,6 @@ #include -#define MERGE_TREE_MARK_SIZE (2 * sizeof(size_t)) - - namespace DB { diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index 3fb706c25ae..87175a54f43 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -11,7 +11,7 @@ class ReplicatedMergeTreeBlockOutputStream : public IBlockOutputStream { public: ReplicatedMergeTreeBlockOutputStream(StorageReplicatedMergeTree & storage_, const String & insert_id_) - : storage(storage_), insert_id(insert_id_), block_index(0) {} + : storage(storage_), insert_id(insert_id_), block_index(0), log(&Logger::get("ReplicatedMergeTreeBlockOutputStream")) {} void write(const Block & block) override { @@ -33,6 +33,8 @@ public: if (!block_id.empty() && storage.zookeeper.tryGet( storage.zookeeper_path + "/blocks/" + block_id + "/checksums", expected_checksums_str)) { + LOG_INFO(log, "Block with this ID already exists; ignoring it"); + /// Блок с таким ID уже когда-то вставляли. Проверим чексуммы и не будем его вставлять. auto expected_checksums = MergeTreeData::DataPart::Checksums::parse(expected_checksums_str); expected_checksums.check(part->checksums); @@ -49,28 +51,38 @@ public: log_entry.type = StorageReplicatedMergeTree::LogEntry::GET_PART; log_entry.new_part_name = part->name; + String checksums_str = part->checksums.toString(); + /// Одновременно добавим информацию о куске во все нужные места в ZooKeeper и снимем block_number_lock. zkutil::Ops ops; - ops.push_back(new zkutil::Op::Create( - storage.zookeeper_path + "/blocks/" + block_id, - "", - storage.zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - ops.push_back(new zkutil::Op::Create( - storage.zookeeper_path + "/blocks/" + block_id + "/checksums", - part->checksums.toString(), - storage.zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - ops.push_back(new zkutil::Op::Create( - storage.zookeeper_path + "/blocks/" + block_id + "/number", - toString(part_number), - storage.zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); + if (!block_id.empty()) + { + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id, + "", + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id + "/checksums", + checksums_str, + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.zookeeper_path + "/blocks/" + block_id + "/number", + toString(part_number), + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + } ops.push_back(new zkutil::Op::Create( storage.replica_path + "/parts/" + part->name, "", storage.zookeeper.getDefaultACL(), zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + storage.replica_path + "/parts/" + part->name + "/checksums", + checksums_str, + storage.zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); ops.push_back(new zkutil::Op::Create( storage.replica_path + "/log/log-", log_entry.toString(), @@ -86,6 +98,8 @@ private: StorageReplicatedMergeTree & storage; String insert_id; size_t block_index; + + Logger * log; }; } diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h new file mode 100644 index 00000000000..c509bb526c9 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include +#include + + +namespace DB +{ + +class ReplicatedMergeTreePartsServer : public InterserverIOEndpoint +{ +public: + ReplicatedMergeTreePartsServer(MergeTreeData & data_, StoragePtr owned_storage_) : data(data_), + owned_storage(owned_storage_), log(&Logger::get("ReplicatedMergeTreePartsServer")) {} + + void processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) override + { + String part_name = params.get("part"); + LOG_TRACE(log, "Sending part " << part_name); + + auto storage_lock = owned_storage->lockStructure(false); + + MergeTreeData::DataPartPtr part = findPart(part_name); + + /// Список файлов возьмем из списка контрольных сумм. + MergeTreeData::DataPart::Checksums checksums = part->checksums; + checksums.files["checksums.txt"]; + + writeBinary(checksums.files.size(), out); + for (const auto & it : checksums.files) + { + String path = data.getFullPath() + part_name + "/" + it.first; + UInt64 size = Poco::File(path).getSize(); + + writeStringBinary(it.first, out); + writeBinary(size, out); + + ReadBufferFromFile file_in(path); + HashingWriteBuffer hashing_out(out); + copyData(file_in, hashing_out); + + if (hashing_out.count() != size) + throw Exception("Unexpected size of file " + path, ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART); + + writeBinary(hashing_out.getHash(), out); + } + } + +private: + MergeTreeData & data; + StoragePtr owned_storage; + + Logger * log; + + MergeTreeData::DataPartPtr findPart(const String & name) + { + MergeTreeData::DataParts parts = data.getDataParts(); + for (const auto & part : parts) + { + if (part->name == name) + return part; + } + throw Exception("No part " + name + " in table"); + } +}; + +class ReplicatedMergeTreePartsFetcher +{ +public: + ReplicatedMergeTreePartsFetcher(MergeTreeData & data_) : data(data_), log(&Logger::get("ReplicatedMergeTreePartsFetcher")) {} + + /// Скачивает кусок в tmp_директорию, проверяет чексуммы. + MergeTreeData::MutableDataPartPtr fetchPart( + const String & part_name, + const String & replica_path, + const String & host, + int port) + { + LOG_TRACE(log, "Fetching part " << part_name); + ReadBufferFromHTTP::Params params = { + std::make_pair("endpoint", "ReplicatedMergeTree:" + replica_path), + std::make_pair("part", part_name)}; + ReadBufferFromHTTP in(host, port, params); + + String part_path = data.getFullPath() + "tmp_" + part_name + "/"; + if (!Poco::File(part_path).createDirectory()) + throw Exception("Directory " + part_path + " already exists"); + + size_t files; + readBinary(files, in); + for (size_t i = 0; i < files; ++i) + { + String file_name; + UInt64 file_size; + + readStringBinary(file_name, in); + readBinary(file_size, in); + + WriteBufferFromFile file_out(part_path + file_name); + HashingWriteBuffer hashing_out(file_out); + copyData(in, hashing_out, file_size); + + uint128 expected_hash; + readBinary(expected_hash, in); + + if (expected_hash != hashing_out.getHash()) + throw Exception("Checksum mismatch for file " + part_path + file_name + " transferred from " + replica_path); + } + + assertEOF(in); + + MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); + data.parsePartName(part_name, *new_data_part); + new_data_part->name = "tmp_" + part_name; + new_data_part->modification_time = time(0); + new_data_part->loadIndex(); + new_data_part->loadChecksums(); + + return new_data_part; + } + +private: + MergeTreeData & data; + + Logger * log; +}; + +} diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 018386ca0dc..0c1b01f0377 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -103,23 +103,6 @@ private: typedef std::list LogEntries; - class MyInterserverIOEndpoint : public InterserverIOEndpoint - { - public: - MyInterserverIOEndpoint(StorageReplicatedMergeTree & storage_) : storage(storage_) {} - - void setOwnedStorage(StoragePtr owned_storage_) - { - owned_storage = owned_storage_; - } - - void processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) override; - - private: - StorageReplicatedMergeTree & storage; - StoragePtr owned_storage; - }; - Context & context; zkutil::ZooKeeper & zookeeper; diff --git a/dbms/src/IO/copyData.cpp b/dbms/src/IO/copyData.cpp index adf1e116f1a..4eed3510b2f 100644 --- a/dbms/src/IO/copyData.cpp +++ b/dbms/src/IO/copyData.cpp @@ -14,5 +14,19 @@ void copyData(ReadBuffer & from, WriteBuffer & to) from.position() = from.buffer().end(); } } + +void copyData(ReadBuffer & from, WriteBuffer & to, size_t bytes) +{ + while (bytes > 0 && !from.eof()) + { + size_t count = std::min(bytes, static_cast(from.buffer().end() - from.position())); + to.write(from.position(), count); + from.position() += count; + bytes -= count; + } + + if (bytes > 0) + throw Exception("Attempt to read after EOF.", ErrorCodes::ATTEMPT_TO_READ_AFTER_EOF); +} } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index a5d5d081179..bd534fbd002 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -120,8 +119,18 @@ String MergeTreeData::getPartName(DayNum_t left_date, DayNum_t right_date, UInt6 } -void MergeTreeData::parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part) +void MergeTreeData::parsePartName(const String & file_name, DataPart & part, const Poco::RegularExpression::MatchVec * matches_p) { + Poco::RegularExpression::MatchVec match_vec; + if (!matches_p) + { + if (!isPartDirectory(file_name, match_vec)) + throw Exception("Unexpected part name: " + file_name, ErrorCodes::BAD_DATA_PART_NAME); + matches_p = &match_vec; + } + + const Poco::RegularExpression::MatchVec & matches = *matches_p; + DateLUTSingleton & date_lut = DateLUTSingleton::instance(); part.left_date = date_lut.toDayNum(OrderedIdentifier2Date(file_name.substr(matches[1].offset, matches[1].length))); @@ -181,7 +190,7 @@ void MergeTreeData::loadDataParts() continue; MutableDataPartPtr part = std::make_shared(*this); - parsePartName(file_name, matches, *part); + parsePartName(file_name, *part, &matches); part->name = file_name; /// Для битых кусков, которые могут образовываться после грубого перезапуска сервера, попытаться восстановить куски, из которых они сделаны. @@ -202,10 +211,6 @@ void MergeTreeData::loadDataParts() continue; } - /// Размер - в количестве засечек. - part->size = Poco::File(full_path + file_name + "/" + escapeForFileName(columns->front().first) + ".mrk").getSize() - / MERGE_TREE_MARK_SIZE; - part->modification_time = Poco::File(full_path + file_name).getLastModified().epochTime(); try @@ -618,9 +623,8 @@ Strings MergeTreeData::tryRestorePart(const String & path, const String & file_n Poco::RegularExpression::MatchVec matches; Strings restored_parts; - isPartDirectory(file_name, matches); DataPart broken_part(*this); - parsePartName(file_name, matches, broken_part); + parsePartName(file_name, broken_part); for (int i = static_cast(old_parts.size()) - 1; i >= 0; --i) { @@ -632,7 +636,7 @@ Strings MergeTreeData::tryRestorePart(const String & path, const String & file_n old_parts.erase(old_parts.begin() + i); continue; } - parsePartName(name, matches, old_part); + parsePartName(name, old_part, &matches); if (broken_part.contains(old_part)) { /// Восстанавливаем все содержащиеся куски. Если некоторые из них содержатся в других, их удалит loadDataParts. diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index d0772fb0c21..5ad7779d7cd 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,14 +8,6 @@ namespace DB { -void StorageReplicatedMergeTree::MyInterserverIOEndpoint::processQuery(const Poco::Net::HTMLForm & params, WriteBuffer & out) -{ - writeString("Hello. You requested part ", out); - writeString(params.get("part"), out); - writeString(".", out); -} - - StorageReplicatedMergeTree::StorageReplicatedMergeTree( const String & zookeeper_path_, const String & replica_name_, @@ -59,10 +52,6 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( checkParts(); } - String endpoint_name = "ReplicatedMergeTree:" + replica_path; - InterserverIOEndpointPtr endpoint = new MyInterserverIOEndpoint(*this); - endpoint_holder = new InterserverIOEndpointHolder(endpoint_name, endpoint, context.getInterserverIOHandler()); - activateReplica(); } @@ -84,7 +73,9 @@ StoragePtr StorageReplicatedMergeTree::create( path_, name_, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_, mode_, sign_column_, settings_); StoragePtr res_ptr = res->thisPtr(); - dynamic_cast(*res->endpoint_holder->getEndpoint()).setOwnedStorage(res_ptr); + String endpoint_name = "ReplicatedMergeTree:" + res->replica_path; + InterserverIOEndpointPtr endpoint = new ReplicatedMergeTreePartsServer(res->data, res_ptr); + res->endpoint_holder = new InterserverIOEndpointHolder(endpoint_name, endpoint, res->context.getInterserverIOHandler()); return res_ptr; } @@ -127,6 +118,7 @@ void StorageReplicatedMergeTree::createTable() zookeeper.create(zookeeper_path + "/replicas", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/blocks", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/block-numbers", "", zkutil::CreateMode::Persistent); + zookeeper.create(zookeeper_path + "/temp", "", zkutil::CreateMode::Persistent); } /** Проверить, что список столбцов и настройки таблицы совпадают с указанными в ZK (/metadata). diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index ff5d96f6278..46c671a6e22 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -15,6 +15,10 @@ public: KeeperException(ReturnCode::type code_) : DB::Exception(ReturnCode::toString(code_)), code(code_) {} + const char * name() const throw() { return "zkutil::KeeperException"; } + const char * className() const throw() { return "zkutil::KeeperException"; } + KeeperException * clone() const { return new KeeperException(message(), code); } + ReturnCode::type code; }; From 143ec07e065729d4bfe74aefe25937cd43321b8f Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 2 Apr 2014 21:13:10 +0400 Subject: [PATCH 126/281] dbms: fixed error with parser [#METR-10718]. --- dbms/include/DB/Parsers/CommonParsers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Parsers/CommonParsers.h b/dbms/include/DB/Parsers/CommonParsers.h index 0d9b4d1f6e1..8da1b7e9bd8 100644 --- a/dbms/include/DB/Parsers/CommonParsers.h +++ b/dbms/include/DB/Parsers/CommonParsers.h @@ -67,7 +67,7 @@ protected: bool parseImpl(Pos & pos, Pos end, ASTPtr & node, const char *& expected) { Pos begin = pos; - while (*pos == ' ' || *pos == '\t' || (allow_newlines && *pos == '\n') || *pos == '\r' || *pos == '\f') + while (pos < end && (*pos == ' ' || *pos == '\t' || (allow_newlines && *pos == '\n') || *pos == '\r' || *pos == '\f')) ++pos; return pos != begin; From eb1e9ddbab00a96057ec7f9b96b7a4645e9e90fd Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Wed, 2 Apr 2014 22:38:17 +0400 Subject: [PATCH 127/281] dbms: removed useless memset, lowered default max_query_size [#METR-10718]. --- dbms/include/DB/Common/PODArray.h | 8 +------- dbms/include/DB/Core/Defines.h | 2 +- dbms/src/Interpreters/executeQuery.cpp | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/dbms/include/DB/Common/PODArray.h b/dbms/include/DB/Common/PODArray.h index 0589eef3a2d..e4393c248ed 100644 --- a/dbms/include/DB/Common/PODArray.h +++ b/dbms/include/DB/Common/PODArray.h @@ -90,8 +90,6 @@ private: size_t bytes_to_alloc = to_size(n); c_start = c_end = Allocator::allocate(bytes_to_alloc); c_end_of_storage = c_start + bytes_to_alloc; - - //memset(c_start, 0, bytes_to_alloc); } void dealloc() @@ -107,8 +105,6 @@ private: void realloc(size_t n) { -// std::cerr << "realloc" << std::endl; - if (c_start == NULL) { alloc(n); @@ -138,9 +134,7 @@ private: memcpy(c_start, old_c_start, old_c_end_of_storage - old_c_start); Allocator::deallocate(old_c_start, old_c_end_of_storage - old_c_start); } - - //memset(c_start + (old_c_end_of_storage - old_c_start), 0, bytes_to_alloc - (old_c_end_of_storage - old_c_start)); - + c_end = c_start + end_diff; c_end_of_storage = c_start + bytes_to_alloc; diff --git a/dbms/include/DB/Core/Defines.h b/dbms/include/DB/Core/Defines.h index e492db12a58..b1e1d961f37 100644 --- a/dbms/include/DB/Core/Defines.h +++ b/dbms/include/DB/Core/Defines.h @@ -20,7 +20,7 @@ /// То же самое, но для операций слияния. Меньше DEFAULT_BLOCK_SIZE для экономии оперативки (так как читаются все столбцы). #define DEFAULT_MERGE_BLOCK_SIZE 10000 -#define DEFAULT_MAX_QUERY_SIZE 1048576 +#define DEFAULT_MAX_QUERY_SIZE 65536 #define SHOW_CHARS_ON_SYNTAX_ERROR 160L #define DEFAULT_MAX_THREADS 8 #define DEFAULT_MAX_DISTRIBUTED_CONNECTIONS 1024 diff --git a/dbms/src/Interpreters/executeQuery.cpp b/dbms/src/Interpreters/executeQuery.cpp index a21eba175bb..9b84d14198b 100644 --- a/dbms/src/Interpreters/executeQuery.cpp +++ b/dbms/src/Interpreters/executeQuery.cpp @@ -33,7 +33,7 @@ void executeQuery( ASTPtr ast; const char * expected = ""; - std::vector parse_buf; + PODArray parse_buf; const char * begin; const char * end; From 5542aa8f2d2a75d7368c6eb1c38d77c82e9bda0f Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:29:54 +0400 Subject: [PATCH 128/281] Moved DB::Exception and StackTrace to statdaemons (to allow zkutil to have stack trace in exceptions without depending on dbms). [#METR-10202] --- dbms/include/DB/Core/Exception.h | 29 +--------- dbms/include/DB/Core/StackTrace.h | 28 ---------- dbms/src/Core/Exception.cpp | 41 --------------- dbms/src/Core/StackTrace.cpp | 82 ----------------------------- dbms/src/Core/tests/stack_trace.cpp | 12 ----- 5 files changed, 1 insertion(+), 191 deletions(-) delete mode 100644 dbms/include/DB/Core/StackTrace.h delete mode 100644 dbms/src/Core/StackTrace.cpp delete mode 100644 dbms/src/Core/tests/stack_trace.cpp diff --git a/dbms/include/DB/Core/Exception.h b/dbms/include/DB/Core/Exception.h index b746b4f9c0f..047c156e491 100644 --- a/dbms/include/DB/Core/Exception.h +++ b/dbms/include/DB/Core/Exception.h @@ -3,40 +3,13 @@ #include #include -#include +#include #include -#include - namespace DB { -class Exception : public Poco::Exception -{ -public: - Exception(int code = 0); - Exception(const std::string & msg, int code = 0); - Exception(const std::string & msg, const std::string & arg, int code = 0); - Exception(const std::string & msg, const Exception & exc, int code = 0); - Exception(const Exception & exc); - explicit Exception(const Poco::Exception & exc); - ~Exception() throw(); - Exception & operator = (const Exception & exc); - const char * name() const throw(); - const char * className() const throw(); - Exception * clone() const; - void rethrow() const; - - /// Дописать к существующему сообщению что-нибудь ещё. - void addMessage(const std::string & arg); - - const StackTrace & getStackTrace() const { return trace; } - -private: - StackTrace trace; -}; - using Poco::SharedPtr; typedef SharedPtr ExceptionPtr; diff --git a/dbms/include/DB/Core/StackTrace.h b/dbms/include/DB/Core/StackTrace.h deleted file mode 100644 index 857f4ecd3f6..00000000000 --- a/dbms/include/DB/Core/StackTrace.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#define DBMS_STACK_TRACE_MAX_DEPTH 32 - - -namespace DB -{ - -/// Позволяет получить стек-трейс -class StackTrace -{ -public: - /// Стектрейс снимается в момент создания объекта - StackTrace(); - - /// Вывести в строку - std::string toString() const; - -private: - typedef void* Frame; - Frame frames[DBMS_STACK_TRACE_MAX_DEPTH]; - size_t frames_size; -}; - -} diff --git a/dbms/src/Core/Exception.cpp b/dbms/src/Core/Exception.cpp index 15aa3266242..1194d913d46 100644 --- a/dbms/src/Core/Exception.cpp +++ b/dbms/src/Core/Exception.cpp @@ -11,47 +11,6 @@ namespace DB { -Exception::Exception(int code): Poco::Exception(code) {} -Exception::Exception(const std::string & msg, int code) : Poco::Exception(msg, code) {} -Exception::Exception(const std::string & msg, const std::string & arg, int code): Poco::Exception(msg, arg, code) {} -Exception::Exception(const std::string & msg, const Exception & exc, int code): Poco::Exception(msg, exc, code), trace(exc.trace) {} -Exception::Exception(const Exception & exc) : Poco::Exception(exc), trace(exc.trace) {} -Exception::Exception(const Poco::Exception & exc) : Poco::Exception(exc.displayText()) {} -Exception::~Exception() throw() {} - -Exception & Exception::operator=(const Exception& exc) -{ - Poco::Exception::operator=(exc); - trace = exc.trace; - return *this; -} - -const char* Exception::name() const throw() -{ - return "DB::Exception"; -} - -const char* Exception::className() const throw() -{ - return "DB::Exception"; -} - -Exception * Exception::clone() const -{ - return new Exception(*this); -} - -void Exception::rethrow() const -{ - throw *this; -} - -void Exception::addMessage(const std::string & arg) -{ - extendedMessage(arg); -} - - void throwFromErrno(const std::string & s, int code, int e) { char buf[128]; diff --git a/dbms/src/Core/StackTrace.cpp b/dbms/src/Core/StackTrace.cpp deleted file mode 100644 index 5b5be938aa8..00000000000 --- a/dbms/src/Core/StackTrace.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - - -#define DBMS_STACK_TRACE_MAX_DEPTH 32 - - -namespace DB -{ - -StackTrace::StackTrace() -{ - frames_size = backtrace(frames, DBMS_STACK_TRACE_MAX_DEPTH); -} - -std::string StackTrace::toString() const -{ - char ** symbols = backtrace_symbols(frames, frames_size); - std::stringstream res; - - if (!symbols) - return "Cannot get symbols for stack trace.\n"; - - try - { - for (size_t i = 0, size = frames_size; i < size; ++i) - { - /// Делаем demangling имён. Имя находится в скобках, до символа '+'. - - char * name_start = NULL; - char * name_end = NULL; - char * demangled_name = NULL; - int status = 0; - - if (NULL != (name_start = strchr(symbols[i], '(')) - && NULL != (name_end = strchr(name_start, '+'))) - { - ++name_start; - *name_end = '\0'; - demangled_name = abi::__cxa_demangle(name_start, 0, 0, &status); - *name_end = '+'; - } - - try - { - res << i << ". "; - - if (NULL != demangled_name && 0 == status) - { - res.write(symbols[i], name_start - symbols[i]); - res << demangled_name << name_end; - } - else - res << symbols[i]; - - res << std::endl; - } - catch (...) - { - free(demangled_name); - throw; - } - free(demangled_name); - } - } - catch (...) - { - free(symbols); - throw; - } - - free(symbols); - return res.str(); -} - -} diff --git a/dbms/src/Core/tests/stack_trace.cpp b/dbms/src/Core/tests/stack_trace.cpp deleted file mode 100644 index 061fe0fecb4..00000000000 --- a/dbms/src/Core/tests/stack_trace.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include - - -int main(int argc, char ** argv) -{ - DB::StackTrace trace; - std::cerr << trace.toString(); - - return 0; -} From 4d4aeb2ee9bdaea98fe628489f8136f154bcf89e Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:38:27 +0400 Subject: [PATCH 129/281] zkutil: Inherited KeeperException from DB::Exception. [#METR-10202] --- libs/libzkutil/include/zkutil/KeeperException.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index f4ec86fe958..b2375a3d282 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -1,15 +1,15 @@ #pragma once -#include +#include #include namespace zkutil { -class KeeperException : public Poco::Exception +class KeeperException : public DB::Exception { public: - KeeperException(const std::string & msg) : Poco::Exception(msg), code(ReturnCode::Ok) {} + KeeperException(const std::string & msg) : DB::Exception(msg), code(ReturnCode::Ok) {} KeeperException(const std::string & msg, ReturnCode::type code_) : Poco::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} KeeperException(ReturnCode::type code_) From fa7cba6de49833ea58e1427278e0606f2e57481b Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Wed, 2 Apr 2014 14:44:55 +0400 Subject: [PATCH 130/281] Fixed build. [#METR-10202] --- libs/libzkutil/include/zkutil/KeeperException.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/libzkutil/include/zkutil/KeeperException.h b/libs/libzkutil/include/zkutil/KeeperException.h index b2375a3d282..ff5d96f6278 100644 --- a/libs/libzkutil/include/zkutil/KeeperException.h +++ b/libs/libzkutil/include/zkutil/KeeperException.h @@ -11,9 +11,9 @@ class KeeperException : public DB::Exception public: KeeperException(const std::string & msg) : DB::Exception(msg), code(ReturnCode::Ok) {} KeeperException(const std::string & msg, ReturnCode::type code_) - : Poco::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} + : DB::Exception(msg + " (" + ReturnCode::toString(code_) + ")"), code(code_) {} KeeperException(ReturnCode::type code_) - : Poco::Exception(ReturnCode::toString(code_)), code(code_) {} + : DB::Exception(ReturnCode::toString(code_)), code(code_) {} ReturnCode::type code; }; From 5a0aab3bb24cd2c62b0c6979a9cf5612f8e30715 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 3 Apr 2014 12:47:59 +0400 Subject: [PATCH 131/281] Merge --- .../ReplicatedMergeTreePartsExchange.h | 3 +- .../DB/Storages/StorageReplicatedMergeTree.h | 2 + dbms/src/Server/Server.cpp | 23 ++++--- .../Storages/StorageReplicatedMergeTree.cpp | 67 +++++++++++++++++-- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h index c509bb526c9..91f9b1a338c 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h @@ -82,7 +82,8 @@ public: LOG_TRACE(log, "Fetching part " << part_name); ReadBufferFromHTTP::Params params = { std::make_pair("endpoint", "ReplicatedMergeTree:" + replica_path), - std::make_pair("part", part_name)}; + std::make_pair("part", part_name), + std::make_pair("compress", "false")}; ReadBufferFromHTTP in(host, port, params); String part_path = data.getFullPath() + "tmp_" + part_name + "/"; diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 0c1b01f0377..4fd0c662c30 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -5,6 +5,7 @@ #include #include #include +#include "MergeTree/ReplicatedMergeTreePartsExchange.h" #include namespace DB @@ -133,6 +134,7 @@ private: MergeTreeData data; MergeTreeDataSelectExecutor reader; MergeTreeDataWriter writer; + ReplicatedMergeTreePartsFetcher fetcher; Logger * log; diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index 28e79e0d6e3..b1961992199 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -216,6 +216,20 @@ int Server::main(const std::vector & args) if (config.has("zookeeper")) global_context->setZooKeeper(new zkutil::ZooKeeper(config, "zookeeper")); + if (config.has("interserver_http_port")) + { + String this_host; + if (config.has("interserver_http_host")) + this_host = config.getString("interserver_http_host"); + else + this_host = Poco::Net::DNS::hostName(); + + String port_str = config.getString("interserver_http_port"); + int port = parse(port_str); + + global_context->setInterserverIOHost(this_host, port); + } + std::string users_config_path = config.getString("users_config", config.getString("config-file", "config.xml")); users_config_reloader = new UsersConfigReloader(users_config_path, global_context); @@ -285,16 +299,7 @@ int Server::main(const std::vector & args) Poco::SharedPtr interserver_io_http_server; if (config.has("interserver_http_port")) { - String this_host; - if (config.has("interserver_http_host")) - this_host = config.getString("interserver_http_host"); - else - this_host = Poco::Net::DNS::hostName(); - String port_str = config.getString("interserver_http_port"); - int port = parse(port_str); - - global_context->setInterserverIOHost(this_host, port); Poco::Net::ServerSocket interserver_io_http_socket(Poco::Net::SocketAddress("[::]:" + port_str)); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 5ad7779d7cd..30074ae6202 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -27,7 +27,7 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( replica_name(replica_name_), data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), - reader(data), writer(data), + reader(data), writer(data), fetcher(data), log(&Logger::get("StorageReplicatedMergeTree")), shutdown_called(false) { @@ -52,6 +52,7 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( checkParts(); } + loadQueue(); activateReplica(); } @@ -172,7 +173,7 @@ void StorageReplicatedMergeTree::activateReplica() host << "host: " << context.getInterserverIOHost() << std::endl; host << "port: " << context.getInterserverIOPort() << std::endl; - /// Одновременно объявим, что эта реплика активна и обновим хост. + /// Одновременно объявим, что эта реплика активна, и обновим хост. zkutil::Ops ops; ops.push_back(new zkutil::Op::Create(replica_path + "/is_active", "", zookeeper.getDefaultACL(), zkutil::CreateMode::Ephemeral)); ops.push_back(new zkutil::Op::SetData(replica_path + "/host", host.str(), -1)); @@ -229,11 +230,67 @@ void StorageReplicatedMergeTree::checkParts() } } -void StorageReplicatedMergeTree::loadQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::loadQueue() +{ + Poco::ScopedLock lock(queue_mutex); -void StorageReplicatedMergeTree::pullLogsToQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + Strings children = zookeeper.getChildren(replica_path + "/queue"); + std::sort(children.begin(), children.end()); + for (const String & child : children) + { + String s = zookeeper.get(child); + queue.push_back(LogEntry::parse(s)); + } +} -void StorageReplicatedMergeTree::optimizeQueue() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::pullLogsToQueue() +{ + Poco::ScopedLock lock(queue_mutex); + + Strings replicas = zookeeper.getChildren(zookeeper_path + "/replicas"); + for (const String & replica : replicas) + { + String log_path = zookeeper_path + "/" + replica + "/log"; + + String pointer_str; + UInt64 pointer; + + if (zookeeper.tryGet(replica_path + "/log_pointers/" + replica, pointer_str)) + { + pointer = Poco::NumberParser::parseUnsigned64(pointer_str); + } + else + { + /// Если у нас еще нет указателя на лог этой реплики, поставим указатель на первую запись в нем. + Strings entries = zookeeper.getChildren(log_path); + std::sort(entries.begin(), entries.end()); + pointer = entries.empty() ? 0 : Poco::NumberParser::parseUnsigned64(entries[0].substr(strlen("log-"))); + + zookeeper.create(replica_path + "/log_pointers/" + replica, toString(pointer), zkutil::CreateMode::Persistent); + } + + String entry_str; + while (zookeeper.tryGet(log_path + "/log-" + toString(pointer), entry_str)) + { + queue.push_back(LogEntry::parse(entry_str)); + + /// Одновременно добавим запись в очередь и продвинем указатель на лог. + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/queue/queue-", entry_str, zookeeper.getDefaultACL(), zkutil::CreateMode::PersistentSequential)); + ops.push_back(new zkutil::Op::SetData( + replica_path + "/log_pointers/" + replica, toString(pointer + 1), -1)); + zookeeper.multi(ops); + + ++pointer; + } + } +} + +void StorageReplicatedMergeTree::optimizeQueue() +{ + +} void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } From 765d0740f4f2efbe81025e3b3ee61c74d6dcbadc Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 2 Apr 2014 22:53:30 +0400 Subject: [PATCH 132/281] dbms: add support for drop nested columns [#METR-10550] --- .../DB/Interpreters/InterpreterAlterQuery.h | 3 + .../Interpreters/InterpreterAlterQuery.cpp | 73 +++++++++++++++++-- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/dbms/include/DB/Interpreters/InterpreterAlterQuery.h b/dbms/include/DB/Interpreters/InterpreterAlterQuery.h index b63d78da613..e22656fde6b 100644 --- a/dbms/include/DB/Interpreters/InterpreterAlterQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterAlterQuery.h @@ -5,6 +5,7 @@ namespace DB { +class ASTIdentifier; /** Позволяет добавить или удалить столбец в таблице. */ @@ -16,6 +17,8 @@ public: void execute(); private: + void dropColumnFromAST(const ASTIdentifier & drop_column, ASTs & columns); + ASTPtr query_ptr; Context context; diff --git a/dbms/src/Interpreters/InterpreterAlterQuery.cpp b/dbms/src/Interpreters/InterpreterAlterQuery.cpp index ae4808d338b..05ebe7cac5b 100644 --- a/dbms/src/Interpreters/InterpreterAlterQuery.cpp +++ b/dbms/src/Interpreters/InterpreterAlterQuery.cpp @@ -39,6 +39,65 @@ static bool namesEqualIgnoreAfterDot(const String & name_without_dot, const ASTP return (name_without_dot == name_type.name || name_with_dot == name_type.name.substr(0, name_with_dot.length())); } +void InterpreterAlterQuery::dropColumnFromAST(const ASTIdentifier & drop_column, ASTs & columns) +{ + Exception e("Wrong column name. Cannot find column " + drop_column.name + " to drop", DB::ErrorCodes::ILLEGAL_COLUMN); + ASTs::iterator drop_it; + + size_t dot_pos = drop_column.name.find('.'); + /// случай удаления nested столбца + if (dot_pos != std::string::npos) + { + /// в Distributed таблицах столбцы имеют название "nested.column" + drop_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, drop_column.name, _1)); + if (drop_it != columns.end()) + columns.erase(drop_it); + else + { + try + { + /// в MergeTree таблицах есть ASTFunction "nested" + /// в аргументах которой записаны столбцы + ASTs::iterator nested_it; + std::string nested_base_name = drop_column.name.substr(0, dot_pos); + nested_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, nested_base_name, _1)); + if (nested_it == columns.end()) + throw e; + + if ((**nested_it).children.size() != 1) + throw e; + + ASTFunction & f = dynamic_cast(*(**nested_it).children.back()); + if (f.name != "Nested") + throw e; + + ASTs & nested_columns = dynamic_cast(*f.arguments).children; + + drop_it = std::find_if(nested_columns.begin(), nested_columns.end(), boost::bind(namesEqual, drop_column.name.substr(dot_pos + 1), _1)); + if (drop_it == nested_columns.end()) + throw e; + else + nested_columns.erase(drop_it); + + if (nested_columns.empty()) + columns.erase(nested_it); + } + catch (std::bad_cast & bad_cast_err) + { + throw e; + } + } + } + else + { + drop_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, drop_column.name, _1)); + if (drop_it == columns.end()) + throw e; + else + columns.erase(drop_it); + } +} + void InterpreterAlterQuery::execute() { ASTAlterQuery & alter = dynamic_cast(*query_ptr); @@ -62,7 +121,10 @@ void InterpreterAlterQuery::execute() ASTs & columns = dynamic_cast(*attach.columns).children; /// Различные проверки, на возможность выполнения запроса - ASTs columns_copy = columns; + ASTs columns_copy; + for (const auto & ast : columns) + columns_copy.push_back(ast->clone()); + IdentifierNameSet identifier_names; attach.storage->collectIdentifierNames(identifier_names); for (ASTAlterQuery::ParameterContainer::const_iterator alter_it = alter.parameters.begin(); @@ -101,11 +163,7 @@ void InterpreterAlterQuery::execute() if (identifier_names.find(drop_column.name) != identifier_names.end()) throw Exception("Cannot drop key column", DB::ErrorCodes::ILLEGAL_COLUMN); - ASTs::iterator drop_it = std::find_if(columns_copy.begin(), columns_copy.end(), boost::bind(namesEqual, drop_column.name, _1)); - if (drop_it == columns_copy.end()) - throw Exception("Wrong column name. Cannot find column " + drop_column.name +" to drop", DB::ErrorCodes::ILLEGAL_COLUMN); - else - columns_copy.erase(drop_it); + dropColumnFromAST(drop_column, columns_copy); } else if (params.type == ASTAlterQuery::MODIFY) { @@ -172,8 +230,7 @@ void InterpreterAlterQuery::execute() else if (params.type == ASTAlterQuery::DROP) { const ASTIdentifier & drop_column = dynamic_cast (*params.column); - ASTs::iterator drop_it = std::find_if(columns.begin(), columns.end(), boost::bind(namesEqual, drop_column.name, _1)); - columns.erase(drop_it); + dropColumnFromAST(drop_column, columns); } else if (params.type == ASTAlterQuery::MODIFY) { From d50aaf9368a063332f7fb3fb293996189ea7acc0 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Thu, 3 Apr 2014 12:53:32 +0400 Subject: [PATCH 133/281] dbms: added removing of nested columns to merge tree [#METR-10550] --- .../DB/Storages/MergeTree/MergeTreeData.h | 2 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 28 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 616d7392697..c6e9dfb75cc 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -383,7 +383,7 @@ private: /// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта. void loadDataParts(); - void removeColumnFiles(String column_name); + void removeColumnFiles(String column_name, bool remove_array_size_files); /// Определить, не битые ли данные в директории. Проверяет индекс и засечеки, но не сами данные. bool isBrokenPart(const String & path); diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 9c8fc38fc44..f813419b1fd 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -334,11 +334,21 @@ void MergeTreeData::dropAllData() Poco::File(full_path).remove(true); } -void MergeTreeData::removeColumnFiles(String column_name) +void MergeTreeData::removeColumnFiles(String column_name, bool remove_array_size_files) { Poco::ScopedLock lock(data_parts_mutex); Poco::ScopedLock lock_all(all_data_parts_mutex); + size_t dot_pos = column_name.find('.'); + if (dot_pos != std::string::npos) + { + std::string nested_column = column_name.substr(0, dot_pos); + column_name = nested_column + "%2E" + column_name.substr(dot_pos + 1); + + if (remove_array_size_files) + column_name = std::string("(?:") + nested_column + "|" + column_name + ")"; + } + /// Регэксп выбирает файлы столбца для удаления Poco::RegularExpression re(column_name + "(?:(?:\\.|\\%2E).+){0,1}" +"(?:\\.mrk|\\.bin|\\.size\\d+\\.bin|\\.size\\d+\\.mrk)"); /// Цикл по всем директориям кусочков @@ -388,6 +398,12 @@ static DataTypePtr getDataTypeByName(const String & name, const NamesAndTypesLis throw Exception("No column " + name + " in table", ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); } +/// одинаковыми считаются имена, вида "name.*" +static bool namesWithDotEqual(const String & name_with_dot, const DB::NameAndTypePair & name_type) +{ + return (name_with_dot == name_type.first.substr(0, name_with_dot.length())); +} + void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) { { @@ -398,7 +414,15 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) if (params.type == ASTAlterQuery::DROP) { String column_name = dynamic_cast(*params.column).name; - removeColumnFiles(column_name); + + /// Если нет колонок вида nested_name.*, то удалим столбцы размера массивов + bool remove_array_size_files = false; + size_t dot_pos = column_name.find('.'); + if (dot_pos != std::string::npos) + { + remove_array_size_files = (columns->end() == std::find_if(columns->begin(), columns->end(), boost::bind(namesWithDotEqual, column_name.substr(0, dot_pos), _1))); + } + removeColumnFiles(column_name, remove_array_size_files); context.getUncompressedCache()->reset(); context.getMarkCache()->reset(); From b4d85c1676cda6ee9753ec2ccc69b5784907008b Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 3 Apr 2014 15:48:28 +0400 Subject: [PATCH 134/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 2 + .../DB/Storages/MergeTree/MergeTreeData.h | 13 +- .../ReplicatedMergeTreePartsExchange.h | 11 +- dbms/include/DB/Storages/StorageMergeTree.h | 9 +- .../DB/Storages/StorageReplicatedMergeTree.h | 34 +++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 31 +++ .../Storages/StorageReplicatedMergeTree.cpp | 182 +++++++++++++++++- 7 files changed, 249 insertions(+), 33 deletions(-) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index f016e90b3fb..b4c68a7d215 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -240,6 +240,8 @@ namespace ErrorCodes TOO_MANY_UNEXPECTED_DATA_PARTS, NO_SUCH_DATA_PART, BAD_DATA_PART_NAME, + NO_REPLICA_HAS_PART, + DUPLICATE_DATA_PART, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 6957902fbd8..e913bfeb9fb 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -82,6 +81,9 @@ struct MergeTreeSettings /// Сколько потоков использовать для объединения кусков. size_t merging_threads = 2; + /// Сколько потоков использовать для загрузки кусков с других реплик и объединения кусков (для ReplicatedMergeTree). + size_t replication_threads = 4; + /// Если из одного файла читается хотя бы столько строк, чтение можно распараллелить. size_t min_rows_for_concurrent_read = 20 * 8192; @@ -91,9 +93,8 @@ struct MergeTreeSettings /// Если отрезок индекса может содержать нужные ключи, делим его на столько частей и рекурсивно проверяем их. size_t coarse_index_granularity = 8; - /** Максимальное количество строк на запрос, для использования кэша разжатых данных. Если запрос большой - кэш не используется. - * (Чтобы большие запросы не вымывали кэш.) - */ + /// Максимальное количество строк на запрос, для использования кэша разжатых данных. Если запрос большой - кэш не используется. + /// (Чтобы большие запросы не вымывали кэш.) size_t max_rows_to_use_cache = 1024 * 1024; /// Через сколько секунд удалять old_куски. @@ -341,6 +342,10 @@ public: */ DataParts getDataParts(); + /** Возвращает кусок с указанным именем или кусок, покрывающий его. Если такого нет, возвращает nullptr. + */ + DataPartPtr getContainingPart(const String & part_name); + /** Удаляет куски old_parts и добавляет кусок new_part. Если какого-нибудь из удаляемых кусков нет, бросает исключение. */ void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h index 91f9b1a338c..707839ac661 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h @@ -57,12 +57,9 @@ private: MergeTreeData::DataPartPtr findPart(const String & name) { - MergeTreeData::DataParts parts = data.getDataParts(); - for (const auto & part : parts) - { - if (part->name == name) - return part; - } + MergeTreeData::DataPartPtr part = data.getContainingPart(name); + if (part && part->name == name) + return part; throw Exception("No part " + name + " in table"); } }; @@ -72,7 +69,7 @@ class ReplicatedMergeTreePartsFetcher public: ReplicatedMergeTreePartsFetcher(MergeTreeData & data_) : data(data_), log(&Logger::get("ReplicatedMergeTreePartsFetcher")) {} - /// Скачивает кусок в tmp_директорию, проверяет чексуммы. + /// Скачивает кусок в tmp_директорию. MergeTreeData::MutableDataPartPtr fetchPart( const String & part_name, const String & replica_path, diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index bb8c5b8cf45..46a9c129b60 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -1,10 +1,11 @@ #pragma once #include -#include "MergeTree/MergeTreeDataSelectExecutor.h" -#include "MergeTree/MergeTreeDataWriter.h" -#include "MergeTree/MergeTreeDataMerger.h" -#include "MergeTree/DiskSpaceMonitor.h" +#include +#include +#include +#include +#include namespace DB { diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 4fd0c662c30..a27345e7578 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -7,6 +7,7 @@ #include #include "MergeTree/ReplicatedMergeTreePartsExchange.h" #include +#include namespace DB { @@ -136,6 +137,14 @@ private: MergeTreeDataWriter writer; ReplicatedMergeTreePartsFetcher fetcher; + typedef std::vector Threads; + + /// Поток, следящий за обновлениями в логах всех реплик и загружающий их в очередь. + std::thread queue_updating_thread; + + /// Потоки, выполняющие действия из очереди. + Threads queue_threads; + Logger * log; volatile bool shutdown_called; @@ -197,21 +206,28 @@ private: */ void optimizeQueue(); - /** По порядку пытается выполнить действия из очереди, пока не получится. Что получилось, выбрасывает из очереди. + /** Выполнить действие из очереди. Бросает исключение, если что-то не так. */ - void executeSomeQueueEntry(); + void executeLogEntry(const LogEntry & entry); - /** Попробовать выполнить действие из очереди. Возвращает false, если не получилось по какой-то ожидаемой причине: - * - GET_PART, и ни у кого нет этого куска. Это возможно, если этот кусок уже слили с кем-то и удалили. - * - Не смогли скачать у кого-то кусок, потому что его там уже нет. - * - Не смогли объединить куски, потому что не все из них у нас есть. + /** В бесконечном цикле обновляет очередь. */ - bool tryExecute(const LogEntry & entry); + void queueUpdatingThread(); + + /** В бесконечном цикле выполняет действия из очереди. + */ + void queueThread(); /// Обмен кусками. - String findReplicaHavingPart(const String & part_name); - void getPart(const String & name, const String & replica_name); + /** Бросает исключение, если куска ни у кого нет. + */ + String findActiveReplicaHavingPart(const String & part_name); + + /** Скачать указанный кусок с указанной реплики. + */ + void fetchPart(const String & part_name, const String & replica_name); + }; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index bd534fbd002..6a6a4ada49f 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -698,6 +698,9 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in part->left = part->right = part_id; part->name = getPartName(part->left_date, part->right_date, part_id, part_id, 0); + if (data_parts.count(part)) + throw Exception("Part " + part->name + " already exists", ErrorCodes::DUPLICATE_DATA_PART); + String new_path = getFullPath() + part->name + "/"; /// Переименовываем кусок. @@ -722,6 +725,34 @@ MergeTreeData::DataParts MergeTreeData::getDataParts() return data_parts; } +MergeTreeData::DataPartPtr MergeTreeData::getContainingPart(const String & part_name) +{ + MutableDataPartPtr tmp_part(new DataPart(*this)); + parsePartName(part_name, *tmp_part); + + Poco::ScopedLock lock(data_parts_mutex); + + /// Кусок может покрываться только предыдущим или следующим в data_parts. + DataParts::iterator it = data_parts.lower_bound(tmp_part); + + if (it != data_parts.end()) + { + if ((*it)->name == part_name) + return *it; + if ((*it)->contains(*tmp_part)) + return *it; + } + + if (it != data_parts.begin()) + { + --it; + if ((*it)->contains(*tmp_part)) + return *it; + } + + return nullptr; +} + void MergeTreeData::DataPart::Checksums::check(const Checksums & rhs) const { diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 30074ae6202..9c8a9c5f499 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -8,6 +8,13 @@ namespace DB { + +const auto QUEUE_UPDATE_SLEEP = std::chrono::seconds(5); +const auto QUEUE_NO_WORK_SLEEP = std::chrono::seconds(5); +const auto QUEUE_ERROR_SLEEP = std::chrono::seconds(1); +const auto QUEUE_AFTER_WORK_SLEEP = std::chrono::seconds(0); + + StorageReplicatedMergeTree::StorageReplicatedMergeTree( const String & zookeeper_path_, const String & replica_name_, @@ -54,6 +61,10 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( loadQueue(); activateReplica(); + + queue_updating_thread = std::thread(&StorageReplicatedMergeTree::queueUpdatingThread, this); + for (size_t i = 0; i < settings_.replication_threads; ++i) + queue_threads.push_back(std::thread(&StorageReplicatedMergeTree::queueThread, this)); } StoragePtr StorageReplicatedMergeTree::create( @@ -239,7 +250,9 @@ void StorageReplicatedMergeTree::loadQueue() for (const String & child : children) { String s = zookeeper.get(child); - queue.push_back(LogEntry::parse(s)); + LogEntry entry = LogEntry::parse(s); + entry.znode_name = child; + queue.push_back(entry); } } @@ -272,7 +285,7 @@ void StorageReplicatedMergeTree::pullLogsToQueue() String entry_str; while (zookeeper.tryGet(log_path + "/log-" + toString(pointer), entry_str)) { - queue.push_back(LogEntry::parse(entry_str)); + LogEntry entry = LogEntry::parse(entry_str); /// Одновременно добавим запись в очередь и продвинем указатель на лог. zkutil::Ops ops; @@ -280,7 +293,11 @@ void StorageReplicatedMergeTree::pullLogsToQueue() replica_path + "/queue/queue-", entry_str, zookeeper.getDefaultACL(), zkutil::CreateMode::PersistentSequential)); ops.push_back(new zkutil::Op::SetData( replica_path + "/log_pointers/" + replica, toString(pointer + 1), -1)); - zookeeper.multi(ops); + auto results = zookeeper.multi(ops); + + String path_created = dynamic_cast((*results)[0]).getPathCreated(); + entry.znode_name = path.substr(path.find_last_of('/') + 1); + queue.push_back(entry); ++pointer; } @@ -289,16 +306,157 @@ void StorageReplicatedMergeTree::pullLogsToQueue() void StorageReplicatedMergeTree::optimizeQueue() { - } -void StorageReplicatedMergeTree::executeSomeQueueEntry() { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } +void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) +{ + if (entry.type == LogEntry::GET_PART || + entry.type == LogEntry::MERGE_PARTS) + { + /// Если у нас уже есть этот кусок или покрывающий его кусок, ничего делать не нужно. + MergeTreeData::DataPartPtr containing_part = data.getContainingPart(entry.new_part_name); -bool StorageReplicatedMergeTree::tryExecute(const LogEntry & entry) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + /// Даже если кусок есть локально, его (в исключительных случаях) может не быть в zookeeper. + if (containing_part && zookeeper.exists(replica_path + "/parts/" + containing_part->name)) + return; + } -String StorageReplicatedMergeTree::findReplicaHavingPart(const String & part_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + if (entry.type != LogEntry::GET_PART) + throw Exception("Merging is not implemented.", ErrorCodes::NOT_IMPLEMENTED); -void StorageReplicatedMergeTree::getPart(const String & name, const String & replica_name) { throw Exception("Not implemented", ErrorCodes::NOT_IMPLEMENTED); } + String replica = findActiveReplicaHavingPart(entry.new_part_name); + fetchPart(entry.new_part_name, replica); +} + +void StorageReplicatedMergeTree::queueUpdatingThread() +{ + while (!shutdown_called) + { + pullLogsToQueue(); + optimizeQueue(); + std::this_thread::sleep_for(QUEUE_UPDATE_SLEEP); + } +} + +void StorageReplicatedMergeTree::queueThread() +{ + while (!shutdown_called) + { + LogEntry entry; + bool empty; + + { + Poco::ScopedLock lock(queue_mutex); + empty = queue.empty(); + if (!empty) + { + entry = queue.front(); + queue.pop_front(); + } + } + + if (empty) + { + std::this_thread::sleep_for(QUEUE_NO_WORK_SLEEP); + continue; + } + + bool success = false; + + try + { + executeLogEntry(entry); + + success = true; + } + catch (const Exception & e) + { + LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl + << std::endl + << "Stack trace:" << std::endl + << e.getStackTrace().toString()); + } + catch (const Poco::Exception & e) + { + LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); + } + catch (const std::exception & e) + { + LOG_ERROR(log, "std::exception: " << e.what()); + } + catch (...) + { + LOG_ERROR(log, "Unknown exception"); + } + + if (shutdown_called) + break; + + if (success) + { + std::this_thread::sleep_for(QUEUE_AFTER_WORK_SLEEP); + } + else + { + { + /// Добавим действие, которое не получилось выполнить, в конец очереди. + Poco::ScopedLock lock(queue_mutex); + queue.push_back(entry); + } + std::this_thread::sleep_for(QUEUE_ERROR_SLEEP); + } + } +} + +String StorageReplicatedMergeTree::findActiveReplicaHavingPart(const String & part_name) +{ + Strings replicas = zookeeper.getChildren(zookeeper_path + "/replicas"); + + /// Из реплик, у которых есть кусок, выберем одну равновероятно. + std::random_shuffle(replicas.begin(), replicas.end()); + + for (const String & replica : replicas) + { + if (zookeeper.exists(zookeeper_path + "/replicas/" + replica + "/parts/" + part_name) && + zookeeper.exists(zookeeper_path + "/replicas/" + replica + "/is_active")) + return replica; + } + + throw Exception("No active replica has part " + part_name, ErrorCodes::NO_REPLICA_HAS_PART); +} + +void StorageReplicatedMergeTree::fetchPart(const String & part_name, const String & replica_name) +{ + auto table_lock = lockStructure(true); + + String host; + int port; + + String host_port_str = zookeeper.get(zookeeper_path + "/replicas/" + replica_name + "/host"); + ReadBufferFromString buf(host_port_str); + assertString("host: ", buf); + readString(host, buf); + assertString("\nport: ", buf); + readText(port, buf); + assertString("\n", buf); + assertEOF(buf); + + MergeTreeData::MutableDataPartPtr part = fetcher.fetchPart(name, zookeeper_path + "/replicas/" + replica_name, host, port); + data.renameTempPartAndAdd(part, nullptr); + + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name, + "", + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name + "/checksums", + part->checksums.toString(), + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + zookeeper.multi(ops); +} void StorageReplicatedMergeTree::shutdown() { @@ -308,7 +466,11 @@ void StorageReplicatedMergeTree::shutdown() replica_is_active_node = nullptr; endpoint_holder = nullptr; - /// Кажется, чтобы был невозможен дедлок, тут придется дождаться удаления MyInterserverIOEndpoint. + LOG_TRACE(log, "Waiting for threads to finish"); + queue_updating_thread.join(); + for (auto & thread : queue_threads) + thread.join(); + LOG_TRACE(log, "Threads finished"); } StorageReplicatedMergeTree::~StorageReplicatedMergeTree() @@ -345,6 +507,8 @@ BlockOutputStreamPtr StorageReplicatedMergeTree::write(ASTPtr query) void StorageReplicatedMergeTree::drop() { + shutdown(); + replica_is_active_node = nullptr; zookeeper.removeRecursive(replica_path); if (zookeeper.getChildren(zookeeper_path + "/replicas").empty()) From 50ba6ca8e1e5a41e8367e307f589dbb13fd97a05 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 3 Apr 2014 16:49:01 +0400 Subject: [PATCH 135/281] Merge --- .../ReplicatedMergeTreeBlockOutputStream.h | 2 +- dbms/src/Interpreters/loadMetadata.cpp | 3 +- .../Storages/StorageReplicatedMergeTree.cpp | 97 +++++++++++++------ 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index 87175a54f43..cad531aa01d 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -22,7 +22,7 @@ public: String block_id = insert_id.empty() ? "" : insert_id + "__" + toString(block_index); AbandonableLockInZooKeeper block_number_lock( - storage.zookeeper_path + "/block-numbers/block-", + storage.zookeeper_path + "/block_numbers/block-", storage.zookeeper_path + "/temp", storage.zookeeper); UInt64 part_number = block_number_lock.getNumber(); diff --git a/dbms/src/Interpreters/loadMetadata.cpp b/dbms/src/Interpreters/loadMetadata.cpp index 97812b819f8..fe364b6a92d 100644 --- a/dbms/src/Interpreters/loadMetadata.cpp +++ b/dbms/src/Interpreters/loadMetadata.cpp @@ -133,7 +133,8 @@ void loadMetadata(Context & context) } catch (const Exception & e) { - throw Exception("Cannot create table from metadata file " + tables[j] + ", error: " + e.displayText(), + throw Exception("Cannot create table from metadata file " + tables[j] + ", error: " + e.displayText() + + ", stack trace:\n" + e.getStackTrace().toString(), ErrorCodes::CANNOT_CREATE_TABLE_FROM_METADATA); } } diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 9c8a9c5f499..2e675bbe627 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -129,7 +129,7 @@ void StorageReplicatedMergeTree::createTable() /// Создадим нужные "директории". zookeeper.create(zookeeper_path + "/replicas", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/blocks", "", zkutil::CreateMode::Persistent); - zookeeper.create(zookeeper_path + "/block-numbers", "", zkutil::CreateMode::Persistent); + zookeeper.create(zookeeper_path + "/block_numbers", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/temp", "", zkutil::CreateMode::Persistent); } @@ -249,7 +249,7 @@ void StorageReplicatedMergeTree::loadQueue() std::sort(children.begin(), children.end()); for (const String & child : children) { - String s = zookeeper.get(child); + String s = zookeeper.get(replica_path + "/queue/" + child); LogEntry entry = LogEntry::parse(s); entry.znode_name = child; queue.push_back(entry); @@ -260,47 +260,88 @@ void StorageReplicatedMergeTree::pullLogsToQueue() { Poco::ScopedLock lock(queue_mutex); + /// Сольем все логи в хронологическом порядке. + + struct LogIterator + { + String replica; /// Имя реплики. + UInt64 index; /// Номер записи в логе (суффикс имени ноды). + + Int64 timestamp; /// Время (czxid) создания записи в логе. + String entry_str; /// Сама запись. + + bool operator<(const LogIterator & rhs) const + { + return timestamp < rhs.timestamp; + } + + bool readEntry(zkutil::ZooKeeper & zookeeper, const String & zookeeper_path) + { + String index_str = toString(index); + while (index_str.size() < 10) + index_str = '0' + index_str; + zkutil::Stat stat; + if (!zookeeper.tryGet(zookeeper_path + "/replicas/" + replica + "/log/log-" + index_str, entry_str, &stat)) + return false; + timestamp = stat.getczxid(); + return true; + } + }; + + typedef std::priority_queue PriorityQueue; + PriorityQueue priority_queue; + Strings replicas = zookeeper.getChildren(zookeeper_path + "/replicas"); + for (const String & replica : replicas) { - String log_path = zookeeper_path + "/" + replica + "/log"; + String index_str; + UInt64 index; - String pointer_str; - UInt64 pointer; - - if (zookeeper.tryGet(replica_path + "/log_pointers/" + replica, pointer_str)) + if (zookeeper.tryGet(replica_path + "/log_pointers/" + replica, index_str)) { - pointer = Poco::NumberParser::parseUnsigned64(pointer_str); + index = Poco::NumberParser::parseUnsigned64(index_str); } else { /// Если у нас еще нет указателя на лог этой реплики, поставим указатель на первую запись в нем. - Strings entries = zookeeper.getChildren(log_path); + Strings entries = zookeeper.getChildren(zookeeper_path + "/replicas/" + replica + "/log"); std::sort(entries.begin(), entries.end()); - pointer = entries.empty() ? 0 : Poco::NumberParser::parseUnsigned64(entries[0].substr(strlen("log-"))); + index = entries.empty() ? 0 : Poco::NumberParser::parseUnsigned64(entries[0].substr(strlen("log-"))); - zookeeper.create(replica_path + "/log_pointers/" + replica, toString(pointer), zkutil::CreateMode::Persistent); + zookeeper.create(replica_path + "/log_pointers/" + replica, toString(index), zkutil::CreateMode::Persistent); } - String entry_str; - while (zookeeper.tryGet(log_path + "/log-" + toString(pointer), entry_str)) - { - LogEntry entry = LogEntry::parse(entry_str); + LogIterator iterator; + iterator.replica = replica; + iterator.index = index; - /// Одновременно добавим запись в очередь и продвинем указатель на лог. - zkutil::Ops ops; - ops.push_back(new zkutil::Op::Create( - replica_path + "/queue/queue-", entry_str, zookeeper.getDefaultACL(), zkutil::CreateMode::PersistentSequential)); - ops.push_back(new zkutil::Op::SetData( - replica_path + "/log_pointers/" + replica, toString(pointer + 1), -1)); - auto results = zookeeper.multi(ops); + if (iterator.readEntry(zookeeper, zookeeper_path)) + priority_queue.push(iterator); + } - String path_created = dynamic_cast((*results)[0]).getPathCreated(); - entry.znode_name = path.substr(path.find_last_of('/') + 1); - queue.push_back(entry); + while (!priority_queue.empty()) + { + LogIterator iterator = priority_queue.top(); + priority_queue.pop(); - ++pointer; - } + LogEntry entry = LogEntry::parse(iterator.entry_str); + + /// Одновременно добавим запись в очередь и продвинем указатель на лог. + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/queue/queue-", iterator.entry_str, zookeeper.getDefaultACL(), zkutil::CreateMode::PersistentSequential)); + ops.push_back(new zkutil::Op::SetData( + replica_path + "/log_pointers/" + iterator.replica, toString(iterator.index + 1), -1)); + auto results = zookeeper.multi(ops); + + String path_created = dynamic_cast((*results)[0]).getPathCreated(); + entry.znode_name = path.substr(path.find_last_of('/') + 1); + queue.push_back(entry); + + ++iterator.index; + if (iterator.readEntry(zookeeper, zookeeper_path)) + priority_queue.push(iterator); } } @@ -441,7 +482,7 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin assertString("\n", buf); assertEOF(buf); - MergeTreeData::MutableDataPartPtr part = fetcher.fetchPart(name, zookeeper_path + "/replicas/" + replica_name, host, port); + MergeTreeData::MutableDataPartPtr part = fetcher.fetchPart(part_name, zookeeper_path + "/replicas/" + replica_name, host, port); data.renameTempPartAndAdd(part, nullptr); zkutil::Ops ops; From b00c303f7d223273419d5a112e0384c6ea8fcc91 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 3 Apr 2014 17:11:11 +0400 Subject: [PATCH 136/281] Merge --- dbms/src/Storages/StorageReplicatedMergeTree.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 2e675bbe627..78be3dec93b 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -188,7 +188,19 @@ void StorageReplicatedMergeTree::activateReplica() zkutil::Ops ops; ops.push_back(new zkutil::Op::Create(replica_path + "/is_active", "", zookeeper.getDefaultACL(), zkutil::CreateMode::Ephemeral)); ops.push_back(new zkutil::Op::SetData(replica_path + "/host", host.str(), -1)); - zookeeper.multi(ops); + + try + { + zookeeper.multi(ops); + } + catch (zkutil::KeeperException & e) + { + if (e.code == zkutil::ReturnCode::NodeExists) + throw Exception("Replica " + replica_path + " appears to be already active. If you're sure it's not, " + "try again in a minute or remove znode " + replica_path + "/is_active manually", ErrorCodes::REPLICA_IS_ALREADY_ACTIVE); + + throw; + } replica_is_active_node = zkutil::EphemeralNodeHolder::existing(replica_path + "/is_active", zookeeper); } From 95fc39c5598e6f0856e3f0fd9a058f166db9b305 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Thu, 3 Apr 2014 17:30:21 +0400 Subject: [PATCH 137/281] Merge --- dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h | 2 +- dbms/src/Storages/StorageReplicatedMergeTree.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h index 0b429de68c0..d4f5a3c12ef 100644 --- a/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h +++ b/dbms/include/DB/Storages/MergeTree/AbandonableLockInZooKeeper.h @@ -28,7 +28,7 @@ public: : zookeeper(zookeeper_), path_prefix(path_prefix_) { /// Создадим вспомогательную эфемерную ноду. - holder_path = zookeeper.create(temp_path + "/abandonable-lock-", "", zkutil::CreateMode::EphemeralSequential); + holder_path = zookeeper.create(temp_path + "/abandonable_lock-", "", zkutil::CreateMode::EphemeralSequential); /// Запишем в основную ноду путь к вспомогательной. path = zookeeper.create(path_prefix, holder_path, zkutil::CreateMode::PersistentSequential); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 78be3dec93b..61f51a781b3 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -419,6 +419,7 @@ void StorageReplicatedMergeTree::queueThread() try { executeLogEntry(entry); + zookeeper.remove(replica_path + "/queue/" + entry.znode_name); success = true; } From 00d5cfbe45acdd80ae90f9ef687059b644dac206 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Thu, 3 Apr 2014 19:57:36 +0400 Subject: [PATCH 138/281] dbms: added test for alter [#METR-10550] --- .../queries/0_stateless/00030_1_alter_table.reference | 10 ++++++++++ .../tests/queries/0_stateless/00030_1_alter_table.sql | 11 +++++++++++ .../queries/0_stateless/00030_2_alter_table.reference | 7 +++++++ .../tests/queries/0_stateless/00030_2_alter_table.sql | 8 ++++++++ .../queries/0_stateless/00030_3_alter_table.reference | 1 + .../tests/queries/0_stateless/00030_3_alter_table.sql | 3 +++ 6 files changed, 40 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00030_1_alter_table.reference create mode 100644 dbms/tests/queries/0_stateless/00030_1_alter_table.sql create mode 100644 dbms/tests/queries/0_stateless/00030_2_alter_table.reference create mode 100644 dbms/tests/queries/0_stateless/00030_2_alter_table.sql create mode 100644 dbms/tests/queries/0_stateless/00030_3_alter_table.reference create mode 100644 dbms/tests/queries/0_stateless/00030_3_alter_table.sql diff --git a/dbms/tests/queries/0_stateless/00030_1_alter_table.reference b/dbms/tests/queries/0_stateless/00030_1_alter_table.reference new file mode 100644 index 00000000000..9fda58741f4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_1_alter_table.reference @@ -0,0 +1,10 @@ +CounterID UInt32 +StartDate Date +UserID UInt32 +VisitID UInt32 +NestedColumn.A Array(UInt8) +NestedColumn.S Array(String) +ToDrop UInt32 +Added0 UInt32 +Added1 UInt32 +Added2 UInt32 diff --git a/dbms/tests/queries/0_stateless/00030_1_alter_table.sql b/dbms/tests/queries/0_stateless/00030_1_alter_table.sql new file mode 100644 index 00000000000..77d25d15cb4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_1_alter_table.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS alter_test; + +CREATE TABLE alter_test (CounterID UInt32, StartDate Date, UserID UInt32, VisitID UInt32, NestedColumn Nested(A UInt8, S String), ToDrop UInt32) ENGINE = MergeTree(StartDate, intHash32(UserID), (CounterID, StartDate, intHash32(UserID), VisitID), 8192); + +INSERT INTO alter_test VALUES (1, '2014-01-01', 2, 3, [1,2,3], ['a','b','c'], 4); + +ALTER TABLE alter_test ADD COLUMN Added0 UInt32; +ALTER TABLE alter_test ADD COLUMN Added2 UInt32; +ALTER TABLE alter_test ADD COLUMN Added1 UInt32 AFTER Added0; + +DESC TABLE alter_test; diff --git a/dbms/tests/queries/0_stateless/00030_2_alter_table.reference b/dbms/tests/queries/0_stateless/00030_2_alter_table.reference new file mode 100644 index 00000000000..8c47f345849 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_2_alter_table.reference @@ -0,0 +1,7 @@ +CounterID UInt32 +StartDate Date +UserID UInt32 +VisitID UInt32 +Added0 String +Added1 UInt32 +Added2 UInt32 diff --git a/dbms/tests/queries/0_stateless/00030_2_alter_table.sql b/dbms/tests/queries/0_stateless/00030_2_alter_table.sql new file mode 100644 index 00000000000..d6fd3cf4a4a --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_2_alter_table.sql @@ -0,0 +1,8 @@ +ALTER TABLE alter_test DROP COLUMN ToDrop; + +ALTER TABLE alter_test MODIFY COLUMN Added0 String; + +ALTER TABLE alter_test DROP COLUMN `NestedColumn.A`; +ALTER TABLE alter_test DROP COLUMN `NestedColumn.S`; + +DESC TABLE alter_test; diff --git a/dbms/tests/queries/0_stateless/00030_3_alter_table.reference b/dbms/tests/queries/0_stateless/00030_3_alter_table.reference new file mode 100644 index 00000000000..123652833e9 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_3_alter_table.reference @@ -0,0 +1 @@ +1 2014-01-01 2 3 0 0 diff --git a/dbms/tests/queries/0_stateless/00030_3_alter_table.sql b/dbms/tests/queries/0_stateless/00030_3_alter_table.sql new file mode 100644 index 00000000000..d7ecb07f159 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00030_3_alter_table.sql @@ -0,0 +1,3 @@ +SELECT * FROM alter_test; + +DROP TABLE alter_test; From 8a834d3025f67a4d703b1295e7f28997c794d0db Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 4 Apr 2014 06:27:01 +0400 Subject: [PATCH 139/281] dbms: added checking of max_query_size [#METR-10718]. --- dbms/include/DB/Core/ErrorCodes.h | 1 + dbms/src/Interpreters/executeQuery.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index a5a36a09f18..376746811a4 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -230,6 +230,7 @@ namespace ErrorCodes NO_FILE_IN_DATA_PART, UNEXPECTED_FILE_IN_DATA_PART, BAD_SIZE_OF_FILE_IN_DATA_PART, + QUERY_IS_TOO_LARGE, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/src/Interpreters/executeQuery.cpp b/dbms/src/Interpreters/executeQuery.cpp index 9b84d14198b..8a1a5a8733a 100644 --- a/dbms/src/Interpreters/executeQuery.cpp +++ b/dbms/src/Interpreters/executeQuery.cpp @@ -76,7 +76,13 @@ void executeQuery( /// Засунем запрос в строку. Она выводится в лог и в processlist. Если запрос INSERT, то не будем включать данные для вставки. auto insert = dynamic_cast(&*ast); - String query(begin, (insert && insert->data) ? (insert->data - begin) : (pos - begin)); + size_t query_size = (insert && insert->data) ? (insert->data - begin) : (pos - begin); + + if (query_size > max_query_size) + throw Exception("Query is too large (" + toString(query_size) + ")." + " max_query_size = " + toString(max_query_size), ErrorCodes::QUERY_IS_TOO_LARGE); + + String query(begin, query_size); LOG_DEBUG(&Logger::get("executeQuery"), query); From 8d736e91c74b0496d7922de7fed816f3f22d9250 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 4 Apr 2014 06:32:35 +0400 Subject: [PATCH 140/281] dbms: fixed reading past end of buffer [#METR-10718]. --- dbms/src/Parsers/ExpressionElementParsers.cpp | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/dbms/src/Parsers/ExpressionElementParsers.cpp b/dbms/src/Parsers/ExpressionElementParsers.cpp index 92ae0fba3dc..46a4202f2ea 100644 --- a/dbms/src/Parsers/ExpressionElementParsers.cpp +++ b/dbms/src/Parsers/ExpressionElementParsers.cpp @@ -280,33 +280,44 @@ bool ParserNumber::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char *& ex if (pos == end) return false; + /** Максимальная длина числа. 319 символов достаточно, чтобы записать максимальный double в десятичной форме. + * Лишнее копирование нужно, чтобы воспользоваться функциями strto*, которым нужна 0-терминированная строка. + */ + char buf[320]; + + size_t bytes_to_copy = end - pos < 319 ? end - pos : 319; + memcpy(buf, pos, bytes_to_copy); + buf[bytes_to_copy] = 0; + + char * pos_double = buf; errno = 0; /// Функции strto* не очищают errno. - Float64 float_value = std::strtod(pos, const_cast(&pos)); - if (pos == begin || errno == ERANGE) + Float64 float_value = std::strtod(buf, &pos_double); + if (pos_double == buf || errno == ERANGE) { - expected = "number (this cause range error)"; + expected = "number"; return false; } res = float_value; /// попробуем использовать более точный тип - UInt64 или Int64 - Pos pos_integer = begin; + char * pos_integer = buf; if (float_value < 0) { errno = 0; - Int64 int_value = std::strtoll(pos_integer, const_cast(&pos_integer), 0); - if (pos_integer == pos && errno != ERANGE) + Int64 int_value = std::strtoll(buf, &pos_integer, 0); + if (pos_integer == pos_double && errno != ERANGE) res = int_value; } else { errno = 0; - UInt64 uint_value = std::strtoull(pos_integer, const_cast(&pos_integer), 0); - if (pos_integer == pos && errno != ERANGE) + UInt64 uint_value = std::strtoull(buf, &pos_integer, 0); + if (pos_integer == pos_double && errno != ERANGE) res = uint_value; } + pos += pos_double - buf; node = new ASTLiteral(StringRange(begin, pos), res); return true; } From 90200cccf59d96a49233b7fa284f6ce8bb08b216 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 4 Apr 2014 06:35:46 +0400 Subject: [PATCH 141/281] dbms: added test [#METR-10718]. --- dbms/tests/queries/0_stateless/00031_parser_number.reference | 3 +++ dbms/tests/queries/0_stateless/00031_parser_number.sql | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 dbms/tests/queries/0_stateless/00031_parser_number.reference create mode 100644 dbms/tests/queries/0_stateless/00031_parser_number.sql diff --git a/dbms/tests/queries/0_stateless/00031_parser_number.reference b/dbms/tests/queries/0_stateless/00031_parser_number.reference new file mode 100644 index 00000000000..5427720faea --- /dev/null +++ b/dbms/tests/queries/0_stateless/00031_parser_number.reference @@ -0,0 +1,3 @@ +0 1 -1 128 -127 -128 255 -128 255 -127 65535 4294967295 12300 4656 -0 -0 0 18446744073709551615 2.09883e+19 -1.84467e+19 -9223372036854775807 -8.98847e+307 -5.56268e-309 inf -inf nan -nan 1e-302 UInt8 UInt8 Int8 UInt8 Int8 Int8 UInt8 Int8 UInt8 Int8 UInt16 UInt32 Float64 Float64 Float64 Float64 UInt8 UInt64 Float64 Float64 Int64 Float64 Float64 Float64 Float64 Float64 Float32 Float64 +1e+308 +-1e-307 diff --git a/dbms/tests/queries/0_stateless/00031_parser_number.sql b/dbms/tests/queries/0_stateless/00031_parser_number.sql new file mode 100644 index 00000000000..65db3211465 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00031_parser_number.sql @@ -0,0 +1,3 @@ +SELECT 0 AS x1, 1 AS x2, -1 AS x3, 128 AS x4, -127 AS x5, -128 AS x6, 0xFF AS x7, -0x80 AS x8, 0377 AS x9, -0177 AS x10, 0xFFFF AS x11, 0xFFFFFFFF AS x12, 123e2 AS x13, 0x123p4 AS x14, -0. AS x15, -.0 AS x16, 0. AS x17, 0xFFFFFFFFFFFFFFFF AS x18, 0x123456789ABCDEF01 AS x19, -0xFFFFFFFFFFFFFFFF AS x20, -0x7FFFFFFFFFFFFFFF AS x21, -0x1P1023 AS x22, -0x1p-1024 AS x23, inf AS x24, -INF AS x25, nan AS x26, 0 / 0 AS x27, 0.01e-300 AS x28, toTypeName(x1), toTypeName(x2), toTypeName(x3), toTypeName(x4), toTypeName(x5), toTypeName(x6), toTypeName(x7), toTypeName(x8), toTypeName(x9), toTypeName(x10), toTypeName(x11), toTypeName(x12), toTypeName(x13), toTypeName(x14), toTypeName(x15), toTypeName(x16), toTypeName(x17), toTypeName(x18), toTypeName(x19), toTypeName(x20), toTypeName(x21), toTypeName(x22), toTypeName(x23), toTypeName(x24), toTypeName(x25), toTypeName(x26), toTypeName(x27), toTypeName(x28); +SELECT 0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; +SELECT -0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001; From 847128152e7ff78f80b93d99b6328c4d234ed4c9 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 4 Apr 2014 14:37:33 +0400 Subject: [PATCH 142/281] Merge --- .../Storages/MergeTree/MergeTreeDataMerger.h | 6 +- .../DB/Storages/StorageReplicatedMergeTree.h | 73 +++++++- .../MergeTree/MergeTreeDataMerger.cpp | 39 +++-- dbms/src/Storages/StorageMergeTree.cpp | 9 +- .../Storages/StorageReplicatedMergeTree.cpp | 156 +++++++++++++++--- .../libzkutil/include/zkutil/LeaderElection.h | 103 ++++++++++++ libs/libzkutil/include/zkutil/ZooKeeper.h | 7 +- 7 files changed, 343 insertions(+), 50 deletions(-) create mode 100644 libs/libzkutil/include/zkutil/LeaderElection.h diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h index 5f10ca5df0e..06626c4a2a9 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h @@ -12,10 +12,11 @@ class MergeTreeDataMerger public: MergeTreeDataMerger(MergeTreeData & data_) : data(data_), log(&Logger::get("MergeTreeDataMerger")), canceled(false) {} - typedef boost::function AllowedMergingPredicate; + typedef std::function AllowedMergingPredicate; /** Выбирает, какие куски слить. Использует кучу эвристик. * Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров. + * Если available_disk_space > 0, выбирает куски так, чтобы места на диске хватило с запасом для их слияния. * * can_merge - функция, определяющая, можно ли объединить пару соседних кусков. * Эта функция должна координировать слияния со вставками и другими слияниями, обеспечивая, что: @@ -24,6 +25,7 @@ public: */ bool selectPartsToMerge( MergeTreeData::DataPartsVector & what, + String & merged_name, size_t available_disk_space, bool merge_anything_for_old_months, bool aggressive, @@ -31,7 +33,7 @@ public: const AllowedMergingPredicate & can_merge); /// Сливает куски. Возвращает название нового куска. Если слияние отменили, возвращает пустую строку. - String mergeParts(const MergeTreeData::DataPartsVector & parts); + String mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name); /// Примерное количество места на диске, нужное для мерджа. С запасом. size_t estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts); diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index a27345e7578..794439413ea 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -5,8 +5,9 @@ #include #include #include -#include "MergeTree/ReplicatedMergeTreePartsExchange.h" +#include #include +#include #include namespace DB @@ -66,6 +67,44 @@ public: private: friend class ReplicatedMergeTreeBlockOutputStream; + struct CurrentlyMergingPartsTagger + { + Strings parts; + StorageReplicatedMergeTree & storage; + + CurrentlyMergingPartsTagger(const Strings & parts_, StorageReplicatedMergeTree & storage_) + : parts(parts_), storage(storage_) + { + Poco::ScopedLock lock(storage.currently_merging_mutex); + for (const auto & name : parts) + { + if (storage.currently_merging.count(name)) + throw Exception("Tagging alreagy tagged part " + name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + } + storage.currently_merging.insert(parts.begin(), parts.end()); + } + + ~CurrentlyMergingPartsTagger() + { + try + { + Poco::ScopedLock lock(storage.currently_merging_mutex); + for (const auto & name : parts) + { + if (!storage.currently_merging.count(name)) + throw Exception("Untagging already untagged part " + name + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + storage.currently_merging.erase(name); + } + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + } + }; + + typedef Poco::SharedPtr CurrentlyMergingPartsTaggerPtr; + struct LogEntry { enum Type @@ -80,6 +119,14 @@ private: String new_part_name; Strings parts_to_merge; + CurrentlyMergingPartsTaggerPtr currently_merging_tagger; + + void tagPartsAsCurrentlyMerging(StorageReplicatedMergeTree & storage) + { + if (type == MERGE_PARTS) + currently_merging_tagger = new CurrentlyMergingPartsTagger(parts_to_merge, storage); + } + void writeText(WriteBuffer & out) const; void readText(ReadBuffer & in); @@ -135,7 +182,9 @@ private: MergeTreeData data; MergeTreeDataSelectExecutor reader; MergeTreeDataWriter writer; + MergeTreeDataMerger merger; ReplicatedMergeTreePartsFetcher fetcher; + zkutil::LeaderElectionPtr leader_election; typedef std::vector Threads; @@ -145,6 +194,15 @@ private: /// Потоки, выполняющие действия из очереди. Threads queue_threads; + /// Поток, выбирающий куски для слияния. + std::thread merge_selecting_thread; + + typedef std::set StringSet; + + /// Куски, для которых в очереди есть задание на слияние. + StringSet currently_merging; + Poco::FastMutex currently_merging_mutex; + Logger * log; volatile bool shutdown_called; @@ -190,7 +248,7 @@ private: */ void checkParts(); - /// Работа с очередью и логом. + /// Выполнение заданий из очереди. /** Кладет в queue записи из ZooKeeper (/replicas/me/queue/). */ @@ -218,6 +276,17 @@ private: */ void queueThread(); + void becomeLeader(); + + /// Выбор кусков для слияния. + + /** В бесконечном цикле выбирается куски для слияния и записывает в лог. + */ + void mergeSelectingThread(); + + /// Вызывается во время выбора кусков для слияния. + bool canMergeParts(const MergeTreeData::DataPartPtr & left, const MergeTreeData::DataPartPtr & right); + /// Обмен кусками. /** Бросает исключение, если куска ни у кого нет. diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 9477d64c280..7fe16b2a79e 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -35,7 +35,7 @@ static const double DISK_USAGE_COEFFICIENT_TO_RESERVE = 1.4; /// 4) Если в одном из потоков идет мердж крупных кусков, то во втором сливать только маленькие кусочки /// 5) С ростом логарифма суммарного размера кусочков в мердже увеличиваем требование сбалансированности -bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & parts, size_t available_disk_space, +bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & parts, String & merged_name, size_t available_disk_space, bool merge_anything_for_old_months, bool aggressive, bool only_small, const AllowedMergingPredicate & can_merge) { LOG_DEBUG(log, "Selecting parts to merge"); @@ -44,6 +44,9 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa DateLUTSingleton & date_lut = DateLUTSingleton::instance(); + if (available_disk_space == 0) + available_disk_space = std::numeric_limits::max(); + size_t min_max = -1U; size_t min_min = -1U; int max_len = 0; @@ -209,13 +212,25 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa { parts.clear(); + DayNum_t left_date = DayNum_t(std::numeric_limits::max()); + DayNum_t right_date = DayNum_t(std::numeric_limits::min()); + UInt32 level = 0; + MergeTreeData::DataParts::iterator it = best_begin; for (int i = 0; i < max_len; ++i) { parts.push_back(*it); + + level = std::max(level, parts[i]->level); + left_date = std::min(left_date, parts[i]->left_date); + right_date = std::max(right_date, parts[i]->right_date); + ++it; } + merged_name = MergeTreeData::getPartName( + left_date, right_date, parts.front()->left, parts.back()->right, level + 1); + LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); } else @@ -228,7 +243,7 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa /// parts должны быть отсортированы. -String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & parts) +String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name) { LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); @@ -237,25 +252,9 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa for (const auto & it : columns_list) all_column_names.push_back(it.first); - DateLUTSingleton & date_lut = DateLUTSingleton::instance(); - MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); - new_data_part->left_date = std::numeric_limits::max(); - new_data_part->right_date = std::numeric_limits::min(); - new_data_part->left = parts.front()->left; - new_data_part->right = parts.back()->right; - new_data_part->level = 0; - for (size_t i = 0; i < parts.size(); ++i) - { - new_data_part->level = std::max(new_data_part->level, parts[i]->level); - new_data_part->left_date = std::min(new_data_part->left_date, parts[i]->left_date); - new_data_part->right_date = std::max(new_data_part->right_date, parts[i]->right_date); - } - ++new_data_part->level; - new_data_part->name = MergeTreeData::getPartName( - new_data_part->left_date, new_data_part->right_date, new_data_part->left, new_data_part->right, new_data_part->level); - new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date); - new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date); + data.parsePartName(merged_name, *new_data_part); + new_data_part->name = merged_name; /** Читаем из всех кусков, сливаем и пишем в новый. * Попутно вычисляем выражение для сортировки. diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index fe3d5be0ffe..28f1bb2bf30 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -145,12 +145,13 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) /// К концу этого логического блока должен быть вызван деструктор, чтобы затем корректно определить удаленные куски /// Нужно вызывать деструктор под незалоченным currently_merging_mutex. CurrentlyMergingPartsTaggerPtr merging_tagger; + String merged_name; { Poco::ScopedLock lock(currently_merging_mutex); MergeTreeData::DataPartsVector parts; - auto can_merge = boost::bind(&StorageMergeTree::canMergeParts, this, _1, _2); + auto can_merge = std::bind(&StorageMergeTree::canMergeParts, this, std::placeholders::_1, std::placeholders::_2); bool only_small = false; /// Если есть активный мердж крупных кусков, то ограничиваемся мерджем только маленьких частей. @@ -163,14 +164,14 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) } } - if (!merger.selectPartsToMerge(parts, disk_space, false, aggressive, only_small, can_merge) && - !merger.selectPartsToMerge(parts, disk_space, true, aggressive, only_small, can_merge)) + if (!merger.selectPartsToMerge(parts, merged_name, disk_space, false, aggressive, only_small, can_merge) && + !merger.selectPartsToMerge(parts, merged_name, disk_space, true, aggressive, only_small, can_merge)) break; merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); } - merger.mergeParts(merging_tagger->parts); + merger.mergeParts(merging_tagger->parts, merged_name); } if (shutdown_called) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 61f51a781b3..2e279982c39 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -13,6 +13,7 @@ const auto QUEUE_UPDATE_SLEEP = std::chrono::seconds(5); const auto QUEUE_NO_WORK_SLEEP = std::chrono::seconds(5); const auto QUEUE_ERROR_SLEEP = std::chrono::seconds(1); const auto QUEUE_AFTER_WORK_SLEEP = std::chrono::seconds(0); +const auto MERGE_SELECTING_SLEEP = std::chrono::seconds(5); StorageReplicatedMergeTree::StorageReplicatedMergeTree( @@ -31,10 +32,10 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( : context(context_), zookeeper(context.getZooKeeper()), path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), zookeeper_path(zookeeper_path_), - replica_name(replica_name_), + replica_name(replica_name_), is_leader_node(false), data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), - reader(data), writer(data), fetcher(data), + reader(data), writer(data), merger(data), fetcher(data), log(&Logger::get("StorageReplicatedMergeTree")), shutdown_called(false) { @@ -62,6 +63,9 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( loadQueue(); activateReplica(); + leader_election = new zkutil::LeaderElection(zookeeper_path + "/leader_election", zookeeper, + std::bind(&StorageReplicatedMergeTree::becomeLeader, this), replica_name); + queue_updating_thread = std::thread(&StorageReplicatedMergeTree::queueUpdatingThread, this); for (size_t i = 0; i < settings_.replication_threads; ++i) queue_threads.push_back(std::thread(&StorageReplicatedMergeTree::queueThread, this)); @@ -126,10 +130,10 @@ void StorageReplicatedMergeTree::createTable() zookeeper.create(zookeeper_path + "/metadata", metadata.str(), zkutil::CreateMode::Persistent); - /// Создадим нужные "директории". zookeeper.create(zookeeper_path + "/replicas", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/blocks", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/block_numbers", "", zkutil::CreateMode::Persistent); + zookeeper.create(zookeeper_path + "/leader_election", "", zkutil::CreateMode::Persistent); zookeeper.create(zookeeper_path + "/temp", "", zkutil::CreateMode::Persistent); } @@ -264,6 +268,7 @@ void StorageReplicatedMergeTree::loadQueue() String s = zookeeper.get(replica_path + "/queue/" + child); LogEntry entry = LogEntry::parse(s); entry.znode_name = child; + entry.tagPartsAsCurrentlyMerging(*this); queue.push_back(entry); } } @@ -349,6 +354,7 @@ void StorageReplicatedMergeTree::pullLogsToQueue() String path_created = dynamic_cast((*results)[0]).getPathCreated(); entry.znode_name = path.substr(path.find_last_of('/') + 1); + entry.tagPartsAsCurrentlyMerging(*this); queue.push_back(entry); ++iterator.index; @@ -385,8 +391,16 @@ void StorageReplicatedMergeTree::queueUpdatingThread() { while (!shutdown_called) { - pullLogsToQueue(); - optimizeQueue(); + try + { + pullLogsToQueue(); + optimizeQueue(); + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + std::this_thread::sleep_for(QUEUE_UPDATE_SLEEP); } } @@ -423,24 +437,9 @@ void StorageReplicatedMergeTree::queueThread() success = true; } - catch (const Exception & e) - { - LOG_ERROR(log, "Code: " << e.code() << ". " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTrace().toString()); - } - catch (const Poco::Exception & e) - { - LOG_ERROR(log, "Poco::Exception: " << e.code() << ". " << e.displayText()); - } - catch (const std::exception & e) - { - LOG_ERROR(log, "std::exception: " << e.what()); - } catch (...) { - LOG_ERROR(log, "Unknown exception"); + tryLogCurrentException(__PRETTY_FUNCTION__); } if (shutdown_called) @@ -462,6 +461,118 @@ void StorageReplicatedMergeTree::queueThread() } } +void StorageReplicatedMergeTree::mergeSelectingThread() +{ + pullLogsToQueue(); + + while (!shutdown_called && is_leader_node) + { + size_t merges_queued = 0; + + { + Poco::ScopedLock lock(queue_mutex); + + for (const auto & entry : queue) + if (entry.type == LogEntry::MERGE_PARTS) + ++merges_queued; + } + + if (merges_queued >= data.settings.merging_threads) + { + std::this_thread::sleep_for(MERGE_SELECTING_SLEEP); + continue; + } + + /// Есть ли активный мердж крупных кусков. + bool has_big_merge = false; + + { + Poco::ScopedLock lock(currently_merging_mutex); + + for (const auto & name : currently_merging) + { + MergeTreeData::DataPartPtr part = data.getContainingPart(name); + if (!part) + continue; + if (part->name != name) + throw Exception("Assertion failed in mergeSelectingThread().", ErrorCodes::LOGICAL_ERROR); + if (part->size * data.index_granularity > 25 * 1024 * 1024) + { + has_big_merge = true; + break; + } + } + } + + bool success = false; + + try + { + Poco::ScopedLock lock(currently_merging_mutex); + + MergeTreeData::DataPartsVector parts; + String merged_name; + auto can_merge = std::bind(&StorageReplicatedMergeTree::canMergeParts, this, std::placeholders::_1, std::placeholders::_2); + + if (merger.selectPartsToMerge(parts, merged_name, 0, false, false, has_big_merge, can_merge) || + merger.selectPartsToMerge(parts, merged_name, 0, true, false, has_big_merge, can_merge)) + { + LogEntry entry; + entry.type = LogEntry::MERGE_PARTS; + entry.new_part_name = merged_name; + + for (const auto & part : parts) + { + entry.parts_to_merge.push_back(part->name); + } + + zookeeper.create(replica_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); + + /// Нужно загрузить эту запись в очередь перед тем, как в следующий раз выбирать куски для слияния. + pullLogsToQueue(); + + success = true; + } + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + + if (shutdown_called) + break; + + if (!success) + std::this_thread::sleep_for(QUEUE_AFTER_WORK_SLEEP); + } +} + +bool StorageReplicatedMergeTree::canMergeParts(const MergeTreeData::DataPartPtr & left, const MergeTreeData::DataPartPtr & right) +{ + if (currently_merging.count(left->name) || currently_merging.count(right->name)) + return false; + + /// Можно слить куски, если все номера между ними заброшены - не соответствуют никаким блокам. + for (UInt64 number = left->right + 1; number <= right->left - 1; ++number) + { + String number_str = toString(number); + while (number_str.size() < 10) + number_str = '0' + number_str; + String path = zookeeper_path + "/block_numbers/block-" + number_str; + + if (AbandonableLockInZooKeeper::check(path, zookeeper) != AbandonableLockInZooKeeper::ABANDONED) + return false; + } + + return true; +} + +void StorageReplicatedMergeTree::becomeLeader() +{ + is_leader_node = true; + merge_selecting_thread = std::thread(&StorageReplicatedMergeTree::mergeSelectingThread, this); +} + String StorageReplicatedMergeTree::findActiveReplicaHavingPart(const String & part_name) { Strings replicas = zookeeper.getChildren(zookeeper_path + "/replicas"); @@ -516,11 +627,14 @@ void StorageReplicatedMergeTree::shutdown() { if (shutdown_called) return; + leader_election = nullptr; shutdown_called = true; replica_is_active_node = nullptr; endpoint_holder = nullptr; LOG_TRACE(log, "Waiting for threads to finish"); + if (is_leader_node) + merge_selecting_thread.join(); queue_updating_thread.join(); for (auto & thread : queue_threads) thread.join(); diff --git a/libs/libzkutil/include/zkutil/LeaderElection.h b/libs/libzkutil/include/zkutil/LeaderElection.h new file mode 100644 index 00000000000..12f1fe92e3f --- /dev/null +++ b/libs/libzkutil/include/zkutil/LeaderElection.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include + + +namespace zkutil +{ + +/** Реализует метод выбора лидера, описанный здесь: http://zookeeper.apache.org/doc/r3.4.5/recipes.html#sc_leaderElection + */ +class LeaderElection +{ +public: + typedef std::function LeadershipHandler; + + /** handler вызывается, когда этот экземпляр становится лидером. + */ + LeaderElection(const std::string & path_, ZooKeeper & zookeeper_, LeadershipHandler handler_, const std::string & identifier_ = "") + : path(path_), zookeeper(zookeeper_), handler(handler_), identifier(identifier_), + shutdown(false), log(&Logger::get("LeaderElection")) + { + node = EphemeralNodeHolder::createSequential(path + "/leader_election-", zookeeper, identifier); + + std::string node_path = node->getPath(); + node_name = node_path.substr(node_path.find_last_of('/') + 1); + + thread = std::thread(&LeaderElection::threadFunction, this); + } + + ~LeaderElection() + { + shutdown = true; + thread.join(); + } + +private: + std::string path; + ZooKeeper & zookeeper; + LeadershipHandler handler; + std::string identifier; + + EphemeralNodeHolderPtr node; + std::string node_name; + + std::thread thread; + volatile bool shutdown; + + Logger * log; + + void threadFunction() + { + try + { + while (!shutdown) + { + Strings children = zookeeper.getChildren(path); + std::sort(children.begin(), children.end()); + auto it = std::lower_bound(children.begin(), children.end(), node_name); + if (it == children.end() || *it != node_name) + throw Poco::Exception("Assertion failed in LeaderElection"); + + if (it == children.begin()) + { + handler(); + return; + } + + WatchFuture future; + if (zookeeper.exists(*(it - 1), nullptr, &future)) + { + while (!shutdown) + if (future.wait_for(std::chrono::seconds(2)) != std::future_status::timeout) + break; + } + } + } + catch (const DB::Exception & e) + { + LOG_ERROR(log, "Exception in LeaderElection: Code: " << e.code() << ". " << e.displayText() << std::endl + << std::endl + << "Stack trace:" << std::endl + << e.getStackTrace().toString()); + } + catch (const Poco::Exception & e) + { + LOG_ERROR(log, "Poco::Exception in LeaderElection: " << e.code() << ". " << e.displayText()); + } + catch (const std::exception & e) + { + LOG_ERROR(log, "std::exception in LeaderElection: " << e.what()); + } + catch (...) + { + LOG_ERROR(log, "Unknown exception in LeaderElection"); + } + } +}; + +typedef Poco::SharedPtr LeaderElectionPtr; + +} diff --git a/libs/libzkutil/include/zkutil/ZooKeeper.h b/libs/libzkutil/include/zkutil/ZooKeeper.h index bb1005d5ff9..b5112b326e3 100644 --- a/libs/libzkutil/include/zkutil/ZooKeeper.h +++ b/libs/libzkutil/include/zkutil/ZooKeeper.h @@ -144,7 +144,12 @@ public: : path(path_), zookeeper(zookeeper_) { if (create) - zookeeper.create(path, data, sequential ? CreateMode::EphemeralSequential : CreateMode::Ephemeral); + path = zookeeper.create(path, data, sequential ? CreateMode::EphemeralSequential : CreateMode::Ephemeral); + } + + std::string getPath() const + { + return path; } static Ptr create(const std::string & path, ZooKeeper & zookeeper, const std::string & data = "") From 1b5b9a1f1e46915d73e51e5f6b18d75c4798eafc Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 4 Apr 2014 16:47:57 +0400 Subject: [PATCH 143/281] Merge --- .../ReplicatedMergeTreePartsExchange.h | 1 - .../DB/Storages/StorageReplicatedMergeTree.h | 20 ++++----- .../MergeTree/MergeTreeDataMerger.cpp | 6 --- dbms/src/Storages/StorageMergeTree.cpp | 5 +++ .../Storages/StorageReplicatedMergeTree.cpp | 43 +++++++++++++++---- 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h index 707839ac661..b3662650795 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreePartsExchange.h @@ -76,7 +76,6 @@ public: const String & host, int port) { - LOG_TRACE(log, "Fetching part " << part_name); ReadBufferFromHTTP::Params params = { std::make_pair("endpoint", "ReplicatedMergeTree:" + replica_path), std::make_pair("part", part_name), diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 794439413ea..efb46e84947 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -42,7 +42,7 @@ public: return "Replicated" + data.getModePrefix() + "MergeTree"; } - std::string getTableName() const override { return name; } + std::string getTableName() const override { return table_name; } std::string getSignColumnName() const { return data.getSignColumnName(); } bool supportsSampling() const override { return data.supportsSampling(); } bool supportsFinal() const override { return data.supportsFinal(); } @@ -152,17 +152,23 @@ private: typedef std::list LogEntries; + typedef std::set StringSet; + typedef std::vector Threads; + Context & context; zkutil::ZooKeeper & zookeeper; + /// Куски, для которых в очереди есть задание на слияние. + StringSet currently_merging; + Poco::FastMutex currently_merging_mutex; + /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из ZooKeeper (/replicas/me/queue/). * В ZK записи в хронологическом порядке. Здесь записи в том порядке, в котором их лучше выполнять. */ LogEntries queue; Poco::FastMutex queue_mutex; - String path; - String name; + String table_name; String full_path; String zookeeper_path; @@ -186,8 +192,6 @@ private: ReplicatedMergeTreePartsFetcher fetcher; zkutil::LeaderElectionPtr leader_election; - typedef std::vector Threads; - /// Поток, следящий за обновлениями в логах всех реплик и загружающий их в очередь. std::thread queue_updating_thread; @@ -197,12 +201,6 @@ private: /// Поток, выбирающий куски для слияния. std::thread merge_selecting_thread; - typedef std::set StringSet; - - /// Куски, для которых в очереди есть задание на слияние. - StringSet currently_merging; - Poco::FastMutex currently_merging_mutex; - Logger * log; volatile bool shutdown_called; diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 7fe16b2a79e..97466f115ec 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -38,8 +38,6 @@ static const double DISK_USAGE_COEFFICIENT_TO_RESERVE = 1.4; bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & parts, String & merged_name, size_t available_disk_space, bool merge_anything_for_old_months, bool aggressive, bool only_small, const AllowedMergingPredicate & can_merge) { - LOG_DEBUG(log, "Selecting parts to merge"); - MergeTreeData::DataParts data_parts = data.getDataParts(); DateLUTSingleton & date_lut = DateLUTSingleton::instance(); @@ -233,10 +231,6 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa LOG_DEBUG(log, "Selected " << parts.size() << " parts from " << parts.front()->name << " to " << parts.back()->name); } - else - { - LOG_DEBUG(log, "No parts to merge"); - } return found; } diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index 28f1bb2bf30..038bc5aea31 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -164,9 +164,14 @@ void StorageMergeTree::mergeThread(bool while_can, bool aggressive) } } + LOG_DEBUG(log, "Selecting parts to merge"); + if (!merger.selectPartsToMerge(parts, merged_name, disk_space, false, aggressive, only_small, can_merge) && !merger.selectPartsToMerge(parts, merged_name, disk_space, true, aggressive, only_small, can_merge)) + { + LOG_DEBUG(log, "No parts to merge"); break; + } merging_tagger = new CurrentlyMergingPartsTagger(parts, merger.estimateDiskSpaceForMerge(parts), *this); } diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 2e279982c39..db49f45721c 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -31,12 +31,12 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( const MergeTreeSettings & settings_) : context(context_), zookeeper(context.getZooKeeper()), - path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), zookeeper_path(zookeeper_path_), + table_name(name_), full_path(path_ + escapeForFileName(table_name) + '/'), zookeeper_path(zookeeper_path_), replica_name(replica_name_), is_leader_node(false), data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), reader(data), writer(data), merger(data), fetcher(data), - log(&Logger::get("StorageReplicatedMergeTree")), + log(&Logger::get("StorageReplicatedMergeTree: " + table_name)), shutdown_called(false) { if (!zookeeper_path.empty() && *zookeeper_path.rbegin() == '/') @@ -337,10 +337,13 @@ void StorageReplicatedMergeTree::pullLogsToQueue() priority_queue.push(iterator); } + size_t count = 0; + while (!priority_queue.empty()) { LogIterator iterator = priority_queue.top(); priority_queue.pop(); + ++count; LogEntry entry = LogEntry::parse(iterator.entry_str); @@ -353,7 +356,7 @@ void StorageReplicatedMergeTree::pullLogsToQueue() auto results = zookeeper.multi(ops); String path_created = dynamic_cast((*results)[0]).getPathCreated(); - entry.znode_name = path.substr(path.find_last_of('/') + 1); + entry.znode_name = path_created.substr(path_created.find_last_of('/') + 1); entry.tagPartsAsCurrentlyMerging(*this); queue.push_back(entry); @@ -361,6 +364,9 @@ void StorageReplicatedMergeTree::pullLogsToQueue() if (iterator.readEntry(zookeeper, zookeeper_path)) priority_queue.push(iterator); } + + if (count > 0) + LOG_DEBUG(log, "Pulled " << count << " entries to queue"); } void StorageReplicatedMergeTree::optimizeQueue() @@ -377,14 +383,25 @@ void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) /// Даже если кусок есть локально, его (в исключительных случаях) может не быть в zookeeper. if (containing_part && zookeeper.exists(replica_path + "/parts/" + containing_part->name)) + { + LOG_DEBUG(log, "Skipping action for part " + entry.new_part_name + " - part already exists"); return; + } } - if (entry.type != LogEntry::GET_PART) + if (entry.type == LogEntry::GET_PART) + { + String replica = findActiveReplicaHavingPart(entry.new_part_name); + fetchPart(entry.new_part_name, replica); + } + else if (entry.type == LogEntry::MERGE_PARTS) + { throw Exception("Merging is not implemented.", ErrorCodes::NOT_IMPLEMENTED); - - String replica = findActiveReplicaHavingPart(entry.new_part_name); - fetchPart(entry.new_part_name, replica); + } + else + { + throw Exception("Unexpected log entry type: " + toString(static_cast(entry.type))); + } } void StorageReplicatedMergeTree::queueUpdatingThread() @@ -514,6 +531,8 @@ void StorageReplicatedMergeTree::mergeSelectingThread() String merged_name; auto can_merge = std::bind(&StorageReplicatedMergeTree::canMergeParts, this, std::placeholders::_1, std::placeholders::_2); + LOG_TRACE(log, "Selecting parts to merge" << (has_big_merge ? " (only small)" : "")); + if (merger.selectPartsToMerge(parts, merged_name, 0, false, false, has_big_merge, can_merge) || merger.selectPartsToMerge(parts, merged_name, 0, true, false, has_big_merge, can_merge)) { @@ -543,7 +562,7 @@ void StorageReplicatedMergeTree::mergeSelectingThread() break; if (!success) - std::this_thread::sleep_for(QUEUE_AFTER_WORK_SLEEP); + std::this_thread::sleep_for(MERGE_SELECTING_SLEEP); } } @@ -561,7 +580,10 @@ bool StorageReplicatedMergeTree::canMergeParts(const MergeTreeData::DataPartPtr String path = zookeeper_path + "/block_numbers/block-" + number_str; if (AbandonableLockInZooKeeper::check(path, zookeeper) != AbandonableLockInZooKeeper::ABANDONED) + { + LOG_DEBUG(log, "Can't merge parts " << left->name << " and " << right->name << " because block " << path << " exists"); return false; + } } return true; @@ -569,6 +591,7 @@ bool StorageReplicatedMergeTree::canMergeParts(const MergeTreeData::DataPartPtr void StorageReplicatedMergeTree::becomeLeader() { + LOG_INFO(log, "Became leader"); is_leader_node = true; merge_selecting_thread = std::thread(&StorageReplicatedMergeTree::mergeSelectingThread, this); } @@ -592,6 +615,8 @@ String StorageReplicatedMergeTree::findActiveReplicaHavingPart(const String & pa void StorageReplicatedMergeTree::fetchPart(const String & part_name, const String & replica_name) { + LOG_DEBUG(log, "Fetching part " << part_name << " from " << replica_name); + auto table_lock = lockStructure(true); String host; @@ -621,6 +646,8 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin zookeeper.getDefaultACL(), zkutil::CreateMode::Persistent)); zookeeper.multi(ops); + + LOG_DEBUG(log, "Fetched part"); } void StorageReplicatedMergeTree::shutdown() From 954a97764c85968d235fcfc03c540812cae1d38c Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 4 Apr 2014 17:27:47 +0400 Subject: [PATCH 144/281] Merge --- dbms/include/DB/Core/ErrorCodes.h | 1 + .../Storages/MergeTree/MergeTreeDataMerger.h | 6 ++--- .../MergeTree/MergeTreeDataMerger.cpp | 11 +++----- .../Storages/StorageReplicatedMergeTree.cpp | 26 ++++++++++++++++++- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/dbms/include/DB/Core/ErrorCodes.h b/dbms/include/DB/Core/ErrorCodes.h index b4c68a7d215..e0777343f56 100644 --- a/dbms/include/DB/Core/ErrorCodes.h +++ b/dbms/include/DB/Core/ErrorCodes.h @@ -242,6 +242,7 @@ namespace ErrorCodes BAD_DATA_PART_NAME, NO_REPLICA_HAS_PART, DUPLICATE_DATA_PART, + ABORTED, POCO_EXCEPTION = 1000, STD_EXCEPTION, diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h index 06626c4a2a9..782968c62c4 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h @@ -32,13 +32,13 @@ public: bool only_small, const AllowedMergingPredicate & can_merge); - /// Сливает куски. Возвращает название нового куска. Если слияние отменили, возвращает пустую строку. - String mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name); + /// Сливает куски. Если получившийся кусок оказался пустым, возаращает nullptr. + MergeTreeData::DataPartPtr mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name); /// Примерное количество места на диске, нужное для мерджа. С запасом. size_t estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts); - /** Отменяет все текущие мерджи. Все выполняющиеся сейчас вызовы mergeParts скоро отменят слияние и вернут пустую строку. + /** Отменяет все текущие мерджи. Все выполняющиеся сейчас вызовы mergeParts скоро бросят исключение. * После этого с этим экземпляром ничего делать нельзя. */ void cancelAll() { canceled = true; } diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 97466f115ec..d940a04c9e3 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -237,7 +237,7 @@ bool MergeTreeDataMerger::selectPartsToMerge(MergeTreeData::DataPartsVector & pa /// parts должны быть отсортированы. -String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name) +MergeTreeData::DataPartPtr MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name) { LOG_DEBUG(log, "Merging " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); @@ -298,10 +298,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa to->write(block); if (canceled) - { - LOG_INFO(log, "Canceled merging parts."); - return ""; - } + throw Exception("Canceled merging parts", ErrorCodes::ABORTED); merged_stream->readSuffix(); new_data_part->checksums = to->writeSuffixAndGetChecksums(); @@ -318,7 +315,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa if (0 == to->marksCount()) { LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); - return ""; + return nullptr; } /// Переименовываем кусок. @@ -329,7 +326,7 @@ String MergeTreeDataMerger::mergeParts(const MergeTreeData::DataPartsVector & pa LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); - return new_data_part->name; + return new_data_part; } size_t MergeTreeDataMerger::estimateDiskSpaceForMerge(const MergeTreeData::DataPartsVector & parts) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index db49f45721c..2cd5b6025e6 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -396,7 +396,31 @@ void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) } else if (entry.type == LogEntry::MERGE_PARTS) { - throw Exception("Merging is not implemented.", ErrorCodes::NOT_IMPLEMENTED); + MergeTreeData::DataPartsVector parts; + for (const String & name : entry.parts_to_merge) + { + MergeTreeData::DataPartPtr part = data.getContainingPart(name); + if (!part || part->name != name) + throw Exception("Part to merge doesn't exist: " + name, ErrorCodes::NOT_FOUND_EXPECTED_DATA_PART); + parts.push_back(part); + } + MergeTreeData::DataPartPtr part = merger.mergeParts(parts, entry.new_part_name); + + if (part) + { + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name, + "", + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name + "/checksums", + part->checksums.toString(), + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + zookeeper.multi(ops); + } } else { From 4a45c7c089a03fc86efefe0906b4ae42d293e321 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Fri, 4 Apr 2014 21:20:45 +0400 Subject: [PATCH 145/281] Merge --- .../CollapsingFinalBlockInputStream.h | 2 +- .../Storages/MergeTree/MergeTreeDataMerger.h | 2 +- .../MergeTree/MergeTreeDataMerger.cpp | 4 +- .../Storages/StorageReplicatedMergeTree.cpp | 85 ++++++++++++------- 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h index 33f32d964ce..01ff24b56c2 100644 --- a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h +++ b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h @@ -17,7 +17,7 @@ public: CollapsingFinalBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, const String & sign_column_) : description(description_), sign_column(sign_column_), - log(&Logger::get("CollapsingSortedBlockInputStream")), + log(&Logger::get("CollapsingFinalBlockInputStream")), first(true), count_positive(0), count_negative(0), count_incorrect_data(0), blocks_fetched(0), blocks_output(0) { children.insert(children.end(), inputs_.begin(), inputs_.end()); diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h index 782968c62c4..73aaa6b5dd9 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeDataMerger.h @@ -32,7 +32,7 @@ public: bool only_small, const AllowedMergingPredicate & can_merge); - /// Сливает куски. Если получившийся кусок оказался пустым, возаращает nullptr. + /// Сливает куски. MergeTreeData::DataPartPtr mergeParts(const MergeTreeData::DataPartsVector & parts, const String & merged_name); /// Примерное количество места на диске, нужное для мерджа. С запасом. diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index d940a04c9e3..9448823a1ed 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -305,8 +305,8 @@ MergeTreeData::DataPartPtr MergeTreeDataMerger::mergeParts(const MergeTreeData:: new_data_part->index.swap(to->getIndex()); - /// В обычном режиме строчки не могут удалиться при мердже. - if (0 == to->marksCount() && data.mode == MergeTreeData::Ordinary) + /// Для удобства, даже CollapsingSortedBlockInputStream не может выдать ноль строк. + if (0 == to->marksCount()) throw Exception("Empty part after merge", ErrorCodes::LOGICAL_ERROR); new_data_part->size = to->marksCount(); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 2cd5b6025e6..ed863d41268 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -406,21 +406,27 @@ void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) } MergeTreeData::DataPartPtr part = merger.mergeParts(parts, entry.new_part_name); - if (part) + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name, + "", + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name + "/checksums", + part->checksums.toString(), + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + + for (const auto & part : parts) { - zkutil::Ops ops; - ops.push_back(new zkutil::Op::Create( - replica_path + "/parts/" + part->name, - "", - zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - ops.push_back(new zkutil::Op::Create( - replica_path + "/parts/" + part->name + "/checksums", - part->checksums.toString(), - zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - zookeeper.multi(ops); + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name + "/checksums", -1)); + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name, -1)); } + + zookeeper.multi(ops); + + data.clearOldParts(); } else { @@ -549,32 +555,51 @@ void StorageReplicatedMergeTree::mergeSelectingThread() try { - Poco::ScopedLock lock(currently_merging_mutex); - MergeTreeData::DataPartsVector parts; - String merged_name; - auto can_merge = std::bind(&StorageReplicatedMergeTree::canMergeParts, this, std::placeholders::_1, std::placeholders::_2); - LOG_TRACE(log, "Selecting parts to merge" << (has_big_merge ? " (only small)" : "")); - - if (merger.selectPartsToMerge(parts, merged_name, 0, false, false, has_big_merge, can_merge) || - merger.selectPartsToMerge(parts, merged_name, 0, true, false, has_big_merge, can_merge)) { - LogEntry entry; - entry.type = LogEntry::MERGE_PARTS; - entry.new_part_name = merged_name; + Poco::ScopedLock lock(currently_merging_mutex); - for (const auto & part : parts) + String merged_name; + auto can_merge = std::bind( + &StorageReplicatedMergeTree::canMergeParts, this, std::placeholders::_1, std::placeholders::_2); + + LOG_TRACE(log, "Selecting parts to merge" << (has_big_merge ? " (only small)" : "")); + + if (merger.selectPartsToMerge(parts, merged_name, 0, false, false, has_big_merge, can_merge) || + merger.selectPartsToMerge(parts, merged_name, 0, true, false, has_big_merge, can_merge)) { - entry.parts_to_merge.push_back(part->name); + LogEntry entry; + entry.type = LogEntry::MERGE_PARTS; + entry.new_part_name = merged_name; + + for (const auto & part : parts) + { + entry.parts_to_merge.push_back(part->name); + } + + zookeeper.create(replica_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); + + success = true; } + } - zookeeper.create(replica_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); + /// Нужно загрузить новую запись в очередь перед тем, как в следующий раз выбирать куски для слияния. + /// (чтобы куски пометились как currently_merging). + pullLogsToQueue(); - /// Нужно загрузить эту запись в очередь перед тем, как в следующий раз выбирать куски для слияния. - pullLogsToQueue(); + for (size_t i = 0; i + 1 < parts.size(); ++i) + { + /// Уберем больше не нужные отметки о несуществующих блоках. + for (UInt64 number = parts[i]->right + 1; number <= parts[i + 1]->left - 1; ++number) + { + String number_str = toString(number); + while (number_str.size() < 10) + number_str = '0' + number_str; + String path = zookeeper_path + "/block_numbers/block-" + number_str; - success = true; + zookeeper.tryRemove(path); + } } } catch (...) From 4903752cd0184d6712d29a49a02482aa1b6d1006 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 4 Apr 2014 22:08:01 +0400 Subject: [PATCH 146/281] dbms: added support for 'point of view' argument of functions working with dictionaries [#METR-10713]. --- .../DB/Functions/FunctionsDictionaries.h | 201 ++++++++++++++---- dbms/include/DB/Interpreters/Dictionaries.h | 14 +- dbms/src/Functions/FunctionFactory.cpp | 12 +- 3 files changed, 177 insertions(+), 50 deletions(-) diff --git a/dbms/include/DB/Functions/FunctionsDictionaries.h b/dbms/include/DB/Functions/FunctionsDictionaries.h index 1e7b21518ef..1315dd894e1 100644 --- a/dbms/include/DB/Functions/FunctionsDictionaries.h +++ b/dbms/include/DB/Functions/FunctionsDictionaries.h @@ -119,8 +119,41 @@ struct CategoryHierarchyImpl }; +/** Вспомогательная вещь, позволяющая достать из словаря конкретный словарь, соответствующий точке зрения + * (ключу словаря, передаваемому в аргументе функции). + * Пример: при вызове regionToCountry(x, 'ua'), может быть использован словарь, в котором Крым относится к Украине. + */ +struct RegionsHierarchyGetter +{ + typedef RegionsHierarchies Src; + typedef RegionsHierarchy Dst; + + static const Dst & get(const Src & src, const std::string & key) + { + return src.get(key); + } +}; + +/** Для словарей без поддержки ключей. Ничего не делает. + */ +template +struct IdentityDictionaryGetter +{ + typedef Dict Src; + typedef Dict Dst; + + static const Dst & get(const Src & src, const std::string & key) + { + if (key.empty()) + return src; + else + throw Exception("Dictionary doesn't support 'point of view' keys.", ErrorCodes::BAD_ARGUMENTS); + } +}; + + /// Преобразует идентификатор, используя словарь. -template +template class FunctionTransformWithDictionary : public IFunction { private: @@ -143,9 +176,9 @@ public: /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { - if (arguments.size() != 1) + if (arguments.size() != 1 && arguments.size() != 2) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 1.", + + toString(arguments.size()) + ", should be 1 or 2.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (arguments[0]->getName() != TypeName::get()) @@ -153,12 +186,35 @@ public: + " (must be " + TypeName::get() + ")", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + if (arguments.size() == 2 && arguments[1]->getName() != TypeName::get()) + throw Exception("Illegal type " + arguments[1]->getName() + " of the second ('point of view') argument of function " + getName() + + " (must be " + TypeName::get() + ")", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + return arguments[0]; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { + /// Ключ словаря, определяющий "точку зрения". + std::string dict_key; + + if (arguments.size() == 2) + { + const ColumnConstString * key_col = dynamic_cast(&*block.getByPosition(arguments[1]).column); + + if (!key_col) + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second ('point of view') argument of function " + Name::get() + + ". Must be constant string.", + ErrorCodes::ILLEGAL_COLUMN); + + dict_key = key_col->getData(); + } + + const typename DictGetter::Dst & dict = DictGetter::get(*owned_dict, dict_key); + if (const ColumnVector * col_from = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) { ColumnVector * col_to = new ColumnVector; @@ -169,13 +225,12 @@ public: size_t size = vec_from.size(); vec_to.resize(size); - const Dict & dict = *owned_dict; for (size_t i = 0; i < size; ++i) vec_to[i] = Transform::apply(vec_from[i], dict); } else if (const ColumnConst * col_from = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) { - block.getByPosition(result).column = new ColumnConst(col_from->size(), Transform::apply(col_from->getData(), *owned_dict)); + block.getByPosition(result).column = new ColumnConst(col_from->size(), Transform::apply(col_from->getData(), dict)); } else throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() @@ -186,7 +241,7 @@ public: /// Проверяет принадлежность, используя словарь. -template +template class FunctionIsInWithDictionary : public IFunction { private: @@ -209,9 +264,9 @@ public: /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { - if (arguments.size() != 2) + if (arguments.size() != 2 && arguments.size() != 3) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 1.", + + toString(arguments.size()) + ", should be 2 or 3.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (arguments[0]->getName() != TypeName::get()) @@ -224,12 +279,35 @@ public: + " (must be " + TypeName::get() + ")", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + if (arguments.size() == 3 && arguments[2]->getName() != TypeName::get()) + throw Exception("Illegal type " + arguments[2]->getName() + " of the third ('point of view') argument of function " + getName() + + " (must be " + TypeName::get() + ")", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + return new DataTypeUInt8; } /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { + /// Ключ словаря, определяющий "точку зрения". + std::string dict_key; + + if (arguments.size() == 3) + { + const ColumnConstString * key_col = dynamic_cast(&*block.getByPosition(arguments[2]).column); + + if (!key_col) + throw Exception("Illegal column " + block.getByPosition(arguments[2]).column->getName() + + " of third ('point of view') argument of function " + Name::get() + + ". Must be constant string.", + ErrorCodes::ILLEGAL_COLUMN); + + dict_key = key_col->getData(); + } + + const typename DictGetter::Dst & dict = DictGetter::get(*owned_dict, dict_key); + const ColumnVector * col_vec1 = dynamic_cast *>(&*block.getByPosition(arguments[0]).column); const ColumnVector * col_vec2 = dynamic_cast *>(&*block.getByPosition(arguments[1]).column); const ColumnConst * col_const1 = dynamic_cast *>(&*block.getByPosition(arguments[0]).column); @@ -246,7 +324,6 @@ public: size_t size = vec_from1.size(); vec_to.resize(size); - const Dict & dict = *owned_dict; for (size_t i = 0; i < size; ++i) vec_to[i] = Transform::apply(vec_from1[i], vec_from2[i], dict); } @@ -261,7 +338,6 @@ public: size_t size = vec_from1.size(); vec_to.resize(size); - const Dict & dict = *owned_dict; for (size_t i = 0; i < size; ++i) vec_to[i] = Transform::apply(vec_from1[i], const_from2, dict); } @@ -276,14 +352,13 @@ public: size_t size = vec_from2.size(); vec_to.resize(size); - const Dict & dict = *owned_dict; for (size_t i = 0; i < size; ++i) vec_to[i] = Transform::apply(const_from1, vec_from2[i], dict); } else if (col_const1 && col_const2) { block.getByPosition(result).column = new ColumnConst(col_const1->size(), - Transform::apply(col_const1->getData(), col_const2->getData(), *owned_dict)); + Transform::apply(col_const1->getData(), col_const2->getData(), dict)); } else throw Exception("Illegal columns " + block.getByPosition(arguments[0]).column->getName() @@ -295,7 +370,7 @@ public: /// Получает массив идентификаторов, состоящий из исходного и цепочки родителей. -template +template class FunctionHierarchyWithDictionary : public IFunction { private: @@ -318,15 +393,20 @@ public: /// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение. DataTypePtr getReturnType(const DataTypes & arguments) const { - if (arguments.size() != 1) + if (arguments.size() != 1 && arguments.size() != 2) throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " - + toString(arguments.size()) + ", should be 1.", - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + toString(arguments.size()) + ", should be 1 or 2.", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); if (arguments[0]->getName() != TypeName::get()) throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName() + " (must be " + TypeName::get() + ")", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (arguments.size() == 2 && arguments[1]->getName() != TypeName::get()) + throw Exception("Illegal type " + arguments[1]->getName() + " of the second ('point of view') argument of function " + getName() + + " (must be " + TypeName::get() + ")", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); return new DataTypeArray(arguments[0]); } @@ -334,6 +414,24 @@ public: /// Выполнить функцию над блоком. void execute(Block & block, const ColumnNumbers & arguments, size_t result) { + /// Ключ словаря, определяющий "точку зрения". + std::string dict_key; + + if (arguments.size() == 2) + { + const ColumnConstString * key_col = dynamic_cast(&*block.getByPosition(arguments[1]).column); + + if (!key_col) + throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName() + + " of second ('point of view') argument of function " + Name::get() + + ". Must be constant string.", + ErrorCodes::ILLEGAL_COLUMN); + + dict_key = key_col->getData(); + } + + const typename DictGetter::Dst & dict = DictGetter::get(*owned_dict, dict_key); + if (const ColumnVector * col_from = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) { ColumnVector * col_values = new ColumnVector; @@ -348,7 +446,6 @@ public: res_offsets.resize(size); res_values.reserve(size * 4); - const Dict & dict = *owned_dict; for (size_t i = 0; i < size; ++i) { T cur = vec_from[i]; @@ -364,7 +461,6 @@ public: { Array res; - const Dict & dict = *owned_dict; T cur = col_from->getData(); while (cur) { @@ -402,24 +498,54 @@ struct NameSEHierarchy { static const char * get() { return "SEHierarchy"; } }; struct NameCategoryHierarchy{ static const char * get() { return "categoryHierarchy"; } }; -typedef FunctionTransformWithDictionary FunctionRegionToCity; -typedef FunctionTransformWithDictionary FunctionRegionToArea; -typedef FunctionTransformWithDictionary FunctionRegionToCountry; -typedef FunctionTransformWithDictionary FunctionRegionToContinent; -typedef FunctionTransformWithDictionary FunctionOSToRoot; -typedef FunctionTransformWithDictionary FunctionSEToRoot; -typedef FunctionTransformWithDictionary FunctionCategoryToRoot; -typedef FunctionTransformWithDictionary FunctionCategoryToSecondLevel; +typedef FunctionTransformWithDictionary + FunctionRegionToCity; -typedef FunctionIsInWithDictionary FunctionRegionIn; -typedef FunctionIsInWithDictionary FunctionOSIn; -typedef FunctionIsInWithDictionary FunctionSEIn; -typedef FunctionIsInWithDictionary FunctionCategoryIn; +typedef FunctionTransformWithDictionary + FunctionRegionToArea; -typedef FunctionHierarchyWithDictionary FunctionRegionHierarchy; -typedef FunctionHierarchyWithDictionary FunctionOSHierarchy; -typedef FunctionHierarchyWithDictionary FunctionSEHierarchy; -typedef FunctionHierarchyWithDictionaryFunctionCategoryHierarchy; +typedef FunctionTransformWithDictionary + FunctionRegionToCountry; + +typedef FunctionTransformWithDictionary + FunctionRegionToContinent; + +typedef FunctionTransformWithDictionary + , NameOSToRoot> FunctionOSToRoot; + +typedef FunctionTransformWithDictionary + , NameSEToRoot> FunctionSEToRoot; + +typedef FunctionTransformWithDictionary + , NameCategoryToRoot> FunctionCategoryToRoot; + +typedef FunctionTransformWithDictionary + , NameCategoryToSecondLevel> + FunctionCategoryToSecondLevel; + +typedef FunctionIsInWithDictionary + FunctionRegionIn; + +typedef FunctionIsInWithDictionary + , NameOSIn> FunctionOSIn; + +typedef FunctionIsInWithDictionary + , NameSEIn> FunctionSEIn; + +typedef FunctionIsInWithDictionary + , NameCategoryIn> FunctionCategoryIn; + +typedef FunctionHierarchyWithDictionary + FunctionRegionHierarchy; + +typedef FunctionHierarchyWithDictionary + , NameOSHierarchy> FunctionOSHierarchy; + +typedef FunctionHierarchyWithDictionary + , NameSEHierarchy> FunctionSEHierarchy; + +typedef FunctionHierarchyWithDictionary + , NameCategoryHierarchy> FunctionCategoryHierarchy; /// Преобразует числовой идентификатор региона в имя на заданном языке, используя словарь. @@ -478,7 +604,9 @@ public: + " of the second argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN); } - + + const RegionsNames & dict = *owned_dict; + if (const ColumnVector * col_from = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) { ColumnString * col_to = new ColumnString; @@ -486,7 +614,6 @@ public: const ColumnVector::Container_t & region_ids = col_from->getData(); - const RegionsNames & dict = *owned_dict; for (size_t i = 0; i < region_ids.size(); ++i) { const DB::StringRef & name_ref = dict.getRegionName(region_ids[i], language); @@ -496,7 +623,7 @@ public: else if (const ColumnConst * col_from = dynamic_cast *>(&*block.getByPosition(arguments[0]).column)) { UInt32 region_id = col_from->getData(); - const DB::StringRef & name_ref = owned_dict->getRegionName(region_id, language); + const DB::StringRef & name_ref = dict.getRegionName(region_id, language); block.getByPosition(result).column = new ColumnConstString(col_from->size(), name_ref.toString()); } diff --git a/dbms/include/DB/Interpreters/Dictionaries.h b/dbms/include/DB/Interpreters/Dictionaries.h index c072276b4db..33b677526bb 100644 --- a/dbms/include/DB/Interpreters/Dictionaries.h +++ b/dbms/include/DB/Interpreters/Dictionaries.h @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include #include @@ -22,7 +22,7 @@ using Poco::SharedPtr; class Dictionaries { private: - MultiVersion regions_hierarchy; + MultiVersion regions_hierarchies; MultiVersion tech_data_hierarchy; MultiVersion categories_hierarchy; MultiVersion regions_names; @@ -81,9 +81,9 @@ private: try { - MultiVersion::Version new_regions_hierarchy = new RegionsHierarchy; - new_regions_hierarchy->reload(); - regions_hierarchy.set(new_regions_hierarchy); + MultiVersion::Version new_regions_hierarchies = new RegionsHierarchies; + new_regions_hierarchies->reload(); + regions_hierarchies.set(new_regions_hierarchies); } catch (...) @@ -148,9 +148,9 @@ public: reloading_thread.join(); } - MultiVersion::Version getRegionsHierarchy() const + MultiVersion::Version getRegionsHierarchies() const { - return regions_hierarchy.get(); + return regions_hierarchies.get(); } MultiVersion::Version getTechDataHierarchy() const diff --git a/dbms/src/Functions/FunctionFactory.cpp b/dbms/src/Functions/FunctionFactory.cpp index 1ed0f4cabd7..26727d48376 100644 --- a/dbms/src/Functions/FunctionFactory.cpp +++ b/dbms/src/Functions/FunctionFactory.cpp @@ -217,19 +217,19 @@ FunctionPtr FunctionFactory::get( else if (name == "if") return new FunctionIf; - else if (name == "regionToCity") return new FunctionRegionToCity(context.getDictionaries().getRegionsHierarchy()); - else if (name == "regionToArea") return new FunctionRegionToArea(context.getDictionaries().getRegionsHierarchy()); - else if (name == "regionToCountry") return new FunctionRegionToCountry(context.getDictionaries().getRegionsHierarchy()); - else if (name == "regionToContinent") return new FunctionRegionToContinent(context.getDictionaries().getRegionsHierarchy()); + else if (name == "regionToCity") return new FunctionRegionToCity(context.getDictionaries().getRegionsHierarchies()); + else if (name == "regionToArea") return new FunctionRegionToArea(context.getDictionaries().getRegionsHierarchies()); + else if (name == "regionToCountry") return new FunctionRegionToCountry(context.getDictionaries().getRegionsHierarchies()); + else if (name == "regionToContinent") return new FunctionRegionToContinent(context.getDictionaries().getRegionsHierarchies()); else if (name == "OSToRoot") return new FunctionOSToRoot(context.getDictionaries().getTechDataHierarchy()); else if (name == "SEToRoot") return new FunctionSEToRoot(context.getDictionaries().getTechDataHierarchy()); else if (name == "categoryToRoot") return new FunctionCategoryToRoot(context.getDictionaries().getCategoriesHierarchy()); else if (name == "categoryToSecondLevel") return new FunctionCategoryToSecondLevel(context.getDictionaries().getCategoriesHierarchy()); - else if (name == "regionIn") return new FunctionRegionIn(context.getDictionaries().getRegionsHierarchy()); + else if (name == "regionIn") return new FunctionRegionIn(context.getDictionaries().getRegionsHierarchies()); else if (name == "OSIn") return new FunctionOSIn(context.getDictionaries().getTechDataHierarchy()); else if (name == "SEIn") return new FunctionSEIn(context.getDictionaries().getTechDataHierarchy()); else if (name == "categoryIn") return new FunctionCategoryIn(context.getDictionaries().getCategoriesHierarchy()); - else if (name == "regionHierarchy") return new FunctionRegionHierarchy(context.getDictionaries().getRegionsHierarchy()); + else if (name == "regionHierarchy") return new FunctionRegionHierarchy(context.getDictionaries().getRegionsHierarchies()); else if (name == "OSHierarchy") return new FunctionOSHierarchy(context.getDictionaries().getTechDataHierarchy()); else if (name == "SEHierarchy") return new FunctionSEHierarchy(context.getDictionaries().getTechDataHierarchy()); else if (name == "categoryHierarchy") return new FunctionCategoryHierarchy(context.getDictionaries().getCategoriesHierarchy()); From 8f225d508dd95e19705a4def8d751be44efa5c77 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 03:41:09 +0400 Subject: [PATCH 147/281] Added gitignore [#METR-2807]. --- dbms/src/Server/data/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 dbms/src/Server/data/.gitignore diff --git a/dbms/src/Server/data/.gitignore b/dbms/src/Server/data/.gitignore new file mode 100644 index 00000000000..5d26e622e3e --- /dev/null +++ b/dbms/src/Server/data/.gitignore @@ -0,0 +1,2 @@ +*.bin +*.mrk From 9f4f900920bfaf4770e8ec5ebc9f4c2b0c9bb40e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 03:41:54 +0400 Subject: [PATCH 148/281] dbms: more simple [#METR-10713]. --- .../DB/Functions/FunctionsDictionaries.h | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/dbms/include/DB/Functions/FunctionsDictionaries.h b/dbms/include/DB/Functions/FunctionsDictionaries.h index 1315dd894e1..a4e0ee052d3 100644 --- a/dbms/include/DB/Functions/FunctionsDictionaries.h +++ b/dbms/include/DB/Functions/FunctionsDictionaries.h @@ -153,14 +153,14 @@ struct IdentityDictionaryGetter /// Преобразует идентификатор, используя словарь. -template +template class FunctionTransformWithDictionary : public IFunction { private: - const SharedPtr owned_dict; + const SharedPtr owned_dict; public: - FunctionTransformWithDictionary(const SharedPtr & owned_dict_) + FunctionTransformWithDictionary(const SharedPtr & owned_dict_) : owned_dict(owned_dict_) { if (!owned_dict) @@ -241,14 +241,14 @@ public: /// Проверяет принадлежность, используя словарь. -template +template class FunctionIsInWithDictionary : public IFunction { private: - const SharedPtr owned_dict; + const SharedPtr owned_dict; public: - FunctionIsInWithDictionary(const SharedPtr & owned_dict_) + FunctionIsInWithDictionary(const SharedPtr & owned_dict_) : owned_dict(owned_dict_) { if (!owned_dict) @@ -370,14 +370,14 @@ public: /// Получает массив идентификаторов, состоящий из исходного и цепочки родителей. -template +template class FunctionHierarchyWithDictionary : public IFunction { private: - const SharedPtr owned_dict; + const SharedPtr owned_dict; public: - FunctionHierarchyWithDictionary(const SharedPtr & owned_dict_) + FunctionHierarchyWithDictionary(const SharedPtr & owned_dict_) : owned_dict(owned_dict_) { if (!owned_dict) @@ -499,53 +499,52 @@ struct NameCategoryHierarchy{ static const char * get() { return "categoryHierar typedef FunctionTransformWithDictionary - FunctionRegionToCity; + FunctionRegionToCity; typedef FunctionTransformWithDictionary - FunctionRegionToArea; + FunctionRegionToArea; typedef FunctionTransformWithDictionary - FunctionRegionToCountry; + FunctionRegionToCountry; typedef FunctionTransformWithDictionary - FunctionRegionToContinent; + FunctionRegionToContinent; typedef FunctionTransformWithDictionary - , NameOSToRoot> FunctionOSToRoot; + , NameOSToRoot> FunctionOSToRoot; typedef FunctionTransformWithDictionary - , NameSEToRoot> FunctionSEToRoot; + , NameSEToRoot> FunctionSEToRoot; typedef FunctionTransformWithDictionary - , NameCategoryToRoot> FunctionCategoryToRoot; + , NameCategoryToRoot> FunctionCategoryToRoot; typedef FunctionTransformWithDictionary - , NameCategoryToSecondLevel> - FunctionCategoryToSecondLevel; + , NameCategoryToSecondLevel> FunctionCategoryToSecondLevel; typedef FunctionIsInWithDictionary - FunctionRegionIn; + FunctionRegionIn; typedef FunctionIsInWithDictionary - , NameOSIn> FunctionOSIn; + , NameOSIn> FunctionOSIn; typedef FunctionIsInWithDictionary - , NameSEIn> FunctionSEIn; + , NameSEIn> FunctionSEIn; typedef FunctionIsInWithDictionary - , NameCategoryIn> FunctionCategoryIn; + , NameCategoryIn> FunctionCategoryIn; typedef FunctionHierarchyWithDictionary - FunctionRegionHierarchy; + FunctionRegionHierarchy; typedef FunctionHierarchyWithDictionary - , NameOSHierarchy> FunctionOSHierarchy; + , NameOSHierarchy> FunctionOSHierarchy; typedef FunctionHierarchyWithDictionary - , NameSEHierarchy> FunctionSEHierarchy; + , NameSEHierarchy> FunctionSEHierarchy; typedef FunctionHierarchyWithDictionary - , NameCategoryHierarchy> FunctionCategoryHierarchy; + , NameCategoryHierarchy> FunctionCategoryHierarchy; /// Преобразует числовой идентификатор региона в имя на заданном языке, используя словарь. From ae90ba08a667b1668f2c0be46684e9126324f2da Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 04:47:36 +0400 Subject: [PATCH 149/281] dbms: OLAP compatibility: added support for 'regions_point_of_view' setting [#METR-10715]. --- dbms/src/Server/OLAPQueryConverter.cpp | 96 +++++++++++++++++--------- dbms/src/Server/OLAPQueryConverter.h | 48 ++++++++++--- dbms/src/Server/OLAPQueryParser.cpp | 4 ++ dbms/src/Server/OLAPQueryParser.h | 3 + 4 files changed, 107 insertions(+), 44 deletions(-) diff --git a/dbms/src/Server/OLAPQueryConverter.cpp b/dbms/src/Server/OLAPQueryConverter.cpp index 9fb4b3b3d4b..c4e1791c03f 100644 --- a/dbms/src/Server/OLAPQueryConverter.cpp +++ b/dbms/src/Server/OLAPQueryConverter.cpp @@ -19,7 +19,7 @@ QueryConverter::QueryConverter(Poco::Util::AbstractConfiguration & config) attribute_metadatas = GetOLAPAttributeMetadata(); } -static std::string FirstWord(std::string s) +static std::string firstWord(std::string s) { for (size_t i = 0; i < s.length(); ++i) { @@ -32,8 +32,18 @@ static std::string FirstWord(std::string s) return s; } -void QueryConverter::OLAPServerQueryToClickhouse(const QueryParseResult & query, Context & inout_context, std::string & out_query) +void QueryConverter::OLAPServerQueryToClickhouse(const QueryParseResult & query, Context & inout_context, std::string & out_query) const { + /// Пустая строка, или строка вида ", 'ua'". + std::string regions_point_of_view_formatted; + + if (!query.regions_point_of_view.empty()) + { + std::stringstream tmp; + tmp << ", " << mysqlxx::quote << query.regions_point_of_view; + regions_point_of_view_formatted = tmp.str(); + } + /// Проверим, умеем ли мы выполнять такой запрос. if (query.format != FORMAT_TAB) throw Exception("Only tab-separated output format is supported", ErrorCodes::UNSUPPORTED_PARAMETER); @@ -75,22 +85,22 @@ void QueryConverter::OLAPServerQueryToClickhouse(const QueryParseResult & query, for (size_t i = 0; i < query.key_attributes.size(); ++i) { const QueryParseResult::KeyAttribute & key = query.key_attributes[i]; - std::string s = convertAttributeFormatted(key.attribute, key.parameter); + std::string s = convertAttributeFormatted(key.attribute, key.parameter, regions_point_of_view_formatted); if (i > 0) out_query += ", "; - out_query += s + " AS _" + FirstWord(key.attribute) + (key.parameter ? "_" + toString(key.parameter) : ""); + out_query += s + " AS _" + firstWord(key.attribute) + (key.parameter ? "_" + toString(key.parameter) : ""); selected_expressions.push_back(s); } for (size_t i = 0; i < query.aggregates.size(); ++i) { const QueryParseResult::Aggregate & aggregate = query.aggregates[i]; - std::string s = convertAggregateFunction(aggregate.attribute, aggregate.parameter, aggregate.function, query); + std::string s = convertAggregateFunction(aggregate.attribute, aggregate.parameter, aggregate.function, query, regions_point_of_view_formatted); if (query.key_attributes.size() + i > 0) out_query += ", "; - out_query += s + " AS _" + FirstWord(aggregate.function) + "_" + FirstWord(aggregate.attribute) + (aggregate.parameter ? "_" + toString(aggregate.parameter) : ""); + out_query += s + " AS _" + firstWord(aggregate.function) + "_" + firstWord(aggregate.attribute) + (aggregate.parameter ? "_" + toString(aggregate.parameter) : ""); selected_expressions.push_back(s); } @@ -115,7 +125,8 @@ void QueryConverter::OLAPServerQueryToClickhouse(const QueryParseResult & query, for (size_t i = 0; i < query.where_conditions.size(); ++i) { const QueryParseResult::WhereCondition & condition = query.where_conditions[i]; - out_query += " AND " + convertCondition(condition.attribute, condition.parameter, condition.relation, condition.rhs); + out_query += " AND " + convertCondition( + condition.attribute, condition.parameter, condition.relation, condition.rhs, regions_point_of_view_formatted); } /// Группировка. @@ -153,17 +164,24 @@ void QueryConverter::OLAPServerQueryToClickhouse(const QueryParseResult & query, out_query += " LIMIT " + toString(query.limit); } -std::string QueryConverter::convertAttributeFormatted(const std::string & attribute, unsigned parameter) +std::string QueryConverter::convertAttributeFormatted(const std::string & attribute, unsigned parameter, + const std::string & regions_point_of_view_formatted) const { if (formatted_attribute_map.count(attribute)) - return Poco::format(formatted_attribute_map[attribute], parameter); + return Poco::format(formatted_attribute_map.at(attribute), parameter); + /** Для атрибутов по регионам, выражение содержит подстановку %s, + * куда должна быть подставлена regions_point_of_view_formatted. + */ + if (regions_attributes_set.count(attribute)) + return Poco::format(numeric_attribute_map.at(attribute), regions_point_of_view_formatted); + if (numeric_attribute_map.count(attribute)) { - std::string numeric = Poco::format(numeric_attribute_map[attribute], parameter); + std::string numeric = Poco::format(numeric_attribute_map.at(attribute), parameter); if (formatting_aggregated_attribute_map.count(attribute)) - return Poco::format(formatting_aggregated_attribute_map[attribute], std::string("(") + numeric + ")"); + return Poco::format(formatting_aggregated_attribute_map.at(attribute), std::string("(") + numeric + ")"); else return numeric; } @@ -171,10 +189,17 @@ std::string QueryConverter::convertAttributeFormatted(const std::string & attrib throw Exception("Unknown attribute: " + attribute, ErrorCodes::UNKNOWN_IDENTIFIER); } -std::string QueryConverter::convertAttributeNumeric(const std::string & attribute, unsigned parameter) +std::string QueryConverter::convertAttributeNumeric(const std::string & attribute, unsigned parameter, + const std::string & regions_point_of_view_formatted) const { + /** Для атрибутов по регионам, выражение содержит подстановку %s, + * куда должна быть подставлена regions_point_of_view_formatted. + */ + if (regions_attributes_set.count(attribute)) + return Poco::format(numeric_attribute_map.at(attribute), regions_point_of_view_formatted); + if (numeric_attribute_map.count(attribute)) - return Poco::format(numeric_attribute_map[attribute], parameter); + return Poco::format(numeric_attribute_map.at(attribute), parameter); throw Exception("Unknown attribute: " + attribute, ErrorCodes::UNKNOWN_IDENTIFIER); } @@ -185,7 +210,7 @@ static bool StartsWith(const std::string & str, const std::string & prefix) } std::string QueryConverter::convertAggregateFunction(const std::string & attribute, unsigned parameter, const std::string & name, - const QueryParseResult & query) + const QueryParseResult & query, const std::string & regions_point_of_view_formatted) const { bool float_value = false; @@ -201,7 +226,7 @@ std::string QueryConverter::convertAggregateFunction(const std::string & attribu return "sum(Sign)"; } - std::string numeric = convertAttributeNumeric(attribute, parameter); + std::string numeric = convertAttributeNumeric(attribute, parameter, regions_point_of_view_formatted); if (name == "uniq" || name == "uniq_sort" || @@ -247,7 +272,7 @@ std::string QueryConverter::convertAggregateFunction(const std::string & attribu std::string format; if (formatting_aggregated_attribute_map.count(attribute)) { - format = formatting_aggregated_attribute_map[attribute]; + format = formatting_aggregated_attribute_map.at(attribute); trivial_format = false; } else @@ -305,16 +330,21 @@ std::string QueryConverter::convertAggregateFunction(const std::string & attribu return Poco::format(format, std::string() + (need_cast ? "toInt64" : "") + "(" + s + ")"); } -std::string QueryConverter::convertConstant(const std::string & attribute, const std::string & value) +std::string QueryConverter::convertConstant(const std::string & attribute, const std::string & value) const { if (!attribute_metadatas.count(attribute)) throw Exception("Unknown attribute " + attribute, ErrorCodes::UNKNOWN_IDENTIFIER); - return toString(attribute_metadatas[attribute]->parse(value)); + return toString(attribute_metadatas.at(attribute)->parse(value)); } -std::string QueryConverter::convertCondition(const std::string & attribute, unsigned parameter, const std::string & name, const std::string & rhs) +std::string QueryConverter::convertCondition( + const std::string & attribute, + unsigned parameter, + const std::string & name, + const std::string & rhs, + const std::string & regions_point_of_view_formatted) const { - std::string value = convertAttributeNumeric(attribute, parameter); + std::string value = convertAttributeNumeric(attribute, parameter, regions_point_of_view_formatted); std::string constant = convertConstant(attribute, rhs); if (name == "equals") @@ -330,9 +360,9 @@ std::string QueryConverter::convertCondition(const std::string & attribute, unsi if (name == "greater_or_equals") return "(" + value + ")" + " >= " + constant; if (name == "region_in") - return "regionIn(" + value + ", toUInt32(" + constant + "))"; + return "regionIn(" + value + ", toUInt32(" + constant + ")" + regions_point_of_view_formatted + ")"; if (name == "region_not_in") - return "NOT regionIn(" + value + ", toUInt32(" + constant + "))"; + return "NOT regionIn(" + value + ", toUInt32(" + constant + ")" + regions_point_of_view_formatted + ")"; if (name == "os_in") return "OSIn(" + value + ", " + constant + ")"; if (name == "os_not_in") @@ -356,7 +386,7 @@ std::string QueryConverter::convertCondition(const std::string & attribute, unsi throw Exception("Unknown relation " + name, ErrorCodes::UNKNOWN_RELATION); } -std::string QueryConverter::convertSortDirection(const std::string & direction) +std::string QueryConverter::convertSortDirection(const std::string & direction) const { if (direction == "descending") return "DESC"; @@ -364,7 +394,7 @@ std::string QueryConverter::convertSortDirection(const std::string & direction) return "ASC"; } -std::string QueryConverter::convertDateRange(time_t date_first, time_t date_last) +std::string QueryConverter::convertDateRange(time_t date_first, time_t date_last) const { std::string first_str; std::string last_str; @@ -377,12 +407,12 @@ std::string QueryConverter::convertDateRange(time_t date_first, time_t date_last return "StartDate >= toDate('" + first_str + "') AND StartDate <= toDate('" + last_str + "')"; } -std::string QueryConverter::convertCounterID(CounterID_t CounterID) +std::string QueryConverter::convertCounterID(CounterID_t CounterID) const { return "CounterID == " + toString(CounterID); } -std::string QueryConverter::getTableName(CounterID_t CounterID, bool local) +std::string QueryConverter::getTableName(CounterID_t CounterID, bool local) const { if (CounterID == 0 && !local) return table_for_all_counters; @@ -390,7 +420,7 @@ std::string QueryConverter::getTableName(CounterID_t CounterID, bool local) return table_for_single_counter; } -std::string QueryConverter::getHavingSection() +std::string QueryConverter::getHavingSection() const { return "HAVING sum(Sign) > 0"; } @@ -449,13 +479,13 @@ void QueryConverter::fillNumericAttributeMap() M("StartURLHash", "NormalizedStartURLHash") M("StartURLDomainHash", "StartURLDomainHash") M("RegionID", "RegionID") - M("RegionCity", "regionToCity(RegionID)") - M("RegionArea", "regionToArea(RegionID)") - M("RegionCountry", "regionToCountry(RegionID)") + M("RegionCity", "regionToCity(RegionID%s)") + M("RegionArea", "regionToArea(RegionID%s)") + M("RegionCountry", "regionToCountry(RegionID%s)") M("URLRegionID", "URLRegions[0]") - M("URLRegionCity", "regionToCity(URLRegions[0])") - M("URLRegionArea", "regionToArea(URLRegions[0])") - M("URLRegionCountry", "regionToCountry(URLRegions[0])") + M("URLRegionCity", "regionToCity(URLRegions[0]%s)") + M("URLRegionArea", "regionToArea(URLRegions[0]%s)") + M("URLRegionCountry", "regionToCountry(URLRegions[0]%s)") M("URLCategoryID", "URLCategories[0]") M("URLCategoryMostAncestor", "categoryToRoot(URLCategories[0])") M("URLCategorySecondLevel", "categoryToSecondLevel(URLCategories[0])") diff --git a/dbms/src/Server/OLAPQueryConverter.h b/dbms/src/Server/OLAPQueryConverter.h index 2804507f852..9eea3a2bc4a 100644 --- a/dbms/src/Server/OLAPQueryConverter.h +++ b/dbms/src/Server/OLAPQueryConverter.h @@ -18,29 +18,41 @@ public: QueryConverter(Poco::Util::AbstractConfiguration & config); /// Получает из запроса в формате OLAP-server запрос и настройки для clickhouse. - void OLAPServerQueryToClickhouse(const QueryParseResult & query, Context & inout_context, std::string & out_query); + void OLAPServerQueryToClickhouse(const QueryParseResult & query, Context & inout_context, std::string & out_query) const; + private: /// Значение атрибута, подходящее для вывода в ответ и для группировки по нему. - std::string convertAttributeFormatted(const std::string & attribute, unsigned parameter); + std::string convertAttributeFormatted(const std::string & attribute, unsigned parameter, const std::string & regions_point_of_view_formatted) const; + /// Числовое значение атрибута, подходящее для подстановки в условия, агрегатные функции и ключи сортировки. - std::string convertAttributeNumeric(const std::string & attribute, unsigned parameter); + std::string convertAttributeNumeric(const std::string & attribute, unsigned parameter, const std::string & regions_point_of_view_formatted) const; /// => SELECT x std::string convertAggregateFunction(const std::string & attribute, unsigned parameter, const std::string & function, - const QueryParseResult & query); + const QueryParseResult & query, const std::string & regions_point_of_view_formatted) const; + /// => SELECT ... where F(A, x) - std::string convertConstant(const std::string & attribute, const std::string & value); + std::string convertConstant(const std::string & attribute, const std::string & value) const; + /// => SELECT ... WHERE x - std::string convertCondition(const std::string & attribute, unsigned parameter, const std::string & relation, const std::string & rhs); + std::string convertCondition( + const std::string & attribute, + unsigned parameter, + const std::string & relation, + const std::string & rhs, + const std::string & regions_point_of_view_formatted) const; + /// ASC или DESC - std::string convertSortDirection(const std::string & direction); + std::string convertSortDirection(const std::string & direction) const; + /// => SELECT ... WHERE x - std::string convertDateRange(time_t date_first, time_t date_last); + std::string convertDateRange(time_t date_first, time_t date_last) const; + /// => SELECT ... WHERE x - std::string convertCounterID(CounterID_t CounterID); + std::string convertCounterID(CounterID_t CounterID) const; - std::string getTableName(CounterID_t CounterID, bool local); - std::string getHavingSection(); + std::string getTableName(CounterID_t CounterID, bool local) const; + std::string getHavingSection() const; void fillFormattedAttributeMap(); void fillNumericAttributeMap(); @@ -51,10 +63,24 @@ private: /// Форматная строка для convertAttributeNumeric. Есть для всех атрибутов. std::map numeric_attribute_map; + /// Форматная строка для получения выводимого значения из агрегированного числового значения. std::map formatting_aggregated_attribute_map; + /// Форматная строка для convertAttributeFormatted. std::map formatted_attribute_map; + + /// Список атрибутов-регионов, для которых нужна передача параметра regions_point_of_view. + std::set regions_attributes_set = + { + "RegionCity", + "RegionArea", + "RegionCountry", + "URLRegionCity", + "URLRegionArea", + "URLRegionCountry" + }; + /// Парсеры значений атрибутов. AttributeMetadatas attribute_metadatas; }; diff --git a/dbms/src/Server/OLAPQueryParser.cpp b/dbms/src/Server/OLAPQueryParser.cpp index b0e997fb9ce..27dc221a4de 100644 --- a/dbms/src/Server/OLAPQueryParser.cpp +++ b/dbms/src/Server/OLAPQueryParser.cpp @@ -180,6 +180,10 @@ QueryParseResult QueryParser::parse(std::istream & s) if (result.sample <= 0 || result.sample > 1.) throw Exception(std::string("Wrong sample = ") + DB::toString(result.sample) + ". Sampling must be in range (0, 1]"); } + else if (settings_child_nodes->item(i)->nodeName() == "regions_point_of_view") + { + result.regions_point_of_view = settings_child_nodes->item(i)->innerText(); + } } } diff --git a/dbms/src/Server/OLAPQueryParser.h b/dbms/src/Server/OLAPQueryParser.h index 22abb12c289..0efb9fd412c 100644 --- a/dbms/src/Server/OLAPQueryParser.h +++ b/dbms/src/Server/OLAPQueryParser.h @@ -100,6 +100,9 @@ struct QueryParseResult std::vector aggregates; std::vector where_conditions; std::vector sort_columns; + + /// Какую иерархию регионов использовать. + std::string regions_point_of_view; }; From e41c11e788196077871bbb474faee1e285573f2e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 22:25:46 +0400 Subject: [PATCH 150/281] dbms: fixed test [#METR-2944]. --- dbms/tests/queries/0_stateless/00031_parser_number.reference | 2 +- dbms/tests/queries/0_stateless/00031_parser_number.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/tests/queries/0_stateless/00031_parser_number.reference b/dbms/tests/queries/0_stateless/00031_parser_number.reference index 5427720faea..dba9f499bfc 100644 --- a/dbms/tests/queries/0_stateless/00031_parser_number.reference +++ b/dbms/tests/queries/0_stateless/00031_parser_number.reference @@ -1,3 +1,3 @@ -0 1 -1 128 -127 -128 255 -128 255 -127 65535 4294967295 12300 4656 -0 -0 0 18446744073709551615 2.09883e+19 -1.84467e+19 -9223372036854775807 -8.98847e+307 -5.56268e-309 inf -inf nan -nan 1e-302 UInt8 UInt8 Int8 UInt8 Int8 Int8 UInt8 Int8 UInt8 Int8 UInt16 UInt32 Float64 Float64 Float64 Float64 UInt8 UInt64 Float64 Float64 Int64 Float64 Float64 Float64 Float64 Float64 Float32 Float64 +0 1 -1 128 -127 -128 255 -128 255 -127 65535 4294967295 12300 4656 -0 -0 0 18446744073709551615 2.09883e+19 -1.84467e+19 -9223372036854775807 -8.98847e+307 -2.22507e-308 inf -inf nan -nan 1e-302 UInt8 UInt8 Int8 UInt8 Int8 Int8 UInt8 Int8 UInt8 Int8 UInt16 UInt32 Float64 Float64 Float64 Float64 UInt8 UInt64 Float64 Float64 Int64 Float64 Float64 Float64 Float64 Float64 Float32 Float64 1e+308 -1e-307 diff --git a/dbms/tests/queries/0_stateless/00031_parser_number.sql b/dbms/tests/queries/0_stateless/00031_parser_number.sql index 65db3211465..6e5a1180297 100644 --- a/dbms/tests/queries/0_stateless/00031_parser_number.sql +++ b/dbms/tests/queries/0_stateless/00031_parser_number.sql @@ -1,3 +1,3 @@ -SELECT 0 AS x1, 1 AS x2, -1 AS x3, 128 AS x4, -127 AS x5, -128 AS x6, 0xFF AS x7, -0x80 AS x8, 0377 AS x9, -0177 AS x10, 0xFFFF AS x11, 0xFFFFFFFF AS x12, 123e2 AS x13, 0x123p4 AS x14, -0. AS x15, -.0 AS x16, 0. AS x17, 0xFFFFFFFFFFFFFFFF AS x18, 0x123456789ABCDEF01 AS x19, -0xFFFFFFFFFFFFFFFF AS x20, -0x7FFFFFFFFFFFFFFF AS x21, -0x1P1023 AS x22, -0x1p-1024 AS x23, inf AS x24, -INF AS x25, nan AS x26, 0 / 0 AS x27, 0.01e-300 AS x28, toTypeName(x1), toTypeName(x2), toTypeName(x3), toTypeName(x4), toTypeName(x5), toTypeName(x6), toTypeName(x7), toTypeName(x8), toTypeName(x9), toTypeName(x10), toTypeName(x11), toTypeName(x12), toTypeName(x13), toTypeName(x14), toTypeName(x15), toTypeName(x16), toTypeName(x17), toTypeName(x18), toTypeName(x19), toTypeName(x20), toTypeName(x21), toTypeName(x22), toTypeName(x23), toTypeName(x24), toTypeName(x25), toTypeName(x26), toTypeName(x27), toTypeName(x28); +SELECT 0 AS x1, 1 AS x2, -1 AS x3, 128 AS x4, -127 AS x5, -128 AS x6, 0xFF AS x7, -0x80 AS x8, 0377 AS x9, -0177 AS x10, 0xFFFF AS x11, 0xFFFFFFFF AS x12, 123e2 AS x13, 0x123p4 AS x14, -0. AS x15, -.0 AS x16, 0. AS x17, 0xFFFFFFFFFFFFFFFF AS x18, 0x123456789ABCDEF01 AS x19, -0xFFFFFFFFFFFFFFFF AS x20, -0x7FFFFFFFFFFFFFFF AS x21, -0x1P1023 AS x22, -0x1p-1022 AS x23, inf AS x24, -INF AS x25, nan AS x26, 0 / 0 AS x27, 0.01e-300 AS x28, toTypeName(x1), toTypeName(x2), toTypeName(x3), toTypeName(x4), toTypeName(x5), toTypeName(x6), toTypeName(x7), toTypeName(x8), toTypeName(x9), toTypeName(x10), toTypeName(x11), toTypeName(x12), toTypeName(x13), toTypeName(x14), toTypeName(x15), toTypeName(x16), toTypeName(x17), toTypeName(x18), toTypeName(x19), toTypeName(x20), toTypeName(x21), toTypeName(x22), toTypeName(x23), toTypeName(x24), toTypeName(x25), toTypeName(x26), toTypeName(x27), toTypeName(x28); SELECT 0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; SELECT -0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001; From cf5a86434d19a80756305f80fbb470c5ff5e0cfb Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 23:54:00 +0400 Subject: [PATCH 151/281] dbms: fixed error when uncompressed_cache or marks_cache is not set [#METR-2944]. --- dbms/include/DB/Interpreters/Context.h | 8 ++++++++ dbms/src/Interpreters/Context.cpp | 12 ++++++++++++ dbms/src/Storages/MergeTree/MergeTreeData.cpp | 13 +++++-------- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index a57839ad699..1e451cc9393 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -295,6 +295,14 @@ public: void setMarkCache(size_t cache_size_in_bytes); MarkCachePtr getMarkCache() const; + /** Очистить кэши разжатых блоков и засечек. + * Обычно это делается при переименовании таблиц, изменении типа столбцов, удалении таблицы. + * - так как кэши привязаны к именам файлов, и становятся некорректными. + * (при удалении таблицы - нужно, так как на её месте может появиться другая) + * const - потому что изменение кэша не считается существенным. + */ + void resetCaches() const; + void initClusters(); Cluster & getCluster(const std::string & cluster_name); diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 5712e750131..a31ba4f49f7 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -532,9 +532,21 @@ void Context::setMarkCache(size_t cache_size_in_bytes) MarkCachePtr Context::getMarkCache() const { + /// Исходим из допущения, что функция setMarksCache, если вызывалась, то раньше. Иначе поставьте mutex. return shared->mark_cache; } +void Context::resetCaches() const +{ + /// Исходим из допущения, что функции setUncompressedCache, setMarkCache, если вызывались, то раньше (при старте сервера). Иначе поставьте mutex. + + if (shared->uncompressed_cache) + shared->uncompressed_cache->reset(); + + if (shared->mark_cache) + shared->mark_cache->reset(); +} + void Context::initClusters() { Poco::ScopedLock lock(shared->mutex); diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index f813419b1fd..2d2628bd4e2 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -317,8 +317,7 @@ void MergeTreeData::setPath(const String & new_full_path) Poco::File(full_path).renameTo(new_full_path); full_path = new_full_path; - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); + context.resetCaches(); log = &Logger::get(lastTwoPathComponents(full_path)); } @@ -328,8 +327,7 @@ void MergeTreeData::dropAllData() data_parts.clear(); all_data_parts.clear(); - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); + context.resetCaches(); Poco::File(full_path).remove(true); } @@ -411,6 +409,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) Poco::ScopedLock lock_all(all_data_parts_mutex); alterColumns(params, columns, context); } + if (params.type == ASTAlterQuery::DROP) { String column_name = dynamic_cast(*params.column).name; @@ -424,8 +423,7 @@ void MergeTreeData::alter(const ASTAlterQuery::Parameters & params) } removeColumnFiles(column_name, remove_array_size_files); - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); + context.resetCaches(); } } @@ -571,8 +569,7 @@ void MergeTreeData::commitAlterModify(const ASTAlterQuery::Parameters & params) } } - context.getUncompressedCache()->reset(); - context.getMarkCache()->reset(); + context.resetCaches(); { Poco::ScopedLock lock(data_parts_mutex); From 3f6c1012225515ffdf88b54eebaf136c0fba773d Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 5 Apr 2014 23:59:38 +0400 Subject: [PATCH 152/281] dbms: fixed error with parsing config [#METR-10570]. --- dbms/src/Server/Server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index 552dfee7a8d..c9c62e7f52c 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -219,12 +219,12 @@ int Server::main(const std::vector & args) global_context->getProcessList().setMaxSize(config.getInt("max_concurrent_queries", 0)); /// Размер кэша разжатых блоков. Если нулевой - кэш отключён. - size_t uncompressed_cache_size = config.getInt("uncompressed_cache_size", 0); + size_t uncompressed_cache_size = parse(config.getString("uncompressed_cache_size", "0")); if (uncompressed_cache_size) global_context->setUncompressedCache(uncompressed_cache_size); /// Размер кэша засечек. Если нулевой - кэш отключён. - size_t mark_cache_size = config.getInt("mark_cache_size", 0); + size_t mark_cache_size = parse(config.getString("mark_cache_size", "0")); if (mark_cache_size) global_context->setMarkCache(mark_cache_size); From 8b3df69a46babec6ad257eb38b032b5f7626a1df Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 05:18:28 +0400 Subject: [PATCH 153/281] dbms: removed useless variable [#METR-2944]. --- dbms/include/DB/Storages/MergeTree/MergeTreeData.h | 1 - 1 file changed, 1 deletion(-) diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index c6e9dfb75cc..b5da8ec285f 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -364,7 +364,6 @@ private: NamesAndTypesListPtr columns; Logger * log; - volatile bool shutdown_called; /// Регулярное выражение соответсвующее названию директории с кусочками Poco::RegularExpression file_name_regexp; From 0fc10a40aeaaf89338233fac8dc70d04e0232f1e Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 05:19:02 +0400 Subject: [PATCH 154/281] dbms: tiny modification [#METR-2944]. --- dbms/src/Storages/StorageMergeTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Storages/StorageMergeTree.cpp b/dbms/src/Storages/StorageMergeTree.cpp index fe3d5be0ffe..f95b6fc2bbf 100644 --- a/dbms/src/Storages/StorageMergeTree.cpp +++ b/dbms/src/Storages/StorageMergeTree.cpp @@ -17,7 +17,7 @@ StorageMergeTree::StorageMergeTree(const String & path_, const String & name_, N const String & sign_column_, const MergeTreeSettings & settings_) : path(path_), name(name_), full_path(path + escapeForFileName(name) + '/'), increment(full_path + "increment.txt"), - data( full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, + data(full_path, columns_, context_, primary_expr_ast_, date_column_name_, sampling_expression_, index_granularity_,mode_, sign_column_, settings_), reader(data), writer(data), merger(data), log(&Logger::get("StorageMergeTree")), From 1bf23a9b7e47fdb3dc6eb2a4316449476e03b61c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 05:19:43 +0400 Subject: [PATCH 155/281] dbms: benchmark: better [#METR-2944]. --- dbms/src/Client/Benchmark.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dbms/src/Client/Benchmark.cpp b/dbms/src/Client/Benchmark.cpp index 21a36be3a80..fccdcb532d0 100644 --- a/dbms/src/Client/Benchmark.cpp +++ b/dbms/src/Client/Benchmark.cpp @@ -53,23 +53,23 @@ namespace DB class Benchmark { public: - Benchmark(unsigned concurrency_, + Benchmark(unsigned concurrency_, double delay_, const String & host_, UInt16 port_, const String & default_database_, const String & user_, const String & password_) - : concurrency(concurrency_), queue(concurrency), pool(concurrency), + : concurrency(concurrency_), delay(delay_), queue(concurrency), pool(concurrency), connections(concurrency, host_, port_, default_database_, user_, password_, data_type_factory) { std::cerr << std::fixed << std::setprecision(3); readQueries(); run(); - report(); } private: typedef std::string Query; unsigned concurrency; + double delay; typedef std::vector Queries; Queries queries; @@ -129,7 +129,7 @@ private: queue.push(queries[i]); - if (watch.elapsedSeconds() > 1) + if (watch.elapsedSeconds() > delay) { report(); watch.restart(); @@ -239,8 +239,10 @@ private: << "MiB/s: " << (total_bytes / total_watch.elapsedSeconds() / 1048576) << "." << std::endl; - for (double level = 0; level < 1; level += 0.1) - std::cerr << int(level * 100) << "%\t" << sampler.quantileInterpolated(level) << " sec." << std::endl; + for (size_t percent = 0; percent <= 100; percent += 10) + std::cerr << percent << "%\t" << sampler.quantileInterpolated(percent / 100.0) << " sec." << std::endl; + + sampler.clear(); } }; @@ -257,6 +259,7 @@ int main(int argc, char ** argv) desc.add_options() ("help", "produce help message") ("concurrency,c", boost::program_options::value()->default_value(1), "number of parallel queries") + ("delay,d", boost::program_options::value()->default_value(1), "delay between reports in seconds") ("host,h", boost::program_options::value()->default_value("localhost"), "") ("port", boost::program_options::value()->default_value(9000), "") ("user", boost::program_options::value()->default_value("default"), "") @@ -276,6 +279,7 @@ int main(int argc, char ** argv) Benchmark benchmark( options["concurrency"].as(), + options["delay"].as(), options["host"].as(), options["port"].as(), options["database"].as(), From 7dd8e5f0d0f863b055be50e117e68d19d8797fee Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 10:43:16 +0400 Subject: [PATCH 156/281] dbms: fixed latency regression (caused by Nagle's algorithm) [#METR-10071]. --- dbms/src/Client/Connection.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index df4b933769e..6a1cea3794a 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -225,8 +225,6 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 writeStringBinary(query, *out); - out->next(); - maybe_compressed_in = NULL; maybe_compressed_out = NULL; block_in = NULL; @@ -234,14 +232,17 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 /// Если версия сервера достаточно новая и стоит флаг, отправляем пустой блок, символизируя конец передачи данных. if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES && !with_pending_data) + { sendData(Block()); + out->next(); + } } void Connection::sendCancel() { //LOG_TRACE(log, "Sending cancel (" << getServerAddress() << ")"); - + writeVarUInt(Protocol::Client::Cancel, *out); out->next(); } @@ -272,6 +273,7 @@ void Connection::sendData(const Block & block, const String & name) out->next(); } + void Connection::sendExternalTablesData(ExternalTablesData & data) { /// Если работаем со старым сервером, то никакой информации не отправляем From d48daeea8fa07b36503fd029b6e244e744c37638 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 10:53:45 +0400 Subject: [PATCH 157/281] dbms: added TCP_NODELAY option [#METR-10071]. --- dbms/src/Client/Connection.cpp | 1 + dbms/src/Server/TCPHandler.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 6a1cea3794a..c39173b4a33 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -34,6 +34,7 @@ void Connection::connect() socket.connect(Poco::Net::SocketAddress(host, port), connect_timeout); socket.setReceiveTimeout(receive_timeout); socket.setSendTimeout(send_timeout); + socket.setNoDelay(true); in = new ReadBufferFromPocoSocket(socket); out = new WriteBufferFromPocoSocket(socket); diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 9c99f035005..819ab1819b4 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -40,6 +40,7 @@ void TCPHandler::runImpl() socket().setReceiveTimeout(global_settings.receive_timeout); socket().setSendTimeout(global_settings.send_timeout); + socket().setNoDelay(true); in = new ReadBufferFromPocoSocket(socket()); out = new WriteBufferFromPocoSocket(socket()); From f628605b1a9c8c0b300cd7b7e86cc026318e75aa Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 11:22:22 +0400 Subject: [PATCH 158/281] dbms: removed whitespace [#METR-2944]. --- dbms/src/Client/Benchmark.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Client/Benchmark.cpp b/dbms/src/Client/Benchmark.cpp index fccdcb532d0..ae9b3180d41 100644 --- a/dbms/src/Client/Benchmark.cpp +++ b/dbms/src/Client/Benchmark.cpp @@ -206,7 +206,7 @@ private: size_t rows = 0; size_t bytes = 0; - stream.setProgressCallback([&](size_t rows_inc , size_t bytes_inc) { rows += rows_inc; bytes += bytes_inc; }); + stream.setProgressCallback([&](size_t rows_inc, size_t bytes_inc) { rows += rows_inc; bytes += bytes_inc; }); stream.readPrefix(); while (Block block = stream.read()) From 9174b461f2c3b4c5da67890ab71b638fe8969489 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 6 Apr 2014 11:23:42 +0400 Subject: [PATCH 159/281] dbms: tiny improvement [#METR-10718]. --- dbms/include/DB/Parsers/IParserBase.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dbms/include/DB/Parsers/IParserBase.h b/dbms/include/DB/Parsers/IParserBase.h index aa777fcca50..cf709f2f33d 100644 --- a/dbms/include/DB/Parsers/IParserBase.h +++ b/dbms/include/DB/Parsers/IParserBase.h @@ -20,6 +20,10 @@ public: { expected = getName(); bool res = parseImpl(pos, end, node, expected); + + if (pos > end) + throw Exception("Logical error: pos > end.", ErrorCodes::LOGICAL_ERROR); + return res; } protected: From 0f17a5798a4ba08ed2e8b00a61fc68997ab0e0c9 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 01:39:48 +0400 Subject: [PATCH 160/281] dbms: StorageDistributed: don't need to specify sign column to use FINAL [#METR-2944]. --- dbms/include/DB/Storages/StorageDistributed.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/include/DB/Storages/StorageDistributed.h b/dbms/include/DB/Storages/StorageDistributed.h index 5640bb532f0..416527b5207 100644 --- a/dbms/include/DB/Storages/StorageDistributed.h +++ b/dbms/include/DB/Storages/StorageDistributed.h @@ -41,7 +41,7 @@ public: std::string getTableName() const { return name; } std::string getSignColumnName() const { return sign_column_name; }; bool supportsSampling() const { return true; } - bool supportsFinal() const { return !sign_column_name.empty(); } + bool supportsFinal() const { return true; } bool supportsPrewhere() const { return true; } const NamesAndTypesList & getColumnsList() const { return *columns; } From 312feedecb3a743aafecac53bc40a762f29c9d08 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 01:59:27 +0400 Subject: [PATCH 161/281] dbms: removed virtual columns from StorageDistributed [#METR-9172]. --- dbms/include/DB/Common/VirtualColumnUtils.h | 22 --- .../DB/DataStreams/RemoteBlockInputStream.h | 29 +--- dbms/include/DB/Storages/StorageDistributed.h | 13 +- dbms/src/Storages/StorageDistributed.cpp | 156 +----------------- 4 files changed, 14 insertions(+), 206 deletions(-) diff --git a/dbms/include/DB/Common/VirtualColumnUtils.h b/dbms/include/DB/Common/VirtualColumnUtils.h index 37897dc568f..00f5b8ce2c7 100644 --- a/dbms/include/DB/Common/VirtualColumnUtils.h +++ b/dbms/include/DB/Common/VirtualColumnUtils.h @@ -49,28 +49,6 @@ std::multiset extractSingleValueFromBlocks(BlockInputStreamPtr input, const return res; } -/// Извлечь из входного потока множество пар значений в столбцах first_name и second_name -template -std::multiset< std::pair > extractTwoValuesFromBlocks(BlockInputStreamPtr input, - const String & first_name, const String & second_name) -{ - std::multiset< std::pair > res; - input->readPrefix(); - while(1) - { - Block block = input->read(); - if (!block) break; - const ColumnWithNameAndType & first = block.getByName(first_name); - const ColumnWithNameAndType & second = block.getByName(second_name); - for (size_t i = 0; i < block.rows(); ++i) - { - T1 val1 = (*first.column)[i].get(); - T2 val2 = (*second.column)[i].get(); - res.insert(std::make_pair(val1, val2)); - } - } - return res; } } -} diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index 5828d2f64f6..d4b0906fb1b 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -48,9 +48,9 @@ public: } RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, - const String & _host_column_, const String & _port_column_, const Tables & external_tables_, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : pool_entry(pool_entry_), connection(*pool_entry), query(query_), _host_column(_host_column_), - _port_column(_port_column_), external_tables(external_tables_), stage(stage_), sent_query(false), finished(false), + const Tables & external_tables_, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + : pool_entry(pool_entry_), connection(*pool_entry), query(query_), + external_tables(external_tables_), stage(stage_), sent_query(false), finished(false), was_cancelled(false), got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection.getServerAddress() + ")")) { @@ -107,22 +107,6 @@ public: } protected: - void populateBlock(Block & res) - { - if (!_host_column.empty() && !res.has(_host_column)) - { - ColumnPtr column_ptr = ColumnConst(res.rows(), connection.getHost(), new DataTypeString).convertToFullColumn(); - ColumnWithNameAndType column(column_ptr, new DataTypeString, _host_column); - res.insert(column); - } - if (!_port_column.empty() && !res.has(_port_column)) - { - ColumnPtr column_ptr = ColumnConst(res.rows(), connection.getPort(), new DataTypeUInt16).convertToFullColumn(); - ColumnWithNameAndType column(column_ptr, new DataTypeUInt16, _port_column); - res.insert(column); - } - } - /// Отправить на удаленные сервера все временные таблицы void sendExternalTables() { @@ -159,10 +143,7 @@ protected: case Protocol::Server::Data: /// Если блок не пуст и не является заголовочным блоком if (packet.block && packet.block.rows() > 0) - { - populateBlock(packet.block); return packet.block; - } break; /// Если блок пустой - получим другие пакеты до EndOfStream. case Protocol::Server::Exception: @@ -269,10 +250,6 @@ private: const String query; bool send_settings; Settings settings; - /// Имя столбца, куда записать имя хоста (Например "_host"). Пустая строка, если записывать не надо. - String _host_column; - /// Имя столбца, куда записать номер порта (Например "_port"). Пустая строка, если записывать не надо. - String _port_column; /// Временные таблицы, которые необходимо переслать на удаленные сервера. Tables external_tables; QueryProcessingStage::Enum stage; diff --git a/dbms/include/DB/Storages/StorageDistributed.h b/dbms/include/DB/Storages/StorageDistributed.h index 416527b5207..4c426543325 100644 --- a/dbms/include/DB/Storages/StorageDistributed.h +++ b/dbms/include/DB/Storages/StorageDistributed.h @@ -45,8 +45,6 @@ public: bool supportsPrewhere() const { return true; } const NamesAndTypesList & getColumnsList() const { return *columns; } - NameAndTypePair getColumn(const String &column_name) const; - bool hasColumn(const String &column_name) const; bool isRemote() const { return true; } /// Сохранить временные таблицы, чтобы при следующем вызове метода read переслать их на удаленные сервера @@ -66,8 +64,6 @@ public: /// структура подтаблиц не проверяется void alter(const ASTAlterQuery::Parameters ¶ms); - Block getBlockWithVirtualColumns(); - private: StorageDistributed( const std::string & name_, @@ -78,8 +74,8 @@ private: const Context & context_, const String & sign_column_name_ = ""); - /// Создает копию запроса, меняет имена базы данных и таблицы, записавыет значения переменных host и port, если они не пустые. - ASTPtr remakeQuery(ASTPtr query, const String & host, size_t port); + /// Создает копию запроса, меняет имена базы данных и таблицы. + ASTPtr rewriteQuery(ASTPtr query); String name; NamesAndTypesListPtr columns; @@ -87,11 +83,6 @@ private: String remote_table; String sign_column_name; - /// Имя виртуального столбца, куда записывается имя хоста (Например "_host"). - String _host_column_name; - /// Имя виртуального столбца, куда записывается номер порта (Например "_port"). - String _port_column_name; - const Context & context; /// Временные таблицы, которые необходимо отправить на сервер. Переменная очищается после каждого вызова метода read diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index d60c2f80b64..650bbb6e71c 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -29,12 +29,6 @@ StorageDistributed::StorageDistributed( context(context_), cluster(cluster_) { - std::vector virtual_columns; - virtual_columns.push_back("_host"); - virtual_columns.push_back("_port"); - String suffix = VirtualColumnUtils::chooseSuffixForSet(getColumnsList(), virtual_columns); - _host_column_name = virtual_columns[0] + suffix; - _port_column_name = virtual_columns[1] + suffix; } StoragePtr StorageDistributed::create( @@ -68,37 +62,11 @@ StoragePtr StorageDistributed::create( return res->thisPtr(); } -NameAndTypePair StorageDistributed::getColumn(const String &column_name) const -{ - if (column_name == _host_column_name) - return std::make_pair(_host_column_name, new DataTypeString); - if (column_name == _port_column_name) - return std::make_pair(_port_column_name, new DataTypeUInt16); - - return getRealColumn(column_name); -} - -bool StorageDistributed::hasColumn(const String &column_name) const -{ - if (column_name == _host_column_name) - return true; - if (column_name == _port_column_name) - return true; - - return hasRealColumn(column_name); -} - -ASTPtr StorageDistributed::remakeQuery(ASTPtr query, const String & host, size_t port) +ASTPtr StorageDistributed::rewriteQuery(ASTPtr query) { /// Создаем копию запроса. ASTPtr modified_query_ast = query->clone(); - /// Добавляем в запрос значения хоста и порта, если требуется. - if (!host.empty()) - VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _host_column_name, host); - if (port != 0) - VirtualColumnUtils::rewriteEntityInAst(modified_query_ast, _port_column_name, port); - /// Меняем имена таблицы и базы данных ASTSelectQuery & select = dynamic_cast(*modified_query_ast); select.database = new ASTIdentifier(StringRange(), remote_database, ASTIdentifier::Database); @@ -123,110 +91,35 @@ BlockInputStreams StorageDistributed::read( size_t max_block_size, unsigned threads) { - /// Узнаем на каком порту слушает сервер - UInt16 clickhouse_port = Poco::Util::Application::instance().config().getInt("tcp_port", 0); - /// Установим sign_rewrite = 0, чтобы второй раз не переписывать запрос Settings new_settings = settings; new_settings.sign_rewrite = false; new_settings.queue_max_wait_ms = Cluster::saturate(new_settings.queue_max_wait_ms, settings.limits.max_execution_time); - /** Запрошены ли виртуальные столбцы? - * Если да - будем добавлять их в виде констант в запрос, предназначенный для выполнения на удалённом сервере, - * а также при получении результата с удалённого сервера. - */ - bool need_host_column = false; - bool need_port_column = false; - for (const auto & it : column_names) - { - if (it == _host_column_name) - need_host_column = true; - else if (it == _port_column_name) - need_port_column = true; - } - - /** Есть ли виртуальные столбцы в секции SELECT? - * Если нет - в случае вычисления запроса до стадии Complete, необходимо удалить их из блока. - */ - bool select_host_column = false; - bool select_port_column = false; - const ASTExpressionList & select_list = dynamic_cast(*(dynamic_cast(*query)).select_expression_list); - for (const auto & it : select_list.children) - { - if (const ASTIdentifier * identifier = dynamic_cast(&*it)) - { - if (identifier->name == _host_column_name) - select_host_column = true; - else if (identifier->name == _port_column_name) - select_port_column = true; - } - } - - Names columns_to_remove; - if (!select_host_column && need_host_column) - columns_to_remove.push_back(_host_column_name); - if (!select_port_column && need_port_column) - columns_to_remove.push_back(_port_column_name); - - Block virtual_columns_block = getBlockWithVirtualColumns(); - BlockInputStreamPtr virtual_columns; - - /// Если запрошен хотя бы один виртуальный столбец, пробуем индексировать - if (need_host_column || need_port_column) - virtual_columns = VirtualColumnUtils::getVirtualColumnsBlocks(query->clone(), virtual_columns_block, context); - else /// Иначе, считаем допустимыми все возможные значения - virtual_columns = new OneBlockInputStream(virtual_columns_block); - - std::multiset< std::pair > values = - VirtualColumnUtils::extractTwoValuesFromBlocks(virtual_columns, _host_column_name, _port_column_name); - bool all_inclusive = values.size() == virtual_columns_block.rows(); - - size_t result_size = values.size(); - if (cluster.getLocalNodesNum() > 0 && values.find(std::make_pair("localhost", clickhouse_port)) != values.end()) - result_size += cluster.getLocalNodesNum() - 1; + size_t result_size = cluster.pools.size() + cluster.getLocalNodesNum(); processed_stage = result_size == 1 ? QueryProcessingStage::Complete : QueryProcessingStage::WithMergeableState; BlockInputStreams res; + ASTPtr modified_query_ast = rewriteQuery(query); for (auto & conn_pool : cluster.pools) { - String current_host = conn_pool->get()->getHost(); - UInt16 current_port = conn_pool->get()->getPort(); + String modified_query = selectToString(modified_query_ast); - if (!all_inclusive && values.find(std::make_pair(current_host, current_port)) == values.end()) - continue; - - String modified_query = selectToString(remakeQuery( - query, - need_host_column ? current_host : "", - need_port_column ? current_port : 0)); - - BlockInputStreamPtr temp = new RemoteBlockInputStream( + res.push_back(new RemoteBlockInputStream( conn_pool->get(&new_settings), modified_query, &new_settings, - need_host_column ? _host_column_name : "", - need_port_column ? _port_column_name : "", external_tables, - processed_stage); - - if (processed_stage == QueryProcessingStage::WithMergeableState || columns_to_remove.empty()) - res.push_back(temp); - else - res.push_back(new RemoveColumnsBlockInputStream(temp, columns_to_remove)); + processed_stage)); } - if (cluster.getLocalNodesNum() > 0 && (all_inclusive || values.find(std::make_pair("localhost", clickhouse_port)) != values.end())) + /// Добавляем запросы к локальному ClickHouse + if (cluster.getLocalNodesNum() > 0) { - ASTPtr modified_query_ast = remakeQuery( - query, - need_host_column ? "localhost" : "", - need_port_column ? clickhouse_port : 0); - - /// Добавляем запросы к локальному ClickHouse DB::Context new_context = context; new_context.setSettings(new_settings); for (auto & it : external_tables) @@ -236,42 +129,11 @@ BlockInputStreams StorageDistributed::read( for(size_t i = 0; i < cluster.getLocalNodesNum(); ++i) { InterpreterSelectQuery interpreter(modified_query_ast, new_context, processed_stage); - if (processed_stage == QueryProcessingStage::WithMergeableState || columns_to_remove.empty()) res.push_back(interpreter.execute()); - else - res.push_back(new RemoveColumnsBlockInputStream(interpreter.execute(), columns_to_remove)); } } + external_tables.clear(); - - return res; -} - -/// Построить блок состоящий только из возможных значений виртуальных столбцов -Block StorageDistributed::getBlockWithVirtualColumns() -{ - Block res; - ColumnWithNameAndType _host(new ColumnString, new DataTypeString, _host_column_name); - ColumnWithNameAndType _port(new ColumnUInt16, new DataTypeUInt16, _port_column_name); - - for (ConnectionPools::iterator it = cluster.pools.begin(); it != cluster.pools.end(); ++it) - { - _host.column->insert((*it)->get()->getHost()); - _port.column->insert(static_cast((*it)->get()->getPort())); - } - - if (cluster.getLocalNodesNum() > 0) - { - /// Узнаем на каком порту слушает сервер - UInt64 clickhouse_port = Poco::Util::Application::instance().config().getInt("tcp_port", 0); - String clockhouse_host = "localhost"; - _host.column->insert(clockhouse_host); - _port.column->insert(clickhouse_port); - } - - res.insert(_host); - res.insert(_port); - return res; } From d986afc31910ff0602df6cd2d53a6a018bd88789 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 03:15:27 +0400 Subject: [PATCH 162/281] dbms: ConnectionPoolWithFailover: better [#METR-10500]. --- dbms/include/DB/Client/ConnectionPool.h | 10 +- .../DB/Client/ConnectionPoolWithFailover.h | 134 +++++++++++------- 2 files changed, 92 insertions(+), 52 deletions(-) diff --git a/dbms/include/DB/Client/ConnectionPool.h b/dbms/include/DB/Client/ConnectionPool.h index c8b31840a3d..e5c964e1faa 100644 --- a/dbms/include/DB/Client/ConnectionPool.h +++ b/dbms/include/DB/Client/ConnectionPool.h @@ -87,7 +87,7 @@ class IConnectionPool : private boost::noncopyable { public: typedef detail::ConnectionPoolEntry Entry; - virtual Entry get(Settings * settings = NULL) = 0; + virtual Entry get(Settings * settings = nullptr) = 0; virtual ~IConnectionPool() {} }; @@ -122,7 +122,7 @@ public: /** Выделяет соединение для работы. */ - Entry get(Settings * settings = NULL) + Entry get(Settings * settings = nullptr) { Poco::ScopedLock lock(mutex); @@ -136,7 +136,11 @@ public: return Entry(allocConnection()); LOG_INFO(log, "No free connections in pool. Waiting."); - available.wait(mutex); + + if (settings) + available.wait(mutex, settings->queue_max_wait_ms.totalMilliseconds()); + else + available.wait(mutex); } } diff --git a/dbms/include/DB/Client/ConnectionPoolWithFailover.h b/dbms/include/DB/Client/ConnectionPoolWithFailover.h index 1985e0cd3bd..ba32cec3e99 100644 --- a/dbms/include/DB/Client/ConnectionPoolWithFailover.h +++ b/dbms/include/DB/Client/ConnectionPoolWithFailover.h @@ -5,8 +5,6 @@ #include -#include - namespace DB { @@ -28,7 +26,7 @@ public: ConnectionPoolWithFailover(ConnectionPools & nested_pools_, LoadBalancing load_balancing, size_t max_tries_ = DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES, - size_t decrease_error_period_ = DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_DECREASE_ERROR_PERIOD) + time_t decrease_error_period_ = DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_DECREASE_ERROR_PERIOD) : nested_pools(nested_pools_.begin(), nested_pools_.end(), decrease_error_period_), max_tries(max_tries_), log(&Logger::get("ConnectionPoolWithFailover")), default_load_balancing(load_balancing) { @@ -41,22 +39,33 @@ public: if (settings) load_balancing = settings->load_balancing; - Poco::ScopedLock lock(mutex); - + /// Обновление случайных чисел, а также счётчиков ошибок. nested_pools.update(load_balancing); - std::sort(nested_pools.begin(), nested_pools.end(), boost::bind(&PoolWithErrorCount::compare, _1, _2, load_balancing)); + + typedef std::vector PoolPtrs; + + size_t pools_size = nested_pools.size(); + PoolPtrs pool_ptrs(pools_size); + for (size_t i = 0; i < pools_size; ++i) + pool_ptrs[i] = &nested_pools[i]; + + std::sort(pool_ptrs.begin(), pool_ptrs.end(), + [=](const PoolPtrs::value_type & lhs, const PoolPtrs::value_type & rhs) + { + return PoolWithErrorCount::compare(*lhs, *rhs, load_balancing); + }); std::stringstream fail_messages; for (size_t try_no = 0; try_no < max_tries; ++try_no) { - for (size_t i = 0, size = nested_pools.size(); i < size; ++i) + for (size_t i = 0; i < pools_size; ++i) { std::stringstream fail_message; try { - Entry res = nested_pools[i].pool->get(); + Entry res = pool_ptrs[i]->pool->get(settings); res->forceConnected(); return res; } @@ -73,8 +82,8 @@ public: fail_messages << fail_message.str() << std::endl; - ++nested_pools[i].random_error_count; - ++nested_pools[i].nearest_hostname_error_count; + __sync_fetch_and_add(&pool_ptrs[i]->random_error_count, 1); + __sync_fetch_and_add(&pool_ptrs[i]->nearest_hostname_error_count, 1); } } @@ -82,26 +91,26 @@ public: ErrorCodes::ALL_CONNECTION_TRIES_FAILED); } + private: struct PoolWithErrorCount { ConnectionPoolPtr pool; - UInt64 random_error_count; - UInt32 random; + UInt64 random_error_count = 0; + UInt32 random = 0; drand48_data rand_state; /// берётся имя локального сервера (Poco::Net::DNS::hostName) и имя хоста из конфига; строки обрезаются до минимальной длины; /// затем считается количество отличающихся позиций /// Пример example01-01-1 и example01-02-2 отличаются в двух позициях. - size_t hostname_difference; - UInt64 nearest_hostname_error_count; + size_t hostname_difference = 0; + UInt64 nearest_hostname_error_count = 0; - PoolWithErrorCount(const ConnectionPoolPtr & pool_) - : pool(pool_), random_error_count(0), random(0), nearest_hostname_error_count(0) + PoolWithErrorCount(const ConnectionPoolPtr & pool_) : pool(pool_) { /// Инициализация плохая, но это не важно. - srand48_r(reinterpret_cast(this), &rand_state); + srand48_r(reinterpret_cast(this), &rand_state); std::string local_hostname = Poco::Net::DNS::hostName(); @@ -109,12 +118,10 @@ private: const std::string & host = connection_pool.getHost(); hostname_difference = 0; for (size_t i = 0; i < std::min(local_hostname.length(), host.length()); ++i) - { if (local_hostname[i] != host[i]) ++hostname_difference; - } } - + void randomize() { long int rand_res; @@ -126,64 +133,93 @@ private: { if (load_balancing_mode == LoadBalancing::RANDOM) { - return lhs.random_error_count < rhs.random_error_count - || (lhs.random_error_count == rhs.random_error_count && lhs.random < rhs.random); + return std::tie(lhs.random_error_count, lhs.random) + < std::tie(rhs.random_error_count, rhs.random); } else if (load_balancing_mode == LoadBalancing::NEAREST_HOSTNAME) { - return lhs.nearest_hostname_error_count < rhs.nearest_hostname_error_count - || (lhs.nearest_hostname_error_count == rhs.nearest_hostname_error_count - && lhs.hostname_difference < rhs.hostname_difference); + return std::tie(lhs.nearest_hostname_error_count, lhs.hostname_difference) + < std::tie(rhs.nearest_hostname_error_count, rhs.hostname_difference); } else - throw Poco::Exception("Unsupported load_balancing_mode: " + toString(static_cast(load_balancing_mode))); + throw Exception("Unknown load_balancing_mode: " + toString(static_cast(load_balancing_mode)), ErrorCodes::LOGICAL_ERROR); } }; + class PoolsWithErrorCount : public std::vector { public: - PoolsWithErrorCount(DB::ConnectionPools::iterator first, DB::ConnectionPools::iterator last, - size_t decrease_error_period_) : - std::vector(first, last), last_get_time(0), - decrease_error_period(decrease_error_period_) + PoolsWithErrorCount(DB::ConnectionPools::iterator begin_, DB::ConnectionPools::iterator end_, + time_t decrease_error_period_) + : std::vector(begin_, end_), + decrease_error_period(decrease_error_period_) { } void update(LoadBalancing load_balancing_mode) { - if (load_balancing_mode == LoadBalancing::RANDOM) + Poco::ScopedLock lock(mutex); + + switch (load_balancing_mode) { - for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it) - it->randomize(); - } - /// Для режима NEAREST_HOSTNAME каждые N секунд уменьшаем количество ошибок в 2 раза - if (last_get_time) - { - time_t delta = time(0) - last_get_time; - for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it) + case LoadBalancing::RANDOM: { - it->nearest_hostname_error_count = it->nearest_hostname_error_count >> (delta / decrease_error_period); + for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it) + it->randomize(); + /// NOTE Почему бы не делить счётчики ошибок в случае LoadBalancing::RANDOM тоже? + break; } + + case LoadBalancing::NEAREST_HOSTNAME: + { + /// Для режима NEAREST_HOSTNAME каждые N секунд уменьшаем количество ошибок в 2 раза + time_t current_time = time(0); + + if (last_decrease_time) + { + time_t delta = current_time - last_decrease_time; + + if (delta < 0) + return; + + /// Каждые decrease_error_period секунд, делим количество ошибок на два. + size_t shift_amount = delta / decrease_error_period; + + if (shift_amount > sizeof(UInt64)) + { + last_decrease_time = current_time; + for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it) + it->nearest_hostname_error_count = 0; + } + else if (shift_amount) + { + last_decrease_time = current_time; + for (PoolsWithErrorCount::iterator it = begin(); it != end(); ++it) + it->nearest_hostname_error_count >>= shift_amount; + } + } + else + last_decrease_time = current_time; + + break; + } + + default: + throw Exception("Unknown load_balancing_mode: " + toString(static_cast(load_balancing_mode)), ErrorCodes::LOGICAL_ERROR); } - last_get_time = time(0); } private: - - /// время, когда последний раз вызывался update - time_t last_get_time; + /// Время, когда последний раз уменьшался счётчик ошибок для LoadBalancing::NEAREST_HOSTNAME + time_t last_decrease_time = 0; time_t decrease_error_period; + Poco::FastMutex mutex; }; - Poco::FastMutex mutex; - PoolsWithErrorCount nested_pools; - size_t max_tries; - Logger * log; - LoadBalancing default_load_balancing; }; From d8cf48d76569a07bad1cb17bc94d47eb3f907ccc Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 03:18:07 +0400 Subject: [PATCH 163/281] dbms: benchmark: better [#METR-2944]. --- dbms/src/Client/Benchmark.cpp | 36 ++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/dbms/src/Client/Benchmark.cpp b/dbms/src/Client/Benchmark.cpp index ae9b3180d41..488e7039cf4 100644 --- a/dbms/src/Client/Benchmark.cpp +++ b/dbms/src/Client/Benchmark.cpp @@ -63,6 +63,8 @@ public: readQueries(); run(); + + std::cerr << "\nTotal queries executed: " << queries_total << std::endl; } private: @@ -82,10 +84,11 @@ private: DataTypeFactory data_type_factory; ConnectionPool connections; - Stopwatch total_watch; - size_t total_queries = 0; - size_t total_rows = 0; - size_t total_bytes = 0; + Stopwatch watch_per_interval; + size_t queries_total = 0; + size_t queries_per_interval = 0; + size_t rows_per_interval = 0; + size_t bytes_per_interval = 0; ReservoirSampler sampler {1 << 16}; Poco::FastMutex mutex; @@ -118,7 +121,7 @@ private: InterruptListener interrupt_listener; - total_watch.restart(); + watch_per_interval.restart(); Stopwatch watch; /// В цикле, кладём все запросы в очередь. @@ -221,9 +224,10 @@ private: { Poco::ScopedLock lock(mutex); - ++total_queries; - total_rows += rows; - total_bytes += bytes; + ++queries_total; + ++queries_per_interval; + rows += rows_per_interval; + bytes += bytes_per_interval; sampler.insert(seconds); } @@ -234,15 +238,25 @@ private: std::cerr << std::endl - << "QPS: " << (total_queries / total_watch.elapsedSeconds()) << ", " - << "RPS: " << (total_rows / total_watch.elapsedSeconds()) << ", " - << "MiB/s: " << (total_bytes / total_watch.elapsedSeconds() / 1048576) << "." + << "QPS: " << (queries_per_interval / watch_per_interval.elapsedSeconds()) << ", " + << "RPS: " << (rows_per_interval / watch_per_interval.elapsedSeconds()) << ", " + << "MiB/s: " << (bytes_per_interval / watch_per_interval.elapsedSeconds() / 1048576) << "." << std::endl; for (size_t percent = 0; percent <= 100; percent += 10) std::cerr << percent << "%\t" << sampler.quantileInterpolated(percent / 100.0) << " sec." << std::endl; + resetCounts(); + } + + + void resetCounts() + { sampler.clear(); + queries_per_interval = 0; + rows_per_interval = 0; + bytes_per_interval = 0; + watch_per_interval.restart(); } }; From 6f12e7f372f83c6d47dc3c0ad2a37d6969a61e17 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 03:36:39 +0400 Subject: [PATCH 164/281] dbms: preparation [#METR-10500]. --- .../DB/DataStreams/RemoteBlockInputStream.h | 53 ++++--------------- .../DB/TableFunctions/TableFunctionRemote.h | 2 +- dbms/src/Client/Benchmark.cpp | 2 +- 3 files changed, 13 insertions(+), 44 deletions(-) diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index d4b0906fb1b..86dc546a53c 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -16,43 +16,12 @@ namespace DB class RemoteBlockInputStream : public IProfilingBlockInputStream { public: - RemoteBlockInputStream(Connection & connection_, const String & query_, const Settings * settings_, - QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : connection(connection_), query(query_), stage(stage_), - sent_query(false), finished(false), was_cancelled(false), got_exception_from_server(false), - log(&Logger::get("RemoteBlockInputStream (" + connection.getServerAddress() + ")")) - { - if (settings_) - { - send_settings = true; - settings = *settings_; - } - else - send_settings = false; - } - - /// Захватывает владение соединением из пула. RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, - QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : pool_entry(pool_entry_), connection(*pool_entry), query(query_), - stage(stage_), sent_query(false), finished(false), was_cancelled(false), - got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection.getServerAddress() + ")")) - { - if (settings_) - { - send_settings = true; - settings = *settings_; - } - else - send_settings = false; - } - - RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, - const Tables & external_tables_, QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : pool_entry(pool_entry_), connection(*pool_entry), query(query_), + const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + : pool_entry(pool_entry_), connection(&*pool_entry), query(query_), external_tables(external_tables_), stage(stage_), sent_query(false), finished(false), was_cancelled(false), - got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection.getServerAddress() + ")")) + got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection->getServerAddress() + ")")) { if (settings_) { @@ -91,7 +60,7 @@ public: LOG_TRACE(log, "Cancelling query"); /// Если запрошено прервать запрос - попросим удалённый сервер тоже прервать запрос. - connection.sendCancel(); + connection->sendCancel(); was_cancelled = true; } } @@ -103,7 +72,7 @@ public: * чтобы оно не осталось висеть в рассихронизированном состоянии. */ if (sent_query && !finished) - connection.disconnect(); + connection->disconnect(); } protected: @@ -122,21 +91,21 @@ protected: else res.push_back(std::make_pair(input[0], it->first)); } - connection.sendExternalTablesData(res); + connection->sendExternalTablesData(res); } Block readImpl() { if (!sent_query) { - connection.sendQuery(query, "", stage, send_settings ? &settings : NULL, true); + connection->sendQuery(query, "", stage, send_settings ? &settings : NULL, true); sendExternalTables(); sent_query = true; } while (true) { - Connection::Packet packet = connection.receivePacket(); + Connection::Packet packet = connection->receivePacket(); switch (packet.type) { @@ -208,13 +177,13 @@ protected: LOG_TRACE(log, "Cancelling query because enough data has been read"); was_cancelled = true; - connection.sendCancel(); + connection->sendCancel(); } /// Получим оставшиеся пакеты, чтобы не было рассинхронизации в соединении с сервером. while (true) { - Connection::Packet packet = connection.receivePacket(); + Connection::Packet packet = connection->receivePacket(); switch (packet.type) { @@ -245,7 +214,7 @@ private: /// Используется, если нужно владеть соединением из пула ConnectionPool::Entry pool_entry; - Connection & connection; + Connection * connection = nullptr; const String query; bool send_settings; diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index 621a7278abb..3fbdcce9563 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -91,7 +91,7 @@ private: NamesAndTypesList res; - BlockInputStreamPtr input = new RemoteBlockInputStream(entry, query, &settings, QueryProcessingStage::Complete); + BlockInputStreamPtr input = new RemoteBlockInputStream(entry, query, &settings, Tables(), QueryProcessingStage::Complete); input->readPrefix(); while (true) diff --git a/dbms/src/Client/Benchmark.cpp b/dbms/src/Client/Benchmark.cpp index 488e7039cf4..b23fae67901 100644 --- a/dbms/src/Client/Benchmark.cpp +++ b/dbms/src/Client/Benchmark.cpp @@ -205,7 +205,7 @@ private: void execute(ConnectionPool::Entry & connection, Query & query) { Stopwatch watch; - RemoteBlockInputStream stream(*connection, query, nullptr); + RemoteBlockInputStream stream(connection, query, nullptr); size_t rows = 0; size_t bytes = 0; From 763f354b3d23e09c34988e456c6212ef3a8bc102 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 04:00:23 +0400 Subject: [PATCH 165/281] dbms: probably lowered latency of global distributed queries [#METR-10500]. --- .../DB/DataStreams/RemoteBlockInputStream.h | 48 ++++++++++++------- .../DB/TableFunctions/TableFunctionRemote.h | 6 +-- dbms/src/Storages/StorageDistributed.cpp | 5 +- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index 86dc546a53c..b749097c3b0 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -16,12 +16,26 @@ namespace DB class RemoteBlockInputStream : public IProfilingBlockInputStream { public: + /// Принимает готовое соединение. RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : pool_entry(pool_entry_), connection(&*pool_entry), query(query_), - external_tables(external_tables_), stage(stage_), sent_query(false), finished(false), - was_cancelled(false), - got_exception_from_server(false), log(&Logger::get("RemoteBlockInputStream (" + connection->getServerAddress() + ")")) + : connection(pool_entry_), query(query_), + external_tables(external_tables_), stage(stage_) + { + if (settings_) + { + send_settings = true; + settings = *settings_; + } + else + send_settings = false; + } + + /// Принимает пул, из которого нужно будет достать соединение. + RemoteBlockInputStream(IConnectionPool * pool_, const String & query_, const Settings * settings_, + const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + : pool(pool_), query(query_), + external_tables(external_tables_), stage(stage_) { if (settings_) { @@ -57,7 +71,7 @@ public: if (sent_query && !was_cancelled && !finished && !got_exception_from_server) { - LOG_TRACE(log, "Cancelling query"); + LOG_TRACE(log, "(" + connection->getServerAddress() + ") Cancelling query"); /// Если запрошено прервать запрос - попросим удалённый сервер тоже прервать запрос. connection->sendCancel(); @@ -98,7 +112,11 @@ protected: { if (!sent_query) { - connection->sendQuery(query, "", stage, send_settings ? &settings : NULL, true); + /// Если надо - достаём соединение из пула. + if (pool) + connection = pool->get(send_settings ? &settings : nullptr); + + connection->sendQuery(query, "", stage, send_settings ? &settings : nullptr, true); sendExternalTables(); sent_query = true; } @@ -174,7 +192,7 @@ protected: /// Отправим просьбу прервать выполнение запроса, если ещё не отправляли. if (!was_cancelled) { - LOG_TRACE(log, "Cancelling query because enough data has been read"); + LOG_TRACE(log, "(" + connection->getServerAddress() + ") Cancelling query because enough data has been read"); was_cancelled = true; connection->sendCancel(); @@ -211,10 +229,8 @@ protected: } private: - /// Используется, если нужно владеть соединением из пула - ConnectionPool::Entry pool_entry; - - Connection * connection = nullptr; + IConnectionPool * pool = nullptr; + ConnectionPool::Entry connection; const String query; bool send_settings; @@ -224,25 +240,25 @@ private: QueryProcessingStage::Enum stage; /// Отправили запрос (это делается перед получением первого блока). - bool sent_query; + bool sent_query = false; /** Получили все данные от сервера, до пакета EndOfStream. * Если при уничтожении объекта, ещё не все данные считаны, * то для того, чтобы не было рассинхронизации, на сервер отправляется просьба прервать выполнение запроса, * и после этого считываются все пакеты до EndOfStream. */ - bool finished; + bool finished = false; /** На сервер была отправлена просьба прервать выполенение запроса, так как данные больше не нужны. * Это может быть из-за того, что данных достаточно (например, при использовании LIMIT), * или если на стороне клиента произошло исключение. */ - bool was_cancelled; + bool was_cancelled = false; /// С сервера было получено исключение. В этом случае получать больше пакетов или просить прервать запрос не нужно. - bool got_exception_from_server; + bool got_exception_from_server = false; - Logger * log; + Logger * log = &Logger::get("RemoteBlockInputStream"); }; } diff --git a/dbms/include/DB/TableFunctions/TableFunctionRemote.h b/dbms/include/DB/TableFunctions/TableFunctionRemote.h index 3fbdcce9563..137b32c18a9 100644 --- a/dbms/include/DB/TableFunctions/TableFunctionRemote.h +++ b/dbms/include/DB/TableFunctions/TableFunctionRemote.h @@ -86,12 +86,10 @@ private: /// Запрос на описание таблицы String query = "DESC TABLE " + database + "." + table; Settings settings = context.getSettings(); - /// Отправляем на первый попавшийся сервер - auto entry = (*cluster.pools.begin())->get(&settings); - NamesAndTypesList res; - BlockInputStreamPtr input = new RemoteBlockInputStream(entry, query, &settings, Tables(), QueryProcessingStage::Complete); + /// Отправляем на первый попавшийся шард + BlockInputStreamPtr input = new RemoteBlockInputStream(&*cluster.pools.front(), query, &settings, Tables(), QueryProcessingStage::Complete); input->readPrefix(); while (true) diff --git a/dbms/src/Storages/StorageDistributed.cpp b/dbms/src/Storages/StorageDistributed.cpp index 650bbb6e71c..a8bfc5dc10c 100644 --- a/dbms/src/Storages/StorageDistributed.cpp +++ b/dbms/src/Storages/StorageDistributed.cpp @@ -105,19 +105,20 @@ BlockInputStreams StorageDistributed::read( BlockInputStreams res; ASTPtr modified_query_ast = rewriteQuery(query); + /// Цикл по шардам. for (auto & conn_pool : cluster.pools) { String modified_query = selectToString(modified_query_ast); res.push_back(new RemoteBlockInputStream( - conn_pool->get(&new_settings), + conn_pool, modified_query, &new_settings, external_tables, processed_stage)); } - /// Добавляем запросы к локальному ClickHouse + /// Добавляем запросы к локальному ClickHouse. if (cluster.getLocalNodesNum() > 0) { DB::Context new_context = context; From a3efebda9d60893638625bb915c8aa6a770bc9ed Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 04:09:19 +0400 Subject: [PATCH 166/281] dbms: addition to prev. revision [#METR-10500]. --- .../DB/DataStreams/RemoteBlockInputStream.h | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h index b749097c3b0..0fbbd76f696 100644 --- a/dbms/include/DB/DataStreams/RemoteBlockInputStream.h +++ b/dbms/include/DB/DataStreams/RemoteBlockInputStream.h @@ -15,12 +15,8 @@ namespace DB */ class RemoteBlockInputStream : public IProfilingBlockInputStream { -public: - /// Принимает готовое соединение. - RemoteBlockInputStream(ConnectionPool::Entry pool_entry_, const String & query_, const Settings * settings_, - const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : connection(pool_entry_), query(query_), - external_tables(external_tables_), stage(stage_) +private: + void init(const Settings * settings_) { if (settings_) { @@ -30,20 +26,29 @@ public: else send_settings = false; } +public: + /// Принимает готовое соединение. + RemoteBlockInputStream(Connection & connection_, const String & query_, const Settings * settings_, + const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + : connection(&connection_), query(query_), external_tables(external_tables_), stage(stage_) + { + init(settings_); + } + + /// Принимает готовое соединение. Захватывает владение соединением из пула. + RemoteBlockInputStream(ConnectionPool::Entry & pool_entry_, const String & query_, const Settings * settings_, + const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) + : pool_entry(pool_entry_), connection(&*pool_entry_), query(query_), external_tables(external_tables_), stage(stage_) + { + init(settings_); + } /// Принимает пул, из которого нужно будет достать соединение. RemoteBlockInputStream(IConnectionPool * pool_, const String & query_, const Settings * settings_, const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete) - : pool(pool_), query(query_), - external_tables(external_tables_), stage(stage_) + : pool(pool_), query(query_), external_tables(external_tables_), stage(stage_) { - if (settings_) - { - send_settings = true; - settings = *settings_; - } - else - send_settings = false; + init(settings_); } @@ -114,7 +119,10 @@ protected: { /// Если надо - достаём соединение из пула. if (pool) - connection = pool->get(send_settings ? &settings : nullptr); + { + pool_entry = pool->get(send_settings ? &settings : nullptr); + connection = &*pool_entry; + } connection->sendQuery(query, "", stage, send_settings ? &settings : nullptr, true); sendExternalTables(); @@ -230,7 +238,8 @@ protected: private: IConnectionPool * pool = nullptr; - ConnectionPool::Entry connection; + ConnectionPool::Entry pool_entry; + Connection * connection = nullptr; const String query; bool send_settings; From f8d9252cf41c143af411bb77adf614f9f74fe343 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 04:31:44 +0400 Subject: [PATCH 167/281] dbms: fixed error in client [#METR-10071]. --- dbms/src/Client/Client.cpp | 6 +++--- dbms/src/Client/Connection.cpp | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dbms/src/Client/Client.cpp b/dbms/src/Client/Client.cpp index 1a723213ab4..98c4cd224cf 100644 --- a/dbms/src/Client/Client.cpp +++ b/dbms/src/Client/Client.cpp @@ -550,7 +550,7 @@ private: if (exit_strings.end() != exit_strings.find(line)) return false; - block_std_out = NULL; + block_std_out = nullptr; watch.restart(); @@ -614,7 +614,7 @@ private: /// Обработать запрос, который не требует передачи блоков данных на сервер. void processOrdinaryQuery() { - connection->sendQuery(query, "", QueryProcessingStage::Complete, NULL, true); + connection->sendQuery(query, "", QueryProcessingStage::Complete, nullptr, true); sendExternalTables(); receiveResult(); } @@ -632,7 +632,7 @@ private: if (!parsed_insert_query.data && (is_interactive || (stdin_is_not_tty && std_in.eof()))) throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); - connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete, NULL, true); + connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete, nullptr, true); sendExternalTables(); /// Получим структуру таблицы diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index c39173b4a33..270f841c41d 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -279,7 +279,10 @@ void Connection::sendExternalTablesData(ExternalTablesData & data) { /// Если работаем со старым сервером, то никакой информации не отправляем if (server_revision < DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES) + { + out->next(); return; + } for (size_t i = 0; i < data.size(); ++i) { From 4e975b43ecd6bc352524e5159f56b9780eba50da Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 7 Apr 2014 19:45:46 +0400 Subject: [PATCH 168/281] Merge --- dbms/include/DB/Common/ProfileEvents.h | 4 + .../DB/Storages/MergeTree/MergeTreeData.h | 12 +- .../ReplicatedMergeTreeBlockOutputStream.h | 3 +- .../DB/Storages/StorageReplicatedMergeTree.h | 61 ++++- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 53 +++-- .../MergeTree/MergeTreeDataMerger.cpp | 35 ++- .../Storages/StorageReplicatedMergeTree.cpp | 208 +++++++++++++++--- 7 files changed, 300 insertions(+), 76 deletions(-) diff --git a/dbms/include/DB/Common/ProfileEvents.h b/dbms/include/DB/Common/ProfileEvents.h index fb28b5304ff..41dcc0fbd15 100644 --- a/dbms/include/DB/Common/ProfileEvents.h +++ b/dbms/include/DB/Common/ProfileEvents.h @@ -26,6 +26,10 @@ M(FunctionExecute, "Function executes") \ M(MarkCacheHits, "Mark cache hits") \ M(MarkCacheMisses, "Mark cache misses") \ + M(ReplicatedPartFetches, "Replicated part fetches") \ + M(ObsoleteReplicatedParts, "Replicated parts rendered obsolete by fetches") \ + M(ReplicatedPartMerges, "Replicated part merges") \ + M(ReplicatedPartFetchesOfMerged, "Replicated part merges replaced with fetches") \ \ M(END, "") diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index e913bfeb9fb..4c9288d93c2 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -346,14 +346,16 @@ public: */ DataPartPtr getContainingPart(const String & part_name); - /** Удаляет куски old_parts и добавляет кусок new_part. Если какого-нибудь из удаляемых кусков нет, бросает исключение. - */ - void replaceParts(DataPartsVector old_parts, DataPartPtr new_part); - /** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор. * Если increment!=nullptr, индекс куска берется из инкремента. Иначе индекс куска не меняется. + * Предполагается, что кусок не пересекается с существующими. */ - void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment); + void renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment = nullptr); + + /** То же, что renameTempPartAndAdd, но кусок может покрывать существующие куски. + * Удаляет и возвращает все куски, покрытые добавляемым (в возрастающем порядке). + */ + DataPartsVector renameTempPartAndReplace(MutableDataPartPtr part, Increment * increment = nullptr); /** Переименовывает кусок в prefix_кусок и убирает его из рабочего набора. * Лучше использовать только когда никто не может читать или писать этот кусок diff --git a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h index cad531aa01d..4634cda4365 100644 --- a/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.h @@ -45,10 +45,11 @@ public: continue; } - storage.data.renameTempPartAndAdd(part, nullptr); + storage.data.renameTempPartAndAdd(part); StorageReplicatedMergeTree::LogEntry log_entry; log_entry.type = StorageReplicatedMergeTree::LogEntry::GET_PART; + log_entry.source_replica = storage.replica_name; log_entry.new_part_name = part->name; String checksums_str = part->checksums.toString(); diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index efb46e84947..240d8076dd3 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -67,6 +67,7 @@ public: private: friend class ReplicatedMergeTreeBlockOutputStream; + /// Добавляет куски в множество currently_merging. struct CurrentlyMergingPartsTagger { Strings parts; @@ -105,6 +106,36 @@ private: typedef Poco::SharedPtr CurrentlyMergingPartsTaggerPtr; + /// Добавляет кусок в множество future_parts. + struct FuturePartTagger + { + String part; + StorageReplicatedMergeTree & storage; + + FuturePartTagger(const String & part_, StorageReplicatedMergeTree & storage_) + : part(part_), storage(storage_) + { + if (!storage.future_parts.insert(part).second) + throw Exception("Tagging already tagged future part " + part + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + } + + ~FuturePartTagger() + { + try + { + Poco::ScopedLock lock(storage.queue_mutex); + if (!storage.currently_merging.erase(part)) + throw Exception("Untagging already untagged future part " + part + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + } + }; + + typedef Poco::SharedPtr FuturePartTaggerPtr; + struct LogEntry { enum Type @@ -116,10 +147,12 @@ private: String znode_name; Type type; + String source_replica; String new_part_name; Strings parts_to_merge; CurrentlyMergingPartsTaggerPtr currently_merging_tagger; + FuturePartTaggerPtr future_part_tagger; void tagPartsAsCurrentlyMerging(StorageReplicatedMergeTree & storage) { @@ -127,6 +160,12 @@ private: currently_merging_tagger = new CurrentlyMergingPartsTagger(parts_to_merge, storage); } + void tagPartAsFuture(StorageReplicatedMergeTree & storage) + { + if (type == MERGE_PARTS || type == GET_PART) + future_part_tagger = new FuturePartTagger(new_part_name, storage); + } + void writeText(WriteBuffer & out) const; void readText(ReadBuffer & in); @@ -162,12 +201,17 @@ private: StringSet currently_merging; Poco::FastMutex currently_merging_mutex; - /** "Очередь" того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из ZooKeeper (/replicas/me/queue/). - * В ZK записи в хронологическом порядке. Здесь записи в том порядке, в котором их лучше выполнять. + /** Очередь того, что нужно сделать на этой реплике, чтобы всех догнать. Берется из ZooKeeper (/replicas/me/queue/). + * В ZK записи в хронологическом порядке. Здесь - не обязательно. */ LogEntries queue; Poco::FastMutex queue_mutex; + /** Куски, которые появятся в результате действий, выполняемых прямо сейчас фоновыми потоками (этих действий нет в очереди). + * Использовать под залоченным queue_mutex. + */ + StringSet future_parts; + String table_name; String full_path; @@ -256,11 +300,10 @@ private: */ void pullLogsToQueue(); - /** Делает преобразования над очередью: - * - Если есть MERGE_PARTS кусков, не все из которых у нас есть, заменяем его на GET_PART и - * убираем GET_PART для всех составляющих его кусков. NOTE: Наверно, это будет плохо работать. Придумать эвристики получше. + /** Можно ли сейчас попробовать выполнить это действие. Если нет, нужно оставить его в очереди и попробовать выполнить другое. + * Вызывается под queue_mutex. */ - void optimizeQueue(); + bool shouldExecuteLogEntry(const LogEntry & entry); /** Выполнить действие из очереди. Бросает исключение, если что-то не так. */ @@ -274,11 +317,11 @@ private: */ void queueThread(); - void becomeLeader(); - /// Выбор кусков для слияния. - /** В бесконечном цикле выбирается куски для слияния и записывает в лог. + void becomeLeader(); + + /** В бесконечном цикле выбирает куски для слияния и записывает в лог. */ void mergeSelectingThread(); diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 6a6a4ada49f..1e151552d0c 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -661,23 +661,17 @@ Strings MergeTreeData::tryRestorePart(const String & path, const String & file_n return restored_parts; } -void MergeTreeData::replaceParts(DataPartsVector old_parts, DataPartPtr new_part) +void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment) { - Poco::ScopedLock lock(data_parts_mutex); - Poco::ScopedLock all_lock(all_data_parts_mutex); - - for (size_t i = 0; i < old_parts.size(); ++i) - if (data_parts.end() == data_parts.find(old_parts[i])) - throw Exception("Logical error: cannot find data part " + old_parts[i]->name + " in list", ErrorCodes::LOGICAL_ERROR); - - data_parts.insert(new_part); - all_data_parts.insert(new_part); - - for (size_t i = 0; i < old_parts.size(); ++i) - data_parts.erase(data_parts.find(old_parts[i])); + auto removed = renameTempPartAndReplace(part, increment); + if (!removed.empty()) + { + LOG_ERROR(log, "Added part " << part->name << + " covers " << toString(removed.size()) + << " existing part(s) (including " << removed[0]->name << ")"); + } } -void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment) +MergeTreeData::DataPartsVector MergeTreeData::renameTempPartAndReplace(MutableDataPartPtr part, Increment * increment) { LOG_TRACE(log, "Renaming."); @@ -686,17 +680,14 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in String old_path = getFullPath() + part->name + "/"; - UInt64 part_id = part->left; - - /** Важно, что получение номера куска происходит атомарно с добавлением этого куска в набор. + /** Для StorageMergeTree важно, что получение номера куска происходит атомарно с добавлением этого куска в набор. * Иначе есть race condition - может произойти слияние пары кусков, диапазоны номеров которых * содержат ещё не добавленный кусок. */ if (increment) - part_id = increment->get(false); + part->left = part->right = increment->get(false); - part->left = part->right = part_id; - part->name = getPartName(part->left_date, part->right_date, part_id, part_id, 0); + part->name = getPartName(part->left_date, part->right_date, part->left, part->right, 0); if (data_parts.count(part)) throw Exception("Part " + part->name + " already exists", ErrorCodes::DUPLICATE_DATA_PART); @@ -706,8 +697,30 @@ void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * in /// Переименовываем кусок. Poco::File(old_path).renameTo(new_path); + DataPartsVector res; + /// Куски, содержащиеся в part, идут в data_parts подряд, задевая место, куда вставился бы сам part. + DataParts::iterator it = data_parts.lower_bound(part); + /// Пойдем влево. + while (it != data_parts.begin()) + { + --it; + if (!part->contains(**it)) + break; + res.push_back(*it); + data_parts.erase(it++); /// Да, ++, а не --. + } + std::reverse(res.begin(), res.end()); /// Нужно получить куски в порядке возрастания. + /// Пойдем вправо. + while (it != data_parts.end() && part->contains(**it)) + { + res.push_back(*it); + data_parts.erase(it++); + } + data_parts.insert(part); all_data_parts.insert(part); + + return res; } void MergeTreeData::renameAndDetachPart(DataPartPtr part, const String & prefix) diff --git a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp index 9448823a1ed..8eee0e6f869 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp @@ -248,7 +248,7 @@ MergeTreeData::DataPartPtr MergeTreeDataMerger::mergeParts(const MergeTreeData:: MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared(data); data.parsePartName(merged_name, *new_data_part); - new_data_part->name = merged_name; + new_data_part->name = "tmp_" + merged_name; /** Читаем из всех кусков, сливаем и пишем в новый. * Попутно вычисляем выражение для сортировки. @@ -285,8 +285,7 @@ MergeTreeData::DataPartPtr MergeTreeDataMerger::mergeParts(const MergeTreeData:: throw Exception("Unknown mode of operation for MergeTreeData: " + toString(data.mode), ErrorCodes::LOGICAL_ERROR); } - String new_part_tmp_path = data.getFullPath() + "tmp_" + new_data_part->name + "/"; - String new_part_res_path = data.getFullPath() + new_data_part->name + "/"; + String new_part_tmp_path = data.getFullPath() + "tmp_" + merged_name + "/"; MergedBlockOutputStreamPtr to = new MergedBlockOutputStream(data, new_part_tmp_path, data.getColumnsList()); @@ -314,15 +313,33 @@ MergeTreeData::DataPartPtr MergeTreeDataMerger::mergeParts(const MergeTreeData:: if (0 == to->marksCount()) { - LOG_INFO(log, "All rows have been deleted while merging from " << parts.front()->name << " to " << parts.back()->name); - return nullptr; + throw Exception("All rows have been deleted while merging from " + parts.front()->name + + " to " + parts.back()->name, ErrorCodes::LOGICAL_ERROR); } - /// Переименовываем кусок. - Poco::File(new_part_tmp_path).renameTo(new_part_res_path); + /// Переименовываем новый кусок, добавляем в набор и убираем исходные куски. + auto replaced_parts = data.renameTempPartAndReplace(new_data_part); - /// Добавляем новый кусок в набор. - data.replaceParts(parts, new_data_part); + if (new_data_part->name != merged_name) + LOG_ERROR(log, "Unexpected part name: " << new_data_part->name << " instead of " << merged_name); + + /// Проверим, что удалились все исходные куски и только они. + if (replaced_parts.size() != parts.size()) + { + LOG_ERROR(log, "Unexpected number of parts removed when adding " << new_data_part->name << ": " << replaced_parts.size() + << " instead of " << parts.size()); + } + else + { + for (size_t i = 0; i < parts.size(); ++i) + { + if (parts[i]->name != replaced_parts[i]->name) + { + LOG_ERROR(log, "Unexpected part removed when adding " << new_data_part->name << ": " << replaced_parts[i]->name + << " instead of " << parts[i]->name); + } + } + } LOG_TRACE(log, "Merged " << parts.size() << " parts: from " << parts.front()->name << " to " << parts.back()->name); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index ed863d41268..c3bec5c6293 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -369,8 +369,26 @@ void StorageReplicatedMergeTree::pullLogsToQueue() LOG_DEBUG(log, "Pulled " << count << " entries to queue"); } -void StorageReplicatedMergeTree::optimizeQueue() +bool StorageReplicatedMergeTree::shouldExecuteLogEntry(const LogEntry & entry) { + if (entry.type == LogEntry::MERGE_PARTS) + { + /** Если какая-то из нужных частей сейчас передается или мерджится, подождем окончания этой операции. + * Иначе, даже если всех нужных частей для мерджа нет, нужно попытаться сделать мердж. + * Если каких-то частей не хватает, вместо мерджа будет попытка скачать кусок. + * Такая ситуация возможна, если получение какого-то куска пофейлилось, и его переместили в конец очереди. + */ + for (const auto & name : entry.parts_to_merge) + { + if (future_parts.count(name)) + { + LOG_TRACE(log, "Not merging into part " << entry.new_part_name << " yet because part " << name << " is not ready yet."); + return false; + } + } + } + + return true; } void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) @@ -384,54 +402,148 @@ void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) /// Даже если кусок есть локально, его (в исключительных случаях) может не быть в zookeeper. if (containing_part && zookeeper.exists(replica_path + "/parts/" + containing_part->name)) { - LOG_DEBUG(log, "Skipping action for part " + entry.new_part_name + " - part already exists"); + if (!(entry.type == LogEntry::GET_PART && entry.source_replica == replica_name)) + LOG_DEBUG(log, "Skipping action for part " + entry.new_part_name + " - part already exists"); return; } } + if (entry.type == LogEntry::GET_PART && entry.source_replica == replica_name) + LOG_ERROR(log, "Part " << entry.new_part_name << " from own log doesn't exist. This is a bug."); + + bool do_fetch = false; + if (entry.type == LogEntry::GET_PART) { - String replica = findActiveReplicaHavingPart(entry.new_part_name); - fetchPart(entry.new_part_name, replica); + do_fetch = true; } else if (entry.type == LogEntry::MERGE_PARTS) { MergeTreeData::DataPartsVector parts; + bool have_all_parts = true;; for (const String & name : entry.parts_to_merge) { MergeTreeData::DataPartPtr part = data.getContainingPart(name); - if (!part || part->name != name) - throw Exception("Part to merge doesn't exist: " + name, ErrorCodes::NOT_FOUND_EXPECTED_DATA_PART); + if (!part) + { + have_all_parts = false; + break; + } + if (part->name != name) + { + LOG_ERROR(log, "Log and parts set look inconsistent: " << name << " is covered by " << part->name + << " but should be merged into " << entry.new_part_name); + have_all_parts = false; + break; + } parts.push_back(part); } - MergeTreeData::DataPartPtr part = merger.mergeParts(parts, entry.new_part_name); - zkutil::Ops ops; - ops.push_back(new zkutil::Op::Create( - replica_path + "/parts/" + part->name, - "", - zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - ops.push_back(new zkutil::Op::Create( - replica_path + "/parts/" + part->name + "/checksums", - part->checksums.toString(), - zookeeper.getDefaultACL(), - zkutil::CreateMode::Persistent)); - - for (const auto & part : parts) + if (!have_all_parts) { - ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name + "/checksums", -1)); - ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name, -1)); + /// Если нет всех нужных кусков, попробуем взять у кого-нибудь уже помердженный кусок. + do_fetch = true; + LOG_DEBUG(log, "Don't have all parts for merge " << entry.new_part_name << "; will try to fetch it instead"); } + else + { + MergeTreeData::DataPartPtr part = merger.mergeParts(parts, entry.new_part_name); - zookeeper.multi(ops); + zkutil::Ops ops; + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name, + "", + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); + ops.push_back(new zkutil::Op::Create( + replica_path + "/parts/" + part->name + "/checksums", + part->checksums.toString(), + zookeeper.getDefaultACL(), + zkutil::CreateMode::Persistent)); - data.clearOldParts(); + for (const auto & part : parts) + { + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name + "/checksums", -1)); + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + part->name, -1)); + } + + zookeeper.multi(ops); + + data.clearOldParts(); + + ProfileEvents::increment(ProfileEvents::ReplicatedPartMerges); + } } else { throw Exception("Unexpected log entry type: " + toString(static_cast(entry.type))); } + + if (do_fetch) + { + try + { + String replica = findActiveReplicaHavingPart(entry.new_part_name); + fetchPart(entry.new_part_name, replica); + + if (entry.type == LogEntry::MERGE_PARTS) + ProfileEvents::increment(ProfileEvents::ReplicatedPartFetchesOfMerged); + } + catch (...) + { + /** Если не получилось скачать кусок, нужный для какого-то мерджа, лучше не пытаться получить другие куски для этого мерджа, + * а попытаться сразу получить помердженный кусок. Чтобы так получилось, переместим действия для получения остальных кусков + * для этого мерджа в конец очереди. + * + */ + try + { + Poco::ScopedLock lock(queue_mutex); + + /// Найдем действие по объединению этого куска с другими. Запомним других. + StringSet parts_for_merge; + LogEntries::iterator merge_entry; + for (LogEntries::iterator it = queue.begin(); it != queue.end(); ++it) + { + if (it->type == LogEntry::MERGE_PARTS) + { + if (std::find(it->parts_to_merge.begin(), it->parts_to_merge.end(), entry.new_part_name) + != it->parts_to_merge.end()) + { + parts_for_merge = StringSet(it->parts_to_merge.begin(), it->parts_to_merge.end()); + merge_entry = it; + break; + } + } + } + + if (!parts_for_merge.empty()) + { + /// Переместим в конец очереди действия, получающие parts_for_merge. + for (LogEntries::iterator it = queue.begin(); it != queue.end();) + { + auto it0 = it; + ++it; + + if (it0 == merge_entry) + break; + + if ((it0->type == LogEntry::MERGE_PARTS || it0->type == LogEntry::GET_PART) + && parts_for_merge.count(it0->new_part_name)) + { + queue.splice(queue.end(), queue, it0, it); + } + } + } + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + + throw; + } + } } void StorageReplicatedMergeTree::queueUpdatingThread() @@ -441,7 +553,6 @@ void StorageReplicatedMergeTree::queueUpdatingThread() try { pullLogsToQueue(); - optimizeQueue(); } catch (...) { @@ -457,19 +568,33 @@ void StorageReplicatedMergeTree::queueThread() while (!shutdown_called) { LogEntry entry; - bool empty; + bool have_work = false; + try { Poco::ScopedLock lock(queue_mutex); - empty = queue.empty(); + bool empty = queue.empty(); if (!empty) { - entry = queue.front(); - queue.pop_front(); + for (LogEntries::iterator it = queue.begin(); it != queue.end(); ++it) + { + if (shouldExecuteLogEntry(*it)) + { + entry = *it; + entry.tagPartAsFuture(*this); + queue.erase(it); + have_work = true; + break; + } + } } } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } - if (empty) + if (!have_work) { std::this_thread::sleep_for(QUEUE_NO_WORK_SLEEP); continue; @@ -480,7 +605,11 @@ void StorageReplicatedMergeTree::queueThread() try { executeLogEntry(entry); - zookeeper.remove(replica_path + "/queue/" + entry.znode_name); + + auto code = zookeeper.tryRemove(replica_path + "/queue/" + entry.znode_name); + if (code != zkutil::ReturnCode::Ok) + LOG_ERROR(log, "Couldn't remove " << replica_path + "/queue/" + entry.znode_name << ": " + << zkutil::ReturnCode::toString(code) + ". There must be a bug somewhere. Ignoring it."); success = true; } @@ -500,6 +629,7 @@ void StorageReplicatedMergeTree::queueThread() { { /// Добавим действие, которое не получилось выполнить, в конец очереди. + entry.future_part_tagger = nullptr; Poco::ScopedLock lock(queue_mutex); queue.push_back(entry); } @@ -571,6 +701,7 @@ void StorageReplicatedMergeTree::mergeSelectingThread() { LogEntry entry; entry.type = LogEntry::MERGE_PARTS; + entry.source_replica = replica_name; entry.new_part_name = merged_name; for (const auto & part : parts) @@ -681,7 +812,13 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin assertEOF(buf); MergeTreeData::MutableDataPartPtr part = fetcher.fetchPart(part_name, zookeeper_path + "/replicas/" + replica_name, host, port); - data.renameTempPartAndAdd(part, nullptr); + auto removed_parts = data.renameTempPartAndReplace(part); + + for (const auto & removed_part : removed_parts) + { + LOG_DEBUG(log, "Part " << removed_part->name << " is rendered obsolete by fetching part " << part_name); + ProfileEvents::increment(ProfileEvents::ObsoleteReplicatedParts); + } zkutil::Ops ops; ops.push_back(new zkutil::Op::Create( @@ -696,6 +833,8 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin zkutil::CreateMode::Persistent)); zookeeper.multi(ops); + ProfileEvents::increment(ProfileEvents::ReplicatedPartFetches); + LOG_DEBUG(log, "Fetched part"); } @@ -762,6 +901,9 @@ void StorageReplicatedMergeTree::drop() void StorageReplicatedMergeTree::LogEntry::writeText(WriteBuffer & out) const { writeString("format version: 1\n", out); + writeString("source replica: ", out); + writeString(source_replica, out); + writeString("\n", out); switch (type) { case GET_PART: @@ -788,6 +930,8 @@ void StorageReplicatedMergeTree::LogEntry::readText(ReadBuffer & in) assertString("format version: 1\n", in); readString(type_str, in); + assertString("\nsource replica: ", in); + readString(source_replica, in); assertString("\n", in); if (type_str == "get") From 2a408f28415558acdd09f105b6213b36039f3ba4 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Thu, 20 Mar 2014 16:25:26 +0400 Subject: [PATCH 169/281] development [#METR-10498] --- .../DB/Storages/MergeTree/PKCondition.h | 38 +++++++++++++++++++ dbms/src/Storages/MergeTree/PKCondition.cpp | 14 ++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index b90e4f8ee2e..f1ece3ed72e 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -116,6 +116,37 @@ public: #pragma GCC diagnostic pop +struct Set +{ +public: + Set(DB::ASTPtr column_set_ptr_) : column_set_ptr(column_set_ptr_) + { + } + + std::string toString() + { + std::ostringstream ss; + ss << "{"; + bool first = true; + for (auto & n : data) + { + if (first) + { + ss << n; + first = false; + } + else + { + ss << ", " << n; + } + } + ss << "}"; + return ss.str(); + } + +private: + DB::ASTPtr column_set_ptr; +}; /** Диапазон с открытыми или закрытыми концами; возможно, неограниченный. */ @@ -295,6 +326,7 @@ private: /// Атомы логического выражения. FUNCTION_IN_RANGE, FUNCTION_NOT_IN_RANGE, + FUNCTION_IN_SET, FUNCTION_UNKNOWN, /// Может принимать любое значение. /// Операторы логического выражения. FUNCTION_NOT, @@ -320,6 +352,10 @@ private: return "not"; case FUNCTION_UNKNOWN: return "unknown"; + case FUNCTION_IN_SET: + std::ostringstream ss; + ss << "(column " << key_column << " in " << set.toString() << ")"; + return ss.str(); case FUNCTION_IN_RANGE: case FUNCTION_NOT_IN_RANGE: { @@ -337,6 +373,8 @@ private: /// Для FUNCTION_IN_RANGE и FUNCTION_NOT_IN_RANGE. Range range; size_t key_column; + /// Для FUNCTION_IN_SET + Set set; }; typedef std::vector RPN; diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index 60404456475..d5c6ba60a2b 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -1,6 +1,7 @@ #include #include #include +#include namespace DB { @@ -114,7 +115,7 @@ void PKCondition::traverseAST(ASTPtr & node, Block & block_with_constants) bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNElement & out) { - /// Фнукции < > = != <= >=, у которых один агрумент константа, другой - один из столбцов первичного ключа. + /// Фнукции < > = != <= >= in , у которых один агрумент константа, другой - один из столбцов первичного ключа. if (ASTFunction * func = dynamic_cast(&*node)) { ASTs & args = dynamic_cast(*func->arguments).children; @@ -126,6 +127,7 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl bool inverted; size_t column; Field value; + if (pk_columns.count(args[0]->getColumnName()) && getConstant(args[1], block_with_constants, value)) { inverted = false; @@ -136,6 +138,11 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl inverted = true; column = pk_columns[args[1]->getColumnName()]; } + /// для In, notIn + else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1])) + { + column = pk_columns[args[1]->getColumnName()]; + } else return false; @@ -172,6 +179,11 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl out.range = Range::createRightBounded(value, true); else if (func->name == "greaterOrEquals") out.range = Range::createLeftBounded(value, true); + else if (func->name == "in" || func->name == "notIn") + { + out.function = RPNElement::FUNCTION_IN_SET; + out.set = Set(args[1]); + } else return false; From 14168120f1165d72bb61223ae14c505934cda72e Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 26 Mar 2014 14:56:21 +0400 Subject: [PATCH 170/281] development [#METR-10498] --- dbms/include/DB/Core/StringRef.h | 5 + dbms/include/DB/Interpreters/Set.h | 112 ++++++++++++++++++ .../DB/Storages/MergeTree/PKCondition.h | 55 ++++----- dbms/src/Storages/MergeTree/PKCondition.cpp | 41 +++---- 4 files changed, 156 insertions(+), 57 deletions(-) diff --git a/dbms/include/DB/Core/StringRef.h b/dbms/include/DB/Core/StringRef.h index 43c2d1d2917..fbef3c363ef 100644 --- a/dbms/include/DB/Core/StringRef.h +++ b/dbms/include/DB/Core/StringRef.h @@ -69,6 +69,11 @@ namespace DB } return true; } + + inline bool operator<(StringRef lhs, StringRef rhs) + { + return strcmp(lhs.data, rhs.data) < 0 ? true : false; + } } namespace std diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 795dc492307..625232a0954 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -19,6 +20,8 @@ #include #include +#include + namespace DB { @@ -60,7 +63,39 @@ public: * Записать результат в столбец в позиции result. */ void execute(Block & block, const ColumnNumbers & arguments, size_t result, bool negative) const; + + std::string toString() + { + if (type == KEY_64) + return setToString(key64); + else if (type == KEY_STRING) + return setToString(key_string); + else if (type == HASHED) + return setToString(hashed); + else if (type == EMPTY) + return "{}"; + } + void createOrderedSet() + { + for (auto & key : key64) + ordered_key64.push_back(key); + std::sort(ordered_key64.begin(), ordered_key64.end()); + + for (auto & key : key_string) + ordered_string.push_back(key); + std::sort(ordered_string.begin(), ordered_string.end()); + } + + BoolMask mayBeTrueInRange(const Range & key_range) + { + if (type == KEY_64) + return mayBeTrueInRangeImpl(key_range, ordered_key64); + else if (type == KEY_STRING) + return mayBeTrueInRangeImpl(key_range, ordered_string); + else + throw DB::Exception("Unsupported set of type " << type); + } private: /** Разные структуры данных, которые могут использоваться для проверки принадлежности * одного или нескольких столбцов значений множеству. @@ -125,6 +160,83 @@ private: size_t getTotalRowCount() const; /// Считает суммарный размер в байтах буфферов всех Set'ов + размер string_pool'а size_t getTotalByteCount() const; + + template + std::string setToString(const T & set) + { + std::stringstream ss; + bool first = false; + for (auto & n : set) + { + if (first) + { + ss << n; + first = false; + } + else + { + ss << ", " << n; + } + } + + ss << "}"; + return ss.str(); + } + + /// несколько столбцов пока не поддерживаем + std::vector ordered_key64; + std::vector ordered_string; + + template + BoolMask mayBeTrueInRangeImpl(const Range & key_range, const Set & set) + { + bool can_be_true; + bool can_be_false = true; + + /// Если во всем диапазоне одинаковый ключ и он есть в Set, то выбираем блок для in и не выбираем для notIn + if (key_range.left == key_range.right) + { + if (set.find(key_range.left) != set.end()) + { + can_be_false = false; + can_be_true = true; + } + } + else + { + auto left_it = set.lower_boud(key_range.left); + /// если весь диапазон, правее in + if (left_it == set.end()) + { + can_be_true = false; + } + else + { + auto right_it = set.upper_bound(key_range.right); + /// весь диапазон, левее in + if (right_it == set.begin()) + { + can_be_true = false; + } + else + { + --right_it; + /// в диапазон не попадает ни одного ключа из in + if (*right_it < *left_it) + { + can_be_true = false; + } + else + { + can_be_true = true; + } + } + } + } + + return BoolMask(can_be_true, can_be_false); + } + }; typedef SharedPtr SetPtr; diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index f1ece3ed72e..fdf1f82d481 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace DB @@ -114,40 +115,31 @@ public: bool operator() (const Array & l, const Array & r) const { return l < r; } }; -#pragma GCC diagnostic pop - -struct Set +/// Множество значений булевой переменной. То есть два булевых значения: может ли быть true, может ли быть false. +struct BoolMask { -public: - Set(DB::ASTPtr column_set_ptr_) : column_set_ptr(column_set_ptr_) - { - } + bool can_be_true; + bool can_be_false; - std::string toString() - { - std::ostringstream ss; - ss << "{"; - bool first = true; - for (auto & n : data) - { - if (first) - { - ss << n; - first = false; - } - else - { - ss << ", " << n; - } - } - ss << "}"; - return ss.str(); - } + BoolMask() {} + BoolMask(bool can_be_true_, bool can_be_false_) : can_be_true(can_be_true_), can_be_false(can_be_false_) {} -private: - DB::ASTPtr column_set_ptr; + BoolMask operator &(const BoolMask & m) + { + return BoolMask(can_be_true && m.can_be_true, can_be_false || m.can_be_false); + } + BoolMask operator |(const BoolMask & m) + { + return BoolMask(can_be_true || m.can_be_true, can_be_false && m.can_be_false); + } + BoolMask operator !() + { + return BoolMask(can_be_false, can_be_true); + } }; +#pragma GCC diagnostic pop + /** Диапазон с открытыми или закрытыми концами; возможно, неограниченный. */ struct Range @@ -327,6 +319,7 @@ private: FUNCTION_IN_RANGE, FUNCTION_NOT_IN_RANGE, FUNCTION_IN_SET, + FUNCTION_NOT_IN_SET, FUNCTION_UNKNOWN, /// Может принимать любое значение. /// Операторы логического выражения. FUNCTION_NOT, @@ -354,7 +347,7 @@ private: return "unknown"; case FUNCTION_IN_SET: std::ostringstream ss; - ss << "(column " << key_column << " in " << set.toString() << ")"; + ss << "(column " << key_column << " in " << set->toString() << ")"; return ss.str(); case FUNCTION_IN_RANGE: case FUNCTION_NOT_IN_RANGE: @@ -374,7 +367,7 @@ private: Range range; size_t key_column; /// Для FUNCTION_IN_SET - Set set; + SetPtr set; }; typedef std::vector RPN; diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index d5c6ba60a2b..be553444200 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -141,7 +141,10 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl /// для In, notIn else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1])) { - column = pk_columns[args[1]->getColumnName()]; + /// не поддерживаем Primary Key, если аргумент функции in tuple + if (dynamic_cast(args[0])) + return false; + column = pk_columns[args[0]->getColumnName()]; } else return false; @@ -181,8 +184,9 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl out.range = Range::createLeftBounded(value, true); else if (func->name == "in" || func->name == "notIn") { - out.function = RPNElement::FUNCTION_IN_SET; - out.set = Set(args[1]); + out.function = func->name == "in" ? RPNElement::FUNCTION_IN_SET : RPNElement::FUNCTION_NOT_IN_SET; + out.set = (dynamic_cast(args[1])->getData(); + out.set->createOrderedSet(); } else return false; @@ -230,29 +234,6 @@ String PKCondition::toString() return res; } -/// Множество значений булевой переменной. То есть два булевых значения: может ли быть true, может ли быть false. -struct BoolMask -{ - bool can_be_true; - bool can_be_false; - - BoolMask() {} - BoolMask(bool can_be_true_, bool can_be_false_) : can_be_true(can_be_true_), can_be_false(can_be_false_) {} - - BoolMask operator &(const BoolMask & m) - { - return BoolMask(can_be_true && m.can_be_true, can_be_false || m.can_be_false); - } - BoolMask operator |(const BoolMask & m) - { - return BoolMask(can_be_true || m.can_be_true, can_be_false && m.can_be_false); - } - BoolMask operator !() - { - return BoolMask(can_be_false, can_be_true); - } -}; - bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk, bool right_bounded) { /// Найдем диапазоны элементов ключа. @@ -296,6 +277,14 @@ bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk if (element.function == RPNElement::FUNCTION_NOT_IN_RANGE) rpn_stack.back() = !rpn_stack.back(); } + else if (element.function == RPNElement::FUNCTION_IN_SET || element.function == RPNElement::FUNCTION_NOT_IN_SET) + { + const Range & key_range = key_ranges[element.key_column]; + + rpn_stack.push_back(element.set->mayBeTrueInRange(key_range)); + if (element.function == RPNElement::FUNCTION_NOT_IN_SET) + rpn_stack.back() = !rpn_stack.back(); + } else if (element.function == RPNElement::FUNCTION_NOT) { rpn_stack.back() = !rpn_stack.back(); From a737895eb449234ad579a5d4682221e992584e3b Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Wed, 26 Mar 2014 22:19:25 +0400 Subject: [PATCH 171/281] build fix [#METR-10498] --- dbms/include/DB/Core/StringRef.h | 8 +++ dbms/include/DB/Interpreters/Set.h | 55 ++++++++++++------- dbms/include/DB/Storages/MergeTree/BoolMask.h | 24 ++++++++ .../DB/Storages/MergeTree/PKCondition.h | 32 ++--------- dbms/src/Storages/MergeTree/PKCondition.cpp | 11 ++-- 5 files changed, 79 insertions(+), 51 deletions(-) create mode 100644 dbms/include/DB/Storages/MergeTree/BoolMask.h diff --git a/dbms/include/DB/Core/StringRef.h b/dbms/include/DB/Core/StringRef.h index fbef3c363ef..c5177b3151f 100644 --- a/dbms/include/DB/Core/StringRef.h +++ b/dbms/include/DB/Core/StringRef.h @@ -74,6 +74,14 @@ namespace DB { return strcmp(lhs.data, rhs.data) < 0 ? true : false; } + + inline std::ostream & operator<<(std::ostream & os, const StringRef & str) + { + if (str.data) + return os << str.toString(); + else + return os; + } } namespace std diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 625232a0954..7fd58a56dd5 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -20,8 +20,7 @@ #include #include -#include - +#include namespace DB { @@ -64,16 +63,18 @@ public: */ void execute(Block & block, const ColumnNumbers & arguments, size_t result, bool negative) const; - std::string toString() + std::string descibe() { if (type == KEY_64) return setToString(key64); else if (type == KEY_STRING) return setToString(key_string); else if (type == HASHED) - return setToString(hashed); + return "{hashed values}"; else if (type == EMPTY) return "{}"; + else + throw DB::Exception("Unknown type"); } void createOrderedSet() @@ -87,14 +88,17 @@ public: std::sort(ordered_string.begin(), ordered_string.end()); } - BoolMask mayBeTrueInRange(const Range & key_range) + BoolMask mayBeTrueInRange(const Field & left, const Field & right) { if (type == KEY_64) - return mayBeTrueInRangeImpl(key_range, ordered_key64); + return mayBeTrueInRangeImpl(left, right, ordered_key64); else if (type == KEY_STRING) - return mayBeTrueInRangeImpl(key_range, ordered_string); - else - throw DB::Exception("Unsupported set of type " << type); + return mayBeTrueInRangeImpl(left, right, ordered_string); + else{ + std::stringstream ss; + ss << "Unsupported set of type " << type; + throw DB::Exception(ss.str()); + } } private: /** Разные структуры данных, которые могут использоваться для проверки принадлежности @@ -166,16 +170,17 @@ private: { std::stringstream ss; bool first = false; - for (auto & n : set) + + for (auto it = set.begin(); it != set.end(); ++it) { if (first) { - ss << n; + ss << *it; first = false; } else { - ss << ", " << n; + ss << ", " << *it; } } @@ -187,34 +192,42 @@ private: std::vector ordered_key64; std::vector ordered_string; - template - BoolMask mayBeTrueInRangeImpl(const Range & key_range, const Set & set) + template + BoolMask mayBeTrueInRangeImpl(const Field & field_left, const Field & field_right, const std::vector & v) { + T left = field_left.get(); + T right = field_right.get(); + bool can_be_true; bool can_be_false = true; /// Если во всем диапазоне одинаковый ключ и он есть в Set, то выбираем блок для in и не выбираем для notIn - if (key_range.left == key_range.right) + if (left == right) { - if (set.find(key_range.left) != set.end()) + if (std::find(v.begin(), v.end(), left) != v.end()) { can_be_false = false; can_be_true = true; } + else + { + can_be_true = false; + can_be_false = true; + } } else { - auto left_it = set.lower_boud(key_range.left); + auto left_it = std::lower_bound(v.begin(), v.end(), left); /// если весь диапазон, правее in - if (left_it == set.end()) + if (left_it == v.end()) { can_be_true = false; } else { - auto right_it = set.upper_bound(key_range.right); + auto right_it = std::upper_bound(v.begin(), v.end(), right); /// весь диапазон, левее in - if (right_it == set.begin()) + if (right_it == v.begin()) { can_be_true = false; } @@ -239,7 +252,7 @@ private: }; -typedef SharedPtr SetPtr; +typedef Poco::SharedPtr SetPtr; typedef std::vector Sets; diff --git a/dbms/include/DB/Storages/MergeTree/BoolMask.h b/dbms/include/DB/Storages/MergeTree/BoolMask.h new file mode 100644 index 00000000000..dbc591d8b22 --- /dev/null +++ b/dbms/include/DB/Storages/MergeTree/BoolMask.h @@ -0,0 +1,24 @@ +#pragma once + +/// Множество значений булевой переменной. То есть два булевых значения: может ли быть true, может ли быть false. +struct BoolMask +{ + bool can_be_true; + bool can_be_false; + + BoolMask() {} + BoolMask(bool can_be_true_, bool can_be_false_) : can_be_true(can_be_true_), can_be_false(can_be_false_) {} + + BoolMask operator &(const BoolMask & m) + { + return BoolMask(can_be_true && m.can_be_true, can_be_false || m.can_be_false); + } + BoolMask operator |(const BoolMask & m) + { + return BoolMask(can_be_true || m.can_be_true, can_be_false && m.can_be_false); + } + BoolMask operator !() + { + return BoolMask(can_be_false, can_be_true); + } +}; diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index fdf1f82d481..f3dfd56f9bc 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace DB @@ -115,29 +116,6 @@ public: bool operator() (const Array & l, const Array & r) const { return l < r; } }; -/// Множество значений булевой переменной. То есть два булевых значения: может ли быть true, может ли быть false. -struct BoolMask -{ - bool can_be_true; - bool can_be_false; - - BoolMask() {} - BoolMask(bool can_be_true_, bool can_be_false_) : can_be_true(can_be_true_), can_be_false(can_be_false_) {} - - BoolMask operator &(const BoolMask & m) - { - return BoolMask(can_be_true && m.can_be_true, can_be_false || m.can_be_false); - } - BoolMask operator |(const BoolMask & m) - { - return BoolMask(can_be_true || m.can_be_true, can_be_false && m.can_be_false); - } - BoolMask operator !() - { - return BoolMask(can_be_false, can_be_true); - } -}; - #pragma GCC diagnostic pop /** Диапазон с открытыми или закрытыми концами; возможно, неограниченный. @@ -335,6 +313,7 @@ private: String toString() { + std::ostringstream ss; switch (function) { case FUNCTION_AND: @@ -345,14 +324,15 @@ private: return "not"; case FUNCTION_UNKNOWN: return "unknown"; + case FUNCTION_NOT_IN_SET: case FUNCTION_IN_SET: - std::ostringstream ss; - ss << "(column " << key_column << " in " << set->toString() << ")"; + { + ss << "(column " << key_column << (function == FUNCTION_IN_SET ? " in " : " notIn ") << set->descibe() << ")"; return ss.str(); + } case FUNCTION_IN_RANGE: case FUNCTION_NOT_IN_RANGE: { - std::ostringstream ss; ss << "(column " << key_column << (function == FUNCTION_NOT_IN_RANGE ? " not" : "") << " in " << range.toString() << ")"; return ss.str(); } diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index be553444200..d6f63d56cf5 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace DB { @@ -139,10 +140,12 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl column = pk_columns[args[1]->getColumnName()]; } /// для In, notIn - else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1])) + else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1].get())) { + /// для in не бывает inverted + inverted = false; /// не поддерживаем Primary Key, если аргумент функции in tuple - if (dynamic_cast(args[0])) + if (dynamic_cast(args[0].get())) return false; column = pk_columns[args[0]->getColumnName()]; } @@ -185,7 +188,7 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl else if (func->name == "in" || func->name == "notIn") { out.function = func->name == "in" ? RPNElement::FUNCTION_IN_SET : RPNElement::FUNCTION_NOT_IN_SET; - out.set = (dynamic_cast(args[1])->getData(); + out.set = (dynamic_cast(args[1].get())->getData()); out.set->createOrderedSet(); } else @@ -281,7 +284,7 @@ bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk { const Range & key_range = key_ranges[element.key_column]; - rpn_stack.push_back(element.set->mayBeTrueInRange(key_range)); + rpn_stack.push_back(element.set->mayBeTrueInRange(key_range.left, key_range.right)); if (element.function == RPNElement::FUNCTION_NOT_IN_SET) rpn_stack.back() = !rpn_stack.back(); } From bdeee5386b9e620735779ef0e1b461f9b04f55e2 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Mon, 31 Mar 2014 18:49:43 +0400 Subject: [PATCH 172/281] dbms: development [#METR-10498] Signed-off-by: Pavel Kartavyy --- .../DB/Interpreters/ExpressionAnalyzer.h | 7 +++++ dbms/include/DB/Interpreters/Set.h | 1 + .../DB/Storages/MergeTree/PKCondition.h | 3 +- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 31 +++++++++++++++++-- .../Interpreters/InterpreterSelectQuery.cpp | 1 + dbms/src/Storages/MergeTree/PKCondition.cpp | 30 +++++++++++------- 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index c3418bc12d8..9bb436a3109 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -101,6 +101,10 @@ public: /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN. Tables external_tables; + + /// создает Set, заданные явно + void makeExplicitSets(); + private: typedef std::set NamesSet; @@ -313,6 +317,9 @@ private: void assertSelect(); void assertAggregation(); void assertArrayJoin(); + + void makeExplicitSet(ASTFunction * node, const Block & sample_block); + void makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block); }; } diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 7fd58a56dd5..73bc5e93bdb 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -21,6 +21,7 @@ #include #include +#include namespace DB { diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index f3dfd56f9bc..e7fb71f4007 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -347,7 +346,7 @@ private: Range range; size_t key_column; /// Для FUNCTION_IN_SET - SetPtr set; + ASTPtr in_function; }; typedef std::vector RPN; diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 208bc0c1e9f..f1ad0c5ee65 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -498,6 +498,26 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as finished_asts[initial_ast] = ast; } +void ExpressionAnalyzer::makeExplicitSets() +{ + makeExplicitSetsRecursively(ast, storage->getSampleBlock()); +} + +void ExpressionAnalyzer::makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block) +{ + for (auto & child : node->children) + makeExplicitSetsRecursively(child, sample_block); + + ASTFunction * func = dynamic_cast(node.get()); + if (func && func->kind == ASTFunction::FUNCTION && (func->name == "in" || func->name == "notIn")) + { + IAST & args = *func->arguments; + ASTPtr & arg = args.children[1]; + + if (!dynamic_cast(&*arg) && !dynamic_cast(&*arg)) + makeExplicitSet(func, sample_block); + } +} void ExpressionAnalyzer::findGlobalFunctions(ASTPtr & ast, std::vector & global_nodes) { @@ -679,7 +699,15 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) } else { - /// Случай явного перечисления значений. + makeExplicitSet(node, sample_block); + } +} + +/// Случай явного перечисления значений. +void ExpressionAnalyzer::makeExplicitSet(ASTFunction * node, const Block & sample_block) +{ + IAST & args = *node->arguments; + ASTPtr & arg = args.children[1]; DataTypes set_element_types; ASTPtr & left_arg = args.children[0]; @@ -741,7 +769,6 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) ast_set->set = new Set(settings.limits); ast_set->set->createFromAST(set_element_types, elements_ast); arg = ast_set_ptr; - } } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 9e8a4bcc61f..5d8f3fe4c81 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -501,6 +501,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns; + query_analyzer->makeExplicitSets(); /// Инициализируем изначальные потоки данных, на которые накладываются преобразования запроса. Таблица или подзапрос? if (!interpreter_subquery) { diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index d6f63d56cf5..00fe4201505 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace DB { @@ -139,15 +140,11 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl inverted = true; column = pk_columns[args[1]->getColumnName()]; } - /// для In, notIn - else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1].get())) + else if (pk_columns.count(args[0]->getColumnName()) && dynamic_cast(args[1].get())) { - /// для in не бывает inverted inverted = false; - /// не поддерживаем Primary Key, если аргумент функции in tuple - if (dynamic_cast(args[0].get())) - return false; column = pk_columns[args[0]->getColumnName()]; + dynamic_cast(args[1].get())->set->createOrderedSet(); } else return false; @@ -188,8 +185,7 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl else if (func->name == "in" || func->name == "notIn") { out.function = func->name == "in" ? RPNElement::FUNCTION_IN_SET : RPNElement::FUNCTION_NOT_IN_SET; - out.set = (dynamic_cast(args[1].get())->getData()); - out.set->createOrderedSet(); + out.in_function = node; } else return false; @@ -282,11 +278,21 @@ bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk } else if (element.function == RPNElement::FUNCTION_IN_SET || element.function == RPNElement::FUNCTION_NOT_IN_SET) { - const Range & key_range = key_ranges[element.key_column]; + ASTFunction * in_func = dynamic_cast(element.in_function.get()); + ASTs & args = dynamic_cast(*in_func->arguments).children; + ASTSet * ast_set = dynamic_cast(args[1].get()); + if (in_func && ast_set) + { + const Range & key_range = key_ranges[element.key_column]; - rpn_stack.push_back(element.set->mayBeTrueInRange(key_range.left, key_range.right)); - if (element.function == RPNElement::FUNCTION_NOT_IN_SET) - rpn_stack.back() = !rpn_stack.back(); + rpn_stack.push_back(ast_set->set->mayBeTrueInRange(key_range.left, key_range.right)); + if (element.function == RPNElement::FUNCTION_NOT_IN_SET) + rpn_stack.back() = !rpn_stack.back(); + } + else + { + throw DB::Exception("Set for IN is not created yet!"); + } } else if (element.function == RPNElement::FUNCTION_NOT) { From 049db94426019aab9415494eb64695e8f5863259 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Tue, 1 Apr 2014 14:09:22 +0400 Subject: [PATCH 173/281] dbms: development [#METR-10498] --- dbms/include/DB/Core/Field.h | 31 +++- .../DB/Interpreters/ExpressionAnalyzer.h | 9 +- dbms/include/DB/Interpreters/Set.h | 140 +++--------------- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 16 +- .../Interpreters/InterpreterSelectQuery.cpp | 3 +- dbms/src/Interpreters/Set.cpp | 91 +++++++++++- dbms/src/Storages/MergeTree/PKCondition.cpp | 3 +- 7 files changed, 153 insertions(+), 140 deletions(-) diff --git a/dbms/include/DB/Core/Field.h b/dbms/include/DB/Core/Field.h index bfbc9d93a0f..8d106ba4b6b 100644 --- a/dbms/include/DB/Core/Field.h +++ b/dbms/include/DB/Core/Field.h @@ -352,7 +352,36 @@ private: } }; - +static std::ostream & operator<< (std::ostream & os, const Field & x) +{ + bool first = false; + switch (x.getType()) + { + case Field::Types::Null: os << "Null"; break; + case Field::Types::UInt64: os << x.get(); break; + case Field::Types::Int64: os << x.get(); break; + case Field::Types::Float64: os << x.get(); break; + case Field::Types::String: os << x.get(); break; + case Field::Types::Array: + for(const Field & f : x.get()) + { + os << "["; + if (first) + os << f; + else + os << ", " << f; + os << "]"; + } + break; + default: + { + std::stringstream ss; + ss << "Unsupported type " << x.getTypeName(); + throw DB::Exception(ss.str()); + } + } + return os; +} template <> struct Field::TypeToEnum { static const Types::Which value = Types::Null; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::UInt64; }; template <> struct Field::TypeToEnum { static const Types::Which value = Types::Int64; }; diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 9bb436a3109..9d4c2beeec8 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -102,9 +102,8 @@ public: /// Все новые временные таблицы, полученные при выполнении подзапросов GLOBAL IN. Tables external_tables; - /// создает Set, заданные явно - void makeExplicitSets(); - + /// ordered_set нужен если в In используется Primary Key + void makeExplicitSets(bool create_ordered_set); private: typedef std::set NamesSet; @@ -318,8 +317,8 @@ private: void assertAggregation(); void assertArrayJoin(); - void makeExplicitSet(ASTFunction * node, const Block & sample_block); - void makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block); + void makeExplicitSet(ASTFunction * node, const Block & sample_block, bool create_ordered_set); + void makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block, bool create_ordered_set); }; } diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 73bc5e93bdb..91123c58538 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -46,7 +46,7 @@ public: * types - типы того, что стоит слева от IN. * node - это список значений: 1, 2, 3 или список tuple-ов: (1, 2), (3, 4), (5, 6). */ - void createFromAST(DataTypes & types, ASTPtr node); + void createFromAST(DataTypes & types, ASTPtr node, bool create_ordered_set); /** Запомнить поток блоков (для подзапросов), чтобы потом его можно было прочитать и создать множество. */ @@ -55,7 +55,7 @@ public: BlockInputStreamPtr getSource() { return source; } // Возвращает false, если превышено какое-нибудь ограничение, и больше не нужно вставлять. - bool insertFromBlock(Block & block); + bool insertFromBlock(Block & block, bool create_ordered_set = false); size_t size() const { return getTotalRowCount(); } @@ -66,41 +66,25 @@ public: std::string descibe() { - if (type == KEY_64) - return setToString(key64); - else if (type == KEY_STRING) - return setToString(key_string); - else if (type == HASHED) - return "{hashed values}"; - else if (type == EMPTY) + if (!ordered_set) return "{}"; - else - throw DB::Exception("Unknown type"); - } - - void createOrderedSet() - { - for (auto & key : key64) - ordered_key64.push_back(key); - std::sort(ordered_key64.begin(), ordered_key64.end()); - - for (auto & key : key_string) - ordered_string.push_back(key); - std::sort(ordered_string.begin(), ordered_string.end()); - } - - BoolMask mayBeTrueInRange(const Field & left, const Field & right) - { - if (type == KEY_64) - return mayBeTrueInRangeImpl(left, right, ordered_key64); - else if (type == KEY_STRING) - return mayBeTrueInRangeImpl(left, right, ordered_string); - else{ - std::stringstream ss; - ss << "Unsupported set of type " << type; - throw DB::Exception(ss.str()); + + bool first = true; + std::stringstream ss; + + for (const Field & f : *ordered_set) + { + if (!first) + ss << ", " << f; + else + ss << f; + first = false; } + return ss.str(); } + + BoolMask mayBeTrueInRange(const Range & range); + private: /** Разные структуры данных, которые могут использоваться для проверки принадлежности * одного или нескольких столбцов значений множеству. @@ -166,91 +150,9 @@ private: /// Считает суммарный размер в байтах буфферов всех Set'ов + размер string_pool'а size_t getTotalByteCount() const; - template - std::string setToString(const T & set) - { - std::stringstream ss; - bool first = false; - - for (auto it = set.begin(); it != set.end(); ++it) - { - if (first) - { - ss << *it; - first = false; - } - else - { - ss << ", " << *it; - } - } - - ss << "}"; - return ss.str(); - } - - /// несколько столбцов пока не поддерживаем - std::vector ordered_key64; - std::vector ordered_string; - - template - BoolMask mayBeTrueInRangeImpl(const Field & field_left, const Field & field_right, const std::vector & v) - { - T left = field_left.get(); - T right = field_right.get(); - - bool can_be_true; - bool can_be_false = true; - - /// Если во всем диапазоне одинаковый ключ и он есть в Set, то выбираем блок для in и не выбираем для notIn - if (left == right) - { - if (std::find(v.begin(), v.end(), left) != v.end()) - { - can_be_false = false; - can_be_true = true; - } - else - { - can_be_true = false; - can_be_false = true; - } - } - else - { - auto left_it = std::lower_bound(v.begin(), v.end(), left); - /// если весь диапазон, правее in - if (left_it == v.end()) - { - can_be_true = false; - } - else - { - auto right_it = std::upper_bound(v.begin(), v.end(), right); - /// весь диапазон, левее in - if (right_it == v.begin()) - { - can_be_true = false; - } - else - { - --right_it; - /// в диапазон не попадает ни одного ключа из in - if (*right_it < *left_it) - { - can_be_true = false; - } - else - { - can_be_true = true; - } - } - } - } - - return BoolMask(can_be_true, can_be_false); - } - + typedef std::vector OrderedSet; + typedef std::unique_ptr OrderedSetPtr; + OrderedSetPtr ordered_set; }; typedef Poco::SharedPtr SetPtr; diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index f1ad0c5ee65..1aae1be79ca 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -498,15 +498,15 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as finished_asts[initial_ast] = ast; } -void ExpressionAnalyzer::makeExplicitSets() +void ExpressionAnalyzer::makeExplicitSets(bool create_ordered_set) { - makeExplicitSetsRecursively(ast, storage->getSampleBlock()); + makeExplicitSetsRecursively(ast, storage->getSampleBlock(), create_ordered_set); } -void ExpressionAnalyzer::makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block) +void ExpressionAnalyzer::makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block, bool create_ordered_set) { for (auto & child : node->children) - makeExplicitSetsRecursively(child, sample_block); + makeExplicitSetsRecursively(child, sample_block, create_ordered_set); ASTFunction * func = dynamic_cast(node.get()); if (func && func->kind == ASTFunction::FUNCTION && (func->name == "in" || func->name == "notIn")) @@ -515,7 +515,7 @@ void ExpressionAnalyzer::makeExplicitSetsRecursively(ASTPtr & node, const Block ASTPtr & arg = args.children[1]; if (!dynamic_cast(&*arg) && !dynamic_cast(&*arg)) - makeExplicitSet(func, sample_block); + makeExplicitSet(func, sample_block, create_ordered_set); } } @@ -699,12 +699,12 @@ void ExpressionAnalyzer::makeSet(ASTFunction * node, const Block & sample_block) } else { - makeExplicitSet(node, sample_block); + makeExplicitSet(node, sample_block, false); } } /// Случай явного перечисления значений. -void ExpressionAnalyzer::makeExplicitSet(ASTFunction * node, const Block & sample_block) +void ExpressionAnalyzer::makeExplicitSet(ASTFunction * node, const Block & sample_block, bool create_ordered_set) { IAST & args = *node->arguments; ASTPtr & arg = args.children[1]; @@ -767,7 +767,7 @@ void ExpressionAnalyzer::makeExplicitSet(ASTFunction * node, const Block & sampl ASTSet * ast_set = new ASTSet(arg->getColumnName()); ASTPtr ast_set_ptr = ast_set; ast_set->set = new Set(settings.limits); - ast_set->set->createFromAST(set_element_types, elements_ast); + ast_set->set->createFromAST(set_element_types, elements_ast, create_ordered_set); arg = ast_set_ptr; } diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index 5d8f3fe4c81..fcf9cc487a1 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -501,7 +501,8 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns; - query_analyzer->makeExplicitSets(); + /// Создаем In заданные явно. Внутри In создаем дополнительно сортированный вектор значений + query_analyzer->makeExplicitSets(true); /// Инициализируем изначальные потоки данных, на которые накладываются преобразования запроса. Таблица или подзапрос? if (!interpreter_subquery) { diff --git a/dbms/src/Interpreters/Set.cpp b/dbms/src/Interpreters/Set.cpp index faaf75a6a38..756de0c9ada 100644 --- a/dbms/src/Interpreters/Set.cpp +++ b/dbms/src/Interpreters/Set.cpp @@ -82,13 +82,12 @@ Set::Type Set::chooseMethod(const ConstColumnPlainPtrs & key_columns, bool & key || dynamic_cast(key_columns[0]) || (dynamic_cast(key_columns[0]) && !keys_fit_128_bits))) return KEY_STRING; - /// Если много ключей - будем строить множество хэшей от них return HASHED; } -bool Set::insertFromBlock(Block & block) +bool Set::insertFromBlock(Block & block, bool create_ordered_set) { size_t keys_size = block.columns(); Row key(keys_size); @@ -119,6 +118,9 @@ bool Set::insertFromBlock(Block & block) /// Строим ключ UInt64 key = get(column[i]); res.insert(key); + + if(create_ordered_set) + ordered_set->push_back(column[i]); } } else if (type == KEY_STRING) @@ -143,6 +145,9 @@ bool Set::insertFromBlock(Block & block) if (inserted) it->data = string_pool.insert(ref.data, ref.size); + + if(create_ordered_set) + ordered_set->push_back(std::string(ref.data, ref.size)); } } else if (const ColumnFixedString * column_string = dynamic_cast(&column)) @@ -162,6 +167,9 @@ bool Set::insertFromBlock(Block & block) if (inserted) it->data = string_pool.insert(ref.data, ref.size); + + if(create_ordered_set) + ordered_set->push_back(std::string(ref.data, ref.size)); } } else @@ -198,7 +206,7 @@ bool Set::insertFromBlock(Block & block) } -void Set::createFromAST(DataTypes & types, ASTPtr node) +void Set::createFromAST(DataTypes & types, ASTPtr node, bool create_ordered_set) { data_types = types; @@ -247,7 +255,13 @@ void Set::createFromAST(DataTypes & types, ASTPtr node) /// NOTE: Потом можно реализовать возможность задавать константные выражения в множествах. } - insertFromBlock(block); + if (create_ordered_set) + ordered_set = OrderedSetPtr(new std::vector()); + + insertFromBlock(block, create_ordered_set); + + if (create_ordered_set) + std::sort(ordered_set->begin(), ordered_set->end()); } @@ -528,4 +542,73 @@ void Set::executeConstArray(const ColumnConstArray * key_column, ColumnUInt8::Co vec_res[i] = res; } +BoolMask Set::mayBeTrueInRange(const Range & range) +{ + if (!ordered_set) + throw DB::Exception("Ordered set in not created."); + + if (ordered_set->empty()) + return BoolMask(false, true); + + const Field & left = range.left; + const Field & right = range.right; + + bool can_be_true; + bool can_be_false = true; + + /// Если во всем диапазоне одинаковый ключ и он есть в Set, то выбираем блок для in и не выбираем для notIn + if (range.left_bounded && range.right_bounded && range.right_included && range.left_included && left == right) + { + if (std::find(ordered_set->begin(), ordered_set->end(), left) != ordered_set->end()) + { + can_be_false = false; + can_be_true = true; + } + else + { + can_be_true = false; + can_be_false = true; + } + } + else + { + auto left_it = range.left_bounded ? std::lower_bound(ordered_set->begin(), ordered_set->end(), left) : ordered_set->begin(); + if (!range.left_included && left_it != ordered_set->end() && *left_it == left) + ++left_it; + + /// если весь диапазон, правее in + if (left_it == ordered_set->end()) + { + can_be_true = false; + } + else + { + auto right_it = range.right_bounded ? std::upper_bound(ordered_set->begin(), ordered_set->end(), right) : ordered_set->end(); + if (!range.right_included && right_it != ordered_set->begin() && *(right_it--) == right) + --right_it; + + /// весь диапазон, левее in + if (right_it == ordered_set->begin()) + { + can_be_true = false; + } + else + { + --right_it; + /// в диапазон не попадает ни одного ключа из in + if (*right_it < *left_it) + { + can_be_true = false; + } + else + { + can_be_true = true; + } + } + } + } + + return BoolMask(can_be_true, can_be_false); +} + } diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index 00fe4201505..433243cb598 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -144,7 +144,6 @@ bool PKCondition::atomFromAST(ASTPtr & node, Block & block_with_constants, RPNEl { inverted = false; column = pk_columns[args[0]->getColumnName()]; - dynamic_cast(args[1].get())->set->createOrderedSet(); } else return false; @@ -285,7 +284,7 @@ bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk { const Range & key_range = key_ranges[element.key_column]; - rpn_stack.push_back(ast_set->set->mayBeTrueInRange(key_range.left, key_range.right)); + rpn_stack.push_back(ast_set->set->mayBeTrueInRange(key_range)); if (element.function == RPNElement::FUNCTION_NOT_IN_SET) rpn_stack.back() = !rpn_stack.back(); } From 6ad275725e7ba7fe289d38ad52c400a60fb84408 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Tue, 1 Apr 2014 14:25:56 +0400 Subject: [PATCH 174/281] dbms: add desciption of in [#METR-10498] --- dbms/include/DB/Interpreters/Set.h | 2 +- .../DB/Storages/MergeTree/PKCondition.h | 34 ++-------------- dbms/src/Storages/MergeTree/PKCondition.cpp | 39 +++++++++++++++++++ 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 91123c58538..683486e67dd 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -64,7 +64,7 @@ public: */ void execute(Block & block, const ColumnNumbers & arguments, size_t result, bool negative) const; - std::string descibe() + std::string describe() { if (!ordered_set) return "{}"; diff --git a/dbms/include/DB/Storages/MergeTree/PKCondition.h b/dbms/include/DB/Storages/MergeTree/PKCondition.h index e7fb71f4007..b3fc269bb9a 100644 --- a/dbms/include/DB/Storages/MergeTree/PKCondition.h +++ b/dbms/include/DB/Storages/MergeTree/PKCondition.h @@ -261,7 +261,7 @@ public: } }; - +struct ASTSet; class PKCondition { public: @@ -310,35 +310,7 @@ private: RPNElement(Function function_, size_t key_column_, const Range & range_) : function(function_), range(range_), key_column(key_column_){} - String toString() - { - std::ostringstream ss; - switch (function) - { - case FUNCTION_AND: - return "and"; - case FUNCTION_OR: - return "or"; - case FUNCTION_NOT: - return "not"; - case FUNCTION_UNKNOWN: - return "unknown"; - case FUNCTION_NOT_IN_SET: - case FUNCTION_IN_SET: - { - ss << "(column " << key_column << (function == FUNCTION_IN_SET ? " in " : " notIn ") << set->descibe() << ")"; - return ss.str(); - } - case FUNCTION_IN_RANGE: - case FUNCTION_NOT_IN_RANGE: - { - ss << "(column " << key_column << (function == FUNCTION_NOT_IN_RANGE ? " not" : "") << " in " << range.toString() << ")"; - return ss.str(); - } - default: - return "ERROR"; - } - } + String toString(); Function function; @@ -347,6 +319,8 @@ private: size_t key_column; /// Для FUNCTION_IN_SET ASTPtr in_function; + + ASTSet * inFunctionToSet(); }; typedef std::vector RPN; diff --git a/dbms/src/Storages/MergeTree/PKCondition.cpp b/dbms/src/Storages/MergeTree/PKCondition.cpp index 433243cb598..0a38a7d23f3 100644 --- a/dbms/src/Storages/MergeTree/PKCondition.cpp +++ b/dbms/src/Storages/MergeTree/PKCondition.cpp @@ -331,4 +331,43 @@ bool PKCondition::mayBeTrueAfter(const Field * left_pk) return mayBeTrueInRange(left_pk, NULL, false); } +ASTSet * PKCondition::RPNElement::inFunctionToSet() +{ + ASTFunction * in_func = dynamic_cast(in_function.get()); + if (!in_func) + return nullptr; + ASTs & args = dynamic_cast(*in_func->arguments).children; + ASTSet * ast_set = dynamic_cast(args[1].get()); + return ast_set; +} + +String PKCondition::RPNElement::toString() +{ + std::ostringstream ss; + switch (function) + { + case FUNCTION_AND: + return "and"; + case FUNCTION_OR: + return "or"; + case FUNCTION_NOT: + return "not"; + case FUNCTION_UNKNOWN: + return "unknown"; + case FUNCTION_NOT_IN_SET: + case FUNCTION_IN_SET: + { + ss << "(column " << key_column << (function == FUNCTION_IN_SET ? " in " : " notIn ") << inFunctionToSet()->set->describe() << ")"; + return ss.str(); + } + case FUNCTION_IN_RANGE: + case FUNCTION_NOT_IN_RANGE: + { + ss << "(column " << key_column << (function == FUNCTION_NOT_IN_RANGE ? " not" : "") << " in " << range.toString() << ")"; + return ss.str(); + } + default: + return "ERROR"; + } +} } From 61ebb6ea3419de8f1629a294ece00d480a2c0208 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Tue, 1 Apr 2014 14:50:29 +0400 Subject: [PATCH 175/281] fixed issue [#METR-10498] --- dbms/src/Interpreters/Set.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dbms/src/Interpreters/Set.cpp b/dbms/src/Interpreters/Set.cpp index 756de0c9ada..06b1fe880fb 100644 --- a/dbms/src/Interpreters/Set.cpp +++ b/dbms/src/Interpreters/Set.cpp @@ -573,7 +573,7 @@ BoolMask Set::mayBeTrueInRange(const Range & range) else { auto left_it = range.left_bounded ? std::lower_bound(ordered_set->begin(), ordered_set->end(), left) : ordered_set->begin(); - if (!range.left_included && left_it != ordered_set->end() && *left_it == left) + if (range.left_bounded && !range.left_included && left_it != ordered_set->end() && *left_it == left) ++left_it; /// если весь диапазон, правее in @@ -584,7 +584,7 @@ BoolMask Set::mayBeTrueInRange(const Range & range) else { auto right_it = range.right_bounded ? std::upper_bound(ordered_set->begin(), ordered_set->end(), right) : ordered_set->end(); - if (!range.right_included && right_it != ordered_set->begin() && *(right_it--) == right) + if (range.right_bounded && !range.right_included && right_it != ordered_set->begin() && *(right_it--) == right) --right_it; /// весь диапазон, левее in From d9e931a8fe4d5fe3c346373f827b8a888656c996 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Mon, 7 Apr 2014 19:48:16 +0400 Subject: [PATCH 176/281] dbms: fixed Null pointer [#METR-10498] --- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 1aae1be79ca..0a6ada8327d 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -500,7 +500,8 @@ void ExpressionAnalyzer::normalizeTreeImpl(ASTPtr & ast, MapOfASTs & finished_as void ExpressionAnalyzer::makeExplicitSets(bool create_ordered_set) { - makeExplicitSetsRecursively(ast, storage->getSampleBlock(), create_ordered_set); + if (storage && ast) + makeExplicitSetsRecursively(ast, storage->getSampleBlock(), create_ordered_set); } void ExpressionAnalyzer::makeExplicitSetsRecursively(ASTPtr & node, const Block & sample_block, bool create_ordered_set) From 28ff4749161386193e7c9fe3359040a25e2e2394 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 7 Apr 2014 21:39:45 +0400 Subject: [PATCH 177/281] Fixes. [#METR-10202] --- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 5 ++++- dbms/src/Storages/StorageReplicatedMergeTree.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index 1e151552d0c..f2178bda6ca 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -687,7 +687,7 @@ MergeTreeData::DataPartsVector MergeTreeData::renameTempPartAndReplace(MutableDa if (increment) part->left = part->right = increment->get(false); - part->name = getPartName(part->left_date, part->right_date, part->left, part->right, 0); + part->name = getPartName(part->left_date, part->right_date, part->left, part->right, part->level); if (data_parts.count(part)) throw Exception("Part " + part->name + " already exists", ErrorCodes::DUPLICATE_DATA_PART); @@ -705,7 +705,10 @@ MergeTreeData::DataPartsVector MergeTreeData::renameTempPartAndReplace(MutableDa { --it; if (!part->contains(**it)) + { + ++it; break; + } res.push_back(*it); data_parts.erase(it++); /// Да, ++, а не --. } diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index c3bec5c6293..aa35417b267 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -317,14 +317,14 @@ void StorageReplicatedMergeTree::pullLogsToQueue() if (zookeeper.tryGet(replica_path + "/log_pointers/" + replica, index_str)) { - index = Poco::NumberParser::parseUnsigned64(index_str); + index = parse(index_str); } else { /// Если у нас еще нет указателя на лог этой реплики, поставим указатель на первую запись в нем. Strings entries = zookeeper.getChildren(zookeeper_path + "/replicas/" + replica + "/log"); std::sort(entries.begin(), entries.end()); - index = entries.empty() ? 0 : Poco::NumberParser::parseUnsigned64(entries[0].substr(strlen("log-"))); + index = entries.empty() ? 0 : parse(entries[0].substr(strlen("log-"))); zookeeper.create(replica_path + "/log_pointers/" + replica, toString(index), zkutil::CreateMode::Persistent); } @@ -929,10 +929,11 @@ void StorageReplicatedMergeTree::LogEntry::readText(ReadBuffer & in) String type_str; assertString("format version: 1\n", in); - readString(type_str, in); - assertString("\nsource replica: ", in); + assertString("source replica: ", in); readString(source_replica, in); assertString("\n", in); + readString(type_str, in); + assertString("\n", in); if (type_str == "get") { From 3e61e7b7f6ada6a5b92c0b25a601f0c2ca73ff58 Mon Sep 17 00:00:00 2001 From: Pavel Kartavyy Date: Mon, 7 Apr 2014 21:43:50 +0400 Subject: [PATCH 178/281] dbms: improvements [#METR-10498] --- dbms/include/DB/Interpreters/Set.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dbms/include/DB/Interpreters/Set.h b/dbms/include/DB/Interpreters/Set.h index 683486e67dd..367223fc4e5 100644 --- a/dbms/include/DB/Interpreters/Set.h +++ b/dbms/include/DB/Interpreters/Set.h @@ -72,6 +72,7 @@ public: bool first = true; std::stringstream ss; + ss << "{"; for (const Field & f : *ordered_set) { if (!first) @@ -80,6 +81,7 @@ public: ss << f; first = false; } + ss << "}"; return ss.str(); } From afafcc85b4b4844c9ee8b9a524da6a105ff2129d Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 7 Apr 2014 22:14:39 +0400 Subject: [PATCH 179/281] Fixes. [#METR-10202] --- .../DB/Storages/StorageReplicatedMergeTree.h | 2 +- .../Storages/StorageReplicatedMergeTree.cpp | 99 +++++++++++-------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 240d8076dd3..9955e4f9af9 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -124,7 +124,7 @@ private: try { Poco::ScopedLock lock(storage.queue_mutex); - if (!storage.currently_merging.erase(part)) + if (!storage.future_parts.erase(part)) throw Exception("Untagging already untagged future part " + part + ". This is a bug.", ErrorCodes::LOGICAL_ERROR); } catch (...) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index aa35417b267..8b1e5aabc62 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -613,6 +613,14 @@ void StorageReplicatedMergeTree::queueThread() success = true; } + catch (Exception & e) + { + if (e.code() == ErrorCodes::NO_REPLICA_HAS_PART) + /// Если ни у кого нет нужного куска, это нормальная ситуация; не будем писать в лог с уровнем Error. + LOG_INFO(log, e.displayText()); + else + tryLogCurrentException(__PRETTY_FUNCTION__); + } catch (...) { tryLogCurrentException(__PRETTY_FUNCTION__); @@ -644,47 +652,48 @@ void StorageReplicatedMergeTree::mergeSelectingThread() while (!shutdown_called && is_leader_node) { - size_t merges_queued = 0; - - { - Poco::ScopedLock lock(queue_mutex); - - for (const auto & entry : queue) - if (entry.type == LogEntry::MERGE_PARTS) - ++merges_queued; - } - - if (merges_queued >= data.settings.merging_threads) - { - std::this_thread::sleep_for(MERGE_SELECTING_SLEEP); - continue; - } - - /// Есть ли активный мердж крупных кусков. - bool has_big_merge = false; - - { - Poco::ScopedLock lock(currently_merging_mutex); - - for (const auto & name : currently_merging) - { - MergeTreeData::DataPartPtr part = data.getContainingPart(name); - if (!part) - continue; - if (part->name != name) - throw Exception("Assertion failed in mergeSelectingThread().", ErrorCodes::LOGICAL_ERROR); - if (part->size * data.index_granularity > 25 * 1024 * 1024) - { - has_big_merge = true; - break; - } - } - } - bool success = false; try { + size_t merges_queued = 0; + + { + Poco::ScopedLock lock(queue_mutex); + + for (const auto & entry : queue) + if (entry.type == LogEntry::MERGE_PARTS) + ++merges_queued; + } + + if (merges_queued >= data.settings.merging_threads) + { + std::this_thread::sleep_for(MERGE_SELECTING_SLEEP); + continue; + } + + /// Есть ли активный мердж крупных кусков. + bool has_big_merge = false; + + { + Poco::ScopedLock lock(currently_merging_mutex); + + for (const auto & name : currently_merging) + { + MergeTreeData::DataPartPtr part = data.getContainingPart(name); + if (!part) + continue; + if (part->name != name) + throw Exception("Assertion failed in mergeSelectingThread(): expected " + name + ", found " + part->name, + ErrorCodes::LOGICAL_ERROR); + if (part->size * data.index_granularity > 25 * 1024 * 1024) + { + has_big_merge = true; + break; + } + } + } + MergeTreeData::DataPartsVector parts; { @@ -814,12 +823,6 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin MergeTreeData::MutableDataPartPtr part = fetcher.fetchPart(part_name, zookeeper_path + "/replicas/" + replica_name, host, port); auto removed_parts = data.renameTempPartAndReplace(part); - for (const auto & removed_part : removed_parts) - { - LOG_DEBUG(log, "Part " << removed_part->name << " is rendered obsolete by fetching part " << part_name); - ProfileEvents::increment(ProfileEvents::ObsoleteReplicatedParts); - } - zkutil::Ops ops; ops.push_back(new zkutil::Op::Create( replica_path + "/parts/" + part->name, @@ -831,6 +834,16 @@ void StorageReplicatedMergeTree::fetchPart(const String & part_name, const Strin part->checksums.toString(), zookeeper.getDefaultACL(), zkutil::CreateMode::Persistent)); + + for (const auto & removed_part : removed_parts) + { + LOG_DEBUG(log, "Part " << removed_part->name << " is rendered obsolete by fetching part " << part_name); + ProfileEvents::increment(ProfileEvents::ObsoleteReplicatedParts); + + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + removed_part->name + "/checksums", -1)); + ops.push_back(new zkutil::Op::Remove(replica_path + "/parts/" + removed_part->name, -1)); + } + zookeeper.multi(ops); ProfileEvents::increment(ProfileEvents::ReplicatedPartFetches); From b0edb3b470dc1e8ac1563374af37f0d443d7e894 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 7 Apr 2014 22:44:10 +0400 Subject: [PATCH 180/281] Fixes. [#METR-10202] --- dbms/src/Storages/StorageReplicatedMergeTree.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index 8b1e5aabc62..739a18b671c 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -469,6 +469,7 @@ void StorageReplicatedMergeTree::executeLogEntry(const LogEntry & entry) zookeeper.multi(ops); + parts.clear(); data.clearOldParts(); ProfileEvents::increment(ProfileEvents::ReplicatedPartMerges); @@ -631,6 +632,7 @@ void StorageReplicatedMergeTree::queueThread() if (success) { + entry.currently_merging_tagger = nullptr; std::this_thread::sleep_for(QUEUE_AFTER_WORK_SLEEP); } else @@ -641,6 +643,7 @@ void StorageReplicatedMergeTree::queueThread() Poco::ScopedLock lock(queue_mutex); queue.push_back(entry); } + entry.currently_merging_tagger = nullptr; std::this_thread::sleep_for(QUEUE_ERROR_SLEEP); } } @@ -684,8 +687,10 @@ void StorageReplicatedMergeTree::mergeSelectingThread() if (!part) continue; if (part->name != name) - throw Exception("Assertion failed in mergeSelectingThread(): expected " + name + ", found " + part->name, - ErrorCodes::LOGICAL_ERROR); + { + LOG_INFO(log, "currently_merging contains obsolete part " << name << " contained in" << part->name); + continue; + } if (part->size * data.index_granularity > 25 * 1024 * 1024) { has_big_merge = true; From 4cd3f3581e72d70f8b329f56bef7d163af76c452 Mon Sep 17 00:00:00 2001 From: Michael Kolupaev Date: Mon, 7 Apr 2014 23:02:45 +0400 Subject: [PATCH 181/281] Tiny piece of logging. [#METR-2807] --- dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp b/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp index 2fbdeddf913..ae0c1c69493 100644 --- a/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp +++ b/dbms/src/DataStreams/CollapsingSortedBlockInputStream.cpp @@ -41,6 +41,7 @@ void CollapsingSortedBlockInputStream::insertRows(ColumnPlainPtrs & merged_colum /// Если все строки во входных потоках схлопнулись, мы все равно хотим выдать хоть один блок в результат. if (last_in_stream && merged_rows == 0 && !blocks_written) { + LOG_INFO(log, "All rows collapsed"); ++merged_rows; for (size_t i = 0; i < num_columns; ++i) merged_columns[i]->insert(last_positive[i]); From 8411f2ae32789c39bb874b8ed9295862185541c5 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Mon, 7 Apr 2014 23:08:47 +0400 Subject: [PATCH 182/281] dbms: fixed error with INSERT more than MAX_BLOCK_SIZE via HTTP [#METR-10789]. --- .../DB/Interpreters/InterpreterInsertQuery.h | 8 +++++--- dbms/include/DB/Interpreters/InterpreterQuery.h | 2 +- dbms/src/Interpreters/InterpreterInsertQuery.cpp | 13 +++++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/dbms/include/DB/Interpreters/InterpreterInsertQuery.h b/dbms/include/DB/Interpreters/InterpreterInsertQuery.h index c9a66a8da97..daa0d7ad87d 100644 --- a/dbms/include/DB/Interpreters/InterpreterInsertQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterInsertQuery.h @@ -17,13 +17,15 @@ public: InterpreterInsertQuery(ASTPtr query_ptr_, Context & context_); /** Выполнить запрос. - * remaining_data_istr, если не NULL, может содержать нераспарсенные данные для вставки. + * remaining_data_istr, если не nullptr, может содержать нераспарсенные данные для вставки. * (заранее может быть считан в оперативку для парсинга лишь небольшой кусок запроса, который содержит не все данные) */ void execute(ReadBuffer * remaining_data_istr); - /** Подготовить запрос к выполнению. Вернуть поток блоков, в который можно писать данные для выполнения запроса. - * Или вернуть NULL, если запрос INSERT SELECT (самодостаточный запрос - не принимает входные данные). + /** Подготовить запрос к выполнению. Вернуть потоки блоков + * - поток, в который можно писать данные для выполнения запроса, если INSERT; + * - поток, из которого можно читать результат выполнения запроса, если SELECT и подобные; + * Или ничего, если запрос INSERT SELECT (самодостаточный запрос - не принимает входные данные, не отдаёт результат). */ BlockIO execute(); diff --git a/dbms/include/DB/Interpreters/InterpreterQuery.h b/dbms/include/DB/Interpreters/InterpreterQuery.h index 032fed8265e..0f928bebb73 100644 --- a/dbms/include/DB/Interpreters/InterpreterQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterQuery.h @@ -20,7 +20,7 @@ public: * * ostr - куда писать результат выполнения запроса, если он есть. * - * remaining_data_istr, если не NULL, может содержать нераспарсенный остаток запроса с данными. + * remaining_data_istr, если не nullptr, может содержать нераспарсенный остаток запроса с данными. * (заранее может быть считан в оперативку для парсинга лишь небольшой кусок запроса, который содержит не все данные) * * В query_plan, diff --git a/dbms/src/Interpreters/InterpreterInsertQuery.cpp b/dbms/src/Interpreters/InterpreterInsertQuery.cpp index f8242d30717..419338c1e29 100644 --- a/dbms/src/Interpreters/InterpreterInsertQuery.cpp +++ b/dbms/src/Interpreters/InterpreterInsertQuery.cpp @@ -84,19 +84,16 @@ void InterpreterInsertQuery::execute(ReadBuffer * remaining_data_istr) /// Данные могут содержаться в распарсенной (query.data) и ещё не распарсенной (remaining_data_istr) части запроса. - /// Если данных нет. - bool has_remaining_data = remaining_data_istr && !remaining_data_istr->eof(); - - if (!query.data && !has_remaining_data) - throw Exception("No data to insert", ErrorCodes::NO_DATA_TO_INSERT); - ConcatReadBuffer::ReadBuffers buffers; ReadBuffer buf1(const_cast(query.data), query.data ? query.end - query.data : 0, 0); if (query.data) buffers.push_back(&buf1); - if (has_remaining_data) - buffers.push_back(remaining_data_istr); + buffers.push_back(remaining_data_istr); + + /** NOTE Нельзя читать из remaining_data_istr до того, как прочтём всё между query.data и query.end. + * - потому что query.data может ссылаться на кусок памяти, использующийся в качестве буфера в remaining_data_istr. + */ ConcatReadBuffer istr(buffers); Block sample = table->getSampleBlock(); From c0db3d0328fed3c9c590086cc88820db1dd9bdfa Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 10:29:02 +0400 Subject: [PATCH 183/281] dbms: removed useless file [#METR-2944]. --- dbms/include/DB/Core/NameAndType.h | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 dbms/include/DB/Core/NameAndType.h diff --git a/dbms/include/DB/Core/NameAndType.h b/dbms/include/DB/Core/NameAndType.h deleted file mode 100644 index acc94d31066..00000000000 --- a/dbms/include/DB/Core/NameAndType.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef DBMS_CORE_NAME_AND_TYPE_H -#define DBMS_CORE_NAME_AND_TYPE_H - -#include - -#include -#include - - -namespace DB -{ - -using Poco::SharedPtr; - -/** Имя столбца и тип столбца. - */ - -struct NameAndType -{ - DataTypePtr type; - String name; -}; - -} - -#endif From 34bf7dd0aeac12e99f8219eeb53597e1f3263105 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 10:51:53 +0400 Subject: [PATCH 184/281] Removed useless code [#METR-2807]. --- dbms/include/DB/Core/ColumnNumbers.h | 5 +---- dbms/include/DB/IO/copyData.h | 5 +---- dbms/include/DB/Parsers/StringRange.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Connection.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Date.h | 5 +---- libs/libmysqlxx/include/mysqlxx/DateTime.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Null.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Pool.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Query.h | 5 +---- libs/libmysqlxx/include/mysqlxx/ResultBase.h | 5 +---- libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h | 5 +---- libs/libmysqlxx/include/mysqlxx/String.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Transaction.h | 5 +---- libs/libmysqlxx/include/mysqlxx/Types.h | 5 +---- libs/libmysqlxx/include/mysqlxx/UseQueryResult.h | 5 +---- libs/libmysqlxx/include/mysqlxx/mysqlxx.h | 5 +---- .../include/Poco/Ext/PatternFormatterWithOwnThreadNumber.h | 5 +---- libs/libpocoext/utils/make-charset.cpp | 5 +---- 18 files changed, 18 insertions(+), 72 deletions(-) diff --git a/dbms/include/DB/Core/ColumnNumbers.h b/dbms/include/DB/Core/ColumnNumbers.h index 710770817dd..82d8bed097d 100644 --- a/dbms/include/DB/Core/ColumnNumbers.h +++ b/dbms/include/DB/Core/ColumnNumbers.h @@ -1,5 +1,4 @@ -#ifndef DBMS_CORE_COLUMN_NUMBERS_H -#define DBMS_CORE_COLUMN_NUMBERS_H +#pragma once #include #include @@ -11,5 +10,3 @@ namespace DB typedef std::vector ColumnNumbers; } - -#endif diff --git a/dbms/include/DB/IO/copyData.h b/dbms/include/DB/IO/copyData.h index c44698954c4..d2c4ba8b272 100644 --- a/dbms/include/DB/IO/copyData.h +++ b/dbms/include/DB/IO/copyData.h @@ -1,5 +1,4 @@ -#ifndef DBMS_IO_COPY_DATA_H -#define DBMS_IO_COPY_DATA_H +#pragma once #include #include @@ -17,5 +16,3 @@ void copyData(ReadBuffer & from, WriteBuffer & to); void copyData(ReadBuffer & from, WriteBuffer & to, size_t bytes); } - -#endif diff --git a/dbms/include/DB/Parsers/StringRange.h b/dbms/include/DB/Parsers/StringRange.h index afbb6895f05..ae82df3c81c 100644 --- a/dbms/include/DB/Parsers/StringRange.h +++ b/dbms/include/DB/Parsers/StringRange.h @@ -1,5 +1,4 @@ -#ifndef DBMS_PARSERS_STRINGRANGE_H -#define DBMS_PARSERS_STRINGRANGE_H +#pragma once #include @@ -11,5 +10,3 @@ typedef std::pair StringRange; typedef Poco::SharedPtr StringPtr; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Connection.h b/libs/libmysqlxx/include/mysqlxx/Connection.h index 0e5608c50ac..c6ebb8ce293 100644 --- a/libs/libmysqlxx/include/mysqlxx/Connection.h +++ b/libs/libmysqlxx/include/mysqlxx/Connection.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_CONNECTION_H -#define MYSQLXX_CONNECTION_H +#pragma once #include @@ -132,5 +131,3 @@ private: } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Date.h b/libs/libmysqlxx/include/mysqlxx/Date.h index 9336cf01280..330e51d5112 100644 --- a/libs/libmysqlxx/include/mysqlxx/Date.h +++ b/libs/libmysqlxx/include/mysqlxx/Date.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_DATE_H -#define MYSQLXX_DATE_H +#pragma once #include #include @@ -174,5 +173,3 @@ inline std::ostream & operator<< (std::ostream & ostr, const Date & date) } } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/DateTime.h b/libs/libmysqlxx/include/mysqlxx/DateTime.h index 25e8a8f4a05..5ab3ebfb3d4 100644 --- a/libs/libmysqlxx/include/mysqlxx/DateTime.h +++ b/libs/libmysqlxx/include/mysqlxx/DateTime.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_DATETIME_H -#define MYSQLXX_DATETIME_H +#pragma once #include #include @@ -187,5 +186,3 @@ inline std::ostream & operator<< (std::ostream & ostr, const DateTime & datetime } } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Null.h b/libs/libmysqlxx/include/mysqlxx/Null.h index 4dcb5f83f9b..a518add9663 100644 --- a/libs/libmysqlxx/include/mysqlxx/Null.h +++ b/libs/libmysqlxx/include/mysqlxx/Null.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_NULL_H -#define MYSQLXX_NULL_H +#pragma once #include @@ -84,5 +83,3 @@ public: } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Pool.h b/libs/libmysqlxx/include/mysqlxx/Pool.h index f1ed2e47131..de6ad05c1cd 100644 --- a/libs/libmysqlxx/include/mysqlxx/Pool.h +++ b/libs/libmysqlxx/include/mysqlxx/Pool.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_POOL_H -#define MYSQLXX_POOL_H +#pragma once #include @@ -424,5 +423,3 @@ private: }; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Query.h b/libs/libmysqlxx/include/mysqlxx/Query.h index 59c1a5acfd0..363b92aff47 100644 --- a/libs/libmysqlxx/include/mysqlxx/Query.h +++ b/libs/libmysqlxx/include/mysqlxx/Query.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_QUERY_H -#define MYSQLXX_QUERY_H +#pragma once #include #include @@ -81,5 +80,3 @@ inline std::ostream & operator<< (std::ostream & ostr, const Query & query) } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/ResultBase.h b/libs/libmysqlxx/include/mysqlxx/ResultBase.h index a6f854a1b9b..e5dd311b913 100644 --- a/libs/libmysqlxx/include/mysqlxx/ResultBase.h +++ b/libs/libmysqlxx/include/mysqlxx/ResultBase.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_RESULTBASE_H -#define MYSQLXX_RESULTBASE_H +#pragma once #include @@ -44,5 +43,3 @@ protected: }; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h b/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h index b5385015f35..26c6196034f 100644 --- a/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h +++ b/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_STOREQUERYRESULT_H -#define MYSQLXX_STOREQUERYRESULT_H +#pragma once #include @@ -44,5 +43,3 @@ private: }; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/String.h b/libs/libmysqlxx/include/mysqlxx/String.h index 70d3a4f661c..cf9e51a08a6 100644 --- a/libs/libmysqlxx/include/mysqlxx/String.h +++ b/libs/libmysqlxx/include/mysqlxx/String.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_STRING_H -#define MYSQLXX_STRING_H +#pragma once #include #include @@ -408,5 +407,3 @@ inline std::ostream & operator<< (std::ostream & ostr, const String & x) } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Transaction.h b/libs/libmysqlxx/include/mysqlxx/Transaction.h index 75f3e173dbd..ab77e868dc8 100644 --- a/libs/libmysqlxx/include/mysqlxx/Transaction.h +++ b/libs/libmysqlxx/include/mysqlxx/Transaction.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_TRANSACTION_H -#define MYSQLXX_TRANSACTION_H +#pragma once #include @@ -46,5 +45,3 @@ private: } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Types.h b/libs/libmysqlxx/include/mysqlxx/Types.h index e36f6abcf6f..db9c9daf40b 100644 --- a/libs/libmysqlxx/include/mysqlxx/Types.h +++ b/libs/libmysqlxx/include/mysqlxx/Types.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_TYPES_H -#define MYSQLXX_TYPES_H +#pragma once #include #include @@ -22,5 +21,3 @@ typedef MYSQL_LENGTH * MYSQL_LENGTHS; typedef MYSQL_FIELD * MYSQL_FIELDS; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/UseQueryResult.h b/libs/libmysqlxx/include/mysqlxx/UseQueryResult.h index 449d2aa4482..b07469c6a5b 100644 --- a/libs/libmysqlxx/include/mysqlxx/UseQueryResult.h +++ b/libs/libmysqlxx/include/mysqlxx/UseQueryResult.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_USEQUERYRESULT_H -#define MYSQLXX_USEQUERYRESULT_H +#pragma once #include #include @@ -36,5 +35,3 @@ public: }; } - -#endif diff --git a/libs/libmysqlxx/include/mysqlxx/mysqlxx.h b/libs/libmysqlxx/include/mysqlxx/mysqlxx.h index 6bed76f6a61..2a75152cc39 100644 --- a/libs/libmysqlxx/include/mysqlxx/mysqlxx.h +++ b/libs/libmysqlxx/include/mysqlxx/mysqlxx.h @@ -1,5 +1,4 @@ -#ifndef MYSQLXX_H -#define MYSQLXX_H +#pragma once #include #include @@ -66,5 +65,3 @@ * (при необходимости, зависимости можно убрать). * Предполагается, что пользователь сам допишет недостающий функционал. */ - -#endif diff --git a/libs/libpocoext/include/Poco/Ext/PatternFormatterWithOwnThreadNumber.h b/libs/libpocoext/include/Poco/Ext/PatternFormatterWithOwnThreadNumber.h index a4d22fd1347..09a4f3cf2ee 100644 --- a/libs/libpocoext/include/Poco/Ext/PatternFormatterWithOwnThreadNumber.h +++ b/libs/libpocoext/include/Poco/Ext/PatternFormatterWithOwnThreadNumber.h @@ -1,5 +1,4 @@ -#ifndef Ext_Foundation_PatternFormatter_INCLUDED -#define Ext_Foundation_PatternFormatter_INCLUDED +#pragma once #include @@ -19,5 +18,3 @@ public: }; } - -#endif diff --git a/libs/libpocoext/utils/make-charset.cpp b/libs/libpocoext/utils/make-charset.cpp index fbdbd244b72..308a743c96d 100644 --- a/libs/libpocoext/utils/make-charset.cpp +++ b/libs/libpocoext/utils/make-charset.cpp @@ -204,8 +204,7 @@ Encoding::buildHead (std::ostream& _os, const std::string& _class_name) const "//\n" "// This file is generated automatically. Do not edit it.\n" "//\n\n\n" - "#ifndef Foundation_" << _class_name << "_INCLUDED\n" - "#define Foundation_" << _class_name << "_INCLUDED\n\n\n"; + "#pragma once\n\n\n"; _os << "#include \n"; _os << "#include \n"; @@ -247,8 +246,6 @@ Encoding::buildHead (std::ostream& _os, const std::string& _class_name) const _os << "\t\tint convert(int ch, unsigned char* bytes, int length) const;\n"; _os << "\t};\n"; _os << "}\n\n"; - - _os << "#endif // Foundation_" << _class_name << "_INCLUDED\n"; } void From be281c42a4b69f92c593064980da4da65000e188 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 10:52:56 +0400 Subject: [PATCH 185/281] dbms: removed useless code [#METR-2944]. --- dbms/include/DB/Core/Field.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dbms/include/DB/Core/Field.h b/dbms/include/DB/Core/Field.h index 8d106ba4b6b..afe75cb5ce9 100644 --- a/dbms/include/DB/Core/Field.h +++ b/dbms/include/DB/Core/Field.h @@ -87,7 +87,6 @@ public: Field() : which(Types::Null) { -// std::cerr << "Field()" << std::endl; } /** Не смотря на наличие шаблонного конструктора, этот конструктор всё-равно нужен, @@ -95,13 +94,11 @@ public: */ Field(const Field & rhs) { -// std::cerr << this << " Field::Field(const Field &)" << std::endl; create(rhs); } Field & operator= (const Field & rhs) { -// std::cerr << this << " Field::operator=(const Field &)" << std::endl; destroy(); create(rhs); return *this; @@ -110,7 +107,6 @@ public: template Field(const T & rhs) { -// std::cerr << this << " Field::Field(" << Types::toString(TypeToEnum::value) << ")" << std::endl; create(rhs); } @@ -140,7 +136,6 @@ public: template Field & operator= (const T & rhs) { -// std::cerr << this << " Field::operator=(" << Types::toString(TypeToEnum::value) << ")" << std::endl; destroy(); create(rhs); return *this; @@ -148,7 +143,6 @@ public: ~Field() { -// std::cerr << this << " Field::~Field()" << std::endl; destroy(); } @@ -285,7 +279,6 @@ private: void create(const T & x) { which = TypeToEnum::value; -// std::cerr << this << " Creating " << getTypeName() << std::endl; T * __attribute__((__may_alias__)) ptr = reinterpret_cast(storage); new (ptr) T(x); } @@ -293,13 +286,10 @@ private: void create(const Null & x) { which = Types::Null; -// std::cerr << this << " Creating " << getTypeName() << std::endl; } void create(const Field & x) { -// std::cerr << this << " Creating Field" << std::endl; - switch (x.which) { case Types::Null: create(Null()); break; @@ -326,8 +316,6 @@ private: __attribute__((__always_inline__)) void destroy() { -// std::cerr << this << " Destroying " << getTypeName() << std::endl; - if (which < Types::MIN_NON_POD) return; From 646421e45c03cb8bcbab3fb5e09b2b259f47010c Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 11:24:21 +0400 Subject: [PATCH 186/281] dbms: fixed build [#METR-2944]. --- dbms/include/DB/IO/RemoteReadBuffer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dbms/include/DB/IO/RemoteReadBuffer.h b/dbms/include/DB/IO/RemoteReadBuffer.h index ef80b364634..c2e115917eb 100644 --- a/dbms/include/DB/IO/RemoteReadBuffer.h +++ b/dbms/include/DB/IO/RemoteReadBuffer.h @@ -22,6 +22,7 @@ public: bool compress = true, size_t timeout = 0, size_t buffer_size = DBMS_DEFAULT_BUFFER_SIZE) + : ReadBuffer(nullptr, 0) { ReadBufferFromHTTP::Params params = { std::make_pair("action", "read"), From 5ca21127c7969a99e2da6c28d7b078ca6add118d Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 11:31:51 +0400 Subject: [PATCH 187/281] Improvement [#METR-2807]. --- dbms/include/DB/Client/Connection.h | 2 +- dbms/include/DB/Columns/ColumnArray.h | 2 +- dbms/include/DB/Columns/ColumnNested.h | 2 +- dbms/include/DB/Common/PODArray.h | 6 +-- dbms/include/DB/Core/SortDescription.h | 4 +- dbms/include/DB/Core/StringRef.h | 8 +-- .../CollapsingFinalBlockInputStream.h | 4 +- .../DB/DataStreams/IBlockInputStream.h | 2 +- .../DataStreams/IProfilingBlockInputStream.h | 34 +++++------- .../include/DB/DataTypes/DataTypeExpression.h | 2 +- dbms/include/DB/Functions/FunctionsArray.h | 2 +- .../DB/Functions/FunctionsHigherOrder.h | 2 +- .../DB/Functions/FunctionsStringArray.h | 11 ++-- dbms/include/DB/Functions/FunctionsURL.h | 32 +++++------ dbms/include/DB/IO/BufferWithOwnMemory.h | 4 +- .../DB/IO/CachedCompressedReadBuffer.h | 2 +- dbms/include/DB/IO/CompressedReadBufferBase.h | 14 ++--- dbms/include/DB/IO/CompressedWriteBuffer.h | 2 +- dbms/include/DB/IO/ReadBufferFromFile.h | 2 +- .../DB/IO/ReadBufferFromFileDescriptor.h | 2 +- dbms/include/DB/IO/WriteBufferFromFile.h | 2 +- .../DB/IO/WriteBufferFromFileDescriptor.h | 2 +- .../DB/IO/WriteBufferFromHTTPServerResponse.h | 4 +- dbms/include/DB/Interpreters/Aggregator.h | 16 +++--- dbms/include/DB/Interpreters/Context.h | 12 ++--- .../DB/Interpreters/ExpressionActions.h | 2 +- .../DB/Interpreters/InterpreterSelectQuery.h | 6 +-- dbms/include/DB/Interpreters/Users.h | 4 +- dbms/include/DB/Parsers/ASTInsertQuery.h | 6 +-- dbms/include/DB/Parsers/ASTOrderByElement.h | 2 +- .../MergeTree/MergeTreeBlockInputStream.h | 2 +- .../MergeTree/MergedBlockOutputStream.h | 2 +- dbms/src/Client/Connection.cpp | 14 ++--- dbms/src/Core/tests/string_pool.cpp | 2 +- .../IProfilingBlockInputStream.cpp | 4 +- dbms/src/IO/WriteHelpers.cpp | 2 +- dbms/src/Interpreters/Aggregator.cpp | 4 +- dbms/src/Interpreters/ExpressionActions.cpp | 4 +- .../Interpreters/InterpreterCreateQuery.cpp | 2 +- .../tests/expression_analyzer.cpp | 2 +- dbms/src/Parsers/ExpressionElementParsers.cpp | 4 +- dbms/src/Parsers/ParserInsertQuery.cpp | 2 +- dbms/src/Server/Server.cpp | 4 +- dbms/src/Server/TCPHandler.cpp | 3 +- dbms/src/Storages/StorageChunkRef.cpp | 2 +- dbms/src/Storages/StorageChunks.cpp | 2 +- dbms/src/Storages/tests/merge_tree.cpp | 2 +- libs/libmysqlxx/include/mysqlxx/Pool.h | 18 +++---- libs/libmysqlxx/include/mysqlxx/Row.h | 10 ++-- libs/libmysqlxx/include/mysqlxx/String.h | 2 +- libs/libmysqlxx/src/PoolWithFailover.cpp | 4 +- .../include/Poco/Ext/LevelFilterChannel.h | 53 ++----------------- libs/libpocoext/src/LevelFilterChannel.cpp | 7 +-- utils/iotest/iotest_aio.cpp | 8 +-- 54 files changed, 143 insertions(+), 211 deletions(-) diff --git a/dbms/include/DB/Client/Connection.h b/dbms/include/DB/Client/Connection.h index d5ab6afe10a..dbee7b0aa8c 100644 --- a/dbms/include/DB/Client/Connection.h +++ b/dbms/include/DB/Client/Connection.h @@ -85,7 +85,7 @@ public: /// Если последний флаг true, то затем необходимо вызвать sendExternalTablesData void sendQuery(const String & query, const String & query_id_ = "", UInt64 stage = QueryProcessingStage::Complete, - const Settings * settings = NULL, bool with_pending_data = false); + const Settings * settings = nullptr, bool with_pending_data = false); void sendCancel(); /// Отправить блок данных, на сервере сохранить во временную таблицу name diff --git a/dbms/include/DB/Columns/ColumnArray.h b/dbms/include/DB/Columns/ColumnArray.h index 97901d5daad..4c622008be8 100644 --- a/dbms/include/DB/Columns/ColumnArray.h +++ b/dbms/include/DB/Columns/ColumnArray.h @@ -28,7 +28,7 @@ public: typedef ColumnVector ColumnOffsets_t; /** Создать пустой столбец массивов, с типом значений, как в столбце nested_column */ - explicit ColumnArray(ColumnPtr nested_column, ColumnPtr offsets_column = NULL) + explicit ColumnArray(ColumnPtr nested_column, ColumnPtr offsets_column = nullptr) : data(nested_column), offsets(offsets_column) { if (!offsets_column) diff --git a/dbms/include/DB/Columns/ColumnNested.h b/dbms/include/DB/Columns/ColumnNested.h index 89e7aabbfde..5ef6b50cf37 100644 --- a/dbms/include/DB/Columns/ColumnNested.h +++ b/dbms/include/DB/Columns/ColumnNested.h @@ -32,7 +32,7 @@ public: typedef ColumnVector ColumnOffsets_t; /** Создать пустой столбец вложенных таблиц, с типом значений, как в столбце nested_column */ - explicit ColumnNested(Columns nested_columns, ColumnPtr offsets_column = NULL) + explicit ColumnNested(Columns nested_columns, ColumnPtr offsets_column = nullptr) : data(nested_columns), offsets(offsets_column) { if (!offsets_column) diff --git a/dbms/include/DB/Common/PODArray.h b/dbms/include/DB/Common/PODArray.h index e4393c248ed..c03e8b8d99a 100644 --- a/dbms/include/DB/Common/PODArray.h +++ b/dbms/include/DB/Common/PODArray.h @@ -83,7 +83,7 @@ private: { if (n == 0) { - c_start = c_end = c_end_of_storage = NULL; + c_start = c_end = c_end_of_storage = nullptr; return; } @@ -94,7 +94,7 @@ private: void dealloc() { - if (c_start == NULL) + if (c_start == nullptr) return; if (use_libc_realloc) @@ -105,7 +105,7 @@ private: void realloc(size_t n) { - if (c_start == NULL) + if (c_start == nullptr) { alloc(n); return; diff --git a/dbms/include/DB/Core/SortDescription.h b/dbms/include/DB/Core/SortDescription.h index 820b205da19..34c731ad7e8 100644 --- a/dbms/include/DB/Core/SortDescription.h +++ b/dbms/include/DB/Core/SortDescription.h @@ -20,10 +20,10 @@ struct SortColumnDescription int direction; /// 1 - по возрастанию, -1 - по убыванию. Poco::SharedPtr collator; /// Collator для locale-specific сортировки строк - SortColumnDescription(size_t column_number_, int direction_, const Poco::SharedPtr & collator_ = NULL) + SortColumnDescription(size_t column_number_, int direction_, const Poco::SharedPtr & collator_ = nullptr) : column_number(column_number_), direction(direction_), collator(collator_) {} - SortColumnDescription(String column_name_, int direction_, const Poco::SharedPtr & collator_ = NULL) + SortColumnDescription(String column_name_, int direction_, const Poco::SharedPtr & collator_ = nullptr) : column_name(column_name_), column_number(0), direction(direction_), collator(collator_) {} /// Для IBlockInputStream. diff --git a/dbms/include/DB/Core/StringRef.h b/dbms/include/DB/Core/StringRef.h index c5177b3151f..086547e5a96 100644 --- a/dbms/include/DB/Core/StringRef.h +++ b/dbms/include/DB/Core/StringRef.h @@ -14,13 +14,13 @@ namespace DB /// Штука, чтобы не создавать строки для поиска подстроки в хэш таблице. struct StringRef { - const char * data; - size_t size; + const char * data = nullptr; + size_t size = 0; StringRef(const char * data_, size_t size_) : data(data_), size(size_) {} StringRef(const unsigned char * data_, size_t size_) : data(reinterpret_cast(data_)), size(size_) {} StringRef(const std::string & s) : data(s.data()), size(s.size()) {} - StringRef() : data(NULL), size(0) {} + StringRef() {} std::string toString() const { return std::string(data, size); } }; @@ -57,7 +57,7 @@ namespace DB struct StringRefZeroTraits { static inline bool check(DB::StringRef x) { return NULL == x.data; } - static inline void set(DB::StringRef & x) { x.data = NULL; } + static inline void set(DB::StringRef & x) { x.data = nullptr; } }; inline bool operator==(StringRef lhs, const char * rhs) diff --git a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h index 01ff24b56c2..8bc5a00151a 100644 --- a/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h +++ b/dbms/include/DB/DataStreams/CollapsingFinalBlockInputStream.h @@ -144,7 +144,7 @@ private: --ptr->refcount; if (!ptr->refcount) delete ptr; - ptr = NULL; + ptr = nullptr; } } @@ -168,7 +168,7 @@ private: else ptr->output_blocks->push_back(ptr); } - ptr = NULL; + ptr = nullptr; } } }; diff --git a/dbms/include/DB/DataStreams/IBlockInputStream.h b/dbms/include/DB/DataStreams/IBlockInputStream.h index c07c93b2640..10887a4ee66 100644 --- a/dbms/include/DB/DataStreams/IBlockInputStream.h +++ b/dbms/include/DB/DataStreams/IBlockInputStream.h @@ -86,7 +86,7 @@ protected: BlockInputStreams children; private: - void getLeavesImpl(BlockInputStreams & res, BlockInputStreamPtr this_shared_ptr = NULL); + void getLeavesImpl(BlockInputStreams & res, BlockInputStreamPtr this_shared_ptr = nullptr); size_t checkDepthImpl(size_t max_depth, size_t level) const; diff --git a/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h b/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h index 337bb74d155..0e0d0248551 100644 --- a/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h +++ b/dbms/include/DB/DataStreams/IProfilingBlockInputStream.h @@ -20,15 +20,15 @@ namespace DB /// Информация для профайлинга. struct BlockStreamProfileInfo { - bool started; + bool started = false; Stopwatch work_stopwatch; /// Время вычислений (выполнения функции read()) Stopwatch total_stopwatch; /// Время с учётом ожидания String stream_name; /// Короткое имя потока, для которого собирается информация - size_t rows; - size_t blocks; - size_t bytes; + size_t rows = 0; + size_t blocks = 0; + size_t bytes = 0; /// Информация о вложенных потоках - для выделения чистого времени работы. typedef std::vector BlockStreamProfileInfos; @@ -36,12 +36,6 @@ struct BlockStreamProfileInfo String column_names; - BlockStreamProfileInfo() : - started(false), rows(0), blocks(0), bytes(0), - applied_limit(false), rows_before_limit(0), calculated_rows_before_limit(false) - { - } - /// Собрать BlockStreamProfileInfo для ближайших в дереве источников с именем name. Пример; собрать все info для PartialSorting stream-ов. void collectInfosForStreamsWithName(const String & name, BlockStreamProfileInfos & res) const; @@ -64,9 +58,9 @@ private: void calculateRowsBeforeLimit() const; /// Для этих полей сделаем accessor'ы, т.к. их необходимо предварительно вычислять. - mutable bool applied_limit; /// Применялся ли LIMIT - mutable size_t rows_before_limit; - mutable bool calculated_rows_before_limit; /// Вычислялось ли поле rows_before_limit + mutable bool applied_limit = false; /// Применялся ли LIMIT + mutable size_t rows_before_limit = 0; + mutable bool calculated_rows_before_limit = false; /// Вычислялось ли поле rows_before_limit }; @@ -78,10 +72,6 @@ private: class IProfilingBlockInputStream : public IBlockInputStream { public: - IProfilingBlockInputStream() - : is_cancelled(false), process_list_elem(NULL), - enabled_extremes(false), quota(NULL), prev_elapsed(0) {} - Block read(); /** Реализация по-умолчанию вызывает рекурсивно readSuffix() у всех детей, а затем readSuffixImpl() у себя. @@ -205,11 +195,11 @@ public: protected: BlockStreamProfileInfo info; - volatile bool is_cancelled; + volatile bool is_cancelled = false; ProgressCallback progress_callback; - ProcessList::Element * process_list_elem; + ProcessList::Element * process_list_elem = nullptr; - bool enabled_extremes; + bool enabled_extremes = false; /// Дополнительная информация, которая может образоваться в процессе работы. @@ -222,8 +212,8 @@ protected: LocalLimits limits; - QuotaForIntervals * quota; /// Если NULL - квота не используется. - double prev_elapsed; + QuotaForIntervals * quota = nullptr; /// Если nullptr - квота не используется. + double prev_elapsed = 0; /// Наследники должны реализовать эту функцию. virtual Block readImpl() = 0; diff --git a/dbms/include/DB/DataTypes/DataTypeExpression.h b/dbms/include/DB/DataTypes/DataTypeExpression.h index c63f93a1e37..abd23e04523 100644 --- a/dbms/include/DB/DataTypes/DataTypeExpression.h +++ b/dbms/include/DB/DataTypes/DataTypeExpression.h @@ -17,7 +17,7 @@ private: public: /// Некоторые типы могут быть еще не известны. - DataTypeExpression(DataTypes argument_types_ = DataTypes(), DataTypePtr return_type_ = NULL) + DataTypeExpression(DataTypes argument_types_ = DataTypes(), DataTypePtr return_type_ = nullptr) : argument_types(argument_types_), return_type(return_type_) {} std::string getName() const diff --git a/dbms/include/DB/Functions/FunctionsArray.h b/dbms/include/DB/Functions/FunctionsArray.h index 724225c537f..f51f47bbd33 100644 --- a/dbms/include/DB/Functions/FunctionsArray.h +++ b/dbms/include/DB/Functions/FunctionsArray.h @@ -633,7 +633,7 @@ public: return; Columns array_columns(arguments.size()); - const ColumnArray::Offsets_t * offsets = NULL; + const ColumnArray::Offsets_t * offsets = nullptr; ConstColumnPlainPtrs data_columns(arguments.size()); for (size_t i = 0; i < arguments.size(); ++i) diff --git a/dbms/include/DB/Functions/FunctionsHigherOrder.h b/dbms/include/DB/Functions/FunctionsHigherOrder.h index da7638a7897..416144b766b 100644 --- a/dbms/include/DB/Functions/FunctionsHigherOrder.h +++ b/dbms/include/DB/Functions/FunctionsHigherOrder.h @@ -430,7 +430,7 @@ public: NameSet argument_names; ColumnPtr column_first_array_ptr; - const ColumnArray * column_first_array = NULL; + const ColumnArray * column_first_array = nullptr; /// Положим в блок аргументы выражения. diff --git a/dbms/include/DB/Functions/FunctionsStringArray.h b/dbms/include/DB/Functions/FunctionsStringArray.h index 91b3cf3b542..f3248971447 100644 --- a/dbms/include/DB/Functions/FunctionsStringArray.h +++ b/dbms/include/DB/Functions/FunctionsStringArray.h @@ -238,14 +238,13 @@ public: class ExtractAllImpl { private: - const OptimizedRegularExpression *re; + const OptimizedRegularExpression * re = nullptr; OptimizedRegularExpression::MatchVec matches; size_t capture; Pos pos; Pos end; public: - ExtractAllImpl():re(NULL) { } /// Получить имя функции. static String getName() { return "extractAll"; } @@ -346,8 +345,8 @@ public: res_strings_offsets.reserve(src_offsets.size() * 5); /// Константа 5 - наугад. res_strings_chars.reserve(src_chars.size()); - Pos token_begin = NULL; - Pos token_end = NULL; + Pos token_begin = nullptr; + Pos token_end = nullptr; size_t size = src_offsets.size(); ColumnString::Offset_t current_src_offset = 0; @@ -387,8 +386,8 @@ public: Array dst; generator.set(src.data(), src.data() + src.size()); - Pos token_begin = NULL; - Pos token_end = NULL; + Pos token_begin = nullptr; + Pos token_end = nullptr; while (generator.get(token_begin, token_end)) dst.push_back(String(token_begin, token_end - token_begin)); diff --git a/dbms/include/DB/Functions/FunctionsURL.h b/dbms/include/DB/Functions/FunctionsURL.h index 42dcb319ac0..f5559b4f005 100644 --- a/dbms/include/DB/Functions/FunctionsURL.h +++ b/dbms/include/DB/Functions/FunctionsURL.h @@ -291,32 +291,32 @@ struct ExtractURLParameterImpl { size_t cur_offset = offsets[i]; - const char * pos = NULL; + const char * pos = nullptr; do { const char * str = reinterpret_cast(&data[prev_offset]); const char * begin = strchr(str, '?'); - if (begin == NULL) + if (begin == nullptr) break; pos = strstr(begin + 1, param_str); - if (pos == NULL) + if (pos == nullptr) break; if (pos != begin + 1 && *(pos - 1) != ';' && *(pos - 1) != '&') { - pos = NULL; + pos = nullptr; break; } pos += param_len; } while (false); - if (pos != NULL) + if (pos != nullptr) { const char * end = strpbrk(pos, "&;#"); - if (end == NULL) + if (end == nullptr) end = pos + strlen(pos); res_data.resize(res_offset + (end - pos) + 1); @@ -367,15 +367,15 @@ struct CutURLParameterImpl do { const char * begin = strchr(url_begin, '?'); - if (begin == NULL) + if (begin == nullptr) break; const char * pos = strstr(begin + 1, param_str); - if (pos == NULL) + if (pos == nullptr) break; if (pos != begin + 1 && *(pos - 1) != ';' && *(pos - 1) != '&') { - pos = NULL; + pos = nullptr; break; } @@ -448,25 +448,25 @@ public: /// Получить следующий токен, если есть, или вернуть false. bool get(Pos & token_begin, Pos & token_end) { - if (pos == NULL) + if (pos == nullptr) return false; if (first) { first = false; pos = strchr(pos, '?'); - if (pos == NULL) + if (pos == nullptr) return false; ++pos; } token_begin = pos; pos = strchr(pos, '='); - if (pos == NULL) + if (pos == nullptr) return false; ++pos; pos = strpbrk(pos, "&;#"); - if (pos == NULL) + if (pos == nullptr) token_end = end; else token_end = pos++; @@ -516,7 +516,7 @@ public: /// Получить следующий токен, если есть, или вернуть false. bool get(Pos & token_begin, Pos & token_end) { - if (pos == NULL) + if (pos == nullptr) return false; if (first) @@ -527,14 +527,14 @@ public: else pos = strchr(pos, '&'); - if (pos == NULL) + if (pos == nullptr) return false; ++pos; token_begin = pos; pos = strchr(pos, '='); - if (pos == NULL) + if (pos == nullptr) return false; else token_end = pos++; diff --git a/dbms/include/DB/IO/BufferWithOwnMemory.h b/dbms/include/DB/IO/BufferWithOwnMemory.h index 0c044ddfb4c..88a0896d4d9 100644 --- a/dbms/include/DB/IO/BufferWithOwnMemory.h +++ b/dbms/include/DB/IO/BufferWithOwnMemory.h @@ -80,7 +80,7 @@ private: { if (!m_capacity) { - m_data = NULL; + m_data = nullptr; return; } @@ -121,7 +121,7 @@ protected: Memory memory; public: /// Если передать не-NULL existing_memory, то буфер не будет создавать свой кусок памяти, а будет использовать существующий (и не будет им владеть). - BufferWithOwnMemory(size_t size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = NULL, size_t alignment = 0) : Base(NULL, 0), memory(existing_memory ? 0 : size, alignment) + BufferWithOwnMemory(size_t size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = nullptr, size_t alignment = 0) : Base(NULL, 0), memory(existing_memory ? 0 : size, alignment) { Base::set(existing_memory ? existing_memory : &memory[0], size); } diff --git a/dbms/include/DB/IO/CachedCompressedReadBuffer.h b/dbms/include/DB/IO/CachedCompressedReadBuffer.h index d96ba7e1aca..548a5e71a99 100644 --- a/dbms/include/DB/IO/CachedCompressedReadBuffer.h +++ b/dbms/include/DB/IO/CachedCompressedReadBuffer.h @@ -67,7 +67,7 @@ private: if (owned_cell->data.m_size == 0) { - owned_cell = NULL; + owned_cell = nullptr; return false; } diff --git a/dbms/include/DB/IO/CompressedReadBufferBase.h b/dbms/include/DB/IO/CompressedReadBufferBase.h index b2bd8a7b60f..5ee8b76ae8e 100644 --- a/dbms/include/DB/IO/CompressedReadBufferBase.h +++ b/dbms/include/DB/IO/CompressedReadBufferBase.h @@ -24,10 +24,10 @@ protected: ReadBuffer * compressed_in; /// Если в буфере compressed_in помещается целый сжатый блок - используем его. Иначе - копируем данные по кусочкам в own_compressed_buffer. - PODArray own_compressed_buffer; - char * compressed_buffer; + PODArray own_compressed_buffer{QUICKLZ_HEADER_SIZE}; + char * compressed_buffer = nullptr; - qlz_state_decompress * qlz_state; + qlz_state_decompress * qlz_state = nullptr; /// Прочитать сжатые данные в compressed_buffer. Достать из их заголовка размер разжатых данных. Проверить чексумму. /// Возвращает количество прочитанных байт. @@ -90,12 +90,8 @@ protected: public: /// compressed_in можно инициализировать отложенно, но до первого вызова readCompressedData. - CompressedReadBufferBase(ReadBuffer * in = NULL) - : - compressed_in(in), - own_compressed_buffer(QUICKLZ_HEADER_SIZE), - compressed_buffer(NULL), - qlz_state(NULL) + CompressedReadBufferBase(ReadBuffer * in = nullptr) + : compressed_in(in) { } diff --git a/dbms/include/DB/IO/CompressedWriteBuffer.h b/dbms/include/DB/IO/CompressedWriteBuffer.h index 6fef2bc357f..f865de71860 100644 --- a/dbms/include/DB/IO/CompressedWriteBuffer.h +++ b/dbms/include/DB/IO/CompressedWriteBuffer.h @@ -36,7 +36,7 @@ private: size_t uncompressed_size = offset(); size_t compressed_size = 0; - char * compressed_buffer_ptr = NULL; + char * compressed_buffer_ptr = nullptr; /** Для того, чтобы различить между QuickLZ и LZ4 и сохранить обратную совместимость (со случаем, когда использовался только QuickLZ), * используем старший бит первого байта в сжатых данных (который сейчас не используется в QuickLZ). diff --git a/dbms/include/DB/IO/ReadBufferFromFile.h b/dbms/include/DB/IO/ReadBufferFromFile.h index 37d9327f2fe..69ce0fdfc7f 100644 --- a/dbms/include/DB/IO/ReadBufferFromFile.h +++ b/dbms/include/DB/IO/ReadBufferFromFile.h @@ -17,7 +17,7 @@ private: public: ReadBufferFromFile(const std::string & file_name_, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, int flags = -1, - char * existing_memory = NULL, size_t alignment = 0) + char * existing_memory = nullptr, size_t alignment = 0) : ReadBufferFromFileDescriptor(-1, buf_size, existing_memory, alignment), file_name(file_name_) { ProfileEvents::increment(ProfileEvents::FileOpen); diff --git a/dbms/include/DB/IO/ReadBufferFromFileDescriptor.h b/dbms/include/DB/IO/ReadBufferFromFileDescriptor.h index bdbd6056770..ea89708910e 100644 --- a/dbms/include/DB/IO/ReadBufferFromFileDescriptor.h +++ b/dbms/include/DB/IO/ReadBufferFromFileDescriptor.h @@ -59,7 +59,7 @@ protected: } public: - ReadBufferFromFileDescriptor(int fd_, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = NULL, size_t alignment = 0) + ReadBufferFromFileDescriptor(int fd_, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = nullptr, size_t alignment = 0) : BufferWithOwnMemory(buf_size, existing_memory, alignment), fd(fd_), pos_in_file(0) {} int getFD() diff --git a/dbms/include/DB/IO/WriteBufferFromFile.h b/dbms/include/DB/IO/WriteBufferFromFile.h index d0cf7e409b2..d3a8553ac35 100644 --- a/dbms/include/DB/IO/WriteBufferFromFile.h +++ b/dbms/include/DB/IO/WriteBufferFromFile.h @@ -21,7 +21,7 @@ private: public: WriteBufferFromFile(const std::string & file_name_, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, int flags = -1, mode_t mode = 0666, - char * existing_memory = NULL, size_t alignment = 0) + char * existing_memory = nullptr, size_t alignment = 0) : WriteBufferFromFileDescriptor(-1, buf_size, existing_memory, alignment), file_name(file_name_) { ProfileEvents::increment(ProfileEvents::FileOpen); diff --git a/dbms/include/DB/IO/WriteBufferFromFileDescriptor.h b/dbms/include/DB/IO/WriteBufferFromFileDescriptor.h index fbd4684920d..6dd76e9d642 100644 --- a/dbms/include/DB/IO/WriteBufferFromFileDescriptor.h +++ b/dbms/include/DB/IO/WriteBufferFromFileDescriptor.h @@ -46,7 +46,7 @@ protected: } public: - WriteBufferFromFileDescriptor(int fd_ = -1, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = NULL, size_t alignment = 0) + WriteBufferFromFileDescriptor(int fd_ = -1, size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = nullptr, size_t alignment = 0) : BufferWithOwnMemory(buf_size, existing_memory, alignment), fd(fd_) {} /** Можно вызывать для инициализации, если нужный fd не был передан в конструктор. diff --git a/dbms/include/DB/IO/WriteBufferFromHTTPServerResponse.h b/dbms/include/DB/IO/WriteBufferFromHTTPServerResponse.h index e554e5e7140..3911b5060be 100644 --- a/dbms/include/DB/IO/WriteBufferFromHTTPServerResponse.h +++ b/dbms/include/DB/IO/WriteBufferFromHTTPServerResponse.h @@ -24,7 +24,7 @@ class WriteBufferFromHTTPServerResponse : public BufferWithOwnMemory(size), response(response_), ostr(NULL) {} + : BufferWithOwnMemory(size), response(response_) {} /** Если данные ещё не были отправлены - отправить хотя бы HTTP заголовки. * Используйте эту функцию после того, как данные, возможно, были отправлены, diff --git a/dbms/include/DB/Interpreters/Aggregator.h b/dbms/include/DB/Interpreters/Aggregator.h index b324bfbbbb3..d8706785901 100644 --- a/dbms/include/DB/Interpreters/Aggregator.h +++ b/dbms/include/DB/Interpreters/Aggregator.h @@ -68,7 +68,7 @@ struct AggregatedDataVariants : private boost::noncopyable * Но это вряд ли можно просто сделать, так как в этот же пул планируется класть строки переменной длины. * В этом случае, пул не сможет знать, по каким смещениям хранятся объекты. */ - Aggregator * aggregator; + Aggregator * aggregator = nullptr; /// Пулы для состояний агрегатных функций. Владение потом будет передано в ColumnAggregateFunction. Arenas aggregates_pools; @@ -76,7 +76,7 @@ struct AggregatedDataVariants : private boost::noncopyable /** Специализация для случая, когда ключи отсутствуют, и для ключей, не попавших в max_rows_to_group_by. */ - AggregatedDataWithoutKey without_key; + AggregatedDataWithoutKey without_key = nullptr; /// Специализация для случая, когда есть один числовой ключ. /// auto_ptr - для ленивой инициализации (так как иначе HashMap в конструкторе выделяет и зануляет слишком много памяти). @@ -107,9 +107,9 @@ struct AggregatedDataVariants : private boost::noncopyable KEYS_128 = 4, HASHED = 5, }; - Type type; + Type type = EMPTY; - AggregatedDataVariants() : aggregator(NULL), aggregates_pools(1, new Arena), aggregates_pool(&*aggregates_pools.back()), without_key(NULL), type(EMPTY) {} + AggregatedDataVariants() : aggregates_pools(1, new Arena), aggregates_pool(&*aggregates_pools.back()) {} bool empty() const { return type == EMPTY; } ~AggregatedDataVariants(); @@ -138,10 +138,10 @@ struct AggregatedDataVariants : private boost::noncopyable { case EMPTY: return 0; case WITHOUT_KEY: return 1; - case KEY_64: return key64->size() + (without_key != NULL); - case KEY_STRING: return key_string->size() + (without_key != NULL); - case KEYS_128: return keys128->size() + (without_key != NULL); - case HASHED: return hashed->size() + (without_key != NULL); + case KEY_64: return key64->size() + (without_key != nullptr); + case KEY_STRING: return key_string->size() + (without_key != nullptr); + case KEYS_128: return keys128->size() + (without_key != nullptr); + case HASHED: return hashed->size() + (without_key != nullptr); default: throw Exception("Unknown aggregated data variant.", ErrorCodes::UNKNOWN_AGGREGATED_DATA_VARIANT); diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index fe29bb5ecd2..aaf93dd5c67 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -165,27 +165,25 @@ class Context { private: typedef SharedPtr Shared; - Shared shared; + Shared shared = new ContextShared; String user; /// Текущий пользователь. Poco::Net::IPAddress ip_address; /// IP-адрес, с которого задан запрос. - QuotaForIntervalsPtr quota; /// Текущая квота. По-умолчанию - пустая квота, которая ничего не ограничивает. + QuotaForIntervalsPtr quota = new QuotaForIntervals; /// Текущая квота. По-умолчанию - пустая квота, которая ничего не ограничивает. String current_database; /// Текущая БД. String current_query_id; /// Id текущего запроса. NamesAndTypesList columns; /// Столбцы текущей обрабатываемой таблицы. Settings settings; /// Настройки выполнения запроса. ProgressCallback progress_callback; /// Колбек для отслеживания прогресса выполнения запроса. - ProcessList::Element * process_list_elem; /// Для отслеживания общего количества потраченных на запрос ресурсов. + ProcessList::Element * process_list_elem = nullptr; /// Для отслеживания общего количества потраченных на запрос ресурсов. String default_format; /// Формат, используемый, если сервер сам форматирует данные, и если в запросе не задан FORMAT. /// То есть, используется в HTTP-интерфейсе. Может быть не задан - тогда используется некоторый глобальный формат по-умолчанию. Tables external_tables; /// Временные таблицы. - Context * session_context; /// Контекст сессии или NULL, если его нет. (Возможно, равен this.) - Context * global_context; /// Глобальный контекст или NULL, если его нет. (Возможно, равен this.) + Context * session_context = nullptr; /// Контекст сессии или NULL, если его нет. (Возможно, равен this.) + Context * global_context = nullptr; /// Глобальный контекст или NULL, если его нет. (Возможно, равен this.) public: - Context() : shared(new ContextShared), quota(new QuotaForIntervals), process_list_elem(NULL), session_context(NULL), global_context(NULL) {} - String getPath() const; void setPath(const String & path); diff --git a/dbms/include/DB/Interpreters/ExpressionActions.h b/dbms/include/DB/Interpreters/ExpressionActions.h index 86915500925..c898be9be5e 100644 --- a/dbms/include/DB/Interpreters/ExpressionActions.h +++ b/dbms/include/DB/Interpreters/ExpressionActions.h @@ -237,7 +237,7 @@ struct ExpressionActionsChain ExpressionActionsPtr actions; Names required_output; - Step(ExpressionActionsPtr actions_ = NULL, Names required_output_ = Names()) + Step(ExpressionActionsPtr actions_ = nullptr, Names required_output_ = Names()) : actions(actions_), required_output(required_output_) {} }; diff --git a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h index f190ca54035..6478d45c205 100644 --- a/dbms/include/DB/Interpreters/InterpreterSelectQuery.h +++ b/dbms/include/DB/Interpreters/InterpreterSelectQuery.h @@ -16,12 +16,12 @@ namespace DB class InterpreterSelectQuery { public: - InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = NULL); + InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = nullptr); InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, const Names & required_column_names, - QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = NULL); + QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = nullptr); - InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, const Names & required_column_names, const NamesAndTypesList & table_column_names, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = NULL); + InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, const Names & required_column_names, const NamesAndTypesList & table_column_names, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete, size_t subquery_depth_ = 0, BlockInputStreamPtr input = nullptr); /// Выполнить запрос, получить поток блоков для чтения BlockInputStreamPtr execute(); diff --git a/dbms/include/DB/Interpreters/Users.h b/dbms/include/DB/Interpreters/Users.h index 4728109e30a..b3ea308158c 100644 --- a/dbms/include/DB/Interpreters/Users.h +++ b/dbms/include/DB/Interpreters/Users.h @@ -111,7 +111,7 @@ public: Poco::Net::IPAddress addr_v6 = toIPv6(addr); /// Резолвим вручную, потому что в Poco не используется флаг AI_ALL, а он важен. - addrinfo * ai = NULL; + addrinfo * ai = nullptr; addrinfo hints; memset(&hints, 0, sizeof(hints)); @@ -124,7 +124,7 @@ public: try { - for (; ai != NULL; ai = ai->ai_next) + for (; ai != nullptr; ai = ai->ai_next) { if (ai->ai_addrlen && ai->ai_addr) { diff --git a/dbms/include/DB/Parsers/ASTInsertQuery.h b/dbms/include/DB/Parsers/ASTInsertQuery.h index d8f9d8565d6..382e089135e 100644 --- a/dbms/include/DB/Parsers/ASTInsertQuery.h +++ b/dbms/include/DB/Parsers/ASTInsertQuery.h @@ -21,11 +21,11 @@ public: /// Идентификатор запроса INSERT. Используется при репликации. String insert_id; /// Данные для вставки - const char * data; - const char * end; + const char * data = nullptr; + const char * end = nullptr; ASTInsertQuery() {} - ASTInsertQuery(StringRange range_) : IAST(range_), data(NULL), end(NULL) {} + ASTInsertQuery(StringRange range_) : IAST(range_) {} /** Получить текст, который идентифицирует этот элемент. */ String getID() const { return "InsertQuery_" + database + "_" + table; }; diff --git a/dbms/include/DB/Parsers/ASTOrderByElement.h b/dbms/include/DB/Parsers/ASTOrderByElement.h index ed8a0168415..c54c71a4d3d 100644 --- a/dbms/include/DB/Parsers/ASTOrderByElement.h +++ b/dbms/include/DB/Parsers/ASTOrderByElement.h @@ -22,7 +22,7 @@ public: Poco::SharedPtr collator; ASTOrderByElement() {} - ASTOrderByElement(StringRange range_, int direction_, const Poco::SharedPtr & collator_ = NULL) + ASTOrderByElement(StringRange range_, int direction_, const Poco::SharedPtr & collator_ = nullptr) : IAST(range_), direction(direction_), collator(collator_) {} /** Получить текст, который идентифицирует этот элемент. */ diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h index eb716cfe50a..e75ed3fa675 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeBlockInputStream.h @@ -243,7 +243,7 @@ protected: * Чтобы при создании многих источников, но одновременном чтении только из нескольких, * буферы не висели в памяти. */ - reader = NULL; + reader = nullptr; } return res; diff --git a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h index 47e7f0ccf1c..d706864f655 100644 --- a/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h +++ b/dbms/include/DB/Storages/MergeTree/MergedBlockOutputStream.h @@ -257,7 +257,7 @@ public: it->second->addToChecksums(checksums); } - index_stream = NULL; + index_stream = nullptr; column_streams.clear(); if (marks_count == 0) diff --git a/dbms/src/Client/Connection.cpp b/dbms/src/Client/Connection.cpp index 270f841c41d..6f39978526b 100644 --- a/dbms/src/Client/Connection.cpp +++ b/dbms/src/Client/Connection.cpp @@ -72,8 +72,8 @@ void Connection::disconnect() //LOG_TRACE(log, "Disconnecting (" << getServerAddress() << ")"); socket.close(); - in = NULL; - out = NULL; + in = nullptr; + out = nullptr; connected = false; } @@ -156,7 +156,7 @@ void Connection::forceConnected() bool Connection::ping() { - //LOG_TRACE(log, "Ping (" << getServerAddress() << ")"); + LOG_TRACE(log, "Ping (" << getServerAddress() << ")"); try { @@ -226,10 +226,10 @@ void Connection::sendQuery(const String & query, const String & query_id_, UInt6 writeStringBinary(query, *out); - maybe_compressed_in = NULL; - maybe_compressed_out = NULL; - block_in = NULL; - block_out = NULL; + maybe_compressed_in = nullptr; + maybe_compressed_out = nullptr; + block_in = nullptr; + block_out = nullptr; /// Если версия сервера достаточно новая и стоит флаг, отправляем пустой блок, символизируя конец передачи данных. if (server_revision >= DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES && !with_pending_data) diff --git a/dbms/src/Core/tests/string_pool.cpp b/dbms/src/Core/tests/string_pool.cpp index f6f50ef353e..accadc5c784 100644 --- a/dbms/src/Core/tests/string_pool.cpp +++ b/dbms/src/Core/tests/string_pool.cpp @@ -61,7 +61,7 @@ int main(int argc, char ** argv) { DB::Arena pool; Stopwatch watch; - const char * res = NULL; + const char * res = nullptr; for (Vec::iterator it = vec.begin(); it != vec.end(); ++it) { diff --git a/dbms/src/DataStreams/IProfilingBlockInputStream.cpp b/dbms/src/DataStreams/IProfilingBlockInputStream.cpp index 975a9706468..fd7d71e17ea 100644 --- a/dbms/src/DataStreams/IProfilingBlockInputStream.cpp +++ b/dbms/src/DataStreams/IProfilingBlockInputStream.cpp @@ -211,7 +211,7 @@ Block IProfilingBlockInputStream::read() return res; } - if (quota != NULL) + if (quota != nullptr) checkQuota(res); } else @@ -398,7 +398,7 @@ void IProfilingBlockInputStream::progressImpl(size_t rows, size_t bytes) ErrorCodes::TOO_SLOW); } - if (quota != NULL && limits.mode == LIMITS_TOTAL) + if (quota != nullptr && limits.mode == LIMITS_TOTAL) { quota->checkAndAddReadRowsBytes(time(0), rows, bytes); } diff --git a/dbms/src/IO/WriteHelpers.cpp b/dbms/src/IO/WriteHelpers.cpp index ad7dc85ebeb..85d10f8c452 100644 --- a/dbms/src/IO/WriteHelpers.cpp +++ b/dbms/src/IO/WriteHelpers.cpp @@ -10,7 +10,7 @@ void writeException(const Exception & e, WriteBuffer & buf) writeBinary(e.displayText(), buf); writeBinary(e.getStackTrace().toString(), buf); - bool has_nested = e.nested() != NULL; + bool has_nested = e.nested() != nullptr; writeBinary(has_nested, buf); if (has_nested) diff --git a/dbms/src/Interpreters/Aggregator.cpp b/dbms/src/Interpreters/Aggregator.cpp index df5c9f58e34..a5c51c50259 100644 --- a/dbms/src/Interpreters/Aggregator.cpp +++ b/dbms/src/Interpreters/Aggregator.cpp @@ -691,7 +691,7 @@ Block Aggregator::convertToBlock(AggregatedDataVariants & data_variants, bool fi if (!final) { /// data_variants не будет уничтожать состояния агрегатных функций в деструкторе. Теперь состояниями владеют ColumnAggregateFunction. - data_variants.aggregator = NULL; + data_variants.aggregator = nullptr; } /// Изменяем размер столбцов-констант в блоке. @@ -856,7 +856,7 @@ AggregatedDataVariantsPtr Aggregator::merge(ManyAggregatedDataVariants & data_va throw Exception("Unknown aggregated data variant.", ErrorCodes::UNKNOWN_AGGREGATED_DATA_VARIANT); /// current не будет уничтожать состояния агрегатных функций в деструкторе - current.aggregator = NULL; + current.aggregator = nullptr; } double elapsed_seconds = watch.elapsedSeconds(); diff --git a/dbms/src/Interpreters/ExpressionActions.cpp b/dbms/src/Interpreters/ExpressionActions.cpp index 5156b4409fa..21d7753cad3 100644 --- a/dbms/src/Interpreters/ExpressionActions.cpp +++ b/dbms/src/Interpreters/ExpressionActions.cpp @@ -127,7 +127,7 @@ void ExpressionActions::Action::prepare(Block & sample_block) ColumnWithNameAndType & col = sample_block.getByPosition(result_position); if (!col.column->isConst()) { - col.column = NULL; + col.column = nullptr; } } else @@ -144,7 +144,7 @@ void ExpressionActions::Action::prepare(Block & sample_block) if (!array_type) throw Exception("ARRAY JOIN requires array argument", ErrorCodes::TYPE_MISMATCH); current.type = array_type->getNestedType(); - current.column = NULL; + current.column = nullptr; } } else if (type == ADD_COLUMN) diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index b10d07689d9..56457981253 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -227,7 +227,7 @@ StoragePtr InterpreterCreateQuery::execute(bool assume_metadata_exists) /// Для engine VIEW необходимо сохранить сам селект запрос, для остальных - наоборот if (storage_name != "View" && storage_name != "MaterializedView") - attach.select = NULL; + attach.select = nullptr; Poco::FileOutputStream metadata_file(metadata_path); formatAST(attach, metadata_file, 0, false); diff --git a/dbms/src/Interpreters/tests/expression_analyzer.cpp b/dbms/src/Interpreters/tests/expression_analyzer.cpp index 71c211d2211..a9db557457e 100644 --- a/dbms/src/Interpreters/tests/expression_analyzer.cpp +++ b/dbms/src/Interpreters/tests/expression_analyzer.cpp @@ -39,7 +39,7 @@ int main(int argc, char ** argv) if (parser.parse(pos ,end, root, expected)) break; else - root = NULL; + root = nullptr; } if (!root) { diff --git a/dbms/src/Parsers/ExpressionElementParsers.cpp b/dbms/src/Parsers/ExpressionElementParsers.cpp index 46a4202f2ea..690088a64b9 100644 --- a/dbms/src/Parsers/ExpressionElementParsers.cpp +++ b/dbms/src/Parsers/ExpressionElementParsers.cpp @@ -231,7 +231,7 @@ bool ParserFunction::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char *& if (open.ignore(pos, end, expected)) { expr_list_params = expr_list_args; - expr_list_args = NULL; + expr_list_args = nullptr; ws.ignore(pos, end); contents.parse(pos, end, expr_list_args, expected); @@ -521,7 +521,7 @@ bool ParserOrderByElement::parseImpl(Pos & pos, Pos end, ASTPtr & node, const ch ws.ignore(pos, end); - Poco::SharedPtr collator = NULL; + Poco::SharedPtr collator = nullptr; if (collate.ignore(pos, end)) { ws.ignore(pos, end); diff --git a/dbms/src/Parsers/ParserInsertQuery.cpp b/dbms/src/Parsers/ParserInsertQuery.cpp index a7da264f980..ddb4f42dce2 100644 --- a/dbms/src/Parsers/ParserInsertQuery.cpp +++ b/dbms/src/Parsers/ParserInsertQuery.cpp @@ -40,7 +40,7 @@ bool ParserInsertQuery::parseImpl(Pos & pos, Pos end, ASTPtr & node, const char ASTPtr select; ASTPtr id; /// Данные для вставки - const char * data = NULL; + const char * data = nullptr; ws.ignore(pos, end); diff --git a/dbms/src/Server/Server.cpp b/dbms/src/Server/Server.cpp index a78a438aab8..e45e4ebb912 100644 --- a/dbms/src/Server/Server.cpp +++ b/dbms/src/Server/Server.cpp @@ -342,7 +342,7 @@ int Server::main(const std::vector & args) LOG_DEBUG(log, "Received termination signal. Waiting for current connections to close."); - users_config_reloader = NULL; + users_config_reloader = nullptr; is_cancelled = true; @@ -365,7 +365,7 @@ int Server::main(const std::vector & args) /** Явно уничтожаем контекст - это удобнее, чем в деструкторе Server-а, так как ещё доступен логгер. * В этот момент никто больше не должен владеть shared-частью контекста. */ - global_context = NULL; + global_context = nullptr; LOG_DEBUG(log, "Destroyed global context."); diff --git a/dbms/src/Server/TCPHandler.cpp b/dbms/src/Server/TCPHandler.cpp index 819ab1819b4..48e1ebdf372 100644 --- a/dbms/src/Server/TCPHandler.cpp +++ b/dbms/src/Server/TCPHandler.cpp @@ -3,7 +3,6 @@ #include #include -#include #include @@ -121,7 +120,7 @@ void TCPHandler::runImpl() /// Очищаем, так как, получая данные внешних таблиц, мы получили пустой блок. /// А значит, stream помечен как cancelled и читать из него нельзя. - state.block_in = NULL; + state.block_in = nullptr; /// Обрабатываем Query state.io = executeQuery(state.query, query_context, false, state.stage); diff --git a/dbms/src/Storages/StorageChunkRef.cpp b/dbms/src/Storages/StorageChunkRef.cpp index 4c0ada7e39f..5e2b4442da1 100644 --- a/dbms/src/Storages/StorageChunkRef.cpp +++ b/dbms/src/Storages/StorageChunkRef.cpp @@ -76,7 +76,7 @@ const StorageChunks & StorageChunkRef::getSource() const const StoragePtr table_ptr = context.getTable(source_database_name, source_table_name); const StorageChunks * chunks = dynamic_cast(&*table_ptr); - if (chunks == NULL) + if (chunks == nullptr) throw Exception("Referenced table " + source_table_name + " in database " + source_database_name + " doesn't exist", ErrorCodes::UNKNOWN_TABLE); return *chunks; diff --git a/dbms/src/Storages/StorageChunks.cpp b/dbms/src/Storages/StorageChunks.cpp index c3579bbff2a..7c6c8a85ddf 100644 --- a/dbms/src/Storages/StorageChunks.cpp +++ b/dbms/src/Storages/StorageChunks.cpp @@ -126,7 +126,7 @@ BlockOutputStreamPtr StorageChunks::writeToNewChunk( chunk_names.push_back(chunk_name); } - return StorageLog::write(NULL); + return StorageLog::write(nullptr); } StorageChunks::StorageChunks( diff --git a/dbms/src/Storages/tests/merge_tree.cpp b/dbms/src/Storages/tests/merge_tree.cpp index a1e2de8e69f..8963c3e2040 100644 --- a/dbms/src/Storages/tests/merge_tree.cpp +++ b/dbms/src/Storages/tests/merge_tree.cpp @@ -69,7 +69,7 @@ int main(int argc, char ** argv) block.insert(column2); - SharedPtr out = table->write(NULL); + SharedPtr out = table->write(nullptr); out->write(block); } diff --git a/libs/libmysqlxx/include/mysqlxx/Pool.h b/libs/libmysqlxx/include/mysqlxx/Pool.h index de6ad05c1cd..91f69e03912 100644 --- a/libs/libmysqlxx/include/mysqlxx/Pool.h +++ b/libs/libmysqlxx/include/mysqlxx/Pool.h @@ -53,7 +53,7 @@ public: class Entry { public: - Entry() : data(NULL), pool(NULL) {} + Entry() {} Entry(const Entry & src) : data(src.data), pool(src.pool) @@ -79,12 +79,12 @@ public: bool isNull() const { - return data == NULL; + return data == nullptr; } operator mysqlxx::Connection & () { - if (data == NULL) + if (data == nullptr) throw Poco::RuntimeException("Tried to access NULL database connection."); forceConnected(); return data->conn; @@ -92,7 +92,7 @@ public: operator const mysqlxx::Connection & () const { - if (data == NULL) + if (data == nullptr) throw Poco::RuntimeException("Tried to access NULL database connection."); forceConnected(); return data->conn; @@ -100,7 +100,7 @@ public: const mysqlxx::Connection * operator->() const { - if (data == NULL) + if (data == nullptr) throw Poco::RuntimeException("Tried to access NULL database connection."); forceConnected(); return &data->conn; @@ -108,7 +108,7 @@ public: mysqlxx::Connection * operator->() { - if (data == NULL) + if (data == nullptr) throw Poco::RuntimeException("Tried to access NULL database connection."); forceConnected(); return &data->conn; @@ -131,9 +131,9 @@ public: private: /** Указатель на соединение. */ - Connection * data; + Connection * data = nullptr; /** Указатель на пул, которому мы принадлежим. */ - Pool * pool; + Pool * pool = nullptr; /** Переподключается к базе данных в случае необходимости. Если не удалось - подождать и попробовать снова. */ void forceConnected() const @@ -197,7 +197,7 @@ public: Pool(const std::string & config_name, unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS, unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS, - const char * parent_config_name_ = NULL) + const char * parent_config_name_ = nullptr) : default_connections(default_connections_), max_connections(max_connections_), initialized(false), was_successful(false) { diff --git a/libs/libmysqlxx/include/mysqlxx/Row.h b/libs/libmysqlxx/include/mysqlxx/Row.h index ab8d4490416..814b1fe3eee 100644 --- a/libs/libmysqlxx/include/mysqlxx/Row.h +++ b/libs/libmysqlxx/include/mysqlxx/Row.h @@ -30,7 +30,7 @@ private: public: /** Для возможности отложенной инициализации. */ - Row() : row(NULL), res(NULL) + Row() { } @@ -76,16 +76,16 @@ public: * при использовании UseQueryResult. Или это значит, что объект не инициализирован. * Вы можете использовать вместо этого преобразование в bool. */ - bool empty() const { return row == NULL; } + bool empty() const { return row == nullptr; } /** Преобразование в bool. * (Точнее - в тип, который преобразуется в bool, и с которым больше почти ничего нельзя сделать.) */ - operator private_bool_type() const { return row == NULL ? NULL : &Row::row; } + operator private_bool_type() const { return row == nullptr ? NULL : &Row::row; } private: - MYSQL_ROW row; - ResultBase * res; + MYSQL_ROW row = nullptr; + ResultBase * res = nullptr; MYSQL_LENGTHS lengths; }; diff --git a/libs/libmysqlxx/include/mysqlxx/String.h b/libs/libmysqlxx/include/mysqlxx/String.h index cf9e51a08a6..a6feb4d400c 100644 --- a/libs/libmysqlxx/include/mysqlxx/String.h +++ b/libs/libmysqlxx/include/mysqlxx/String.h @@ -112,7 +112,7 @@ public: /// Является ли NULL. bool isNull() const { - return m_data == NULL; + return m_data == nullptr; } /// Для совместимости (используйте вместо этого метод isNull()) diff --git a/libs/libmysqlxx/src/PoolWithFailover.cpp b/libs/libmysqlxx/src/PoolWithFailover.cpp index 5cd0c9ac433..8afeb283b95 100644 --- a/libs/libmysqlxx/src/PoolWithFailover.cpp +++ b/libs/libmysqlxx/src/PoolWithFailover.cpp @@ -38,11 +38,11 @@ PoolWithFailover::Entry PoolWithFailover::Get() Poco::Util::Application & app = Poco::Util::Application::instance(); /// Если к какой-то реплике не подключились, потому что исчерпан лимит соединений, можно подождать и подключиться к ней. - Replica * full_pool = NULL; + Replica * full_pool = nullptr; for (size_t try_no = 0; try_no < max_tries; ++try_no) { - full_pool = NULL; + full_pool = nullptr; for (ReplicasByPriority::iterator it = replicas_by_priority.begin(); it != replicas_by_priority.end(); ++it) { diff --git a/libs/libpocoext/include/Poco/Ext/LevelFilterChannel.h b/libs/libpocoext/include/Poco/Ext/LevelFilterChannel.h index 7827be1cda9..ed4f293f100 100644 --- a/libs/libpocoext/include/Poco/Ext/LevelFilterChannel.h +++ b/libs/libpocoext/include/Poco/Ext/LevelFilterChannel.h @@ -1,44 +1,4 @@ -// -// LevelFilterChannel.h -// -// $Id$ -// -// Library: Ext -// Package: Logging -// Module: LevelFilterChannel -// -// Definition of the ISO_8859_5Encoding class. -// -// Copyright (c) 2004-2007, Applied Informatics Software Engineering GmbH. -// and Contributors. -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// - - -#ifndef Foundation_LevelFilterChannel_INCLUDED -#define Foundation_LevelFilterChannel_INCLUDED - +#pragma once #include "Poco/Foundation.h" #include "Poco/Channel.h" @@ -54,8 +14,6 @@ class Foundation_API LevelFilterChannel: public Channel /// This channel sends messages only higher then specified level { public: - LevelFilterChannel(); - void log(const Message& msg); /// Sends the given Message to all /// attaches channels. @@ -90,12 +48,9 @@ protected: ~LevelFilterChannel(); private: - Channel* _channel; - Message::Priority _priority; + Channel* _channel = nullptr; + Message::Priority _priority = Message::PRIO_ERROR; }; -} // namespace Poco - - -#endif // Foundation_LevelFilterChannel_INCLUDED +} diff --git a/libs/libpocoext/src/LevelFilterChannel.cpp b/libs/libpocoext/src/LevelFilterChannel.cpp index 5544f4de13b..797a673d6f1 100644 --- a/libs/libpocoext/src/LevelFilterChannel.cpp +++ b/libs/libpocoext/src/LevelFilterChannel.cpp @@ -42,11 +42,6 @@ namespace Poco { -LevelFilterChannel::LevelFilterChannel() - : Channel(), _channel(NULL), _priority(Message::PRIO_ERROR) -{ -} - LevelFilterChannel::~LevelFilterChannel() { if (_channel) @@ -136,4 +131,4 @@ void LevelFilterChannel::log(const Message& msg) } -} // namespace Poco +} diff --git a/utils/iotest/iotest_aio.cpp b/utils/iotest/iotest_aio.cpp index b3364d9e4f1..5bde2ef5b73 100644 --- a/utils/iotest/iotest_aio.cpp +++ b/utils/iotest/iotest_aio.cpp @@ -60,10 +60,10 @@ enum Mode struct AlignedBuffer { - int size; - char * data; + int size = 0; + char * data = nullptr; - AlignedBuffer() : size(0), data(NULL) {} + AlignedBuffer() {} void init(int size_) { @@ -79,7 +79,7 @@ struct AlignedBuffer { if (data) free(data); - data = NULL; + data = nullptr; size = 0; } From 9dad91788ba45fda25db56678587c441bf708e68 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 11:47:51 +0400 Subject: [PATCH 188/281] Improvement [#METR-2807]. --- dbms/include/DB/Common/HashTableAllocator.h | 6 +++--- dbms/include/DB/Common/PODArray.h | 4 ++-- dbms/include/DB/Core/StringRef.h | 2 +- dbms/include/DB/Functions/FunctionsURL.h | 8 ++++---- dbms/include/DB/IO/RemoteWriteBuffer.h | 2 +- dbms/include/DB/IO/WriteHelpers.h | 2 +- dbms/include/DB/Interpreters/Users.h | 2 +- dbms/include/DB/Parsers/ASTAlterQuery.h | 2 +- dbms/include/DB/Parsers/IAST.h | 2 +- dbms/src/Common/VirtualColumnUtils.cpp | 4 ++-- dbms/src/Interpreters/InterpreterSelectQuery.cpp | 2 +- dbms/src/Interpreters/executeQuery.cpp | 4 ++-- dbms/src/Interpreters/tests/hash_map3.cpp | 6 +++--- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/dbms/include/DB/Common/HashTableAllocator.h b/dbms/include/DB/Common/HashTableAllocator.h index f6546368296..8a152a0f2ac 100644 --- a/dbms/include/DB/Common/HashTableAllocator.h +++ b/dbms/include/DB/Common/HashTableAllocator.h @@ -21,7 +21,7 @@ public: void * alloc(size_t size) { void * buf = ::calloc(size, 1); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot calloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); return buf; @@ -41,7 +41,7 @@ public: void * realloc(void * buf, size_t old_size, size_t new_size) { buf = ::realloc(buf, new_size); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot realloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); memset(reinterpret_cast(buf) + old_size, 0, new_size - old_size); @@ -82,7 +82,7 @@ public: return HashTableAllocator::realloc(buf, old_size, new_size); buf = ::malloc(new_size); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot malloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); memcpy(buf, stack_memory, old_size); diff --git a/dbms/include/DB/Common/PODArray.h b/dbms/include/DB/Common/PODArray.h index c03e8b8d99a..fc6844b29d6 100644 --- a/dbms/include/DB/Common/PODArray.h +++ b/dbms/include/DB/Common/PODArray.h @@ -121,14 +121,14 @@ private: { c_start = reinterpret_cast(::realloc(c_start, bytes_to_alloc)); - if (NULL == c_start) + if (nullptr == c_start) throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY); } else { c_start = reinterpret_cast(malloc(bytes_to_alloc)); - if (NULL == c_start) + if (nullptr == c_start) throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY); memcpy(c_start, old_c_start, old_c_end_of_storage - old_c_start); diff --git a/dbms/include/DB/Core/StringRef.h b/dbms/include/DB/Core/StringRef.h index 086547e5a96..91793b648aa 100644 --- a/dbms/include/DB/Core/StringRef.h +++ b/dbms/include/DB/Core/StringRef.h @@ -56,7 +56,7 @@ namespace DB struct StringRefZeroTraits { - static inline bool check(DB::StringRef x) { return NULL == x.data; } + static inline bool check(DB::StringRef x) { return nullptr == x.data; } static inline void set(DB::StringRef & x) { x.data = nullptr; } }; diff --git a/dbms/include/DB/Functions/FunctionsURL.h b/dbms/include/DB/Functions/FunctionsURL.h index f5559b4f005..9ea35c2d375 100644 --- a/dbms/include/DB/Functions/FunctionsURL.h +++ b/dbms/include/DB/Functions/FunctionsURL.h @@ -167,7 +167,7 @@ struct ExtractPath Pos pos = data; Pos end = pos + size; - if (NULL != (pos = strchr(data, '/')) && pos[1] == '/' && NULL != (pos = strchr(pos + 2, '/'))) + if (nullptr != (pos = strchr(data, '/')) && pos[1] == '/' && nullptr != (pos = strchr(pos + 2, '/'))) { Pos query_string_or_fragment = strpbrk(pos, "?#"); @@ -190,7 +190,7 @@ struct ExtractQueryString Pos pos = data; Pos end = pos + size; - if (NULL != (pos = strchr(data, '?'))) + if (nullptr != (pos = strchr(data, '?'))) { Pos fragment = strchr(pos, '#'); @@ -213,7 +213,7 @@ struct ExtractFragment Pos pos = data; Pos end = pos + size; - if (NULL != (pos = strchr(data, '#'))) + if (nullptr != (pos = strchr(data, '#'))) { res_data = pos + (without_leading_char ? 1 : 0); res_size = end - res_data; @@ -234,7 +234,7 @@ struct ExtractQueryStringAndFragment Pos pos = data; Pos end = pos + size; - if (NULL != (pos = strchr(data, '?'))) + if (nullptr != (pos = strchr(data, '?'))) { res_data = pos + (without_leading_char ? 1 : 0); res_size = end - res_data; diff --git a/dbms/include/DB/IO/RemoteWriteBuffer.h b/dbms/include/DB/IO/RemoteWriteBuffer.h index 8ee3cf9f2b3..81c63f62984 100644 --- a/dbms/include/DB/IO/RemoteWriteBuffer.h +++ b/dbms/include/DB/IO/RemoteWriteBuffer.h @@ -231,7 +231,7 @@ private: { /// Если в прошлую попытку от сервера не пришло ответа, но файл всё же был переименован. if (i != 0 && e.code() == ErrorCodes::RECEIVED_ERROR_FROM_REMOTE_IO_SERVER - && NULL != strstr(e.displayText().data(), "File not found")) + && nullptr != strstr(e.displayText().data(), "File not found")) { LOG_TRACE((&Logger::get("RemoteWriteBuffer")), "File already renamed"); } diff --git a/dbms/include/DB/IO/WriteHelpers.h b/dbms/include/DB/IO/WriteHelpers.h index 06bad60e829..c3f676fd7f6 100644 --- a/dbms/include/DB/IO/WriteHelpers.h +++ b/dbms/include/DB/IO/WriteHelpers.h @@ -253,7 +253,7 @@ void writeAnyEscapedString(const String & s, WriteBuffer & buf) inline void writeEscapedString(const String & s, WriteBuffer & buf) { /// strpbrk в libc под Linux на процессорах с SSE 4.2 хорошо оптимизирована (этот if ускоряет код в 1.5 раза) - if (NULL == strpbrk(s.data(), "\b\f\n\r\t\'\\") && strlen(s.data()) == s.size()) + if (nullptr == strpbrk(s.data(), "\b\f\n\r\t\'\\") && strlen(s.data()) == s.size()) writeString(s, buf); else writeAnyEscapedString<'\''>(s, buf); diff --git a/dbms/include/DB/Interpreters/Users.h b/dbms/include/DB/Interpreters/Users.h index b3ea308158c..ef9a0215cef 100644 --- a/dbms/include/DB/Interpreters/Users.h +++ b/dbms/include/DB/Interpreters/Users.h @@ -52,7 +52,7 @@ public: { const char * pos = strchr(str.c_str(), '/'); - if (NULL == pos) + if (nullptr == pos) { construct(Poco::Net::IPAddress(str)); } diff --git a/dbms/include/DB/Parsers/ASTAlterQuery.h b/dbms/include/DB/Parsers/ASTAlterQuery.h index 2e6e2c4f7e4..a39251bfb1f 100644 --- a/dbms/include/DB/Parsers/ASTAlterQuery.h +++ b/dbms/include/DB/Parsers/ASTAlterQuery.h @@ -54,7 +54,7 @@ public: String table; - ASTAlterQuery(StringRange range_ = StringRange(NULL, NULL)) : IAST(range_) {}; + ASTAlterQuery(StringRange range_ = StringRange()) : IAST(range_) {}; /** Получить текст, который идентифицирует этот элемент. */ String getID() const { return ("AlterQuery_" + database + "_" + table); }; diff --git a/dbms/include/DB/Parsers/IAST.h b/dbms/include/DB/Parsers/IAST.h index 5c813079726..056e63e7884 100644 --- a/dbms/include/DB/Parsers/IAST.h +++ b/dbms/include/DB/Parsers/IAST.h @@ -38,7 +38,7 @@ public: */ StringPtr query_string; - IAST() : range(NULL, NULL) {} + IAST() {} IAST(StringRange range_) : range(range_) {} virtual ~IAST() {} diff --git a/dbms/src/Common/VirtualColumnUtils.cpp b/dbms/src/Common/VirtualColumnUtils.cpp index e247a796ccd..0b96bbe74d2 100644 --- a/dbms/src/Common/VirtualColumnUtils.cpp +++ b/dbms/src/Common/VirtualColumnUtils.cpp @@ -71,7 +71,7 @@ void rewriteEntityInAst(ASTPtr ast, const String & column_name, const Field & va ASTSelectQuery & select = dynamic_cast(*ast); ASTExpressionList & node = dynamic_cast(*select.select_expression_list); ASTs & asts = node.children; - ASTLiteral * cur = new ASTLiteral(StringRange(NULL, NULL), value); + ASTLiteral * cur = new ASTLiteral(StringRange(), value); cur->alias = column_name; ASTPtr column_value = cur; bool is_replaced = false; @@ -161,7 +161,7 @@ BlockInputStreamPtr getVirtualColumnsBlocks(ASTPtr query, const Block & input, c new_select.select_expression_list = new ASTExpressionList(); ASTExpressionList & select_list = dynamic_cast(*new_select.select_expression_list); for (size_t i = 0; i < columns.size(); ++i) - select_list.children.push_back(new ASTIdentifier(StringRange(NULL, NULL), columns[i])); + select_list.children.push_back(new ASTIdentifier(StringRange(), columns[i])); std::vector functions; extractFunctions(select.where_expression, columns, functions); diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp index fcf9cc487a1..ad32d958ac9 100644 --- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp +++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp @@ -450,7 +450,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu /// если таблица не поддерживает сэмплинг получим исключение /// поэтому запросы типа SHOW TABLES работать с включенном default_sample не будут if (!query.sample_size && settings.default_sample != 1) - query.sample_size = new ASTLiteral(StringRange(NULL, NULL), Float64(settings.default_sample)); + query.sample_size = new ASTLiteral(StringRange(), Float64(settings.default_sample)); if (query.sample_size && (!storage || !storage->supportsSampling())) throw Exception("Illegal SAMPLE: table doesn't support sampling", ErrorCodes::SAMPLING_NOT_SUPPORTED); diff --git a/dbms/src/Interpreters/executeQuery.cpp b/dbms/src/Interpreters/executeQuery.cpp index 8a1a5a8733a..0e55090545f 100644 --- a/dbms/src/Interpreters/executeQuery.cpp +++ b/dbms/src/Interpreters/executeQuery.cpp @@ -88,7 +88,7 @@ void executeQuery( /// Положим запрос в список процессов. Но запрос SHOW PROCESSLIST класть не будем. ProcessList::EntryPtr process_list_entry; - if (!internal && NULL == dynamic_cast(&*ast)) + if (!internal && nullptr == dynamic_cast(&*ast)) { process_list_entry = context.getProcessList().insert( query, context.getUser(), context.getCurrentQueryId(), context.getIPAddress(), context.getSettingsRef().queue_max_wait_ms.totalMilliseconds()); @@ -159,7 +159,7 @@ BlockIO executeQuery( /// Положим запрос в список процессов. Но запрос SHOW PROCESSLIST класть не будем. ProcessList::EntryPtr process_list_entry; - if (!internal && NULL == dynamic_cast(&*ast)) + if (!internal && nullptr == dynamic_cast(&*ast)) { process_list_entry = context.getProcessList().insert( query, context.getUser(), context.getCurrentQueryId(), context.getIPAddress(), context.getSettingsRef().queue_max_wait_ms.totalMilliseconds(), context.getSettingsRef().replace_running_query); diff --git a/dbms/src/Interpreters/tests/hash_map3.cpp b/dbms/src/Interpreters/tests/hash_map3.cpp index dcddac39072..46b84b992f8 100644 --- a/dbms/src/Interpreters/tests/hash_map3.cpp +++ b/dbms/src/Interpreters/tests/hash_map3.cpp @@ -439,7 +439,7 @@ public: void * alloc(size_t size) { void * buf = ::calloc(size, 1); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot calloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); return buf; @@ -459,7 +459,7 @@ public: void * realloc(void * buf, size_t old_size, size_t new_size) { buf = ::realloc(buf, new_size); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot realloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); memset(reinterpret_cast(buf) + old_size, 0, new_size - old_size); @@ -501,7 +501,7 @@ public: return HashTableAllocator::realloc(buf, old_size, new_size); buf = ::malloc(new_size); - if (NULL == buf) + if (nullptr == buf) throwFromErrno("HashTableAllocator: Cannot malloc.", ErrorCodes::CANNOT_ALLOCATE_MEMORY); memcpy(buf, stack_memory, old_size); From 63cff6a5a4b3da34f1138a24f5b97a7f9d6d40fa Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Tue, 8 Apr 2014 11:58:53 +0400 Subject: [PATCH 189/281] Improvement [#METR-2807]. --- dbms/include/DB/Common/Arena.h | 2 +- dbms/include/DB/Functions/FunctionsConditional.h | 2 +- dbms/include/DB/Functions/FunctionsConversion.h | 2 +- dbms/include/DB/Functions/IFunction.h | 2 +- dbms/include/DB/IO/AsynchronousWriteBuffer.h | 2 +- dbms/include/DB/IO/BufferWithOwnMemory.h | 2 +- dbms/include/DB/IO/CachedCompressedReadBuffer.h | 2 +- dbms/include/DB/IO/ChunkedReadBuffer.h | 2 +- dbms/include/DB/IO/ConcatReadBuffer.h | 4 ++-- dbms/include/DB/IO/LimitReadBuffer.h | 2 +- dbms/include/DB/IO/ReadBufferFromHTTP.h | 2 +- dbms/include/DB/IO/RemoteWriteBuffer.h | 2 +- dbms/include/DB/Interpreters/Aggregator.h | 2 +- dbms/include/DB/Interpreters/Context.h | 6 +++--- dbms/include/DB/Interpreters/ExpressionActions.h | 2 +- dbms/include/DB/Interpreters/ExpressionAnalyzer.h | 2 +- dbms/include/DB/Interpreters/Users.h | 4 ++-- dbms/include/DB/Parsers/ASTOrderByElement.h | 2 +- dbms/include/DB/Storages/MergeTree/MergeTreeData.h | 2 +- dbms/include/DB/Storages/MergeTree/MergeTreeReader.h | 2 +- dbms/include/DB/Storages/StorageMergeTree.h | 4 ++-- dbms/include/DB/Storages/StorageReplicatedMergeTree.h | 2 +- dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp | 6 +++--- dbms/src/Client/Benchmark.cpp | 2 +- dbms/src/Client/InterruptListener.h | 6 +++--- dbms/src/Common/VirtualColumnUtils.cpp | 2 +- dbms/src/DataStreams/tests/union_stream2.cpp | 2 +- dbms/src/DataTypes/DataTypeFactory.cpp | 2 +- dbms/src/Interpreters/ExpressionActions.cpp | 4 ++-- dbms/src/Interpreters/ExpressionAnalyzer.cpp | 2 +- dbms/src/Parsers/ExpressionElementParsers.cpp | 2 +- dbms/src/Storages/MergeTree/MergeTreeData.cpp | 2 +- dbms/src/Storages/MergeTree/MergeTreeDataMerger.cpp | 2 +- dbms/src/Storages/MergeTree/PKCondition.cpp | 2 +- dbms/src/Storages/StorageMergeTree.cpp | 2 +- dbms/src/Storages/tests/merge_tree.cpp | 2 +- libs/libmysqlxx/include/mysqlxx/Connection.h | 2 +- libs/libmysqlxx/include/mysqlxx/Pool.h | 2 +- libs/libmysqlxx/include/mysqlxx/String.h | 2 +- libs/libmysqlxx/src/Connection.cpp | 4 ++-- utils/iotest/iotest_aio.cpp | 4 ++-- 41 files changed, 53 insertions(+), 53 deletions(-) diff --git a/dbms/include/DB/Common/Arena.h b/dbms/include/DB/Common/Arena.h index 9c7688a210b..42af26f3baa 100644 --- a/dbms/include/DB/Common/Arena.h +++ b/dbms/include/DB/Common/Arena.h @@ -92,7 +92,7 @@ private: public: Arena(size_t initial_size_ = 4096, size_t growth_factor_ = 2, size_t linear_growth_threshold_ = 128 * 1024 * 1024) : growth_factor(growth_factor_), linear_growth_threshold(linear_growth_threshold_), - head(new Chunk(initial_size_, NULL)), size_in_bytes(head->size()) + head(new Chunk(initial_size_, nullptr)), size_in_bytes(head->size()) { } diff --git a/dbms/include/DB/Functions/FunctionsConditional.h b/dbms/include/DB/Functions/FunctionsConditional.h index 323fbc8d263..988a460190d 100644 --- a/dbms/include/DB/Functions/FunctionsConditional.h +++ b/dbms/include/DB/Functions/FunctionsConditional.h @@ -289,7 +289,7 @@ struct DataTypeFromFieldTypeOrError { static DataTypePtr getDataType() { - return NULL; + return nullptr; } }; diff --git a/dbms/include/DB/Functions/FunctionsConversion.h b/dbms/include/DB/Functions/FunctionsConversion.h index 34a2388708f..028400d28ed 100644 --- a/dbms/include/DB/Functions/FunctionsConversion.h +++ b/dbms/include/DB/Functions/FunctionsConversion.h @@ -320,7 +320,7 @@ public: /** Получить тип результата по типам аргументов и значениям константных аргументов. * Если функция неприменима для данных аргументов - кинуть исключение. - * Для неконстантных столбцов arguments[i].column=NULL. + * Для неконстантных столбцов arguments[i].column = nullptr. */ void getReturnTypeAndPrerequisites(const ColumnsWithNameAndType & arguments, DataTypePtr & out_return_type, diff --git a/dbms/include/DB/Functions/IFunction.h b/dbms/include/DB/Functions/IFunction.h index 24231264823..383ece76e33 100644 --- a/dbms/include/DB/Functions/IFunction.h +++ b/dbms/include/DB/Functions/IFunction.h @@ -48,7 +48,7 @@ public: /** Получить тип результата по типам аргументов и значениям константных аргументов. * Если функция неприменима для данных аргументов - кинуть исключение. * Еще можно вернуть описание дополнительных столбцов, которые требуются для выполнения функции. - * Для неконстантных столбцов arguments[i].column=NULL. + * Для неконстантных столбцов arguments[i].column = nullptr. * Осмысленные типы элементов в out_prerequisites: APPLY_FUNCTION, ADD_COLUMN. */ virtual void getReturnTypeAndPrerequisites( diff --git a/dbms/include/DB/IO/AsynchronousWriteBuffer.h b/dbms/include/DB/IO/AsynchronousWriteBuffer.h index e4f9bc643be..138fbd62c2e 100644 --- a/dbms/include/DB/IO/AsynchronousWriteBuffer.h +++ b/dbms/include/DB/IO/AsynchronousWriteBuffer.h @@ -55,7 +55,7 @@ private: } public: - AsynchronousWriteBuffer(WriteBuffer & out_) : WriteBuffer(NULL, 0), out(out_), memory(out.buffer().size()), pool(1), started(false) + AsynchronousWriteBuffer(WriteBuffer & out_) : WriteBuffer(nullptr, 0), out(out_), memory(out.buffer().size()), pool(1), started(false) { /// Данные пишутся в дублирующий буфер. set(&memory[0], memory.size()); diff --git a/dbms/include/DB/IO/BufferWithOwnMemory.h b/dbms/include/DB/IO/BufferWithOwnMemory.h index 88a0896d4d9..3791463c4bb 100644 --- a/dbms/include/DB/IO/BufferWithOwnMemory.h +++ b/dbms/include/DB/IO/BufferWithOwnMemory.h @@ -121,7 +121,7 @@ protected: Memory memory; public: /// Если передать не-NULL existing_memory, то буфер не будет создавать свой кусок памяти, а будет использовать существующий (и не будет им владеть). - BufferWithOwnMemory(size_t size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = nullptr, size_t alignment = 0) : Base(NULL, 0), memory(existing_memory ? 0 : size, alignment) + BufferWithOwnMemory(size_t size = DBMS_DEFAULT_BUFFER_SIZE, char * existing_memory = nullptr, size_t alignment = 0) : Base(nullptr, 0), memory(existing_memory ? 0 : size, alignment) { Base::set(existing_memory ? existing_memory : &memory[0], size); } diff --git a/dbms/include/DB/IO/CachedCompressedReadBuffer.h b/dbms/include/DB/IO/CachedCompressedReadBuffer.h index 548a5e71a99..31b748ed2cc 100644 --- a/dbms/include/DB/IO/CachedCompressedReadBuffer.h +++ b/dbms/include/DB/IO/CachedCompressedReadBuffer.h @@ -80,7 +80,7 @@ private: public: CachedCompressedReadBuffer(const std::string & path_, UncompressedCache * cache_, size_t buf_size_ = DBMS_DEFAULT_BUFFER_SIZE) - : ReadBuffer(NULL, 0), path(path_), cache(cache_), buf_size(buf_size_), file_pos(0) + : ReadBuffer(nullptr, 0), path(path_), cache(cache_), buf_size(buf_size_), file_pos(0) { } diff --git a/dbms/include/DB/IO/ChunkedReadBuffer.h b/dbms/include/DB/IO/ChunkedReadBuffer.h index 6c57e7f8e7e..07fcd3b895e 100644 --- a/dbms/include/DB/IO/ChunkedReadBuffer.h +++ b/dbms/include/DB/IO/ChunkedReadBuffer.h @@ -72,7 +72,7 @@ protected: public: ChunkedReadBuffer(ReadBuffer & in_, UInt64 assert_query_id_) - : ReadBuffer(NULL, 0), in(in_), all_read(false), read_in_chunk(0), chunk_size(0), assert_query_id(assert_query_id_) {} + : ReadBuffer(nullptr, 0), in(in_), all_read(false), read_in_chunk(0), chunk_size(0), assert_query_id(assert_query_id_) {} }; diff --git a/dbms/include/DB/IO/ConcatReadBuffer.h b/dbms/include/DB/IO/ConcatReadBuffer.h index a0ef6bc9c98..7b2c1199a91 100644 --- a/dbms/include/DB/IO/ConcatReadBuffer.h +++ b/dbms/include/DB/IO/ConcatReadBuffer.h @@ -51,9 +51,9 @@ protected: } public: - ConcatReadBuffer(const ReadBuffers & buffers_) : ReadBuffer(NULL, 0), buffers(buffers_), current(buffers.begin()) {} + ConcatReadBuffer(const ReadBuffers & buffers_) : ReadBuffer(nullptr, 0), buffers(buffers_), current(buffers.begin()) {} - ConcatReadBuffer(ReadBuffer & buf1, ReadBuffer & buf2) : ReadBuffer(NULL, 0) + ConcatReadBuffer(ReadBuffer & buf1, ReadBuffer & buf2) : ReadBuffer(nullptr, 0) { buffers.push_back(&buf1); buffers.push_back(&buf2); diff --git a/dbms/include/DB/IO/LimitReadBuffer.h b/dbms/include/DB/IO/LimitReadBuffer.h index 97ef5ed0eb1..c0988cdd600 100644 --- a/dbms/include/DB/IO/LimitReadBuffer.h +++ b/dbms/include/DB/IO/LimitReadBuffer.h @@ -27,7 +27,7 @@ private: } public: - LimitReadBuffer(ReadBuffer & in_, size_t limit_) : ReadBuffer(NULL, 0), in(in_), limit(limit_) {} + LimitReadBuffer(ReadBuffer & in_, size_t limit_) : ReadBuffer(nullptr, 0), in(in_), limit(limit_) {} }; } diff --git a/dbms/include/DB/IO/ReadBufferFromHTTP.h b/dbms/include/DB/IO/ReadBufferFromHTTP.h index 9e23cffc413..17f07701eb2 100644 --- a/dbms/include/DB/IO/ReadBufferFromHTTP.h +++ b/dbms/include/DB/IO/ReadBufferFromHTTP.h @@ -38,7 +38,7 @@ public: const Params & params, size_t timeout_ = 0, size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE) - : ReadBuffer(NULL, 0), host(host_), port(port_) + : ReadBuffer(nullptr, 0), host(host_), port(port_) { std::stringstream uri; uri << "http://" << host << ":" << port << "/"; diff --git a/dbms/include/DB/IO/RemoteWriteBuffer.h b/dbms/include/DB/IO/RemoteWriteBuffer.h index 81c63f62984..e805fdb3358 100644 --- a/dbms/include/DB/IO/RemoteWriteBuffer.h +++ b/dbms/include/DB/IO/RemoteWriteBuffer.h @@ -51,7 +51,7 @@ public: const std::string & tmp_path_ = "", const std::string & if_exists_ = "remove", bool decompress_ = false, size_t timeout_ = 0, unsigned connection_retries_ = 3, size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE) - : WriteBuffer(NULL, 0), host(host_), port(port_), path(path_), + : WriteBuffer(nullptr, 0), host(host_), port(port_), path(path_), tmp_path(tmp_path_), if_exists(if_exists_), decompress(decompress_), connection_retries(connection_retries_), finalized(false) { diff --git a/dbms/include/DB/Interpreters/Aggregator.h b/dbms/include/DB/Interpreters/Aggregator.h index d8706785901..d4d50563909 100644 --- a/dbms/include/DB/Interpreters/Aggregator.h +++ b/dbms/include/DB/Interpreters/Aggregator.h @@ -60,7 +60,7 @@ struct AggregatedDataVariants : private boost::noncopyable * - если при агрегации, до вызова Aggregator::convertToBlock вылетело исключение, то состояния агрегатных функций всё-равно должны быть уничтожены, * иначе для сложных состояний (наприемер, AggregateFunctionUniq), будут утечки памяти; * - чтобы, в этом случае, уничтожить состояния, в деструкторе вызывается метод Aggregator::destroyAggregateStates, - * но только если переменная aggregator (см. ниже) не NULL; + * но только если переменная aggregator (см. ниже) не nullptr; * - то есть, пока вы не передали владение состояниями агрегатных функций в ColumnAggregateFunction, установите переменную aggregator, * чтобы при возникновении исключения, состояния были корректно уничтожены. * diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index aaf93dd5c67..0cbbeb556ef 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -180,8 +180,8 @@ private: String default_format; /// Формат, используемый, если сервер сам форматирует данные, и если в запросе не задан FORMAT. /// То есть, используется в HTTP-интерфейсе. Может быть не задан - тогда используется некоторый глобальный формат по-умолчанию. Tables external_tables; /// Временные таблицы. - Context * session_context = nullptr; /// Контекст сессии или NULL, если его нет. (Возможно, равен this.) - Context * global_context = nullptr; /// Глобальный контекст или NULL, если его нет. (Возможно, равен this.) + Context * session_context = nullptr; /// Контекст сессии или nullptr, если его нет. (Возможно, равен this.) + Context * global_context = nullptr; /// Глобальный контекст или nullptr, если его нет. (Возможно, равен this.) public: String getPath() const; @@ -293,7 +293,7 @@ public: * чтобы обновлять и контролировать информацию об общем количестве потраченных на запрос ресурсов. */ void setProcessListElement(ProcessList::Element * elem); - /// Может вернуть NULL, если запрос не был вставлен в ProcessList. + /// Может вернуть nullptr, если запрос не был вставлен в ProcessList. ProcessList::Element * getProcessListElement(); /// Список всех запросов. diff --git a/dbms/include/DB/Interpreters/ExpressionActions.h b/dbms/include/DB/Interpreters/ExpressionActions.h index c898be9be5e..bb31cec4bd9 100644 --- a/dbms/include/DB/Interpreters/ExpressionActions.h +++ b/dbms/include/DB/Interpreters/ExpressionActions.h @@ -139,7 +139,7 @@ public: { for (NamesAndTypesList::iterator it = input_columns.begin(); it != input_columns.end(); ++it) { - sample_block.insert(ColumnWithNameAndType(NULL, it->second, it->first)); + sample_block.insert(ColumnWithNameAndType(nullptr, it->second, it->first)); } } diff --git a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h index 9d4c2beeec8..de750b4658a 100644 --- a/dbms/include/DB/Interpreters/ExpressionAnalyzer.h +++ b/dbms/include/DB/Interpreters/ExpressionAnalyzer.h @@ -186,7 +186,7 @@ private: for (NamesAndTypesList::const_iterator it = input_columns.begin(); it != input_columns.end(); ++it) { - all_columns.push_back(ColumnWithNameAndType(NULL, it->second, it->first)); + all_columns.push_back(ColumnWithNameAndType(nullptr, it->second, it->first)); new_names.insert(it->first); stack.back().new_columns.insert(it->first); } diff --git a/dbms/include/DB/Interpreters/Users.h b/dbms/include/DB/Interpreters/Users.h index ef9a0215cef..f75f96be467 100644 --- a/dbms/include/DB/Interpreters/Users.h +++ b/dbms/include/DB/Interpreters/Users.h @@ -118,7 +118,7 @@ public: hints.ai_family = AF_UNSPEC; hints.ai_flags |= AI_V4MAPPED | AI_ALL; - int ret = getaddrinfo(host.c_str(), NULL, &hints, &ai); + int ret = getaddrinfo(host.c_str(), nullptr, &hints, &ai); if (0 != ret) throw Exception("Cannot getaddrinfo: " + std::string(gai_strerror(ret)), ErrorCodes::DNS_ERROR); @@ -175,7 +175,7 @@ public: /// Резолвим вручную, потому что в Poco нет такой функциональности. char domain[1024]; - int gai_errno = getnameinfo(sock_addr.addr(), sock_addr.length(), domain, sizeof(domain), NULL, 0, NI_NAMEREQD); + int gai_errno = getnameinfo(sock_addr.addr(), sock_addr.length(), domain, sizeof(domain), nullptr, 0, NI_NAMEREQD); if (0 != gai_errno) throw Exception("Cannot getnameinfo: " + std::string(gai_strerror(gai_errno)), ErrorCodes::DNS_ERROR); diff --git a/dbms/include/DB/Parsers/ASTOrderByElement.h b/dbms/include/DB/Parsers/ASTOrderByElement.h index c54c71a4d3d..8132ae015cd 100644 --- a/dbms/include/DB/Parsers/ASTOrderByElement.h +++ b/dbms/include/DB/Parsers/ASTOrderByElement.h @@ -17,7 +17,7 @@ public: int direction; /// 1, если ASC, -1, если DESC /** Collator для locale-specific сортировки строк. - * Если NULL, то производится сортировка по байтам. + * Если nullptr, то производится сортировка по байтам. */ Poco::SharedPtr collator; diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h index 938a199ded6..19e47dc9f13 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeData.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeData.h @@ -309,7 +309,7 @@ public: const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, - const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + const ASTPtr & sampling_expression_, /// nullptr, если семплирование не поддерживается. size_t index_granularity_, Mode mode_, const String & sign_column_, diff --git a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h index 041edc68989..d85a3366639 100644 --- a/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h +++ b/dbms/include/DB/Storages/MergeTree/MergeTreeReader.h @@ -58,7 +58,7 @@ public: bool has_missing_columns = false; /// Указатели на столбцы смещений, общие для столбцов из вложенных структур данных - /// Если append, все значения NULL, и offset_columns используется только для проверки, что столбец смещений уже прочитан. + /// Если append, все значения nullptr, и offset_columns используется только для проверки, что столбец смещений уже прочитан. OffsetColumns offset_columns; for (Names::const_iterator it = column_names.begin(); it != column_names.end(); ++it) diff --git a/dbms/include/DB/Storages/StorageMergeTree.h b/dbms/include/DB/Storages/StorageMergeTree.h index 46a9c129b60..a6c2fc27ec2 100644 --- a/dbms/include/DB/Storages/StorageMergeTree.h +++ b/dbms/include/DB/Storages/StorageMergeTree.h @@ -29,7 +29,7 @@ public: const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, - const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + const ASTPtr & sampling_expression_, /// nullptr, если семплирование не поддерживается. size_t index_granularity_, MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, const String & sign_column_ = "", @@ -143,7 +143,7 @@ private: const Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, - const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + const ASTPtr & sampling_expression_, /// nullptr, если семплирование не поддерживается. size_t index_granularity_, MergeTreeData::Mode mode_, const String & sign_column_, diff --git a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h index 9955e4f9af9..df33c746c38 100644 --- a/dbms/include/DB/Storages/StorageReplicatedMergeTree.h +++ b/dbms/include/DB/Storages/StorageReplicatedMergeTree.h @@ -28,7 +28,7 @@ public: Context & context_, ASTPtr & primary_expr_ast_, const String & date_column_name_, - const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается. + const ASTPtr & sampling_expression_, /// nullptr, если семплирование не поддерживается. size_t index_granularity_, MergeTreeData::Mode mode_ = MergeTreeData::Ordinary, const String & sign_column_ = "", diff --git a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp index c037a48a252..9d50eb3b097 100644 --- a/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp +++ b/dbms/src/AggregateFunctions/AggregateFunctionFactory.cpp @@ -46,7 +46,7 @@ static IAggregateFunction * createWithNumericType(const IDataType & argument_typ else if (dynamic_cast(&argument_type)) return new AggregateFunctionTemplate; else if (dynamic_cast(&argument_type)) return new AggregateFunctionTemplate; else - return NULL; + return nullptr; } template