marine_sqlite_connector/
statement.rs

1use crate::sqlite3_connector as ffi;
2use crate::{Cursor, Result, Type, Value};
3
4use std::marker::PhantomData;
5
6/// A prepared statement.
7pub struct Statement {
8    raw: (ffi::Sqlite3StmtHandle, ffi::Sqlite3DbHandle),
9    phantom: PhantomData<(ffi::Sqlite3StmtHandle, ffi::Sqlite3DbHandle)>,
10}
11
12/// A state of a prepared statement.
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum State {
15    /// There is a row available for reading.
16    Row,
17    /// The statement has been entirely evaluated.
18    Done,
19}
20
21/// A type suitable for binding to a prepared statement.
22pub trait Bindable {
23    /// Bind to a parameter.
24    ///
25    /// The leftmost parameter has the index 1.
26    fn bind(self, _: &mut Statement, _: usize) -> Result<()>;
27}
28
29/// A type suitable for reading from a prepared statement.
30pub trait Readable: Sized {
31    /// Read from a column.
32    ///
33    /// The leftmost column has the index 0.
34    fn read(_: &Statement, _: usize) -> Result<Self>;
35}
36
37impl Statement {
38    /// Bind a value to a parameter.
39    ///
40    /// The leftmost parameter has the index 1.
41    #[inline]
42    pub fn bind<T: Bindable>(&mut self, i: usize, value: T) -> Result<()> {
43        value.bind(self, i)
44    }
45
46    /// Return the number of columns.
47    #[inline]
48    pub fn count(&self) -> usize {
49        unsafe { ffi::sqlite3_column_count(self.raw.0) as usize }
50    }
51
52    /// Return the type of a column.
53    ///
54    /// The type becomes available after taking a step.
55    pub fn kind(&self, i: usize) -> Type {
56        debug_assert!(i < self.count(), "the index is out of range");
57        match unsafe { ffi::sqlite3_column_type(self.raw.0, i as _) } {
58            ffi::SQLITE_BLOB => Type::Binary,
59            ffi::SQLITE_FLOAT => Type::Float,
60            ffi::SQLITE_INTEGER => Type::Integer,
61            ffi::SQLITE_TEXT => Type::String,
62            ffi::SQLITE_NULL => Type::Null,
63            _ => unreachable!(),
64        }
65    }
66
67    /// Return the name of a column.
68    #[inline]
69    pub fn name(&self, i: usize) -> String {
70        debug_assert!(i < self.count(), "the index is out of range");
71        unsafe { ffi::sqlite3_column_name(self.raw.0, i as _) }
72    }
73
74    /// Return column names.
75    #[inline]
76    pub fn names(&self) -> Vec<String> {
77        (0..self.count()).map(|i| self.name(i)).collect()
78    }
79
80    /// Advance to the next state.
81    ///
82    /// The function should be called multiple times until `State::Done` is
83    /// reached in order to evaluate the statement entirely.
84    pub fn next(&mut self) -> Result<State> {
85        Ok(match unsafe { ffi::sqlite3_step(self.raw.0) } {
86            ffi::SQLITE_ROW => State::Row,
87            ffi::SQLITE_DONE => State::Done,
88            code => error!(self.raw.1, code),
89        })
90    }
91
92    /// Read a value from a column.
93    ///
94    /// The leftmost column has the index 0.
95    #[inline]
96    pub fn read<T: Readable>(&self, i: usize) -> Result<T> {
97        debug_assert!(i < self.count(), "the index is out of range");
98        Readable::read(self, i)
99    }
100
101    /// Reset the statement.
102    #[inline]
103    pub fn reset(&mut self) -> Result<()> {
104        unsafe { ok_raw!(self.raw.1, ffi::sqlite3_reset(self.raw.0)) };
105        Ok(())
106    }
107
108    /// Upgrade to a cursor.
109    #[inline]
110    pub fn cursor(self) -> Cursor {
111        crate::cursor::new(self)
112    }
113
114    /// Return the raw pointer.
115    #[inline]
116    pub fn as_raw(&self) -> ffi::Sqlite3StmtHandle {
117        self.raw.0
118    }
119}
120
121impl<'l> Drop for Statement {
122    #[inline]
123    fn drop(&mut self) {
124        unsafe { ffi::sqlite3_finalize(self.raw.0) };
125    }
126}
127
128impl Bindable for &Value {
129    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
130        match self {
131            Value::Binary(value) => value.bind(statement, i),
132            &Value::Float(value) => value.bind(statement, i),
133            &Value::Integer(value) => value.bind(statement, i),
134            Value::String(value) => value.as_str().bind(statement, i),
135            Value::Null => ().bind(statement, i),
136        }
137    }
138}
139
140impl Bindable for &Vec<u8> {
141    #[inline]
142    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
143        debug_assert!(i > 0, "the indexing starts from 1");
144        unsafe {
145            ok_raw!(
146                statement.raw.1,
147                ffi::sqlite3_bind_blob(statement.raw.0, i as _, self.into(), 0)
148            );
149        }
150        Ok(())
151    }
152}
153
154impl Bindable for f64 {
155    #[inline]
156    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
157        debug_assert!(i > 0, "the indexing starts from 1");
158        unsafe {
159            ok_raw!(
160                statement.raw.1,
161                ffi::sqlite3_bind_double(statement.raw.0, i as i32, self)
162            );
163        }
164        Ok(())
165    }
166}
167
168impl Bindable for i64 {
169    #[inline]
170    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
171        debug_assert!(i > 0, "the indexing starts from 1");
172        unsafe {
173            ok_raw!(
174                statement.raw.1,
175                ffi::sqlite3_bind_int64(statement.raw.0, i as i32, self as _)
176            );
177        }
178        Ok(())
179    }
180}
181
182impl<'l> Bindable for &'l str {
183    #[inline]
184    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
185        debug_assert!(i > 0, "the indexing starts from 1");
186        unsafe {
187            ok_raw!(
188                statement.raw.1,
189                ffi::sqlite3_bind_text(statement.raw.0, i as i32, self.into(), 0)
190            );
191        }
192        Ok(())
193    }
194}
195
196impl Bindable for () {
197    #[inline]
198    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
199        debug_assert!(i > 0, "the indexing starts from 1");
200        unsafe {
201            ok_raw!(
202                statement.raw.1,
203                ffi::sqlite3_bind_null(statement.raw.0, i as i32)
204            );
205        }
206        Ok(())
207    }
208}
209
210impl<T: Bindable> Bindable for Option<T> {
211    #[inline]
212    fn bind(self, statement: &mut Statement, i: usize) -> Result<()> {
213        debug_assert!(i > 0, "the indexing starts from 1");
214        match self {
215            Some(inner) => inner.bind(statement, i),
216            None => ().bind(statement, i),
217        }
218    }
219}
220
221impl Readable for Value {
222    fn read(statement: &Statement, i: usize) -> Result<Self> {
223        Ok(match statement.kind(i) {
224            Type::Binary => Value::Binary(Readable::read(statement, i)?),
225            Type::Float => Value::Float(Readable::read(statement, i)?),
226            Type::Integer => Value::Integer(Readable::read(statement, i)?),
227            Type::String => Value::String(Readable::read(statement, i)?),
228            Type::Null => Value::Null,
229        })
230    }
231}
232
233impl Readable for f64 {
234    #[inline]
235    fn read(statement: &Statement, i: usize) -> Result<Self> {
236        Ok(unsafe { ffi::sqlite3_column_double(statement.raw.0, i as _) })
237    }
238}
239
240impl Readable for i64 {
241    #[inline]
242    fn read(statement: &Statement, i: usize) -> Result<Self> {
243        Ok(unsafe { ffi::sqlite3_column_int64(statement.raw.0, i as _) })
244    }
245}
246
247impl Readable for String {
248    #[inline]
249    fn read(statement: &Statement, i: usize) -> Result<Self> {
250        unsafe { Ok(ffi::sqlite3_column_text(statement.raw.0, i as _)) }
251    }
252}
253
254impl Readable for Vec<u8> {
255    #[inline]
256    fn read(statement: &Statement, i: usize) -> Result<Self> {
257        unsafe { Ok(ffi::sqlite3_column_blob(statement.raw.0, i as i32)) }
258    }
259}
260
261impl<T: Readable> Readable for Option<T> {
262    #[inline]
263    fn read(statement: &Statement, i: usize) -> Result<Self> {
264        if statement.kind(i) == Type::Null {
265            Ok(None)
266        } else {
267            T::read(statement, i).map(Some)
268        }
269    }
270}
271
272#[inline]
273pub fn new<T: AsRef<str>>(raw1: ffi::Sqlite3DbHandle, statement: T) -> Result<Statement> {
274    let result = unsafe { ffi::sqlite3_prepare_v2(raw1, statement.as_ref().into()) };
275    if result.ret_code != ffi::SQLITE_OK {
276        error!(raw1, result.ret_code)
277    }
278
279    Ok(Statement {
280        raw: (result.stmt_handle, raw1),
281        phantom: PhantomData,
282    })
283}