DatabaseCatalog.cpp 27.2 KB
Newer Older
A
Alexander Tokmakov 已提交
1 2 3
#include <Interpreters/DatabaseCatalog.h>
#include <Interpreters/Context.h>
#include <Interpreters/loadMetadata.h>
A
Alexander Tokmakov 已提交
4
#include <Storages/IStorage.h>
A
Alexander Tokmakov 已提交
5 6
#include <Databases/IDatabase.h>
#include <Databases/DatabaseMemory.h>
7
#include <Databases/DatabaseAtomic.h>
8
#include <Poco/File.h>
9
#include <Common/quoteString.h>
A
Alexander Tokmakov 已提交
10
#include <Storages/StorageMemory.h>
11 12 13 14
#include <Core/BackgroundSchedulePool.h>
#include <Parsers/formatAST.h>
#include <IO/ReadHelpers.h>
#include <Poco/DirectoryIterator.h>
15
#include <Common/renameat2.h>
A
Alexander Tokmakov 已提交
16

17 18
#include <filesystem>

A
Alexander Tokmakov 已提交
19 20 21 22 23 24 25 26 27
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;
28
    extern const int DATABASE_NOT_EMPTY;
A
fixes  
Alexander Tokmakov 已提交
29
    extern const int DATABASE_ACCESS_DENIED;
A
Alexander Tokmakov 已提交
30
    extern const int LOGICAL_ERROR;
A
Alexander Tokmakov 已提交
31 32
}

A
Alexander Tokmakov 已提交
33 34
TemporaryTableHolder::TemporaryTableHolder(const Context & context_,
                                           const TemporaryTableHolder::Creator & creator, const ASTPtr & query)
A
Alexander Tokmakov 已提交
35 36
    : global_context(&context_.getGlobalContext())
    , temporary_tables(DatabaseCatalog::instance().getDatabaseForTemporaryTables().get())
A
Alexander Tokmakov 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
{
    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);
A
Alexander Tokmakov 已提交
58
    temporary_tables->createTable(*global_context, global_name, table, original_create);
A
Alexander Tokmakov 已提交
59 60 61 62
    table->startup();
}


63 64 65 66 67
TemporaryTableHolder::TemporaryTableHolder(
    const Context & context_,
    const ColumnsDescription & columns,
    const ConstraintsDescription & constraints,
    const ASTPtr & query)
A
Alexander Tokmakov 已提交
68 69 70 71 72
    : TemporaryTableHolder
      (
          context_,
          [&](const StorageID & table_id)
          {
73
              return StorageMemory::create(table_id, ColumnsDescription{columns}, ConstraintsDescription{constraints});
A
Alexander Tokmakov 已提交
74 75 76 77 78 79 80
          },
          query
      )
{
}

TemporaryTableHolder::TemporaryTableHolder(TemporaryTableHolder && rhs)
A
Alexander Tokmakov 已提交
81
        : global_context(rhs.global_context), temporary_tables(rhs.temporary_tables), id(rhs.id)
A
Alexander Tokmakov 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95
{
    rhs.id = UUIDHelpers::Nil;
}

TemporaryTableHolder & TemporaryTableHolder::operator = (TemporaryTableHolder && rhs)
{
    id = rhs.id;
    rhs.id = UUIDHelpers::Nil;
    return *this;
}

TemporaryTableHolder::~TemporaryTableHolder()
{
    if (id != UUIDHelpers::Nil)
96
        temporary_tables->dropTable(*global_context, "_tmp_" + toString(id));
A
Alexander Tokmakov 已提交
97 98 99 100 101 102 103 104 105
}

StorageID TemporaryTableHolder::getGlobalTableID() const
{
    return StorageID{DatabaseCatalog::TEMPORARY_DATABASE, "_tmp_" + toString(id), id};
}

StoragePtr TemporaryTableHolder::getTable() const
{
106
    auto table = temporary_tables->tryGetTable("_tmp_" + toString(id), *global_context);
A
Alexander Tokmakov 已提交
107 108 109 110 111
    if (!table)
        throw Exception("Temporary table " + getGlobalTableID().getNameForLogs() + " not found", ErrorCodes::LOGICAL_ERROR);
    return table;
}

112

A
Alexander Tokmakov 已提交
113 114
void DatabaseCatalog::loadDatabases()
{
115
    drop_delay_sec = global_context->getConfigRef().getInt("database_atomic_delay_before_drop_table_sec", default_drop_delay_sec);
A
Alexander Tokmakov 已提交
116

117
    auto db_for_temporary_and_external_tables = std::make_shared<DatabaseMemory>(TEMPORARY_DATABASE, *global_context);
A
Alexander Tokmakov 已提交
118
    attachDatabase(TEMPORARY_DATABASE, db_for_temporary_and_external_tables);
119 120 121 122 123

    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();
A
Alexander Tokmakov 已提交
124 125
}

A
fixes  
Alexander Tokmakov 已提交
126
void DatabaseCatalog::shutdownImpl()
A
Alexander Tokmakov 已提交
127
{
128 129 130
    if (drop_task)
        (*drop_task)->deactivate();

A
Alexander Tokmakov 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    /** 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);
A
fixes  
Alexander Tokmakov 已提交
149
    assert(std::find_if_not(uuid_map.begin(), uuid_map.end(), [](const auto & elem) { return elem.map.empty(); }) == uuid_map.end());
150
    databases.clear();
151
    db_uuid_map.clear();
152
    view_dependencies.clear();
A
Alexander Tokmakov 已提交
153 154 155 156
}

DatabaseAndTable DatabaseCatalog::tryGetByUUID(const UUID & uuid) const
{
N
Nikita Mikhaylov 已提交
157
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
158 159 160 161 162 163 164 165 166
    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;
}


A
alexey-milovidov 已提交
167 168
DatabaseAndTable DatabaseCatalog::getTableImpl(
    const StorageID & table_id,
169
    const Context & context,
A
alexey-milovidov 已提交
170
    std::optional<Exception> * exception) const
A
Alexander Tokmakov 已提交
171
{
A
Alexander Tokmakov 已提交
172 173 174 175 176 177 178
    if (!table_id)
    {
        if (exception)
            exception->emplace("Cannot find table: StorageID is empty", ErrorCodes::UNKNOWN_TABLE);
        return {};
    }

179 180
    if (table_id.hasUUID())
    {
A
Alexander Tokmakov 已提交
181
        /// Shortcut for tables which have persistent UUID
182 183 184 185 186 187 188 189 190 191
        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;
    }
A
Alexander Tokmakov 已提交
192

A
Alexander Tokmakov 已提交
193 194 195 196 197 198 199 200 201 202
    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 {};
    }

A
fix  
Alexander Tokmakov 已提交
203
    DatabasePtr database;
A
Alexander Tokmakov 已提交
204
    {
A
alexey-milovidov 已提交
205
        std::lock_guard lock{databases_mutex};
A
fix  
Alexander Tokmakov 已提交
206 207 208 209 210 211 212 213 214
        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;
A
Alexander Tokmakov 已提交
215 216
    }

217
    auto table = database->tryGetTable(table_id.table_name, context);
A
Alexander Tokmakov 已提交
218 219 220
    if (!table && exception)
            exception->emplace("Table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);

A
Alexander Tokmakov 已提交
221
    return {database, table};
A
Alexander Tokmakov 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
}

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
{
A
fixes  
Alexander Tokmakov 已提交
238
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
239 240 241 242 243 244 245
    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
{
A
fixes  
Alexander Tokmakov 已提交
246
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
247 248 249 250
    if (databases.end() != databases.find(database_name))
        throw Exception("Database " + backQuoteIfNeed(database_name) + " already exists.", ErrorCodes::DATABASE_ALREADY_EXISTS);
}

A
Alexander Tokmakov 已提交
251
void DatabaseCatalog::attachDatabase(const String & database_name, const DatabasePtr & database)
A
Alexander Tokmakov 已提交
252 253 254
{
    std::lock_guard lock{databases_mutex};
    assertDatabaseDoesntExistUnlocked(database_name);
255
    databases.emplace(database_name, database);
A
Alexander Tokmakov 已提交
256 257 258 259
    UUID db_uuid = database->getUUID();
    assert((db_uuid != UUIDHelpers::Nil) ^ (dynamic_cast<DatabaseAtomic *>(database.get()) == nullptr));
    if (db_uuid != UUIDHelpers::Nil)
        db_uuid_map.emplace(db_uuid, database);
A
Alexander Tokmakov 已提交
260 261 262
}


263
DatabasePtr DatabaseCatalog::detachDatabase(const String & database_name, bool drop, bool check_empty)
A
Alexander Tokmakov 已提交
264
{
A
Alexander Tokmakov 已提交
265 266 267
    if (database_name == TEMPORARY_DATABASE)
        throw Exception("Cannot detach database with temporary tables.", ErrorCodes::DATABASE_ACCESS_DENIED);

A
Alexander Tokmakov 已提交
268
    DatabasePtr db;
269 270 271 272
    {
        std::lock_guard lock{databases_mutex};
        assertDatabaseExistsUnlocked(database_name);
        db = databases.find(database_name)->second;
A
Alexander Tokmakov 已提交
273 274 275
        db_uuid_map.erase(db->getUUID());
        databases.erase(database_name);
    }
276

A
Alexander Tokmakov 已提交
277 278 279
    if (check_empty)
    {
        try
A
Alexander Tokmakov 已提交
280
        {
281
            if (!db->empty())
A
Alexander Tokmakov 已提交
282 283
                throw Exception("New table appeared in database being dropped or detached. Try again.",
                                ErrorCodes::DATABASE_NOT_EMPTY);
A
Alexander Tokmakov 已提交
284
            auto * database_atomic = typeid_cast<DatabaseAtomic *>(db.get());
A
Alexander Tokmakov 已提交
285 286 287
            if (!drop && database_atomic)
                database_atomic->assertCanBeDetached(false);
        }
A
Alexander Tokmakov 已提交
288 289 290 291 292
        catch (...)
        {
            attachDatabase(database_name, db);
            throw;
        }
293
    }
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

    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;
A
Alexander Tokmakov 已提交
310 311
}

A
Alexander Tokmakov 已提交
312
void DatabaseCatalog::updateDatabaseName(const String & old_name, const String & new_name)
313 314
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
315
    assert(databases.find(new_name) == databases.end());
316
    auto it = databases.find(old_name);
A
Alexander Tokmakov 已提交
317
    assert(it != databases.end());
318 319 320 321 322
    auto db = it->second;
    databases.erase(it);
    databases.emplace(new_name, db);
}

323
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name) const
A
Alexander Tokmakov 已提交
324 325 326 327 328 329
{
    std::lock_guard lock{databases_mutex};
    assertDatabaseExistsUnlocked(database_name);
    return databases.find(database_name)->second;
}

330
DatabasePtr DatabaseCatalog::tryGetDatabase(const String & database_name) const
A
Alexander Tokmakov 已提交
331
{
332
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
333 334 335 336 337 338 339
    std::lock_guard lock{databases_mutex};
    auto it = databases.find(database_name);
    if (it == databases.end())
        return {};
    return it->second;
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
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;
}

A
Alexander Tokmakov 已提交
359 360
bool DatabaseCatalog::isDatabaseExist(const String & database_name) const
{
361
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
362 363 364 365 366 367 368 369 370 371
    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;
}

372
bool DatabaseCatalog::isTableExist(const DB::StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
373
{
374 375 376 377 378 379 380 381 382 383
    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;
    }
384
    return db && db->isTableExist(table_id.table_name, context);
A
Alexander Tokmakov 已提交
385 386
}

387
void DatabaseCatalog::assertTableDoesntExist(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
388
{
389
    if (isTableExist(table_id, context))
A
Alexander Tokmakov 已提交
390 391 392 393 394
        throw Exception("Table " + table_id.getNameForLogs() + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
}

DatabasePtr DatabaseCatalog::getDatabaseForTemporaryTables() const
{
395 396 397 398 399 400
    return getDatabase(TEMPORARY_DATABASE);
}

DatabasePtr DatabaseCatalog::getSystemDatabase() const
{
    return getDatabase(SYSTEM_DATABASE);
A
Alexander Tokmakov 已提交
401 402 403 404
}

void DatabaseCatalog::addUUIDMapping(const UUID & uuid, DatabasePtr database, StoragePtr table)
{
N
Nikita Mikhaylov 已提交
405
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
406 407 408 409 410 411 412 413 414
    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)
{
N
Nikita Mikhaylov 已提交
415
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
416 417 418 419 420 421
    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);
}

A
Alexander Tokmakov 已提交
422 423 424 425 426 427 428 429 430 431 432
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));
}

A
fixup  
Alexander Kuzmenkov 已提交
433 434
std::unique_ptr<DatabaseCatalog> DatabaseCatalog::database_catalog;

435
DatabaseCatalog::DatabaseCatalog(Context * global_context_)
436
    : global_context(global_context_), log(&Poco::Logger::get("DatabaseCatalog"))
437
{
438
    if (!global_context)
439
        throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::LOGICAL_ERROR);
440 441
}

442
DatabaseCatalog & DatabaseCatalog::init(Context * global_context_)
443
{
444 445
    if (database_catalog)
    {
446
        throw Exception("Database catalog is initialized twice. This is a bug.",
447 448 449 450 451 452
            ErrorCodes::LOGICAL_ERROR);
    }

    database_catalog.reset(new DatabaseCatalog(global_context_));

    return *database_catalog;
453 454
}

455 456
DatabaseCatalog & DatabaseCatalog::instance()
{
457 458
    if (!database_catalog)
    {
459
        throw Exception("Database catalog is not initialized. This is a bug.",
460 461 462 463
            ErrorCodes::LOGICAL_ERROR);
    }

    return *database_catalog;
464 465
}

A
fixes  
Alexander Tokmakov 已提交
466 467
void DatabaseCatalog::shutdown()
{
468 469 470
    // 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)
A
fixes  
Alexander Tokmakov 已提交
471
    {
472
        database_catalog->shutdownImpl();
A
fixes  
Alexander Tokmakov 已提交
473 474 475
    }
}

476 477 478 479 480 481
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name, const Context & local_context) const
{
    String resolved_database = local_context.resolveDatabase(database_name);
    return getDatabase(resolved_database);
}

482 483 484
void DatabaseCatalog::addDependency(const StorageID & from, const StorageID & where)
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
485 486 487
    // 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);
488 489 490 491 492 493

}

void DatabaseCatalog::removeDependency(const StorageID & from, const StorageID & where)
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
494
    view_dependencies[{from.getDatabaseName(), from.getTableName()}].erase(where);
495 496 497 498 499
}

Dependencies DatabaseCatalog::getDependencies(const StorageID & from) const
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
500
    auto iter = view_dependencies.find({from.getDatabaseName(), from.getTableName()});
501 502 503 504 505 506 507 508 509 510 511
    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())
A
Alexander Tokmakov 已提交
512
        view_dependencies[{old_from.getDatabaseName(), old_from.getTableName()}].erase(old_where);
513
    if (!new_from.empty())
A
Alexander Tokmakov 已提交
514
        view_dependencies[{new_from.getDatabaseName(), new_from.getTableName()}].insert(new_where);
515 516
}

517 518 519 520 521 522
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);
}

A
Alexander Tokmakov 已提交
523
bool DatabaseCatalog::isDictionaryExist(const StorageID & table_id) const
A
Alexander Tokmakov 已提交
524 525
{
    auto db = tryGetDatabase(table_id.getDatabaseName());
526
    return db && db->isDictionaryExist(table_id.getTableName());
A
Alexander Tokmakov 已提交
527 528
}

529
StoragePtr DatabaseCatalog::getTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
530
{
531
    std::optional<Exception> exc;
532
    auto res = getTableImpl(table_id, context, &exc);
533 534 535
    if (!res.second)
        throw Exception(*exc);
    return res.second;
A
Alexander Tokmakov 已提交
536 537
}

538
StoragePtr DatabaseCatalog::tryGetTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
539
{
540
    return getTableImpl(table_id, context, nullptr).second;
A
Alexander Tokmakov 已提交
541 542
}

543
DatabaseAndTable DatabaseCatalog::getDatabaseAndTable(const StorageID & table_id, const Context & context) const
A
fix  
Alexander Tokmakov 已提交
544 545
{
    std::optional<Exception> exc;
546
    auto res = getTableImpl(table_id, context, &exc);
A
fix  
Alexander Tokmakov 已提交
547 548 549 550 551
    if (!res.second)
        throw Exception(*exc);
    return res;
}

552
DatabaseAndTable DatabaseCatalog::tryGetDatabaseAndTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
553
{
554
    return getTableImpl(table_id, context, nullptr);
A
Alexander Tokmakov 已提交
555 556
}

557 558
void DatabaseCatalog::loadMarkedAsDroppedTables()
{
A
fixes  
Alexander Tokmakov 已提交
559 560 561 562 563 564 565
    /// /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

566 567
    std::map<String, StorageID> dropped_metadata;
    String path = global_context->getPath() + "metadata_dropped/";
568 569 570 571 572 573

    if (!std::filesystem::exists(path))
    {
        return;
    }

574 575 576
    Poco::DirectoryIterator dir_end;
    for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
    {
A
fixes  
Alexander Tokmakov 已提交
577 578 579 580
        /// File name has the following format:
        /// database_name.table_name.uuid.sql

        /// Ignore unexpected files
581 582 583 584
        if (!it.name().ends_with(".sql"))
            continue;

        /// Process .sql files with metadata of tables which were marked as dropped
A
Alexander Tokmakov 已提交
585 586 587 588 589
        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));
590

A
Alexander Tokmakov 已提交
591 592 593
        size_t prev_dot_pos = dot_pos;
        dot_pos = it.name().find('.', prev_dot_pos + 1);
        if (dot_pos == std::string::npos)
594
            continue;
A
Alexander Tokmakov 已提交
595
        dropped_id.table_name = unescapeForFileName(it.name().substr(prev_dot_pos + 1, dot_pos - prev_dot_pos - 1));
596

A
Alexander Tokmakov 已提交
597 598 599 600 601
        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));
602

A
Alexander Tokmakov 已提交
603
        String full_path = path + it.name();
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
        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));

A
fixes  
Alexander Tokmakov 已提交
632
    /// Table was removed from database. Enqueue removal of its data from disk.
633 634 635 636 637
    time_t drop_time;
    if (table)
        drop_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    else
    {
A
fixes  
Alexander Tokmakov 已提交
638
        /// Try load table from metadata to drop it correctly (e.g. remove metadata from zk or remove data from all volumes)
A
Alexey Milovidov 已提交
639
        LOG_INFO(log, "Trying load partially dropped table {} from {}", table_id.getNameForLogs(), dropped_metadata_path);
640
        ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata(log, *global_context, dropped_metadata_path, /*throw_on_error*/ false, /*remove_empty*/false);
T
tavplubix 已提交
641
        auto * create = typeid_cast<ASTCreateQuery *>(ast.get());
642 643 644 645
        assert(!create || create->uuid == table_id.uuid);

        if (create)
        {
A
Alexander Tokmakov 已提交
646
            String data_path = "store/" + getPathForUUID(table_id.uuid);
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
            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
        {
A
Alexey Milovidov 已提交
664
            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);
665 666 667 668 669
        }

        drop_time = Poco::File(dropped_metadata_path).getLastModified().epochTime();
    }

A
fixes  
Alexander Tokmakov 已提交
670
    std::lock_guard lock(tables_marked_dropped_mutex);
671
    if (ignore_delay)
A
fixes  
Alexander Tokmakov 已提交
672
        tables_marked_dropped.push_front({table_id, table, dropped_metadata_path, 0});
673
    else
A
fixes  
Alexander Tokmakov 已提交
674
        tables_marked_dropped.push_back({table_id, table, dropped_metadata_path, drop_time});
A
fixes  
Alexander Tokmakov 已提交
675
    /// If list of dropped tables was empty, start a drop task
A
fix  
Alexander Tokmakov 已提交
676
    if (drop_task && tables_marked_dropped.size() == 1)
A
fixes  
Alexander Tokmakov 已提交
677
        (*drop_task)->schedule();
678 679 680 681
}

void DatabaseCatalog::dropTableDataTask()
{
A
fixes  
Alexander Tokmakov 已提交
682 683 684 685
    /// 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;
686 687 688
    TableMarkedAsDropped table;
    try
    {
A
fixes  
Alexander Tokmakov 已提交
689
        std::lock_guard lock(tables_marked_dropped_mutex);
690
        time_t current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
A
fixes  
Alexander Tokmakov 已提交
691
        auto it = std::find_if(tables_marked_dropped.begin(), tables_marked_dropped.end(), [&](const auto & elem)
692
        {
A
fix  
Alexander Tokmakov 已提交
693
            bool not_in_use = !elem.table || elem.table.unique();
A
fixes  
Alexander Tokmakov 已提交
694
            bool old_enough = elem.drop_time + drop_delay_sec < current_time;
695
            return not_in_use && old_enough;
696
        });
A
fixes  
Alexander Tokmakov 已提交
697
        if (it != tables_marked_dropped.end())
698 699
        {
            table = std::move(*it);
A
Alexey Milovidov 已提交
700
            LOG_INFO(log, "Will try drop {}", table.table_id.getNameForLogs());
A
fixes  
Alexander Tokmakov 已提交
701
            tables_marked_dropped.erase(it);
702
        }
A
fixes  
Alexander Tokmakov 已提交
703
        need_reschedule = !tables_marked_dropped.empty();
704 705 706 707 708 709 710 711
    }
    catch (...)
    {
        tryLogCurrentException(log, __PRETTY_FUNCTION__);
    }

    if (table.table_id)
    {
A
fix  
Alexander Tokmakov 已提交
712

713 714 715 716 717 718 719 720 721
        try
        {
            dropTableFinally(table);
        }
        catch (...)
        {
            tryLogCurrentException(log, "Cannot drop table " + table.table_id.getNameForLogs() +
                                        ". Will retry later.");
            {
A
fixes  
Alexander Tokmakov 已提交
722 723
                std::lock_guard lock(tables_marked_dropped_mutex);
                tables_marked_dropped.emplace_back(std::move(table));
A
fixes  
Alexander Tokmakov 已提交
724 725 726
                /// If list of dropped tables was empty, schedule a task to retry deletion.
                if (tables_marked_dropped.size() == 1)
                    need_reschedule = true;
727 728 729 730
            }
        }
    }

A
fixes  
Alexander Tokmakov 已提交
731 732 733
    /// Do not schedule a task if there is no tables to drop
    if (need_reschedule)
        (*drop_task)->scheduleAfter(reschedule_time_ms);
734 735 736 737 738 739 740 741 742 743 744 745
}

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
A
Alexander Tokmakov 已提交
746
    String data_path = global_context->getPath() + "store/" + getPathForUUID(table.table_id.uuid);
747 748 749
    Poco::File table_data_dir{data_path};
    if (table_data_dir.exists())
    {
A
Alexey Milovidov 已提交
750
        LOG_INFO(log, "Removing data directory {} of dropped table {}", data_path, table.table_id.getNameForLogs());
751 752 753
        table_data_dir.remove(true);
    }

A
Alexey Milovidov 已提交
754
    LOG_INFO(log, "Removing metadata {} of dropped table {}", table.metadata_path, table.table_id.getNameForLogs());
755 756 757
    Poco::File(table.metadata_path).remove();
}

A
Alexander Tokmakov 已提交
758 759 760 761 762 763
String DatabaseCatalog::getPathForUUID(const UUID & uuid)
{
    const size_t uuid_prefix_len = 3;
    return toString(uuid).substr(0, uuid_prefix_len) + '/' + toString(uuid) + '/';
}

764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

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);
    }
}

A
Alexander Tokmakov 已提交
785
}