1 use std::ffi::CStr;
2 use std::os::raw::{c_char, c_int};
3 #[cfg(feature = "load_extension")]
4 use std::path::Path;
5 use std::ptr;
6 use std::str;
7 use std::sync::atomic::{AtomicBool, Ordering};
8 use std::sync::{Arc, Mutex};
9 
10 use super::ffi;
11 use super::str_for_sqlite;
12 use super::{Connection, InterruptHandle, OpenFlags, Result};
13 use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
14 use crate::raw_statement::RawStatement;
15 use crate::statement::Statement;
16 use crate::version::version_number;
17 
18 pub struct InnerConnection {
19     pub db: *mut ffi::sqlite3,
20     // It's unsafe to call `sqlite3_close` while another thread is performing
21     // a `sqlite3_interrupt`, and vice versa, so we take this mutex during
22     // those functions. This protects a copy of the `db` pointer (which is
23     // cleared on closing), however the main copy, `db`, is unprotected.
24     // Otherwise, a long running query would prevent calling interrupt, as
25     // interrupt would only acquire the lock after the query's completion.
26     interrupt_lock: Arc<Mutex<*mut ffi::sqlite3>>,
27     #[cfg(feature = "hooks")]
28     pub free_commit_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
29     #[cfg(feature = "hooks")]
30     pub free_rollback_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
31     #[cfg(feature = "hooks")]
32     pub free_update_hook: Option<unsafe fn(*mut std::os::raw::c_void)>,
33     #[cfg(feature = "hooks")]
34     pub progress_handler: Option<Box<dyn FnMut() -> bool + Send>>,
35     #[cfg(feature = "hooks")]
36     pub authorizer: Option<crate::hooks::BoxedAuthorizer>,
37     owned: bool,
38 }
39 
40 unsafe impl Send for InnerConnection {}
41 
42 impl InnerConnection {
43     #[allow(clippy::mutex_atomic)]
44     #[inline]
new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection45     pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
46         InnerConnection {
47             db,
48             interrupt_lock: Arc::new(Mutex::new(db)),
49             #[cfg(feature = "hooks")]
50             free_commit_hook: None,
51             #[cfg(feature = "hooks")]
52             free_rollback_hook: None,
53             #[cfg(feature = "hooks")]
54             free_update_hook: None,
55             #[cfg(feature = "hooks")]
56             progress_handler: None,
57             #[cfg(feature = "hooks")]
58             authorizer: None,
59             owned,
60         }
61     }
62 
open_with_flags( c_path: &CStr, flags: OpenFlags, vfs: Option<&CStr>, ) -> Result<InnerConnection>63     pub fn open_with_flags(
64         c_path: &CStr,
65         flags: OpenFlags,
66         vfs: Option<&CStr>,
67     ) -> Result<InnerConnection> {
68         ensure_safe_sqlite_threading_mode()?;
69 
70         // Replicate the check for sane open flags from SQLite, because the check in
71         // SQLite itself wasn't added until version 3.7.3.
72         debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits(), 0x02);
73         debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits(), 0x04);
74         debug_assert_eq!(
75             1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits(),
76             0x40
77         );
78         if (1 << (flags.bits() & 0x7)) & 0x46 == 0 {
79             return Err(Error::SqliteFailure(
80                 ffi::Error::new(ffi::SQLITE_MISUSE),
81                 None,
82             ));
83         }
84 
85         let z_vfs = match vfs {
86             Some(c_vfs) => c_vfs.as_ptr(),
87             None => ptr::null(),
88         };
89 
90         unsafe {
91             let mut db: *mut ffi::sqlite3 = ptr::null_mut();
92             let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs);
93             if r != ffi::SQLITE_OK {
94                 let e = if db.is_null() {
95                     error_from_sqlite_code(r, Some(c_path.to_string_lossy().to_string()))
96                 } else {
97                     let mut e = error_from_handle(db, r);
98                     if let Error::SqliteFailure(
99                         ffi::Error {
100                             code: ffi::ErrorCode::CannotOpen,
101                             ..
102                         },
103                         Some(msg),
104                     ) = e
105                     {
106                         e = Error::SqliteFailure(
107                             ffi::Error::new(r),
108                             Some(format!("{msg}: {}", c_path.to_string_lossy())),
109                         );
110                     }
111                     ffi::sqlite3_close(db);
112                     e
113                 };
114 
115                 return Err(e);
116             }
117 
118             // attempt to turn on extended results code; don't fail if we can't.
119             ffi::sqlite3_extended_result_codes(db, 1);
120 
121             let r = ffi::sqlite3_busy_timeout(db, 5000);
122             if r != ffi::SQLITE_OK {
123                 let e = error_from_handle(db, r);
124                 ffi::sqlite3_close(db);
125                 return Err(e);
126             }
127 
128             Ok(InnerConnection::new(db, true))
129         }
130     }
131 
132     #[inline]
db(&self) -> *mut ffi::sqlite3133     pub fn db(&self) -> *mut ffi::sqlite3 {
134         self.db
135     }
136 
137     #[inline]
decode_result(&self, code: c_int) -> Result<()>138     pub fn decode_result(&self, code: c_int) -> Result<()> {
139         unsafe { InnerConnection::decode_result_raw(self.db(), code) }
140     }
141 
142     #[inline]
decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()>143     unsafe fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> {
144         if code == ffi::SQLITE_OK {
145             Ok(())
146         } else {
147             Err(error_from_handle(db, code))
148         }
149     }
150 
151     #[allow(clippy::mutex_atomic)]
close(&mut self) -> Result<()>152     pub fn close(&mut self) -> Result<()> {
153         if self.db.is_null() {
154             return Ok(());
155         }
156         self.remove_hooks();
157         let mut shared_handle = self.interrupt_lock.lock().unwrap();
158         assert!(
159             !shared_handle.is_null(),
160             "Bug: Somehow interrupt_lock was cleared before the DB was closed"
161         );
162         if !self.owned {
163             self.db = ptr::null_mut();
164             return Ok(());
165         }
166         unsafe {
167             let r = ffi::sqlite3_close(self.db);
168             // Need to use _raw because _guard has a reference out, and
169             // decode_result takes &mut self.
170             let r = InnerConnection::decode_result_raw(self.db, r);
171             if r.is_ok() {
172                 *shared_handle = ptr::null_mut();
173                 self.db = ptr::null_mut();
174             }
175             r
176         }
177     }
178 
179     #[inline]
get_interrupt_handle(&self) -> InterruptHandle180     pub fn get_interrupt_handle(&self) -> InterruptHandle {
181         InterruptHandle {
182             db_lock: Arc::clone(&self.interrupt_lock),
183         }
184     }
185 
186     #[inline]
187     #[cfg(feature = "load_extension")]
enable_load_extension(&mut self, onoff: c_int) -> Result<()>188     pub unsafe fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
189         let r = ffi::sqlite3_enable_load_extension(self.db, onoff);
190         self.decode_result(r)
191     }
192 
193     #[cfg(feature = "load_extension")]
load_extension( &self, dylib_path: &Path, entry_point: Option<&str>, ) -> Result<()>194     pub unsafe fn load_extension(
195         &self,
196         dylib_path: &Path,
197         entry_point: Option<&str>,
198     ) -> Result<()> {
199         let dylib_str = super::path_to_cstring(dylib_path)?;
200         let mut errmsg: *mut c_char = ptr::null_mut();
201         let r = if let Some(entry_point) = entry_point {
202             let c_entry = crate::str_to_cstring(entry_point)?;
203             ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), c_entry.as_ptr(), &mut errmsg)
204         } else {
205             ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg)
206         };
207         if r == ffi::SQLITE_OK {
208             Ok(())
209         } else {
210             let message = super::errmsg_to_string(errmsg);
211             ffi::sqlite3_free(errmsg.cast::<std::os::raw::c_void>());
212             Err(error_from_sqlite_code(r, Some(message)))
213         }
214     }
215 
216     #[inline]
last_insert_rowid(&self) -> i64217     pub fn last_insert_rowid(&self) -> i64 {
218         unsafe { ffi::sqlite3_last_insert_rowid(self.db()) }
219     }
220 
prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>>221     pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
222         let mut c_stmt = ptr::null_mut();
223         let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
224         let mut c_tail = ptr::null();
225         // TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728
226         #[cfg(not(feature = "unlock_notify"))]
227         let r = unsafe {
228             ffi::sqlite3_prepare_v2(
229                 self.db(),
230                 c_sql,
231                 len,
232                 &mut c_stmt as *mut *mut ffi::sqlite3_stmt,
233                 &mut c_tail as *mut *const c_char,
234             )
235         };
236         #[cfg(feature = "unlock_notify")]
237         let r = unsafe {
238             use crate::unlock_notify;
239             let mut rc;
240             loop {
241                 rc = ffi::sqlite3_prepare_v2(
242                     self.db(),
243                     c_sql,
244                     len,
245                     &mut c_stmt as *mut *mut ffi::sqlite3_stmt,
246                     &mut c_tail as *mut *const c_char,
247                 );
248                 if !unlock_notify::is_locked(self.db, rc) {
249                     break;
250                 }
251                 rc = unlock_notify::wait_for_unlock_notify(self.db);
252                 if rc != ffi::SQLITE_OK {
253                     break;
254                 }
255             }
256             rc
257         };
258         // If there is an error, *ppStmt is set to NULL.
259         if r != ffi::SQLITE_OK {
260             return Err(unsafe { error_with_offset(self.db, r, sql) });
261         }
262         // If the input text contains no SQL (if the input is an empty string or a
263         // comment) then *ppStmt is set to NULL.
264         let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
265         let c_tail: *const c_char = c_tail;
266         let tail = if c_tail.is_null() {
267             0
268         } else {
269             let n = (c_tail as isize) - (c_sql as isize);
270             if n <= 0 || n >= len as isize {
271                 0
272             } else {
273                 n as usize
274             }
275         };
276         Ok(Statement::new(conn, unsafe {
277             RawStatement::new(c_stmt, tail)
278         }))
279     }
280 
281     #[inline]
changes(&self) -> u64282     pub fn changes(&self) -> u64 {
283         #[cfg(not(feature = "modern_sqlite"))]
284         unsafe {
285             ffi::sqlite3_changes(self.db()) as u64
286         }
287         #[cfg(feature = "modern_sqlite")] // 3.37.0
288         unsafe {
289             ffi::sqlite3_changes64(self.db()) as u64
290         }
291     }
292 
293     #[inline]
is_autocommit(&self) -> bool294     pub fn is_autocommit(&self) -> bool {
295         unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
296     }
297 
is_busy(&self) -> bool298     pub fn is_busy(&self) -> bool {
299         let db = self.db();
300         unsafe {
301             let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut());
302             while !stmt.is_null() {
303                 if ffi::sqlite3_stmt_busy(stmt) != 0 {
304                     return true;
305                 }
306                 stmt = ffi::sqlite3_next_stmt(db, stmt);
307             }
308         }
309         false
310     }
311 
cache_flush(&mut self) -> Result<()>312     pub fn cache_flush(&mut self) -> Result<()> {
313         crate::error::check(unsafe { ffi::sqlite3_db_cacheflush(self.db()) })
314     }
315 
316     #[cfg(not(feature = "hooks"))]
317     #[inline]
remove_hooks(&mut self)318     fn remove_hooks(&mut self) {}
319 
db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool>320     pub fn db_readonly(&self, db_name: super::DatabaseName<'_>) -> Result<bool> {
321         let name = db_name.as_cstring()?;
322         let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) };
323         match r {
324             0 => Ok(false),
325             1 => Ok(true),
326             -1 => Err(Error::SqliteFailure(
327                 ffi::Error::new(ffi::SQLITE_MISUSE),
328                 Some(format!("{db_name:?} is not the name of a database")),
329             )),
330             _ => Err(error_from_sqlite_code(
331                 r,
332                 Some("Unexpected result".to_owned()),
333             )),
334         }
335     }
336 
337     #[cfg(feature = "modern_sqlite")] // 3.37.0
txn_state( &self, db_name: Option<super::DatabaseName<'_>>, ) -> Result<super::transaction::TransactionState>338     pub fn txn_state(
339         &self,
340         db_name: Option<super::DatabaseName<'_>>,
341     ) -> Result<super::transaction::TransactionState> {
342         let r = if let Some(ref name) = db_name {
343             let name = name.as_cstring()?;
344             unsafe { ffi::sqlite3_txn_state(self.db, name.as_ptr()) }
345         } else {
346             unsafe { ffi::sqlite3_txn_state(self.db, ptr::null()) }
347         };
348         match r {
349             0 => Ok(super::transaction::TransactionState::None),
350             1 => Ok(super::transaction::TransactionState::Read),
351             2 => Ok(super::transaction::TransactionState::Write),
352             -1 => Err(Error::SqliteFailure(
353                 ffi::Error::new(ffi::SQLITE_MISUSE),
354                 Some(format!("{db_name:?} is not the name of a valid schema")),
355             )),
356             _ => Err(error_from_sqlite_code(
357                 r,
358                 Some("Unexpected result".to_owned()),
359             )),
360         }
361     }
362 
363     #[inline]
364     #[cfg(feature = "release_memory")]
release_memory(&self) -> Result<()>365     pub fn release_memory(&self) -> Result<()> {
366         self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) })
367     }
368 }
369 
370 impl Drop for InnerConnection {
371     #[allow(unused_must_use)]
372     #[inline]
drop(&mut self)373     fn drop(&mut self) {
374         self.close();
375     }
376 }
377 
378 #[cfg(not(any(target_arch = "wasm32")))]
379 static SQLITE_INIT: std::sync::Once = std::sync::Once::new();
380 
381 pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
382 
383 // threading mode checks are not necessary (and do not work) on target
384 // platforms that do not have threading (such as webassembly)
385 #[cfg(any(target_arch = "wasm32"))]
ensure_safe_sqlite_threading_mode() -> Result<()>386 fn ensure_safe_sqlite_threading_mode() -> Result<()> {
387     Ok(())
388 }
389 
390 #[cfg(not(any(target_arch = "wasm32")))]
ensure_safe_sqlite_threading_mode() -> Result<()>391 fn ensure_safe_sqlite_threading_mode() -> Result<()> {
392     // Ensure SQLite was compiled in threadsafe mode.
393     if unsafe { ffi::sqlite3_threadsafe() == 0 } {
394         return Err(Error::SqliteSingleThreadedMode);
395     }
396 
397     // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode,
398     // but it's possible someone configured it to be in Single-thread mode
399     // before calling into us. That would mean we're exposing an unsafe API via
400     // a safe one (in Rust terminology), which is no good. We have two options
401     // to protect against this, depending on the version of SQLite we're linked
402     // with:
403     //
404     // 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for
405     //    the magic value 8. This isn't documented, but it's what SQLite
406     //    returns for its mutex allocation function in Single-thread mode.
407     // 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the
408     //    threading mode. The check we perform for >= 3.7.0 will segfault.
409     //    Instead, we insist on being able to call sqlite3_config and
410     //    sqlite3_initialize ourself, ensuring we know the threading
411     //    mode. This will fail if someone else has already initialized SQLite
412     //    even if they initialized it safely. That's not ideal either, which is
413     //    why we expose bypass_sqlite_initialization    above.
414     if version_number() >= 3_007_000 {
415         const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
416         let is_singlethreaded = unsafe {
417             let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
418             let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
419             ffi::sqlite3_mutex_free(mutex_ptr);
420             is_singlethreaded
421         };
422         if is_singlethreaded {
423             Err(Error::SqliteSingleThreadedMode)
424         } else {
425             Ok(())
426         }
427     } else {
428         SQLITE_INIT.call_once(|| {
429             if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
430                 return;
431             }
432 
433             unsafe {
434                 assert!(ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) == ffi::SQLITE_OK && ffi::sqlite3_initialize() == ffi::SQLITE_OK,
435                         "Could not ensure safe initialization of SQLite.\n\
436                          To fix this, either:\n\
437                          * Upgrade SQLite to at least version 3.7.0\n\
438                          * Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call\n\
439                            rusqlite::bypass_sqlite_initialization() prior to your first connection attempt."
440                     );
441             }
442         });
443         Ok(())
444     }
445 }
446