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-09-14 19:25:02 +00:00
# include <Databases/DatabaseOnDisk.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>
2021-01-13 17:59:20 +00:00
# include <Storages/LiveView/TemporaryLiveViewCleaner.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-10-25 17:27:21 +00:00
# include <Common/CurrentMetrics.h>
2021-10-02 07:13:14 +00:00
# include <base/logger_useful.h>
2021-05-06 06:07:58 +00:00
# include <Poco/Util/AbstractConfiguration.h>
2021-04-28 10:42:07 +00:00
# include <filesystem>
2021-05-28 21:57:53 +00:00
# include <Common/filesystemHelpers.h>
2020-02-03 12:54:36 +00:00
2021-10-27 23:10:39 +00:00
# include "config_core.h"
2020-09-14 19:25:02 +00:00
# if USE_MYSQL
2021-07-26 18:17:28 +00:00
# include <Databases / MySQL / MaterializedMySQLSyncThread.h>
# include <Storages / StorageMaterializedMySQL.h>
2020-09-14 19:25:02 +00:00
# endif
2021-07-01 07:33:58 +00:00
# if USE_LIBPQXX
# include <Storages / PostgreSQL / StorageMaterializedPostgreSQL.h>
2021-08-27 12:50:45 +00:00
# include <Databases / PostgreSQL / DatabaseMaterializedPostgreSQL.h>
2021-07-01 07:33:58 +00:00
# endif
2021-04-28 10:42:07 +00:00
namespace fs = std : : filesystem ;
2020-10-25 17:27:21 +00:00
namespace CurrentMetrics
{
extern const Metric TablesToDropQueueSize ;
}
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 ;
2021-11-01 18:53:07 +00:00
extern const int HAVE_DEPENDENT_OBJECTS ;
2020-02-03 12:54:36 +00:00
}
2021-04-10 23:33:54 +00:00
TemporaryTableHolder : : TemporaryTableHolder ( ContextPtr context_ , const TemporaryTableHolder : : Creator & creator , const ASTPtr & query )
: WithContext ( context_ - > getGlobalContext ( ) )
2020-03-13 15:41:36 +00:00
, 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 ;
2021-05-08 14:21:36 +00:00
if ( create )
2020-03-10 19:36:17 +00:00
{
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 ) ;
2021-04-10 23:33:54 +00:00
temporary_tables - > createTable ( getContext ( ) , global_name , table , original_create ) ;
2020-03-10 19:36:17 +00:00
table - > startup ( ) ;
}
2020-05-29 02:08:48 +00:00
TemporaryTableHolder : : TemporaryTableHolder (
2021-04-10 23:33:54 +00:00
ContextPtr context_ ,
2020-05-29 02:08:48 +00:00
const ColumnsDescription & columns ,
const ConstraintsDescription & constraints ,
2020-09-04 08:36:47 +00:00
const ASTPtr & query ,
bool create_for_global_subquery )
2021-04-23 12:18:23 +00:00
: TemporaryTableHolder (
context_ ,
2021-05-13 06:22:05 +00:00
[ & ] ( const StorageID & table_id )
{
2021-04-23 12:18:23 +00:00
auto storage = StorageMemory : : create ( table_id , ColumnsDescription { columns } , ConstraintsDescription { constraints } , String { } ) ;
2020-09-04 08:36:47 +00:00
2021-04-23 12:18:23 +00:00
if ( create_for_global_subquery )
storage - > delayReadForGlobalSubqueries ( ) ;
2020-09-04 08:36:47 +00:00
2021-04-23 12:18:23 +00:00
return storage ;
} ,
query )
2020-03-10 19:36:17 +00:00
{
}
TemporaryTableHolder : : TemporaryTableHolder ( TemporaryTableHolder & & rhs )
2021-04-10 23:33:54 +00:00
: WithContext ( rhs . 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 )
2021-04-10 23:33:54 +00:00
temporary_tables - > dropTable ( getContext ( ) , " _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
{
2021-04-10 23:33:54 +00:00
auto table = temporary_tables - > tryGetTable ( " _tmp_ " + toString ( id ) , getContext ( ) ) ;
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
2021-06-03 14:05:37 +00:00
void DatabaseCatalog : : initializeAndLoadTemporaryDatabase ( )
2020-02-03 12:54:36 +00:00
{
2021-04-10 23:33:54 +00:00
drop_delay_sec = getContext ( ) - > getConfigRef ( ) . getInt ( " database_atomic_delay_before_drop_table_sec " , default_drop_delay_sec ) ;
2020-02-03 12:54:36 +00:00
2021-04-10 23:33:54 +00:00
auto db_for_temporary_and_external_tables = std : : make_shared < DatabaseMemory > ( TEMPORARY_DATABASE , getContext ( ) ) ;
2020-07-08 14:28:07 +00:00
attachDatabase ( TEMPORARY_DATABASE , db_for_temporary_and_external_tables ) ;
2021-02-10 14:12:49 +00:00
}
2020-03-19 21:14:52 +00:00
2021-02-10 14:12:49 +00:00
void DatabaseCatalog : : loadDatabases ( )
{
2021-04-10 23:33:54 +00:00
auto task_holder = getContext ( ) - > getSchedulePool ( ) . createTask ( " DatabaseCatalog " , [ this ] ( ) { this - > dropTableDataTask ( ) ; } ) ;
2020-03-19 21:14:52 +00:00
drop_task = std : : make_unique < BackgroundSchedulePoolTaskHolder > ( std : : move ( task_holder ) ) ;
2020-11-01 17:37:54 +00:00
( * drop_task ) - > activate ( ) ;
std : : lock_guard lock { tables_marked_dropped_mutex } ;
if ( ! tables_marked_dropped . empty ( ) )
( * drop_task ) - > schedule ( ) ;
2021-01-13 17:59:20 +00:00
/// Another background thread which drops temporary LiveViews.
/// We should start it after loadMarkedAsDroppedTables() to avoid race condition.
2021-04-08 13:13:10 +00:00
TemporaryLiveViewCleaner : : instance ( ) . startup ( ) ;
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
{
2021-01-13 17:59:20 +00:00
TemporaryLiveViewCleaner : : 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 ( ) ;
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-10-25 21:51:30 +00:00
assert ( std : : find_if ( uuid_map . begin ( ) , uuid_map . end ( ) , [ ] ( const auto & elem )
{
2020-11-12 23:27:18 +00:00
/// Ensure that all UUID mappings are empty (i.e. all mappings contain nullptr instead of a pointer to storage)
2020-10-27 20:52:49 +00:00
const auto & not_empty_mapping = [ ] ( const auto & mapping )
{
auto & table = mapping . second . second ;
return table ;
} ;
2020-10-25 21:51:30 +00:00
auto it = std : : find_if ( elem . map . begin ( ) , elem . map . end ( ) , not_empty_mapping ) ;
return it ! = elem . map . end ( ) ;
} ) = = uuid_map . end ( ) ) ;
2020-02-12 16:54:26 +00:00
databases . clear ( ) ;
2020-07-06 08:30:11 +00:00
db_uuid_map . clear ( ) ;
2020-02-12 16:54:26 +00:00
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 ,
2021-04-10 23:33:54 +00:00
ContextPtr context_ ,
2020-03-23 02:12:31 +00:00
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 )
2020-12-14 14:21:31 +00:00
exception - > emplace ( ErrorCodes : : UNKNOWN_TABLE , " Cannot find table: StorageID is empty " ) ;
2020-02-17 13:52:59 +00:00
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 )
2020-12-14 14:21:31 +00:00
exception - > emplace ( ErrorCodes : : UNKNOWN_TABLE , " Table {} doesn't exist " , table_id . getNameForLogs ( ) ) ;
2020-03-12 18:04:29 +00:00
return { } ;
}
2020-09-14 19:25:02 +00:00
2021-07-01 07:33:58 +00:00
# if USE_LIBPQXX
if ( ! context_ - > isInternalQuery ( ) & & ( db_and_table . first - > getEngineName ( ) = = " MaterializedPostgreSQL " ) )
{
2021-08-27 12:50:45 +00:00
db_and_table . second = std : : make_shared < StorageMaterializedPostgreSQL > ( std : : move ( db_and_table . second ) , getContext ( ) ,
assert_cast < const DatabaseMaterializedPostgreSQL * > ( db_and_table . first . get ( ) ) - > getPostgreSQLDatabaseName ( ) ,
db_and_table . second - > getStorageID ( ) . table_name ) ;
2021-07-01 07:33:58 +00:00
}
# endif
2020-09-14 19:25:02 +00:00
# if USE_MYSQL
2021-07-26 18:17:28 +00:00
/// It's definitely not the best place for this logic, but behaviour must be consistent with DatabaseMaterializedMySQL::tryGetTable(...)
if ( db_and_table . first - > getEngineName ( ) = = " MaterializedMySQL " )
2020-09-14 19:25:02 +00:00
{
2021-07-26 18:17:28 +00:00
if ( ! MaterializedMySQLSyncThread : : isMySQLSyncThread ( ) )
db_and_table . second = std : : make_shared < StorageMaterializedMySQL > ( std : : move ( db_and_table . second ) , db_and_table . first . get ( ) ) ;
2020-09-14 19:25:02 +00:00
}
# endif
2020-03-12 18:04:29 +00:00
return db_and_table ;
}
2020-02-03 12:54:36 +00:00
2021-07-01 07:33:58 +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 )
2020-12-14 14:21:31 +00:00
exception - > emplace ( ErrorCodes : : DATABASE_ACCESS_DENIED , " Direct access to `{}` database is not allowed " , String ( TEMPORARY_DATABASE ) ) ;
2020-03-13 15:41:36 +00:00
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 )
2020-12-14 14:21:31 +00:00
exception - > emplace ( ErrorCodes : : UNKNOWN_DATABASE , " Database {} doesn't exist " , backQuoteIfNeed ( table_id . getDatabaseName ( ) ) ) ;
2020-02-11 17:25:26 +00:00
return { } ;
}
database = it - > second ;
2020-02-03 12:54:36 +00:00
}
2021-04-10 23:33:54 +00:00
auto table = database - > tryGetTable ( table_id . table_name , context_ ) ;
2020-02-03 12:54:36 +00:00
if ( ! table & & exception )
2020-12-14 14:21:31 +00:00
exception - > emplace ( ErrorCodes : : UNKNOWN_TABLE , " Table {} doesn't exist " , table_id . getNameForLogs ( ) ) ;
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 ) ;
2020-07-06 08:30:11 +00:00
databases . emplace ( database_name , database ) ;
2020-07-08 14:28:07 +00:00
UUID db_uuid = database - > getUUID ( ) ;
if ( db_uuid ! = UUIDHelpers : : Nil )
db_uuid_map . emplace ( db_uuid , database ) ;
2020-02-03 12:54:36 +00:00
}
2021-10-13 10:34:18 +00:00
DatabasePtr DatabaseCatalog : : detachDatabase ( ContextPtr local_context , 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
{
2020-04-23 16:51:48 +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-09-14 19:25:02 +00:00
if ( ! drop )
db - > assertCanBeDetached ( false ) ;
2020-04-10 23:02:15 +00:00
}
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.
2021-10-13 10:34:18 +00:00
db - > drop ( local_context ) ;
2020-02-13 21:00:03 +00:00
/// Old ClickHouse versions did not store database.sql files
2021-10-13 10:34:18 +00:00
/// Remove metadata dir (if exists) to avoid recreation of .sql file on server startup
fs : : path database_metadata_dir = fs : : path ( getContext ( ) - > getPath ( ) ) / " metadata " / escapeForFileName ( database_name ) ;
fs : : remove ( database_metadata_dir ) ;
2021-04-28 10:42:07 +00:00
fs : : path database_metadata_file = fs : : path ( getContext ( ) - > getPath ( ) ) / " metadata " / ( escapeForFileName ( database_name ) + " .sql " ) ;
2021-10-13 10:34:18 +00:00
fs : : remove ( database_metadata_file ) ;
2020-02-13 21:00:03 +00:00
}
return db ;
2020-02-03 12:54:36 +00:00
}
2021-11-02 12:58:45 +00:00
void DatabaseCatalog : : updateDatabaseName ( const String & old_name , const String & new_name , const Strings & tables_in_database )
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 ) ;
2021-11-02 12:58:45 +00:00
for ( const auto & table_name : tables_in_database )
{
QualifiedTableName new_table_name { new_name , table_name } ;
auto dependencies = tryRemoveLoadingDependenciesUnlocked ( QualifiedTableName { old_name , table_name } , /* check_dependencies */ false ) ;
DependenciesInfos new_info ;
for ( const auto & dependency : dependencies )
new_info [ dependency ] . dependent_database_objects . insert ( new_table_name ) ;
new_info [ new_table_name ] . dependencies = std : : move ( dependencies ) ;
mergeDependenciesGraphs ( loading_dependencies , new_info ) ;
}
2020-07-07 12:11:58 +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 ;
}
2020-07-06 08:30:11 +00:00
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
{
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 ;
}
2021-04-10 23:33:54 +00:00
bool DatabaseCatalog : : isTableExist ( const DB : : StorageID & table_id , ContextPtr 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 ;
}
2021-04-10 23:33:54 +00:00
return db & & db - > isTableExist ( table_id . table_name , context_ ) ;
2020-02-03 12:54:36 +00:00
}
2021-04-10 23:33:54 +00:00
void DatabaseCatalog : : assertTableDoesntExist ( const StorageID & table_id , ContextPtr context_ ) const
2020-02-03 12:54:36 +00:00
{
2021-04-10 23:33:54 +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
{
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
}
2020-10-27 20:52:49 +00:00
void DatabaseCatalog : : addUUIDMapping ( const UUID & uuid )
{
addUUIDMapping ( uuid , nullptr , nullptr ) ;
}
2020-10-25 17:27:21 +00:00
void DatabaseCatalog : : addUUIDMapping ( const UUID & uuid , const DatabasePtr & database , const StoragePtr & table )
2020-02-03 12:54:36 +00:00
{
2020-03-16 09:16:14 +00:00
assert ( uuid ! = UUIDHelpers : : Nil & & getFirstLevelIdx ( uuid ) < uuid_map . size ( ) ) ;
2020-10-25 17:27:21 +00:00
assert ( ( database & & table ) | | ( ! database & & ! table ) ) ;
2020-02-03 12:54:36 +00:00
UUIDToStorageMapPart & map_part = uuid_map [ getFirstLevelIdx ( uuid ) ] ;
std : : lock_guard lock { map_part . mutex } ;
2020-10-25 17:27:21 +00:00
auto [ it , inserted ] = map_part . map . try_emplace ( uuid , database , table ) ;
if ( inserted )
return ;
auto & prev_database = it - > second . first ;
auto & prev_table = it - > second . second ;
assert ( ( prev_database & & prev_table ) | | ( ! prev_database & & ! prev_table ) ) ;
if ( ! prev_table & & table )
{
/// It's empty mapping, it was created to "lock" UUID and prevent collision. Just update it.
prev_database = database ;
prev_table = table ;
return ;
}
/// We are trying to replace existing mapping (prev_table != nullptr), it's logical error
if ( table )
throw Exception ( ErrorCodes : : LOGICAL_ERROR , " Mapping for table with UUID={} already exists " , toString ( uuid ) ) ;
2020-10-15 16:01:23 +00:00
/// Normally this should never happen, but it's possible when the same UUIDs are explicitly specified in different CREATE queries,
/// so it's not LOGICAL_ERROR
2020-10-25 17:27:21 +00:00
throw Exception ( ErrorCodes : : TABLE_ALREADY_EXISTS , " Mapping for table with UUID={} already exists. It happened due to UUID collision, "
" most likely because some not random UUIDs were manually specified in CREATE queries. " , toString ( uuid ) ) ;
2020-02-03 12:54:36 +00:00
}
void DatabaseCatalog : : removeUUIDMapping ( const UUID & uuid )
2020-10-25 17:27:21 +00:00
{
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 ( ErrorCodes : : LOGICAL_ERROR , " Mapping for table with UUID={} doesn't exist " , toString ( uuid ) ) ;
it - > second = { } ;
}
void DatabaseCatalog : : removeUUIDMappingFinally ( const UUID & uuid )
2020-02-03 12:54:36 +00:00
{
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 ) )
2020-10-25 17:27:21 +00:00
throw Exception ( ErrorCodes : : LOGICAL_ERROR , " Mapping for table with UUID={} doesn't exist " , toString ( uuid ) ) ;
2020-02-03 12:54:36 +00:00
}
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 ( ) ) ;
2020-10-25 17:27:21 +00:00
assert ( database & & table ) ;
2020-03-23 00:12:13 +00:00
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 ( ) )
2020-10-25 17:27:21 +00:00
throw Exception ( ErrorCodes : : LOGICAL_ERROR , " Mapping for table with UUID={} doesn't exist " , toString ( uuid ) ) ;
auto & prev_database = it - > second . first ;
auto & prev_table = it - > second . second ;
assert ( prev_database & & prev_table ) ;
prev_database = std : : move ( database ) ;
prev_table = std : : move ( table ) ;
2020-03-23 00:12:13 +00:00
}
2020-06-03 13:34:36 +00:00
std : : unique_ptr < DatabaseCatalog > DatabaseCatalog : : database_catalog ;
2021-05-31 14:49:02 +00:00
DatabaseCatalog : : DatabaseCatalog ( ContextMutablePtr global_context_ )
: WithMutableContext ( global_context_ ) , log ( & Poco : : Logger : : get ( " DatabaseCatalog " ) )
2020-02-10 13:10:17 +00:00
{
2021-04-10 23:33:54 +00:00
TemporaryLiveViewCleaner : : init ( global_context_ ) ;
2020-02-13 21:00:03 +00:00
}
2021-05-31 14:49:02 +00:00
DatabaseCatalog & DatabaseCatalog : : init ( ContextMutablePtr global_context_ )
2020-02-13 21:00:03 +00:00
{
2020-06-03 12:59:11 +00:00
if ( database_catalog )
{
2020-06-04 11:05:34 +00:00
throw Exception ( " Database catalog is initialized twice. This is a bug. " ,
2020-06-03 12:59:11 +00:00
ErrorCodes : : LOGICAL_ERROR ) ;
}
database_catalog . reset ( new DatabaseCatalog ( global_context_ ) ) ;
return * database_catalog ;
2020-02-10 13:10:17 +00:00
}
2020-02-13 21:00:03 +00:00
DatabaseCatalog & DatabaseCatalog : : instance ( )
{
2020-06-03 12:59:11 +00:00
if ( ! database_catalog )
{
2020-06-04 11:06:07 +00:00
throw Exception ( " Database catalog is not initialized. This is a bug. " ,
2020-06-03 12:59:11 +00:00
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 ( )
{
2020-06-03 12:59:11 +00:00
// 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
{
2020-06-03 12:59:11 +00:00
database_catalog - > shutdownImpl ( ) ;
2020-04-08 23:59:39 +00:00
}
}
2021-04-10 23:33:54 +00:00
DatabasePtr DatabaseCatalog : : getDatabase ( const String & database_name , ContextPtr local_context ) const
2020-02-10 13:10:17 +00:00
{
2021-04-10 23:33:54 +00:00
String resolved_database = local_context - > resolveDatabase ( database_name ) ;
2020-02-10 13:10:17 +00:00
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
}
2021-02-04 19:41:44 +00:00
DDLGuardPtr DatabaseCatalog : : getDDLGuard ( const String & database , const String & table )
2020-02-10 18:31:52 +00:00
{
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 ;
2020-10-13 15:00:36 +00:00
return std : : make_unique < DDLGuard > ( db_guard . first , db_guard . second , std : : move ( lock ) , table , database ) ;
2020-08-18 15:15:27 +00:00
}
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
{
2021-04-26 10:20:57 +00:00
auto storage = tryGetTable ( table_id , getContext ( ) ) ;
2021-04-22 14:09:30 +00:00
bool storage_is_dictionary = storage & & storage - > isDictionary ( ) ;
return storage_is_dictionary ;
2020-02-17 13:52:59 +00:00
}
2021-04-10 23:33:54 +00:00
StoragePtr DatabaseCatalog : : getTable ( const StorageID & table_id , ContextPtr local_context ) const
2020-02-17 13:52:59 +00:00
{
2020-03-30 23:36:23 +00:00
std : : optional < Exception > exc ;
2021-04-10 23:33:54 +00:00
auto res = getTableImpl ( table_id , local_context , & exc ) ;
2020-03-30 23:36:23 +00:00
if ( ! res . second )
throw Exception ( * exc ) ;
return res . second ;
2020-02-17 13:52:59 +00:00
}
2021-04-10 23:33:54 +00:00
StoragePtr DatabaseCatalog : : tryGetTable ( const StorageID & table_id , ContextPtr local_context ) const
2020-02-17 13:52:59 +00:00
{
2021-04-10 23:33:54 +00:00
return getTableImpl ( table_id , local_context , nullptr ) . second ;
2020-03-04 20:29:52 +00:00
}
2021-04-10 23:33:54 +00:00
DatabaseAndTable DatabaseCatalog : : getDatabaseAndTable ( const StorageID & table_id , ContextPtr local_context ) const
2020-04-13 14:09:56 +00:00
{
std : : optional < Exception > exc ;
2021-04-10 23:33:54 +00:00
auto res = getTableImpl ( table_id , local_context , & exc ) ;
2020-04-13 14:09:56 +00:00
if ( ! res . second )
throw Exception ( * exc ) ;
return res ;
}
2021-04-10 23:33:54 +00:00
DatabaseAndTable DatabaseCatalog : : tryGetDatabaseAndTable ( const StorageID & table_id , ContextPtr local_context ) const
2020-03-04 20:29:52 +00:00
{
2021-04-10 23:33:54 +00:00
return getTableImpl ( table_id , local_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 ;
2021-04-10 23:33:54 +00:00
String path = getContext ( ) - > getPath ( ) + " metadata_dropped/ " ;
2020-06-24 19:03:28 +00:00
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 ) ) ;
}
2020-10-25 17:27:21 +00:00
LOG_INFO ( log , " Found {} partially dropped tables. Will load them and retry removal. " , dropped_metadata . size ( ) ) ;
2020-07-16 23:12:47 +00:00
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
{
2021-04-10 23:33:54 +00:00
return getContext ( ) - > getPath ( ) + " metadata_dropped/ " +
2020-03-19 21:14:52 +00:00
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 )
2020-09-09 13:32:50 +00:00
{
2020-03-19 21:14:52 +00:00
drop_time = std : : chrono : : system_clock : : to_time_t ( std : : chrono : : system_clock : : now ( ) ) ;
2020-09-09 13:32:50 +00:00
table - > is_dropped = true ;
}
2020-03-19 21:14:52 +00:00
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 ) ;
2021-04-10 23:33:54 +00:00
ASTPtr ast = DatabaseOnDisk : : parseQueryFromMetadata (
log , getContext ( ) , 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
{
2021-04-10 23:33:54 +00:00
table = createTableFromAST ( * create , table_id . getDatabaseName ( ) , data_path , getContext ( ) , false ) . second ;
2020-09-09 13:32:50 +00:00
table - > is_dropped = true ;
2020-03-19 21:14:52 +00:00
}
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
}
2020-10-27 20:52:49 +00:00
addUUIDMapping ( table_id . uuid ) ;
2021-05-28 18:17:16 +00:00
drop_time = FS : : getModificationTime ( dropped_metadata_path ) ;
2020-03-19 21:14:52 +00:00
}
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-11-01 17:37:54 +00:00
tables_marked_dropped . push_front ( { table_id , table , dropped_metadata_path , drop_time } ) ;
2020-03-19 21:14:52 +00:00
else
2020-11-01 17:37:54 +00:00
tables_marked_dropped . push_back ( { table_id , table , dropped_metadata_path , drop_time + drop_delay_sec } ) ;
2020-09-29 13:42:58 +00:00
tables_marked_dropped_ids . insert ( table_id . uuid ) ;
2020-10-25 17:27:21 +00:00
CurrentMetrics : : add ( CurrentMetrics : : TablesToDropQueueSize , 1 ) ;
2020-11-01 17:37:54 +00:00
/// If list of dropped tables was empty, start a drop task.
/// If ignore_delay is set, schedule drop task as soon as possible.
if ( drop_task & & ( tables_marked_dropped . size ( ) = = 1 | | ignore_delay ) )
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-11-01 17:37:54 +00:00
/// Default reschedule time for the case when we are waiting for reference count to become 1.
size_t schedule_after_ms = reschedule_time_ms ;
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-11-01 17:37:54 +00:00
assert ( ! tables_marked_dropped . empty ( ) ) ;
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-11-01 17:37:54 +00:00
time_t min_drop_time = std : : numeric_limits < time_t > : : max ( ) ;
size_t tables_in_use_count = 0 ;
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-11-01 17:37:54 +00:00
bool old_enough = elem . drop_time < = current_time ;
min_drop_time = std : : min ( min_drop_time , elem . drop_time ) ;
tables_in_use_count + = ! not_in_use ;
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-11-01 17:37:54 +00:00
LOG_INFO ( log , " Have {} tables in drop queue ({} of them are in use), will try drop {} " ,
tables_marked_dropped . size ( ) , tables_in_use_count , table . table_id . getNameForLogs ( ) ) ;
2020-04-08 01:02:00 +00:00
tables_marked_dropped . erase ( it ) ;
2020-11-01 17:37:54 +00:00
/// Schedule the task as soon as possible, while there are suitable tables to drop.
schedule_after_ms = 0 ;
2020-03-19 21:14:52 +00:00
}
2020-11-01 17:37:54 +00:00
else if ( current_time < min_drop_time )
2020-10-25 17:27:21 +00:00
{
2020-11-01 17:37:54 +00:00
/// We are waiting for drop_delay_sec to exceed, no sense to wakeup until min_drop_time.
/// If new table is added to the queue with ignore_delay flag, schedule() is called to wakeup the task earlier.
schedule_after_ms = ( min_drop_time - current_time ) * 1000 ;
LOG_TRACE ( log , " Not found any suitable tables to drop, still have {} tables in drop queue ({} of them are in use). "
" Will check again after {} seconds " , tables_marked_dropped . size ( ) , tables_in_use_count , min_drop_time - current_time ) ;
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 ) ;
2020-09-29 13:42:58 +00:00
std : : lock_guard lock ( tables_marked_dropped_mutex ) ;
2020-09-29 14:15:05 +00:00
[[maybe_unused]] auto removed = tables_marked_dropped_ids . erase ( table . table_id . uuid ) ;
2020-09-29 13:42:58 +00:00
assert ( removed ) ;
2020-03-19 21:14:52 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( log , " Cannot drop table " + table . table_id . getNameForLogs ( ) +
" . Will retry later. " ) ;
{
2020-11-01 17:37:54 +00:00
table . drop_time = std : : chrono : : system_clock : : to_time_t ( std : : chrono : : system_clock : : now ( ) ) + drop_error_cooldown_sec ;
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 )
2020-11-01 17:37:54 +00:00
{
2020-04-22 20:43:10 +00:00
need_reschedule = true ;
2020-11-01 17:37:54 +00:00
schedule_after_ms = drop_error_cooldown_sec * 1000 ;
}
2020-03-19 21:14:52 +00:00
}
}
2020-09-29 13:42:58 +00:00
wait_table_finally_dropped . notify_all ( ) ;
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 )
2020-11-01 17:37:54 +00:00
( * drop_task ) - > scheduleAfter ( schedule_after_ms ) ;
2020-03-19 21:14:52 +00:00
}
2020-10-25 17:27:21 +00:00
void DatabaseCatalog : : dropTableFinally ( const TableMarkedAsDropped & table )
2020-03-19 21:14:52 +00:00
{
if ( table . table )
{
table . table - > drop ( ) ;
}
/// Even if table is not loaded, try remove its data from disk.
/// TODO remove data from all volumes
2021-04-28 10:42:07 +00:00
fs : : path data_path = fs : : path ( getContext ( ) - > getPath ( ) ) / " store " / getPathForUUID ( table . table_id . uuid ) ;
if ( fs : : exists ( data_path ) )
2020-03-19 21:14:52 +00:00
{
2021-04-28 10:42:07 +00:00
LOG_INFO ( log , " Removing data directory {} of dropped table {} " , data_path . string ( ) , table . table_id . getNameForLogs ( ) ) ;
fs : : remove_all ( data_path ) ;
2020-03-19 21:14:52 +00:00
}
2020-05-23 22:24:01 +00:00
LOG_INFO ( log , " Removing metadata {} of dropped table {} " , table . metadata_path , table . table_id . getNameForLogs ( ) ) ;
2021-04-28 10:42:07 +00:00
fs : : remove ( fs : : path ( table . metadata_path ) ) ;
2020-10-25 17:27:21 +00:00
removeUUIDMappingFinally ( table . table_id . uuid ) ;
CurrentMetrics : : sub ( CurrentMetrics : : TablesToDropQueueSize , 1 ) ;
2020-03-19 21:14:52 +00:00
}
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-09-29 13:42:58 +00:00
void DatabaseCatalog : : waitTableFinallyDropped ( const UUID & uuid )
{
if ( uuid = = UUIDHelpers : : Nil )
return ;
2020-10-25 17:27:21 +00:00
LOG_DEBUG ( log , " Waiting for table {} to be finally dropped " , toString ( uuid ) ) ;
2020-09-29 13:42:58 +00:00
std : : unique_lock lock { tables_marked_dropped_mutex } ;
2020-09-29 15:19:30 +00:00
wait_table_finally_dropped . wait ( lock , [ & ] ( )
{
2020-09-29 13:42:58 +00:00
return tables_marked_dropped_ids . count ( uuid ) = = 0 ;
} ) ;
}
2021-11-02 12:58:45 +00:00
void DatabaseCatalog : : addLoadingDependencies ( const QualifiedTableName & table , TableNamesSet & & dependencies )
{
DependenciesInfos new_info ;
for ( const auto & dependency : dependencies )
new_info [ dependency ] . dependent_database_objects . insert ( table ) ;
new_info [ table ] . dependencies = std : : move ( dependencies ) ;
addLoadingDependencies ( new_info ) ;
}
2021-11-01 18:53:07 +00:00
void DatabaseCatalog : : addLoadingDependencies ( const DependenciesInfos & new_infos )
{
std : : lock_guard lock { databases_mutex } ;
mergeDependenciesGraphs ( loading_dependencies , new_infos ) ;
}
DependenciesInfo DatabaseCatalog : : getLoadingDependenciesInfo ( const StorageID & table_id ) const
{
std : : lock_guard lock { databases_mutex } ;
auto it = loading_dependencies . find ( table_id . getQualifiedName ( ) ) ;
if ( it = = loading_dependencies . end ( ) )
return { } ;
return it - > second ;
}
2021-11-02 12:58:45 +00:00
TableNamesSet DatabaseCatalog : : tryRemoveLoadingDependencies ( const StorageID & table_id , bool check_dependencies , bool is_drop_database )
2021-11-01 18:53:07 +00:00
{
QualifiedTableName removing_table = table_id . getQualifiedName ( ) ;
std : : lock_guard lock { databases_mutex } ;
2021-11-02 12:58:45 +00:00
return tryRemoveLoadingDependenciesUnlocked ( removing_table , check_dependencies , is_drop_database ) ;
}
TableNamesSet DatabaseCatalog : : tryRemoveLoadingDependenciesUnlocked ( const QualifiedTableName & removing_table , bool check_dependencies , bool is_drop_database )
{
2021-11-01 18:53:07 +00:00
auto it = loading_dependencies . find ( removing_table ) ;
if ( it = = loading_dependencies . end ( ) )
2021-11-02 12:58:45 +00:00
return { } ;
2021-11-01 18:53:07 +00:00
TableNamesSet & dependent = it - > second . dependent_database_objects ;
2021-11-02 12:58:45 +00:00
if ( ! dependent . empty ( ) )
2021-11-01 18:53:07 +00:00
{
2021-11-02 12:58:45 +00:00
if ( check_dependencies & & ! is_drop_database )
throw Exception ( ErrorCodes : : HAVE_DEPENDENT_OBJECTS , " Cannot drop or rename {}, because some tables depend on it: {} " ,
removing_table , fmt : : join ( dependent , " , " ) ) ;
2021-11-01 18:53:07 +00:00
/// For DROP DATABASE we should ignore dependent tables from the same database.
/// TODO unload tables in reverse topological order and remove this code
2021-11-02 12:58:45 +00:00
if ( check_dependencies )
{
TableNames from_other_databases ;
for ( const auto & table : dependent )
if ( table . database ! = removing_table . database )
from_other_databases . push_back ( table ) ;
if ( ! from_other_databases . empty ( ) )
throw Exception ( ErrorCodes : : HAVE_DEPENDENT_OBJECTS , " Cannot drop or rename {}, because some tables depend on it: {} " ,
removing_table , fmt : : join ( from_other_databases , " , " ) ) ;
}
2021-11-01 18:53:07 +00:00
for ( const auto & table : dependent )
{
[[maybe_unused]] bool removed = loading_dependencies [ table ] . dependencies . erase ( removing_table ) ;
assert ( removed ) ;
}
dependent . clear ( ) ;
}
2021-11-02 12:58:45 +00:00
TableNamesSet dependencies = it - > second . dependencies ;
2021-11-01 18:53:07 +00:00
for ( const auto & table : dependencies )
{
[[maybe_unused]] bool removed = loading_dependencies [ table ] . dependent_database_objects . erase ( removing_table ) ;
assert ( removed ) ;
}
loading_dependencies . erase ( it ) ;
2021-11-02 12:58:45 +00:00
return dependencies ;
}
void DatabaseCatalog : : checkTableCanBeRemovedOrRenamed ( const StorageID & table_id ) const
{
QualifiedTableName removing_table = table_id . getQualifiedName ( ) ;
std : : lock_guard lock { databases_mutex } ;
auto it = loading_dependencies . find ( removing_table ) ;
if ( it = = loading_dependencies . end ( ) )
return ;
const TableNamesSet & dependent = it - > second . dependent_database_objects ;
if ( ! dependent . empty ( ) )
throw Exception ( ErrorCodes : : HAVE_DEPENDENT_OBJECTS , " Cannot drop or rename {}, because some tables depend on it: {} " ,
table_id . getNameForLogs ( ) , fmt : : join ( dependent , " , " ) ) ;
2021-11-01 18:53:07 +00:00
}
2020-02-10 18:31:52 +00:00
2020-10-13 15:00:36 +00:00
DDLGuard : : DDLGuard ( Map & map_ , std : : shared_mutex & db_mutex_ , std : : unique_lock < std : : mutex > guards_lock_ , const String & elem , const String & database_name )
2020-08-18 15:15:27 +00:00
: 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 ) ;
2021-02-02 19:39:04 +00:00
is_database_guard = elem . empty ( ) ;
if ( ! is_database_guard )
2020-10-13 15:00:36 +00:00
{
bool locked_database_for_read = db_mutex . try_lock_shared ( ) ;
if ( ! locked_database_for_read )
{
2021-02-02 19:39:04 +00:00
releaseTableLock ( ) ;
2020-10-13 15:00:36 +00:00
throw Exception ( ErrorCodes : : UNKNOWN_DATABASE , " Database {} is currently dropped or renamed " , database_name ) ;
}
}
2020-02-10 18:31:52 +00:00
}
2021-02-02 19:39:04 +00:00
void DDLGuard : : releaseTableLock ( ) noexcept
2020-02-10 18:31:52 +00:00
{
2021-02-02 19:39:04 +00:00
if ( table_lock_removed )
return ;
table_lock_removed = true ;
2020-02-10 18:31:52 +00:00
guards_lock . lock ( ) ;
2021-02-03 20:02:37 +00:00
UInt32 counter = - - it - > second . counter ;
table_lock . unlock ( ) ;
if ( counter = = 0 )
2020-02-10 18:31:52 +00:00
map . erase ( it ) ;
2021-02-02 19:39:04 +00:00
guards_lock . unlock ( ) ;
2020-02-10 18:31:52 +00:00
}
2020-10-13 15:00:36 +00:00
DDLGuard : : ~ DDLGuard ( )
{
2021-02-02 19:39:04 +00:00
if ( ! is_database_guard )
2020-10-13 15:00:36 +00:00
db_mutex . unlock_shared ( ) ;
2021-02-02 19:39:04 +00:00
releaseTableLock ( ) ;
2020-10-13 15:00:36 +00:00
}
2020-02-03 12:54:36 +00:00
}