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