sqlite_bindings_lunatic/
statement.rs

1use ffi;
2use libc::{c_double, c_int};
3use std::collections::HashMap;
4use std::marker::PhantomData;
5use std::rc::Rc;
6
7use cursor::{Cursor, CursorWithOwnership, Row};
8use error::Result;
9use value::{Type, Value};
10
11// https://sqlite.org/c3ref/c_static.html
12macro_rules! transient(
13    () => (::std::mem::transmute(!0 as *const ::libc::c_void));
14);
15
16/// A prepared statement.
17#[derive(Debug, Clone)]
18pub struct Statement {
19    raw: *mut ffi::sqlite3_stmt,
20    column_names: Vec<String>,
21    column_mapping: Rc<HashMap<String, usize>>,
22    phantom: PhantomData<ffi::sqlite3_stmt>,
23}
24
25unsafe impl<'l> Sync for Statement {}
26unsafe impl<'l> Send for Statement {}
27
28/// A type suitable for binding to a prepared statement.
29pub trait Bindable {
30    /// Bind to a parameter.
31    fn bind(self, _: &mut Statement) -> Result<()>;
32}
33
34/// A type suitable for binding to a prepared statement given a parameter index.
35pub trait BindableWithIndex {
36    /// Bind to a parameter.
37    ///
38    /// In case of integer indices, the first parameter has index 1.
39    fn bind<T: ParameterIndex>(self, _: &mut Statement, _: T) -> Result<()>;
40}
41
42/// A type suitable for indexing columns in a prepared statement.
43pub trait ColumnIndex: Copy + std::fmt::Debug {
44    /// Identify the ordinal position.
45    ///
46    /// The first column has index 0.
47    fn index(self, statement: &Statement) -> Result<usize>;
48}
49
50/// A type suitable for indexing parameters in a prepared statement.
51pub trait ParameterIndex: Copy + std::fmt::Debug {
52    /// Identify the ordinal position.
53    ///
54    /// The first parameter has index 1.
55    fn index(self, statement: &Statement) -> Result<usize>;
56}
57
58/// A type suitable for reading from a prepared statement given a column index.
59pub trait ReadableWithIndex: Sized {
60    /// Read from a column.
61    ///
62    /// In case of integer indices, the first column has index 0.
63    fn read<T: ColumnIndex>(_: &Statement, _: T) -> Result<Self>;
64}
65
66/// The state of a prepared statement.
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum State {
69    /// There is a row available for reading.
70    Row,
71    /// The statement has been entirely evaluated.
72    Done,
73}
74
75impl Statement {
76    /// Bind values to parameters.
77    ///
78    /// In case of integer indices, the first parameter has index 1.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
84    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
85    /// let query = "SELECT * FROM users WHERE name = ?";
86    /// let mut statement = connection.prepare(query)?;
87    /// statement.bind((1, "Bob"))?;
88    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
89    /// ```
90    ///
91    /// ```
92    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
93    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
94    /// let query = "SELECT * FROM users WHERE name = ?";
95    /// let mut statement = connection.prepare(query)?;
96    /// statement.bind(&[(1, "Bob")][..])?;
97    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
98    /// ```
99    ///
100    /// ```
101    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
102    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
103    /// let query = "SELECT * FROM users WHERE name = :name";
104    /// let mut statement = connection.prepare(query)?;
105    /// statement.bind((":name", "Bob"))?;
106    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
107    /// ```
108    ///
109    /// ```
110    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
111    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
112    /// let query = "SELECT * FROM users WHERE name = :name";
113    /// let mut statement = connection.prepare(query)?;
114    /// statement.bind(&[(":name", "Bob")][..])?;
115    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
116    /// ```
117    ///
118    /// ```
119    /// # use sqlite_bindings_lunatic::Value;
120    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
121    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
122    /// let query = "SELECT * FROM users WHERE id = :id AND name = :name";
123    /// let mut statement = connection.prepare(query)?;
124    /// statement.bind::<&[(_, Value)]>(&[
125    ///     (":id", 1.into()),
126    ///     (":name", "Bob".into()),
127    /// ][..])?;
128    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
129    /// ```
130    #[inline]
131    pub fn bind<T: Bindable>(&mut self, value: T) -> Result<()> {
132        value.bind(self)?;
133        Ok(())
134    }
135
136    /// Bind values to parameters via an iterator.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// # use sqlite_bindings_lunatic::Value;
142    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
143    /// # connection.execute("CREATE TABLE users (id INTEGER, name STRING)");
144    /// let query = "INSERT INTO users VALUES (:id, :name)";
145    /// let mut statement = connection.prepare(query)?;
146    /// statement.bind_iter::<_, (_, Value)>([
147    ///     (":name", "Bob".into()),
148    ///     (":id", 42.into()),
149    /// ])?;
150    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
151    /// ```
152    pub fn bind_iter<T, U>(&mut self, value: T) -> Result<()>
153    where
154        T: IntoIterator<Item = U>,
155        U: Bindable,
156    {
157        for value in value {
158            self.bind(value)?;
159        }
160        Ok(())
161    }
162
163    /// Return the number of columns.
164    #[inline]
165    pub fn column_count(&self) -> usize {
166        self.column_names.len()
167    }
168
169    #[doc(hidden)]
170    #[inline]
171    pub fn column_mapping(&self) -> Rc<HashMap<String, usize>> {
172        self.column_mapping.clone()
173    }
174
175    /// Return the name of a column.
176    ///
177    /// In case of integer indices, the first column has index 0.
178    #[inline]
179    pub fn column_name<T: ColumnIndex>(&self, index: T) -> Result<&str> {
180        Ok(&self.column_names[index.index(self)?])
181    }
182
183    /// Return column names.
184    #[inline]
185    pub fn column_names(&self) -> &[String] {
186        &self.column_names
187    }
188
189    /// Return the type of a column.
190    ///
191    /// The type becomes available after taking a step. In case of integer
192    /// indices, the first column has index 0.
193    pub fn column_type<T: ColumnIndex>(&self, index: T) -> Result<Type> {
194        Ok(
195            match unsafe { ffi::sqlite3_column_type(self.raw, index.index(self)? as c_int) } {
196                ffi::SQLITE_BLOB => Type::Binary,
197                ffi::SQLITE_FLOAT => Type::Float,
198                ffi::SQLITE_INTEGER => Type::Integer,
199                ffi::SQLITE_TEXT => Type::String,
200                ffi::SQLITE_NULL => Type::Null,
201                _ => unreachable!(),
202            },
203        )
204    }
205
206    /// Create a cursor.
207    #[inline]
208    pub fn iter(&mut self) -> Cursor {
209        self.into()
210    }
211
212    /// Advance to the next state.
213    ///
214    /// The function should be called multiple times until `State::Done` is
215    /// reached in order to evaluate the statement entirely.
216    pub fn next(&mut self) -> Result<State> {
217        Ok(match unsafe { ffi::sqlite3_step(self.raw) } {
218            ffi::SQLITE_ROW => State::Row,
219            ffi::SQLITE_DONE => State::Done,
220            code => {
221                return Err(::Error {
222                    code: Some(code as isize),
223                    message: None,
224                })
225            }
226        })
227    }
228
229    /// Return the index for a named parameter if exists.
230    ///
231    /// # Examples
232    ///
233    /// ```
234    /// # let connection = sqlite_bindings_lunatic::open(":memory:").unwrap();
235    /// # connection.execute("CREATE TABLE users (name STRING)");
236    /// let query = "SELECT * FROM users WHERE name = :name";
237    /// let statement = connection.prepare(query)?;
238    /// assert_eq!(statement.parameter_index(":name")?.unwrap(), 1);
239    /// assert_eq!(statement.parameter_index(":asdf")?, None);
240    /// # Ok::<(), sqlite_bindings_lunatic::Error>(())
241    /// ```
242    #[inline]
243    pub fn parameter_index(&self, parameter: &str) -> Result<Option<usize>> {
244        let index = unsafe {
245            ffi::sqlite3_bind_parameter_index(self.raw, str_to_cstr!(parameter).as_ptr())
246        };
247        match index {
248            0 => Ok(None),
249            _ => Ok(Some(index as usize)),
250        }
251    }
252
253    /// Read a value from a column.
254    ///
255    /// In case of integer indices, the first column has index 0.
256    #[inline]
257    pub fn read<T, U>(&self, index: U) -> Result<T>
258    where
259        T: ReadableWithIndex,
260        U: ColumnIndex,
261    {
262        ReadableWithIndex::read(self, index)
263    }
264
265    /// Reset the internal state.
266    #[inline]
267    pub fn reset(&mut self) -> Result<()> {
268        unsafe { ok!(ffi::sqlite3_reset(self.raw)) };
269        Ok(())
270    }
271
272    #[doc(hidden)]
273    #[inline]
274    pub fn as_raw(&self) -> *mut ffi::sqlite3_stmt {
275        self.raw
276    }
277}
278
279impl Drop for Statement {
280    #[inline]
281    fn drop(&mut self) {
282        unsafe { ffi::sqlite3_finalize(self.raw) };
283    }
284}
285
286impl<'m> From<&'m mut Statement> for Cursor<'m> {
287    #[inline]
288    fn from(statement: &'m mut Statement) -> Self {
289        ::cursor::new(statement)
290    }
291}
292
293impl IntoIterator for Statement {
294    type Item = Result<Row>;
295    type IntoIter = CursorWithOwnership;
296
297    #[inline]
298    fn into_iter(self) -> Self::IntoIter {
299        ::cursor::new_with_ownership(self)
300    }
301}
302
303impl<T, U> Bindable for (T, U)
304where
305    T: ParameterIndex,
306    U: BindableWithIndex,
307{
308    #[inline]
309    fn bind(self, statement: &mut Statement) -> Result<()> {
310        self.1.bind(statement, self.0)
311    }
312}
313
314impl<T> Bindable for &[T]
315where
316    T: BindableWithIndex + Clone,
317{
318    fn bind(self, statement: &mut Statement) -> Result<()> {
319        for (index, value) in self.iter().enumerate() {
320            value.clone().bind(statement, index + 1)?;
321        }
322        Ok(())
323    }
324}
325
326impl<T, U> Bindable for &[(T, U)]
327where
328    T: ParameterIndex,
329    U: BindableWithIndex + Clone,
330{
331    fn bind(self, statement: &mut Statement) -> Result<()> {
332        for (index, value) in self.iter() {
333            value.clone().bind(statement, *index)?;
334        }
335        Ok(())
336    }
337}
338
339impl BindableWithIndex for &[u8] {
340    #[inline]
341    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
342        unsafe {
343            ok!(ffi::sqlite3_bind_blob(
344                statement.raw,
345                index.index(statement)? as c_int,
346                self.as_ptr() as *const _,
347                self.len() as c_int,
348                transient!(),
349            ));
350        }
351        Ok(())
352    }
353}
354
355impl BindableWithIndex for f64 {
356    #[inline]
357    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
358        unsafe {
359            ok!(ffi::sqlite3_bind_double(
360                statement.raw,
361                index.index(statement)? as c_int,
362                self as c_double
363            ));
364        }
365        Ok(())
366    }
367}
368
369impl BindableWithIndex for i64 {
370    #[inline]
371    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
372        unsafe {
373            ok!(ffi::sqlite3_bind_int64(
374                statement.raw,
375                index.index(statement)? as c_int,
376                self as ffi::sqlite3_int64
377            ));
378        }
379        Ok(())
380    }
381}
382
383impl BindableWithIndex for &str {
384    #[inline]
385    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
386        unsafe {
387            let value = ffi::sqlite3_bind_text(
388                statement.raw,
389                index.index(statement)? as c_int,
390                self.as_ptr() as *const _,
391                self.len() as c_int,
392                transient!(),
393            );
394            ok!(value);
395        }
396        Ok(())
397    }
398}
399
400impl BindableWithIndex for () {
401    #[inline]
402    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
403        unsafe {
404            ok!(ffi::sqlite3_bind_null(
405                statement.raw,
406                index.index(statement)? as c_int
407            ));
408        }
409        Ok(())
410    }
411}
412
413impl BindableWithIndex for Value {
414    #[inline]
415    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
416        (index, &self).bind(statement)
417    }
418}
419
420impl BindableWithIndex for &Value {
421    fn bind<T: ParameterIndex>(self, statement: &mut Statement, index: T) -> Result<()> {
422        match self {
423            &Value::Binary(ref value) => (value as &[u8]).bind(statement, index),
424            &Value::Float(value) => value.bind(statement, index),
425            &Value::Integer(value) => value.bind(statement, index),
426            &Value::String(ref value) => (value as &str).bind(statement, index),
427            &Value::Null => ().bind(statement, index),
428        }
429    }
430}
431
432impl<T> BindableWithIndex for Option<T>
433where
434    T: BindableWithIndex,
435{
436    #[inline]
437    fn bind<U: ParameterIndex>(self, statement: &mut Statement, index: U) -> Result<()> {
438        match self {
439            Some(value) => value.bind(statement, index),
440            None => ().bind(statement, index),
441        }
442    }
443}
444
445impl<T> BindableWithIndex for &Option<T>
446where
447    T: BindableWithIndex + Clone,
448{
449    #[inline]
450    fn bind<U: ParameterIndex>(self, statement: &mut Statement, index: U) -> Result<()> {
451        match self {
452            Some(value) => value.clone().bind(statement, index),
453            None => ().bind(statement, index),
454        }
455    }
456}
457
458impl ColumnIndex for &str {
459    #[inline]
460    fn index(self, statement: &Statement) -> Result<usize> {
461        if statement.column_mapping.contains_key(self) {
462            Ok(statement.column_mapping[self])
463        } else {
464            raise!("the index is out of range ({})", self);
465        }
466    }
467}
468
469impl ColumnIndex for usize {
470    #[inline]
471    fn index(self, statement: &Statement) -> Result<usize> {
472        if self < statement.column_count() {
473            Ok(self)
474        } else {
475            raise!("the index is out of range ({})", self);
476        }
477    }
478}
479
480impl ParameterIndex for &str {
481    #[inline]
482    fn index(self, statement: &Statement) -> Result<usize> {
483        match statement.parameter_index(self)? {
484            Some(index) => return Ok(index),
485            _ => raise!("the index is out of range ({})", self),
486        }
487    }
488}
489
490impl ParameterIndex for usize {
491    #[inline]
492    fn index(self, _: &Statement) -> Result<usize> {
493        if self > 0 {
494            Ok(self)
495        } else {
496            raise!("the index is out of range ({})", self);
497        }
498    }
499}
500
501impl ReadableWithIndex for Value {
502    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
503        Ok(match statement.column_type(index)? {
504            Type::Binary => Value::Binary(ReadableWithIndex::read(statement, index)?),
505            Type::Float => Value::Float(ReadableWithIndex::read(statement, index)?),
506            Type::Integer => Value::Integer(ReadableWithIndex::read(statement, index)?),
507            Type::String => Value::String(ReadableWithIndex::read(statement, index)?),
508            Type::Null => Value::Null,
509        })
510    }
511}
512
513impl ReadableWithIndex for f64 {
514    #[inline]
515    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
516        Ok(unsafe {
517            ffi::sqlite3_column_double(statement.raw, index.index(statement)? as c_int) as f64
518        })
519    }
520}
521
522impl ReadableWithIndex for i64 {
523    #[inline]
524    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
525        Ok(unsafe {
526            ffi::sqlite3_column_int64(statement.raw, index.index(statement)? as c_int) as i64
527        })
528    }
529}
530
531impl ReadableWithIndex for String {
532    #[inline]
533    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
534        unsafe {
535            let pointer = ffi::sqlite3_column_text(statement.raw, index.index(statement)? as c_int);
536            if pointer.is_null() {
537                raise!("cannot read a text column");
538            }
539            Ok(c_str_to_string!(pointer))
540        }
541    }
542}
543
544impl ReadableWithIndex for Vec<u8> {
545    #[inline]
546    fn read<T: ColumnIndex>(statement: &Statement, index: T) -> Result<Self> {
547        use std::ptr::copy_nonoverlapping as copy;
548        unsafe {
549            let pointer = ffi::sqlite3_column_blob(statement.raw, index.index(statement)? as c_int);
550            if pointer.is_null() {
551                return Ok(vec![]);
552            }
553            let count =
554                ffi::sqlite3_column_bytes(statement.raw, index.index(statement)? as c_int) as usize;
555            let mut buffer = Vec::with_capacity(count);
556            buffer.set_len(count);
557            copy(pointer as *const u8, buffer.as_mut_ptr(), count);
558            Ok(buffer)
559        }
560    }
561}
562
563impl<T: ReadableWithIndex> ReadableWithIndex for Option<T> {
564    #[inline]
565    fn read<U: ColumnIndex>(statement: &Statement, index: U) -> Result<Self> {
566        if statement.column_type(index)? == Type::Null {
567            Ok(None)
568        } else {
569            T::read(statement, index).map(Some)
570        }
571    }
572}
573
574#[inline]
575pub fn new<'l, T>(raw_connection: *mut ffi::sqlite3, statement: T) -> Result<Statement>
576where
577    T: AsRef<str>,
578{
579    let mut raw_statement = 0 as *mut _;
580    unsafe {
581        ok!(
582            raw_connection,
583            ffi::sqlite3_prepare_v2(
584                raw_connection,
585                str_to_cstr!(statement.as_ref()).as_ptr(),
586                -1,
587                &mut raw_statement,
588                0 as *mut _,
589            )
590        );
591    }
592    let column_count = unsafe { ffi::sqlite3_column_count(raw_statement) as usize };
593    let column_names = (0..column_count)
594        .map(|index| unsafe {
595            let raw = ffi::sqlite3_column_name(raw_statement, index as c_int);
596            debug_assert!(!raw.is_null());
597            c_str_to_str!(raw).unwrap().to_string()
598        })
599        .collect::<Vec<_>>();
600    let column_mapping = column_names
601        .iter()
602        .enumerate()
603        .map(|(index, name)| (name.to_string(), index))
604        .collect();
605    Ok(Statement {
606        raw: raw_statement,
607        column_names: column_names,
608        column_mapping: Rc::new(column_mapping),
609        phantom: PhantomData,
610    })
611}