ClickHouse/src/Interpreters/DatabaseCatalog.cpp

835 lines
29 KiB
C++
Raw Normal View History

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 <IO/ReadHelpers.h>
#include <Poco/DirectoryIterator.h>
2020-07-07 12:11:58 +00:00
#include <Common/renameat2.h>
2020-02-03 12:54:36 +00:00
#include <filesystem>
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 ConstraintsDescription & constraints,
const ASTPtr & query)
2020-03-10 19:36:17 +00:00
: TemporaryTableHolder
(
context_,
[&](const StorageID & table_id)
{
return StorageMemory::create(table_id, ColumnsDescription{columns}, ConstraintsDescription{constraints});
2020-03-10 19:36:17 +00:00
},
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)
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-05-28 20:10:45 +00:00
auto table = temporary_tables->tryGetTable("_tmp_" + toString(id), *global_context);
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-03 12:54:36 +00:00
void DatabaseCatalog::loadDatabases()
{
drop_delay_sec = global_context->getConfigRef().getInt("database_atomic_delay_before_drop_table_sec", default_drop_delay_sec);
2020-02-03 12:54:36 +00:00
2020-05-28 20:10:45 +00:00
auto db_for_temporary_and_external_tables = std::make_shared<DatabaseMemory>(TEMPORARY_DATABASE, *global_context);
2020-07-08 14:28:07 +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
}
2020-04-08 23:59:39 +00:00
void DatabaseCatalog::shutdownImpl()
2020-02-03 12:54:36 +00:00
{
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();
2020-07-17 17:54:24 +00:00
tables_marked_dropped.clear();
2020-02-03 12:54:36 +00:00
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());
databases.clear();
db_uuid_map.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;
}
DatabaseAndTable DatabaseCatalog::getTableImpl(
const StorageID & table_id,
2020-05-28 23:01:18 +00:00
const Context & 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
{
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
}
2020-05-28 23:01:18 +00:00
auto table = database->tryGetTable(table_id.table_name, context);
2020-02-03 12:54:36 +00:00
if (!table && exception)
exception->emplace("Table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);
2020-07-15 19:25:31 +00:00
if (!table)
database = nullptr;
2020-02-03 12:54:36 +00:00
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-07-08 14:28:07 +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.emplace(database_name, database);
2020-07-08 14:28:07 +00:00
UUID db_uuid = database->getUUID();
assert((db_uuid != UUIDHelpers::Nil) ^ (dynamic_cast<DatabaseAtomic *>(database.get()) == nullptr));
if (db_uuid != UUIDHelpers::Nil)
db_uuid_map.emplace(db_uuid, database);
2020-02-03 12:54:36 +00:00
}
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-07-08 14:28:07 +00:00
DatabasePtr db;
Fix deadlock on failed database attach at start with materialized view This is not the problem for plain DROP DATABASE query since it first remove tables. (gdb) bt 0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:103 1 0x00007f353c262774 in __GI___pthread_mutex_lock (mutex=mutex@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../nptl/pthread_mutex_lock.c:80 2 0x0000000004fd5d1e in DB::pthread_mutex_lock (arg=arg@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../src/Common/ThreadFuzzer.cpp:253 3 0x000000000c70b3a9 in std::__1::__libcpp_mutex_lock (__m=__m@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../contrib/libcxx/include/__threading_support:322 4 std::__1::mutex::lock (this=this@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../contrib/libcxx/src/mutex.cpp:33 5 0x0000000008acd4e2 in std::__1::lock_guard<std::__1::mutex>::lock_guard (__m=..., this=<synthetic pointer>) at ../contrib/libcxx/include/__mutex_base:90 6 DB::DatabaseCatalog::removeDependency (this=this@entry=0x11049280 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog>, from=..., where=...) at ../src/Interpreters/DatabaseCatalog.cpp:388 7 0x000000000975044b in DB::StorageMaterializedView::shutdown (this=0x7f338d178a00) at ../src/Storages/StorageMaterializedView.cpp:362 8 0x0000000008a79602 in DB::DatabaseWithOwnTablesBase::shutdown (this=0x7f353be3cc60) at ../contrib/libcxx/include/__tree:184 9 0x0000000009546196 in DB::DatabaseWithDictionaries::shutdown (this=0x7f353be3cc60) at ../src/Databases/DatabaseWithDictionaries.cpp:265 10 0x0000000008acecdb in DB::DatabaseCatalog::detachDatabase (this=0x11049280 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog>, database_name=..., drop=drop@entry=false, check_empty=check_empty@entry=false) at ../contrib/libcxx/include/memory:3826 11 0x0000000004bf0aa5 in DB::InterpreterCreateQuery::createDatabase (this=0x7ffd6e1bab80, create=...) at ../src/Interpreters/InterpreterCreateQuery.cpp:162 12 0x0000000008b04327 in DB::InterpreterCreateQuery::execute (this=this@entry=0x7ffd6e1bab80) at ../src/Interpreters/InterpreterCreateQuery.cpp:722 13 0x0000000008d0fdaa in DB::executeCreateQuery (has_force_restore_data_flag=false, file_name=..., database=..., context=..., query=...) at ../src/Interpreters/loadMetadata.cpp:48 14 DB::loadDatabase (context=..., database=..., database_path=..., force_restore_data=<optimized out>) at ../src/Interpreters/loadMetadata.cpp:72 15 0x0000000008d103c3 in DB::loadMetadata (context=...) at ../src/Interpreters/loadMetadata.cpp:111 16 0x0000000004f4d25e in DB::Server::main (this=<optimized out>) at ../contrib/libcxx/include/memory:2582 17 0x000000000bbc8963 in Poco::Util::Application::run (this=this@entry=0x7ffd6e1bcc60) at ../contrib/poco/Util/src/Application.cpp:334 18 0x0000000004ffe1de in DB::Server::run (this=0x7ffd6e1bcc60) at ../programs/server/Server.cpp:178 19 0x0000000004ff36cc in mainEntryClickHouseServer (argc=3, argv=0x7f353be58bc0) at ../programs/server/Server.cpp:1060 20 0x0000000004f49b99 in main (argc_=<optimized out>, argv_=<optimized out>) at ../contrib/libcxx/include/vector:655
2020-04-05 11:00:11 +00:00
{
std::lock_guard lock{databases_mutex};
assertDatabaseExistsUnlocked(database_name);
db = databases.find(database_name)->second;
2020-07-08 14:28:07 +00:00
db_uuid_map.erase(db->getUUID());
databases.erase(database_name);
}
2020-02-13 21:00:03 +00:00
2020-07-08 14:28:07 +00:00
if (check_empty)
{
try
2020-04-10 23:02:15 +00:00
{
if (!db->empty())
2020-04-10 23:02:15 +00:00
throw Exception("New table appeared in database being dropped or detached. Try again.",
ErrorCodes::DATABASE_NOT_EMPTY);
2020-04-23 19:50:05 +00:00
auto * database_atomic = typeid_cast<DatabaseAtomic *>(db.get());
2020-04-10 23:02:15 +00:00
if (!drop && database_atomic)
database_atomic->assertCanBeDetached(false);
}
2020-07-08 14:28:07 +00:00
catch (...)
{
attachDatabase(database_name, db);
throw;
}
Fix deadlock on failed database attach at start with materialized view This is not the problem for plain DROP DATABASE query since it first remove tables. (gdb) bt 0 __lll_lock_wait () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:103 1 0x00007f353c262774 in __GI___pthread_mutex_lock (mutex=mutex@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../nptl/pthread_mutex_lock.c:80 2 0x0000000004fd5d1e in DB::pthread_mutex_lock (arg=arg@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../src/Common/ThreadFuzzer.cpp:253 3 0x000000000c70b3a9 in std::__1::__libcpp_mutex_lock (__m=__m@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../contrib/libcxx/include/__threading_support:322 4 std::__1::mutex::lock (this=this@entry=0x11049288 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog+8>) at ../contrib/libcxx/src/mutex.cpp:33 5 0x0000000008acd4e2 in std::__1::lock_guard<std::__1::mutex>::lock_guard (__m=..., this=<synthetic pointer>) at ../contrib/libcxx/include/__mutex_base:90 6 DB::DatabaseCatalog::removeDependency (this=this@entry=0x11049280 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog>, from=..., where=...) at ../src/Interpreters/DatabaseCatalog.cpp:388 7 0x000000000975044b in DB::StorageMaterializedView::shutdown (this=0x7f338d178a00) at ../src/Storages/StorageMaterializedView.cpp:362 8 0x0000000008a79602 in DB::DatabaseWithOwnTablesBase::shutdown (this=0x7f353be3cc60) at ../contrib/libcxx/include/__tree:184 9 0x0000000009546196 in DB::DatabaseWithDictionaries::shutdown (this=0x7f353be3cc60) at ../src/Databases/DatabaseWithDictionaries.cpp:265 10 0x0000000008acecdb in DB::DatabaseCatalog::detachDatabase (this=0x11049280 <DB::DatabaseCatalog::init(DB::Context const*)::database_catalog>, database_name=..., drop=drop@entry=false, check_empty=check_empty@entry=false) at ../contrib/libcxx/include/memory:3826 11 0x0000000004bf0aa5 in DB::InterpreterCreateQuery::createDatabase (this=0x7ffd6e1bab80, create=...) at ../src/Interpreters/InterpreterCreateQuery.cpp:162 12 0x0000000008b04327 in DB::InterpreterCreateQuery::execute (this=this@entry=0x7ffd6e1bab80) at ../src/Interpreters/InterpreterCreateQuery.cpp:722 13 0x0000000008d0fdaa in DB::executeCreateQuery (has_force_restore_data_flag=false, file_name=..., database=..., context=..., query=...) at ../src/Interpreters/loadMetadata.cpp:48 14 DB::loadDatabase (context=..., database=..., database_path=..., force_restore_data=<optimized out>) at ../src/Interpreters/loadMetadata.cpp:72 15 0x0000000008d103c3 in DB::loadMetadata (context=...) at ../src/Interpreters/loadMetadata.cpp:111 16 0x0000000004f4d25e in DB::Server::main (this=<optimized out>) at ../contrib/libcxx/include/memory:2582 17 0x000000000bbc8963 in Poco::Util::Application::run (this=this@entry=0x7ffd6e1bcc60) at ../contrib/poco/Util/src/Application.cpp:334 18 0x0000000004ffe1de in DB::Server::run (this=0x7ffd6e1bcc60) at ../programs/server/Server.cpp:178 19 0x0000000004ff36cc in mainEntryClickHouseServer (argc=3, argv=0x7f353be58bc0) at ../programs/server/Server.cpp:1060 20 0x0000000004f49b99 in main (argc_=<optimized out>, argv_=<optimized out>) at ../contrib/libcxx/include/vector:655
2020-04-05 11:00:11 +00:00
}
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-07-08 14:28:07 +00:00
void DatabaseCatalog::updateDatabaseName(const String & old_name, const String & new_name)
2020-07-07 12:11:58 +00:00
{
std::lock_guard lock{databases_mutex};
2020-07-08 14:28:07 +00:00
assert(databases.find(new_name) == databases.end());
2020-07-07 12:11:58 +00:00
auto it = databases.find(old_name);
2020-07-08 14:28:07 +00:00
assert(it != databases.end());
2020-07-07 12:11:58 +00:00
auto db = it->second;
databases.erase(it);
databases.emplace(new_name, db);
}
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;
}
DatabasePtr DatabaseCatalog::tryGetDatabase(const String & database_name) const
2020-02-03 12:54:36 +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;
}
DatabasePtr DatabaseCatalog::getDatabase(const UUID & uuid) const
{
std::lock_guard lock{databases_mutex};
auto it = db_uuid_map.find(uuid);
if (it == db_uuid_map.end())
throw Exception(ErrorCodes::UNKNOWN_DATABASE, "Database UUID {} does not exist", toString(uuid));
return it->second;
}
DatabasePtr DatabaseCatalog::tryGetDatabase(const UUID & uuid) const
{
assert(uuid != UUIDHelpers::Nil);
std::lock_guard lock{databases_mutex};
auto it = db_uuid_map.find(uuid);
if (it == db_uuid_map.end())
return {};
return it->second;
}
2020-02-03 12:54:36 +00:00
bool DatabaseCatalog::isDatabaseExist(const String & database_name) const
{
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-05-28 23:01:18 +00:00
bool DatabaseCatalog::isTableExist(const DB::StorageID & table_id, const Context & context) 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-05-28 23:01:18 +00:00
return db && db->isTableExist(table_id.table_name, context);
2020-02-03 12:54:36 +00:00
}
2020-05-28 23:01:18 +00:00
void DatabaseCatalog::assertTableDoesntExist(const StorageID & table_id, const Context & context) const
2020-02-03 12:54:36 +00:00
{
2020-05-28 23:01:18 +00:00
if (isTableExist(table_id, context))
2020-02-03 12:54:36 +00:00
throw Exception("Table " + table_id.getNameForLogs() + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
}
DatabasePtr DatabaseCatalog::getDatabaseForTemporaryTables() const
{
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-06-03 13:34:36 +00:00
std::unique_ptr<DatabaseCatalog> DatabaseCatalog::database_catalog;
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-13 21:00:03 +00:00
if (!global_context)
throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::LOGICAL_ERROR);
2020-02-13 21:00:03 +00:00
}
2020-03-19 21:14:52 +00:00
DatabaseCatalog & DatabaseCatalog::init(Context * global_context_)
2020-02-13 21:00:03 +00:00
{
if (database_catalog)
{
throw Exception("Database catalog is initialized twice. This is a bug.",
ErrorCodes::LOGICAL_ERROR);
}
database_catalog.reset(new DatabaseCatalog(global_context_));
return *database_catalog;
}
2020-02-13 21:00:03 +00:00
DatabaseCatalog & DatabaseCatalog::instance()
{
if (!database_catalog)
{
throw Exception("Database catalog is not initialized. This is a bug.",
ErrorCodes::LOGICAL_ERROR);
}
return *database_catalog;
2020-02-13 21:00:03 +00:00
}
2020-04-08 23:59:39 +00:00
void DatabaseCatalog::shutdown()
{
// The catalog might not be initialized yet by init(global_context). It can
// happen if some exception was thrown on first steps of startup.
if (database_catalog)
2020-04-08 23:59:39 +00:00
{
database_catalog->shutdownImpl();
2020-04-08 23:59:39 +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);
}
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);
}
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);
}
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()});
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);
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-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);
2020-08-18 15:15:27 +00:00
auto db_guard_iter = ddl_guards.try_emplace(database).first;
DatabaseGuard & db_guard = db_guard_iter->second;
return std::make_unique<DDLGuard>(db_guard.first, db_guard.second, std::move(lock), table);
}
std::unique_lock<std::shared_mutex> DatabaseCatalog::getExclusiveDDLGuardForDatabase(const String & database)
{
DDLGuards::iterator db_guard_iter;
{
std::unique_lock lock(ddl_guards_mutex);
db_guard_iter = ddl_guards.try_emplace(database).first;
assert(db_guard_iter->second.first.count(""));
}
DatabaseGuard & db_guard = db_guard_iter->second;
return std::unique_lock{db_guard.second};
2020-02-10 18:31:52 +00:00
}
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());
return db && db->isDictionaryExist(table_id.getTableName());
2020-02-17 13:52:59 +00:00
}
2020-05-28 23:01:18 +00:00
StoragePtr DatabaseCatalog::getTable(const StorageID & table_id, const Context & context) const
2020-02-17 13:52:59 +00:00
{
std::optional<Exception> exc;
2020-05-28 23:01:18 +00:00
auto res = getTableImpl(table_id, context, &exc);
if (!res.second)
throw Exception(*exc);
return res.second;
2020-02-17 13:52:59 +00:00
}
2020-05-28 23:01:18 +00:00
StoragePtr DatabaseCatalog::tryGetTable(const StorageID & table_id, const Context & context) const
2020-02-17 13:52:59 +00:00
{
2020-05-28 23:01:18 +00:00
return getTableImpl(table_id, context, nullptr).second;
2020-03-04 20:29:52 +00:00
}
2020-05-28 23:01:18 +00:00
DatabaseAndTable DatabaseCatalog::getDatabaseAndTable(const StorageID & table_id, const Context & context) const
2020-04-13 14:09:56 +00:00
{
std::optional<Exception> exc;
2020-05-28 23:01:18 +00:00
auto res = getTableImpl(table_id, context, &exc);
2020-04-13 14:09:56 +00:00
if (!res.second)
throw Exception(*exc);
return res;
}
2020-05-28 23:01:18 +00:00
DatabaseAndTable DatabaseCatalog::tryGetDatabaseAndTable(const StorageID & table_id, const Context & context) const
2020-03-04 20:29:52 +00:00
{
2020-05-28 23:01:18 +00:00
return getTableImpl(table_id, context, nullptr);
2020-02-17 13:52:59 +00:00
}
2020-03-19 21:14:52 +00:00
void DatabaseCatalog::loadMarkedAsDroppedTables()
{
2020-04-22 20:43:10 +00:00
/// /clickhouse_root/metadata_dropped/ contains files with metadata of tables,
/// which where marked as dropped by Atomic databases.
/// Data directories of such tables still exists in store/
/// and metadata still exists in ZooKeeper for ReplicatedMergeTree tables.
/// If server restarts before such tables was completely dropped,
/// we should load them and enqueue cleanup to remove data from store/ and metadata from ZooKeeper
2020-03-19 21:14:52 +00:00
std::map<String, StorageID> dropped_metadata;
String path = global_context->getPath() + "metadata_dropped/";
if (!std::filesystem::exists(path))
{
return;
}
2020-03-19 21:14:52 +00:00
Poco::DirectoryIterator dir_end;
for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
{
2020-04-22 20:43:10 +00:00
/// File name has the following format:
/// database_name.table_name.uuid.sql
/// Ignore unexpected files
2020-03-19 21:14:52 +00:00
if (!it.name().ends_with(".sql"))
continue;
/// Process .sql files with metadata of tables which were marked as dropped
2020-04-12 18:52:59 +00:00
StorageID dropped_id = StorageID::createEmpty();
size_t dot_pos = it.name().find('.');
if (dot_pos == std::string::npos)
continue;
dropped_id.database_name = unescapeForFileName(it.name().substr(0, dot_pos));
2020-03-19 21:14:52 +00:00
2020-04-12 18:52:59 +00:00
size_t prev_dot_pos = dot_pos;
dot_pos = it.name().find('.', prev_dot_pos + 1);
if (dot_pos == std::string::npos)
2020-03-19 21:14:52 +00:00
continue;
2020-04-12 18:52:59 +00:00
dropped_id.table_name = unescapeForFileName(it.name().substr(prev_dot_pos + 1, dot_pos - prev_dot_pos - 1));
2020-03-19 21:14:52 +00:00
2020-04-12 18:52:59 +00:00
prev_dot_pos = dot_pos;
dot_pos = it.name().find('.', prev_dot_pos + 1);
if (dot_pos == std::string::npos)
continue;
dropped_id.uuid = parse<UUID>(it.name().substr(prev_dot_pos + 1, dot_pos - prev_dot_pos - 1));
2020-03-19 21:14:52 +00:00
2020-04-12 18:52:59 +00:00
String full_path = path + it.name();
2020-03-19 21:14:52 +00:00
dropped_metadata.emplace(std::move(full_path), std::move(dropped_id));
}
ThreadPool pool;
2020-03-19 21:14:52 +00:00
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));
2020-04-22 20:43:10 +00:00
/// Table was removed from database. Enqueue removal of its data from disk.
2020-03-19 21:14:52 +00:00
time_t drop_time;
if (table)
drop_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
else
{
2020-04-22 20:43:10 +00:00
/// Try load table from metadata to drop it correctly (e.g. remove metadata from zk or remove data from all volumes)
2020-05-23 22:24:01 +00:00
LOG_INFO(log, "Trying load partially dropped table {} from {}", table_id.getNameForLogs(), dropped_metadata_path);
2020-03-19 21:14:52 +00:00
ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata(log, *global_context, dropped_metadata_path, /*throw_on_error*/ false, /*remove_empty*/false);
2020-04-23 22:08:26 +00:00
auto * create = typeid_cast<ASTCreateQuery *>(ast.get());
2020-03-19 21:14:52 +00:00
assert(!create || create->uuid == table_id.uuid);
if (create)
{
2020-04-06 23:22:44 +00:00
String data_path = "store/" + getPathForUUID(table_id.uuid);
2020-03-19 21:14:52 +00:00
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
{
2020-05-23 22:24:01 +00:00
LOG_WARNING(log, "Cannot parse metadata of partially dropped table {} from {}. Will remove metadata file and data directory. Garbage may be left in /store directory and ZooKeeper.", table_id.getNameForLogs(), dropped_metadata_path);
2020-03-19 21:14:52 +00:00
}
drop_time = Poco::File(dropped_metadata_path).getLastModified().epochTime();
}
2020-04-08 01:02:00 +00:00
std::lock_guard lock(tables_marked_dropped_mutex);
2020-03-19 21:14:52 +00:00
if (ignore_delay)
2020-04-08 01:02:00 +00:00
tables_marked_dropped.push_front({table_id, table, dropped_metadata_path, 0});
2020-03-19 21:14:52 +00:00
else
2020-04-08 01:02:00 +00:00
tables_marked_dropped.push_back({table_id, table, dropped_metadata_path, drop_time});
2020-04-22 20:43:10 +00:00
/// If list of dropped tables was empty, start a drop task
2020-04-22 20:49:13 +00:00
if (drop_task && tables_marked_dropped.size() == 1)
2020-04-22 20:43:10 +00:00
(*drop_task)->schedule();
2020-03-19 21:14:52 +00:00
}
void DatabaseCatalog::dropTableDataTask()
{
2020-04-22 20:43:10 +00:00
/// Background task that removes data of tables which were marked as dropped by Atomic databases.
/// Table can be removed when it's not used by queries and drop_delay_sec elapsed since it was marked as dropped.
bool need_reschedule = true;
2020-03-19 21:14:52 +00:00
TableMarkedAsDropped table;
try
{
2020-04-08 01:02:00 +00:00
std::lock_guard lock(tables_marked_dropped_mutex);
2020-03-19 21:14:52 +00:00
time_t current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
2020-04-08 01:02:00 +00:00
auto it = std::find_if(tables_marked_dropped.begin(), tables_marked_dropped.end(), [&](const auto & elem)
2020-03-19 21:14:52 +00:00
{
2020-03-27 22:58:03 +00:00
bool not_in_use = !elem.table || elem.table.unique();
2020-04-22 20:43:10 +00:00
bool old_enough = elem.drop_time + drop_delay_sec < 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-04-08 01:02:00 +00:00
if (it != tables_marked_dropped.end())
2020-03-19 21:14:52 +00:00
{
table = std::move(*it);
2020-05-23 22:24:01 +00:00
LOG_INFO(log, "Will try drop {}", table.table_id.getNameForLogs());
2020-04-08 01:02:00 +00:00
tables_marked_dropped.erase(it);
2020-03-19 21:14:52 +00:00
}
2020-04-22 20:43:10 +00:00
need_reschedule = !tables_marked_dropped.empty();
2020-03-19 21:14:52 +00:00
}
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.");
{
2020-04-08 01:02:00 +00:00
std::lock_guard lock(tables_marked_dropped_mutex);
tables_marked_dropped.emplace_back(std::move(table));
2020-04-22 20:43:10 +00:00
/// If list of dropped tables was empty, schedule a task to retry deletion.
if (tables_marked_dropped.size() == 1)
need_reschedule = true;
2020-03-19 21:14:52 +00:00
}
}
}
2020-04-22 20:43:10 +00:00
/// Do not schedule a task if there is no tables to drop
if (need_reschedule)
(*drop_task)->scheduleAfter(reschedule_time_ms);
2020-03-19 21:14:52 +00:00
}
void DatabaseCatalog::dropTableFinally(const TableMarkedAsDropped & table) const
{
if (table.table)
{
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
2020-04-06 23:22:44 +00:00
String data_path = global_context->getPath() + "store/" + getPathForUUID(table.table_id.uuid);
2020-03-19 21:14:52 +00:00
Poco::File table_data_dir{data_path};
if (table_data_dir.exists())
{
2020-05-23 22:24:01 +00:00
LOG_INFO(log, "Removing data directory {} of dropped table {}", data_path, table.table_id.getNameForLogs());
2020-03-19 21:14:52 +00:00
table_data_dir.remove(true);
}
2020-05-23 22:24:01 +00:00
LOG_INFO(log, "Removing metadata {} of dropped table {}", table.metadata_path, table.table_id.getNameForLogs());
2020-03-19 21:14:52 +00:00
Poco::File(table.metadata_path).remove();
}
2020-04-06 23:22:44 +00:00
String DatabaseCatalog::getPathForUUID(const UUID & uuid)
{
const size_t uuid_prefix_len = 3;
return toString(uuid).substr(0, uuid_prefix_len) + '/' + toString(uuid) + '/';
}
2020-07-15 19:25:31 +00:00
String DatabaseCatalog::resolveDictionaryName(const String & name) const
{
/// If it's dictionary from Atomic database, then we need to convert qualified name to UUID.
/// Try to split name and get id from associated StorageDictionary.
/// If something went wrong, return name as is.
/// TODO support dot in name for dictionaries in Atomic databases
auto pos = name.find('.');
if (pos == std::string::npos || name.find('.', pos + 1) != std::string::npos)
return name;
String maybe_database_name = name.substr(0, pos);
String maybe_table_name = name.substr(pos + 1);
auto db_and_table = tryGetDatabaseAndTable({maybe_database_name, maybe_table_name}, *global_context);
if (!db_and_table.first)
return name;
assert(db_and_table.second);
if (db_and_table.first->getUUID() == UUIDHelpers::Nil)
return name;
if (db_and_table.second->getName() != "Dictionary")
return name;
return toString(db_and_table.second->getStorageID().uuid);
}
2020-02-10 18:31:52 +00:00
2020-08-18 15:15:27 +00:00
DDLGuard::DDLGuard(Map & map_, std::shared_mutex & db_mutex_, std::unique_lock<std::mutex> guards_lock_, const String & elem)
: map(map_), db_mutex(db_mutex_), guards_lock(std::move(guards_lock_))
2020-02-10 18:31:52 +00:00
{
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);
2020-08-18 15:15:27 +00:00
bool is_database = elem.empty();
if (!is_database)
db_mutex.lock_shared();
2020-02-10 18:31:52 +00:00
}
DDLGuard::~DDLGuard()
{
2020-08-18 15:15:27 +00:00
bool is_database = it->first.empty();
if (!is_database)
db_mutex.unlock_shared();
2020-02-10 18:31:52 +00:00
guards_lock.lock();
--it->second.counter;
if (!it->second.counter)
{
table_lock.unlock();
map.erase(it);
}
}
2020-02-03 12:54:36 +00:00
}