use std::path::Path;
use crate::{
config::Config,
database::Database,
error::Result,
helpers::path::path_to_cstring,
raw::{appender::Appender, connection::RawConnection, result::DuckResult},
types::appendable::AppendAble,
};
pub struct Connection(RawConnection);
impl Connection {
pub(crate) fn from_raw(raw: RawConnection) -> Connection {
Connection(raw)
}
}
impl Connection {
#[must_use = "connection should be used or explicitly dropped"]
#[inline]
#[allow(unused)]
pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> {
Self::open_with_flags(path, Config::default())
}
#[must_use = "connection should be used or explicitly dropped"]
#[inline]
#[allow(unused)]
pub fn open_with_flags<P: AsRef<Path>>(
path: P,
config: Config,
) -> Result<Connection> {
let c_path = path_to_cstring(path.as_ref())?;
let config = config.with("duckdb_api", "rust")?;
RawConnection::open_with_flags(&c_path, config).map(Connection)
}
}
impl Connection {
#[must_use = "connection should be used or explicitly dropped"]
#[inline]
#[allow(unused)]
pub fn open_in_memory() -> Result<Connection> {
Self::open_in_memory_with_flags(Config::default())
}
#[must_use = "connection should be used or explicitly dropped"]
#[inline]
#[allow(unused)]
pub fn open_in_memory_with_flags(config: Config) -> Result<Connection> {
Self::open_with_flags(":memory:", config)
}
}
impl Connection {
#[must_use = "execute_batch result should be checked"]
#[allow(unused)]
pub fn execute_batch(
&mut self,
sql: impl AsRef<str>,
) -> Result<()> {
self.0.query(sql).map(|_| ())
}
#[must_use = "the DuckResult carries both affected-row count (.changes()) and a row iterator — consume it"]
pub fn execute(
&mut self,
sql: impl AsRef<str>,
) -> Result<DuckResult> {
self.0.execute(sql, &mut [])
}
#[must_use = "the DuckResult carries both affected-row count (.changes()) and a row iterator — consume it"]
pub fn execute_with(
&mut self,
sql: impl AsRef<str>,
binds: &mut [&mut dyn AppendAble],
) -> Result<DuckResult> {
self.0.execute(sql, binds)
}
#[must_use = "appender should be used to insert rows"]
#[allow(unused)]
pub fn appender(
&mut self,
table: &str,
schema: &str,
) -> Result<Appender> {
self.0.appender(table, schema)
}
}
impl Connection {
#[must_use = "close result should be checked"]
#[inline]
#[allow(unused)]
pub fn close(&mut self) -> Result<()> {
self.0.close()
}
#[inline]
#[allow(unused)]
pub fn is_open(&self) -> bool {
!self.0.con.is_null()
}
#[inline]
#[allow(unused)]
#[allow(private_interfaces)]
pub fn db(&self) -> &RawConnection {
&self.0
}
#[inline]
pub fn try_clone(&self) -> Result<Connection> {
self.0.try_clone().map(Connection)
}
#[inline]
pub fn database(&self) -> Database {
Database::from_raw(std::sync::Arc::clone(&self.0.db))
}
#[cfg(feature = "udf")]
#[inline]
pub(crate) fn raw_con(&self) -> crate::ffi::duckdb_connection {
self.0.con
}
}
unsafe impl Send for Connection {}
#[cfg(test)]
mod connection_tests {
use super::*;
use crate::config::Config;
#[test]
fn test_open_in_memory() {
let mut conn = Connection::open_in_memory().unwrap();
assert!(conn.is_open());
conn.close().unwrap();
assert!(!conn.is_open());
}
#[test]
fn test_open_with_flags() {
let config = Config::default().with("duckdb_api", "rust").unwrap();
let mut conn = Connection::open_with_flags(":memory:", config).unwrap();
assert!(conn.is_open());
conn.close().unwrap();
assert!(!conn.is_open());
}
#[test]
fn test_batch_execution() {
let mut conn = Connection::open_in_memory().unwrap();
let exec = conn.execute_batch("CREATE TABLE test (id INTEGER, name TEXT)");
assert!(exec.is_ok(), "{}", exec.unwrap_err());
let exec = conn.execute_batch("INSERT INTO test VALUES (1, 'example')");
assert!(exec.is_ok(), "{}", exec.unwrap_err());
conn.close().unwrap();
}
}