1 | #include "duckdb/main/db_instance_cache.hpp" |
2 | #include "duckdb/main/extension_helper.hpp" |
3 | |
4 | namespace duckdb { |
5 | |
6 | string GetDBAbsolutePath(const string &database_p) { |
7 | auto database = FileSystem::ExpandPath(path: database_p, opener: nullptr); |
8 | if (database.empty()) { |
9 | return ":memory:" ; |
10 | } |
11 | if (database.rfind(s: ":memory:" , pos: 0) == 0) { |
12 | // this is a memory db, just return it. |
13 | return database; |
14 | } |
15 | if (!ExtensionHelper::ExtractExtensionPrefixFromPath(path: database).empty()) { |
16 | // this database path is handled by a replacement open and is not a file path |
17 | return database; |
18 | } |
19 | if (FileSystem::IsPathAbsolute(path: database)) { |
20 | return FileSystem::NormalizeAbsolutePath(path: database); |
21 | } |
22 | return FileSystem::NormalizeAbsolutePath(path: FileSystem::JoinPath(a: FileSystem::GetWorkingDirectory(), path: database)); |
23 | } |
24 | |
25 | shared_ptr<DuckDB> DBInstanceCache::GetInstanceInternal(const string &database, const DBConfig &config) { |
26 | shared_ptr<DuckDB> db_instance; |
27 | auto abs_database_path = GetDBAbsolutePath(database_p: database); |
28 | if (db_instances.find(x: abs_database_path) != db_instances.end()) { |
29 | db_instance = db_instances[abs_database_path].lock(); |
30 | if (db_instance) { |
31 | if (db_instance->instance->config != config) { |
32 | throw duckdb::ConnectionException( |
33 | "Can't open a connection to same database file with a different configuration " |
34 | "than existing connections" ); |
35 | } |
36 | } else { |
37 | // clean-up |
38 | db_instances.erase(x: abs_database_path); |
39 | } |
40 | } |
41 | return db_instance; |
42 | } |
43 | |
44 | shared_ptr<DuckDB> DBInstanceCache::GetInstance(const string &database, const DBConfig &config) { |
45 | lock_guard<mutex> l(cache_lock); |
46 | return GetInstanceInternal(database, config); |
47 | } |
48 | |
49 | shared_ptr<DuckDB> DBInstanceCache::CreateInstanceInternal(const string &database, DBConfig &config, |
50 | bool cache_instance) { |
51 | auto abs_database_path = GetDBAbsolutePath(database_p: database); |
52 | if (db_instances.find(x: abs_database_path) != db_instances.end()) { |
53 | throw duckdb::Exception(ExceptionType::CONNECTION, |
54 | "Instance with path: " + abs_database_path + " already exists." ); |
55 | } |
56 | // Creates new instance |
57 | string instance_path = abs_database_path; |
58 | if (abs_database_path.rfind(s: ":memory:" , pos: 0) == 0) { |
59 | instance_path = ":memory:" ; |
60 | } |
61 | auto db_instance = make_shared<DuckDB>(args&: instance_path, args: &config); |
62 | if (cache_instance) { |
63 | db_instances[abs_database_path] = db_instance; |
64 | } |
65 | return db_instance; |
66 | } |
67 | |
68 | shared_ptr<DuckDB> DBInstanceCache::CreateInstance(const string &database, DBConfig &config, bool cache_instance) { |
69 | lock_guard<mutex> l(cache_lock); |
70 | return CreateInstanceInternal(database, config, cache_instance); |
71 | } |
72 | |
73 | shared_ptr<DuckDB> DBInstanceCache::GetOrCreateInstance(const string &database, DBConfig &config_dict, |
74 | bool cache_instance) { |
75 | lock_guard<mutex> l(cache_lock); |
76 | if (cache_instance) { |
77 | auto instance = GetInstanceInternal(database, config: config_dict); |
78 | if (instance) { |
79 | return instance; |
80 | } |
81 | } |
82 | return CreateInstanceInternal(database, config&: config_dict, cache_instance); |
83 | } |
84 | |
85 | } // namespace duckdb |
86 | |