sqlite_tiny/api/
sqlite.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! An SQLite database handle

use crate::{
    api::{query::Query, types::PointerMut},
    err,
    error::Error,
    ffi,
};
use std::{ffi::CString, ptr};

/// An SQLite database handle
#[derive(Debug)]
pub struct Sqlite {
    /// The database handle
    pub(in crate::api) raw: PointerMut<ffi::sqlite3>,
}
impl Sqlite {
    /// Opens or creates an SQLite 3 database for reading and writing
    pub fn new(path: &str) -> Result<Self, Error> {
        Self::raw(path, ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE)
    }
    /// Opens an SQLite database from an URL (see <https://www.sqlite.org/uri.html>)
    pub fn uri(uri: &str) -> Result<Self, Error> {
        Self::raw(uri, ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_URI)
    }
    /// Opens an SQLite database at the given location with the given flags
    ///
    /// # Important
    /// To ensure the safety guarantees of the Rust API are not violated, we always add `SQLITE_OPEN_FULLMUTEX` to the
    /// provided flags.
    pub fn raw(location: &str, flags: std::ffi::c_int) -> Result<Self, Error> {
        // Prepare path and database pointer
        let path = CString::new(location).map_err(|e| err!(with: e, "Invalid database location"))?;
        let mut database = ptr::null_mut();

        // Open the database
        let flags = flags | ffi::SQLITE_OPEN_FULLMUTEX;
        let retval = unsafe { ffi::sqlite3_open_v2(path.as_ptr(), &mut database, flags, ptr::null()) };
        unsafe { ffi::sqlite3_check_result(retval, ptr::null_mut()) }?;

        // Init self
        let database = PointerMut::new(database, ffi::sqlite3_close_v2);
        Ok(Self { raw: database })
    }

    /// Creates a new query from a **single** SQL statement
    pub fn query(&self, query: &str) -> Result<Query, Error> {
        // Prepare query and statement pointer
        let query = CString::new(query).map_err(|e| err!(with: e, "Invalid database query"))?;
        let mut statement = ptr::null_mut();

        // Prepare statement and check result code
        let retval =
            unsafe { ffi::sqlite3_prepare_v2(self.raw.as_ptr(), query.as_ptr(), -1, &mut statement, ptr::null_mut()) };
        unsafe { ffi::sqlite3_check_result(retval, self.raw.as_ptr()) }?;

        // Init query
        let statement = PointerMut::new(statement, ffi::sqlite3_finalize);
        Ok(Query { sqlite: self, raw: statement })
    }

    /// Executes one or more SQL queries
    pub fn execute(&self, query: &str) -> Result<(), Error> {
        // Prepare query and statement pointer
        let query = CString::new(query).map_err(|e| err!(with: e, "Invalid database query"))?;
        let retval =
            unsafe { ffi::sqlite3_exec(self.raw.as_ptr(), query.as_ptr(), None, ptr::null_mut(), ptr::null_mut()) };
        unsafe { ffi::sqlite3_check_result(retval, self.raw.as_ptr()) }?;

        // Apparently, the query was successful
        Ok(())
    }
}
unsafe impl Send for Sqlite {
    // This struct is safely send because:
    //  - the underlying database is sync because SQLite guarantees so if the database is opened with
    //    `SQLITE_OPEN_FULLMUTEX`
    //  - the pointer itself is never mutated, and dropping is safe because this struct is no-`Copy`/no-`Clone`, so it
    //    can only be dropped once and access in the destructor is exclusive
}
unsafe impl Sync for Sqlite {
    // This struct is safely sync because:
    //  - the underlying database is sync because SQLite guarantees so if the database is opened with
    //    `SQLITE_OPEN_FULLMUTEX`
    //  - the pointer itself is never mutated, and dropping is safe because this struct is no-`Copy`/no-`Clone`, so it
    //    can only be dropped once and access in the destructor is exclusive
}