sqlite3/
lib.rs

1//! `rust-sqlite3` is a rustic binding to the [sqlite3 API][].
2//!
3//! [sqlite3 API]: http://www.sqlite.org/c3ref/intro.html
4//!
5//! Three layers of API are provided:
6//!
7//!  - `mod ffi` provides exhaustive, though unsafe, [bindgen] bindings for `libsqlite.h`.
8//!  - `mod core` provides a minimal safe interface to the basic sqlite3 API.
9//!  - `mod types` provides `ToSql`/`FromSql` traits, and the library provides
10//!     convenient `query()` and `update()` APIs.
11//!
12//! [bindgen]: https://github.com/crabtw/rust-bindgen
13//!
14//! The following example demonstrates opening a database, executing
15//! DDL, and using the high-level `query()` and `update()` API. Note the
16//! use of `Result` and `try!()` for error handling.
17//!
18//! ```rust
19//! extern crate time;
20//! extern crate sqlite3;
21//! 
22//! use time::Timespec;
23//! 
24//! use sqlite3::{
25//!     DatabaseConnection,
26//!     Query,
27//!     ResultRow,
28//!     ResultRowAccess,
29//!     SqliteResult,
30//!     StatementUpdate,
31//! };
32//! 
33//! #[derive(Debug)]
34//! struct Person {
35//!     id: i32,
36//!     name: String,
37//!     time_created: Timespec,
38//!     // TODO: data: Option<Vec<u8>>
39//! }
40//! 
41//! pub fn main() {
42//!     match io() {
43//!         Ok(ppl) => println!("Found people: {:?}", ppl),
44//!         Err(oops) => panic!(oops)
45//!     }
46//! }
47//! 
48//! fn io() -> SqliteResult<Vec<Person>> {
49//!     let mut conn = try!(DatabaseConnection::in_memory());
50//! 
51//!     try!(conn.exec("CREATE TABLE person (
52//!                  id              SERIAL PRIMARY KEY,
53//!                  name            VARCHAR NOT NULL,
54//!                  time_created    TIMESTAMP NOT NULL
55//!                )"));
56//! 
57//!     let me = Person {
58//!         id: 0,
59//!         name: format!("Dan"),
60//!         time_created: time::get_time(),
61//!     };
62//!     {
63//!         let mut tx = try!(conn.prepare("INSERT INTO person (name, time_created)
64//!                            VALUES ($1, $2)"));
65//!         let changes = try!(tx.update(&[&me.name, &me.time_created]));
66//!         assert_eq!(changes, 1);
67//!     }
68//! 
69//!     let mut stmt = try!(conn.prepare("SELECT id, name, time_created FROM person"));
70//! 
71//!     let to_person = |row: &mut ResultRow| Ok(
72//!         Person {
73//!             id: row.get("id"),
74//!             name: row.get("name"),
75//!             time_created: row.get(2)
76//!         });
77//!     let ppl = try!(stmt.query(&[], to_person));
78//!     ppl.collect()
79//! }
80//! ```
81
82#![crate_name = "sqlite3"]
83#![crate_type = "lib"]
84#![warn(missing_docs)]
85
86extern crate libc;
87extern crate time;
88
89#[macro_use]
90extern crate bitflags;
91
92#[macro_use]
93extern crate enum_primitive;
94
95use std::error::{Error};
96use std::fmt::Display;
97use std::fmt;
98
99pub use core::Access;
100pub use core::{DatabaseConnection, PreparedStatement, ResultSet, ResultRow};
101pub use core::{ColIx, ParamIx};
102pub use types::{FromSql, ToSql};
103
104use self::SqliteErrorCode::SQLITE_MISUSE;
105
106pub mod core;
107pub mod types;
108
109/// bindgen-bindings to libsqlite3
110#[allow(non_camel_case_types, non_snake_case)]
111#[allow(dead_code)]
112#[allow(missing_docs)]
113#[allow(missing_copy_implementations)]  // until I figure out rust-bindgen #89
114pub mod ffi;
115
116pub mod access;
117
118/// Mix in `update()` convenience function.
119pub trait StatementUpdate {
120    /// Execute a statement after binding any parameters.
121    fn update(&mut self,
122              values: &[&ToSql]) -> SqliteResult<u64>;
123}
124
125
126impl StatementUpdate for core::PreparedStatement {
127    /// Execute a statement after binding any parameters.
128    ///
129    /// When the statement is done, The [number of rows
130    /// modified][changes] is reported.
131    ///
132    /// Fail with `Err(SQLITE_MISUSE)` in case the statement results
133    /// in any any rows (e.g. a `SELECT` rather than `INSERT` or
134    /// `UPDATE`).
135    ///
136    /// [changes]: http://www.sqlite.org/c3ref/changes.html
137    fn update(&mut self,
138              values: &[&ToSql]) -> SqliteResult<u64> {
139        let check = {
140            try!(bind_values(self, values));
141            let mut results = self.execute();
142            match try!(results.step()) {
143                None => Ok(()),
144                Some(_row) => Err(SqliteError {
145                    kind: SQLITE_MISUSE,
146                    desc: "unexpected SQLITE_ROW from update",
147                    detail: None
148                })
149            }
150        };
151        check.map(|_ok| self.changes())
152    }
153}
154
155
156/// Mix in `query_each()` convenience function.
157pub trait QueryEach<F>
158    where F: FnMut(&mut ResultRow) -> SqliteResult<()>
159{
160    /// Process rows from a query after binding parameters.
161    fn query_each(&mut self,
162                  values: &[&ToSql],
163                  each_row: &mut F
164                  ) -> SqliteResult<()>;
165}
166
167impl<F> QueryEach<F> for core::PreparedStatement
168    where F: FnMut(&mut ResultRow) -> SqliteResult<()>
169{
170    /// Process rows from a query after binding parameters.
171    ///
172    /// For call `each_row(row)` for each resulting step,
173    /// exiting on `Err`.
174    fn query_each(&mut self,
175                  values: &[&ToSql],
176                  each_row: &mut F
177                  ) -> SqliteResult<()>
178    {
179        try!(bind_values(self, values));
180        let mut results = self.execute();
181        loop {
182            match try!(results.step()) {
183                None => break,
184                Some(ref mut row) => try!(each_row(row)),
185            }
186        }
187        Ok(())
188    }
189}
190
191
192/// Mix in `query_fold()` convenience function.
193pub trait QueryFold<F, A>
194    where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
195{
196    /// Fold rows from a query after binding parameters.
197    fn query_fold(&mut self,
198                  values: &[&ToSql],
199                  init: A,
200                  each_row: F
201                  ) -> SqliteResult<A>;
202}
203
204
205impl<F, A> QueryFold<F, A> for core::PreparedStatement
206    where F: Fn(&mut ResultRow, A) -> SqliteResult<A>
207{
208    /// Fold rows from a query after binding parameters.
209    fn query_fold(&mut self,
210                  values: &[&ToSql],
211                  init: A,
212                  f: F
213                  ) -> SqliteResult<A>
214    {
215        try!(bind_values(self, values));
216        let mut results = self.execute();
217        let mut accum = init;
218        loop {
219            match try!(results.step()) {
220                None => break,
221                Some(ref mut row) => accum = try!(f(row, accum)),
222            }
223        }
224        Ok(accum)
225    }
226}
227
228
229/// Mix in `query()` convenience function.
230pub trait Query<F, T>
231    where F: FnMut(&mut ResultRow) -> SqliteResult<T>
232{
233    /// Iterate over query results after binding parameters.
234    ///
235    /// Each of the `values` is bound to the statement (using `to_sql`)
236    /// and the statement is executed.
237    ///
238    /// Returns an iterator over rows transformed by `txform`,
239    /// which computes a value for each row (or an error).
240    fn query<'stmt>(&'stmt mut self,
241                    values: &[&ToSql],
242                    txform: F
243                ) -> SqliteResult<QueryResults<'stmt, T, F>>;
244}
245
246impl<F, T> Query<F, T> for core::PreparedStatement
247    where F: FnMut(&mut ResultRow) -> SqliteResult<T>
248{
249    fn query<'stmt>(&'stmt mut self,
250                    values: &[&ToSql],
251                    txform: F
252                    ) -> SqliteResult<QueryResults<'stmt, T, F>>
253    {
254        try!(bind_values(self, values));
255        let results = self.execute();
256        Ok(QueryResults { results: results, txform: txform })
257    }
258}
259
260/// An iterator over transformed query results
261pub struct QueryResults<'stmt, T, F>
262    where F: FnMut(&mut ResultRow) -> SqliteResult<T>
263{
264    results: core::ResultSet<'stmt>,
265    txform: F
266}
267
268impl<'stmt, T, F> Iterator for QueryResults<'stmt, T, F>
269    where F: FnMut(&mut ResultRow) -> SqliteResult<T>
270{
271    type Item = SqliteResult<T>;
272
273    fn next(&mut self) -> Option<SqliteResult<T>> {
274        match self.results.step() {
275            Ok(None) => None,
276            Ok(Some(ref mut row)) => Some((self.txform)(row)),
277            Err(e) => Some(Err(e))
278        }
279    }
280}
281
282
283fn bind_values(s: &mut PreparedStatement, values: &[&ToSql]) -> SqliteResult<()> {
284    for (ix, v) in values.iter().enumerate() {
285        let p = ix as ParamIx + 1;
286        try!(v.to_sql(s, p));
287    }
288    Ok(())
289}
290
291
292/// Access result columns of a row by name or numeric index.
293pub trait ResultRowAccess {
294    /// Get `T` type result value from `idx`th column of a row.
295    ///
296    /// # Panic
297    ///
298    /// Panics if there is no such column or value.
299    fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T;
300
301    /// Try to get `T` type result value from `idx`th column of a row.
302    fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T>;
303}
304
305impl<'res, 'row> ResultRowAccess for core::ResultRow<'res, 'row> {
306    fn get<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> T {
307        match self.get_opt(idx.clone()) {
308            Ok(ok) => ok,
309            Err(err) => panic!("retrieving column {}: {}", idx, err)
310        }
311    }
312
313    fn get_opt<I: RowIndex + Display + Clone, T: FromSql>(&mut self, idx: I) -> SqliteResult<T> {
314        match idx.idx(self) {
315            Some(idx) => FromSql::from_sql(self, idx),
316            None => Err(SqliteError {
317                kind: SQLITE_MISUSE,
318                desc: "no such row name/number",
319                detail: Some(format!("{}", idx))
320            })
321        }
322    }
323
324}
325
326/// A trait implemented by types that can index into columns of a row.
327///
328/// *inspired by sfackler's [RowIndex][]*
329/// [RowIndex]: http://www.rust-ci.org/sfackler/rust-postgres/doc/postgres/trait.RowIndex.html
330pub trait RowIndex {
331    /// Try to convert `self` to an index into a row.
332    fn idx(&self, row: &mut ResultRow) -> Option<ColIx>;
333}
334
335impl RowIndex for ColIx {
336    /// Index into a row directly by uint.
337    fn idx(&self, _row: &mut ResultRow) -> Option<ColIx> { Some(*self) }
338}
339
340impl RowIndex for &'static str {
341    /// Index into a row by column name.
342    ///
343    /// *TODO: figure out how to use lifetime of row rather than
344    /// `static`.*
345    fn idx(&self, row: &mut ResultRow) -> Option<ColIx> {
346        let mut ixs = 0 .. row.column_count();
347        ixs.find(|ix| row.with_column_name(*ix, false, |name| name == *self))
348    }
349}
350
351
352/// The type used for returning and propagating sqlite3 errors.
353#[must_use]
354pub type SqliteResult<T> = Result<T, SqliteError>;
355
356/// Result codes for errors.
357///
358/// cf. [sqlite3 result codes][codes].
359///
360/// Note `SQLITE_OK` is not included; we use `Ok(...)` instead.
361///
362/// Likewise, in place of `SQLITE_ROW` and `SQLITE_DONE`, we return
363/// `Some(...)` or `None` from `ResultSet::next()`.
364///
365/// [codes]: http://www.sqlite.org/c3ref/c_abort.html
366enum_from_primitive! {
367    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
368    #[allow(non_camel_case_types)]
369    #[allow(missing_docs)]
370    pub enum SqliteErrorCode {
371        SQLITE_ERROR     =  1,
372        SQLITE_INTERNAL  =  2,
373        SQLITE_PERM      =  3,
374        SQLITE_ABORT     =  4,
375        SQLITE_BUSY      =  5,
376        SQLITE_LOCKED    =  6,
377        SQLITE_NOMEM     =  7,
378        SQLITE_READONLY  =  8,
379        SQLITE_INTERRUPT =  9,
380        SQLITE_IOERR     = 10,
381        SQLITE_CORRUPT   = 11,
382        SQLITE_NOTFOUND  = 12,
383        SQLITE_FULL      = 13,
384        SQLITE_CANTOPEN  = 14,
385        SQLITE_PROTOCOL  = 15,
386        SQLITE_EMPTY     = 16,
387        SQLITE_SCHEMA    = 17,
388        SQLITE_TOOBIG    = 18,
389        SQLITE_CONSTRAINT= 19,
390        SQLITE_MISMATCH  = 20,
391        SQLITE_MISUSE    = 21,
392        SQLITE_NOLFS     = 22,
393        SQLITE_AUTH      = 23,
394        SQLITE_FORMAT    = 24,
395        SQLITE_RANGE     = 25,
396        SQLITE_NOTADB    = 26
397    }
398}
399
400/// Error results
401#[derive(Debug, PartialEq, Eq)]
402pub struct SqliteError {
403    /// kind of error, by code
404    pub kind: SqliteErrorCode,
405    /// static error description
406    pub desc: &'static str,
407    /// dynamic detail (optional)
408    pub detail: Option<String>
409}
410
411impl Display for SqliteError {
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413        match self.detail {
414            Some(ref x) => f.write_fmt(format_args!("{} ({})", x, self.kind as u32)),
415            None => f.write_fmt(format_args!("{} ({})", self.desc, self.kind as u32))
416        }
417    }
418}
419
420impl SqliteError {
421    /// Get a detailed description of the error
422    pub fn detail(&self) -> Option<String> { self.detail.clone() }
423}
424
425impl Error for SqliteError {
426    fn description(&self) -> &str { self.desc }
427    fn cause(&self) -> Option<&Error> { None }
428}
429
430
431/// Fundamental Datatypes
432enum_from_primitive! {
433    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
434    #[allow(non_camel_case_types)]
435    #[allow(missing_docs)]
436    pub enum ColumnType {
437        SQLITE_INTEGER = 1,
438        SQLITE_FLOAT   = 2,
439        SQLITE_TEXT    = 3,
440        SQLITE_BLOB    = 4,
441        SQLITE_NULL    = 5
442    }
443}
444
445#[cfg(test)]
446mod bind_tests {
447    use super::{DatabaseConnection, ResultSet};
448    use super::{ResultRowAccess};
449    use super::{SqliteResult};
450
451    #[test]
452    fn bind_fun() {
453        fn go() -> SqliteResult<()> {
454            let mut database = try!(DatabaseConnection::in_memory());
455
456            try!(database.exec(
457                "BEGIN;
458                CREATE TABLE test (id int, name text, address text);
459                INSERT INTO test (id, name, address) VALUES (1, 'John Doe', '123 w Pine');
460                COMMIT;"));
461
462            {
463                let mut tx = try!(database.prepare(
464                    "INSERT INTO test (id, name, address) VALUES (?, ?, ?)"));
465                assert_eq!(tx.bind_parameter_count(), 3);
466                try!(tx.bind_int(1, 2));
467                try!(tx.bind_text(2, "Jane Doe"));
468                try!(tx.bind_text(3, "345 e Walnut"));
469                let mut results = tx.execute();
470                assert!(results.step().ok().unwrap().is_none());
471            }
472            assert_eq!(database.changes(), 1);
473
474            let mut q = try!(database.prepare("select * from test order by id"));
475            let mut rows = q.execute();
476            match rows.step() {
477                Ok(Some(ref mut row)) => {
478                    assert_eq!(row.get::<u32, i32>(0), 1);
479                    // TODO let name = q.get_text(1);
480                    // assert_eq!(name.as_slice(), "John Doe");
481                },
482                _ => panic!()
483            }
484
485            match rows.step() {
486                Ok(Some(ref mut row)) => {
487                    assert_eq!(row.get::<u32, i32>(0), 2);
488                    //TODO let addr = q.get_text(2);
489                    // assert_eq!(addr.as_slice(), "345 e Walnut");
490                },
491                _ => panic!()
492            }
493            Ok(())
494        }
495        match go() {
496            Ok(_) => (),
497            Err(e) => panic!("oops! {:?}", e)
498        }
499    }
500
501    fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
502        where F: FnMut(&mut ResultSet) -> T
503    {
504        let db = try!(DatabaseConnection::in_memory());
505        let mut s = try!(db.prepare(sql));
506        let mut rows = s.execute();
507        let x = f(&mut rows);
508        return Ok(x);
509    }
510
511    #[test]
512    fn named_rowindex() {
513        fn go() -> SqliteResult<(u32, i32)> {
514            let mut count = 0;
515            let mut sum = 0i32;
516
517            with_query("select 1 as col1
518                       union all
519                       select 2", |rows| {
520                loop {
521                    match rows.step() {
522                        Ok(Some(ref mut row)) => {
523                            count += 1;
524                            sum += row.column_int(0);
525                        },
526                        _ => break
527                    }
528                }
529                (count, sum)
530            })
531        }
532        assert_eq!(go(), Ok((2, 3)))
533    }
534
535    #[test]
536    fn err_with_detail() {
537        let io = || {
538            let mut conn = try!(DatabaseConnection::in_memory());
539            conn.exec("CREATE gobbledygook")
540        };
541
542        let go = || match io() {
543            Ok(_) => panic!(),
544            Err(oops) => {
545                format!("{:?}: {}: {}",
546                        oops.kind, oops.desc,
547                        oops.detail.unwrap())
548            }
549        };
550
551        let expected = "SQLITE_ERROR: sqlite3_exec: near \"gobbledygook\": syntax error";
552        assert_eq!(go(), expected.to_string())
553    }
554}