1use std::{
2 ffi::{CStr, CString},
3 mem,
4 os::raw::c_char,
5 ptr, str,
6 sync::{Arc, Mutex},
7};
8
9use super::{Appender, Config, Connection, Result, ffi};
10use crate::{
11 error::{
12 Error, result_from_duckdb_appender, result_from_duckdb_extract, result_from_duckdb_prepare,
13 result_from_duckdb_result,
14 },
15 raw_statement::RawStatement,
16 statement::Statement,
17};
18
19#[derive(Debug)]
20struct DatabaseHandle {
21 db: ffi::duckdb_database,
22 close_on_drop: bool,
23}
24
25unsafe impl Send for DatabaseHandle {}
29
30impl DatabaseHandle {
31 #[inline]
32 pub fn new(db: ffi::duckdb_database, close_on_drop: bool) -> Self {
33 Self { db, close_on_drop }
34 }
35
36 #[inline]
37 pub fn raw(&self) -> ffi::duckdb_database {
38 self.db
39 }
40}
41
42impl Drop for DatabaseHandle {
43 fn drop(&mut self) {
44 if !self.close_on_drop {
45 return;
46 }
47 if self.db.is_null() {
48 return;
49 }
50 unsafe {
51 ffi::duckdb_close(&mut self.db);
52 self.db = ptr::null_mut();
53 }
54 }
55}
56
57pub struct InnerConnection {
58 database: Arc<Mutex<DatabaseHandle>>,
61 pub con: ffi::duckdb_connection,
62 interrupt: Arc<InterruptHandle>,
63}
64
65impl InnerConnection {
66 #[inline]
67 unsafe fn new(database: Arc<Mutex<DatabaseHandle>>) -> Result<Self> {
68 unsafe {
69 let mut con: ffi::duckdb_connection = ptr::null_mut();
70 let db_raw = database.lock().expect("database handle mutex poisoned").raw();
71 let r = ffi::duckdb_connect(db_raw, &mut con);
72 if r != ffi::DuckDBSuccess {
73 ffi::duckdb_disconnect(&mut con);
74 return Err(Error::DuckDBFailure(
75 ffi::Error::new(r),
76 Some("connect error".to_owned()),
77 ));
78 }
79 let interrupt = Arc::new(InterruptHandle::new(con));
80
81 Ok(Self {
82 database,
83 con,
84 interrupt,
85 })
86 }
87 }
88
89 pub fn open_with_flags(c_path: &CStr, config: Config) -> Result<Self> {
90 unsafe {
91 let mut db: ffi::duckdb_database = ptr::null_mut();
92 let mut c_err = std::ptr::null_mut();
93 let r = ffi::duckdb_open_ext(c_path.as_ptr(), &mut db, config.duckdb_config(), &mut c_err);
94 if r != ffi::DuckDBSuccess {
95 let msg = ffi::DuckDbString::from_nullable_ptr(c_err).map(|c_err| c_err.to_string_lossy().to_string());
96 return Err(Error::DuckDBFailure(ffi::Error::new(r), msg));
97 }
98 Self::new_from_raw_db(db, true)
99 }
100 }
101
102 #[inline]
103 pub(crate) unsafe fn new_from_raw_db(raw: ffi::duckdb_database, close_on_drop: bool) -> Result<Self> {
104 unsafe { Self::new(Arc::new(Mutex::new(DatabaseHandle::new(raw, close_on_drop)))) }
105 }
106
107 pub fn close(&mut self) -> Result<()> {
108 if self.con.is_null() {
109 return Ok(());
110 }
111 unsafe {
112 ffi::duckdb_disconnect(&mut self.con);
113 self.con = ptr::null_mut();
114 self.interrupt.clear();
115 }
116 Ok(())
117 }
118
119 pub fn try_clone(&self) -> Result<Self> {
121 unsafe { Self::new(self.database.clone()) }
122 }
123
124 pub fn execute(&mut self, sql: &str) -> Result<()> {
125 let c_str = CString::new(sql)?;
126 unsafe {
127 let mut out = mem::zeroed();
128 let r = ffi::duckdb_query(self.con, c_str.as_ptr() as *const c_char, &mut out);
129 result_from_duckdb_result(r, &mut out)?;
130 ffi::duckdb_destroy_result(&mut out);
131 Ok(())
132 }
133 }
134
135 pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
136 let c_str = CString::new(sql)?;
137
138 let mut extracted = ptr::null_mut();
140 let num_stmts =
141 unsafe { ffi::duckdb_extract_statements(self.con, c_str.as_ptr() as *const c_char, &mut extracted) };
142 result_from_duckdb_extract(num_stmts, extracted)?;
143
144 let _guard = ExtractedStatementsGuard(extracted);
146
147 for i in 0..num_stmts - 1 {
149 self.execute_extracted_statement(extracted, i)?;
150 }
151
152 let final_stmt = self.prepare_extracted_statement(extracted, num_stmts - 1)?;
154 Ok(Statement::new(conn, unsafe { RawStatement::new(final_stmt) }))
155 }
156
157 fn prepare_extracted_statement(
158 &self,
159 extracted: ffi::duckdb_extracted_statements,
160 index: ffi::idx_t,
161 ) -> Result<ffi::duckdb_prepared_statement> {
162 let mut stmt = ptr::null_mut();
163 let res = unsafe { ffi::duckdb_prepare_extracted_statement(self.con, extracted, index, &mut stmt) };
164 result_from_duckdb_prepare(res, stmt)?;
165 Ok(stmt)
166 }
167
168 fn execute_extracted_statement(
169 &self,
170 extracted: ffi::duckdb_extracted_statements,
171 index: ffi::idx_t,
172 ) -> Result<()> {
173 let mut stmt = self.prepare_extracted_statement(extracted, index)?;
174
175 let mut result = unsafe { mem::zeroed() };
176 let rc = unsafe { ffi::duckdb_execute_prepared(stmt, &mut result) };
177
178 let error = if rc != ffi::DuckDBSuccess {
179 unsafe {
180 let c_err = ffi::duckdb_result_error(&mut result as *mut _);
181 let msg = if c_err.is_null() {
182 None
183 } else {
184 Some(CStr::from_ptr(c_err).to_string_lossy().to_string())
185 };
186 Some(Error::DuckDBFailure(ffi::Error::new(rc), msg))
187 }
188 } else {
189 None
190 };
191
192 unsafe {
193 ffi::duckdb_destroy_prepare(&mut stmt);
194 ffi::duckdb_destroy_result(&mut result);
195 }
196
197 error.map_or(Ok(()), Err)
198 }
199
200 pub fn appender<'a>(
201 &mut self,
202 conn: &'a Connection,
203 catalog: Option<&str>,
204 schema: &str,
205 table: &str,
206 ) -> Result<Appender<'a>> {
207 let c_app = self.create_table_appender(catalog, schema, table)?;
208 Ok(Appender::new(conn, c_app))
209 }
210
211 fn create_table_appender(
212 &mut self,
213 catalog: Option<&str>,
214 schema: &str,
215 table: &str,
216 ) -> Result<ffi::duckdb_appender> {
217 let mut c_app: ffi::duckdb_appender = ptr::null_mut();
218 let c_catalog = catalog.map(CString::new).transpose()?;
219 let c_table = CString::new(table)?;
220 let c_schema = CString::new(schema)?;
221 let r = unsafe {
222 ffi::duckdb_appender_create_ext(
223 self.con,
224 c_catalog.as_ref().map_or(ptr::null(), |c| c.as_ptr() as *const c_char),
225 c_schema.as_ptr() as *const c_char,
226 c_table.as_ptr() as *const c_char,
227 &mut c_app,
228 )
229 };
230 result_from_duckdb_appender(r, &mut c_app)?;
231 Ok(c_app)
232 }
233
234 pub fn appender_with_columns<'a>(
235 &mut self,
236 conn: &'a Connection,
237 catalog: Option<&str>,
238 schema: &str,
239 table: &str,
240 columns: &[&str],
241 ) -> Result<Appender<'a>> {
242 let c_app = self.create_table_appender(catalog, schema, table)?;
245 let mut appender = Appender::new(conn, c_app);
246 for column in columns {
247 appender.add_column(column)?;
248 }
249 Ok(appender)
250 }
251
252 pub fn get_interrupt_handle(&self) -> Arc<InterruptHandle> {
253 self.interrupt.clone()
254 }
255
256 #[inline]
257 pub fn is_autocommit(&self) -> bool {
258 true
259 }
260}
261
262struct ExtractedStatementsGuard(ffi::duckdb_extracted_statements);
263
264impl Drop for ExtractedStatementsGuard {
265 fn drop(&mut self) {
266 unsafe { ffi::duckdb_destroy_extracted(&mut self.0) }
267 }
268}
269
270impl Drop for InnerConnection {
271 #[inline]
272 fn drop(&mut self) {
273 use std::thread::panicking;
274 if let Err(e) = self.close() {
275 if panicking() {
276 eprintln!("Error while closing DuckDB connection: {e:?}");
277 } else {
278 panic!("Error while closing DuckDB connection: {e:?}");
279 }
280 }
281 }
282}
283
284pub struct InterruptHandle {
286 conn: Mutex<ffi::duckdb_connection>,
287}
288
289unsafe impl Send for InterruptHandle {}
290unsafe impl Sync for InterruptHandle {}
291
292impl InterruptHandle {
293 fn new(conn: ffi::duckdb_connection) -> Self {
294 Self { conn: Mutex::new(conn) }
295 }
296
297 fn clear(&self) {
298 *(self.conn.lock().unwrap()) = ptr::null_mut();
299 }
300
301 pub fn interrupt(&self) {
308 let db_handle = self.conn.lock().unwrap();
309
310 if !db_handle.is_null() {
311 unsafe {
312 ffi::duckdb_interrupt(*db_handle);
313 }
314 }
315 }
316}