kontrak_sqlite/
statement.rs

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