Skip to main content

sqlite3_ext/query/
mod.rs

1//! Facilities for running SQL queries.
2//!
3//! The main entry points into this module are [Connection::prepare], [Connection::execute],
4//! and [Connection::query_row].
5use super::{ffi, iterator::*, sqlite3_match_version, types::*, value::*, Connection};
6pub use params::*;
7use std::{
8    convert::{AsMut, AsRef},
9    ffi::{CStr, CString},
10    mem::MaybeUninit,
11    num::NonZeroI32,
12    ops::{Index, IndexMut},
13    slice, str,
14};
15
16mod params;
17mod test;
18
19#[derive(Debug, Copy, Clone, Eq, PartialEq)]
20enum QueryState {
21    Ready,
22    Active,
23    Finished,
24}
25
26/// A prepared statement.
27///
28/// The basic method for accessing data using sqlite3_ext is:
29///
30/// 1. Create a Statement using [Connection::prepare].
31/// 2. Bind parameters (if necessary) using [Statement::query].
32/// 3. Retrieve results using [Statement::map] or [Statement::next].
33///
34/// Statement objects can be reused for multiple executions. A call to [query](Self::query) resets
35/// the bound parameters and restarts the query. This also applies to methods that call query
36/// internally, like [execute](Self::execute) and [query_row](Self::query_row).
37///
38/// Results can be accessed in an imperative or functional style. The imperative style looks like
39/// this:
40///
41/// ```no_run
42/// use sqlite3_ext::*;
43///
44/// fn pages_imperative(conn: &Connection, user_id: i64) -> Result<Vec<(i64, String)>> {
45///     let mut stmt = conn.prepare("SELECT id, name FROM pages WHERE owner_id = ?")?;
46///     stmt.query([user_id])?;
47///     let mut results = Vec::new();
48///     while let Some(row) = stmt.next()? {
49///         results.push((
50///             row[0].get_i64(),
51///             row[1].get_str()?.to_owned(),
52///         ));
53///     }
54///     Ok(results)
55/// }
56/// ```
57///
58/// The functional style makes use of [FallibleIterator] methods.
59///
60/// ```no_run
61/// use sqlite3_ext::*;
62///
63/// fn pages_functional(conn: &Connection, user_id: i64) -> Result<Vec<(i64, String)>> {
64///     let results: Vec<(i64, String)> = conn
65///         .prepare("SELECT id, name FROM pages WHERE owner_id = ?")?
66///         .query([user_id])?
67///         .map(|row| {
68///             Ok((
69///                 row[0].get_i64(),
70///                 row[1].get_str()?.to_owned(),
71///             ))
72///         })
73///         .collect()?;
74///     Ok(results)
75/// }
76/// ```
77pub struct Statement {
78    base: *mut ffi::sqlite3_stmt,
79    state: QueryState,
80    // We allocate column objects for all columns so that they can be returned by our Index
81    // implementation. It's possible to skip this if we add a lifetime parameter to Column to
82    // prevent pointer aliasing, but then we can't use Index and IndexMut.
83    columns: Box<[Column]>,
84}
85
86impl Connection {
87    /// Prepare some SQL for execution. This method will return the prepared statement and
88    /// a slice containing the portion of the original input which was after the first SQL
89    /// statement.
90    pub fn prepare_first<'a>(&self, sql: &'a str) -> Result<(Option<Statement>, &'a str)> {
91        const FLAGS: u32 = 0;
92        let guard = self.lock();
93        let mut ret = MaybeUninit::uninit();
94        let mut rest = MaybeUninit::uninit();
95        Error::from_sqlite_desc(
96            unsafe {
97                sqlite3_match_version! {
98                    3_020_000 => ffi::sqlite3_prepare_v3(
99                        self.as_mut_ptr(),
100                        sql.as_ptr() as _,
101                        sql.len() as _,
102                        FLAGS,
103                        ret.as_mut_ptr(),
104                        rest.as_mut_ptr(),
105                    ),
106                    _ => ffi::sqlite3_prepare_v2(
107                        self.as_mut_ptr(),
108                        sql.as_ptr() as _,
109                        sql.len() as _,
110                        ret.as_mut_ptr(),
111                        rest.as_mut_ptr(),
112                    ),
113                }
114            },
115            guard,
116        )?;
117
118        let stmt = unsafe { ret.assume_init() };
119        let stmt = if stmt.is_null() {
120            None
121        } else {
122            let len = unsafe { ffi::sqlite3_column_count(stmt) as usize };
123            let columns = (0..len).map(|i| Column::new(stmt, i)).collect();
124            Some(Statement {
125                base: stmt,
126                state: QueryState::Ready,
127                columns,
128            })
129        };
130
131        let rest = unsafe { rest.assume_init() };
132        let offset = rest as usize - sql.as_ptr() as usize;
133        let rest = unsafe { sql.get_unchecked(offset..) };
134        Ok((stmt, rest))
135    }
136
137    /// Prepare some SQL for execution. This method will return Err([SQLITE_MISUSE]) if the
138    /// input string does not contain any SQL statements.
139    pub fn prepare(&self, sql: &str) -> Result<Statement> {
140        self.prepare_first(sql)?.0.ok_or(SQLITE_MISUSE)
141    }
142
143    /// Convenience method to prepare a query and bind it with values. See
144    /// [Statement::query].
145    pub fn query<P>(&self, sql: &str, params: P) -> Result<Statement>
146    where
147        P: Params,
148    {
149        let mut stmt = self.prepare(sql)?;
150        stmt.query(params)?;
151        Ok(stmt)
152    }
153
154    /// Convenience method for `self.prepare(sql)?.query_row(params, f)`. See
155    /// [Statement::query_row].
156    pub fn query_row<P, R, F>(&self, sql: &str, params: P, f: F) -> Result<R>
157    where
158        P: Params,
159        F: FnOnce(&mut QueryResult) -> Result<R>,
160    {
161        self.prepare(sql)?.query_row(params, f)
162    }
163
164    /// Convenience method for `self.prepare(sql)?.execute(params)`. See [Statement::execute].
165    pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<i64> {
166        self.prepare(sql)?.execute(params)
167    }
168
169    /// Convenience method for `self.prepare(sql)?.insert(params)`. See [Statement::insert].
170    pub fn insert<P: Params>(&self, sql: &str, params: P) -> Result<i64> {
171        self.prepare(sql)?.insert(params)
172    }
173}
174
175impl Statement {
176    /// Return the underlying sqlite3_stmt pointer.
177    ///
178    /// # Safety
179    ///
180    /// This method is unsafe because applying SQLite methods to the sqlite3_stmt pointer returned
181    /// by this method may violate invariants of other methods on this statement.
182    pub unsafe fn as_ptr(&self) -> *mut ffi::sqlite3_stmt {
183        self.base
184    }
185
186    /// Bind the provided parameters to the query. If the query was previously used, it is reset
187    /// and existing parameters are cleared.
188    ///
189    /// This method is not necessary to call on the first execution of a query where there are no
190    /// parameters to bind (e.g. on a single-use hard-coded query).
191    pub fn query<P: Params>(&mut self, params: P) -> Result<&mut Self> {
192        if self.state != QueryState::Ready {
193            self.reset()?;
194        }
195        params.bind_params(self)?;
196        Ok(self)
197    }
198
199    /// Execute a query which is expected to return only a single row.
200    ///
201    /// This method will fail with [SQLITE_EMPTY] if the query does not return any rows. If
202    /// the query has multiple rows, only the first will be returned.
203    ///
204    /// If you are not storing this Statement for later reuse, [Connection::query_row] is a
205    /// shortcut for this method.
206    pub fn query_row<P, R, F>(&mut self, params: P, f: F) -> Result<R>
207    where
208        P: Params,
209        F: FnOnce(&mut QueryResult) -> Result<R>,
210    {
211        let res = self.query(params)?.next().map(|o| o.map(|row| f(row)));
212        // Always reset the query after using, although we prioritize a query failure
213        // in the return value.
214        let reset_res = self.reset();
215        match res {
216            Ok(None) => Err(SQLITE_EMPTY),
217            Ok(Some(r)) => {
218                reset_res?;
219                r
220            }
221            Err(e) => Err(e),
222        }
223    }
224
225    /// Execute a query that is expected to return no results (such as an INSERT, UPDATE, or
226    /// DELETE).
227    ///
228    /// If this query returns rows, this method will fail with [SQLITE_MISUSE] (use
229    /// [query](Self::query) for a query which returns rows).
230    ///
231    /// If you are not storing this Statement for later reuse, [Connection::execute] is a shortcut
232    /// for this method.
233    pub fn execute<P: Params>(&mut self, params: P) -> Result<i64> {
234        let db = unsafe { self.db() }.lock();
235
236        let res = self.query(params)?.next().map(|r| r.is_some());
237        // Always reset the query after using, although we prioritize a query failure
238        // in the return value.
239        let reset_res = self.reset();
240        match res {
241            Ok(false) => {
242                reset_res?;
243                Ok(unsafe {
244                    sqlite3_match_version! {
245                        3_037_000 => ffi::sqlite3_changes64(db.as_mut_ptr()),
246                        _ => ffi::sqlite3_changes(db.as_mut_ptr()) as _,
247                    }
248                })
249            }
250            Ok(true) => Err(SQLITE_MISUSE), // Query returned rows!
251            Err(e) => Err(e),
252        }
253    }
254
255    /// Execute a query that is expected to be an INSERT, then return the inserted rowid.
256    ///
257    /// This method will fail with [SQLITE_MISUSE] if this method returns rows, but there are no
258    /// other verifications that the executed statement is actually an INSERT. If this Statement is
259    /// not an INSERT, the return value of this function is meaningless.
260    pub fn insert<P: Params>(&mut self, params: P) -> Result<i64> {
261        let db = unsafe { self.db() }.lock();
262        let res = self.query(params)?.next().map(|r| r.is_some());
263        // Always reset the query after using, although we prioritize a query failure
264        // in the return value.
265        let reset_res = self.reset();
266        match res {
267            Ok(false) => {
268                reset_res?;
269                Ok(unsafe { ffi::sqlite3_last_insert_rowid(db.as_mut_ptr()) })
270            }
271            Ok(true) => Err(SQLITE_MISUSE), // Query returned rows!
272            Err(e) => Err(e),
273        }
274    }
275
276    /// Returns the original text of the prepared statement.
277    pub fn sql(&self) -> Result<&str> {
278        unsafe {
279            let ret = ffi::sqlite3_sql(self.base);
280            Ok(CStr::from_ptr(ret).to_str()?)
281        }
282    }
283
284    /// Returns the number of parameters which should be bound to the query. Valid
285    /// parameter positions are `1..=self.parameter_count()`.
286    pub fn parameter_count(&self) -> i32 {
287        unsafe { ffi::sqlite3_bind_parameter_count(self.base) }
288    }
289
290    /// Returns the name of the parameter at the given position. Note that the first
291    /// parameter has a position of 1, not 0.
292    pub fn parameter_name(&self, position: i32) -> Option<&str> {
293        unsafe {
294            let ptr = ffi::sqlite3_bind_parameter_name(self.base, position);
295            match ptr.is_null() {
296                true => None,
297                // Safety - in safe code this value must have originally come
298                // from a &str, so it's valid UTF-8.
299                false => Some(str::from_utf8_unchecked(CStr::from_ptr(ptr).to_bytes())),
300            }
301        }
302    }
303
304    /// Return the position of the parameter with the provided name.
305    pub fn parameter_position(&self, name: impl Into<Vec<u8>>) -> Option<NonZeroI32> {
306        CString::new(name).ok().and_then(|name| {
307            NonZeroI32::new(unsafe { ffi::sqlite3_bind_parameter_index(self.base, name.as_ptr()) })
308        })
309    }
310
311    /// Returns the number of columns in the result set returned by this query.
312    pub fn column_count(&self) -> usize {
313        unsafe { ffi::sqlite3_column_count(self.base) as _ }
314    }
315
316    /// Returns the current result, without advancing the cursor. This method returns `None` if the
317    /// query has already run to completion, or if the query has not been started using
318    /// [query](Self::query).
319    pub fn current_result(&self) -> Option<&QueryResult> {
320        match self.state {
321            QueryState::Active => Some(QueryResult::from_statement(self)),
322            _ => None,
323        }
324    }
325
326    /// Mutable version of [current_result](Self::current_result).
327    pub fn current_result_mut(&mut self) -> Option<&mut QueryResult> {
328        match self.state {
329            QueryState::Active => Some(QueryResult::from_statement_mut(self)),
330            _ => None,
331        }
332    }
333
334    /// Returns a handle to the Connection associated with this statement.
335    ///
336    /// # Safety
337    ///
338    /// The returned reference's lifetime is not tied to the lifetime of this Statement. It
339    /// is the responsibility of the caller to ensure that the Connection reference is not
340    /// improperly used.
341    pub unsafe fn db<'a>(&self) -> &'a Connection {
342        Connection::from_ptr(ffi::sqlite3_db_handle(self.base))
343    }
344
345    fn reset(&mut self) -> Result<()> {
346        unsafe {
347            ffi::sqlite3_reset(self.base);
348            Error::from_sqlite(ffi::sqlite3_clear_bindings(self.base))?;
349        }
350        self.state = QueryState::Ready;
351        Ok(())
352    }
353}
354
355impl FallibleIteratorMut for Statement {
356    type Item = QueryResult;
357    type Error = Error;
358
359    fn next(&mut self) -> Result<Option<&mut Self::Item>> {
360        match self.state {
361            QueryState::Ready | QueryState::Active => unsafe {
362                let guard = self.db().lock();
363                let rc = ffi::sqlite3_step(self.base);
364                Error::from_sqlite_desc(rc, guard)?;
365                match rc {
366                    ffi::SQLITE_DONE => {
367                        self.state = QueryState::Finished;
368                        Ok(None)
369                    }
370                    ffi::SQLITE_ROW => {
371                        self.state = QueryState::Active;
372                        Ok(Some(QueryResult::from_statement_mut(self)))
373                    }
374                    _ => unreachable!(),
375                }
376            },
377            QueryState::Finished => Ok(None),
378        }
379    }
380}
381
382impl std::fmt::Debug for Statement {
383    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
384        f.debug_struct("Statement")
385            .field("state", &self.state)
386            .finish_non_exhaustive()
387    }
388}
389
390impl Drop for Statement {
391    fn drop(&mut self) {
392        unsafe { ffi::sqlite3_finalize(self.base) };
393    }
394}
395
396/// A row returned from a query.
397#[repr(transparent)]
398pub struct QueryResult {
399    stmt: Statement,
400}
401
402impl QueryResult {
403    fn from_statement(stmt: &Statement) -> &Self {
404        unsafe { &*(stmt as *const Statement as *const Self) }
405    }
406
407    fn from_statement_mut(stmt: &mut Statement) -> &mut Self {
408        unsafe { &mut *(stmt as *mut Statement as *mut Self) }
409    }
410
411    /// Returns the number of columns in the result.
412    pub fn len(&self) -> usize {
413        self.stmt.column_count()
414    }
415}
416
417impl Index<usize> for QueryResult {
418    type Output = Column;
419
420    fn index(&self, index: usize) -> &Self::Output {
421        &self.stmt.columns[index]
422    }
423}
424
425impl IndexMut<usize> for QueryResult {
426    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
427        &mut self.stmt.columns[index]
428    }
429}
430
431impl std::fmt::Debug for QueryResult {
432    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433        let mut dt = f.debug_tuple("QueryResult");
434        for i in 0..self.len() {
435            dt.field(&self[i]);
436        }
437        dt.finish()
438    }
439}
440
441/// A single value returned from a query.
442///
443/// SQLite automatically converts between data types on request, which is why many of the
444/// methods require `&mut`.
445pub struct Column {
446    stmt: *mut ffi::sqlite3_stmt,
447    position: usize,
448}
449
450impl Column {
451    fn new(stmt: *mut ffi::sqlite3_stmt, position: usize) -> Self {
452        Self { stmt, position }
453    }
454
455    /// Returns the value of the AS clause for this column, if one was specified. If no AS
456    /// clause was specified, the name of the column is unspecified and may change from one
457    /// release of SQLite to the next.
458    pub fn name(&self) -> Result<&str> {
459        unsafe {
460            let ret = ffi::sqlite3_column_name(self.stmt, self.position as _);
461            if ret.is_null() {
462                Err(SQLITE_NOMEM)
463            } else {
464                Ok(CStr::from_ptr(ret).to_str()?)
465            }
466        }
467    }
468
469    /// Returns the original, unaliased name of the database that is the origin of this
470    /// column.
471    pub fn database_name(&self) -> Result<Option<&str>> {
472        unsafe {
473            let ret = ffi::sqlite3_column_database_name(self.stmt, self.position as _);
474            if ret.is_null() {
475                Ok(None)
476            } else {
477                Ok(Some(CStr::from_ptr(ret).to_str()?))
478            }
479        }
480    }
481
482    /// Returns the original, unaliased name of the table that is the origin of this
483    /// column.
484    pub fn table_name(&self) -> Result<Option<&str>> {
485        unsafe {
486            let ret = ffi::sqlite3_column_table_name(self.stmt, self.position as _);
487            if ret.is_null() {
488                Ok(None)
489            } else {
490                Ok(Some(CStr::from_ptr(ret).to_str()?))
491            }
492        }
493    }
494
495    /// Returns the original, unaliased name of the column that is the origin of this
496    /// column.
497    pub fn origin_name(&self) -> Result<Option<&str>> {
498        unsafe {
499            let ret = ffi::sqlite3_column_origin_name(self.stmt, self.position as _);
500            if ret.is_null() {
501                Ok(None)
502            } else {
503                Ok(Some(CStr::from_ptr(ret).to_str()?))
504            }
505        }
506    }
507
508    /// Returns the declared type of the column that is the origin of this column. Note
509    /// that this does not mean that values contained in this column comply with the
510    /// declared type.
511    pub fn decltype(&self) -> Result<Option<&str>> {
512        unsafe {
513            let ret = ffi::sqlite3_column_decltype(self.stmt, self.position as _);
514            if ret.is_null() {
515                Ok(None)
516            } else {
517                Ok(Some(CStr::from_ptr(ret).to_str()?))
518            }
519        }
520    }
521}
522
523impl AsRef<ValueRef> for Column {
524    fn as_ref(&self) -> &ValueRef {
525        unsafe { ValueRef::from_ptr(ffi::sqlite3_column_value(self.stmt, self.position as _)) }
526    }
527}
528
529impl AsMut<ValueRef> for Column {
530    fn as_mut(&mut self) -> &mut ValueRef {
531        unsafe { ValueRef::from_ptr(ffi::sqlite3_column_value(self.stmt, self.position as _)) }
532    }
533}
534
535impl FromValue for Column {
536    fn value_type(&self) -> ValueType {
537        unsafe { ValueType::from_sqlite(ffi::sqlite3_column_type(self.stmt, self.position as _)) }
538    }
539
540    fn get_i32(&self) -> i32 {
541        unsafe { ffi::sqlite3_column_int(self.stmt, self.position as _) }
542    }
543
544    fn get_i64(&self) -> i64 {
545        unsafe { ffi::sqlite3_column_int64(self.stmt, self.position as _) }
546    }
547
548    fn get_f64(&self) -> f64 {
549        unsafe { ffi::sqlite3_column_double(self.stmt, self.position as _) }
550    }
551
552    unsafe fn get_blob_unchecked(&self) -> &[u8] {
553        let len = ffi::sqlite3_column_bytes(self.stmt, self.position as _);
554        if len == 0 {
555            return &[];
556        }
557        let data = ffi::sqlite3_column_blob(self.stmt, self.position as _);
558        slice::from_raw_parts(data as _, len as _)
559    }
560
561    fn get_blob(&mut self) -> Result<&[u8]> {
562        unsafe {
563            let len = ffi::sqlite3_column_bytes(self.stmt, self.position as _);
564            if len == 0 {
565                return Ok(&[]);
566            }
567            let data = ffi::sqlite3_column_blob(self.stmt, self.position as _);
568            if data.is_null() {
569                return Err(SQLITE_NOMEM);
570            } else {
571                Ok(slice::from_raw_parts(data as _, len as _))
572            }
573        }
574    }
575}
576
577impl std::fmt::Debug for Column {
578    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
579        match self.value_type() {
580            ValueType::Integer => f.debug_tuple("Integer").field(&self.get_i64()).finish(),
581            ValueType::Float => f.debug_tuple("Float").field(&self.get_f64()).finish(),
582            ValueType::Text => f
583                .debug_tuple("Text")
584                .field(unsafe { &self.get_str_unchecked() })
585                .finish(),
586            ValueType::Blob => f
587                .debug_tuple("Blob")
588                .field(unsafe { &self.get_blob_unchecked() })
589                .finish(),
590            ValueType::Null => f.debug_tuple("Null").finish(),
591        }
592    }
593}