use std::ffi::CStr;
use std::os::raw::c_char;
use libduckdb_sys::{
duckdb_config, duckdb_create_instance_cache, duckdb_database, duckdb_destroy_instance_cache,
duckdb_free, duckdb_get_or_create_from_cache, duckdb_instance_cache, DuckDBSuccess,
};
use crate::config::DbConfig;
use crate::error::ExtensionError;
pub struct InstanceCache {
cache: duckdb_instance_cache,
}
impl InstanceCache {
#[must_use]
pub fn new() -> Self {
let cache = unsafe { duckdb_create_instance_cache() };
Self { cache }
}
pub fn get_or_create(
&self,
path: &CStr,
config: Option<&DbConfig>,
) -> Result<duckdb_database, ExtensionError> {
let mut out_db: duckdb_database = std::ptr::null_mut();
let mut out_err: *mut c_char = std::ptr::null_mut();
let cfg: duckdb_config = config.map_or(std::ptr::null_mut(), DbConfig::as_raw);
let state = unsafe {
duckdb_get_or_create_from_cache(
self.cache,
path.as_ptr(),
&raw mut out_db,
cfg,
&raw mut out_err,
)
};
if state == DuckDBSuccess && !out_db.is_null() {
return Ok(out_db);
}
let message = if out_err.is_null() {
"failed to open database from instance cache".to_owned()
} else {
let msg = unsafe { CStr::from_ptr(out_err) }
.to_str()
.unwrap_or("failed to open database from instance cache")
.to_owned();
unsafe { duckdb_free(out_err.cast()) };
msg
};
Err(ExtensionError::new(message))
}
#[inline]
#[must_use]
pub const fn as_raw(&self) -> duckdb_instance_cache {
self.cache
}
}
impl Default for InstanceCache {
fn default() -> Self {
Self::new()
}
}
impl Drop for InstanceCache {
fn drop(&mut self) {
if !self.cache.is_null() {
unsafe { duckdb_destroy_instance_cache(&raw mut self.cache) };
}
}
}
#[cfg(all(test, feature = "bundled-test"))]
mod tests {
use super::*;
#[test]
fn open_in_memory_via_cache() {
let _db = crate::testing::InMemoryDb::open().unwrap();
let cache = InstanceCache::new();
let result = cache.get_or_create(c"", None);
assert!(result.is_ok(), "get_or_create failed: {:?}", result.err());
let mut db = result.unwrap();
unsafe { libduckdb_sys::duckdb_close(&raw mut db) };
}
}