mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 15:42:02 +00:00
Move renameInCreateQuery() to a better place.
This commit is contained in:
parent
54d554f55d
commit
e8fc9cf476
@ -1,87 +0,0 @@
|
||||
#include <Backups/BackupRenamingConfig.h>
|
||||
#include <Parsers/ASTBackupQuery.h>
|
||||
#include <Interpreters/DatabaseCatalog.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
using Kind = ASTBackupQuery::Kind;
|
||||
using ElementType = ASTBackupQuery::ElementType;
|
||||
|
||||
void BackupRenamingConfig::setNewTableName(const DatabaseAndTableName & old_table_name, const DatabaseAndTableName & new_table_name)
|
||||
{
|
||||
old_to_new_table_names[old_table_name] = new_table_name;
|
||||
}
|
||||
|
||||
void BackupRenamingConfig::setNewDatabaseName(const String & old_database_name, const String & new_database_name)
|
||||
{
|
||||
old_to_new_database_names[old_database_name] = new_database_name;
|
||||
}
|
||||
|
||||
void BackupRenamingConfig::setFromBackupQuery(const ASTBackupQuery & backup_query, const String & current_database)
|
||||
{
|
||||
setFromBackupQueryElements(backup_query.elements, current_database);
|
||||
}
|
||||
|
||||
void BackupRenamingConfig::setFromBackupQueryElements(const ASTBackupQuery::Elements & backup_query_elements, const String & current_database)
|
||||
{
|
||||
for (const auto & element : backup_query_elements)
|
||||
{
|
||||
switch (element.type)
|
||||
{
|
||||
case ElementType::TABLE:
|
||||
{
|
||||
const String & table_name = element.name.second;
|
||||
String database_name = element.name.first;
|
||||
if (element.name_is_in_temp_db)
|
||||
database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (database_name.empty())
|
||||
database_name = current_database;
|
||||
|
||||
const String & new_table_name = element.new_name.second;
|
||||
String new_database_name = element.new_name.first;
|
||||
if (element.new_name_is_in_temp_db)
|
||||
new_database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (new_database_name.empty())
|
||||
new_database_name = current_database;
|
||||
|
||||
setNewTableName({database_name, table_name}, {new_database_name, new_table_name});
|
||||
break;
|
||||
}
|
||||
|
||||
case ASTBackupQuery::DATABASE:
|
||||
{
|
||||
String database_name = element.name.first;
|
||||
if (element.name_is_in_temp_db)
|
||||
database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
|
||||
String new_database_name = element.new_name.first;
|
||||
if (element.new_name_is_in_temp_db)
|
||||
new_database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
|
||||
setNewDatabaseName(database_name, new_database_name);
|
||||
break;
|
||||
}
|
||||
|
||||
case ASTBackupQuery::ALL_DATABASES: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseAndTableName BackupRenamingConfig::getNewTableName(const DatabaseAndTableName & old_table_name) const
|
||||
{
|
||||
auto it = old_to_new_table_names.find(old_table_name);
|
||||
if (it != old_to_new_table_names.end())
|
||||
return it->second;
|
||||
return {getNewDatabaseName(old_table_name.first), old_table_name.second};
|
||||
}
|
||||
|
||||
const String & BackupRenamingConfig::getNewDatabaseName(const String & old_database_name) const
|
||||
{
|
||||
auto it = old_to_new_database_names.find(old_database_name);
|
||||
if (it != old_to_new_database_names.end())
|
||||
return it->second;
|
||||
return old_database_name;
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
#include <Backups/BackupUtils.h>
|
||||
#include <Backups/BackupEntryFromMemory.h>
|
||||
#include <Backups/BackupRenamingConfig.h>
|
||||
#include <Backups/DDLRenamingVisitor.h>
|
||||
#include <Backups/IBackup.h>
|
||||
#include <Backups/renameInCreateQuery.h>
|
||||
#include <Common/escapeForFileName.h>
|
||||
#include <Access/Common/AccessFlags.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
@ -38,10 +37,8 @@ namespace
|
||||
/// Prepares internal structures for making backup entries.
|
||||
void prepare(const ASTBackupQuery::Elements & elements)
|
||||
{
|
||||
auto new_renaming_config = std::make_shared<BackupRenamingConfig>();
|
||||
String current_database = context->getCurrentDatabase();
|
||||
new_renaming_config->setFromBackupQueryElements(elements, current_database);
|
||||
renaming_config = new_renaming_config;
|
||||
renaming_settings.setFromBackupQuery(elements, current_database);
|
||||
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
@ -134,7 +131,7 @@ namespace
|
||||
database->getEngineName());
|
||||
|
||||
/// Check that we are not trying to backup the same table again.
|
||||
DatabaseAndTableName new_table_name = renaming_config->getNewTableName(table_name_);
|
||||
DatabaseAndTableName new_table_name = renaming_settings.getNewTableName(table_name_);
|
||||
if (tables.contains(new_table_name))
|
||||
{
|
||||
String message;
|
||||
@ -203,7 +200,7 @@ namespace
|
||||
context->checkAccess(AccessType::SHOW_DATABASES, database_name_);
|
||||
|
||||
/// Check that we are not trying to restore the same database again.
|
||||
String new_database_name = renaming_config->getNewDatabaseName(database_name_);
|
||||
String new_database_name = renaming_settings.getNewDatabaseName(database_name_);
|
||||
if (databases.contains(new_database_name) && databases[new_database_name].is_explicit)
|
||||
throw Exception(ErrorCodes::CANNOT_BACKUP_DATABASE, "Couldn't backup database {} twice", backQuoteIfNeed(new_database_name));
|
||||
|
||||
@ -248,7 +245,7 @@ namespace
|
||||
/// Do renaming in the create query according to the renaming config.
|
||||
std::shared_ptr<ASTCreateQuery> renameInCreateQuery(const ASTPtr & ast) const
|
||||
{
|
||||
return typeid_cast<std::shared_ptr<ASTCreateQuery>>(::DB::renameInCreateQuery(ast, renaming_config, context));
|
||||
return typeid_cast<std::shared_ptr<ASTCreateQuery>>(::DB::renameInCreateQuery(ast, context, renaming_settings));
|
||||
}
|
||||
|
||||
static bool isSystemOrTemporaryDatabase(const String & database_name)
|
||||
@ -292,7 +289,7 @@ namespace
|
||||
|
||||
ContextPtr context;
|
||||
BackupMutablePtr backup;
|
||||
BackupRenamingConfigPtr renaming_config;
|
||||
DDLRenamingSettings renaming_settings;
|
||||
std::map<String, CreateDatabaseInfo> databases;
|
||||
std::map<DatabaseAndTableName, CreateTableInfo> tables;
|
||||
};
|
||||
|
384
src/Backups/DDLRenamingVisitor.cpp
Normal file
384
src/Backups/DDLRenamingVisitor.cpp
Normal file
@ -0,0 +1,384 @@
|
||||
#include <Backups/DDLRenamingVisitor.h>
|
||||
#include <Interpreters/DatabaseCatalog.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Parsers/ASTBackupQuery.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int WRONG_DDL_RENAMING_SETTINGS;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Replaces names of tables and databases used in a CREATE query, which can be either CREATE TABLE or
|
||||
/// CREATE DICTIONARY or CREATE VIEW or CREATE TEMPORARY TABLE or CREATE DATABASE query.
|
||||
void visitCreateQuery(ASTCreateQuery & create, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if (create.table)
|
||||
{
|
||||
DatabaseAndTableName table_name;
|
||||
table_name.second = create.getTable();
|
||||
if (create.temporary)
|
||||
table_name.first = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (create.database)
|
||||
table_name.first = create.getDatabase();
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Database name specified in the CREATE TABLE query must not be empty");
|
||||
|
||||
table_name = data.renaming_settings.getNewTableName(table_name);
|
||||
|
||||
if (table_name.first == DatabaseCatalog::TEMPORARY_DATABASE)
|
||||
{
|
||||
create.temporary = true;
|
||||
create.setDatabase("");
|
||||
}
|
||||
else
|
||||
{
|
||||
create.temporary = false;
|
||||
create.setDatabase(table_name.first);
|
||||
}
|
||||
create.setTable(table_name.second);
|
||||
}
|
||||
else if (create.database)
|
||||
{
|
||||
String database_name = create.getDatabase();
|
||||
database_name = data.renaming_settings.getNewDatabaseName(database_name);
|
||||
create.setDatabase(database_name);
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Database name specified in the CREATE DATABASE query must not be empty");
|
||||
|
||||
create.uuid = UUIDHelpers::Nil;
|
||||
|
||||
if (!create.as_table.empty() && !create.as_database.empty())
|
||||
std::tie(create.as_database, create.as_table) = data.renaming_settings.getNewTableName({create.as_database, create.as_table});
|
||||
|
||||
if (!create.to_table_id.table_name.empty() && !create.to_table_id.database_name.empty())
|
||||
{
|
||||
auto to_table = data.renaming_settings.getNewTableName({create.to_table_id.database_name, create.to_table_id.table_name});
|
||||
create.to_table_id = StorageID{to_table.first, to_table.second};
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces names of a database and a table in a expression like `db`.`table`
|
||||
void visitTableExpression(ASTTableExpression & expr, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if (!expr.database_and_table_name)
|
||||
return;
|
||||
|
||||
ASTIdentifier * id = expr.database_and_table_name->as<ASTIdentifier>();
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
auto table_id = id->createTable();
|
||||
if (!table_id)
|
||||
return;
|
||||
|
||||
const String & db_name = table_id->getDatabaseName();
|
||||
const String & table_name = table_id->shortName();
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_settings.getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
expr.database_and_table_name = std::make_shared<ASTIdentifier>(Strings{new_db_name, new_table_name});
|
||||
expr.children.push_back(expr.database_and_table_name);
|
||||
}
|
||||
|
||||
/// Replaces a database's name passed via an argument of the function merge() or the table engine Merge.
|
||||
void visitFunctionMerge(ASTFunction & function, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if (!function.arguments)
|
||||
return;
|
||||
|
||||
/// The first argument is a database's name and we can rename it.
|
||||
/// The second argument is a regular expression and we can do nothing about it.
|
||||
auto & args = function.arguments->as<ASTExpressionList &>().children;
|
||||
size_t db_name_arg_index = 0;
|
||||
if (args.size() <= db_name_arg_index)
|
||||
return;
|
||||
|
||||
String db_name = evaluateConstantExpressionForDatabaseName(args[db_name_arg_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
if (db_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name = data.renaming_settings.getNewDatabaseName(db_name);
|
||||
if (new_db_name == db_name)
|
||||
return;
|
||||
args[db_name_arg_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
}
|
||||
|
||||
/// Replaces names of a table and a database passed via arguments of the function remote() or cluster() or the table engine Distributed.
|
||||
void visitFunctionRemote(ASTFunction & function, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if (!function.arguments)
|
||||
return;
|
||||
|
||||
/// The first argument is an address or cluster's name, so we skip it.
|
||||
/// The second argument can be either 'db.name' or just 'db' followed by the third argument 'table'.
|
||||
auto & args = function.arguments->as<ASTExpressionList &>().children;
|
||||
|
||||
const auto * second_arg_as_function = args[1]->as<ASTFunction>();
|
||||
if (second_arg_as_function && TableFunctionFactory::instance().isTableFunctionName(second_arg_as_function->name))
|
||||
return;
|
||||
|
||||
size_t db_name_index = 1;
|
||||
if (args.size() <= db_name_index)
|
||||
return;
|
||||
|
||||
String name = evaluateConstantExpressionForDatabaseName(args[db_name_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
size_t table_name_index = static_cast<size_t>(-1);
|
||||
|
||||
QualifiedTableName qualified_name;
|
||||
|
||||
if (function.name == "Distributed")
|
||||
qualified_name.table = name;
|
||||
else
|
||||
qualified_name = QualifiedTableName::parseFromString(name);
|
||||
|
||||
if (qualified_name.database.empty())
|
||||
{
|
||||
std::swap(qualified_name.database, qualified_name.table);
|
||||
table_name_index = 2;
|
||||
if (args.size() <= table_name_index)
|
||||
return;
|
||||
qualified_name.table = evaluateConstantExpressionForDatabaseName(args[table_name_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
}
|
||||
|
||||
const String & db_name = qualified_name.database;
|
||||
const String & table_name = qualified_name.table;
|
||||
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_settings.getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
if (table_name_index != static_cast<size_t>(-1))
|
||||
{
|
||||
if (new_db_name != db_name)
|
||||
args[db_name_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
if (new_table_name != table_name)
|
||||
args[table_name_index] = std::make_shared<ASTLiteral>(new_table_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[db_name_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
args.insert(args.begin() + db_name_index + 1, std::make_shared<ASTLiteral>(new_table_name));
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces names of tables and databases used in arguments of a table function or a table engine.
|
||||
void visitFunction(ASTFunction & function, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if ((function.name == "merge") || (function.name == "Merge"))
|
||||
{
|
||||
visitFunctionMerge(function, data);
|
||||
}
|
||||
else if ((function.name == "remote") || (function.name == "remoteSecure") || (function.name == "cluster") ||
|
||||
(function.name == "clusterAllReplicas") || (function.name == "Distributed"))
|
||||
{
|
||||
visitFunctionRemote(function, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces names of a table and a database used in source parameters of a dictionary.
|
||||
void visitDictionary(ASTDictionary & dictionary, const DDLRenamingVisitor::Data & data)
|
||||
{
|
||||
if (!dictionary.source || dictionary.source->name != "clickhouse" || !dictionary.source->elements)
|
||||
return;
|
||||
|
||||
auto & elements = dictionary.source->elements->as<ASTExpressionList &>().children;
|
||||
String db_name, table_name;
|
||||
size_t db_name_index = static_cast<size_t>(-1);
|
||||
size_t table_name_index = static_cast<size_t>(-1);
|
||||
|
||||
for (size_t i = 0; i != elements.size(); ++i)
|
||||
{
|
||||
auto & pair = elements[i]->as<ASTPair &>();
|
||||
if (pair.first == "db")
|
||||
{
|
||||
if (db_name_index != static_cast<size_t>(-1))
|
||||
return;
|
||||
db_name = pair.second->as<ASTLiteral &>().value.safeGet<String>();
|
||||
db_name_index = i;
|
||||
}
|
||||
else if (pair.first == "table")
|
||||
{
|
||||
if (table_name_index != static_cast<size_t>(-1))
|
||||
return;
|
||||
table_name = pair.second->as<ASTLiteral &>().value.safeGet<String>();
|
||||
table_name_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_settings.getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
if (new_db_name != db_name)
|
||||
{
|
||||
auto & pair = elements[db_name_index]->as<ASTPair &>();
|
||||
pair.replace(pair.second, std::make_shared<ASTLiteral>(new_db_name));
|
||||
}
|
||||
if (new_table_name != table_name)
|
||||
{
|
||||
auto & pair = elements[table_name_index]->as<ASTPair &>();
|
||||
pair.replace(pair.second, std::make_shared<ASTLiteral>(new_table_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DDLRenamingSettings::setNewTableName(const DatabaseAndTableName & old_table_name, const DatabaseAndTableName & new_table_name)
|
||||
{
|
||||
auto it = old_to_new_table_names.find(old_table_name);
|
||||
if ((it != old_to_new_table_names.end()))
|
||||
{
|
||||
if (it->second == new_table_name)
|
||||
return;
|
||||
throw Exception(ErrorCodes::WRONG_DDL_RENAMING_SETTINGS, "Wrong renaming: it's specified that table {}.{} should be renamed to {}.{} and to {}.{} at the same time",
|
||||
backQuoteIfNeed(old_table_name.first), backQuoteIfNeed(old_table_name.second),
|
||||
backQuoteIfNeed(it->second.first), backQuoteIfNeed(it->second.second),
|
||||
backQuoteIfNeed(new_table_name.first), backQuoteIfNeed(new_table_name.second));
|
||||
}
|
||||
old_to_new_table_names[old_table_name] = new_table_name;
|
||||
}
|
||||
|
||||
void DDLRenamingSettings::setNewDatabaseName(const String & old_database_name, const String & new_database_name)
|
||||
{
|
||||
auto it = old_to_new_database_names.find(old_database_name);
|
||||
if ((it != old_to_new_database_names.end()))
|
||||
{
|
||||
if (it->second == new_database_name)
|
||||
return;
|
||||
throw Exception(ErrorCodes::WRONG_DDL_RENAMING_SETTINGS, "Wrong renaming: it's specified that database {} should be renamed to {} and to {} at the same time",
|
||||
backQuoteIfNeed(old_database_name), backQuoteIfNeed(it->second), backQuoteIfNeed(new_database_name));
|
||||
}
|
||||
old_to_new_database_names[old_database_name] = new_database_name;
|
||||
}
|
||||
|
||||
void DDLRenamingSettings::setFromBackupQuery(const ASTBackupQuery & backup_query, const String & current_database)
|
||||
{
|
||||
setFromBackupQuery(backup_query.elements, current_database);
|
||||
}
|
||||
|
||||
void DDLRenamingSettings::setFromBackupQuery(const ASTBackupQuery::Elements & backup_query_elements, const String & current_database)
|
||||
{
|
||||
old_to_new_table_names.clear();
|
||||
old_to_new_database_names.clear();
|
||||
|
||||
using ElementType = ASTBackupQuery::ElementType;
|
||||
|
||||
for (const auto & element : backup_query_elements)
|
||||
{
|
||||
switch (element.type)
|
||||
{
|
||||
case ElementType::TABLE:
|
||||
{
|
||||
const String & table_name = element.name.second;
|
||||
String database_name = element.name.first;
|
||||
if (element.name_is_in_temp_db)
|
||||
database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (database_name.empty())
|
||||
database_name = current_database;
|
||||
|
||||
const String & new_table_name = element.new_name.second;
|
||||
String new_database_name = element.new_name.first;
|
||||
if (element.new_name_is_in_temp_db)
|
||||
new_database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (new_database_name.empty())
|
||||
new_database_name = current_database;
|
||||
|
||||
setNewTableName({database_name, table_name}, {new_database_name, new_table_name});
|
||||
break;
|
||||
}
|
||||
|
||||
case ASTBackupQuery::DATABASE:
|
||||
{
|
||||
String database_name = element.name.first;
|
||||
if (element.name_is_in_temp_db)
|
||||
database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
|
||||
String new_database_name = element.new_name.first;
|
||||
if (element.new_name_is_in_temp_db)
|
||||
new_database_name = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
|
||||
setNewDatabaseName(database_name, new_database_name);
|
||||
break;
|
||||
}
|
||||
|
||||
case ASTBackupQuery::ALL_DATABASES: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseAndTableName DDLRenamingSettings::getNewTableName(const DatabaseAndTableName & old_table_name) const
|
||||
{
|
||||
auto it = old_to_new_table_names.find(old_table_name);
|
||||
if (it != old_to_new_table_names.end())
|
||||
return it->second;
|
||||
return {getNewDatabaseName(old_table_name.first), old_table_name.second};
|
||||
}
|
||||
|
||||
const String & DDLRenamingSettings::getNewDatabaseName(const String & old_database_name) const
|
||||
{
|
||||
auto it = old_to_new_database_names.find(old_database_name);
|
||||
if (it != old_to_new_database_names.end())
|
||||
return it->second;
|
||||
return old_database_name;
|
||||
}
|
||||
|
||||
|
||||
bool DDLRenamingVisitor::needChildVisit(ASTPtr &, const ASTPtr &) { return true; }
|
||||
|
||||
void DDLRenamingVisitor::visit(ASTPtr & ast, const Data & data)
|
||||
{
|
||||
if (auto * create = ast->as<ASTCreateQuery>())
|
||||
visitCreateQuery(*create, data);
|
||||
else if (auto * expr = ast->as<ASTTableExpression>())
|
||||
visitTableExpression(*expr, data);
|
||||
else if (auto * function = ast->as<ASTFunction>())
|
||||
visitFunction(*function, data);
|
||||
else if (auto * dictionary = ast->as<ASTDictionary>())
|
||||
visitDictionary(*dictionary, data);
|
||||
}
|
||||
|
||||
ASTPtr renameInCreateQuery(const ASTPtr & ast, const ContextPtr & global_context, const DDLRenamingSettings & renaming_settings)
|
||||
{
|
||||
auto new_ast = ast->clone();
|
||||
try
|
||||
{
|
||||
DDLRenamingVisitor::Data data{renaming_settings, global_context};
|
||||
DDLRenamingVisitor::Visitor{data}.visit(new_ast);
|
||||
return new_ast;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException("Backup", "Error while renaming in AST");
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/ASTBackupQuery.h>
|
||||
#include <Core/Types.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Parsers/ASTBackupQuery.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
using DatabaseAndTableName = std::pair<String, String>;
|
||||
class IAST;
|
||||
using ASTPtr = std::shared_ptr<IAST>;
|
||||
class Context;
|
||||
using ContextPtr = std::shared_ptr<const Context>;
|
||||
|
||||
/// Keeps information about renamings of databases or tables being processed
|
||||
/// while we're making a backup or while we're restoring from a backup.
|
||||
class BackupRenamingConfig
|
||||
class DDLRenamingSettings
|
||||
{
|
||||
public:
|
||||
BackupRenamingConfig() = default;
|
||||
DDLRenamingSettings() = default;
|
||||
|
||||
void setNewTableName(const DatabaseAndTableName & old_table_name, const DatabaseAndTableName & new_table_name);
|
||||
void setNewDatabaseName(const String & old_database_name, const String & new_database_name);
|
||||
|
||||
void setFromBackupQuery(const ASTBackupQuery & backup_query, const String & current_database);
|
||||
void setFromBackupQueryElements(const ASTBackupQuery::Elements & backup_query_elements, const String & current_database);
|
||||
void setFromBackupQuery(const ASTBackupQuery::Elements & backup_query_elements, const String & current_database);
|
||||
|
||||
/// Changes names according to the renaming.
|
||||
DatabaseAndTableName getNewTableName(const DatabaseAndTableName & old_table_name) const;
|
||||
@ -32,6 +38,24 @@ private:
|
||||
std::unordered_map<String, String> old_to_new_database_names;
|
||||
};
|
||||
|
||||
using BackupRenamingConfigPtr = std::shared_ptr<const BackupRenamingConfig>;
|
||||
|
||||
/// Changes names in AST according to the renaming settings.
|
||||
ASTPtr renameInCreateQuery(const ASTPtr & ast, const ContextPtr & global_context, const DDLRenamingSettings & config);
|
||||
|
||||
/// Visits ASTCreateQuery and changes names of tables and databases according to passed DDLRenamingConfig.
|
||||
class DDLRenamingVisitor
|
||||
{
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
const DDLRenamingSettings & renaming_settings;
|
||||
ContextPtr context;
|
||||
};
|
||||
|
||||
using Visitor = InDepthNodeVisitor<DDLRenamingVisitor, false>;
|
||||
|
||||
static bool needChildVisit(ASTPtr &, const ASTPtr &);
|
||||
static void visit(ASTPtr & ast, const Data & data);
|
||||
};
|
||||
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
#include <Backups/BackupUtils.h>
|
||||
#include <Backups/BackupRenamingConfig.h>
|
||||
#include <Backups/DDLRenamingVisitor.h>
|
||||
#include <Backups/IBackup.h>
|
||||
#include <Backups/IBackupEntry.h>
|
||||
#include <Backups/IRestoreFromBackupTask.h>
|
||||
#include <Backups/hasCompatibleDataToRestoreTable.h>
|
||||
#include <Backups/renameInCreateQuery.h>
|
||||
#include <Common/escapeForFileName.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
@ -175,10 +174,8 @@ namespace
|
||||
/// Prepares internal structures for making tasks for restoring.
|
||||
void prepare(const ASTBackupQuery::Elements & elements)
|
||||
{
|
||||
auto new_renaming_config = std::make_shared<BackupRenamingConfig>();
|
||||
String current_database = context->getCurrentDatabase();
|
||||
new_renaming_config->setFromBackupQueryElements(elements, current_database);
|
||||
renaming_config = new_renaming_config;
|
||||
renaming_settings.setFromBackupQuery(elements, current_database);
|
||||
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
@ -238,7 +235,7 @@ namespace
|
||||
void prepareToRestoreTable(const DatabaseAndTableName & table_name_, const ASTs & partitions_)
|
||||
{
|
||||
/// Check that we are not trying to restore the same table again.
|
||||
DatabaseAndTableName new_table_name = renaming_config->getNewTableName(table_name_);
|
||||
DatabaseAndTableName new_table_name = renaming_settings.getNewTableName(table_name_);
|
||||
if (tables.contains(new_table_name))
|
||||
{
|
||||
String message;
|
||||
@ -310,7 +307,7 @@ namespace
|
||||
void prepareToRestoreDatabase(const String & database_name_, const std::set<String> & except_list_)
|
||||
{
|
||||
/// Check that we are not trying to restore the same database again.
|
||||
String new_database_name = renaming_config->getNewDatabaseName(database_name_);
|
||||
String new_database_name = renaming_settings.getNewDatabaseName(database_name_);
|
||||
if (databases.contains(new_database_name) && databases[new_database_name].is_explicit)
|
||||
throw Exception(ErrorCodes::CANNOT_RESTORE_DATABASE, "Couldn't restore database {} twice", backQuoteIfNeed(new_database_name));
|
||||
|
||||
@ -407,7 +404,7 @@ namespace
|
||||
/// Do renaming in the create query according to the renaming config.
|
||||
std::shared_ptr<ASTCreateQuery> renameInCreateQuery(const ASTPtr & ast) const
|
||||
{
|
||||
return typeid_cast<std::shared_ptr<ASTCreateQuery>>(::DB::renameInCreateQuery(ast, renaming_config, context));
|
||||
return typeid_cast<std::shared_ptr<ASTCreateQuery>>(::DB::renameInCreateQuery(ast, context, renaming_settings));
|
||||
}
|
||||
|
||||
static bool isSystemOrTemporaryDatabase(const String & database_name)
|
||||
@ -442,7 +439,7 @@ namespace
|
||||
|
||||
ContextMutablePtr context;
|
||||
BackupPtr backup;
|
||||
BackupRenamingConfigPtr renaming_config;
|
||||
DDLRenamingSettings renaming_settings;
|
||||
std::map<String, CreateDatabaseInfo> databases;
|
||||
std::map<DatabaseAndTableName, CreateTableInfo> tables;
|
||||
};
|
||||
|
@ -1,296 +0,0 @@
|
||||
#include <Backups/BackupRenamingConfig.h>
|
||||
#include <Backups/renameInCreateQuery.h>
|
||||
#include <Interpreters/DatabaseCatalog.h>
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class RenameInCreateQueryTransformMatcher
|
||||
{
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
BackupRenamingConfigPtr renaming_config;
|
||||
ContextPtr context;
|
||||
};
|
||||
|
||||
static bool needChildVisit(ASTPtr &, const ASTPtr &) { return true; }
|
||||
|
||||
static void visit(ASTPtr & ast, const Data & data)
|
||||
{
|
||||
if (auto * create = ast->as<ASTCreateQuery>())
|
||||
visitCreateQuery(*create, data);
|
||||
else if (auto * expr = ast->as<ASTTableExpression>())
|
||||
visitTableExpression(*expr, data);
|
||||
else if (auto * function = ast->as<ASTFunction>())
|
||||
visitFunction(*function, data);
|
||||
else if (auto * dictionary = ast->as<ASTDictionary>())
|
||||
visitDictionary(*dictionary, data);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Replaces names of tables and databases used in a CREATE query, which can be either CREATE TABLE or
|
||||
/// CREATE DICTIONARY or CREATE VIEW or CREATE TEMPORARY TABLE or CREATE DATABASE query.
|
||||
static void visitCreateQuery(ASTCreateQuery & create, const Data & data)
|
||||
{
|
||||
if (create.table)
|
||||
{
|
||||
DatabaseAndTableName table_name;
|
||||
table_name.second = create.getTable();
|
||||
if (create.temporary)
|
||||
table_name.first = DatabaseCatalog::TEMPORARY_DATABASE;
|
||||
else if (create.database)
|
||||
table_name.first = create.getDatabase();
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Database name specified in the CREATE TABLE query must not be empty");
|
||||
|
||||
table_name = data.renaming_config->getNewTableName(table_name);
|
||||
|
||||
if (table_name.first == DatabaseCatalog::TEMPORARY_DATABASE)
|
||||
{
|
||||
create.temporary = true;
|
||||
create.setDatabase("");
|
||||
}
|
||||
else
|
||||
{
|
||||
create.temporary = false;
|
||||
create.setDatabase(table_name.first);
|
||||
}
|
||||
create.setTable(table_name.second);
|
||||
}
|
||||
else if (create.database)
|
||||
{
|
||||
String database_name = create.getDatabase();
|
||||
database_name = data.renaming_config->getNewDatabaseName(database_name);
|
||||
create.setDatabase(database_name);
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Database name specified in the CREATE DATABASE query must not be empty");
|
||||
|
||||
create.uuid = UUIDHelpers::Nil;
|
||||
|
||||
if (!create.as_table.empty() && !create.as_database.empty())
|
||||
std::tie(create.as_database, create.as_table) = data.renaming_config->getNewTableName({create.as_database, create.as_table});
|
||||
|
||||
if (!create.to_table_id.table_name.empty() && !create.to_table_id.database_name.empty())
|
||||
{
|
||||
auto to_table = data.renaming_config->getNewTableName({create.to_table_id.database_name, create.to_table_id.table_name});
|
||||
create.to_table_id = StorageID{to_table.first, to_table.second};
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces names of a database and a table in a expression like `db`.`table`
|
||||
static void visitTableExpression(ASTTableExpression & expr, const Data & data)
|
||||
{
|
||||
if (!expr.database_and_table_name)
|
||||
return;
|
||||
|
||||
ASTIdentifier * id = expr.database_and_table_name->as<ASTIdentifier>();
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
auto table_id = id->createTable();
|
||||
if (!table_id)
|
||||
return;
|
||||
|
||||
const String & db_name = table_id->getDatabaseName();
|
||||
const String & table_name = table_id->shortName();
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_config->getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
expr.database_and_table_name = std::make_shared<ASTIdentifier>(Strings{new_db_name, new_table_name});
|
||||
expr.children.push_back(expr.database_and_table_name);
|
||||
}
|
||||
|
||||
/// Replaces names of tables and databases used in arguments of a table function or a table engine.
|
||||
static void visitFunction(ASTFunction & function, const Data & data)
|
||||
{
|
||||
if ((function.name == "merge") || (function.name == "Merge"))
|
||||
{
|
||||
visitFunctionMerge(function, data);
|
||||
}
|
||||
else if ((function.name == "remote") || (function.name == "remoteSecure") || (function.name == "cluster") ||
|
||||
(function.name == "clusterAllReplicas") || (function.name == "Distributed"))
|
||||
{
|
||||
visitFunctionRemote(function, data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces a database's name passed via an argument of the function merge() or the table engine Merge.
|
||||
static void visitFunctionMerge(ASTFunction & function, const Data & data)
|
||||
{
|
||||
if (!function.arguments)
|
||||
return;
|
||||
|
||||
/// The first argument is a database's name and we can rename it.
|
||||
/// The second argument is a regular expression and we can do nothing about it.
|
||||
auto & args = function.arguments->as<ASTExpressionList &>().children;
|
||||
size_t db_name_arg_index = 0;
|
||||
if (args.size() <= db_name_arg_index)
|
||||
return;
|
||||
|
||||
String db_name = evaluateConstantExpressionForDatabaseName(args[db_name_arg_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
if (db_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name = data.renaming_config->getNewDatabaseName(db_name);
|
||||
if (new_db_name == db_name)
|
||||
return;
|
||||
args[db_name_arg_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
}
|
||||
|
||||
/// Replaces names of a table and a database passed via arguments of the function remote() or cluster() or the table engine Distributed.
|
||||
static void visitFunctionRemote(ASTFunction & function, const Data & data)
|
||||
{
|
||||
if (!function.arguments)
|
||||
return;
|
||||
|
||||
/// The first argument is an address or cluster's name, so we skip it.
|
||||
/// The second argument can be either 'db.name' or just 'db' followed by the third argument 'table'.
|
||||
auto & args = function.arguments->as<ASTExpressionList &>().children;
|
||||
|
||||
const auto * second_arg_as_function = args[1]->as<ASTFunction>();
|
||||
if (second_arg_as_function && TableFunctionFactory::instance().isTableFunctionName(second_arg_as_function->name))
|
||||
return;
|
||||
|
||||
size_t db_name_index = 1;
|
||||
if (args.size() <= db_name_index)
|
||||
return;
|
||||
|
||||
String name = evaluateConstantExpressionForDatabaseName(args[db_name_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
size_t table_name_index = static_cast<size_t>(-1);
|
||||
|
||||
QualifiedTableName qualified_name;
|
||||
|
||||
if (function.name == "Distributed")
|
||||
qualified_name.table = name;
|
||||
else
|
||||
qualified_name = QualifiedTableName::parseFromString(name);
|
||||
|
||||
if (qualified_name.database.empty())
|
||||
{
|
||||
std::swap(qualified_name.database, qualified_name.table);
|
||||
table_name_index = 2;
|
||||
if (args.size() <= table_name_index)
|
||||
return;
|
||||
qualified_name.table = evaluateConstantExpressionForDatabaseName(args[table_name_index], data.context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
}
|
||||
|
||||
const String & db_name = qualified_name.database;
|
||||
const String & table_name = qualified_name.table;
|
||||
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_config->getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
if (table_name_index != static_cast<size_t>(-1))
|
||||
{
|
||||
if (new_db_name != db_name)
|
||||
args[db_name_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
if (new_table_name != table_name)
|
||||
args[table_name_index] = std::make_shared<ASTLiteral>(new_table_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
args[db_name_index] = std::make_shared<ASTLiteral>(new_db_name);
|
||||
args.insert(args.begin() + db_name_index + 1, std::make_shared<ASTLiteral>(new_table_name));
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces names of a table and a database used in source parameters of a dictionary.
|
||||
static void visitDictionary(ASTDictionary & dictionary, const Data & data)
|
||||
{
|
||||
if (!dictionary.source || dictionary.source->name != "clickhouse" || !dictionary.source->elements)
|
||||
return;
|
||||
|
||||
auto & elements = dictionary.source->elements->as<ASTExpressionList &>().children;
|
||||
String db_name, table_name;
|
||||
size_t db_name_index = static_cast<size_t>(-1);
|
||||
size_t table_name_index = static_cast<size_t>(-1);
|
||||
|
||||
for (size_t i = 0; i != elements.size(); ++i)
|
||||
{
|
||||
auto & pair = elements[i]->as<ASTPair &>();
|
||||
if (pair.first == "db")
|
||||
{
|
||||
if (db_name_index != static_cast<size_t>(-1))
|
||||
return;
|
||||
db_name = pair.second->as<ASTLiteral &>().value.safeGet<String>();
|
||||
db_name_index = i;
|
||||
}
|
||||
else if (pair.first == "table")
|
||||
{
|
||||
if (table_name_index != static_cast<size_t>(-1))
|
||||
return;
|
||||
table_name = pair.second->as<ASTLiteral &>().value.safeGet<String>();
|
||||
table_name_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (db_name.empty() || table_name.empty())
|
||||
return;
|
||||
|
||||
String new_db_name, new_table_name;
|
||||
std::tie(new_db_name, new_table_name) = data.renaming_config->getNewTableName({db_name, table_name});
|
||||
if ((new_db_name == db_name) && (new_table_name == table_name))
|
||||
return;
|
||||
|
||||
if (new_db_name != db_name)
|
||||
{
|
||||
auto & pair = elements[db_name_index]->as<ASTPair &>();
|
||||
pair.replace(pair.second, std::make_shared<ASTLiteral>(new_db_name));
|
||||
}
|
||||
if (new_table_name != table_name)
|
||||
{
|
||||
auto & pair = elements[table_name_index]->as<ASTPair &>();
|
||||
pair.replace(pair.second, std::make_shared<ASTLiteral>(new_table_name));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using RenameInCreateQueryTransformVisitor = InDepthNodeVisitor<RenameInCreateQueryTransformMatcher, false>;
|
||||
}
|
||||
|
||||
|
||||
ASTPtr renameInCreateQuery(const ASTPtr & ast, const BackupRenamingConfigPtr & renaming_config, const ContextPtr & context)
|
||||
{
|
||||
auto new_ast = ast->clone();
|
||||
try
|
||||
{
|
||||
RenameInCreateQueryTransformVisitor::Data data{renaming_config, context};
|
||||
RenameInCreateQueryTransformVisitor{data}.visit(new_ast);
|
||||
return new_ast;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException("Backup", "Error while renaming in AST");
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class IAST;
|
||||
using ASTPtr = std::shared_ptr<IAST>;
|
||||
class Context;
|
||||
using ContextPtr = std::shared_ptr<const Context>;
|
||||
class BackupRenamingConfig;
|
||||
using BackupRenamingConfigPtr = std::shared_ptr<const BackupRenamingConfig>;
|
||||
|
||||
/// Changes names in AST according to the renaming settings.
|
||||
ASTPtr renameInCreateQuery(const ASTPtr & ast, const BackupRenamingConfigPtr & renaming_config, const ContextPtr & context);
|
||||
}
|
@ -617,6 +617,7 @@
|
||||
M(646, RBAC_VERSION_IS_TOO_NEW) \
|
||||
M(647, CANNOT_BACKUP_DATABASE) \
|
||||
M(648, CANNOT_BACKUP_TABLE) \
|
||||
M(649, WRONG_DDL_RENAMING_SETTINGS) \
|
||||
\
|
||||
M(999, KEEPER_EXCEPTION) \
|
||||
M(1000, POCO_EXCEPTION) \
|
||||
|
Loading…
Reference in New Issue
Block a user