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
12namespace duckdb {
13
14//===--------------------------------------------------------------------===//
15// Source
16//===--------------------------------------------------------------------===//
17SourceResultType 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