use std::path::Path;
use crate::{config::Config, connection::Connection, database::Database, error::Error};
#[derive(Clone, Debug)]
pub struct DuckDbConnectionManager {
database: Database,
}
impl DuckDbConnectionManager {
pub fn new(database: Database) -> DuckDbConnectionManager {
DuckDbConnectionManager { database }
}
pub fn file<P: AsRef<Path>>(path: P) -> crate::error::Result<DuckDbConnectionManager> {
Database::open(path).map(DuckDbConnectionManager::new)
}
pub fn file_with_flags<P: AsRef<Path>>(
path: P,
config: Config,
) -> crate::error::Result<DuckDbConnectionManager> {
Database::open_with_flags(path, config).map(DuckDbConnectionManager::new)
}
pub fn memory() -> crate::error::Result<DuckDbConnectionManager> {
Database::open_in_memory().map(DuckDbConnectionManager::new)
}
pub fn memory_with_flags(config: Config) -> crate::error::Result<DuckDbConnectionManager> {
Database::open_in_memory_with_flags(config).map(DuckDbConnectionManager::new)
}
pub fn database(&self) -> &Database {
&self.database
}
}
impl r2d2::ManageConnection for DuckDbConnectionManager {
type Connection = Connection;
type Error = Error;
fn connect(&self) -> Result<Connection, Error> {
self.database.connect()
}
fn is_valid(
&self,
conn: &mut Connection,
) -> Result<(), Error> {
conn.execute_batch("SELECT 1")
}
fn has_broken(
&self,
conn: &mut Connection,
) -> bool {
!conn.is_open()
}
}
pub type Pool = r2d2::Pool<DuckDbConnectionManager>;
pub type PooledConnection = r2d2::PooledConnection<DuckDbConnectionManager>;
pub use r2d2::Builder as PoolBuilder;
pub use r2d2::Error as PoolError;
pub use r2d2::State as PoolState;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pool_shares_one_in_memory_database() {
let manager = DuckDbConnectionManager::memory().unwrap();
let pool = Pool::builder().max_size(4).build(manager).unwrap();
let mut a = pool.get().unwrap();
a.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
drop(a);
let mut b = pool.get().unwrap();
b.execute_batch("INSERT INTO t VALUES (1)").unwrap();
let mut result = b.execute("SELECT count(*) AS c FROM t").unwrap();
let row = result.next().unwrap().unwrap();
match row.get("c").unwrap() {
crate::types::value::DuckValue::BigInt(n) => assert_eq!(*n, 1),
other => panic!("expected BigInt, got {other:?}"),
}
}
#[test]
fn pool_max_size_is_enforced() {
let manager = DuckDbConnectionManager::memory().unwrap();
let pool = Pool::builder()
.max_size(1)
.connection_timeout(std::time::Duration::from_millis(100))
.build(manager)
.unwrap();
let _held = pool.get().unwrap();
assert!(pool.get().is_err());
}
#[test]
fn manager_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<DuckDbConnectionManager>();
}
}