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-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-04-08 23:59:39 +00:00
extern const int NULL_POINTER_DEREFERENCE ;
2020-02-03 12:54:36 +00:00
}
2020-03-10 19:36:17 +00:00
TemporaryTableHolder : : TemporaryTableHolder ( const Context & context_ ,
const TemporaryTableHolder : : Creator & creator , const ASTPtr & query )
2020-03-13 15:41:36 +00:00
: global_context ( & context_ . getGlobalContext ( ) )
, temporary_tables ( DatabaseCatalog : : instance ( ) . getDatabaseForTemporaryTables ( ) . get ( ) )
2020-03-10 19:36:17 +00:00
{
ASTPtr original_create ;
ASTCreateQuery * create = dynamic_cast < ASTCreateQuery * > ( query . get ( ) ) ;
String global_name ;
if ( query )
{
original_create = create - > clone ( ) ;
if ( create - > uuid = = UUIDHelpers : : Nil )
create - > uuid = UUIDHelpers : : generateV4 ( ) ;
id = create - > uuid ;
create - > table = " _tmp_ " + toString ( id ) ;
global_name = create - > table ;
create - > database = DatabaseCatalog : : TEMPORARY_DATABASE ;
}
else
{
id = UUIDHelpers : : generateV4 ( ) ;
global_name = " _tmp_ " + toString ( id ) ;
}
auto table_id = StorageID ( DatabaseCatalog : : TEMPORARY_DATABASE , global_name , id ) ;
auto table = creator ( table_id ) ;
2020-03-13 15:41:36 +00:00
temporary_tables - > createTable ( * global_context , global_name , table , original_create ) ;
2020-03-10 19:36:17 +00:00
table - > startup ( ) ;
}
TemporaryTableHolder : : TemporaryTableHolder ( const Context & context_ , const ColumnsDescription & columns , const ASTPtr & query )
: TemporaryTableHolder
(
context_ ,
[ & ] ( const StorageID & table_id )
{
return StorageMemory : : create ( table_id , ColumnsDescription { columns } , ConstraintsDescription { } ) ;
} ,
query
)
{
}
TemporaryTableHolder : : TemporaryTableHolder ( TemporaryTableHolder & & rhs )
2020-03-13 15:41:36 +00:00
: global_context ( rhs . global_context ) , temporary_tables ( rhs . temporary_tables ) , id ( rhs . id )
2020-03-10 19:36:17 +00:00
{
rhs . id = UUIDHelpers : : Nil ;
}
TemporaryTableHolder & TemporaryTableHolder : : operator = ( TemporaryTableHolder & & rhs )
{
id = rhs . id ;
rhs . id = UUIDHelpers : : Nil ;
return * this ;
}
TemporaryTableHolder : : ~ TemporaryTableHolder ( )
{
if ( id ! = UUIDHelpers : : Nil )
2020-03-16 11:38:50 +00:00
temporary_tables - > dropTable ( * global_context , " _tmp_ " + toString ( id ) ) ;
2020-03-10 19:36:17 +00:00
}
StorageID TemporaryTableHolder : : getGlobalTableID ( ) const
{
return StorageID { DatabaseCatalog : : TEMPORARY_DATABASE , " _tmp_ " + toString ( id ) , id } ;
}
StoragePtr TemporaryTableHolder : : getTable ( ) const
{
2020-04-23 16:51:48 +00:00
auto table = temporary_tables - > tryGetTable ( " _tmp_ " + toString ( id ) ) ;
2020-03-10 19:36:17 +00:00
if ( ! table )
throw Exception ( " Temporary table " + getGlobalTableID ( ) . getNameForLogs ( ) + " not found " , ErrorCodes : : LOGICAL_ERROR ) ;
return table ;
}
2020-02-10 13:10:17 +00:00
2020-02-03 12:54:36 +00:00
void DatabaseCatalog : : loadDatabases ( )
{
2020-05-07 11:29:58 +00:00
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-04-23 16:51:48 +00:00
auto db_for_temporary_and_external_tables = std : : make_shared < DatabaseMemory > ( TEMPORARY_DATABASE ) ;
2020-02-10 13:10:17 +00:00
attachDatabase ( TEMPORARY_DATABASE , db_for_temporary_and_external_tables ) ;
2020-03-19 21:14:52 +00:00
loadMarkedAsDroppedTables ( ) ;
auto task_holder = global_context - > getSchedulePool ( ) . createTask ( " DatabaseCatalog " , [ this ] ( ) { this - > dropTableDataTask ( ) ; } ) ;
drop_task = std : : make_unique < BackgroundSchedulePoolTaskHolder > ( std : : move ( task_holder ) ) ;
( * drop_task ) - > activateAndSchedule ( ) ;
2020-02-03 12:54:36 +00:00
}
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 ( ) ;
std : : lock_guard lock ( databases_mutex ) ;
2020-03-17 23:51:35 +00:00
assert ( std : : find_if_not ( uuid_map . begin ( ) , uuid_map . end ( ) , [ ] ( const auto & elem ) { return elem . map . empty ( ) ; } ) = = uuid_map . end ( ) ) ;
2020-02-12 16:54:26 +00:00
databases . clear ( ) ;
view_dependencies . clear ( ) ;
2020-02-03 12:54:36 +00:00
}
DatabaseAndTable DatabaseCatalog : : tryGetByUUID ( const UUID & uuid ) const
{
2020-03-16 09:16:14 +00:00
assert ( uuid ! = UUIDHelpers : : Nil & & getFirstLevelIdx ( uuid ) < uuid_map . size ( ) ) ;
2020-02-03 12:54:36 +00:00
const UUIDToStorageMapPart & map_part = uuid_map [ getFirstLevelIdx ( uuid ) ] ;
std : : lock_guard lock { map_part . mutex } ;
auto it = map_part . map . find ( uuid ) ;
if ( it = = map_part . map . end ( ) )
return { } ;
return it - > second ;
}
2020-03-23 02:12:31 +00:00
DatabaseAndTable DatabaseCatalog : : getTableImpl (
const StorageID & table_id ,
std : : optional < Exception > * exception ) const
2020-02-03 12:54:36 +00:00
{
2020-02-17 13:52:59 +00:00
if ( ! table_id )
{
if ( exception )
exception - > emplace ( " Cannot find table: StorageID is empty " , ErrorCodes : : UNKNOWN_TABLE ) ;
return { } ;
}
2020-03-12 18:04:29 +00:00
if ( table_id . hasUUID ( ) )
{
2020-03-13 15:41:36 +00:00
/// Shortcut for tables which have persistent UUID
2020-03-12 18:04:29 +00:00
auto db_and_table = tryGetByUUID ( table_id . uuid ) ;
if ( ! db_and_table . first | | ! db_and_table . second )
{
assert ( ! db_and_table . first & & ! db_and_table . second ) ;
if ( exception )
exception - > emplace ( " Table " + table_id . getNameForLogs ( ) + " doesn't exist. " , ErrorCodes : : UNKNOWN_TABLE ) ;
return { } ;
}
return db_and_table ;
}
2020-02-03 12:54:36 +00:00
2020-03-13 15:41:36 +00:00
if ( table_id . database_name = = TEMPORARY_DATABASE )
{
/// For temporary tables UUIDs are set in Context::resolveStorageID(...).
/// If table_id has no UUID, then the name of database was specified by user and table_id was not resolved through context.
/// Do not allow access to TEMPORARY_DATABASE because it contains all temporary tables of all contexts and users.
if ( exception )
exception - > emplace ( " Direct access to ` " + String ( TEMPORARY_DATABASE ) + " ` database is not allowed. " , ErrorCodes : : DATABASE_ACCESS_DENIED ) ;
return { } ;
}
2020-02-11 17:25:26 +00:00
DatabasePtr database ;
2020-02-03 12:54:36 +00:00
{
2020-03-23 02:12:31 +00:00
std : : lock_guard lock { databases_mutex } ;
2020-02-11 17:25:26 +00:00
auto it = databases . find ( table_id . getDatabaseName ( ) ) ;
if ( databases . end ( ) = = it )
{
if ( exception )
exception - > emplace ( " Database " + backQuoteIfNeed ( table_id . getDatabaseName ( ) ) + " doesn't exist " ,
ErrorCodes : : UNKNOWN_DATABASE ) ;
return { } ;
}
database = it - > second ;
2020-02-03 12:54:36 +00:00
}
2020-04-23 16:51:48 +00:00
auto table = database - > tryGetTable ( table_id . table_name ) ;
2020-02-03 12:54:36 +00:00
if ( ! table & & exception )
exception - > emplace ( " Table " + table_id . getNameForLogs ( ) + " doesn't exist. " , ErrorCodes : : UNKNOWN_TABLE ) ;
2020-03-04 20:29:52 +00:00
return { database , table } ;
2020-02-03 12:54:36 +00:00
}
void DatabaseCatalog : : assertDatabaseExists ( const String & database_name ) const
{
std : : lock_guard lock { databases_mutex } ;
assertDatabaseExistsUnlocked ( database_name ) ;
}
void DatabaseCatalog : : assertDatabaseDoesntExist ( const String & database_name ) const
{
std : : lock_guard lock { databases_mutex } ;
assertDatabaseDoesntExistUnlocked ( database_name ) ;
}
void DatabaseCatalog : : assertDatabaseExistsUnlocked ( const String & database_name ) const
{
2020-02-10 18:19:35 +00:00
assert ( ! database_name . empty ( ) ) ;
2020-02-03 12:54:36 +00:00
if ( databases . end ( ) = = databases . find ( database_name ) )
throw Exception ( " Database " + backQuoteIfNeed ( database_name ) + " doesn't exist " , ErrorCodes : : UNKNOWN_DATABASE ) ;
}
void DatabaseCatalog : : assertDatabaseDoesntExistUnlocked ( const String & database_name ) const
{
2020-02-10 18:19:35 +00:00
assert ( ! database_name . empty ( ) ) ;
2020-02-03 12:54:36 +00:00
if ( databases . end ( ) ! = databases . find ( database_name ) )
throw Exception ( " Database " + backQuoteIfNeed ( database_name ) + " already exists. " , ErrorCodes : : DATABASE_ALREADY_EXISTS ) ;
}
2020-02-10 13:10:17 +00:00
void DatabaseCatalog : : attachDatabase ( const String & database_name , const DatabasePtr & database )
2020-02-03 12:54:36 +00:00
{
std : : lock_guard lock { databases_mutex } ;
assertDatabaseDoesntExistUnlocked ( database_name ) ;
databases [ database_name ] = database ;
}
2020-02-17 19:28:25 +00:00
DatabasePtr DatabaseCatalog : : detachDatabase ( const String & database_name , bool drop , bool check_empty )
2020-02-03 12:54:36 +00:00
{
2020-03-13 15:41:36 +00:00
if ( database_name = = TEMPORARY_DATABASE )
throw Exception ( " Cannot detach database with temporary tables. " , ErrorCodes : : DATABASE_ACCESS_DENIED ) ;
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 : : shared_ptr < IDatabase > db ;
{
std : : lock_guard lock { databases_mutex } ;
assertDatabaseExistsUnlocked ( database_name ) ;
db = databases . find ( database_name ) - > second ;
2020-02-13 21:00:03 +00:00
2020-04-10 23:02:15 +00:00
if ( check_empty )
{
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-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-02-13 21:00:03 +00:00
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
databases . erase ( database_name ) ;
}
2020-02-13 21:00:03 +00:00
db - > shutdown ( ) ;
if ( drop )
{
/// Delete the database.
db - > drop ( * global_context ) ;
/// Old ClickHouse versions did not store database.sql files
Poco : : File database_metadata_file (
global_context - > getPath ( ) + " metadata/ " + escapeForFileName ( database_name ) + " .sql " ) ;
if ( database_metadata_file . exists ( ) )
database_metadata_file . remove ( false ) ;
}
return db ;
2020-02-03 12:54:36 +00:00
}
2020-02-10 13:10:17 +00:00
DatabasePtr DatabaseCatalog : : getDatabase ( const String & database_name ) const
2020-02-03 12:54:36 +00:00
{
std : : lock_guard lock { databases_mutex } ;
assertDatabaseExistsUnlocked ( database_name ) ;
return databases . find ( database_name ) - > second ;
}
2020-02-10 13:10:17 +00:00
DatabasePtr DatabaseCatalog : : tryGetDatabase ( const String & database_name ) const
2020-02-03 12:54:36 +00:00
{
2020-02-10 13:10:17 +00:00
assert ( ! database_name . empty ( ) ) ;
2020-02-03 12:54:36 +00:00
std : : lock_guard lock { databases_mutex } ;
auto it = databases . find ( database_name ) ;
if ( it = = databases . end ( ) )
return { } ;
return it - > second ;
}
bool DatabaseCatalog : : isDatabaseExist ( const String & database_name ) const
{
2020-02-10 13:10:17 +00:00
assert ( ! database_name . empty ( ) ) ;
2020-02-03 12:54:36 +00:00
std : : lock_guard lock { databases_mutex } ;
return databases . end ( ) ! = databases . find ( database_name ) ;
}
Databases DatabaseCatalog : : getDatabases ( ) const
{
std : : lock_guard lock { databases_mutex } ;
return databases ;
}
2020-03-13 15:41:36 +00:00
bool DatabaseCatalog : : isTableExist ( const DB : : StorageID & table_id ) const
2020-02-03 12:54:36 +00:00
{
2020-03-12 18:04:29 +00:00
if ( table_id . hasUUID ( ) )
return tryGetByUUID ( table_id . uuid ) . second ! = nullptr ;
DatabasePtr db ;
{
std : : lock_guard lock { databases_mutex } ;
auto iter = databases . find ( table_id . database_name ) ;
if ( iter ! = databases . end ( ) )
db = iter - > second ;
}
2020-04-23 16:51:48 +00:00
return db & & db - > isTableExist ( table_id . table_name ) ;
2020-02-03 12:54:36 +00:00
}
2020-03-13 15:41:36 +00:00
void DatabaseCatalog : : assertTableDoesntExist ( const StorageID & table_id ) const
2020-02-03 12:54:36 +00:00
{
2020-03-13 15:41:36 +00:00
if ( isTableExist ( table_id ) )
2020-02-03 12:54:36 +00:00
throw Exception ( " Table " + table_id . getNameForLogs ( ) + " already exists. " , ErrorCodes : : TABLE_ALREADY_EXISTS ) ;
}
DatabasePtr DatabaseCatalog : : getDatabaseForTemporaryTables ( ) const
{
2020-02-10 13:10:17 +00:00
return getDatabase ( TEMPORARY_DATABASE ) ;
}
DatabasePtr DatabaseCatalog : : getSystemDatabase ( ) const
{
return getDatabase ( SYSTEM_DATABASE ) ;
2020-02-03 12:54:36 +00:00
}
void DatabaseCatalog : : addUUIDMapping ( const UUID & uuid , DatabasePtr database , StoragePtr table )
{
2020-03-16 09:16:14 +00:00
assert ( uuid ! = UUIDHelpers : : Nil & & getFirstLevelIdx ( uuid ) < uuid_map . size ( ) ) ;
2020-02-03 12:54:36 +00:00
UUIDToStorageMapPart & map_part = uuid_map [ getFirstLevelIdx ( uuid ) ] ;
std : : lock_guard lock { map_part . mutex } ;
auto [ _ , inserted ] = map_part . map . try_emplace ( uuid , std : : move ( database ) , std : : move ( table ) ) ;
if ( ! inserted )
throw Exception ( " Mapping for table with UUID= " + toString ( uuid ) + " already exists " , ErrorCodes : : LOGICAL_ERROR ) ;
}
void DatabaseCatalog : : removeUUIDMapping ( const UUID & uuid )
{
2020-03-16 09:16:14 +00:00
assert ( uuid ! = UUIDHelpers : : Nil & & getFirstLevelIdx ( uuid ) < uuid_map . size ( ) ) ;
2020-02-03 12:54:36 +00:00
UUIDToStorageMapPart & map_part = uuid_map [ getFirstLevelIdx ( uuid ) ] ;
std : : lock_guard lock { map_part . mutex } ;
if ( ! map_part . map . erase ( uuid ) )
throw Exception ( " Mapping for table with UUID= " + toString ( uuid ) + " doesn't exist " , ErrorCodes : : LOGICAL_ERROR ) ;
}
2020-03-23 00:12:13 +00:00
void DatabaseCatalog : : updateUUIDMapping ( const UUID & uuid , DatabasePtr database , StoragePtr table )
{
assert ( uuid ! = UUIDHelpers : : Nil & & getFirstLevelIdx ( uuid ) < uuid_map . size ( ) ) ;
UUIDToStorageMapPart & map_part = uuid_map [ getFirstLevelIdx ( uuid ) ] ;
std : : lock_guard lock { map_part . mutex } ;
auto it = map_part . map . find ( uuid ) ;
if ( it = = map_part . map . end ( ) )
throw Exception ( " Mapping for table with UUID= " + toString ( uuid ) + " doesn't exist " , ErrorCodes : : LOGICAL_ERROR ) ;
it - > second = std : : make_pair ( std : : move ( database ) , std : : move ( table ) ) ;
}
2020-03-19 21:14:52 +00:00
DatabaseCatalog : : DatabaseCatalog ( Context * global_context_ )
2020-02-13 21:00:03 +00:00
: global_context ( global_context_ ) , log ( & Poco : : Logger : : get ( " DatabaseCatalog " ) )
2020-02-10 13:10:17 +00:00
{
2020-02-13 21:00:03 +00:00
if ( ! global_context )
2020-04-08 23:59:39 +00:00
throw Exception ( " DatabaseCatalog is not initialized. It's a bug. " , ErrorCodes : : NULL_POINTER_DEREFERENCE ) ;
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
{
static DatabaseCatalog database_catalog ( global_context_ ) ;
2020-02-10 13:10:17 +00:00
return database_catalog ;
}
2020-02-13 21:00:03 +00:00
DatabaseCatalog & DatabaseCatalog : : instance ( )
{
return init ( nullptr ) ;
}
2020-04-08 23:59:39 +00:00
void DatabaseCatalog : : shutdown ( )
{
try
{
instance ( ) . shutdownImpl ( ) ;
}
catch ( const Exception & e )
{
/// If catalog was not initialized yet by init(global_context), instance() throws NULL_POINTER_DEREFERENCE.
/// It can happen if some exception was thrown on first steps of startup (e.g. command line arguments parsing).
/// Ignore it.
if ( e . code ( ) = = ErrorCodes : : NULL_POINTER_DEREFERENCE )
return ;
throw ;
}
}
2020-02-10 13:10:17 +00:00
DatabasePtr DatabaseCatalog : : getDatabase ( const String & database_name , const Context & local_context ) const
{
String resolved_database = local_context . resolveDatabase ( database_name ) ;
return getDatabase ( resolved_database ) ;
}
2020-02-12 16:54:26 +00:00
void DatabaseCatalog : : addDependency ( const StorageID & from , const StorageID & where )
{
std : : lock_guard lock { databases_mutex } ;
2020-02-12 18:14:12 +00:00
// FIXME when loading metadata storage may not know UUIDs of it's dependencies, because they are not loaded yet,
// so UUID of `from` is not used here. (same for remove, get and update)
view_dependencies [ { from . getDatabaseName ( ) , from . getTableName ( ) } ] . insert ( where ) ;
2020-02-12 16:54:26 +00:00
}
void DatabaseCatalog : : removeDependency ( const StorageID & from , const StorageID & where )
{
std : : lock_guard lock { databases_mutex } ;
2020-02-12 18:14:12 +00:00
view_dependencies [ { from . getDatabaseName ( ) , from . getTableName ( ) } ] . erase ( where ) ;
2020-02-12 16:54:26 +00:00
}
Dependencies DatabaseCatalog : : getDependencies ( const StorageID & from ) const
{
std : : lock_guard lock { databases_mutex } ;
2020-02-12 18:14:12 +00:00
auto iter = view_dependencies . find ( { from . getDatabaseName ( ) , from . getTableName ( ) } ) ;
2020-02-12 16:54:26 +00:00
if ( iter = = view_dependencies . end ( ) )
return { } ;
return Dependencies ( iter - > second . begin ( ) , iter - > second . end ( ) ) ;
}
void
DatabaseCatalog : : updateDependency ( const StorageID & old_from , const StorageID & old_where , const StorageID & new_from ,
const StorageID & new_where )
{
std : : lock_guard lock { databases_mutex } ;
if ( ! old_from . empty ( ) )
2020-02-12 18:14:12 +00:00
view_dependencies [ { old_from . getDatabaseName ( ) , old_from . getTableName ( ) } ] . erase ( old_where ) ;
2020-02-12 16:54:26 +00:00
if ( ! new_from . empty ( ) )
2020-02-12 18:14:12 +00:00
view_dependencies [ { new_from . getDatabaseName ( ) , new_from . getTableName ( ) } ] . insert ( new_where ) ;
2020-02-12 16:54:26 +00:00
}
2020-02-10 18:31:52 +00:00
std : : unique_ptr < DDLGuard > DatabaseCatalog : : getDDLGuard ( const String & database , const String & table )
{
std : : unique_lock lock ( ddl_guards_mutex ) ;
return std : : make_unique < DDLGuard > ( ddl_guards [ database ] , std : : move ( lock ) , table ) ;
}
2020-03-13 15:41:36 +00:00
bool DatabaseCatalog : : isDictionaryExist ( const StorageID & table_id ) const
2020-02-17 13:52:59 +00:00
{
auto db = tryGetDatabase ( table_id . getDatabaseName ( ) ) ;
2020-04-23 16:51:48 +00:00
return db & & db - > isDictionaryExist ( table_id . getTableName ( ) ) ;
2020-02-17 13:52:59 +00:00
}
StoragePtr DatabaseCatalog : : getTable ( const StorageID & table_id ) const
{
2020-03-30 23:36:23 +00:00
std : : optional < Exception > exc ;
2020-04-23 16:51:48 +00:00
auto res = getTableImpl ( table_id , & 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
}
StoragePtr DatabaseCatalog : : tryGetTable ( const StorageID & table_id ) const
{
2020-04-23 16:51:48 +00:00
return getTableImpl ( table_id , nullptr ) . second ;
2020-03-04 20:29:52 +00:00
}
2020-04-13 14:09:56 +00:00
DatabaseAndTable DatabaseCatalog : : getDatabaseAndTable ( const StorageID & table_id ) const
{
std : : optional < Exception > exc ;
2020-04-23 16:51:48 +00:00
auto res = getTableImpl ( table_id , & exc ) ;
2020-04-13 14:09:56 +00:00
if ( ! res . second )
throw Exception ( * exc ) ;
return res ;
}
2020-03-04 20:29:52 +00:00
DatabaseAndTable DatabaseCatalog : : tryGetDatabaseAndTable ( const StorageID & table_id ) const
{
2020-04-23 16:51:48 +00:00
return getTableImpl ( table_id , 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/ " ;
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 ( SettingMaxThreads ( ) . getAutoValue ( ) ) ;
for ( const auto & elem : dropped_metadata )
{
pool . scheduleOrThrowOnError ( [ & ] ( )
{
this - > enqueueDroppedTableCleanup ( elem . second , nullptr , elem . first ) ;
} ) ;
}
pool . wait ( ) ;
}
String DatabaseCatalog : : getPathForDroppedMetadata ( const StorageID & table_id ) const
{
return global_context - > getPath ( ) + " metadata_dropped/ " +
escapeForFileName ( table_id . getDatabaseName ( ) ) + " . " +
escapeForFileName ( table_id . getTableName ( ) ) + " . " +
toString ( table_id . uuid ) + " .sql " ;
}
void DatabaseCatalog : : enqueueDroppedTableCleanup ( StorageID table_id , StoragePtr table , String dropped_metadata_path , bool ignore_delay )
{
assert ( table_id . hasUUID ( ) ) ;
assert ( ! table | | table - > getStorageID ( ) . uuid = = table_id . uuid ) ;
assert ( dropped_metadata_path = = getPathForDroppedMetadata ( table_id ) ) ;
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-02-10 18:31:52 +00:00
DDLGuard : : DDLGuard ( Map & map_ , std : : unique_lock < std : : mutex > guards_lock_ , const String & elem )
: map ( map_ ) , guards_lock ( std : : move ( guards_lock_ ) )
{
it = map . emplace ( elem , Entry { std : : make_unique < std : : mutex > ( ) , 0 } ) . first ;
+ + it - > second . counter ;
guards_lock . unlock ( ) ;
table_lock = std : : unique_lock ( * it - > second . mutex ) ;
}
DDLGuard : : ~ DDLGuard ( )
{
guards_lock . lock ( ) ;
- - it - > second . counter ;
if ( ! it - > second . counter )
{
table_lock . unlock ( ) ;
map . erase ( it ) ;
}
}
2020-02-03 12:54:36 +00:00
}