sqlite_tiny/api/
query.rs

1//! An SQLite query
2
3use crate::api::answer::Answer;
4use crate::api::ffiext::{self, PointerMut};
5use crate::api::types::SqliteType;
6use crate::error::Error;
7use crate::{err, ffi, Sqlite};
8
9/// An SQLite query
10#[derive(Debug)]
11pub struct Query<'db> {
12    /// The database
13    pub(in crate::api) sqlite: &'db Sqlite,
14    /// The statement
15    pub(in crate::api) raw: PointerMut<ffi::sqlite3_stmt>,
16}
17impl<'db> Query<'db> {
18    /// Binds a value
19    ///
20    /// # Important
21    /// Sadly, unless manually specified with `?NNN`, default column indices for binding start with `1` 😭
22    pub fn bind<T>(self, column: std::ffi::c_int, value: T) -> Result<Self, Error>
23    where
24        SqliteType: TryFrom<T>,
25        <SqliteType as TryFrom<T>>::Error: std::error::Error + Send + 'static,
26    {
27        // Create intermediate value and bind it
28        let value =
29            SqliteType::try_from(value).map_err(|e| err!(with: e, "Failed to convert value into SQLite type"))?;
30        match value {
31            SqliteType::Null => self.bind_null(column)?,
32            SqliteType::Integer(value) => self.bind_integer(column, value)?,
33            SqliteType::Real(value) => self.bind_real(column, value)?,
34            SqliteType::Text(value) => self.bind_text(column, value)?,
35            SqliteType::Blob(value) => self.bind_blob(column, value)?,
36        }
37        Ok(self)
38    }
39    /// Binds a NULL value
40    fn bind_null(&self, column: std::ffi::c_int) -> Result<(), Error> {
41        let retval = unsafe { ffi::sqlite3_bind_null(self.raw.as_ptr(), column) };
42        unsafe { ffiext::sqlite3_check_result(retval, self.sqlite.raw.as_ptr()) }
43    }
44    /// Binds an INTEGER value
45    fn bind_integer(&self, column: std::ffi::c_int, value: i64) -> Result<(), Error> {
46        let retval = unsafe { ffi::sqlite3_bind_int64(self.raw.as_ptr(), column, value) };
47        unsafe { ffiext::sqlite3_check_result(retval, self.sqlite.raw.as_ptr()) }
48    }
49    /// Binds a REAL value
50    fn bind_real(&self, column: std::ffi::c_int, value: f64) -> Result<(), Error> {
51        let retval = unsafe { ffi::sqlite3_bind_double(self.raw.as_ptr(), column, value) };
52        unsafe { ffiext::sqlite3_check_result(retval, self.sqlite.raw.as_ptr()) }
53    }
54    /// Binds a TEXT value
55    fn bind_text(&self, column: std::ffi::c_int, value: String) -> Result<(), Error> {
56        let retval = unsafe {
57            // Bind the text value and instruct SQLite to immediately copy the value
58            ffi::sqlite3_bind_text64(
59                self.raw.as_ptr(),
60                column,
61                value.as_ptr() as _,
62                value.len() as _,
63                ffi::sqlite3_transient(),
64                ffi::SQLITE_UTF8 as _,
65            )
66        };
67        unsafe { ffiext::sqlite3_check_result(retval, self.sqlite.raw.as_ptr()) }
68    }
69    /// Binds a BLOB value
70    fn bind_blob(&self, column: std::ffi::c_int, value: Vec<u8>) -> Result<(), Error> {
71        let retval = unsafe {
72            // Bind the blob value and instruct SQLite to immediately copy the value
73            ffi::sqlite3_bind_blob64(
74                self.raw.as_ptr(),
75                column,
76                value.as_ptr() as _,
77                value.len() as _,
78                ffi::sqlite3_transient(),
79            )
80        };
81        unsafe { ffiext::sqlite3_check_result(retval, self.sqlite.raw.as_ptr()) }
82    }
83
84    /// Executes the query and gets the next result row if any
85    pub fn execute(self) -> Result<Answer<'db>, Error> {
86        // Create the result object and do a step to make sure the query is actually executed
87        let mut result = Answer { sqlite: self.sqlite, raw: self.raw, has_row: false };
88        result.step()?;
89
90        // Initialize the result struct
91        Ok(result)
92    }
93}