MergeTreeData.cpp 28.1 KB
Newer Older
M
Merge  
Michael Kolupaev 已提交
1
#include <DB/Storages/MergeTree/MergeTreeData.h>
M
Merge  
Michael Kolupaev 已提交
2
#include <Yandex/time2str.h>
M
Merge  
Michael Kolupaev 已提交
3
#include <Poco/Ext/ScopedTry.h>
M
Merge  
Michael Kolupaev 已提交
4
#include <DB/Interpreters/ExpressionAnalyzer.h>
M
Merge  
Michael Kolupaev 已提交
5
#include <DB/Storages/MergeTree/MergeTreeReader.h>
M
Merge  
Michael Kolupaev 已提交
6 7
#include <DB/Storages/MergeTree/MergeTreeBlockInputStream.h>
#include <DB/Storages/MergeTree/MergedBlockOutputStream.h>
M
Merge  
Michael Kolupaev 已提交
8 9 10
#include <DB/Parsers/ASTIdentifier.h>
#include <DB/Parsers/ASTNameTypePair.h>
#include <DB/DataStreams/ExpressionBlockInputStream.h>
M
Merge  
Michael Kolupaev 已提交
11
#include <DB/DataStreams/copyData.h>
M
Merge  
Michael Kolupaev 已提交
12
#include <DB/IO/WriteBufferFromFile.h>
P
Merge  
Pavel Kartavyy 已提交
13
#include <algorithm>
M
Merge  
Michael Kolupaev 已提交
14 15 16 17 18 19 20



namespace DB
{

MergeTreeData::MergeTreeData(
M
Merge  
Michael Kolupaev 已提交
21
	const String & full_path_, NamesAndTypesListPtr columns_,
M
Merge  
Michael Kolupaev 已提交
22 23 24 25 26 27
	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_,
M
Merge  
Michael Kolupaev 已提交
28
	const MergeTreeSettings & settings_,
M
Merge  
Michael Kolupaev 已提交
29 30
	const String & log_name_,
	bool require_part_metadata_)
M
Merge  
Michael Kolupaev 已提交
31
	: context(context_),
M
Merge  
Michael Kolupaev 已提交
32 33 34
	date_column_name(date_column_name_), sampling_expression(sampling_expression_),
	index_granularity(index_granularity_),
	mode(mode_), sign_column(sign_column_),
M
Merge  
Michael Kolupaev 已提交
35
	settings(settings_), primary_expr_ast(primary_expr_ast_->clone()),
M
Merge  
Michael Kolupaev 已提交
36
	require_part_metadata(require_part_metadata_),
M
Merge  
Michael Kolupaev 已提交
37
	full_path(full_path_), columns(columns_), log_name(log_name_),
38
	log(&Logger::get(log_name + " (Data)"))
M
Merge  
Michael Kolupaev 已提交
39 40 41 42 43 44
{
	/// создаём директорию, если её нет
	Poco::File(full_path).createDirectories();

	/// инициализируем описание сортировки
	sort_descr.reserve(primary_expr_ast->children.size());
M
Merge  
Michael Kolupaev 已提交
45
	for (const ASTPtr & ast : primary_expr_ast->children)
M
Merge  
Michael Kolupaev 已提交
46
	{
M
Merge  
Michael Kolupaev 已提交
47
		String name = ast->getColumnName();
M
Merge  
Michael Kolupaev 已提交
48 49 50 51 52 53 54 55 56
		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();

	loadDataParts();
M
Merge  
Michael Kolupaev 已提交
57
}
M
Merge  
Michael Kolupaev 已提交
58

M
Merge  
Michael Kolupaev 已提交
59 60
UInt64 MergeTreeData::getMaxDataPartIndex()
{
M
Merge  
Michael Kolupaev 已提交
61 62 63 64 65
	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);
	}
M
Merge  
Michael Kolupaev 已提交
66
	return max_part_id;
M
Merge  
Michael Kolupaev 已提交
67 68
}

M
Merge  
Michael Kolupaev 已提交
69
std::string MergeTreeData::getModePrefix() const
M
Merge  
Michael Kolupaev 已提交
70
{
M
Merge  
Michael Kolupaev 已提交
71
	switch (mode)
M
Merge  
Michael Kolupaev 已提交
72
	{
M
Merge  
Michael Kolupaev 已提交
73 74 75
		case Ordinary: 		return "";
		case Collapsing: 	return "Collapsing";
		case Summing: 		return "Summing";
S
Merge  
Sergey Fedorov 已提交
76
		case Aggregating: 	return "Aggregating";
M
Merge  
Michael Kolupaev 已提交
77

M
Merge  
Michael Kolupaev 已提交
78 79
		default:
			throw Exception("Unknown mode of operation for MergeTreeData: " + toString(mode), ErrorCodes::LOGICAL_ERROR);
M
Merge  
Michael Kolupaev 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92
	}
}


void MergeTreeData::loadDataParts()
{
	LOG_DEBUG(log, "Loading data parts");

	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
	Poco::ScopedLock<Poco::FastMutex> lock_all(all_data_parts_mutex);

	data_parts.clear();

M
Merge  
Michael Kolupaev 已提交
93
	Strings all_file_names;
M
Merge  
Michael Kolupaev 已提交
94 95
	Poco::DirectoryIterator end;
	for (Poco::DirectoryIterator it(full_path); it != end; ++it)
M
Merge  
Michael Kolupaev 已提交
96
		all_file_names.push_back(it.name());
M
Merge  
Michael Kolupaev 已提交
97

M
Merge  
Michael Kolupaev 已提交
98 99 100
	Strings part_file_names;
	for (const String & file_name : all_file_names)
	{
M
Merge  
Michael Kolupaev 已提交
101 102 103 104 105
		/// Удаляем временные директории старше суток.
		if (0 == file_name.compare(0, strlen("tmp_"), "tmp_"))
			continue;

		if (0 == file_name.compare(0, strlen("old_"), "old_"))
M
Merge  
Michael Kolupaev 已提交
106 107 108 109 110 111
		{
			String new_file_name = file_name.substr(strlen("old_"));
			LOG_WARNING(log, "Renaming " << file_name << " to " << new_file_name << " for compatibility reasons");
			Poco::File(full_path + file_name).renameTo(full_path + new_file_name);
			part_file_names.push_back(new_file_name);
		}
M
Merge  
Michael Kolupaev 已提交
112
		else
M
Merge  
Michael Kolupaev 已提交
113
		{
M
Merge  
Michael Kolupaev 已提交
114
			part_file_names.push_back(file_name);
M
Merge  
Michael Kolupaev 已提交
115
		}
M
Merge  
Michael Kolupaev 已提交
116 117
	}

M
Merge  
Michael Kolupaev 已提交
118 119
	DataPartsVector broken_parts_to_remove;

M
Merge  
Michael Kolupaev 已提交
120
	Poco::RegularExpression::MatchVec matches;
M
Merge  
Michael Kolupaev 已提交
121
	for (const String & file_name : part_file_names)
M
Merge  
Michael Kolupaev 已提交
122
	{
123
		if (!ActiveDataPartSet::isPartDirectory(file_name, matches))
M
Merge  
Michael Kolupaev 已提交
124 125
			continue;

M
Merge  
Michael Kolupaev 已提交
126
		MutableDataPartPtr part = std::make_shared<DataPart>(*this);
127
		ActiveDataPartSet::parsePartName(file_name, *part, &matches);
M
Merge  
Michael Kolupaev 已提交
128 129
		part->name = file_name;

M
Merge  
Michael Kolupaev 已提交
130 131 132 133
		bool broken = false;

		try
		{
M
Merge  
Michael Kolupaev 已提交
134
			part->loadColumns();
M
Merge  
Michael Kolupaev 已提交
135
			part->loadChecksums();
M
Merge  
Michael Kolupaev 已提交
136
			part->loadIndex();
M
Merge  
Michael Kolupaev 已提交
137 138 139 140 141 142 143 144
			part->checkNotBroken();
		}
		catch (...)
		{
			broken = true;
			tryLogCurrentException(__PRETTY_FUNCTION__);
		}

M
Merge  
Michael Kolupaev 已提交
145
		/// Игнорируем и, возможно, удаляем битые куски, которые могут образовываться после грубого перезапуска сервера.
M
Merge  
Michael Kolupaev 已提交
146
		if (broken)
M
Merge  
Michael Kolupaev 已提交
147 148 149 150
		{
			if (part->level == 0)
			{
				/// Восстановить куски нулевого уровня невозможно.
M
Merge  
Michael Kolupaev 已提交
151
				LOG_ERROR(log, "Removing broken part " << full_path + file_name << " because is't impossible to repair.");
M
Merge  
Michael Kolupaev 已提交
152
				broken_parts_to_remove.push_back(part);
M
Merge  
Michael Kolupaev 已提交
153 154 155
			}
			else
			{
M
Merge  
Michael Kolupaev 已提交
156 157 158 159 160 161 162 163 164 165
				/// Посмотрим, сколько кусков покрыты битым. Если хотя бы два, предполагаем, что битый кусок образован их
				///  слиянием, и мы ничего не потеряем, если его удалим.
				int contained_parts = 0;

				LOG_ERROR(log, "Part " << full_path + file_name << " is broken. Looking for parts to replace it.");

				for (const String & contained_name : part_file_names)
				{
					if (contained_name == file_name)
						continue;
166
					if (!ActiveDataPartSet::isPartDirectory(contained_name, matches))
M
Merge  
Michael Kolupaev 已提交
167 168
						continue;
					DataPart contained_part(*this);
169
					ActiveDataPartSet::parsePartName(contained_name, contained_part, &matches);
M
Merge  
Michael Kolupaev 已提交
170 171 172 173 174 175 176 177 178 179
					if (part->contains(contained_part))
					{
						LOG_ERROR(log, "Found part " << full_path + contained_name);
						++contained_parts;
					}
				}

				if (contained_parts >= 2)
				{
					LOG_ERROR(log, "Removing broken part " << full_path + file_name << " because it covers at least 2 other parts");
M
Merge  
Michael Kolupaev 已提交
180
					broken_parts_to_remove.push_back(part);
M
Merge  
Michael Kolupaev 已提交
181 182 183 184 185 186
				}
				else
				{
					LOG_ERROR(log, "Not removing broken part " << full_path + file_name
						<< " because it covers less than 2 parts. You need to resolve this manually");
				}
M
Merge  
Michael Kolupaev 已提交
187 188 189 190 191 192 193 194 195 196
			}

			continue;
		}

		part->modification_time = Poco::File(full_path + file_name).getLastModified().epochTime();

		data_parts.insert(part);
	}

M
Merge  
Michael Kolupaev 已提交
197 198 199 200 201 202 203
	if (broken_parts_to_remove.size() > 2)
		throw Exception("Suspiciously many (" + toString(broken_parts_to_remove.size()) + ") broken parts to remove.",
			ErrorCodes::TOO_MANY_UNEXPECTED_DATA_PARTS);

	for (const auto & part : broken_parts_to_remove)
		part->remove();

M
Merge  
Michael Kolupaev 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
	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))
			{
M
Merge  
Michael Kolupaev 已提交
230
				(*prev_jt)->remove_time = time(0);
M
Merge  
Michael Kolupaev 已提交
231 232 233 234 235 236
				data_parts.erase(prev_jt);
				prev_jt = curr_jt;
				++curr_jt;
			}
			else if ((*prev_jt)->contains(**curr_jt))
			{
M
Merge  
Michael Kolupaev 已提交
237
				(*curr_jt)->remove_time = time(0);
M
Merge  
Michael Kolupaev 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251
				data_parts.erase(curr_jt++);
			}
			else
			{
				++prev_jt;
				++curr_jt;
			}
		}
	}

	LOG_DEBUG(log, "Loaded data parts (" << data_parts.size() << " items)");
}


M
Merge  
Michael Kolupaev 已提交
252
Strings MergeTreeData::clearOldParts()
M
Merge  
Michael Kolupaev 已提交
253 254
{
	Poco::ScopedTry<Poco::FastMutex> lock;
M
Merge  
Michael Kolupaev 已提交
255
	Strings res;
M
Merge  
Michael Kolupaev 已提交
256 257 258 259

	/// Если метод уже вызван из другого потока (или если all_data_parts прямо сейчас меняют), то можно ничего не делать.
	if (!lock.lock(&all_data_parts_mutex))
	{
M
Merge  
Michael Kolupaev 已提交
260
		return res;
M
Merge  
Michael Kolupaev 已提交
261 262
	}

M
Merge  
Michael Kolupaev 已提交
263
	time_t now = time(0);
M
Merge  
Michael Kolupaev 已提交
264 265
	for (DataParts::iterator it = all_data_parts.begin(); it != all_data_parts.end();)
	{
M
Merge  
Michael Kolupaev 已提交
266
		int ref_count = it->use_count();
M
Merge  
Michael Kolupaev 已提交
267 268
		if (ref_count == 1 && /// После этого ref_count не может увеличиться.
			(*it)->remove_time + settings.old_parts_lifetime < now)
M
Merge  
Michael Kolupaev 已提交
269
		{
M
Merge  
Michael Kolupaev 已提交
270
			LOG_DEBUG(log, "Removing part " << (*it)->name);
M
Merge  
Michael Kolupaev 已提交
271

M
Merge  
Michael Kolupaev 已提交
272 273
			res.push_back((*it)->name);
			(*it)->remove();
M
Merge  
Michael Kolupaev 已提交
274 275 276 277 278 279
			all_data_parts.erase(it++);
		}
		else
			++it;
	}

M
Merge  
Michael Kolupaev 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
	/// Удаляем временные директории старше суток.
	Strings all_file_names;
	Poco::DirectoryIterator end;
	for (Poco::DirectoryIterator it(full_path); it != end; ++it)
		all_file_names.push_back(it.name());

	for (const String & file_name : all_file_names)
	{
		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;
		}
	}

M
Merge  
Michael Kolupaev 已提交
302
	return res;
M
Merge  
Michael Kolupaev 已提交
303 304
}

M
Merge  
Michael Kolupaev 已提交
305
void MergeTreeData::setPath(const String & new_full_path)
M
Merge  
Michael Kolupaev 已提交
306
{
307
	Poco::File(full_path).renameTo(new_full_path);
M
Merge  
Michael Kolupaev 已提交
308
	full_path = new_full_path;
309

310
	context.resetCaches();
M
Merge  
Michael Kolupaev 已提交
311 312
}

M
Merge  
Michael Kolupaev 已提交
313
void MergeTreeData::dropAllData()
M
Merge  
Michael Kolupaev 已提交
314 315 316 317
{
	data_parts.clear();
	all_data_parts.clear();

318
	context.resetCaches();
319

M
Merge  
Michael Kolupaev 已提交
320 321 322 323
	Poco::File(full_path).remove(true);
}


M
Merge  
Michael Kolupaev 已提交
324
void MergeTreeData::checkAlter(const AlterCommands & params)
M
Merge  
Michael Kolupaev 已提交
325
{
M
Merge  
Michael Kolupaev 已提交
326 327 328
	/// Проверим, что указанные преобразования можно совершить над списком столбцов без учета типов.
	NamesAndTypesList new_columns = *columns;
	params.apply(new_columns);
M
Merge  
Michael Kolupaev 已提交
329

M
Merge  
Michael Kolupaev 已提交
330 331 332 333 334
	/// Список столбцов, которые нельзя трогать.
	/// sampling_expression можно не учитывать, потому что он обязан содержаться в первичном ключе.
	Names keys = primary_expr->getRequiredColumns();
	keys.push_back(sign_column);
	std::sort(keys.begin(), keys.end());
M
Merge  
Michael Kolupaev 已提交
335

M
Merge  
Michael Kolupaev 已提交
336
	for (const AlterCommand & command : params)
337
	{
M
Merge  
Michael Kolupaev 已提交
338 339
		if (std::binary_search(keys.begin(), keys.end(), command.column_name))
			throw Exception("trying to ALTER key column " + command.column_name, ErrorCodes::ILLEGAL_COLUMN);
340 341
	}

M
Merge  
Michael Kolupaev 已提交
342 343 344 345
	/// Проверим, что преобразования типов возможны.
	ExpressionActionsPtr unused_expression;
	NameToNameMap unused_map;
	createConvertExpression(nullptr, *columns, new_columns, unused_expression, unused_map);
M
Merge  
Michael Kolupaev 已提交
346 347
}

M
Merge  
Michael Kolupaev 已提交
348 349
void MergeTreeData::createConvertExpression(DataPartPtr part, const NamesAndTypesList & old_columns, const NamesAndTypesList & new_columns,
	ExpressionActionsPtr & out_expression, NameToNameMap & out_rename_map)
M
Merge  
Michael Kolupaev 已提交
350
{
M
Merge  
Michael Kolupaev 已提交
351 352
	out_expression = nullptr;
	out_rename_map.clear();
M
Merge  
Michael Kolupaev 已提交
353

M
Merge  
Michael Kolupaev 已提交
354 355 356
	typedef std::map<String, DataTypePtr> NameToType;
	NameToType new_types;
	for (const NameAndTypePair & column : new_columns)
M
Merge  
Michael Kolupaev 已提交
357
	{
M
Merge  
Michael Kolupaev 已提交
358
		new_types[column.name] = column.type;
M
Merge  
Michael Kolupaev 已提交
359
	}
M
Merge  
Michael Kolupaev 已提交
360

M
Merge  
Michael Kolupaev 已提交
361 362 363
	/// Сколько столбцов сейчас в каждой вложенной структуре. Столбцы не из вложенных структур сюда тоже попадут и не помешают.
	std::map<String, int> nested_table_counts;
	for (const NameAndTypePair & column : old_columns)
M
Merge  
Michael Kolupaev 已提交
364
	{
M
Merge  
Michael Kolupaev 已提交
365
		++nested_table_counts[DataTypeNested::extractNestedTableName(column.name)];
M
Merge  
Michael Kolupaev 已提交
366
	}
367

M
Merge  
Michael Kolupaev 已提交
368
	for (const NameAndTypePair & column : old_columns)
M
Merge  
Michael Kolupaev 已提交
369
	{
M
Merge  
Michael Kolupaev 已提交
370
		if (!new_types.count(column.name))
371
		{
M
Merge  
Michael Kolupaev 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
			if (!part || part->hasColumnFiles(column.name))
			{
				/// Столбец нужно удалить.

				String escaped_column = escapeForFileName(column.name);
				out_rename_map[escaped_column + ".bin"] = "";
				out_rename_map[escaped_column + ".mrk"] = "";

				/// Если это массив или последний столбец вложенной структуры, нужно удалить файлы с размерами.
				if (typeid_cast<const DataTypeArray *>(&*column.type))
				{
					String nested_table = DataTypeNested::extractNestedTableName(column.name);
					/// Если это был последний столбец, относящийся к этим файлам .size0, удалим файлы.
					if (!--nested_table_counts[nested_table])
					{
						String escaped_nested_table = escapeForFileName(nested_table);
						out_rename_map[escaped_nested_table + ".size0.bin"] = "";
						out_rename_map[escaped_nested_table + ".size0.mrk"] = "";
					}
				}
			}
393
		}
M
Merge  
Michael Kolupaev 已提交
394 395 396 397 398 399 400 401 402 403 404
		else
		{
			String new_type_name = new_types[column.name]->getName();

			if (new_type_name != column.type->getName() &&
				(!part || part->hasColumnFiles(column.name)))
			{
				/// Нужно изменить тип столбца.

				if (!out_expression)
					out_expression = new ExpressionActions(NamesAndTypesList(), context.getSettingsRef());
M
Merge  
Michael Kolupaev 已提交
405

M
Merge  
Michael Kolupaev 已提交
406 407 408 409 410 411 412 413 414 415 416 417 418
				out_expression->addInput(ColumnWithNameAndType(nullptr, column.type, column.name));

				FunctionPtr function = context.getFunctionFactory().get("to" + new_type_name, context);
				Names out_names;
				out_expression->add(ExpressionAction::applyFunction(function, Names(1, column.name)), out_names);
				out_expression->add(ExpressionAction::removeColumn(column.name));

				String escaped_expr = escapeForFileName(out_names[0]);
				String escaped_column = escapeForFileName(column.name);
				out_rename_map[escaped_expr + ".bin"] = escaped_column + ".bin";
				out_rename_map[escaped_expr + ".mrk"] = escaped_column + ".mrk";
			}
		}
M
Merge  
Michael Kolupaev 已提交
419 420
	}
}
M
Merge  
Michael Kolupaev 已提交
421

M
Merge  
Michael Kolupaev 已提交
422
MergeTreeData::AlterDataPartTransactionPtr MergeTreeData::alterDataPart(DataPartPtr part, const NamesAndTypesList & new_columns)
M
Merge  
Michael Kolupaev 已提交
423
{
M
Merge  
Michael Kolupaev 已提交
424 425
	ExpressionActionsPtr expression;
	AlterDataPartTransactionPtr transaction(new AlterDataPartTransaction(part));
M
Merge  
Michael Kolupaev 已提交
426
	createConvertExpression(part, part->columns, new_columns, expression, transaction->rename_map);
M
Merge  
Michael Kolupaev 已提交
427

M
Merge  
Michael Kolupaev 已提交
428
	if (transaction->rename_map.empty())
M
Merge  
Michael Kolupaev 已提交
429
	{
M
Merge  
Michael Kolupaev 已提交
430
		transaction->clear();
M
Merge  
Michael Kolupaev 已提交
431
		return transaction;
M
Merge  
Michael Kolupaev 已提交
432
	}
M
Merge  
Michael Kolupaev 已提交
433

M
Merge  
Michael Kolupaev 已提交
434 435 436 437
	DataPart::Checksums add_checksums;

	/// Применим выражение и запишем результат во временные файлы.
	if (expression)
M
Merge  
Michael Kolupaev 已提交
438 439
	{
		MarkRanges ranges(1, MarkRange(0, part->size));
M
Merge  
Michael Kolupaev 已提交
440 441 442
		BlockInputStreamPtr part_in = new MergeTreeBlockInputStream(full_path + part->name + '/',
			DEFAULT_MERGE_BLOCK_SIZE, expression->getRequiredColumns(), *this, part, ranges, false, nullptr, "");
		ExpressionBlockInputStream in(part_in, expression);
M
Merge  
Michael Kolupaev 已提交
443
		MergedColumnOnlyOutputStream out(*this, full_path + part->name + '/', true);
M
Merge  
Michael Kolupaev 已提交
444
		in.readPrefix();
M
Merge  
Michael Kolupaev 已提交
445
		out.writePrefix();
M
Merge  
Michael Kolupaev 已提交
446

M
Merge  
Michael Kolupaev 已提交
447 448
		while (Block b = in.read())
			out.write(b);
M
Merge  
Michael Kolupaev 已提交
449

M
Merge  
Michael Kolupaev 已提交
450 451 452
		in.readSuffix();
		add_checksums = out.writeSuffixAndGetChecksums();
	}
M
Merge  
Michael Kolupaev 已提交
453

M
Merge  
Michael Kolupaev 已提交
454
	/// Обновим контрольные суммы.
M
Merge  
Michael Kolupaev 已提交
455 456 457 458 459 460
	DataPart::Checksums new_checksums = part->checksums;
	for (auto it : transaction->rename_map)
	{
		if (it.second == "")
		{
			new_checksums.files.erase(it.first);
M
Merge  
Michael Kolupaev 已提交
461
		}
M
Merge  
Michael Kolupaev 已提交
462
		else
M
Merge  
Michael Kolupaev 已提交
463
		{
M
Merge  
Michael Kolupaev 已提交
464
			new_checksums.files[it.second] = add_checksums.files[it.first];
M
Merge  
Michael Kolupaev 已提交
465 466
		}
	}
M
Merge  
Michael Kolupaev 已提交
467

M
Merge  
Michael Kolupaev 已提交
468 469
	/// Запишем обновленные контрольные суммы во временный файл
	if (!part->checksums.empty())
M
Merge  
Michael Kolupaev 已提交
470
	{
M
Merge  
Michael Kolupaev 已提交
471
		transaction->new_checksums = new_checksums;
M
Merge  
Michael Kolupaev 已提交
472 473 474
		WriteBufferFromFile checksums_file(full_path + part->name + "/checksums.txt.tmp", 4096);
		new_checksums.writeText(checksums_file);
		transaction->rename_map["checksums.txt.tmp"] = "checksums.txt";
M
Merge  
Michael Kolupaev 已提交
475
	}
M
Merge  
Michael Kolupaev 已提交
476

M
Merge  
Michael Kolupaev 已提交
477 478 479 480 481 482 483 484
	/// Запишем обновленный список столбцов во временный файл.
	{
		transaction->new_columns = new_columns.intersect(part->columns.getNames());
		WriteBufferFromFile columns_file(full_path + part->name + "/columns.txt.tmp", 4096);
		transaction->new_columns.writeText(columns_file);
		transaction->rename_map["columns.txt.tmp"] = "columns.txt";
	}

M
Merge  
Michael Kolupaev 已提交
485 486
	return transaction;
}
M
Merge  
Michael Kolupaev 已提交
487

M
Merge  
Michael Kolupaev 已提交
488 489 490 491 492
void MergeTreeData::AlterDataPartTransaction::commit()
{
	if (!data_part)
		return;
	try
M
Merge  
Michael Kolupaev 已提交
493
	{
M
Merge  
Michael Kolupaev 已提交
494 495
		Poco::ScopedWriteRWLock lock(data_part->columns_lock);

M
Merge  
Michael Kolupaev 已提交
496
		String path = data_part->storage.full_path + data_part->name + "/";
M
Merge  
Michael Kolupaev 已提交
497 498

		/// 1) Переименуем старые файлы.
M
Merge  
Michael Kolupaev 已提交
499
		for (auto it : rename_map)
M
Merge  
Michael Kolupaev 已提交
500
		{
M
Merge  
Michael Kolupaev 已提交
501 502 503 504
			String name = it.second.empty() ? it.first : it.second;
			Poco::File(path + name).renameTo(path + name + ".tmp2");
		}

505
		/// 2) Переместим на их место новые и обновим метаданные в оперативке.
M
Merge  
Michael Kolupaev 已提交
506 507 508
		for (auto it : rename_map)
		{
			if (!it.second.empty())
M
Merge  
Michael Kolupaev 已提交
509
			{
M
Merge  
Michael Kolupaev 已提交
510
				Poco::File(path + it.first).renameTo(path + it.second);
M
Merge  
Michael Kolupaev 已提交
511
			}
M
Merge  
Michael Kolupaev 已提交
512
		}
513 514 515 516

		DataPart & mutable_part = const_cast<DataPart &>(*data_part);
		mutable_part.checksums = new_checksums;
		mutable_part.columns = new_columns;
M
Merge  
Michael Kolupaev 已提交
517 518 519 520 521 522 523 524 525

		/// 3) Удалим старые файлы.
		for (auto it : rename_map)
		{
			String name = it.second.empty() ? it.first : it.second;
			Poco::File(path + name + ".tmp2").remove();
		}

		clear();
M
Merge  
Michael Kolupaev 已提交
526 527 528 529
	}
	catch (...)
	{
		/// Если что-то пошло не так, не будем удалять временные файлы в деструкторе.
M
Merge  
Michael Kolupaev 已提交
530
		clear();
M
Merge  
Michael Kolupaev 已提交
531
		throw;
M
Merge  
Michael Kolupaev 已提交
532
	}
M
Merge  
Michael Kolupaev 已提交
533
}
M
Merge  
Michael Kolupaev 已提交
534

M
Merge  
Michael Kolupaev 已提交
535
MergeTreeData::AlterDataPartTransaction::~AlterDataPartTransaction()
M
Merge  
Michael Kolupaev 已提交
536
{
M
Merge  
Michael Kolupaev 已提交
537
	try
M
Merge  
Michael Kolupaev 已提交
538
	{
M
Merge  
Michael Kolupaev 已提交
539 540 541 542
		if (!data_part)
			return;

		LOG_WARNING(data_part->storage.log, "Aborting ALTER of part " << data_part->name);
M
Merge  
Michael Kolupaev 已提交
543

M
Merge  
Michael Kolupaev 已提交
544 545 546 547
		String path = data_part->storage.full_path + data_part->name + "/";
		for (auto it : rename_map)
		{
			if (!it.second.empty())
M
Merge  
Michael Kolupaev 已提交
548
			{
M
Merge  
Michael Kolupaev 已提交
549 550 551 552 553 554 555 556
				try
				{
					Poco::File(path + it.first).remove();
				}
				catch (Poco::Exception & e)
				{
					LOG_WARNING(data_part->storage.log, "Can't remove " << path + it.first << ": " << e.displayText());
				}
M
Merge  
Michael Kolupaev 已提交
557
			}
M
Merge  
Michael Kolupaev 已提交
558 559
		}
	}
M
Merge  
Michael Kolupaev 已提交
560
	catch (...)
M
Merge  
Michael Kolupaev 已提交
561
	{
M
Merge  
Michael Kolupaev 已提交
562
		tryLogCurrentException(__PRETTY_FUNCTION__);
M
Merge  
Michael Kolupaev 已提交
563
	}
M
Merge  
Michael Kolupaev 已提交
564 565 566
}


M
Merge  
Michael Kolupaev 已提交
567
void MergeTreeData::renameTempPartAndAdd(MutableDataPartPtr part, Increment * increment, Transaction * out_transaction)
M
Merge  
Michael Kolupaev 已提交
568
{
M
Merge  
Michael Kolupaev 已提交
569
	auto removed = renameTempPartAndReplace(part, increment, out_transaction);
M
Merge  
Michael Kolupaev 已提交
570 571 572 573 574
	if (!removed.empty())
	{
		LOG_ERROR(log, "Added part " << part->name << + " covers " << toString(removed.size())
			<< " existing part(s) (including " << removed[0]->name << ")");
	}
M
Merge  
Michael Kolupaev 已提交
575 576
}

M
Merge  
Michael Kolupaev 已提交
577 578
MergeTreeData::DataPartsVector MergeTreeData::renameTempPartAndReplace(
	MutableDataPartPtr part, Increment * increment, Transaction * out_transaction)
M
Merge  
Michael Kolupaev 已提交
579
{
M
Merge  
Michael Kolupaev 已提交
580 581 582
	if (out_transaction && out_transaction->data)
		throw Exception("Using the same MergeTreeData::Transaction for overlapping transactions is invalid");

583
	LOG_TRACE(log, "Renaming " << part->name << ".");
M
Merge  
Michael Kolupaev 已提交
584 585 586 587

	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
	Poco::ScopedLock<Poco::FastMutex> lock_all(all_data_parts_mutex);

588
	String old_path = getFullPath() + part->name + "/";
M
Merge  
Michael Kolupaev 已提交
589

M
Merge  
Michael Kolupaev 已提交
590
	/** Для StorageMergeTree важно, что получение номера куска происходит атомарно с добавлением этого куска в набор.
M
Merge  
Michael Kolupaev 已提交
591 592 593 594
	  * Иначе есть race condition - может произойти слияние пары кусков, диапазоны номеров которых
	  *  содержат ещё не добавленный кусок.
	  */
	if (increment)
M
Merge  
Michael Kolupaev 已提交
595
		part->left = part->right = increment->get(false);
M
Merge  
Michael Kolupaev 已提交
596

597
	part->name = ActiveDataPartSet::getPartName(part->left_date, part->right_date, part->left, part->right, part->level);
M
Merge  
Michael Kolupaev 已提交
598

M
Merge  
Michael Kolupaev 已提交
599 600
	if (data_parts.count(part))
		throw Exception("Part " + part->name + " already exists", ErrorCodes::DUPLICATE_DATA_PART);
M
Merge  
Michael Kolupaev 已提交
601

602
	String new_path = getFullPath() + part->name + "/";
M
Merge  
Michael Kolupaev 已提交
603 604 605 606

	/// Переименовываем кусок.
	Poco::File(old_path).renameTo(new_path);

M
Merge  
Michael Kolupaev 已提交
607
	bool obsolete = false; /// Покрыт ли part каким-нибудь куском.
M
Merge  
Michael Kolupaev 已提交
608 609 610 611 612 613 614 615
	DataPartsVector res;
	/// Куски, содержащиеся в part, идут в data_parts подряд, задевая место, куда вставился бы сам part.
	DataParts::iterator it = data_parts.lower_bound(part);
	/// Пойдем влево.
	while (it != data_parts.begin())
	{
		--it;
		if (!part->contains(**it))
M
Michael Kolupaev 已提交
616
		{
M
Merge  
Michael Kolupaev 已提交
617 618
			if ((*it)->contains(*part))
				obsolete = true;
M
Michael Kolupaev 已提交
619
			++it;
M
Merge  
Michael Kolupaev 已提交
620
			break;
M
Michael Kolupaev 已提交
621
		}
M
Merge  
Michael Kolupaev 已提交
622
		res.push_back(*it);
M
Merge  
Michael Kolupaev 已提交
623
		(*it)->remove_time = time(0);
M
Merge  
Michael Kolupaev 已提交
624 625 626 627
		data_parts.erase(it++); /// Да, ++, а не --.
	}
	std::reverse(res.begin(), res.end()); /// Нужно получить куски в порядке возрастания.
	/// Пойдем вправо.
M
Merge  
Michael Kolupaev 已提交
628
	while (it != data_parts.end())
M
Merge  
Michael Kolupaev 已提交
629
	{
M
Merge  
Michael Kolupaev 已提交
630 631 632 633 634 635
		if (!part->contains(**it))
		{
			if ((*it)->name == part->name || (*it)->contains(*part))
				obsolete = true;
			break;
		}
M
Merge  
Michael Kolupaev 已提交
636
		res.push_back(*it);
M
Merge  
Michael Kolupaev 已提交
637
		(*it)->remove_time = time(0);
M
Merge  
Michael Kolupaev 已提交
638 639 640
		data_parts.erase(it++);
	}

M
Merge  
Michael Kolupaev 已提交
641 642 643 644 645 646 647 648
	if (obsolete)
	{
		LOG_WARNING(log, "Obsolete part " + part->name + " added");
	}
	else
	{
		data_parts.insert(part);
	}
649

M
Merge  
Michael Kolupaev 已提交
650
	all_data_parts.insert(part);
M
Merge  
Michael Kolupaev 已提交
651

M
Merge  
Michael Kolupaev 已提交
652 653 654 655 656 657 658
	if (out_transaction)
	{
		out_transaction->data = this;
		out_transaction->added_parts = res;
		out_transaction->removed_parts = DataPartsVector(1, part);
	}

M
Merge  
Michael Kolupaev 已提交
659
	return res;
M
Merge  
Michael Kolupaev 已提交
660 661
}

M
Merge  
Michael Kolupaev 已提交
662
void MergeTreeData::replaceParts(const DataPartsVector & remove, const DataPartsVector & add, bool clear_without_timeout)
M
Merge  
Michael Kolupaev 已提交
663 664 665 666 667 668 669
{
	LOG_TRACE(log, "Removing " << remove.size() << " parts and adding " << add.size() << " parts.");

	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);

	for (const DataPartPtr & part : remove)
	{
M
Merge  
Michael Kolupaev 已提交
670
		part->remove_time = clear_without_timeout ? 0 : time(0);
M
Merge  
Michael Kolupaev 已提交
671 672 673 674 675 676 677 678
		data_parts.erase(part);
	}
	for (const DataPartPtr & part : add)
	{
		data_parts.insert(part);
	}
}

M
Merge  
Michael Kolupaev 已提交
679
void MergeTreeData::renameAndDetachPart(DataPartPtr part, const String & prefix)
M
Merge  
Michael Kolupaev 已提交
680 681
{
	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
M
Merge  
Michael Kolupaev 已提交
682
	Poco::ScopedLock<Poco::FastMutex> lock_all(all_data_parts_mutex);
M
Merge  
Michael Kolupaev 已提交
683
	if (!all_data_parts.erase(part))
M
Merge  
Michael Kolupaev 已提交
684
		throw Exception("No such data part", ErrorCodes::NO_SUCH_DATA_PART);
M
Merge  
Michael Kolupaev 已提交
685
	data_parts.erase(part);
M
Merge  
Michael Kolupaev 已提交
686
	part->renameAddPrefix(prefix);
M
Merge  
Michael Kolupaev 已提交
687 688
}

M
Merge  
Michael Kolupaev 已提交
689 690 691 692 693 694
void MergeTreeData::deletePart(DataPartPtr part)
{
	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
	data_parts.erase(part);
}

M
Merge  
Michael Kolupaev 已提交
695 696 697 698 699 700 701
MergeTreeData::DataParts MergeTreeData::getDataParts()
{
	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);

	return data_parts;
}

M
Merge  
Michael Kolupaev 已提交
702 703 704 705 706 707 708
MergeTreeData::DataParts MergeTreeData::getAllDataParts()
{
	Poco::ScopedLock<Poco::FastMutex> lock(all_data_parts_mutex);

	return all_data_parts;
}

M
Merge  
Michael Kolupaev 已提交
709
size_t MergeTreeData::getMaxPartsCountForMonth()
M
Merge  
Michael Kolupaev 已提交
710 711 712
{
	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);

M
Merge  
Michael Kolupaev 已提交
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
	size_t res = 0;
	size_t cur_count = 0;
	DayNum_t cur_month = DayNum_t(0);

	for (const auto & part : data_parts)
	{
		if (part->left_month == cur_month)
		{
			++cur_count;
		}
		else
		{
			cur_month = part->left_month;
			cur_count = 1;
		}

		res = std::max(res, cur_count);
	}

	return res;
M
Merge  
Michael Kolupaev 已提交
733 734
}

M
Merge  
Michael Kolupaev 已提交
735 736 737 738 739 740 741
void MergeTreeData::delayInsertIfNeeded()
{
	size_t parts_count = getMaxPartsCountForMonth();
	if (parts_count > settings.parts_to_delay_insert)
	{
		double delay = std::pow(settings.insert_delay_step, parts_count - settings.parts_to_delay_insert);
		delay /= 1000;
742 743
		delay = std::min(delay, DBMS_MAX_DELAY_OF_INSERT);

M
Merge  
Michael Kolupaev 已提交
744 745 746 747 748 749
		LOG_INFO(log, "Delaying inserting block by "
			<< std::fixed << std::setprecision(4) << delay << "s because there are " << parts_count << " parts");
		std::this_thread::sleep_for(std::chrono::duration<double>(delay));
	}
}

M
Merge  
Michael Kolupaev 已提交
750
MergeTreeData::DataPartPtr MergeTreeData::getContainingPart(const String & part_name, bool including_inactive)
M
Merge  
Michael Kolupaev 已提交
751 752
{
	MutableDataPartPtr tmp_part(new DataPart(*this));
753
	ActiveDataPartSet::parsePartName(part_name, *tmp_part);
M
Merge  
Michael Kolupaev 已提交
754

M
Merge  
Michael Kolupaev 已提交
755
	Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
M
Merge  
Michael Kolupaev 已提交
756

M
Merge  
Michael Kolupaev 已提交
757 758 759 760 761 762 763
	if (including_inactive)
	{
		Poco::ScopedLock<Poco::FastMutex> lock(all_data_parts_mutex);
		DataParts::iterator it = all_data_parts.lower_bound(tmp_part);
		if (it != all_data_parts.end() && (*it)->name == part_name)
			return *it;
	}
M
Merge  
Michael Kolupaev 已提交
764 765

	/// Кусок может покрываться только предыдущим или следующим в data_parts.
M
Merge  
Michael Kolupaev 已提交
766
	DataParts::iterator it = data_parts.lower_bound(tmp_part);
M
Merge  
Michael Kolupaev 已提交
767

M
Merge  
Michael Kolupaev 已提交
768
	if (it != data_parts.end())
M
Merge  
Michael Kolupaev 已提交
769 770 771 772 773 774 775
	{
		if ((*it)->name == part_name)
			return *it;
		if ((*it)->contains(*tmp_part))
			return *it;
	}

M
Merge  
Michael Kolupaev 已提交
776
	if (it != data_parts.begin())
M
Merge  
Michael Kolupaev 已提交
777 778 779 780 781 782 783 784 785
	{
		--it;
		if ((*it)->contains(*tmp_part))
			return *it;
	}

	return nullptr;
}

786

M
Merge  
Michael Kolupaev 已提交
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
void MergeTreeData::DataPart::Checksums::Checksum::checkEqual(const Checksum & rhs, bool have_uncompressed, const String & name) const
{
	if (is_compressed && have_uncompressed)
	{
		if (!rhs.is_compressed)
			throw Exception("No uncompressed checksum for file " + name, ErrorCodes::CHECKSUM_DOESNT_MATCH);
		if (rhs.uncompressed_size != uncompressed_size)
			throw Exception("Unexpected size of file " + name + " in data part", ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
		if (rhs.uncompressed_hash != uncompressed_hash)
			throw Exception("Checksum mismatch for file " + name + " in data part", ErrorCodes::CHECKSUM_DOESNT_MATCH);
		return;
	}
	if (rhs.file_size != file_size)
		throw Exception("Unexpected size of file " + name + " in data part", ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
	if (rhs.file_hash != file_hash)
		throw Exception("Checksum mismatch for file " + name + " in data part", ErrorCodes::CHECKSUM_DOESNT_MATCH);
}

void MergeTreeData::DataPart::Checksums::Checksum::checkSize(const String & path) const
{
	Poco::File file(path);
	if (!file.exists())
		throw Exception(path + " doesn't exist", ErrorCodes::FILE_DOESNT_EXIST);
	size_t size = file.getSize();
	if (size != file_size)
		throw Exception(path + " has unexpected size: " + DB::toString(size) + " instead of " + DB::toString(file_size),
			ErrorCodes::BAD_SIZE_OF_FILE_IN_DATA_PART);
}

void MergeTreeData::DataPart::Checksums::checkEqual(const Checksums & rhs, bool have_uncompressed) const
817
{
M
Merge  
Michael Kolupaev 已提交
818
	for (const auto & it : rhs.files)
819 820 821
	{
		const String & name = it.first;

M
Merge  
Michael Kolupaev 已提交
822
		if (!files.count(name))
823 824 825
			throw Exception("Unexpected file " + name + " in data part", ErrorCodes::UNEXPECTED_FILE_IN_DATA_PART);
	}

M
Merge  
Michael Kolupaev 已提交
826
	for (const auto & it : files)
827 828 829
	{
		const String & name = it.first;

M
Merge  
Michael Kolupaev 已提交
830 831
		auto jt = rhs.files.find(name);
		if (jt == rhs.files.end())
832 833
			throw Exception("No file " + name + " in data part", ErrorCodes::NO_FILE_IN_DATA_PART);

M
Merge  
Michael Kolupaev 已提交
834 835 836
		it.second.checkEqual(jt->second, have_uncompressed, name);
	}
}
M
Merge  
Michael Kolupaev 已提交
837

M
Merge  
Michael Kolupaev 已提交
838 839 840 841 842 843
void MergeTreeData::DataPart::Checksums::checkSizes(const String & path) const
{
	for (const auto & it : files)
	{
		const String & name = it.first;
		it.second.checkSize(path + name);
844 845 846
	}
}

847
bool MergeTreeData::DataPart::Checksums::readText(ReadBuffer & in)
848
{
M
Merge  
Michael Kolupaev 已提交
849
	files.clear();
850 851
	size_t count;

M
Merge  
Michael Kolupaev 已提交
852 853 854 855 856
	DB::assertString("checksums format version: ", in);
	int format_version;
	DB::readText(format_version, in);
	if (format_version < 1 || format_version > 2)
		throw Exception("Bad checksums format version: " + DB::toString(format_version), ErrorCodes::UNKNOWN_FORMAT);
857 858
	if (format_version == 1)
		return false;
M
Merge  
Michael Kolupaev 已提交
859
	DB::assertString("\n", in);
860 861 862 863 864 865 866 867 868 869
	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);
M
Merge  
Michael Kolupaev 已提交
870
		DB::readText(sum.file_size, in);
871
		DB::assertString("\n\thash: ", in);
M
Merge  
Michael Kolupaev 已提交
872
		DB::readText(sum.file_hash.first, in);
873
		DB::assertString(" ", in);
M
Merge  
Michael Kolupaev 已提交
874
		DB::readText(sum.file_hash.second, in);
875 876 877
		DB::assertString("\n\tcompressed: ", in);
		DB::readText(sum.is_compressed, in);
		if (sum.is_compressed)
M
Merge  
Michael Kolupaev 已提交
878
		{
879 880 881 882 883 884
			DB::assertString("\n\tuncompressed size: ", in);
			DB::readText(sum.uncompressed_size, in);
			DB::assertString("\n\tuncompressed hash: ", in);
			DB::readText(sum.uncompressed_hash.first, in);
			DB::assertString(" ", in);
			DB::readText(sum.uncompressed_hash.second, in);
M
Merge  
Michael Kolupaev 已提交
885
		}
886
		DB::assertString("\n", in);
887

M
Merge  
Michael Kolupaev 已提交
888
		files.insert(std::make_pair(name, sum));
889
	}
890 891

	return true;
892 893 894 895
}

void MergeTreeData::DataPart::Checksums::writeText(WriteBuffer & out) const
{
M
Merge  
Michael Kolupaev 已提交
896
	DB::writeString("checksums format version: 2\n", out);
M
Merge  
Michael Kolupaev 已提交
897
	DB::writeText(files.size(), out);
898 899
	DB::writeString(" files:\n", out);

M
Merge  
Michael Kolupaev 已提交
900
	for (const auto & it : files)
901
	{
M
Merge  
Michael Kolupaev 已提交
902 903 904
		const String & name = it.first;
		const Checksum & sum = it.second;
		DB::writeString(name, out);
905
		DB::writeString("\n\tsize: ", out);
M
Merge  
Michael Kolupaev 已提交
906
		DB::writeText(sum.file_size, out);
907
		DB::writeString("\n\thash: ", out);
M
Merge  
Michael Kolupaev 已提交
908
		DB::writeText(sum.file_hash.first, out);
909
		DB::writeString(" ", out);
M
Merge  
Michael Kolupaev 已提交
910 911 912
		DB::writeText(sum.file_hash.second, out);
		DB::writeString("\n\tcompressed: ", out);
		DB::writeText(sum.is_compressed, out);
913
		DB::writeString("\n", out);
M
Merge  
Michael Kolupaev 已提交
914 915 916 917 918 919 920 921 922 923
		if (sum.is_compressed)
		{
			DB::writeString("\tuncompressed size: ", out);
			DB::writeText(sum.uncompressed_size, out);
			DB::writeString("\n\tuncompressed hash: ", out);
			DB::writeText(sum.uncompressed_hash.first, out);
			DB::writeString(" ", out);
			DB::writeText(sum.uncompressed_hash.second, out);
			DB::writeString("\n", out);
		}
924 925 926
	}
}

M
Merge  
Michael Kolupaev 已提交
927
}