1#include "duckdb/main/db_instance_cache.hpp"
2#include "duckdb/main/extension_helper.hpp"
3
4namespace duckdb {
5
6string 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
25shared_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
44shared_ptr<DuckDB> DBInstanceCache::GetInstance(const string &database, const DBConfig &config) {
45 lock_guard<mutex> l(cache_lock);
46 return GetInstanceInternal(database, config);
47}
48
49shared_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
68shared_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
73shared_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