better_duck_core/
database.rs1use std::{path::Path, sync::Arc};
2
3use crate::{
4 config::Config,
5 connection::Connection,
6 error::Result,
7 helpers::path::path_to_cstring,
8 raw::connection::{RawConnection, RawDatabase},
9};
10
11#[derive(Clone)]
36pub struct Database {
37 inner: Arc<RawDatabase>,
38}
39
40impl Database {
41 pub(crate) fn from_raw(inner: Arc<RawDatabase>) -> Database {
43 Database { inner }
44 }
45
46 pub fn open<P: AsRef<Path>>(path: P) -> Result<Database> {
52 Self::open_with_flags(path, Config::default())
53 }
54
55 pub fn open_with_flags<P: AsRef<Path>>(
61 path: P,
62 config: Config,
63 ) -> Result<Database> {
64 let c_path = path_to_cstring(path.as_ref())?;
65 let config = config.with("duckdb_api", "rust")?;
66 RawDatabase::open_with_flags(&c_path, config).map(|db| Database { inner: Arc::new(db) })
67 }
68
69 pub fn open_in_memory() -> Result<Database> {
75 Self::open_in_memory_with_flags(Config::default())
76 }
77
78 pub fn open_in_memory_with_flags(config: Config) -> Result<Database> {
84 Self::open_with_flags(":memory:", config)
85 }
86
87 pub fn connect(&self) -> Result<Connection> {
96 RawConnection::new(Arc::clone(&self.inner)).map(Connection::from_raw)
97 }
98}
99
100impl std::fmt::Debug for Database {
101 fn fmt(
102 &self,
103 f: &mut std::fmt::Formatter<'_>,
104 ) -> std::fmt::Result {
105 f.debug_struct("Database").finish_non_exhaustive()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn database_connect_shares_in_memory_state() {
115 let db = Database::open_in_memory().unwrap();
116 let mut a = db.connect().unwrap();
117 let mut b = db.connect().unwrap();
118 a.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
119 b.execute_batch("INSERT INTO t VALUES (1)").unwrap();
120 let mut result = a.execute("SELECT count(*) AS c FROM t").unwrap();
121 let row = result.next().unwrap().unwrap();
122 match row.get("c").unwrap() {
123 crate::types::value::DuckValue::BigInt(n) => assert_eq!(*n, 1),
124 other => panic!("expected BigInt, got {other:?}"),
125 }
126 }
127
128 #[test]
129 fn separate_open_in_memory_are_independent() {
130 let mut a = Connection::open_in_memory().unwrap();
131 let mut b = Connection::open_in_memory().unwrap();
132 a.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
133 assert!(b.execute_batch("INSERT INTO t VALUES (1)").is_err());
135 }
136
137 #[test]
138 fn database_outlives_connection() {
139 let db = Database::open_in_memory().unwrap();
140 let mut conn = db.connect().unwrap();
141 drop(db);
142 conn.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
144 }
145
146 #[test]
147 fn connection_try_clone_shares_state() {
148 let mut a = Connection::open_in_memory().unwrap();
149 a.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
150 let mut b = a.try_clone().unwrap();
151 b.execute_batch("INSERT INTO t VALUES (1)").unwrap();
152 let mut result = a.execute("SELECT count(*) AS c FROM t").unwrap();
153 let row = result.next().unwrap().unwrap();
154 match row.get("c").unwrap() {
155 crate::types::value::DuckValue::BigInt(n) => assert_eq!(*n, 1),
156 other => panic!("expected BigInt, got {other:?}"),
157 }
158 }
159
160 #[test]
161 fn connection_database_round_trip() {
162 let conn = Connection::open_in_memory().unwrap();
163 let db = conn.database();
164 let mut other = db.connect().unwrap();
165 other.execute_batch("CREATE TABLE t (id INTEGER)").unwrap();
166 }
167
168 #[test]
169 fn database_is_send_and_sync() {
170 fn assert_send_sync<T: Send + Sync>() {}
171 assert_send_sync::<Database>();
172 }
173}