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 | |