Allowed concurrent CREATE TABLE IF NOT EXISTS if table exists [#METR-20704].

This commit is contained in:
Alexey Milovidov 2016-04-01 20:41:13 +03:00
parent 60252fed27
commit 1f8f6fa06a
3 changed files with 24 additions and 4 deletions

View File

@ -165,6 +165,8 @@ public:
/// Получить объект, который защищает таблицу от одновременного выполнения нескольких DDL операций.
/// Если такой объект уже есть - кидается исключение.
std::unique_ptr<DDLGuard> getDDLGuard(const String & database, const String & table, const String & message) const;
/// Если таблица уже есть - возвращается nullptr, иначе создаётся guard.
std::unique_ptr<DDLGuard> getDDLGuardIfTableDoesntExist(const String & database, const String & table, const String & message) const;
String getCurrentDatabase() const;
String getCurrentQueryId() const;

View File

@ -127,6 +127,7 @@ struct ContextShared
/// В случае, если элемент уже есть - кидается исключение. См. class DDLGuard ниже.
using DDLGuards = std::unordered_map<String, std::unordered_map<String, String>>;
DDLGuards ddl_guards;
/// Если вы захватываете mutex и ddl_guards_mutex, то захватывать их нужно строго в этом порядке.
mutable std::mutex ddl_guards_mutex;
@ -566,6 +567,18 @@ std::unique_ptr<DDLGuard> Context::getDDLGuard(const String & database, const St
}
std::unique_ptr<DDLGuard> 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();

View File

@ -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 {};