1use std::ffi::CString;
2use std::ptr;
3use std::ptr::NonNull;
45use crate::error::Error;
6use libsqlite3_sys::{
7 sqlite3, sqlite3_close, sqlite3_exec, sqlite3_last_insert_rowid, SQLITE_LOCKED_SHAREDCACHE,
8 SQLITE_OK,
9};
1011use crate::{statement::unlock_notify, SqliteError};
1213/// Managed handle to the raw SQLite3 database handle.
14/// The database handle will be closed when this is dropped and no `ConnectionHandleRef`s exist.
15#[derive(Debug)]
16pub(crate) struct ConnectionHandle(NonNull<sqlite3>);
1718// A SQLite3 handle is safe to send between threads, provided not more than
19// one is accessing it at the same time. This is upheld as long as [SQLITE_CONFIG_MULTITHREAD] is
20// enabled and [SQLITE_THREADSAFE] was enabled when sqlite was compiled. We refuse to work
21// if these conditions are not upheld.
2223// <https://www.sqlite.org/c3ref/threadsafe.html>
2425// <https://www.sqlite.org/c3ref/c_config_covering_index_scan.html#sqliteconfigmultithread>
2627unsafe impl Send for ConnectionHandle {}
2829impl ConnectionHandle {
30#[inline]
31pub(super) unsafe fn new(ptr: *mut sqlite3) -> Self {
32Self(NonNull::new_unchecked(ptr))
33 }
3435#[inline]
36pub(crate) fn as_ptr(&self) -> *mut sqlite3 {
37self.0.as_ptr()
38 }
3940pub(crate) fn as_non_null_ptr(&self) -> NonNull<sqlite3> {
41self.0
42}
4344pub(crate) fn last_insert_rowid(&mut self) -> i64 {
45// SAFETY: we have exclusive access to the database handle
46unsafe { sqlite3_last_insert_rowid(self.as_ptr()) }
47 }
4849pub(crate) fn last_error(&mut self) -> Option<SqliteError> {
50// SAFETY: we have exclusive access to the database handle
51unsafe { SqliteError::try_new(self.as_ptr()) }
52 }
5354#[track_caller]
55pub(crate) fn expect_error(&mut self) -> SqliteError {
56self.last_error()
57 .expect("expected error code to be set in current context")
58 }
5960pub(crate) fn exec(&mut self, query: impl Into<String>) -> Result<(), Error> {
61let query = query.into();
62let query = CString::new(query).map_err(|_| err_protocol!("query contains nul bytes"))?;
6364// SAFETY: we have exclusive access to the database handle
65unsafe {
66loop {
67let status = sqlite3_exec(
68self.as_ptr(),
69 query.as_ptr(),
70// callback if we wanted result rows
71None,
72// callback data
73ptr::null_mut(),
74// out-pointer for the error message, we just use `SqliteError::new()`
75ptr::null_mut(),
76 );
7778match status {
79 SQLITE_OK => return Ok(()),
80 SQLITE_LOCKED_SHAREDCACHE => unlock_notify::wait(self.as_ptr())?,
81_ => return Err(SqliteError::new(self.as_ptr()).into()),
82 }
83 }
84 }
85 }
86}
8788impl Drop for ConnectionHandle {
89fn drop(&mut self) {
90unsafe {
91// https://sqlite.org/c3ref/close.html
92let status = sqlite3_close(self.0.as_ptr());
93if status != SQLITE_OK {
94// this should *only* happen due to an internal bug in SQLite where we left
95 // SQLite handles open
96panic!("{}", SqliteError::new(self.0.as_ptr()));
97 }
98 }
99 }
100}