DatabaseCatalog.cpp 29.8 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
TemporaryTableHolder::TemporaryTableHolder(
    const Context & context_,
    const ColumnsDescription & columns,
    const ConstraintsDescription & constraints,
N
Nikolai Kochetov 已提交
67 68
    const ASTPtr & query,
    bool create_for_global_subquery)
A
Alexander Tokmakov 已提交
69 70 71 72 73
    : TemporaryTableHolder
      (
          context_,
          [&](const StorageID & table_id)
          {
N
Nikolai Kochetov 已提交
74 75 76 77 78 79 80
              auto storage = StorageMemory::create(
                      table_id, ColumnsDescription{columns}, ConstraintsDescription{constraints});

              if (create_for_global_subquery)
                  storage->delayReadForGlobalSubqueries();

              return storage;
A
Alexander Tokmakov 已提交
81 82 83 84 85 86 87
          },
          query
      )
{
}

TemporaryTableHolder::TemporaryTableHolder(TemporaryTableHolder && rhs)
A
Alexander Tokmakov 已提交
88
        : global_context(rhs.global_context), temporary_tables(rhs.temporary_tables), id(rhs.id)
A
Alexander Tokmakov 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101 102
{
    rhs.id = UUIDHelpers::Nil;
}

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

TemporaryTableHolder::~TemporaryTableHolder()
{
    if (id != UUIDHelpers::Nil)
103
        temporary_tables->dropTable(*global_context, "_tmp_" + toString(id));
A
Alexander Tokmakov 已提交
104 105 106 107 108 109 110 111 112
}

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

StoragePtr TemporaryTableHolder::getTable() const
{
113
    auto table = temporary_tables->tryGetTable("_tmp_" + toString(id), *global_context);
A
Alexander Tokmakov 已提交
114 115 116 117 118
    if (!table)
        throw Exception("Temporary table " + getGlobalTableID().getNameForLogs() + " not found", ErrorCodes::LOGICAL_ERROR);
    return table;
}

119

A
Alexander Tokmakov 已提交
120 121
void DatabaseCatalog::loadDatabases()
{
122
    drop_delay_sec = global_context->getConfigRef().getInt("database_atomic_delay_before_drop_table_sec", default_drop_delay_sec);
A
Alexander Tokmakov 已提交
123

124
    auto db_for_temporary_and_external_tables = std::make_shared<DatabaseMemory>(TEMPORARY_DATABASE, *global_context);
A
Alexander Tokmakov 已提交
125
    attachDatabase(TEMPORARY_DATABASE, db_for_temporary_and_external_tables);
126 127 128 129 130

    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 已提交
131 132
}

A
fixes  
Alexander Tokmakov 已提交
133
void DatabaseCatalog::shutdownImpl()
A
Alexander Tokmakov 已提交
134
{
135 136 137
    if (drop_task)
        (*drop_task)->deactivate();

A
Alexander Tokmakov 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    /** 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();

A
fixes  
Alexander Tokmakov 已提交
155 156
    tables_marked_dropped.clear();

A
Alexander Tokmakov 已提交
157
    std::lock_guard lock(databases_mutex);
A
fixes  
Alexander Tokmakov 已提交
158
    assert(std::find_if_not(uuid_map.begin(), uuid_map.end(), [](const auto & elem) { return elem.map.empty(); }) == uuid_map.end());
159
    databases.clear();
160
    db_uuid_map.clear();
161
    view_dependencies.clear();
A
Alexander Tokmakov 已提交
162 163 164 165
}

DatabaseAndTable DatabaseCatalog::tryGetByUUID(const UUID & uuid) const
{
N
Nikita Mikhaylov 已提交
166
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
167 168 169 170 171 172 173 174 175
    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 已提交
176 177
DatabaseAndTable DatabaseCatalog::getTableImpl(
    const StorageID & table_id,
178
    const Context & context,
A
alexey-milovidov 已提交
179
    std::optional<Exception> * exception) const
A
Alexander Tokmakov 已提交
180
{
A
Alexander Tokmakov 已提交
181 182 183 184 185 186 187
    if (!table_id)
    {
        if (exception)
            exception->emplace("Cannot find table: StorageID is empty", ErrorCodes::UNKNOWN_TABLE);
        return {};
    }

188 189
    if (table_id.hasUUID())
    {
A
Alexander Tokmakov 已提交
190
        /// Shortcut for tables which have persistent UUID
191 192 193 194 195 196 197 198 199 200
        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 已提交
201

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

226
    auto table = database->tryGetTable(table_id.table_name, context);
A
Alexander Tokmakov 已提交
227 228
    if (!table && exception)
            exception->emplace("Table " + table_id.getNameForLogs() + " doesn't exist.", ErrorCodes::UNKNOWN_TABLE);
A
Alexander Tokmakov 已提交
229 230
    if (!table)
        database = nullptr;
A
Alexander Tokmakov 已提交
231

A
Alexander Tokmakov 已提交
232
    return {database, table};
A
Alexander Tokmakov 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
}

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 已提交
249
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
250 251 252 253 254 255 256
    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 已提交
257
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
258 259 260 261
    if (databases.end() != databases.find(database_name))
        throw Exception("Database " + backQuoteIfNeed(database_name) + " already exists.", ErrorCodes::DATABASE_ALREADY_EXISTS);
}

A
Alexander Tokmakov 已提交
262
void DatabaseCatalog::attachDatabase(const String & database_name, const DatabasePtr & database)
A
Alexander Tokmakov 已提交
263 264 265
{
    std::lock_guard lock{databases_mutex};
    assertDatabaseDoesntExistUnlocked(database_name);
266
    databases.emplace(database_name, database);
A
Alexander Tokmakov 已提交
267 268 269 270
    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 已提交
271 272 273
}


274
DatabasePtr DatabaseCatalog::detachDatabase(const String & database_name, bool drop, bool check_empty)
A
Alexander Tokmakov 已提交
275
{
A
Alexander Tokmakov 已提交
276 277 278
    if (database_name == TEMPORARY_DATABASE)
        throw Exception("Cannot detach database with temporary tables.", ErrorCodes::DATABASE_ACCESS_DENIED);

A
Alexander Tokmakov 已提交
279
    DatabasePtr db;
280 281 282 283
    {
        std::lock_guard lock{databases_mutex};
        assertDatabaseExistsUnlocked(database_name);
        db = databases.find(database_name)->second;
A
Alexander Tokmakov 已提交
284 285 286
        db_uuid_map.erase(db->getUUID());
        databases.erase(database_name);
    }
287

A
Alexander Tokmakov 已提交
288 289 290
    if (check_empty)
    {
        try
A
Alexander Tokmakov 已提交
291
        {
292
            if (!db->empty())
A
Alexander Tokmakov 已提交
293 294
                throw Exception("New table appeared in database being dropped or detached. Try again.",
                                ErrorCodes::DATABASE_NOT_EMPTY);
A
Alexander Tokmakov 已提交
295
            auto * database_atomic = typeid_cast<DatabaseAtomic *>(db.get());
A
Alexander Tokmakov 已提交
296 297 298
            if (!drop && database_atomic)
                database_atomic->assertCanBeDetached(false);
        }
A
Alexander Tokmakov 已提交
299 300 301 302 303
        catch (...)
        {
            attachDatabase(database_name, db);
            throw;
        }
304
    }
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

    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 已提交
321 322
}

A
Alexander Tokmakov 已提交
323
void DatabaseCatalog::updateDatabaseName(const String & old_name, const String & new_name)
324 325
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
326
    assert(databases.find(new_name) == databases.end());
327
    auto it = databases.find(old_name);
A
Alexander Tokmakov 已提交
328
    assert(it != databases.end());
329 330 331 332 333
    auto db = it->second;
    databases.erase(it);
    databases.emplace(new_name, db);
}

334
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name) const
A
Alexander Tokmakov 已提交
335 336 337 338 339 340
{
    std::lock_guard lock{databases_mutex};
    assertDatabaseExistsUnlocked(database_name);
    return databases.find(database_name)->second;
}

341
DatabasePtr DatabaseCatalog::tryGetDatabase(const String & database_name) const
A
Alexander Tokmakov 已提交
342
{
343
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
344 345 346 347 348 349 350
    std::lock_guard lock{databases_mutex};
    auto it = databases.find(database_name);
    if (it == databases.end())
        return {};
    return it->second;
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
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 已提交
370 371
bool DatabaseCatalog::isDatabaseExist(const String & database_name) const
{
372
    assert(!database_name.empty());
A
Alexander Tokmakov 已提交
373 374 375 376 377 378 379 380 381 382
    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;
}

383
bool DatabaseCatalog::isTableExist(const DB::StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
384
{
385 386 387 388 389 390 391 392 393 394
    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;
    }
395
    return db && db->isTableExist(table_id.table_name, context);
A
Alexander Tokmakov 已提交
396 397
}

398
void DatabaseCatalog::assertTableDoesntExist(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
399
{
400
    if (isTableExist(table_id, context))
A
Alexander Tokmakov 已提交
401 402 403 404 405
        throw Exception("Table " + table_id.getNameForLogs() + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS);
}

DatabasePtr DatabaseCatalog::getDatabaseForTemporaryTables() const
{
406 407 408 409 410 411
    return getDatabase(TEMPORARY_DATABASE);
}

DatabasePtr DatabaseCatalog::getSystemDatabase() const
{
    return getDatabase(SYSTEM_DATABASE);
A
Alexander Tokmakov 已提交
412 413 414 415
}

void DatabaseCatalog::addUUIDMapping(const UUID & uuid, DatabasePtr database, StoragePtr table)
{
N
Nikita Mikhaylov 已提交
416
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
417 418 419 420 421 422 423 424 425
    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 已提交
426
    assert(uuid != UUIDHelpers::Nil && getFirstLevelIdx(uuid) < uuid_map.size());
A
Alexander Tokmakov 已提交
427 428 429 430 431 432
    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 已提交
433 434 435 436 437 438 439 440 441 442 443
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 已提交
444 445
std::unique_ptr<DatabaseCatalog> DatabaseCatalog::database_catalog;

446
DatabaseCatalog::DatabaseCatalog(Context * global_context_)
447
    : global_context(global_context_), log(&Poco::Logger::get("DatabaseCatalog"))
448
{
449
    if (!global_context)
450
        throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::LOGICAL_ERROR);
451 452
}

453
DatabaseCatalog & DatabaseCatalog::init(Context * global_context_)
454
{
455 456
    if (database_catalog)
    {
457
        throw Exception("Database catalog is initialized twice. This is a bug.",
458 459 460 461 462 463
            ErrorCodes::LOGICAL_ERROR);
    }

    database_catalog.reset(new DatabaseCatalog(global_context_));

    return *database_catalog;
464 465
}

466 467
DatabaseCatalog & DatabaseCatalog::instance()
{
468 469
    if (!database_catalog)
    {
470
        throw Exception("Database catalog is not initialized. This is a bug.",
471 472 473 474
            ErrorCodes::LOGICAL_ERROR);
    }

    return *database_catalog;
475 476
}

A
fixes  
Alexander Tokmakov 已提交
477 478
void DatabaseCatalog::shutdown()
{
479 480 481
    // 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 已提交
482
    {
483
        database_catalog->shutdownImpl();
A
fixes  
Alexander Tokmakov 已提交
484 485 486
    }
}

487 488 489 490 491 492
DatabasePtr DatabaseCatalog::getDatabase(const String & database_name, const Context & local_context) const
{
    String resolved_database = local_context.resolveDatabase(database_name);
    return getDatabase(resolved_database);
}

493 494 495
void DatabaseCatalog::addDependency(const StorageID & from, const StorageID & where)
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
496 497 498
    // 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);
499 500 501 502 503 504

}

void DatabaseCatalog::removeDependency(const StorageID & from, const StorageID & where)
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
505
    view_dependencies[{from.getDatabaseName(), from.getTableName()}].erase(where);
506 507 508 509 510
}

Dependencies DatabaseCatalog::getDependencies(const StorageID & from) const
{
    std::lock_guard lock{databases_mutex};
A
Alexander Tokmakov 已提交
511
    auto iter = view_dependencies.find({from.getDatabaseName(), from.getTableName()});
512 513 514 515 516 517 518 519 520 521 522
    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 已提交
523
        view_dependencies[{old_from.getDatabaseName(), old_from.getTableName()}].erase(old_where);
524
    if (!new_from.empty())
A
Alexander Tokmakov 已提交
525
        view_dependencies[{new_from.getDatabaseName(), new_from.getTableName()}].insert(new_where);
526 527
}

528 529 530
std::unique_ptr<DDLGuard> DatabaseCatalog::getDDLGuard(const String & database, const String & table)
{
    std::unique_lock lock(ddl_guards_mutex);
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    auto db_guard_iter = ddl_guards.try_emplace(database).first;
    DatabaseGuard & db_guard = db_guard_iter->second;
    return std::make_unique<DDLGuard>(db_guard.first, db_guard.second, std::move(lock), table);
}

std::unique_lock<std::shared_mutex> DatabaseCatalog::getExclusiveDDLGuardForDatabase(const String & database)
{
    DDLGuards::iterator db_guard_iter;
    {
        std::unique_lock lock(ddl_guards_mutex);
        db_guard_iter = ddl_guards.try_emplace(database).first;
        assert(db_guard_iter->second.first.count(""));
    }
    DatabaseGuard & db_guard = db_guard_iter->second;
    return std::unique_lock{db_guard.second};
546 547
}

A
Alexander Tokmakov 已提交
548
bool DatabaseCatalog::isDictionaryExist(const StorageID & table_id) const
A
Alexander Tokmakov 已提交
549 550
{
    auto db = tryGetDatabase(table_id.getDatabaseName());
551
    return db && db->isDictionaryExist(table_id.getTableName());
A
Alexander Tokmakov 已提交
552 553
}

554
StoragePtr DatabaseCatalog::getTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
555
{
556
    std::optional<Exception> exc;
557
    auto res = getTableImpl(table_id, context, &exc);
558 559 560
    if (!res.second)
        throw Exception(*exc);
    return res.second;
A
Alexander Tokmakov 已提交
561 562
}

563
StoragePtr DatabaseCatalog::tryGetTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
564
{
565
    return getTableImpl(table_id, context, nullptr).second;
A
Alexander Tokmakov 已提交
566 567
}

568
DatabaseAndTable DatabaseCatalog::getDatabaseAndTable(const StorageID & table_id, const Context & context) const
A
fix  
Alexander Tokmakov 已提交
569 570
{
    std::optional<Exception> exc;
571
    auto res = getTableImpl(table_id, context, &exc);
A
fix  
Alexander Tokmakov 已提交
572 573 574 575 576
    if (!res.second)
        throw Exception(*exc);
    return res;
}

577
DatabaseAndTable DatabaseCatalog::tryGetDatabaseAndTable(const StorageID & table_id, const Context & context) const
A
Alexander Tokmakov 已提交
578
{
579
    return getTableImpl(table_id, context, nullptr);
A
Alexander Tokmakov 已提交
580 581
}

582 583
void DatabaseCatalog::loadMarkedAsDroppedTables()
{
A
fixes  
Alexander Tokmakov 已提交
584 585 586 587 588 589 590
    /// /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

591 592
    std::map<String, StorageID> dropped_metadata;
    String path = global_context->getPath() + "metadata_dropped/";
593 594 595 596 597 598

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

599 600 601
    Poco::DirectoryIterator dir_end;
    for (Poco::DirectoryIterator it(path); it != dir_end; ++it)
    {
A
fixes  
Alexander Tokmakov 已提交
602 603 604 605
        /// File name has the following format:
        /// database_name.table_name.uuid.sql

        /// Ignore unexpected files
606 607 608 609
        if (!it.name().ends_with(".sql"))
            continue;

        /// Process .sql files with metadata of tables which were marked as dropped
A
Alexander Tokmakov 已提交
610 611 612 613 614
        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));
615

A
Alexander Tokmakov 已提交
616 617 618
        size_t prev_dot_pos = dot_pos;
        dot_pos = it.name().find('.', prev_dot_pos + 1);
        if (dot_pos == std::string::npos)
619
            continue;
A
Alexander Tokmakov 已提交
620
        dropped_id.table_name = unescapeForFileName(it.name().substr(prev_dot_pos + 1, dot_pos - prev_dot_pos - 1));
621

A
Alexander Tokmakov 已提交
622 623 624 625 626
        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));
627

A
Alexander Tokmakov 已提交
628
        String full_path = path + it.name();
629 630 631
        dropped_metadata.emplace(std::move(full_path), std::move(dropped_id));
    }

632
    ThreadPool pool;
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
    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 已提交
657
    /// Table was removed from database. Enqueue removal of its data from disk.
658 659
    time_t drop_time;
    if (table)
660
    {
661
        drop_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
662 663
        table->is_dropped = true;
    }
664 665
    else
    {
A
fixes  
Alexander Tokmakov 已提交
666
        /// Try load table from metadata to drop it correctly (e.g. remove metadata from zk or remove data from all volumes)
A
Alexey Milovidov 已提交
667
        LOG_INFO(log, "Trying load partially dropped table {} from {}", table_id.getNameForLogs(), dropped_metadata_path);
668
        ASTPtr ast = DatabaseOnDisk::parseQueryFromMetadata(log, *global_context, dropped_metadata_path, /*throw_on_error*/ false, /*remove_empty*/false);
T
tavplubix 已提交
669
        auto * create = typeid_cast<ASTCreateQuery *>(ast.get());
670 671 672 673
        assert(!create || create->uuid == table_id.uuid);

        if (create)
        {
A
Alexander Tokmakov 已提交
674
            String data_path = "store/" + getPathForUUID(table_id.uuid);
675 676 677 678 679
            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;
680
                table->is_dropped = true;
681 682 683 684 685 686 687 688 689 690 691 692
            }
            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 已提交
693
            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);
694 695 696 697 698
        }

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

A
fixes  
Alexander Tokmakov 已提交
699
    std::lock_guard lock(tables_marked_dropped_mutex);
700
    if (ignore_delay)
A
fixes  
Alexander Tokmakov 已提交
701
        tables_marked_dropped.push_front({table_id, table, dropped_metadata_path, 0});
702
    else
A
fixes  
Alexander Tokmakov 已提交
703
        tables_marked_dropped.push_back({table_id, table, dropped_metadata_path, drop_time});
704
    tables_marked_dropped_ids.insert(table_id.uuid);
A
fixes  
Alexander Tokmakov 已提交
705
    /// If list of dropped tables was empty, start a drop task
A
fix  
Alexander Tokmakov 已提交
706
    if (drop_task && tables_marked_dropped.size() == 1)
A
fixes  
Alexander Tokmakov 已提交
707
        (*drop_task)->schedule();
708 709 710 711
}

void DatabaseCatalog::dropTableDataTask()
{
A
fixes  
Alexander Tokmakov 已提交
712 713 714 715
    /// 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;
716 717 718
    TableMarkedAsDropped table;
    try
    {
A
fixes  
Alexander Tokmakov 已提交
719
        std::lock_guard lock(tables_marked_dropped_mutex);
720
        time_t current_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
A
fixes  
Alexander Tokmakov 已提交
721
        auto it = std::find_if(tables_marked_dropped.begin(), tables_marked_dropped.end(), [&](const auto & elem)
722
        {
A
fix  
Alexander Tokmakov 已提交
723
            bool not_in_use = !elem.table || elem.table.unique();
A
fixes  
Alexander Tokmakov 已提交
724
            bool old_enough = elem.drop_time + drop_delay_sec < current_time;
725
            return not_in_use && old_enough;
726
        });
A
fixes  
Alexander Tokmakov 已提交
727
        if (it != tables_marked_dropped.end())
728 729
        {
            table = std::move(*it);
A
Alexey Milovidov 已提交
730
            LOG_INFO(log, "Will try drop {}", table.table_id.getNameForLogs());
A
fixes  
Alexander Tokmakov 已提交
731
            tables_marked_dropped.erase(it);
732
        }
A
fixes  
Alexander Tokmakov 已提交
733
        need_reschedule = !tables_marked_dropped.empty();
734 735 736 737 738 739 740 741
    }
    catch (...)
    {
        tryLogCurrentException(log, __PRETTY_FUNCTION__);
    }

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

743 744 745
        try
        {
            dropTableFinally(table);
746 747 748
            std::lock_guard lock(tables_marked_dropped_mutex);
            auto removed = tables_marked_dropped_ids.erase(table.table_id.uuid);
            assert(removed);
749 750 751 752 753 754
        }
        catch (...)
        {
            tryLogCurrentException(log, "Cannot drop table " + table.table_id.getNameForLogs() +
                                        ". Will retry later.");
            {
A
fixes  
Alexander Tokmakov 已提交
755 756
                std::lock_guard lock(tables_marked_dropped_mutex);
                tables_marked_dropped.emplace_back(std::move(table));
A
fixes  
Alexander Tokmakov 已提交
757 758 759
                /// If list of dropped tables was empty, schedule a task to retry deletion.
                if (tables_marked_dropped.size() == 1)
                    need_reschedule = true;
760 761
            }
        }
762 763

        wait_table_finally_dropped.notify_all();
764 765
    }

A
fixes  
Alexander Tokmakov 已提交
766 767 768
    /// Do not schedule a task if there is no tables to drop
    if (need_reschedule)
        (*drop_task)->scheduleAfter(reschedule_time_ms);
769 770 771 772 773 774 775 776 777 778 779
}

void DatabaseCatalog::dropTableFinally(const TableMarkedAsDropped & table) const
{
    if (table.table)
    {
        table.table->drop();
    }

    /// Even if table is not loaded, try remove its data from disk.
    /// TODO remove data from all volumes
A
Alexander Tokmakov 已提交
780
    String data_path = global_context->getPath() + "store/" + getPathForUUID(table.table_id.uuid);
781 782 783
    Poco::File table_data_dir{data_path};
    if (table_data_dir.exists())
    {
A
Alexey Milovidov 已提交
784
        LOG_INFO(log, "Removing data directory {} of dropped table {}", data_path, table.table_id.getNameForLogs());
785 786 787
        table_data_dir.remove(true);
    }

A
Alexey Milovidov 已提交
788
    LOG_INFO(log, "Removing metadata {} of dropped table {}", table.metadata_path, table.table_id.getNameForLogs());
789 790 791
    Poco::File(table.metadata_path).remove();
}

A
Alexander Tokmakov 已提交
792 793 794 795 796 797
String DatabaseCatalog::getPathForUUID(const UUID & uuid)
{
    const size_t uuid_prefix_len = 3;
    return toString(uuid).substr(0, uuid_prefix_len) + '/' + toString(uuid) + '/';
}

A
Alexander Tokmakov 已提交
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
String DatabaseCatalog::resolveDictionaryName(const String & name) const
{
    /// If it's dictionary from Atomic database, then we need to convert qualified name to UUID.
    /// Try to split name and get id from associated StorageDictionary.
    /// If something went wrong, return name as is.

    /// TODO support dot in name for dictionaries in Atomic databases
    auto pos = name.find('.');
    if (pos == std::string::npos || name.find('.', pos + 1) != std::string::npos)
        return name;
    String maybe_database_name = name.substr(0, pos);
    String maybe_table_name = name.substr(pos + 1);

    auto db_and_table = tryGetDatabaseAndTable({maybe_database_name, maybe_table_name}, *global_context);
    if (!db_and_table.first)
        return name;
    assert(db_and_table.second);
    if (db_and_table.first->getUUID() == UUIDHelpers::Nil)
        return name;
    if (db_and_table.second->getName() != "Dictionary")
        return name;

    return toString(db_and_table.second->getStorageID().uuid);
}

823 824 825 826 827 828 829 830 831 832
void DatabaseCatalog::waitTableFinallyDropped(const UUID & uuid)
{
    if (uuid == UUIDHelpers::Nil)
        return;
    std::unique_lock lock{tables_marked_dropped_mutex};
    wait_table_finally_dropped.wait(lock, [&](){
        return tables_marked_dropped_ids.count(uuid) == 0;
    });
}

833

834 835
DDLGuard::DDLGuard(Map & map_, std::shared_mutex & db_mutex_, std::unique_lock<std::mutex> guards_lock_, const String & elem)
        : map(map_), db_mutex(db_mutex_), guards_lock(std::move(guards_lock_))
836 837 838 839 840
{
    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);
841 842 843
    bool is_database = elem.empty();
    if (!is_database)
        db_mutex.lock_shared();
844 845 846 847
}

DDLGuard::~DDLGuard()
{
848 849 850
    bool is_database = it->first.empty();
    if (!is_database)
        db_mutex.unlock_shared();
851 852 853 854 855 856 857 858 859
    guards_lock.lock();
    --it->second.counter;
    if (!it->second.counter)
    {
        table_lock.unlock();
        map.erase(it);
    }
}

A
Alexander Tokmakov 已提交
860
}