ClickHouse/dbms/src/Interpreters/InterpreterRenameQuery.cpp

158 lines
5.5 KiB
C++
Raw Normal View History

2012-06-18 06:20:23 +00:00
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <DB/Common/escapeForFileName.h>
#include <DB/IO/ReadBufferFromFile.h>
#include <DB/IO/WriteBufferFromString.h>
#include <DB/IO/copyData.h>
#include <DB/Parsers/ASTRenameQuery.h>
2015-03-02 01:10:58 +00:00
#include <DB/Parsers/ASTCreateQuery.h>
2012-06-18 06:20:23 +00:00
#include <DB/Parsers/ParserCreateQuery.h>
#include <DB/Parsers/formatAST.h>
#include <DB/Parsers/parseQuery.h>
2012-06-18 06:20:23 +00:00
#include <DB/Interpreters/InterpreterRenameQuery.h>
namespace DB
{
InterpreterRenameQuery::InterpreterRenameQuery(ASTPtr query_ptr_, Context & context_)
: query_ptr(query_ptr_), context(context_)
{
}
struct RenameDescription
{
RenameDescription(const ASTRenameQuery::Element & elem, const String & path, const String & current_database) :
from_database_name(elem.from.database.empty() ? current_database : elem.from.database),
from_database_name_escaped(escapeForFileName(from_database_name)),
from_table_name(elem.from.table),
from_table_name_escaped(escapeForFileName(from_table_name)),
from_metadata_path(path + "metadata/" + from_database_name_escaped + "/"
+ (!from_table_name.empty() ? from_table_name_escaped + ".sql" : "")),
to_database_name(elem.to.database.empty() ? current_database : elem.to.database),
to_database_name_escaped(escapeForFileName(to_database_name)),
to_table_name(elem.to.table),
to_table_name_escaped(escapeForFileName(to_table_name)),
to_metadata_path(path + "metadata/" + to_database_name_escaped + "/"
+ (!to_table_name.empty() ? to_table_name_escaped + ".sql" : ""))
{}
String from_database_name;
String from_database_name_escaped;
String from_table_name;
String from_table_name_escaped;
String from_metadata_path;
String to_database_name;
String to_database_name_escaped;
String to_table_name;
String to_table_name_escaped;
String to_metadata_path;
};
2012-06-18 06:20:23 +00:00
void InterpreterRenameQuery::execute()
{
2012-08-02 17:33:31 +00:00
String path = context.getPath();
String current_database = context.getCurrentDatabase();
ASTRenameQuery & rename = typeid_cast<ASTRenameQuery &>(*query_ptr);
2012-06-18 06:20:23 +00:00
/** Если в процессе переименования произошла ошибка, то может быть переименована только часть таблиц,
* или состояние может стать неконсистентным. (Это имеет смысл исправить.)
*/
std::vector<RenameDescription> descriptions;
descriptions.reserve(rename.elements.size());
/// Для того, чтобы захватывать блокировки таблиц в одном и том же порядке в разных RENAME-ах.
struct UniqueTableName
{
String database_name;
String table_name;
UniqueTableName(const String & database_name_, const String & table_name_)
: database_name(database_name_), table_name(table_name_) {}
bool operator< (const UniqueTableName & rhs) const
{
return std::tie(database_name, table_name) < std::tie(rhs.database_name, rhs.table_name);
}
};
std::set<UniqueTableName> unique_tables;
for (const auto & elem : rename.elements)
2012-06-18 06:20:23 +00:00
{
descriptions.emplace_back(elem, path, current_database);
unique_tables.emplace(descriptions.back().from_database_name, descriptions.back().from_table_name);
}
2012-06-18 06:20:23 +00:00
std::vector<IStorage::TableFullWriteLockPtr> locks;
locks.reserve(unique_tables.size());
for (const auto & names : unique_tables)
if (auto table = context.tryGetTable(names.database_name, names.table_name))
locks.emplace_back(table->lockForAlter());
/** Все таблицы заблокированы. Теперь можно блокировать Context. Порядок важен, чтобы избежать deadlock-ов.
* Это обеспечивает атомарность всех указанных RENAME с точки зрения пользователя СУБД,
* но лишь в случаях, когда в процессе переименования не было исключений и сервер не падал.
*/
Poco::ScopedLock<Poco::Mutex> lock(context.getMutex());
2012-06-18 06:20:23 +00:00
for (const auto & elem : descriptions)
{
context.assertTableDoesntExist(elem.to_database_name, elem.to_table_name);
/// Уведомляем таблицу о том, что она переименовывается. Если таблица не поддерживает переименование - кинется исключение.
StoragePtr table = context.getTable(elem.from_database_name, elem.from_table_name);
2015-04-13 13:54:49 +00:00
try
{
table->rename(path + "data/" + elem.to_database_name_escaped + "/", elem.to_database_name,
elem.to_table_name);
}
catch (const Poco::Exception & e)
{
throw Exception{e};
}
2012-06-18 06:20:23 +00:00
/// Пишем новый файл с метаданными.
{
String create_query;
{
ReadBufferFromFile in(elem.from_metadata_path, 1024);
2012-06-18 06:20:23 +00:00
WriteBufferFromString out(create_query);
copyData(in, out);
}
ParserCreateQuery parser;
ASTPtr ast = parseQuery(parser, create_query.data(), create_query.data() + create_query.size(), "in file " + elem.from_metadata_path);
2012-06-18 06:20:23 +00:00
typeid_cast<ASTCreateQuery &>(*ast).table = elem.to_table_name;
2012-06-18 06:20:23 +00:00
Poco::FileOutputStream ostr(elem.to_metadata_path);
2012-06-18 06:20:23 +00:00
formatAST(*ast, ostr, 0, false);
ostr << '\n';
}
/// Переименовываем таблицу в контексте.
context.addTable(elem.to_database_name, elem.to_table_name,
context.detachTable(elem.from_database_name, elem.from_table_name));
2012-06-18 06:20:23 +00:00
/// Удаляем старый файл с метаданными.
Poco::File(elem.from_metadata_path).remove();
2012-06-18 06:20:23 +00:00
}
}
}