From 1f8f6fa06a961121b7307687d934a89ffa3e6f90 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Fri, 1 Apr 2016 20:41:13 +0300 Subject: [PATCH] Allowed concurrent CREATE TABLE IF NOT EXISTS if table exists [#METR-20704]. --- dbms/include/DB/Interpreters/Context.h | 2 ++ dbms/src/Interpreters/Context.cpp | 13 +++++++++++++ dbms/src/Interpreters/InterpreterCreateQuery.cpp | 13 +++++++++---- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/dbms/include/DB/Interpreters/Context.h b/dbms/include/DB/Interpreters/Context.h index 65416c9bbb0..6adab673611 100644 --- a/dbms/include/DB/Interpreters/Context.h +++ b/dbms/include/DB/Interpreters/Context.h @@ -165,6 +165,8 @@ public: /// Получить объект, который защищает таблицу от одновременного выполнения нескольких DDL операций. /// Если такой объект уже есть - кидается исключение. std::unique_ptr getDDLGuard(const String & database, const String & table, const String & message) const; + /// Если таблица уже есть - возвращается nullptr, иначе создаётся guard. + std::unique_ptr getDDLGuardIfTableDoesntExist(const String & database, const String & table, const String & message) const; String getCurrentDatabase() const; String getCurrentQueryId() const; diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp index 11dc426b4f2..a3fda7b4e0d 100644 --- a/dbms/src/Interpreters/Context.cpp +++ b/dbms/src/Interpreters/Context.cpp @@ -127,6 +127,7 @@ struct ContextShared /// В случае, если элемент уже есть - кидается исключение. См. class DDLGuard ниже. using DDLGuards = std::unordered_map>; DDLGuards ddl_guards; + /// Если вы захватываете mutex и ddl_guards_mutex, то захватывать их нужно строго в этом порядке. mutable std::mutex ddl_guards_mutex; @@ -566,6 +567,18 @@ std::unique_ptr Context::getDDLGuard(const String & database, const St } +std::unique_ptr Context::getDDLGuardIfTableDoesntExist(const String & database, const String & table, const String & message) const +{ + auto lock = getLock(); + + Databases::const_iterator it = shared->databases.find(database); + if (shared->databases.end() != it && it->second->isTableExist(table)) + return {}; + + return getDDLGuard(database, table, message); +} + + void Context::addDatabase(const String & database_name, const DatabasePtr & database) { auto lock = getLock(); diff --git a/dbms/src/Interpreters/InterpreterCreateQuery.cpp b/dbms/src/Interpreters/InterpreterCreateQuery.cpp index e36a81b0c35..869d0d722ab 100644 --- a/dbms/src/Interpreters/InterpreterCreateQuery.cpp +++ b/dbms/src/Interpreters/InterpreterCreateQuery.cpp @@ -487,12 +487,17 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) if (!create.is_temporary) { - guard = context.getDDLGuard(database_name, table_name, - "Table " + database_name + "." + table_name + " is creating or attaching right now"); - context.assertDatabaseExists(database_name); - if (context.isTableExist(database_name, table_name)) + /** Если таблица уже существует, и в запросе указано IF NOT EXISTS, + * то мы разрешаем конкуррентные запросы CREATE (которые ничего не делают). + * Иначе конкуррентные запросы на создание таблицы, если таблицы не существует, + * могут кидать исключение, даже если указано IF NOT EXISTS. + */ + guard = context.getDDLGuardIfTableDoesntExist(database_name, table_name, + "Table " + database_name + "." + table_name + " is creating or attaching right now"); + + if (!guard) { if (create.if_not_exists) return {};