sqlite_tiny/api/
sqlite.rs

1//! An SQLite database handle
2
3use super::ffiext;
4use crate::api::ffiext::PointerMut;
5use crate::api::query::Query;
6use crate::error::Error;
7use crate::{err, ffi};
8use std::ffi::CString;
9use std::ptr;
10
11/// An SQLite database handle
12#[derive(Debug)]
13pub struct Sqlite {
14    /// The database handle
15    pub(in crate::api) raw: PointerMut<ffi::sqlite3>,
16}
17impl Sqlite {
18    /// Opens or creates an SQLite 3 database for reading and writing
19    pub fn new(path: &str) -> Result<Self, Error> {
20        Self::raw(path, ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE)
21    }
22    /// Opens an SQLite database from an URL (see <https://www.sqlite.org/uri.html>)
23    pub fn uri(uri: &str) -> Result<Self, Error> {
24        Self::raw(uri, ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_URI)
25    }
26    /// Opens an SQLite database at the given location with the given flags
27    ///
28    /// # Important
29    /// To ensure the safety guarantees of the Rust API are not violated, we always add `SQLITE_OPEN_FULLMUTEX` to the
30    /// provided flags.
31    pub fn raw(location: &str, flags: std::ffi::c_int) -> Result<Self, Error> {
32        // Prepare path and database pointer
33        let path = CString::new(location).map_err(|e| err!(with: e, "Invalid database location"))?;
34        let mut database = ptr::null_mut();
35
36        // Open the database
37        let flags = flags | ffi::SQLITE_OPEN_FULLMUTEX;
38        let retval = unsafe { ffi::sqlite3_open_v2(path.as_ptr(), &mut database, flags, ptr::null()) };
39        unsafe { ffiext::sqlite3_check_result(retval, ptr::null_mut()) }?;
40
41        // Init self
42        let database = PointerMut::new(database, ffi::sqlite3_close_v2);
43        Ok(Self { raw: database })
44    }
45
46    /// Creates a new query from a **single** SQL statement
47    pub fn query(&self, query: &str) -> Result<Query, Error> {
48        // Prepare query and statement pointer
49        let query = CString::new(query).map_err(|e| err!(with: e, "Invalid database query"))?;
50        let mut statement = ptr::null_mut();
51
52        // Prepare statement and check result code
53        let retval =
54            unsafe { ffi::sqlite3_prepare_v2(self.raw.as_ptr(), query.as_ptr(), -1, &mut statement, ptr::null_mut()) };
55        unsafe { ffiext::sqlite3_check_result(retval, self.raw.as_ptr()) }?;
56
57        // Init query
58        let statement = PointerMut::new(statement, ffi::sqlite3_finalize);
59        Ok(Query { sqlite: self, raw: statement })
60    }
61
62    /// Executes one or more SQL queries
63    pub fn execute(&self, query: &str) -> Result<(), Error> {
64        // Prepare query and statement pointer
65        let query = CString::new(query).map_err(|e| err!(with: e, "Invalid database query"))?;
66        let retval =
67            unsafe { ffi::sqlite3_exec(self.raw.as_ptr(), query.as_ptr(), None, ptr::null_mut(), ptr::null_mut()) };
68        unsafe { ffiext::sqlite3_check_result(retval, self.raw.as_ptr()) }?;
69
70        // Apparently, the query was successful
71        Ok(())
72    }
73}
74unsafe impl Send for Sqlite {
75    // This struct is safely send because:
76    //  - the underlying database is sync because SQLite guarantees so if the database is opened with
77    //    `SQLITE_OPEN_FULLMUTEX`
78    //  - the pointer itself is never mutated, and dropping is safe because this struct is no-`Copy`/no-`Clone`, so it
79    //    can only be dropped once and access in the destructor is exclusive
80}
81unsafe impl Sync for Sqlite {
82    // This struct is safely sync because:
83    //  - the underlying database is sync because SQLite guarantees so if the database is opened with
84    //    `SQLITE_OPEN_FULLMUTEX`
85    //  - the pointer itself is never mutated, and dropping is safe because this struct is no-`Copy`/no-`Clone`, so it
86    //    can only be dropped once and access in the destructor is exclusive
87}