BlockIO InterpreterDropQuery::execute() { ASTDropQuery & drop = typeid_cast<ASTDropQuery &>(*query_ptr); checkAccess(drop); if (!drop.cluster.empty()) return executeDDLQueryOnCluster(query_ptr, context, {drop.database}); if (!drop.table.empty()) return executeToTable(drop.database, drop.table, drop.kind, drop.if_exists, drop.temporary); else if (!drop.database.empty()) return executeToDatabase(drop.database, drop.kind, drop.if_exists); else throw Exception("Database and table names is empty.", ErrorCodes::LOGICAL_ERROR); }
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) { if (!create.cluster.empty()) { NameSet databases{create.database}; if (!create.to_table.empty()) databases.emplace(create.to_database); return executeDDLQueryOnCluster(query_ptr, context, std::move(databases)); } String path = context.getPath(); String current_database = context.getCurrentDatabase(); String database_name = create.database.empty() ? current_database : create.database; String table_name = create.table; String table_name_escaped = escapeForFileName(table_name); // If this is a stub ATTACH query, read the query definition from the database if (create.attach && !create.storage && !create.columns) { // Table SQL definition is available even if the table is detached auto query = context.getCreateTableQuery(database_name, table_name); auto & as_create = typeid_cast<const ASTCreateQuery &>(*query); create = as_create; // Copy the saved create query, but use ATTACH instead of CREATE create.attach = true; } if (create.to_database.empty()) create.to_database = current_database; if (create.select && (create.is_view || create.is_materialized_view)) { AddDefaultDatabaseVisitor visitor(current_database); visitor.visit(*create.select); } Block as_select_sample; if (create.select && (!create.attach || !create.columns)) as_select_sample = InterpreterSelectWithUnionQuery::getSampleBlock(create.select->clone(), context); String as_database_name = create.as_database.empty() ? current_database : create.as_database; String as_table_name = create.as_table; StoragePtr as_storage; TableStructureReadLockPtr as_storage_lock; if (!as_table_name.empty()) { as_storage = context.getTable(as_database_name, as_table_name); as_storage_lock = as_storage->lockStructure(false); } /// Set and retrieve list of columns. ColumnsDescription columns = setColumns(create, as_select_sample, as_storage); /// Some column types may be not allowed according to settings. if (!create.attach) checkSupportedTypes(columns, context); /// Set the table engine if it was not specified explicitly. setEngine(create); StoragePtr res; { std::unique_ptr<DDLGuard> guard; String data_path; DatabasePtr database; if (!create.temporary) { database = context.getDatabase(database_name); data_path = database->getDataPath(); /** If the request specifies IF NOT EXISTS, we allow concurrent CREATE queries (which do nothing). * If table doesnt exist, one thread is creating table, while others wait in DDLGuard. */ guard = context.getDDLGuard(database_name, table_name); /// Table can be created before or it can be created concurrently in another thread, while we were waiting in DDLGuard. if (database->isTableExist(context, table_name)) { if (create.if_not_exists) return {}; else throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); } } else if (context.tryGetExternalTable(table_name) && create.if_not_exists) return {}; res = StorageFactory::instance().get(create, data_path, table_name, database_name, context, context.getGlobalContext(), columns, create.attach, false); if (create.temporary) context.getSessionContext().addExternalTable(table_name, res, query_ptr); else database->createTable(context, table_name, res, query_ptr); /// We must call "startup" and "shutdown" while holding DDLGuard. /// Because otherwise method "shutdown" (from InterpreterDropQuery) can be called before startup /// (in case when table was created and instantly dropped before started up) /// /// Method "startup" may create background tasks and method "shutdown" will wait for them. /// But if "shutdown" is called before "startup", it will exit early, because there are no background tasks to wait. /// Then background task is created by "startup" method. And when destructor of a table object is called, background task is still active, /// and the task will use references to freed data. res->startup(); } /// If the query is a CREATE SELECT, insert the data into the table. if (create.select && !create.attach && !create.is_view && (!create.is_materialized_view || create.is_populate)) { auto insert = std::make_shared<ASTInsertQuery>(); if (!create.temporary) insert->database = database_name; insert->table = table_name; insert->select = create.select->clone(); if (create.temporary && !context.getSessionContext().hasQueryContext()) context.getSessionContext().setQueryContext(context.getSessionContext()); return InterpreterInsertQuery(insert, create.temporary ? context.getSessionContext() : context, context.getSettingsRef().insert_allow_materialized_columns).execute(); } return {}; }
BlockIO InterpreterAlterQuery::execute() { auto & alter = typeid_cast<ASTAlterQuery &>(*query_ptr); if (!alter.cluster.empty()) return executeDDLQueryOnCluster(query_ptr, context, {alter.table}); const String & table_name = alter.table; String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database; StoragePtr table = context.getTable(database_name, table_name); AlterCommands alter_commands; PartitionCommands partition_commands; MutationCommands mutation_commands; parseAlter(alter.parameters, alter_commands, partition_commands, mutation_commands); if (!mutation_commands.commands.empty()) { mutation_commands.validate(*table, context); table->mutate(mutation_commands, context); } partition_commands.validate(*table); for (const PartitionCommand & command : partition_commands) { switch (command.type) { case PartitionCommand::DROP_PARTITION: table->dropPartition(query_ptr, command.partition, command.detach, context); break; case PartitionCommand::ATTACH_PARTITION: table->attachPartition(command.partition, command.part, context); break; case PartitionCommand::REPLACE_PARTITION: { String from_database = command.from_database.empty() ? context.getCurrentDatabase() : command.from_database; auto from_storage = context.getTable(from_database, command.from_table); table->replacePartitionFrom(from_storage, command.partition, command.replace, context); } break; case PartitionCommand::FETCH_PARTITION: table->fetchPartition(command.partition, command.from_zookeeper_path, context); break; case PartitionCommand::FREEZE_PARTITION: table->freezePartition(command.partition, command.with_name, context); break; case PartitionCommand::CLEAR_COLUMN: table->clearColumnInPartition(command.partition, command.column_name, context); break; } } if (!alter_commands.empty()) { alter_commands.validate(*table, context); table->alter(alter_commands, database_name, table_name, context); } return {}; }
BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create) { if (!create.cluster.empty()) return executeDDLQueryOnCluster(query_ptr, context, {create.database}); String database_name = create.database; if (create.if_not_exists && context.isDatabaseExist(database_name)) return {}; String database_engine_name; if (!create.storage) { database_engine_name = "Ordinary"; /// Default database engine. auto engine = std::make_shared<ASTFunction>(); engine->name = database_engine_name; auto storage = std::make_shared<ASTStorage>(); storage->set(storage->engine, engine); create.set(create.storage, storage); } else { const ASTStorage & storage = *create.storage; const ASTFunction & engine = *storage.engine; /// Currently, there are no database engines, that support any arguments. if (engine.arguments || engine.parameters || storage.partition_by || storage.order_by || storage.sample_by || storage.settings) { std::stringstream ostr; formatAST(storage, ostr, false, false); throw Exception("Unknown database engine: " + ostr.str(), ErrorCodes::UNKNOWN_DATABASE_ENGINE); } database_engine_name = engine.name; } String database_name_escaped = escapeForFileName(database_name); /// Create directories for tables metadata. String path = context.getPath(); String metadata_path = path + "metadata/" + database_name_escaped + "/"; Poco::File(metadata_path).createDirectory(); DatabasePtr database = DatabaseFactory::get(database_engine_name, database_name, metadata_path, context); /// Will write file with database metadata, if needed. String metadata_file_tmp_path = path + "metadata/" + database_name_escaped + ".sql.tmp"; String metadata_file_path = path + "metadata/" + database_name_escaped + ".sql"; bool need_write_metadata = !create.attach; if (need_write_metadata) { create.attach = true; create.if_not_exists = false; std::ostringstream statement_stream; formatAST(create, statement_stream, false); statement_stream << '\n'; String statement = statement_stream.str(); /// Exclusive flag guarantees, that database is not created right now in another thread. WriteBufferFromFile out(metadata_file_tmp_path, statement.size(), O_WRONLY | O_CREAT | O_EXCL); writeString(statement, out); out.next(); if (context.getSettingsRef().fsync_metadata) out.sync(); out.close(); } try { context.addDatabase(database_name, database); if (need_write_metadata) Poco::File(metadata_file_tmp_path).renameTo(metadata_file_path); database->loadTables(context, thread_pool, has_force_restore_data_flag); } catch (...) { if (need_write_metadata) Poco::File(metadata_file_tmp_path).remove(); throw; } return {}; }
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create) { if (!create.cluster.empty()) { NameSet databases{create.database}; if (!create.to_table.empty()) databases.emplace(create.to_database); return executeDDLQueryOnCluster(query_ptr, context, databases); } String path = context.getPath(); String current_database = context.getCurrentDatabase(); String database_name = create.database.empty() ? current_database : create.database; String table_name = create.table; String table_name_escaped = escapeForFileName(table_name); // If this is a stub ATTACH query, read the query definition from the database if (create.attach && !create.storage && !create.columns) { // Table SQL definition is available even if the table is detached auto query = context.getCreateTableQuery(database_name, table_name); auto & as_create = typeid_cast<const ASTCreateQuery &>(*query); create = as_create; // Copy the saved create query, but use ATTACH instead of CREATE create.attach = true; } if (create.to_database.empty()) create.to_database = current_database; if (create.select && (create.is_view || create.is_materialized_view)) create.select->setDatabaseIfNeeded(current_database); Block as_select_sample; if (create.select && (!create.attach || !create.columns)) as_select_sample = InterpreterSelectWithUnionQuery::getSampleBlock(create.select->clone(), context); String as_database_name = create.as_database.empty() ? current_database : create.as_database; String as_table_name = create.as_table; StoragePtr as_storage; TableStructureReadLockPtr as_storage_lock; if (!as_table_name.empty()) { as_storage = context.getTable(as_database_name, as_table_name); as_storage_lock = as_storage->lockStructure(false, __PRETTY_FUNCTION__); } /// Set and retrieve list of columns. ColumnsDescription columns = setColumns(create, as_select_sample, as_storage); /// Set the table engine if it was not specified explicitly. setEngine(create); StoragePtr res; { std::unique_ptr<DDLGuard> guard; String data_path; DatabasePtr database; if (!create.is_temporary) { database = context.getDatabase(database_name); data_path = database->getDataPath(); /** If the table already exists, and the request specifies IF NOT EXISTS, * then we allow concurrent CREATE queries (which do nothing). * Otherwise, concurrent queries for creating a table, if the table does not exist, * can throw an exception, even if IF NOT EXISTS is specified. */ 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 {}; else throw Exception("Table " + database_name + "." + table_name + " already exists.", ErrorCodes::TABLE_ALREADY_EXISTS); } } else if (context.tryGetExternalTable(table_name) && create.if_not_exists) return {}; res = StorageFactory::instance().get(create, data_path, table_name, database_name, context, context.getGlobalContext(), columns, create.attach, false); if (create.is_temporary) context.getSessionContext().addExternalTable(table_name, res, query_ptr); else database->createTable(context, table_name, res, query_ptr); } res->startup(); /// If the query is a CREATE SELECT, insert the data into the table. if (create.select && !create.attach && !create.is_view && (!create.is_materialized_view || create.is_populate)) { auto insert = std::make_shared<ASTInsertQuery>(); if (!create.is_temporary) insert->database = database_name; insert->table = table_name; insert->select = create.select->clone(); return InterpreterInsertQuery(insert, create.is_temporary ? context.getSessionContext() : context, context.getSettingsRef().insert_allow_materialized_columns).execute(); } return {}; }