2014-03-09 17:36:01 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <DB/Core/SortDescription.h>
|
|
|
|
|
#include <DB/Interpreters/Context.h>
|
|
|
|
|
#include <DB/Interpreters/ExpressionActions.h>
|
|
|
|
|
#include <DB/Storages/IStorage.h>
|
2014-05-26 10:41:40 +00:00
|
|
|
|
#include <DB/Storages/MergeTree/ActiveDataPartSet.h>
|
2015-07-16 21:03:53 +00:00
|
|
|
|
#include <DB/Storages/MergeTree/MergeTreeSettings.h>
|
2014-04-02 07:59:43 +00:00
|
|
|
|
#include <DB/IO/ReadBufferFromString.h>
|
2014-07-16 09:32:15 +00:00
|
|
|
|
#include <DB/IO/WriteBufferFromFile.h>
|
2015-04-16 06:12:35 +00:00
|
|
|
|
#include <DB/IO/ReadBufferFromFile.h>
|
2014-04-02 13:45:39 +00:00
|
|
|
|
#include <DB/Common/escapeForFileName.h>
|
2015-04-16 06:12:35 +00:00
|
|
|
|
#include <DB/Common/SipHash.h>
|
2014-07-28 10:36:11 +00:00
|
|
|
|
#include <DB/DataTypes/DataTypeString.h>
|
2014-07-30 12:10:34 +00:00
|
|
|
|
#include <DB/DataTypes/DataTypesNumberFixed.h>
|
2014-03-09 17:36:01 +00:00
|
|
|
|
#include <Poco/RWLock.h>
|
|
|
|
|
|
2014-04-02 13:45:39 +00:00
|
|
|
|
|
|
|
|
|
#define MERGE_TREE_MARK_SIZE (2 * sizeof(size_t))
|
|
|
|
|
|
|
|
|
|
|
2015-06-02 20:22:53 +00:00
|
|
|
|
struct SimpleIncrement;
|
|
|
|
|
|
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2016-01-11 21:46:36 +00:00
|
|
|
|
namespace ErrorCodes
|
|
|
|
|
{
|
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
|
extern const int NO_FILE_IN_DATA_PART;
|
|
|
|
|
extern const int EXPECTED_END_OF_FILE;
|
|
|
|
|
extern const int BAD_SIZE_OF_FILE_IN_DATA_PART;
|
|
|
|
|
extern const int FORMAT_VERSION_TOO_OLD;
|
|
|
|
|
extern const int INVALID_PARTITION_NAME;
|
|
|
|
|
extern const int UNKNOWN_FORMAT;
|
|
|
|
|
extern const int UNEXPECTED_FILE_IN_DATA_PART;
|
|
|
|
|
extern const int TOO_MUCH_PARTS;
|
|
|
|
|
extern const int NO_SUCH_DATA_PART;
|
|
|
|
|
extern const int DUPLICATE_DATA_PART;
|
|
|
|
|
extern const int DIRECTORY_ALREADY_EXISTS;
|
|
|
|
|
extern const int TOO_MANY_UNEXPECTED_DATA_PARTS;
|
|
|
|
|
extern const int NO_SUCH_COLUMN_IN_TABLE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
/** Структура данных для *MergeTree движков.
|
|
|
|
|
* Используется merge tree для инкрементальной сортировки данных.
|
|
|
|
|
* Таблица представлена набором сортированных кусков.
|
|
|
|
|
* При вставке, данные сортируются по указанному выражению (первичному ключу) и пишутся в новый кусок.
|
|
|
|
|
* Куски объединяются в фоне, согласно некоторой эвристике.
|
|
|
|
|
* Для каждого куска, создаётся индексный файл, содержащий значение первичного ключа для каждой n-ой строки.
|
|
|
|
|
* Таким образом, реализуется эффективная выборка по диапазону первичного ключа.
|
|
|
|
|
*
|
|
|
|
|
* Дополнительно:
|
|
|
|
|
*
|
|
|
|
|
* Указывается столбец, содержащий дату.
|
|
|
|
|
* Для каждого куска пишется минимальная и максимальная дата.
|
|
|
|
|
* (по сути - ещё один индекс)
|
|
|
|
|
*
|
|
|
|
|
* Данные разделяются по разным месяцам (пишутся в разные куски для разных месяцев).
|
|
|
|
|
* Куски для разных месяцев не объединяются - для простоты эксплуатации.
|
|
|
|
|
* (дают локальность обновлений, что удобно для синхронизации и бэкапа)
|
|
|
|
|
*
|
|
|
|
|
* Структура файлов:
|
|
|
|
|
* / min-date _ max-date _ min-id _ max-id _ level / - директория с куском.
|
|
|
|
|
* Внутри директории с куском:
|
2014-03-27 11:29:40 +00:00
|
|
|
|
* checksums.txt - список файлов с их размерами и контрольными суммами.
|
2014-07-09 13:39:19 +00:00
|
|
|
|
* columns.txt - список столбцов с их типами.
|
2014-03-09 17:36:01 +00:00
|
|
|
|
* primary.idx - индексный файл.
|
|
|
|
|
* Column.bin - данные столбца
|
|
|
|
|
* Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк.
|
|
|
|
|
*
|
|
|
|
|
* Имеется несколько режимов работы, определяющих, что делать при мердже:
|
|
|
|
|
* - Ordinary - ничего дополнительно не делать;
|
|
|
|
|
* - Collapsing - при склейке кусков "схлопывать"
|
|
|
|
|
* пары записей с разными значениями sign_column для одного значения первичного ключа.
|
|
|
|
|
* (см. CollapsingSortedBlockInputStream.h)
|
|
|
|
|
* - Summing - при склейке кусков, при совпадении PK суммировать все числовые столбцы, не входящие в PK.
|
2014-06-05 19:52:13 +00:00
|
|
|
|
* - Aggregating - при склейке кусков, при совпадении PK, делается слияние состояний столбцов-агрегатных функций.
|
2015-03-13 21:31:23 +00:00
|
|
|
|
* - Unsorted - при склейке кусков, данные не упорядочиваются, а всего лишь конкатенируются;
|
|
|
|
|
* - это позволяет читать данные ровно такими пачками, какими они были записаны.
|
2014-03-09 17:36:01 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
/** Этот класс хранит список кусков и параметры структуры данных.
|
|
|
|
|
* Для чтения и изменения данных используются отдельные классы:
|
2014-03-13 19:36:28 +00:00
|
|
|
|
* - MergeTreeDataSelectExecutor
|
2014-03-13 12:48:07 +00:00
|
|
|
|
* - MergeTreeDataWriter
|
|
|
|
|
* - MergeTreeDataMerger
|
2014-03-09 17:36:01 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
class MergeTreeData : public ITableDeclaration
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
2016-01-28 01:00:27 +00:00
|
|
|
|
friend class ReshardingWorker;
|
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
public:
|
2014-07-23 09:15:41 +00:00
|
|
|
|
/// Функция, которую можно вызвать, если есть подозрение, что данные куска испорчены.
|
|
|
|
|
typedef std::function<void (const String &)> BrokenPartCallback;
|
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
/// Описание куска с данными.
|
2014-05-26 10:41:40 +00:00
|
|
|
|
struct DataPart : public ActiveDataPartSet::Part
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
2014-03-27 11:29:40 +00:00
|
|
|
|
/** Контрольные суммы всех не временных файлов.
|
|
|
|
|
* Для сжатых файлов хранятся чексумма и размер разжатых данных, чтобы не зависеть от способа сжатия.
|
|
|
|
|
*/
|
|
|
|
|
struct Checksums
|
|
|
|
|
{
|
|
|
|
|
struct Checksum
|
|
|
|
|
{
|
2014-04-14 13:08:26 +00:00
|
|
|
|
size_t file_size;
|
|
|
|
|
uint128 file_hash;
|
|
|
|
|
|
|
|
|
|
bool is_compressed = false;
|
|
|
|
|
size_t uncompressed_size;
|
|
|
|
|
uint128 uncompressed_hash;
|
|
|
|
|
|
2014-06-30 10:02:25 +00:00
|
|
|
|
Checksum() {}
|
|
|
|
|
Checksum(size_t file_size_, uint128 file_hash_) : file_size(file_size_), file_hash(file_hash_) {}
|
2014-07-22 08:20:45 +00:00
|
|
|
|
Checksum(size_t file_size_, uint128 file_hash_, size_t uncompressed_size_, uint128 uncompressed_hash_)
|
|
|
|
|
: file_size(file_size_), file_hash(file_hash_), is_compressed(true),
|
|
|
|
|
uncompressed_size(uncompressed_size_), uncompressed_hash(uncompressed_hash_) {}
|
2014-06-30 10:02:25 +00:00
|
|
|
|
|
2014-04-14 13:08:26 +00:00
|
|
|
|
void checkEqual(const Checksum & rhs, bool have_uncompressed, const String & name) const;
|
|
|
|
|
void checkSize(const String & path) const;
|
2014-03-27 11:29:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef std::map<String, Checksum> FileChecksums;
|
2014-03-27 17:30:04 +00:00
|
|
|
|
FileChecksums files;
|
2014-03-27 11:29:40 +00:00
|
|
|
|
|
2014-06-30 10:02:25 +00:00
|
|
|
|
void addFile(const String & file_name, size_t file_size, uint128 file_hash)
|
|
|
|
|
{
|
|
|
|
|
files[file_name] = Checksum(file_size, file_hash);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-27 11:29:40 +00:00
|
|
|
|
/// Проверяет, что множество столбцов и их контрольные суммы совпадают. Если нет - бросает исключение.
|
2014-04-14 13:08:26 +00:00
|
|
|
|
/// Если have_uncompressed, для сжатых файлов сравнивает чексуммы разжатых данных. Иначе сравнивает только чексуммы файлов.
|
|
|
|
|
void checkEqual(const Checksums & rhs, bool have_uncompressed) const;
|
|
|
|
|
|
|
|
|
|
/// Проверяет, что в директории есть все нужные файлы правильных размеров. Не проверяет чексуммы.
|
|
|
|
|
void checkSizes(const String & path) const;
|
2014-03-27 11:29:40 +00:00
|
|
|
|
|
|
|
|
|
/// Сериализует и десериализует в человекочитаемом виде.
|
2014-12-17 18:37:23 +00:00
|
|
|
|
bool read(ReadBuffer & in); /// Возвращает false, если чексуммы в слишком старом формате.
|
|
|
|
|
bool read_v2(ReadBuffer & in);
|
|
|
|
|
bool read_v3(ReadBuffer & in);
|
|
|
|
|
bool read_v4(ReadBuffer & in);
|
|
|
|
|
void write(WriteBuffer & out) const;
|
2014-03-27 17:30:04 +00:00
|
|
|
|
|
|
|
|
|
bool empty() const
|
|
|
|
|
{
|
|
|
|
|
return files.empty();
|
|
|
|
|
}
|
2014-04-02 07:59:43 +00:00
|
|
|
|
|
2014-04-25 12:43:10 +00:00
|
|
|
|
/// Контрольная сумма от множества контрольных сумм .bin файлов.
|
2015-09-24 05:47:17 +00:00
|
|
|
|
void summaryDataChecksum(SipHash & hash) const
|
2014-04-25 12:43:10 +00:00
|
|
|
|
{
|
|
|
|
|
/// Пользуемся тем, что итерирование в детерминированном (лексикографическом) порядке.
|
|
|
|
|
for (const auto & it : files)
|
|
|
|
|
{
|
|
|
|
|
const String & name = it.first;
|
|
|
|
|
const Checksum & sum = it.second;
|
|
|
|
|
if (name.size() < strlen(".bin") || name.substr(name.size() - 4) != ".bin")
|
|
|
|
|
continue;
|
|
|
|
|
size_t len = name.size();
|
|
|
|
|
hash.update(reinterpret_cast<const char *>(&len), sizeof(len));
|
|
|
|
|
hash.update(name.data(), len);
|
|
|
|
|
hash.update(reinterpret_cast<const char *>(&sum.uncompressed_size), sizeof(sum.uncompressed_size));
|
|
|
|
|
hash.update(reinterpret_cast<const char *>(&sum.uncompressed_hash), sizeof(sum.uncompressed_hash));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 10:10:37 +00:00
|
|
|
|
String toString() const
|
|
|
|
|
{
|
|
|
|
|
String s;
|
|
|
|
|
{
|
|
|
|
|
WriteBufferFromString out(s);
|
2014-12-17 18:37:23 +00:00
|
|
|
|
write(out);
|
2014-04-02 10:10:37 +00:00
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 07:59:43 +00:00
|
|
|
|
static Checksums parse(const String & s)
|
|
|
|
|
{
|
|
|
|
|
ReadBufferFromString in(s);
|
|
|
|
|
Checksums res;
|
2014-12-17 18:37:23 +00:00
|
|
|
|
if (!res.read(in))
|
2014-04-18 09:55:21 +00:00
|
|
|
|
throw Exception("Checksums format is too old", ErrorCodes::FORMAT_VERSION_TOO_OLD);
|
2014-04-02 07:59:43 +00:00
|
|
|
|
assertEOF(in);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2014-03-27 11:29:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-11-30 03:23:36 +00:00
|
|
|
|
DataPart(MergeTreeData & storage_) : storage(storage_) {}
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
2015-09-18 12:40:09 +00:00
|
|
|
|
/// Returns the size of .bin file for column `name` if found, zero otherwise
|
|
|
|
|
std::size_t getColumnSize(const String & name) const
|
2015-09-16 14:33:58 +00:00
|
|
|
|
{
|
2015-09-18 12:40:09 +00:00
|
|
|
|
if (checksums.empty())
|
|
|
|
|
return {};
|
2015-09-16 14:33:58 +00:00
|
|
|
|
|
2015-09-18 12:40:09 +00:00
|
|
|
|
const auto & files = checksums.files;
|
|
|
|
|
const auto bin_file_name = escapeForFileName(name) + ".bin";
|
2015-09-16 14:33:58 +00:00
|
|
|
|
|
2015-09-18 12:40:09 +00:00
|
|
|
|
/// Probably a logic error, not sure if this can ever happen if checksums are not empty
|
|
|
|
|
if (0 == files.count(bin_file_name))
|
|
|
|
|
return {};
|
2015-09-16 14:33:58 +00:00
|
|
|
|
|
2015-09-18 12:40:09 +00:00
|
|
|
|
return files.find(bin_file_name)->second.file_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Returns the name of a column with minimum compressed size (as returned by getColumnSize()).
|
|
|
|
|
* If no checksums are present returns the name of the first physically existing column. */
|
|
|
|
|
String getMinimumSizeColumnName() const
|
|
|
|
|
{
|
|
|
|
|
const auto & columns = storage.getColumnsList();
|
|
|
|
|
const std::string * minimum_size_column = nullptr;
|
|
|
|
|
auto minimum_size = std::numeric_limits<std::size_t>::max();
|
2015-09-16 14:33:58 +00:00
|
|
|
|
|
|
|
|
|
for (const auto & column : columns)
|
|
|
|
|
{
|
|
|
|
|
if (!hasColumnFiles(column.name))
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-09-18 12:40:09 +00:00
|
|
|
|
const auto size = getColumnSize(column.name);
|
2015-09-16 14:33:58 +00:00
|
|
|
|
if (size < minimum_size)
|
|
|
|
|
{
|
|
|
|
|
minimum_size = size;
|
|
|
|
|
minimum_size_column = &column.name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!minimum_size_column)
|
|
|
|
|
throw Exception{
|
|
|
|
|
"Could not find a column of minimum size in MergeTree",
|
|
|
|
|
ErrorCodes::LOGICAL_ERROR
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return *minimum_size_column;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 17:19:38 +00:00
|
|
|
|
MergeTreeData & storage;
|
2014-05-26 10:41:40 +00:00
|
|
|
|
|
2014-11-30 03:23:36 +00:00
|
|
|
|
size_t size = 0; /// в количестве засечек.
|
|
|
|
|
volatile size_t size_in_bytes = 0; /// размер в байтах, 0 - если не посчитано;
|
|
|
|
|
/// используется из нескольких потоков без блокировок (изменяется при ALTER).
|
|
|
|
|
time_t modification_time = 0;
|
2014-07-23 12:44:23 +00:00
|
|
|
|
mutable time_t remove_time = std::numeric_limits<time_t>::max(); /// Когда кусок убрали из рабочего набора.
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-07-17 10:44:17 +00:00
|
|
|
|
/// Если true, деструктор удалит директорию с куском.
|
|
|
|
|
bool is_temp = false;
|
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
/// Для перешардирования.
|
|
|
|
|
bool is_sharded = false;
|
|
|
|
|
size_t shard_no = 0;
|
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
/// Первичный ключ. Всегда загружается в оперативку.
|
|
|
|
|
typedef std::vector<Field> Index;
|
|
|
|
|
Index index;
|
|
|
|
|
|
2014-07-23 23:57:58 +00:00
|
|
|
|
/// NOTE Засечки кэшируются в оперативке. См. MarkCache.h.
|
|
|
|
|
|
2014-03-27 11:29:40 +00:00
|
|
|
|
Checksums checksums;
|
|
|
|
|
|
2014-07-09 13:39:19 +00:00
|
|
|
|
/// Описание столбцов.
|
|
|
|
|
NamesAndTypesList columns;
|
2014-07-14 14:07:47 +00:00
|
|
|
|
|
2015-04-08 16:48:47 +00:00
|
|
|
|
using ColumnToSize = std::map<std::string, size_t>;
|
|
|
|
|
|
2014-07-14 14:07:47 +00:00
|
|
|
|
/** Блокируется на запись при изменении columns, checksums или любых файлов куска.
|
|
|
|
|
* Блокируется на чтение при чтении columns, checksums или любых файлов куска.
|
|
|
|
|
*/
|
2014-07-14 15:49:03 +00:00
|
|
|
|
mutable Poco::RWLock columns_lock;
|
2014-07-09 13:39:19 +00:00
|
|
|
|
|
2014-07-14 14:07:47 +00:00
|
|
|
|
/** Берется на все время ALTER куска: от начала записи временных фалов до их переименования в постоянные.
|
|
|
|
|
* Берется при разлоченном columns_lock.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: "Можно" было бы обойтись без этого мьютекса, если бы можно было превращать ReadRWLock в WriteRWLock, не снимая блокировку.
|
|
|
|
|
* Такое превращение невозможно, потому что создало бы дедлок, если делать его из двух потоков сразу.
|
|
|
|
|
* Взятие этого мьютекса означает, что мы хотим заблокировать columns_lock на чтение с намерением потом, не
|
|
|
|
|
* снимая блокировку, заблокировать его на запись.
|
|
|
|
|
*/
|
2016-01-29 02:22:43 +00:00
|
|
|
|
mutable std::mutex alter_mutex;
|
2014-07-14 14:07:47 +00:00
|
|
|
|
|
2014-07-17 10:44:17 +00:00
|
|
|
|
~DataPart()
|
|
|
|
|
{
|
|
|
|
|
if (is_temp)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2016-01-28 01:00:27 +00:00
|
|
|
|
std::string path = storage.full_path + (is_sharded ? ("reshard/" + toString(shard_no) + "/") : "") + name;
|
|
|
|
|
|
|
|
|
|
Poco::File dir(path);
|
2014-07-17 10:44:17 +00:00
|
|
|
|
if (!dir.exists())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (name.substr(0, strlen("tmp")) != "tmp")
|
|
|
|
|
{
|
2016-01-28 01:00:27 +00:00
|
|
|
|
LOG_ERROR(storage.log, "~DataPart() should remove part " << path
|
2014-07-17 10:44:17 +00:00
|
|
|
|
<< " but its name doesn't start with tmp. Too suspicious, keeping the part.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir.remove(true);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
tryLogCurrentException(__PRETTY_FUNCTION__);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-17 05:35:53 +00:00
|
|
|
|
/// Вычисляем суммарный размер всей директории со всеми файлами
|
2014-09-29 20:26:46 +00:00
|
|
|
|
static size_t calcTotalSize(const String & from)
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
|
|
|
|
Poco::File cur(from);
|
|
|
|
|
if (cur.isFile())
|
|
|
|
|
return cur.getSize();
|
|
|
|
|
std::vector<std::string> files;
|
|
|
|
|
cur.list(files);
|
|
|
|
|
size_t res = 0;
|
|
|
|
|
for (size_t i = 0; i < files.size(); ++i)
|
|
|
|
|
res += calcTotalSize(from + files[i]);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-09 15:52:47 +00:00
|
|
|
|
void remove() const
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
|
|
|
|
String from = storage.full_path + name + "/";
|
|
|
|
|
String to = storage.full_path + "tmp2_" + name + "/";
|
|
|
|
|
|
2014-12-24 20:02:04 +00:00
|
|
|
|
Poco::File from_dir{from};
|
|
|
|
|
Poco::File to_dir{to};
|
|
|
|
|
|
|
|
|
|
if (to_dir.exists())
|
|
|
|
|
{
|
|
|
|
|
LOG_WARNING(storage.log, "Directory " << to << " (to which part must be renamed before removing) already exists."
|
|
|
|
|
" Most likely this is due to unclean restart. Removing it.");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
to_dir.remove(true);
|
|
|
|
|
}
|
|
|
|
|
catch (...)
|
|
|
|
|
{
|
|
|
|
|
LOG_ERROR(storage.log, "Cannot remove directory " << to << ". Check owner and access rights.");
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
from_dir.renameTo(to);
|
|
|
|
|
}
|
|
|
|
|
catch (const Poco::FileNotFoundException & e)
|
|
|
|
|
{
|
|
|
|
|
/// Если директория уже удалена. Такое возможно лишь при ручном вмешательстве.
|
|
|
|
|
LOG_WARNING(storage.log, "Directory " << from << " (part to remove) doesn't exist or one of nested files has gone."
|
|
|
|
|
" Most likely this is due to manual removing. This should be discouraged. Ignoring.");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to_dir.remove(true);
|
2014-03-09 17:36:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
void renameTo(const String & new_name) const
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
|
|
|
|
String from = storage.full_path + name + "/";
|
2014-08-08 08:28:13 +00:00
|
|
|
|
String to = storage.full_path + new_name + "/";
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
|
|
|
|
Poco::File f(from);
|
|
|
|
|
f.setLastModified(Poco::Timestamp::fromEpochTime(time(0)));
|
|
|
|
|
f.renameTo(to);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-24 20:02:04 +00:00
|
|
|
|
/// Переименовывает кусок, дописав к имени префикс. to_detached - также перенести в директорию detached.
|
|
|
|
|
void renameAddPrefix(bool to_detached, const String & prefix) const
|
2014-08-08 08:28:13 +00:00
|
|
|
|
{
|
2014-12-24 20:02:04 +00:00
|
|
|
|
unsigned try_no = 0;
|
|
|
|
|
auto dst_name = [&, this] { return (to_detached ? "detached/" : "") + prefix + name + (try_no ? "_try" + toString(try_no) : ""); };
|
|
|
|
|
|
|
|
|
|
if (to_detached)
|
|
|
|
|
{
|
|
|
|
|
/** Если нужно отцепить кусок, и директория, в которую мы хотим его переименовать, уже существует,
|
|
|
|
|
* то будем переименовывать в директорию с именем, в которое добавлен суффикс в виде "_tryN".
|
|
|
|
|
* Это делается только в случае to_detached, потому что считается, что в этом случае, точное имя не имеет значения.
|
|
|
|
|
* Больше 10 попыток не делается, чтобы не оставалось слишком много мусорных директорий.
|
|
|
|
|
*/
|
2015-09-16 19:25:49 +00:00
|
|
|
|
while (try_no < 10 && Poco::File(storage.full_path + dst_name()).exists())
|
2014-12-24 20:02:04 +00:00
|
|
|
|
{
|
|
|
|
|
LOG_WARNING(storage.log, "Directory " << dst_name() << " (to detach to) is already exist."
|
|
|
|
|
" Will detach to directory with '_tryN' suffix.");
|
|
|
|
|
++try_no;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renameTo(dst_name());
|
2014-08-08 08:28:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 13:45:39 +00:00
|
|
|
|
/// Загрузить индекс и вычислить размер. Если size=0, вычислить его тоже.
|
2014-03-09 17:36:01 +00:00
|
|
|
|
void loadIndex()
|
|
|
|
|
{
|
2014-04-02 13:45:39 +00:00
|
|
|
|
/// Размер - в количестве засечек.
|
|
|
|
|
if (!size)
|
2015-02-03 17:39:24 +00:00
|
|
|
|
{
|
|
|
|
|
if (columns.empty())
|
|
|
|
|
throw Exception("No columns in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
|
|
|
|
|
|
2014-07-09 13:39:19 +00:00
|
|
|
|
size = Poco::File(storage.full_path + name + "/" + escapeForFileName(columns.front().name) + ".mrk")
|
2014-04-02 13:45:39 +00:00
|
|
|
|
.getSize() / MERGE_TREE_MARK_SIZE;
|
2015-02-03 17:39:24 +00:00
|
|
|
|
}
|
2014-04-02 13:45:39 +00:00
|
|
|
|
|
2014-03-09 17:36:01 +00:00
|
|
|
|
size_t key_size = storage.sort_descr.size();
|
|
|
|
|
|
2015-03-13 21:31:23 +00:00
|
|
|
|
if (key_size)
|
|
|
|
|
{
|
|
|
|
|
index.resize(key_size * size);
|
|
|
|
|
|
|
|
|
|
String index_path = storage.full_path + name + "/primary.idx";
|
|
|
|
|
ReadBufferFromFile index_file(index_path,
|
|
|
|
|
std::min(static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize()));
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2015-03-13 21:31:23 +00:00
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
for (size_t j = 0; j < key_size; ++j)
|
2015-11-29 08:06:29 +00:00
|
|
|
|
storage.primary_key_data_types[j].get()->deserializeBinary(index[i * key_size + j], index_file);
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2015-03-13 21:31:23 +00:00
|
|
|
|
if (!index_file.eof())
|
|
|
|
|
throw Exception("index file " + index_path + " is unexpectedly long", ErrorCodes::EXPECTED_END_OF_FILE);
|
|
|
|
|
}
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
|
|
|
|
size_in_bytes = calcTotalSize(storage.full_path + name + "/");
|
|
|
|
|
}
|
2014-03-27 11:29:40 +00:00
|
|
|
|
|
|
|
|
|
/// Прочитать контрольные суммы, если есть.
|
2014-08-08 08:28:13 +00:00
|
|
|
|
void loadChecksums(bool require)
|
2014-03-27 11:29:40 +00:00
|
|
|
|
{
|
|
|
|
|
String path = storage.full_path + name + "/checksums.txt";
|
|
|
|
|
if (!Poco::File(path).exists())
|
2014-07-09 13:39:19 +00:00
|
|
|
|
{
|
2014-08-08 08:28:13 +00:00
|
|
|
|
if (require)
|
2014-07-09 13:39:19 +00:00
|
|
|
|
throw Exception("No checksums.txt in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-03-27 11:29:40 +00:00
|
|
|
|
ReadBufferFromFile file(path, std::min(static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(path).getSize()));
|
2014-12-17 18:37:23 +00:00
|
|
|
|
if (checksums.read(file))
|
2014-04-18 10:37:26 +00:00
|
|
|
|
assertEOF(file);
|
2014-07-09 13:39:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-04-08 16:48:47 +00:00
|
|
|
|
void accumulateColumnSizes(ColumnToSize & column_to_size) const
|
|
|
|
|
{
|
|
|
|
|
Poco::ScopedReadRWLock part_lock(columns_lock);
|
|
|
|
|
for (const NameAndTypePair & column : *storage.columns)
|
|
|
|
|
if (Poco::File(storage.full_path + name + "/" + escapeForFileName(column.name) + ".bin").exists())
|
|
|
|
|
column_to_size[column.name] += Poco::File(storage.full_path + name + "/" + escapeForFileName(column.name) + ".bin").getSize();
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
void loadColumns(bool require)
|
2014-07-09 13:39:19 +00:00
|
|
|
|
{
|
|
|
|
|
String path = storage.full_path + name + "/columns.txt";
|
|
|
|
|
if (!Poco::File(path).exists())
|
|
|
|
|
{
|
2014-08-08 08:28:13 +00:00
|
|
|
|
if (require)
|
2014-07-09 13:39:19 +00:00
|
|
|
|
throw Exception("No columns.txt in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
|
2014-07-16 09:32:15 +00:00
|
|
|
|
|
|
|
|
|
/// Если нет файла со списком столбцов, запишем его.
|
2014-08-08 08:28:13 +00:00
|
|
|
|
for (const NameAndTypePair & column : *storage.columns)
|
|
|
|
|
{
|
|
|
|
|
if (Poco::File(storage.full_path + name + "/" + escapeForFileName(column.name) + ".bin").exists())
|
|
|
|
|
columns.push_back(column);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 17:39:24 +00:00
|
|
|
|
if (columns.empty())
|
|
|
|
|
throw Exception("No columns in part " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
|
|
|
|
|
|
2014-07-16 09:32:15 +00:00
|
|
|
|
{
|
|
|
|
|
WriteBufferFromFile out(path + ".tmp", 4096);
|
|
|
|
|
columns.writeText(out);
|
|
|
|
|
}
|
|
|
|
|
Poco::File(path + ".tmp").renameTo(path);
|
|
|
|
|
|
2014-07-09 13:39:19 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadBufferFromFile file(path, std::min(static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(path).getSize()));
|
2015-05-28 03:49:28 +00:00
|
|
|
|
columns.readText(file);
|
2014-03-27 11:29:40 +00:00
|
|
|
|
}
|
2014-04-18 11:05:30 +00:00
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
void checkNotBroken(bool require_part_metadata)
|
2014-04-18 11:05:30 +00:00
|
|
|
|
{
|
|
|
|
|
String path = storage.full_path + name;
|
|
|
|
|
|
|
|
|
|
if (!checksums.empty())
|
|
|
|
|
{
|
2015-03-13 21:31:23 +00:00
|
|
|
|
if (!storage.sort_descr.empty() && !checksums.files.count("primary.idx"))
|
2014-07-09 13:39:19 +00:00
|
|
|
|
throw Exception("No checksum for primary.idx", ErrorCodes::NO_FILE_IN_DATA_PART);
|
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
if (require_part_metadata)
|
2014-07-09 13:39:19 +00:00
|
|
|
|
{
|
2014-07-14 15:49:03 +00:00
|
|
|
|
for (const NameAndTypePair & it : columns)
|
|
|
|
|
{
|
|
|
|
|
String name = escapeForFileName(it.name);
|
|
|
|
|
if (!checksums.files.count(name + ".mrk") ||
|
|
|
|
|
!checksums.files.count(name + ".bin"))
|
|
|
|
|
throw Exception("No .mrk or .bin file checksum for column " + name, ErrorCodes::NO_FILE_IN_DATA_PART);
|
|
|
|
|
}
|
2014-07-09 13:39:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-04-18 11:05:30 +00:00
|
|
|
|
checksums.checkSizes(path + "/");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-13 21:31:23 +00:00
|
|
|
|
if (!storage.sort_descr.empty())
|
|
|
|
|
{
|
|
|
|
|
/// Проверяем, что первичный ключ непуст.
|
|
|
|
|
Poco::File index_file(path + "/primary.idx");
|
2014-04-18 11:05:30 +00:00
|
|
|
|
|
2015-03-13 21:31:23 +00:00
|
|
|
|
if (!index_file.exists() || index_file.getSize() == 0)
|
|
|
|
|
throw Exception("Part " + path + " is broken: primary key is empty.", ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
|
|
|
|
|
}
|
2014-04-18 11:05:30 +00:00
|
|
|
|
|
|
|
|
|
/// Проверяем, что все засечки непусты и имеют одинаковый размер.
|
|
|
|
|
|
|
|
|
|
ssize_t marks_size = -1;
|
2014-07-09 13:39:19 +00:00
|
|
|
|
for (const NameAndTypePair & it : columns)
|
2014-04-18 11:05:30 +00:00
|
|
|
|
{
|
2014-07-09 13:39:19 +00:00
|
|
|
|
Poco::File marks_file(path + "/" + escapeForFileName(it.name) + ".mrk");
|
2014-04-18 11:05:30 +00:00
|
|
|
|
|
|
|
|
|
/// При добавлении нового столбца в таблицу файлы .mrk не создаются. Не будем ничего удалять.
|
|
|
|
|
if (!marks_file.exists())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (marks_size == -1)
|
|
|
|
|
{
|
|
|
|
|
marks_size = marks_file.getSize();
|
|
|
|
|
|
|
|
|
|
if (0 == marks_size)
|
|
|
|
|
throw Exception("Part " + path + " is broken: " + marks_file.path() + " is empty.",
|
|
|
|
|
ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (static_cast<ssize_t>(marks_file.getSize()) != marks_size)
|
|
|
|
|
throw Exception("Part " + path + " is broken: marks have different sizes.",
|
|
|
|
|
ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-11 12:47:45 +00:00
|
|
|
|
|
2014-07-14 09:12:50 +00:00
|
|
|
|
bool hasColumnFiles(const String & column) const
|
2014-07-11 12:47:45 +00:00
|
|
|
|
{
|
2016-01-28 01:00:27 +00:00
|
|
|
|
String prefix = storage.full_path + (is_sharded ? ("reshard/" + toString(shard_no) + "/") : "") + name + "/";
|
2014-07-14 11:45:34 +00:00
|
|
|
|
String escaped_column = escapeForFileName(column);
|
2016-01-28 01:00:27 +00:00
|
|
|
|
return Poco::File(prefix + escaped_column + ".bin").exists() &&
|
|
|
|
|
Poco::File(prefix + escaped_column + ".mrk").exists();
|
2014-07-11 12:47:45 +00:00
|
|
|
|
}
|
2014-03-09 17:36:01 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-03-14 17:19:38 +00:00
|
|
|
|
typedef std::shared_ptr<DataPart> MutableDataPartPtr;
|
|
|
|
|
/// После добавление в рабочее множество DataPart нельзя изменять.
|
|
|
|
|
typedef std::shared_ptr<const DataPart> DataPartPtr;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } };
|
|
|
|
|
typedef std::set<DataPartPtr, DataPartPtrLess> DataParts;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
typedef std::vector<DataPartPtr> DataPartsVector;
|
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
/// Для перешардирования.
|
|
|
|
|
using MutableDataParts = std::set<MutableDataPartPtr, DataPartPtrLess>;
|
2016-01-28 16:06:57 +00:00
|
|
|
|
using PerShardDataParts = std::unordered_map<size_t, MutableDataPartPtr>;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-07-01 15:58:25 +00:00
|
|
|
|
/// Некоторые операции над множеством кусков могут возвращать такой объект.
|
2015-09-09 19:03:46 +00:00
|
|
|
|
/// Если не был вызван commit или rollback, деструктор откатывает операцию.
|
2014-07-01 15:58:25 +00:00
|
|
|
|
class Transaction : private boost::noncopyable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Transaction() {}
|
|
|
|
|
|
|
|
|
|
void commit()
|
|
|
|
|
{
|
2015-09-09 19:03:46 +00:00
|
|
|
|
clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rollback()
|
|
|
|
|
{
|
|
|
|
|
if (data && (!parts_to_remove_on_rollback.empty() || !parts_to_add_on_rollback.empty()))
|
|
|
|
|
{
|
|
|
|
|
LOG_DEBUG(data->log, "Undoing transaction");
|
|
|
|
|
data->replaceParts(parts_to_remove_on_rollback, parts_to_add_on_rollback, true);
|
|
|
|
|
|
|
|
|
|
clear();
|
|
|
|
|
}
|
2014-07-01 15:58:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~Transaction()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2015-09-09 19:03:46 +00:00
|
|
|
|
rollback();
|
2014-07-01 15:58:25 +00:00
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{
|
|
|
|
|
tryLogCurrentException("~MergeTreeData::Transaction");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
friend class MergeTreeData;
|
|
|
|
|
|
|
|
|
|
MergeTreeData * data = nullptr;
|
2014-09-30 20:26:10 +00:00
|
|
|
|
|
|
|
|
|
/// Что делать для отката операции.
|
2015-09-09 18:15:36 +00:00
|
|
|
|
DataPartsVector parts_to_remove_on_rollback;
|
|
|
|
|
DataPartsVector parts_to_add_on_rollback;
|
2015-09-09 19:03:46 +00:00
|
|
|
|
|
|
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
data = nullptr;
|
|
|
|
|
parts_to_remove_on_rollback.clear();
|
|
|
|
|
parts_to_add_on_rollback.clear();
|
|
|
|
|
}
|
2014-07-01 15:58:25 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-07-11 12:47:45 +00:00
|
|
|
|
/// Объект, помнящий какие временные файлы были созданы в директории с куском в ходе изменения (ALTER) его столбцов.
|
|
|
|
|
class AlterDataPartTransaction : private boost::noncopyable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/// Переименовывает временные файлы, завершая ALTER куска.
|
|
|
|
|
void commit();
|
|
|
|
|
|
|
|
|
|
/// Если не был вызван commit(), удаляет временные файлы, отменяя ALTER куска.
|
|
|
|
|
~AlterDataPartTransaction();
|
|
|
|
|
|
2015-02-01 03:49:36 +00:00
|
|
|
|
/// Посмотреть изменения перед коммитом.
|
|
|
|
|
const NamesAndTypesList & getNewColumns() const { return new_columns; }
|
|
|
|
|
const DataPart::Checksums & getNewChecksums() const { return new_checksums; }
|
|
|
|
|
|
2014-07-11 12:47:45 +00:00
|
|
|
|
private:
|
|
|
|
|
friend class MergeTreeData;
|
|
|
|
|
|
2014-07-14 14:07:47 +00:00
|
|
|
|
AlterDataPartTransaction(DataPartPtr data_part_) : data_part(data_part_), alter_lock(data_part->alter_mutex) {}
|
|
|
|
|
|
|
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
alter_lock.unlock();
|
|
|
|
|
data_part = nullptr;
|
|
|
|
|
}
|
2014-07-11 12:47:45 +00:00
|
|
|
|
|
|
|
|
|
DataPartPtr data_part;
|
2016-01-29 02:22:43 +00:00
|
|
|
|
std::unique_lock<std::mutex> alter_lock;
|
2014-07-14 14:07:47 +00:00
|
|
|
|
|
|
|
|
|
DataPart::Checksums new_checksums;
|
|
|
|
|
NamesAndTypesList new_columns;
|
2014-07-11 12:47:45 +00:00
|
|
|
|
/// Если значение - пустая строка, файл нужно удалить, и он не временный.
|
|
|
|
|
NameToNameMap rename_map;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef std::unique_ptr<AlterDataPartTransaction> AlterDataPartTransactionPtr;
|
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
/// Режим работы. См. выше.
|
|
|
|
|
enum Mode
|
2014-03-09 17:36:01 +00:00
|
|
|
|
{
|
2014-03-13 12:48:07 +00:00
|
|
|
|
Ordinary,
|
|
|
|
|
Collapsing,
|
|
|
|
|
Summing,
|
2014-05-26 16:11:20 +00:00
|
|
|
|
Aggregating,
|
2015-03-13 21:31:23 +00:00
|
|
|
|
Unsorted,
|
2014-03-13 12:48:07 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-07-23 09:15:41 +00:00
|
|
|
|
static void doNothing(const String & name) {}
|
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
/** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце),
|
|
|
|
|
* (корректность имён и путей не проверяется)
|
|
|
|
|
* состоящую из указанных столбцов.
|
|
|
|
|
*
|
2015-03-13 21:31:23 +00:00
|
|
|
|
* primary_expr_ast - выражение для сортировки; Пустое для UnsortedMergeTree.
|
2014-03-13 12:48:07 +00:00
|
|
|
|
* date_column_name - имя столбца с датой;
|
|
|
|
|
* index_granularity - на сколько строчек пишется одно значение индекса.
|
2014-07-09 13:39:19 +00:00
|
|
|
|
* require_part_metadata - обязательно ли в директории с куском должны быть checksums.txt и columns.txt
|
2014-03-13 12:48:07 +00:00
|
|
|
|
*/
|
|
|
|
|
MergeTreeData( const String & full_path_, NamesAndTypesListPtr columns_,
|
2014-10-03 15:30:10 +00:00
|
|
|
|
const NamesAndTypesList & materialized_columns_,
|
2014-09-30 03:08:47 +00:00
|
|
|
|
const NamesAndTypesList & alias_columns_,
|
|
|
|
|
const ColumnDefaults & column_defaults_,
|
2016-01-28 01:00:27 +00:00
|
|
|
|
Context & context_,
|
2014-03-13 12:48:07 +00:00
|
|
|
|
ASTPtr & primary_expr_ast_,
|
|
|
|
|
const String & date_column_name_,
|
2014-04-08 07:58:53 +00:00
|
|
|
|
const ASTPtr & sampling_expression_, /// nullptr, если семплирование не поддерживается.
|
2014-03-13 12:48:07 +00:00
|
|
|
|
size_t index_granularity_,
|
|
|
|
|
Mode mode_,
|
2014-11-22 02:22:30 +00:00
|
|
|
|
const String & sign_column_, /// Для Collapsing режима.
|
|
|
|
|
const Names & columns_to_sum_, /// Для Summing режима. Если пустое - то выбирается автоматически.
|
2014-05-08 07:12:01 +00:00
|
|
|
|
const MergeTreeSettings & settings_,
|
2014-07-09 13:39:19 +00:00
|
|
|
|
const String & log_name_,
|
2014-07-23 09:15:41 +00:00
|
|
|
|
bool require_part_metadata_,
|
|
|
|
|
BrokenPartCallback broken_part_callback_ = &MergeTreeData::doNothing);
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-08-13 08:07:52 +00:00
|
|
|
|
/// Загрузить множество кусков с данными с диска. Вызывается один раз - сразу после создания объекта.
|
|
|
|
|
void loadDataParts(bool skip_sanity_checks);
|
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
std::string getModePrefix() const;
|
|
|
|
|
|
|
|
|
|
bool supportsSampling() const { return !!sampling_expression; }
|
|
|
|
|
bool supportsPrewhere() const { return true; }
|
|
|
|
|
|
2015-07-08 04:38:46 +00:00
|
|
|
|
bool supportsFinal() const
|
|
|
|
|
{
|
|
|
|
|
return mode == Mode::Collapsing
|
|
|
|
|
|| mode == Mode::Summing
|
|
|
|
|
|| mode == Mode::Aggregating;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-17 21:09:36 +00:00
|
|
|
|
Int64 getMaxDataPartIndex();
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2015-01-21 03:56:28 +00:00
|
|
|
|
std::string getTableName() const override
|
2014-10-03 17:55:36 +00:00
|
|
|
|
{
|
|
|
|
|
throw Exception("Logical error: calling method getTableName of not a table.", ErrorCodes::LOGICAL_ERROR);
|
2014-05-28 14:54:42 +00:00
|
|
|
|
}
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2014-10-10 15:45:43 +00:00
|
|
|
|
const NamesAndTypesList & getColumnsListImpl() const override { return *columns; }
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2015-01-21 03:56:28 +00:00
|
|
|
|
NameAndTypePair getColumn(const String & column_name) const override
|
2014-07-28 10:36:11 +00:00
|
|
|
|
{
|
2014-10-03 17:55:36 +00:00
|
|
|
|
if (column_name == "_part")
|
|
|
|
|
return NameAndTypePair("_part", new DataTypeString);
|
|
|
|
|
if (column_name == "_part_index")
|
|
|
|
|
return NameAndTypePair("_part_index", new DataTypeUInt64);
|
2014-09-30 03:08:47 +00:00
|
|
|
|
return ITableDeclaration::getColumn(column_name);
|
2014-07-28 10:36:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-21 03:56:28 +00:00
|
|
|
|
bool hasColumn(const String & column_name) const override
|
2014-07-28 10:36:11 +00:00
|
|
|
|
{
|
2014-11-12 10:37:47 +00:00
|
|
|
|
if (column_name == "_part")
|
|
|
|
|
return true;
|
|
|
|
|
if (column_name == "_part_index")
|
|
|
|
|
return true;
|
2014-09-30 03:08:47 +00:00
|
|
|
|
return ITableDeclaration::hasColumn(column_name);
|
2014-07-28 10:36:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-19 10:45:13 +00:00
|
|
|
|
String getFullPath() const { return full_path; }
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2014-05-08 07:12:01 +00:00
|
|
|
|
String getLogName() const { return log_name; }
|
|
|
|
|
|
2014-03-13 17:44:00 +00:00
|
|
|
|
/** Возвращает копию списка, чтобы снаружи можно было не заботиться о блокировках.
|
|
|
|
|
*/
|
2015-11-18 21:37:28 +00:00
|
|
|
|
DataParts getDataParts() const;
|
|
|
|
|
DataPartsVector getDataPartsVector() const;
|
|
|
|
|
DataParts getAllDataParts() const;
|
2014-05-16 15:55:57 +00:00
|
|
|
|
|
2015-04-17 05:35:53 +00:00
|
|
|
|
/** Размер активной части в количестве байт.
|
|
|
|
|
*/
|
2015-11-18 21:37:28 +00:00
|
|
|
|
size_t getTotalActiveSizeInBytes() const;
|
2015-04-17 05:35:53 +00:00
|
|
|
|
|
2014-05-16 15:55:57 +00:00
|
|
|
|
/** Максимальное количество кусков в одном месяце.
|
|
|
|
|
*/
|
2015-11-18 21:37:28 +00:00
|
|
|
|
size_t getMaxPartsCountForMonth() const;
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
2014-05-27 08:43:01 +00:00
|
|
|
|
/** Если в таблице слишком много активных кусков, спит некоторое время, чтобы дать им возможность смерджиться.
|
2014-09-03 02:32:23 +00:00
|
|
|
|
* Если передано until - проснуться раньше, если наступило событие.
|
2014-05-27 08:43:01 +00:00
|
|
|
|
*/
|
2014-09-03 02:32:23 +00:00
|
|
|
|
void delayInsertIfNeeded(Poco::Event * until = nullptr);
|
2014-05-27 08:43:01 +00:00
|
|
|
|
|
2014-07-25 11:38:46 +00:00
|
|
|
|
/** Возвращает активный кусок с указанным именем или кусок, покрывающий его. Если такого нет, возвращает nullptr.
|
2014-03-13 17:44:00 +00:00
|
|
|
|
*/
|
2014-07-25 11:38:46 +00:00
|
|
|
|
DataPartPtr getActiveContainingPart(const String & part_name);
|
|
|
|
|
|
|
|
|
|
/** Возвращает кусок с таким именем (активный или не активный). Если нету, nullptr.
|
|
|
|
|
*/
|
|
|
|
|
DataPartPtr getPartIfExists(const String & part_name);
|
2016-01-28 01:00:27 +00:00
|
|
|
|
DataPartPtr getShardedPartIfExists(const String & part_name, size_t shard_no);
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
|
|
|
|
/** Переименовывает временный кусок в постоянный и добавляет его в рабочий набор.
|
2014-09-30 20:26:10 +00:00
|
|
|
|
* Если increment != nullptr, индекс куска берется из инкремента. Иначе индекс куска не меняется.
|
2014-04-07 15:45:46 +00:00
|
|
|
|
* Предполагается, что кусок не пересекается с существующими.
|
2014-07-01 15:58:25 +00:00
|
|
|
|
* Если out_transaction не nullptr, присваивает туда объект, позволяющий откатить добавление куска (но не переименование).
|
2014-04-07 15:45:46 +00:00
|
|
|
|
*/
|
2015-06-02 20:22:53 +00:00
|
|
|
|
void renameTempPartAndAdd(MutableDataPartPtr & part, SimpleIncrement * increment = nullptr, Transaction * out_transaction = nullptr);
|
2014-04-07 15:45:46 +00:00
|
|
|
|
|
|
|
|
|
/** То же, что renameTempPartAndAdd, но кусок может покрывать существующие куски.
|
|
|
|
|
* Удаляет и возвращает все куски, покрытые добавляемым (в возрастающем порядке).
|
2014-03-13 17:44:00 +00:00
|
|
|
|
*/
|
2015-06-02 20:22:53 +00:00
|
|
|
|
DataPartsVector renameTempPartAndReplace(MutableDataPartPtr & part, SimpleIncrement * increment = nullptr, Transaction * out_transaction = nullptr);
|
2014-07-01 15:58:25 +00:00
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
/** Убирает из рабочего набора куски remove и добавляет куски add. add должны уже быть в all_data_parts.
|
2015-09-16 04:18:16 +00:00
|
|
|
|
* Если clear_without_timeout, данные будут удалены сразу, либо при следующем clearOldParts, игнорируя old_parts_lifetime.
|
2014-07-01 15:58:25 +00:00
|
|
|
|
*/
|
2014-07-07 10:23:24 +00:00
|
|
|
|
void replaceParts(const DataPartsVector & remove, const DataPartsVector & add, bool clear_without_timeout);
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
/** Добавляет новый кусок в список известных кусков и в рабочий набор.
|
|
|
|
|
*/
|
2014-09-29 20:26:46 +00:00
|
|
|
|
void attachPart(const DataPartPtr & part);
|
2014-08-08 08:28:13 +00:00
|
|
|
|
|
2014-08-07 09:23:55 +00:00
|
|
|
|
/** Переименовывает кусок в detached/prefix_кусок и забывает про него. Данные не будут удалены в clearOldParts.
|
2014-07-28 09:46:28 +00:00
|
|
|
|
* Если restore_covered, добавляет в рабочий набор неактивные куски, слиянием которых получен удаляемый кусок.
|
2014-04-02 07:59:43 +00:00
|
|
|
|
*/
|
2014-09-29 20:26:46 +00:00
|
|
|
|
void renameAndDetachPart(const DataPartPtr & part, const String & prefix = "", bool restore_covered = false, bool move_to_detached = true);
|
2014-08-08 08:28:13 +00:00
|
|
|
|
|
|
|
|
|
/** Убирает кусок из списка кусков (включая all_data_parts), но не перемещщает директорию.
|
|
|
|
|
*/
|
2014-09-29 20:26:46 +00:00
|
|
|
|
void detachPartInPlace(const DataPartPtr & part);
|
2014-04-02 10:10:37 +00:00
|
|
|
|
|
2014-07-25 11:15:11 +00:00
|
|
|
|
/** Возвращает старые неактуальные куски, которые можно удалить. Одновременно удаляет их из списка кусков, но не с диска.
|
2014-03-13 17:44:00 +00:00
|
|
|
|
*/
|
2014-07-25 11:15:11 +00:00
|
|
|
|
DataPartsVector grabOldParts();
|
|
|
|
|
|
|
|
|
|
/** Обращает изменения, сделанные grabOldParts().
|
|
|
|
|
*/
|
|
|
|
|
void addOldParts(const DataPartsVector & parts);
|
|
|
|
|
|
|
|
|
|
/** Удалить неактуальные куски.
|
|
|
|
|
*/
|
|
|
|
|
void clearOldParts();
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
|
|
|
|
/** После вызова dropAllData больше ничего вызывать нельзя.
|
2014-03-13 19:14:25 +00:00
|
|
|
|
* Удаляет директорию с данными и сбрасывает кеши разжатых блоков и засечек.
|
2014-03-13 17:44:00 +00:00
|
|
|
|
*/
|
|
|
|
|
void dropAllData();
|
|
|
|
|
|
2014-03-13 19:14:25 +00:00
|
|
|
|
/** Перемещает всю директорию с данными.
|
2014-03-13 19:07:17 +00:00
|
|
|
|
* Сбрасывает кеши разжатых блоков и засечек.
|
2014-07-11 12:47:45 +00:00
|
|
|
|
* Нужно вызывать под залоченным lockStructureForAlter().
|
2014-03-13 17:44:00 +00:00
|
|
|
|
*/
|
2014-07-28 14:33:30 +00:00
|
|
|
|
void setPath(const String & full_path, bool move_data);
|
2014-03-13 17:44:00 +00:00
|
|
|
|
|
2014-07-11 12:47:45 +00:00
|
|
|
|
/* Проверить, что такой ALTER можно выполнить:
|
|
|
|
|
* - Есть все нужные столбцы.
|
|
|
|
|
* - Все преобразования типов допустимы.
|
|
|
|
|
* - Не затронуты столбцы ключа, знака и семплирования.
|
|
|
|
|
* Бросает исключение, если что-то не так.
|
|
|
|
|
*/
|
|
|
|
|
void checkAlter(const AlterCommands & params);
|
|
|
|
|
|
2014-07-15 15:51:27 +00:00
|
|
|
|
/** Выполняет ALTER куска данных, записывает результат во временные файлы.
|
|
|
|
|
* Возвращает объект, позволяющий переименовать временные файлы в постоянные.
|
2014-07-17 09:38:31 +00:00
|
|
|
|
* Если измененных столбцов подозрительно много, и !skip_sanity_checks, бросает исключение.
|
|
|
|
|
* Если никаких действий над данными не требуется, возвращает nullptr.
|
2014-07-15 15:51:27 +00:00
|
|
|
|
*/
|
2014-09-29 20:26:46 +00:00
|
|
|
|
AlterDataPartTransactionPtr alterDataPart(const DataPartPtr & part, const NamesAndTypesList & new_columns, bool skip_sanity_checks = false);
|
2014-07-11 12:47:45 +00:00
|
|
|
|
|
|
|
|
|
/// Нужно вызывать под залоченным lockStructureForAlter().
|
|
|
|
|
void setColumnsList(const NamesAndTypesList & new_columns) { columns = new NamesAndTypesList(new_columns); }
|
2014-03-14 17:03:52 +00:00
|
|
|
|
|
2014-07-23 09:15:41 +00:00
|
|
|
|
/// Нужно вызвать, если есть подозрение, что данные куска испорчены.
|
|
|
|
|
void reportBrokenPart(const String & name)
|
|
|
|
|
{
|
|
|
|
|
broken_part_callback(name);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-14 17:03:52 +00:00
|
|
|
|
ExpressionActionsPtr getPrimaryExpression() const { return primary_expr; }
|
|
|
|
|
SortDescription getSortDescription() const { return sort_descr; }
|
|
|
|
|
|
2014-08-08 08:28:13 +00:00
|
|
|
|
/// Проверить, что кусок не сломан и посчитать для него чексуммы, если их нет.
|
|
|
|
|
MutableDataPartPtr loadPartAndFixMetadata(const String & relative_path);
|
|
|
|
|
|
2014-11-11 04:11:07 +00:00
|
|
|
|
/** Сделать локальный бэкап (снэпшот) для кусков, начинающихся с указанного префикса.
|
|
|
|
|
* Бэкап создаётся в директории clickhouse_dir/shadow/i/, где i - инкрементное число.
|
|
|
|
|
*/
|
|
|
|
|
void freezePartition(const std::string & prefix);
|
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
/** Возвращает размер заданной партиции в байтах.
|
|
|
|
|
*/
|
|
|
|
|
size_t getPartitionSize(const std::string & partition_name) const;
|
|
|
|
|
|
2014-09-19 11:44:29 +00:00
|
|
|
|
size_t getColumnSize(const std::string & name) const
|
|
|
|
|
{
|
2016-01-29 02:22:43 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock{data_parts_mutex};
|
2014-09-23 11:35:27 +00:00
|
|
|
|
|
2014-09-19 11:44:29 +00:00
|
|
|
|
const auto it = column_sizes.find(name);
|
|
|
|
|
return it == std::end(column_sizes) ? 0 : it->second;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-26 11:34:41 +00:00
|
|
|
|
using ColumnSizes = std::unordered_map<std::string, size_t>;
|
|
|
|
|
ColumnSizes getColumnSizes() const
|
|
|
|
|
{
|
2016-01-29 02:22:43 +00:00
|
|
|
|
std::lock_guard<std::mutex> lock{data_parts_mutex};
|
2015-07-26 11:34:41 +00:00
|
|
|
|
return column_sizes;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
/// Для ATTACH/DETACH/DROP/RESHARD PARTITION.
|
2014-10-03 17:57:01 +00:00
|
|
|
|
static String getMonthName(const Field & partition);
|
2016-01-28 01:00:27 +00:00
|
|
|
|
static String getMonthName(DayNum_t month);
|
2014-10-03 17:57:01 +00:00
|
|
|
|
static DayNum_t getMonthDayNum(const Field & partition);
|
2016-01-28 01:00:27 +00:00
|
|
|
|
static DayNum_t getMonthFromName(const String & month_name);
|
2014-10-03 17:57:01 +00:00
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
Context & context;
|
2014-03-14 17:03:52 +00:00
|
|
|
|
const String date_column_name;
|
|
|
|
|
const ASTPtr sampling_expression;
|
|
|
|
|
const size_t index_granularity;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
|
|
|
|
/// Режим работы - какие дополнительные действия делать при мердже.
|
2014-03-14 17:03:52 +00:00
|
|
|
|
const Mode mode;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
/// Для схлопывания записей об изменениях, если используется Collapsing режим работы.
|
2014-03-14 17:03:52 +00:00
|
|
|
|
const String sign_column;
|
2014-11-22 02:22:30 +00:00
|
|
|
|
/// Для суммирования, если используется Summing режим работы.
|
|
|
|
|
const Names columns_to_sum;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2014-03-14 17:03:52 +00:00
|
|
|
|
const MergeTreeSettings settings;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
|
2014-03-22 14:44:44 +00:00
|
|
|
|
const ASTPtr primary_expr_ast;
|
2015-05-28 04:32:38 +00:00
|
|
|
|
Block primary_key_sample;
|
2015-11-29 08:06:29 +00:00
|
|
|
|
DataTypes primary_key_data_types;
|
2014-03-22 14:44:44 +00:00
|
|
|
|
|
2014-03-14 17:03:52 +00:00
|
|
|
|
private:
|
2014-07-09 13:39:19 +00:00
|
|
|
|
bool require_part_metadata;
|
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
ExpressionActionsPtr primary_expr;
|
|
|
|
|
SortDescription sort_descr;
|
|
|
|
|
|
|
|
|
|
String full_path;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-03-13 12:48:07 +00:00
|
|
|
|
NamesAndTypesListPtr columns;
|
2014-09-23 11:35:27 +00:00
|
|
|
|
/// Актуальные размеры столбцов в сжатом виде
|
2015-07-26 11:34:41 +00:00
|
|
|
|
ColumnSizes column_sizes;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2014-07-23 09:15:41 +00:00
|
|
|
|
BrokenPartCallback broken_part_callback;
|
|
|
|
|
|
2014-05-08 07:12:01 +00:00
|
|
|
|
String log_name;
|
2014-03-13 12:48:07 +00:00
|
|
|
|
Logger * log;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
|
|
|
|
/** Актуальное множество кусков с данными. */
|
|
|
|
|
DataParts data_parts;
|
2016-01-29 02:22:43 +00:00
|
|
|
|
mutable std::mutex data_parts_mutex;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
|
|
|
|
/** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов).
|
2014-03-13 12:48:07 +00:00
|
|
|
|
* Ссылки на кусок есть отсюда, из списка актуальных кусков и из каждого потока чтения, который его сейчас использует.
|
2014-03-09 17:36:01 +00:00
|
|
|
|
* То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить.
|
|
|
|
|
*/
|
|
|
|
|
DataParts all_data_parts;
|
2016-01-29 02:22:43 +00:00
|
|
|
|
mutable std::mutex all_data_parts_mutex;
|
2014-03-09 17:36:01 +00:00
|
|
|
|
|
2016-01-28 01:00:27 +00:00
|
|
|
|
/** Для каждого шарда множество шардированных кусков.
|
|
|
|
|
*/
|
|
|
|
|
PerShardDataParts per_shard_data_parts;
|
|
|
|
|
|
2014-07-11 12:47:45 +00:00
|
|
|
|
/** Выражение, преобразующее типы столбцов.
|
|
|
|
|
* Если преобразований типов нет, out_expression=nullptr.
|
|
|
|
|
* out_rename_map отображает файлы-столбцы на выходе выражения в новые файлы таблицы.
|
2015-12-29 12:57:11 +00:00
|
|
|
|
* out_force_update_metadata показывает, нужно ли обновить метаданные даже если out_rename_map пуста (используется
|
|
|
|
|
* для бесплатного изменения списка значений Enum).
|
2014-07-11 12:47:45 +00:00
|
|
|
|
* Файлы, которые нужно удалить, в out_rename_map отображаются в пустую строку.
|
|
|
|
|
* Если !part, просто проверяет, что все нужные преобразования типов допустимы.
|
|
|
|
|
*/
|
2014-09-29 20:26:46 +00:00
|
|
|
|
void createConvertExpression(const DataPartPtr & part, const NamesAndTypesList & old_columns, const NamesAndTypesList & new_columns,
|
2015-12-29 12:57:11 +00:00
|
|
|
|
ExpressionActionsPtr & out_expression, NameToNameMap & out_rename_map, bool & out_force_update_metadata);
|
2014-09-19 11:44:29 +00:00
|
|
|
|
|
2014-09-23 11:35:27 +00:00
|
|
|
|
/// Рассчитывает размеры столбцов в сжатом виде для текущего состояния data_parts
|
2014-09-19 11:44:29 +00:00
|
|
|
|
void calculateColumnSizes();
|
2014-09-23 11:35:27 +00:00
|
|
|
|
/// Добавляет или вычитывает вклад part в размеры столбцов в сжатом виде
|
2014-09-19 11:44:29 +00:00
|
|
|
|
void addPartContributionToColumnSizes(const DataPartPtr & part);
|
|
|
|
|
void removePartContributionToColumnSizes(const DataPartPtr & part);
|
2014-03-09 17:36:01 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|