min_sqlite3_sys/
statement.rs

1//! This module contains data-types and functions to provide
2//! prepared statement functionality.
3
4#![forbid(missing_docs)]
5
6use crate::{
7    bindings::{sqlite3_finalize, sqlite3_step, sqlite3_stmt},
8    ehandle::MinSqliteWrapperError,
9    operations::ColumnCapabilities,
10    prelude::*,
11};
12
13/// This enumeration is the list of the possible status outcomes for the
14/// `execute_prepared(&mut self)` function.
15#[non_exhaustive]
16#[repr(i8)]
17#[derive(Debug, PartialEq, Copy, Clone)]
18pub enum PreparedStatementStatus {
19    /// Indicates the actual error type id from SQLITE as an inner value.
20    Other(i32) = -1,
21    /// Indicates that another row of output is available.
22    FoundRow,
23    /// Indicates that an operation has completed.
24    Done,
25}
26
27/// Binded instance of the sqlite3_stmt.
28pub struct SqlStatement(*mut sqlite3_stmt);
29
30unsafe impl Send for SqlStatement {}
31unsafe impl Sync for SqlStatement {}
32
33impl Drop for SqlStatement {
34    fn drop(&mut self) {
35        self.kill();
36    }
37}
38
39/// Provides prepared statement functionality.
40impl<'a> SqlStatement {
41    /// Creates SqlStatement instance.
42    ///
43    /// # Usage
44    /// let stmt_p = ptr::null_mut();
45    /// SqlStatement::new(stmt_p);
46    /// ```
47    #[inline]
48    pub(crate) fn new(statement: *mut sqlite3_stmt) -> Self {
49        Self(statement)
50    }
51
52    /// Executes the prepared statement and returns PreparedStatementStatus for data and error
53    /// handling.
54    ///
55    /// # Usage
56    /// let db_path = Path::new("./example.db");
57    /// let db = Database::open(db_path).unwrap();
58    ///
59    /// let statement = String::from(
60    ///     "SELECT * FROM example_table WHERE ID = '15';"
61    /// );
62    ///
63    /// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
64    ///
65    /// while let PreparedStatementStatus::FoundRow = sql.execute_prepared() {
66    ///     ...
67    /// }
68    ///
69    /// sql.kill();
70    /// db.close();
71    /// ```
72    #[inline]
73    pub fn execute_prepared(&mut self) -> PreparedStatementStatus {
74        match unsafe { sqlite3_step(self.0) } {
75            100 => PreparedStatementStatus::FoundRow,
76            101 => PreparedStatementStatus::Done,
77            other_id => PreparedStatementStatus::Other(other_id),
78        }
79    }
80
81    /// Reads the column data of the rows that returns from the SQL query.
82    ///
83    /// # Panics
84    /// - If the data type is incorrectly specified.
85    /// - If the column index doesn't match.
86    ///
87    /// # Usage
88    /// ```
89    /// #[derive(Debug)]
90    /// struct Item {
91    ///     id: i64,
92    ///     name: String,
93    ///     tag: String,
94    /// }
95    ///
96    /// let db_path = Path::new("./example.db");
97    /// let db = Database::open(db_path).unwrap();
98    ///
99    /// let statement = String::from(
100    ///     "SELECT * FROM example_table WHERE ID = '15';"
101    /// );
102    ///
103    /// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
104    ///
105    /// while let PreparedStatementStatus::FoundRow = sql.execute_prepared() {
106    ///     println!(
107    ///         "id = {}, name = {}, tag = {}",
108    ///         sql.get_data::<i64>(0).unwrap(),
109    ///         sql.get_data::<String>(1).unwrap(),
110    ///         sql.get_data::<String>(2).unwrap(),
111    ///     );
112    ///
113    ///     // OR
114    ///
115    ///     println!(
116    ///         "{:?}",
117    ///         Item {
118    ///             id: sql.get_data(0).unwrap(),
119    ///             name: sql.get_data(1).unwrap(),
120    ///             tag: sql.get_data(2).unwrap(),
121    ///         }
122    ///     );
123    /// }
124    ///
125    /// sql.kill();
126    /// db.close();
127    /// ```
128    #[inline]
129    pub fn get_data<T: ColumnCapabilities<'a>>(
130        &'a self,
131        i: usize,
132    ) -> Result<T, MinSqliteWrapperError> {
133        ColumnCapabilities::get_data(self.0, i)
134    }
135
136    /// Binds the value of a parameter to a prepared statement indicator.
137    ///
138    /// Supported indicator patterns:
139    /// - ?
140    /// - ?NNN
141    /// - :VVV
142    /// - @VVV
143    /// - $VVV
144    ///
145    /// Returns `SqlitePrimaryResult:Ok` on success or an error code if anything goes wrong.
146    /// `SqlitePrimaryResult::Range` is returned if the parameter index is out of range.
147    ///
148    /// # IMPORTANT
149    /// The first argument isn't index of the column. It's simply index of the
150    /// indicator and always starts at 1. If the first argument is given zero,
151    /// the function will return `SqlitePrimaryResult::Range`.
152    ///
153    /// # Usage
154    /// ```
155    /// let db_path = Path::new("./example.db");
156    /// let db = Database::open(db_path).unwrap();
157    ///
158    /// let statement = String::from(
159    ///     "SELECT * FROM example_table WHERE ID = ;"
160    /// );
161    ///
162    /// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
163    ///
164    /// let status = sql.bind_val(1, 5);
165    /// // You can do some checks by
166    /// assert_eq!(status, SqlitePrimaryResult::Ok);
167    /// // or
168    /// if status == SqlitePrimaryResult::Range {
169    ///     panic!("Out of index on sql.bind_val!");
170    /// }
171    ///
172    /// sql.kill();
173    /// db.close();
174    /// ```
175    #[inline]
176    pub fn bind_val<T: ColumnCapabilities<'a>>(&'a self, i: usize, val: T) -> SqlitePrimaryResult {
177        if i == 0 {
178            return SqlitePrimaryResult::Range;
179        }
180
181        ColumnCapabilities::bind_val(val, self.0, i)
182    }
183
184    /// Called to destroy prepared statement. This function must be called for
185    /// each prepared statement. Otherwise some resource leaks might happen.
186    ///
187    /// # Usage
188    /// let db_path = Path::new("./example.db");
189    /// let db = Database::open(db_path).unwrap();
190    ///
191    /// let statement = String::from(
192    ///     "SELECT * FROM example_table WHERE ID = '15';"
193    /// );
194    ///
195    /// let mut sql = db.prepare(statement, None::<Box<dyn FnOnce(SqlitePrimaryResult, String)>>).unwrap();
196    ///
197    /// sql.kill();
198    /// db.close();
199    /// ```
200    #[inline]
201    pub fn kill(&self) -> SqlitePrimaryResult {
202        unsafe { SqlitePrimaryResult::from(sqlite3_finalize(self.0)) }
203    }
204}