Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/include/duckdb/main/db_instance_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct DatabaseCacheEntry {
mutex update_database_mutex;
};

enum class CacheBehavior { AUTOMATIC, ALWAYS_CACHE, NEVER_CACHE };

class DBInstanceCache {
public:
DBInstanceCache();
Expand All @@ -41,6 +43,9 @@ class DBInstanceCache {
//! Either returns an existing entry, or creates and caches a new DB Instance
shared_ptr<DuckDB> GetOrCreateInstance(const string &database, DBConfig &config_dict, bool cache_instance,
const std::function<void(DuckDB &)> &on_create = nullptr);
shared_ptr<DuckDB> GetOrCreateInstance(const string &database, DBConfig &config_dict,
CacheBehavior cache_behavior = CacheBehavior::AUTOMATIC,
const std::function<void(DuckDB &)> &on_create = nullptr);

private:
shared_ptr<DatabaseFilePathManager> path_manager;
Expand Down
2 changes: 1 addition & 1 deletion src/main/capi/duckdb-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ duckdb_state duckdb_open_internal(DBInstanceCacheWrapper *cache, const char *pat
if (path) {
path_str = path;
}
wrapper->database = cache->instance_cache->GetOrCreateInstance(path_str, *db_config, true);
wrapper->database = cache->instance_cache->GetOrCreateInstance(path_str, *db_config);
} else {
wrapper->database = duckdb::make_shared_ptr<DuckDB>(path, db_config);
}
Expand Down
16 changes: 15 additions & 1 deletion src/main/db_instance_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,23 @@ shared_ptr<DuckDB> DBInstanceCache::CreateInstance(const string &database, DBCon
shared_ptr<DuckDB> DBInstanceCache::GetOrCreateInstance(const string &database, DBConfig &config_dict,
bool cache_instance,
const std::function<void(DuckDB &)> &on_create) {
auto cache_behavior = cache_instance ? CacheBehavior::ALWAYS_CACHE : CacheBehavior::NEVER_CACHE;
return GetOrCreateInstance(database, config_dict, cache_behavior, on_create);
}

shared_ptr<DuckDB> DBInstanceCache::GetOrCreateInstance(const string &database, DBConfig &config_dict,
CacheBehavior cache_behavior,
const std::function<void(DuckDB &)> &on_create) {
unique_lock<mutex> lock(cache_lock, std::defer_lock);
bool cache_instance = cache_behavior == CacheBehavior::ALWAYS_CACHE;
if (cache_behavior == CacheBehavior::AUTOMATIC) {
// cache all unnamed in-memory connections
cache_instance = true;
if (database == IN_MEMORY_PATH || database.empty()) {
cache_instance = false;
}
}
if (cache_instance) {

// While we do not own the lock, we cannot definitively say that the database instance does not exist.
while (!lock.owns_lock()) {
// The problem is, that we have to unlock the mutex in GetInstanceInternal, so we can non-blockingly wait
Expand Down
24 changes: 24 additions & 0 deletions test/api/test_instance_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,27 @@ TEST_CASE("Test attaching the same database path from different databases in rea
REQUIRE_FAIL(con1.Query(read_only_attach));
}
}

TEST_CASE("Test automatic DB instance caching", "[api][.]") {
DBInstanceCache instance_cache;
DBConfig config;

SECTION("Unnamed in-memory connections are not shared") {
auto db1 = instance_cache.GetOrCreateInstance(":memory:", config);
auto db2 = instance_cache.GetOrCreateInstance(":memory:", config);

Connection con(*db1);
Connection con2(*db2);
REQUIRE_NO_FAIL(con.Query("CREATE TABLE t(i INT)"));
REQUIRE_NO_FAIL(con2.Query("CREATE TABLE t(i INT)"));
}
SECTION("Named in-memory connections are shared") {
auto db1 = instance_cache.GetOrCreateInstance(":memory:abc", config);
auto db2 = instance_cache.GetOrCreateInstance(":memory:abc", config);

Connection con(*db1);
Connection con2(*db2);
REQUIRE_NO_FAIL(con.Query("CREATE TABLE t(i INT)"));
REQUIRE_NO_FAIL(con2.Query("SELECT * FROM t"));
}
}
Loading