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}