| 1 | #include "duckdb/execution/operator/schema/physical_attach.hpp" |
| 2 | |
| 3 | #include "duckdb/catalog/catalog.hpp" |
| 4 | #include "duckdb/main/attached_database.hpp" |
| 5 | #include "duckdb/main/database.hpp" |
| 6 | #include "duckdb/main/database_manager.hpp" |
| 7 | #include "duckdb/main/database_path_and_type.hpp" |
| 8 | #include "duckdb/main/extension_helper.hpp" |
| 9 | #include "duckdb/parser/parsed_data/attach_info.hpp" |
| 10 | #include "duckdb/storage/storage_extension.hpp" |
| 11 | |
| 12 | namespace duckdb { |
| 13 | |
| 14 | //===--------------------------------------------------------------------===// |
| 15 | // Source |
| 16 | //===--------------------------------------------------------------------===// |
| 17 | SourceResultType PhysicalAttach::GetData(ExecutionContext &context, DataChunk &chunk, |
| 18 | OperatorSourceInput &input) const { |
| 19 | // parse the options |
| 20 | auto &config = DBConfig::GetConfig(context&: context.client); |
| 21 | AccessMode access_mode = config.options.access_mode; |
| 22 | string type; |
| 23 | string unrecognized_option; |
| 24 | for (auto &entry : info->options) { |
| 25 | if (entry.first == "readonly" || entry.first == "read_only" ) { |
| 26 | auto read_only = BooleanValue::Get(value: entry.second.DefaultCastAs(target_type: LogicalType::BOOLEAN)); |
| 27 | if (read_only) { |
| 28 | access_mode = AccessMode::READ_ONLY; |
| 29 | } else { |
| 30 | access_mode = AccessMode::READ_WRITE; |
| 31 | } |
| 32 | } else if (entry.first == "readwrite" || entry.first == "read_write" ) { |
| 33 | auto read_only = !BooleanValue::Get(value: entry.second.DefaultCastAs(target_type: LogicalType::BOOLEAN)); |
| 34 | if (read_only) { |
| 35 | access_mode = AccessMode::READ_ONLY; |
| 36 | } else { |
| 37 | access_mode = AccessMode::READ_WRITE; |
| 38 | } |
| 39 | } else if (entry.first == "type" ) { |
| 40 | type = StringValue::Get(value: entry.second.DefaultCastAs(target_type: LogicalType::VARCHAR)); |
| 41 | } else if (unrecognized_option.empty()) { |
| 42 | unrecognized_option = entry.first; |
| 43 | } |
| 44 | } |
| 45 | auto &db = DatabaseInstance::GetDatabase(context&: context.client); |
| 46 | if (type.empty()) { |
| 47 | // try to extract type from path |
| 48 | auto path_and_type = DBPathAndType::Parse(combined_path: info->path, config); |
| 49 | type = path_and_type.type; |
| 50 | info->path = path_and_type.path; |
| 51 | } |
| 52 | |
| 53 | if (type.empty() && !unrecognized_option.empty()) { |
| 54 | throw BinderException("Unrecognized option for attach \"%s\"" , unrecognized_option); |
| 55 | } |
| 56 | |
| 57 | // if we are loading a database type from an extension - check if that extension is loaded |
| 58 | if (!type.empty()) { |
| 59 | if (!db.ExtensionIsLoaded(name: type)) { |
| 60 | ExtensionHelper::LoadExternalExtension(context&: context.client, extension: type); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | // attach the database |
| 65 | auto &name = info->name; |
| 66 | const auto &path = info->path; |
| 67 | |
| 68 | if (name.empty()) { |
| 69 | name = AttachedDatabase::ExtractDatabaseName(dbpath: path); |
| 70 | } |
| 71 | auto &db_manager = DatabaseManager::Get(db&: context.client); |
| 72 | auto existing_db = db_manager.GetDatabaseFromPath(context&: context.client, path); |
| 73 | if (existing_db) { |
| 74 | throw BinderException("Database \"%s\" is already attached with alias \"%s\"" , path, existing_db->GetName()); |
| 75 | } |
| 76 | auto new_db = db.CreateAttachedDatabase(info&: *info, type, access_mode); |
| 77 | new_db->Initialize(); |
| 78 | |
| 79 | db_manager.AddDatabase(context&: context.client, db: std::move(new_db)); |
| 80 | |
| 81 | return SourceResultType::FINISHED; |
| 82 | } |
| 83 | |
| 84 | } // namespace duckdb |
| 85 | |