2020-02-03 12:54:36 +00:00
|
|
|
#include <Interpreters/DatabaseCatalog.h>
|
|
|
|
#include <Interpreters/Context.h>
|
|
|
|
#include <Interpreters/loadMetadata.h>
|
2020-03-10 19:36:17 +00:00
|
|
|
#include <Storages/IStorage.h>
|
2020-02-03 12:54:36 +00:00
|
|
|
#include <Databases/IDatabase.h>
|
|
|
|
#include <Databases/DatabaseMemory.h>
|
2020-03-19 21:14:52 +00:00
|
|
|
#include <Databases/DatabaseAtomic.h>
|
2020-02-13 21:00:03 +00:00
|
|
|
#include <Poco/File.h>
|
2020-02-17 19:28:25 +00:00
|
|
|
#include <Common/quoteString.h>
|
2020-03-10 19:36:17 +00:00
|
|
|
#include <Storages/StorageMemory.h>
|
2020-03-19 21:14:52 +00:00
|
|
|
#include <Core/BackgroundSchedulePool.h>
|
|
|
|
#include <Parsers/formatAST.h>
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
|
|
#include <IO/ReadHelpers.h>
|
|
|
|
#include <Poco/DirectoryIterator.h>
|
2020-02-03 12:54:36 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int UNKNOWN_DATABASE;
|
|
|
|
extern const int UNKNOWN_TABLE;
|
|
|
|
extern const int TABLE_ALREADY_EXISTS;
|
|
|
|
extern const int DATABASE_ALREADY_EXISTS;
|
2020-02-13 21:00:03 +00:00
|
|
|
extern const int DATABASE_NOT_EMPTY;
|
2020-03-03 19:53:18 +00:00
|
|
|
extern const int DATABASE_ACCESS_DENIED;
|
2020-03-12 12:16:16 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 19:36:17 +00:00
|
|
|
TemporaryTableHolder::TemporaryTableHolder(const Context & context_,
|
|
|
|
const TemporaryTableHolder::Creator & creator, const ASTPtr & query)
|
2020-03-13 15:41:36 +00:00
|
|
|
: global_context(&context_.getGlobalContext())
|
|
|
|
, temporary_tables(DatabaseCatalog::instance().getDatabaseForTemporaryTables().get())
|
2020-03-10 19:36:17 +00:00
|
|
|
{
|
|
|
|
ASTPtr original_create;
|
|
|
|
ASTCreateQuery * create = dynamic_cast<ASTCreateQuery *>(query.get());
|
|
|
|
String global_name;
|
|
|
|
if (query)
|
|
|
|
{
|
|
|
|
original_create = create->clone();
|
|
|
|
if (create->uuid == UUIDHelpers::Nil)
|
|
|
|
create->uuid = UUIDHelpers::generateV4();
|
|
|
|
id = create->uuid;
|
|
|
|
create->table = "_tmp_" + toString(id);
|
|
|
|
global_name = create->table;
|
|
|
|
create->database = DatabaseCatalog::TEMPORARY_DATABASE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
id = UUIDHelpers::generateV4();
|
|
|
|
global_name = "_tmp_" + toString(id);
|
|
|
|
}
|
|
|
|
auto table_id = StorageID(DatabaseCatalog::TEMPORARY_DATABASE, global_name, id);
|
|
|
|
auto table = creator(table_id);
|
2020-03-13 15:41:36 +00:00
|
|
|
temporary_tables->createTable(*global_context, global_name, table, original_create);
|
2020-03-10 19:36:17 +00:00
|
|
|
table->startup();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TemporaryTableHolder::TemporaryTableHolder(const Context & context_, const ColumnsDescription & columns, const ASTPtr & query)
|
|
|
|
: TemporaryTableHolder
|
|
|
|
(
|
|
|
|
context_,
|
|
|
|
[&](const StorageID & table_id)
|
|
|
|
{
|
|
|
|
return StorageMemory::create(table_id, ColumnsDescription{columns}, ConstraintsDescription{});
|
|
|
|
},
|
|
|
|
query
|
|
|
|
)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TemporaryTableHolder::TemporaryTableHolder(TemporaryTableHolder && rhs)
|
2020-03-13 15:41:36 +00:00
|
|
|
: global_context(rhs.global_context), temporary_tables(rhs.temporary_tables), id(rhs.id)
|
2020-03-10 19:36:17 +00:00
|
|
|
{
|
|
|
|
rhs.id = UUIDHelpers::Nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
TemporaryTableHolder & TemporaryTableHolder::operator = (TemporaryTableHolder && rhs)
|
|
|
|
{
|
|
|
|
id = rhs.id;
|
|
|
|
rhs.id = UUIDHelpers::Nil;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
TemporaryTableHolder::~TemporaryTableHolder()
|
|
|
|
{
|
|
|
|
if (id != UUIDHelpers::Nil)
|
2020-03-16 11:38:50 +00:00
|
|
|
temporary_tables->dropTable(*global_context, "_tmp_" + toString(id));
|
2020-03-10 19:36:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StorageID TemporaryTableHolder::getGlobalTableID() const
|
|
|
|
{
|
|
|
|
return StorageID{DatabaseCatalog::TEMPORARY_DATABASE, "_tmp_" + toString(id), id};
|
|
|
|
}
|
|
|
|
|
|
|
|
StoragePtr TemporaryTableHolder::getTable() const
|
|
|
|
{
|
2020-03-13 15:41:36 +00:00
|
|
|
auto table = temporary_tables->tryGetTable(*global_context, "_tmp_" + toString(id));
|
2020-03-10 19:36:17 +00:00
|
|
|
if (!table)
|
|
|
|
throw Exception("Temporary table " + getGlobalTableID().getNameForLogs() + " not found", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return table;
|
|
|
|
}
|
|
|
|
|
2020-02-10 13:10:17 +00:00
|
|
|
|
2020-02-03 12:54:36 +00:00
|
|
|
void DatabaseCatalog::loadDatabases()
|
|
|
|
{
|
|
|
|
|
|
|
|
auto db_for_temporary_and_external_tables = std::make_shared<DatabaseMemory>(TEMPORARY_DATABASE);
|
2020-02-10 13:10:17 +00:00
|
|
|
attachDatabase(TEMPORARY_DATABASE, db_for_temporary_and_external_tables);
|
2020-03-19 21:14:52 +00:00
|
|
|
|
|
|
|
loadMarkedAsDroppedTables();
|
|
|
|
auto task_holder = global_context->getSchedulePool().createTask("DatabaseCatalog", [this](){ this->dropTableDataTask(); });
|
|
|
|
drop_task = std::make_unique<BackgroundSchedulePoolTaskHolder>(std::move(task_holder));
|
|
|
|
(*drop_task)->activateAndSchedule();
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::shutdown()
|
|
|
|
{
|
2020-03-19 21:14:52 +00:00
|
|
|
if (drop_task)
|
|
|
|
(*drop_task)->deactivate();
|
|
|
|
|
2020-02-03 12:54:36 +00:00
|
|
|
/** At this point, some tables may have threads that block our mutex.
|
|
|
|
* To shutdown them correctly, we will copy the current list of tables,
|
|
|
|
* and ask them all to finish their work.
|
|
|
|
* Then delete all objects with tables.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Databases current_databases;
|
|
|
|
{
|
|
|
|
std::lock_guard lock(databases_mutex);
|
|
|
|
current_databases = databases;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// We still hold "databases" (instead of std::move) for Buffer tables to flush data correctly.
|
|
|
|
|
|
|
|
for (auto & database : current_databases)
|
|
|
|
database.second->shutdown();
|
|
|
|
|
|
|
|
std::lock_guard lock(databases_mutex);
|
2020-03-17 23:51:35 +00:00
|
|
|
assert(std::find_if_not(uuid_map.begin(), uuid_map.end(), [](const auto & elem) { return elem.map.empty(); }) == uuid_map.end());
|
2020-02-12 16:54:26 +00:00
|
|
|
databases.clear();
|
|
|
|
view_dependencies.clear();
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DatabaseAndTable DatabaseCatalog::tryGetByUUID(const UUID & uuid) const
|
|
|
|
{
|
2020-03-16 09:16:14 +00:00
|
|
|
assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
|
2020-02-03 12:54:36 +00:00
|
|
|
const UUIDToStorageMapPart & map_part = uuid_map[getFirstLevelIdx(uuid)];
|
|
|
|
std::lock_guard lock{map_part.mutex};
|
|
|
|
auto it = map_part.map.find(uuid);
|
|
|
|
if (it == map_part.map.end())
|
|
|
|
return {};
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-23 02:12:31 +00:00
|
|
|
DatabaseAndTable DatabaseCatalog::getTableImpl(
|
|
|
|
const StorageID & table_id,
|
|
|
|
const Context & local_context,
|
|
|
|
std::optional<Exception> * exception) const
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-02-17 13:52:59 +00:00
|
|
|
if (!table_id)
|
|
|
|
{
|
|
|
|
if (exception)
|
|
|
|
exception->emplace("Cannot find table: StorageID is empty", ErrorCodes::UNKNOWN_TABLE);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-03-12 18:04:29 +00:00
|
|
|
if (table_id.hasUUID())
|
|
|
|
{
|
2020-03-13 15:41:36 +00:00
|
|
|
/// Shortcut for tables which have persistent UUID
|
2020-03-12 18:04:29 +00:00
|
|
|
auto db_and_table = tryGetByUUID(table_id.uuid);
|
|
|
|
if (!db_and_table.first || !db_and_table.second)
|
|
|
|
{
|
|
|
|
assert(!db_and_table.first && !db_and_table.second);
|
|
|
|
if (exception)
|
|
|
|
exception->emplace("Table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return db_and_table;
|
|
|
|
}
|
2020-02-03 12:54:36 +00:00
|
|
|
|
2020-03-13 15:41:36 +00:00
|
|
|
if (table_id.database_name == TEMPORARY_DATABASE)
|
|
|
|
{
|
|
|
|
/// For temporary tables UUIDs are set in Context::resolveStorageID(...).
|
|
|
|
/// If table_id has no UUID, then the name of database was specified by user and table_id was not resolved through context.
|
|
|
|
/// Do not allow access to TEMPORARY_DATABASE because it contains all temporary tables of all contexts and users.
|
|
|
|
if (exception)
|
|
|
|
exception->emplace("Direct access to `" + String(TEMPORARY_DATABASE) + "` database is not allowed.", ErrorCodes::DATABASE_ACCESS_DENIED);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-02-11 17:25:26 +00:00
|
|
|
DatabasePtr database;
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-03-23 02:12:31 +00:00
|
|
|
std::lock_guard lock{databases_mutex};
|
2020-02-11 17:25:26 +00:00
|
|
|
auto it = databases.find(table_id.getDatabaseName());
|
|
|
|
if (databases.end() == it)
|
|
|
|
{
|
|
|
|
if (exception)
|
|
|
|
exception->emplace("Database " + backQuoteIfNeed(table_id.getDatabaseName()) + " doesn't exist",
|
|
|
|
ErrorCodes::UNKNOWN_DATABASE);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
database = it->second;
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto table = database->tryGetTable(local_context, table_id.table_name);
|
|
|
|
if (!table && exception)
|
|
|
|
exception->emplace("Table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);
|
|
|
|
|
2020-03-04 20:29:52 +00:00
|
|
|
return {database, table};
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::assertDatabaseExists(const String & database_name) const
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
assertDatabaseExistsUnlocked(database_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::assertDatabaseDoesntExist(const String & database_name) const
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
assertDatabaseDoesntExistUnlocked(database_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::assertDatabaseExistsUnlocked(const String & database_name) const
|
|
|
|
{
|
2020-02-10 18:19:35 +00:00
|
|
|
assert(!database_name.empty());
|
2020-02-03 12:54:36 +00:00
|
|
|
if (databases.end() == databases.find(database_name))
|
|
|
|
throw Exception("Database " + backQuoteIfNeed(database_name) + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DatabaseCatalog::assertDatabaseDoesntExistUnlocked(const String & database_name) const
|
|
|
|
{
|
2020-02-10 18:19:35 +00:00
|
|
|
assert(!database_name.empty());
|
2020-02-03 12:54:36 +00:00
|
|
|
if (databases.end() != databases.find(database_name))
|
|
|
|
throw Exception("Database " + backQuoteIfNeed(database_name) + " already exists.", ErrorCodes::DATABASE_ALREADY_EXISTS);
|
|
|
|
}
|
|
|
|
|
2020-02-10 13:10:17 +00:00
|
|
|
void DatabaseCatalog::attachDatabase(const String & database_name, const DatabasePtr & database)
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
assertDatabaseDoesntExistUnlocked(database_name);
|
|
|
|
databases[database_name] = database;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-17 19:28:25 +00:00
|
|
|
DatabasePtr DatabaseCatalog::detachDatabase(const String & database_name, bool drop, bool check_empty)
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-03-13 15:41:36 +00:00
|
|
|
if (database_name == TEMPORARY_DATABASE)
|
|
|
|
throw Exception("Cannot detach database with temporary tables.", ErrorCodes::DATABASE_ACCESS_DENIED);
|
|
|
|
|
2020-02-03 12:54:36 +00:00
|
|
|
std::lock_guard lock{databases_mutex};
|
2020-02-10 18:19:35 +00:00
|
|
|
assertDatabaseExistsUnlocked(database_name);
|
2020-02-17 13:52:59 +00:00
|
|
|
auto db = databases.find(database_name)->second;
|
2020-02-13 21:00:03 +00:00
|
|
|
|
2020-02-17 19:28:25 +00:00
|
|
|
if (check_empty && !db->empty(*global_context))
|
2020-02-13 21:00:03 +00:00
|
|
|
throw Exception("New table appeared in database being dropped or detached. Try again.", ErrorCodes::DATABASE_NOT_EMPTY);
|
|
|
|
|
2020-02-03 12:54:36 +00:00
|
|
|
databases.erase(database_name);
|
2020-02-13 21:00:03 +00:00
|
|
|
|
|
|
|
db->shutdown();
|
|
|
|
|
|
|
|
if (drop)
|
|
|
|
{
|
|
|
|
/// Delete the database.
|
|
|
|
db->drop(*global_context);
|
|
|
|
|
|
|
|
/// Old ClickHouse versions did not store database.sql files
|
|
|
|
Poco::File database_metadata_file(
|
|
|
|
global_context->getPath() + "metadata/" + escapeForFileName(database_name) + ".sql");
|
|
|
|
if (database_metadata_file.exists())
|
|
|
|
database_metadata_file.remove(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return db;
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 13:10:17 +00:00
|
|
|
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name) const
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
assertDatabaseExistsUnlocked(database_name);
|
|
|
|
return databases.find(database_name)->second;
|
|
|
|
}
|
|
|
|
|
2020-02-10 13:10:17 +00:00
|
|
|
DatabasePtr DatabaseCatalog::tryGetDatabase(const String & database_name) const
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-02-10 13:10:17 +00:00
|
|
|
assert(!database_name.empty());
|
2020-02-03 12:54:36 +00:00
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
auto it = databases.find(database_name);
|
|
|
|
if (it == databases.end())
|
|
|
|
return {};
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DatabaseCatalog::isDatabaseExist(const String & database_name) const
|
|
|
|
{
|
2020-02-10 13:10:17 +00:00
|
|
|
assert(!database_name.empty());
|
2020-02-03 12:54:36 +00:00
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
return databases.end() != databases.find(database_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
Databases DatabaseCatalog::getDatabases() const
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
return databases;
|
|
|
|
}
|
|
|
|
|
2020-03-13 15:41:36 +00:00
|
|
|
bool DatabaseCatalog::isTableExist(const DB::StorageID & table_id) const
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-03-12 18:04:29 +00:00
|
|
|
if (table_id.hasUUID())
|
|
|
|
return tryGetByUUID(table_id.uuid).second != nullptr;
|
|
|
|
|
|
|
|
DatabasePtr db;
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
auto iter = databases.find(table_id.database_name);
|
|
|
|
if (iter != databases.end())
|
|
|
|
db = iter->second;
|
|
|
|
}
|
2020-03-13 15:41:36 +00:00
|
|
|
return db && db->isTableExist(*global_context, table_id.table_name);
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
2020-03-13 15:41:36 +00:00
|
|
|
void DatabaseCatalog::assertTableDoesntExist(const StorageID & table_id) const
|
2020-02-03 12:54:36 +00:00
|
|
|
{
|
2020-03-13 15:41:36 +00:00
|
|
|
if (isTableExist(table_id))
|
2020-02-03 12:54:36 +00:00
|
|
|
throw Exception("Table " + table_id.getNameForLogs() + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
DatabasePtr DatabaseCatalog::getDatabaseForTemporaryTables() const
|
|
|
|
{
|
2020-02-10 13:10:17 +00:00
|
|
|
return getDatabase(TEMPORARY_DATABASE);
|
|
|
|
}
|
|
|
|
|
|
|
|
DatabasePtr DatabaseCatalog::getSystemDatabase() const
|
|
|
|
{
|
|
|
|
return getDatabase(SYSTEM_DATABASE);
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::addUUIDMapping(const UUID & uuid, DatabasePtr database, StoragePtr table)
|
|
|
|
{
|
2020-03-16 09:16:14 +00:00
|
|
|
assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
|
2020-02-03 12:54:36 +00:00
|
|
|
UUIDToStorageMapPart & map_part = uuid_map[getFirstLevelIdx(uuid)];
|
|
|
|
std::lock_guard lock{map_part.mutex};
|
|
|
|
auto [_, inserted] = map_part.map.try_emplace(uuid, std::move(database), std::move(table));
|
|
|
|
if (!inserted)
|
|
|
|
throw Exception("Mapping for table with UUID=" + toString(uuid) + " already exists", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::removeUUIDMapping(const UUID & uuid)
|
|
|
|
{
|
2020-03-16 09:16:14 +00:00
|
|
|
assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
|
2020-02-03 12:54:36 +00:00
|
|
|
UUIDToStorageMapPart & map_part = uuid_map[getFirstLevelIdx(uuid)];
|
|
|
|
std::lock_guard lock{map_part.mutex};
|
|
|
|
if (!map_part.map.erase(uuid))
|
|
|
|
throw Exception("Mapping for table with UUID=" + toString(uuid) + " doesn't exist", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
2020-03-23 00:12:13 +00:00
|
|
|
void DatabaseCatalog::updateUUIDMapping(const UUID & uuid, DatabasePtr database, StoragePtr table)
|
|
|
|
{
|
|
|
|
assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
|
|
|
|
UUIDToStorageMapPart & map_part = uuid_map[getFirstLevelIdx(uuid)];
|
|
|
|
std::lock_guard lock{map_part.mutex};
|
|
|
|
auto it = map_part.map.find(uuid);
|
|
|
|
if (it == map_part.map.end())
|
|
|
|
throw Exception("Mapping for table with UUID=" + toString(uuid) + " doesn't exist", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
it->second = std::make_pair(std::move(database), std::move(table));
|
|
|
|
}
|
|
|
|
|
2020-03-19 21:14:52 +00:00
|
|
|
DatabaseCatalog::DatabaseCatalog(Context * global_context_)
|
2020-02-13 21:00:03 +00:00
|
|
|
: global_context(global_context_), log(&Poco::Logger::get("DatabaseCatalog"))
|
2020-02-10 13:10:17 +00:00
|
|
|
{
|
2020-02-13 21:00:03 +00:00
|
|
|
if (!global_context)
|
|
|
|
throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
2020-03-19 21:14:52 +00:00
|
|
|
DatabaseCatalog & DatabaseCatalog::init(Context * global_context_)
|
2020-02-13 21:00:03 +00:00
|
|
|
{
|
|
|
|
static DatabaseCatalog database_catalog(global_context_);
|
2020-02-10 13:10:17 +00:00
|
|
|
return database_catalog;
|
|
|
|
}
|
|
|
|
|
2020-02-13 21:00:03 +00:00
|
|
|
DatabaseCatalog & DatabaseCatalog::instance()
|
|
|
|
{
|
|
|
|
return init(nullptr);
|
|
|
|
}
|
|
|
|
|
2020-02-10 13:10:17 +00:00
|
|
|
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name, const Context & local_context) const
|
|
|
|
{
|
|
|
|
String resolved_database = local_context.resolveDatabase(database_name);
|
|
|
|
return getDatabase(resolved_database);
|
|
|
|
}
|
|
|
|
|
2020-02-12 16:54:26 +00:00
|
|
|
void DatabaseCatalog::addDependency(const StorageID & from, const StorageID & where)
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
2020-02-12 18:14:12 +00:00
|
|
|
// FIXME when loading metadata storage may not know UUIDs of it's dependencies, because they are not loaded yet,
|
|
|
|
// so UUID of `from` is not used here. (same for remove, get and update)
|
|
|
|
view_dependencies[{from.getDatabaseName(), from.getTableName()}].insert(where);
|
2020-02-12 16:54:26 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::removeDependency(const StorageID & from, const StorageID & where)
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
2020-02-12 18:14:12 +00:00
|
|
|
view_dependencies[{from.getDatabaseName(), from.getTableName()}].erase(where);
|
2020-02-12 16:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Dependencies DatabaseCatalog::getDependencies(const StorageID & from) const
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
2020-02-12 18:14:12 +00:00
|
|
|
auto iter = view_dependencies.find({from.getDatabaseName(), from.getTableName()});
|
2020-02-12 16:54:26 +00:00
|
|
|
if (iter == view_dependencies.end())
|
|
|
|
return {};
|
|
|
|
return Dependencies(iter->second.begin(), iter->second.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DatabaseCatalog::updateDependency(const StorageID & old_from, const StorageID & old_where, const StorageID & new_from,
|
|
|
|
const StorageID & new_where)
|
|
|
|
{
|
|
|
|
std::lock_guard lock{databases_mutex};
|
|
|
|
if (!old_from.empty())
|
2020-02-12 18:14:12 +00:00
|
|
|
view_dependencies[{old_from.getDatabaseName(), old_from.getTableName()}].erase(old_where);
|
2020-02-12 16:54:26 +00:00
|
|
|
if (!new_from.empty())
|
2020-02-12 18:14:12 +00:00
|
|
|
view_dependencies[{new_from.getDatabaseName(), new_from.getTableName()}].insert(new_where);
|
2020-02-12 16:54:26 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 18:31:52 +00:00
|
|
|
std::unique_ptr<DDLGuard> DatabaseCatalog::getDDLGuard(const String & database, const String & table)
|
|
|
|
{
|
|
|
|
std::unique_lock lock(ddl_guards_mutex);
|
|
|
|
return std::make_unique<DDLGuard>(ddl_guards[database], std::move(lock), table);
|
|
|
|
}
|
|
|
|
|
2020-03-13 15:41:36 +00:00
|
|
|
bool DatabaseCatalog::isDictionaryExist(const StorageID & table_id) const
|
2020-02-17 13:52:59 +00:00
|
|
|
{
|
|
|
|
auto db = tryGetDatabase(table_id.getDatabaseName());
|
2020-03-13 15:41:36 +00:00
|
|
|
return db && db->isDictionaryExist(*global_context, table_id.getTableName());
|
2020-02-17 13:52:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StoragePtr DatabaseCatalog::getTable(const StorageID & table_id) const
|
|
|
|
{
|
2020-03-30 23:36:23 +00:00
|
|
|
std::optional<Exception> exc;
|
|
|
|
auto res = getTableImpl(table_id, *global_context, &exc);
|
|
|
|
if (!res.second)
|
|
|
|
throw Exception(*exc);
|
|
|
|
return res.second;
|
2020-02-17 13:52:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StoragePtr DatabaseCatalog::tryGetTable(const StorageID & table_id) const
|
|
|
|
{
|
2020-03-04 20:29:52 +00:00
|
|
|
return getTableImpl(table_id, *global_context, nullptr).second;
|
|
|
|
}
|
|
|
|
|
|
|
|
DatabaseAndTable DatabaseCatalog::tryGetDatabaseAndTable(const StorageID & table_id) const
|
|
|
|
{
|
2020-03-30 23:36:23 +00:00
|
|
|
return getTableImpl(table_id, *global_context, nullptr);
|
2020-02-17 13:52:59 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 21:14:52 +00:00
|
|
|
void DatabaseCatalog::loadMarkedAsDroppedTables()
|
|
|
|
{
|
|
|
|
std::map<String, StorageID> dropped_metadata;
|
|
|
|
String path = global_context->getPath() + "metadata_dropped/";
|
|
|
|
Poco::DirectoryIterator dir_end;
|
|
|
|
for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
|
|
|
|
{
|
|
|
|
if (!it.name().ends_with(".sql"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/// Process .sql files with metadata of tables which were marked as dropped
|
|
|
|
String full_path = path + it.name();
|
|
|
|
|
|
|
|
Strings name_parts;
|
2020-03-30 14:53:05 +00:00
|
|
|
boost::split(name_parts, it.name(), boost::is_any_of(".")); // NOLINT: LLVM Bug 41141
|
2020-03-19 21:14:52 +00:00
|
|
|
if (name_parts.size() != 4) /// Unexpected file
|
|
|
|
continue;
|
|
|
|
|
|
|
|
StorageID dropped_id = StorageID::createEmpty();
|
|
|
|
dropped_id.database_name = unescapeForFileName(name_parts[0]);
|
|
|
|
dropped_id.table_name = unescapeForFileName(name_parts[1]);
|
|
|
|
dropped_id.uuid = parse<UUID>(name_parts[2]);
|
|
|
|
|
|
|
|
dropped_metadata.emplace(std::move(full_path), std::move(dropped_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
ThreadPool pool(SettingMaxThreads().getAutoValue());
|
|
|
|
for (const auto & elem : dropped_metadata)
|
|
|
|
{
|
|
|
|
pool.scheduleOrThrowOnError([&]()
|
|
|
|
{
|
|
|
|
this->enqueueDroppedTableCleanup(elem.second, nullptr, elem.first);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
pool.wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
String DatabaseCatalog::getPathForDroppedMetadata(const StorageID & table_id) const
|
|
|
|
{
|
|
|
|
return global_context->getPath() + "metadata_dropped/" +
|
|
|
|
escapeForFileName(table_id.getDatabaseName()) + "." +
|
|
|
|
escapeForFileName(table_id.getTableName()) + "." +
|
|
|
|
toString(table_id.uuid) + ".sql";
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::enqueueDroppedTableCleanup(StorageID table_id, StoragePtr table, String dropped_metadata_path, bool ignore_delay)
|
|
|
|
{
|
|
|
|
assert(table_id.hasUUID());
|
|
|
|
assert(!table || table->getStorageID().uuid == table_id.uuid);
|
|
|
|
assert(dropped_metadata_path == getPathForDroppedMetadata(table_id));
|
|
|
|
|
|
|
|
time_t drop_time;
|
|
|
|
if (table)
|
|
|
|
drop_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/// Try load table from metadata to drop it correctly (e.g. remove metadata from zk)
|
|
|
|
LOG_INFO(log, "Trying load partially dropped table " << table_id.getNameForLogs() << " from " << dropped_metadata_path);
|
|
|
|
ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata(log, *global_context, dropped_metadata_path, /*throw_on_error*/ false, /*remove_empty*/false);
|
|
|
|
auto create = typeid_cast<ASTCreateQuery *>(ast.get());
|
|
|
|
assert(!create || create->uuid == table_id.uuid);
|
|
|
|
|
|
|
|
if (create)
|
|
|
|
{
|
|
|
|
String data_path = "store/" + DatabaseAtomic::getPathForUUID(table_id.uuid);
|
|
|
|
create->database = table_id.database_name;
|
|
|
|
create->table = table_id.table_name;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
table = createTableFromAST(*create, table_id.getDatabaseName(), data_path, *global_context, false).second;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(log, "Cannot load partially dropped table " + table_id.getNameForLogs() +
|
|
|
|
" from: " + dropped_metadata_path +
|
|
|
|
". Parsed query: " + serializeAST(*create) +
|
|
|
|
". Will remove metadata and " + data_path +
|
|
|
|
". Garbage may be left in ZooKeeper.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOG_WARNING(log, "Cannot parse metadata of partially dropped table "
|
|
|
|
<< table_id.getNameForLogs() << " from " << dropped_metadata_path
|
|
|
|
<< ". Will remove metadata file and data directory. Garbage may be left in /store directory and ZooKeeper.");
|
|
|
|
}
|
|
|
|
|
|
|
|
drop_time = Poco::File(dropped_metadata_path).getLastModified().epochTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::lock_guard lock(tables_marked_droped_mutex);
|
|
|
|
if (ignore_delay)
|
2020-03-30 14:53:05 +00:00
|
|
|
tables_marked_droped.push_front({table_id, table, dropped_metadata_path, 0});
|
2020-03-19 21:14:52 +00:00
|
|
|
else
|
2020-03-30 14:53:05 +00:00
|
|
|
tables_marked_droped.push_back({table_id, table, dropped_metadata_path, drop_time});
|
2020-03-19 21:14:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::dropTableDataTask()
|
|
|
|
{
|
|
|
|
//LOG_INFO(log, String("Wake up ") + __PRETTY_FUNCTION__);
|
|
|
|
TableMarkedAsDropped table;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::lock_guard lock(tables_marked_droped_mutex);
|
|
|
|
LOG_INFO(log, "There are " + std::to_string(tables_marked_droped.size()) + " tables to drop");
|
|
|
|
time_t current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
|
|
auto it = std::find_if(tables_marked_droped.begin(), tables_marked_droped.end(), [current_time/*, this*/](const auto & elem)
|
|
|
|
{
|
|
|
|
//LOG_INFO(log, "Check table " + elem.table_id.getNameForLogs() + ": " +
|
|
|
|
// "refcount = " + std::to_string(elem.table.use_count()) + ", " +
|
|
|
|
// "time elapsed = " + std::to_string(current_time - elem.drop_time));
|
2020-03-27 22:58:03 +00:00
|
|
|
bool not_in_use = !elem.table || elem.table.unique();
|
|
|
|
bool old_enough = elem.drop_time + drop_delay_s < current_time;
|
2020-03-30 14:53:05 +00:00
|
|
|
return not_in_use && old_enough;
|
2020-03-19 21:14:52 +00:00
|
|
|
});
|
2020-03-30 14:53:05 +00:00
|
|
|
if (it != tables_marked_droped.end())
|
2020-03-19 21:14:52 +00:00
|
|
|
{
|
|
|
|
table = std::move(*it);
|
|
|
|
LOG_INFO(log, "Will try drop " + table.table_id.getNameForLogs());
|
|
|
|
tables_marked_droped.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(log, __PRETTY_FUNCTION__);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (table.table_id)
|
|
|
|
{
|
2020-03-27 22:58:03 +00:00
|
|
|
|
2020-03-19 21:14:52 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
dropTableFinally(table);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
tryLogCurrentException(log, "Cannot drop table " + table.table_id.getNameForLogs() +
|
|
|
|
". Will retry later.");
|
|
|
|
{
|
|
|
|
std::lock_guard lock(tables_marked_droped_mutex);
|
|
|
|
tables_marked_droped.emplace_back(std::move(table));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(*drop_task)->scheduleAfter(reschedule_time_ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DatabaseCatalog::dropTableFinally(const TableMarkedAsDropped & table) const
|
|
|
|
{
|
|
|
|
if (table.table)
|
|
|
|
{
|
|
|
|
LOG_INFO(log, "Trying to drop table " + table.table_id.getNameForLogs());
|
|
|
|
table.table->drop();
|
|
|
|
table.table->is_dropped = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Even if table is not loaded, try remove its data from disk.
|
|
|
|
/// TODO remove data from all volumes
|
|
|
|
String data_path = global_context->getPath() + "store/" + DatabaseAtomic::getPathForUUID(table.table_id.uuid);
|
|
|
|
Poco::File table_data_dir{data_path};
|
|
|
|
if (table_data_dir.exists())
|
|
|
|
{
|
|
|
|
LOG_INFO(log, "Removing data directory " << data_path << " of table " << table.table_id.getNameForLogs());
|
|
|
|
table_data_dir.remove(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
Poco::File(table.metadata_path).remove();
|
|
|
|
}
|
|
|
|
|
2020-02-10 18:31:52 +00:00
|
|
|
|
|
|
|
DDLGuard::DDLGuard(Map & map_, std::unique_lock<std::mutex> guards_lock_, const String & elem)
|
|
|
|
: map(map_), guards_lock(std::move(guards_lock_))
|
|
|
|
{
|
|
|
|
it = map.emplace(elem, Entry{std::make_unique<std::mutex>(), 0}).first;
|
|
|
|
++it->second.counter;
|
|
|
|
guards_lock.unlock();
|
|
|
|
table_lock = std::unique_lock(*it->second.mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
DDLGuard::~DDLGuard()
|
|
|
|
{
|
|
|
|
guards_lock.lock();
|
|
|
|
--it->second.counter;
|
|
|
|
if (!it->second.counter)
|
|
|
|
{
|
|
|
|
table_lock.unlock();
|
|
|
|
map.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-03 12:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|