sqlite3/
core.rs

1//! A minimal safe interface to sqlite3's basic API.
2//!
3//! The basic sqlite3 API is discussed in the [sqlite intro][intro].
4//! To go beyond that, use the (unsafe) `ffi` module directly.
5//!
6//! [intro]: http://www.sqlite.org/cintro.html
7//!
8//! ```rust
9//! extern crate sqlite3;
10//! 
11//! use sqlite3::{
12//!     DatabaseConnection,
13//!     SqliteResult,
14//! };
15//! 
16//! fn convenience_exec() -> SqliteResult<DatabaseConnection> {
17//!     let mut conn = try!(DatabaseConnection::in_memory());
18//! 
19//!     try!(conn.exec("
20//!        create table items (
21//!                    id integer,
22//!                    description varchar(40),
23//!                    price integer
24//!                    )"));
25//! 
26//!     Ok(conn)
27//! }
28//! 
29//! fn typical_usage(conn: &mut DatabaseConnection) -> SqliteResult<String> {
30//!     {
31//!         let mut stmt = try!(conn.prepare(
32//!             "insert into items (id, description, price)
33//!            values (1, 'stuff', 10)"));
34//!         let mut results = stmt.execute();
35//!         match try!(results.step()) {
36//!             None => (),
37//!             Some(_) => panic!("row from insert?!")
38//!         };
39//!     }
40//!     assert_eq!(conn.changes(), 1);
41//!     assert_eq!(conn.last_insert_rowid(), 1);
42//!     {
43//!         let mut stmt = try!(conn.prepare(
44//!             "select * from items"));
45//!         let mut results = stmt.execute();
46//!         match results.step() {
47//!             Ok(Some(ref mut row1)) => {
48//!                 let id = row1.column_int(0);
49//!                 let desc_opt = row1.column_text(1).expect("desc_opt should be non-null");
50//!                 let price = row1.column_int(2);
51//! 
52//!                 assert_eq!(id, 1);
53//!                 assert_eq!(desc_opt, format!("stuff"));
54//!                 assert_eq!(price, 10);
55//! 
56//!                 Ok(format!("row: {}, {}, {}", id, desc_opt, price))
57//!             },
58//!             Err(oops) => panic!(oops),
59//!             Ok(None) => panic!("where did our row go?")
60//!         }
61//!     }
62//! }
63//! 
64//! pub fn main() {
65//!     match convenience_exec() {
66//!         Ok(ref mut db) => {
67//!             match typical_usage(db) {
68//!                 Ok(txt) => println!("item: {}", txt),
69//!                 Err(oops) => {
70//!                     panic!("error: {:?} msg: {}", oops,
71//!                            db.errmsg())
72//!                 }
73//!             }
74//!         },
75//!         Err(oops) => panic!(oops)
76//!     }
77//! }
78//! ```
79//!
80//! The `DatabaseConnection` and `PreparedStatment` structures are
81//! memory-safe versions of the sqlite3 connection and prepared
82//! statement structures. A `PreparedStatement` maintains mutable,
83//! and hence exclusive, reference to the database connection.
84//! Note the use of blocks avoid borrowing the connection more
85//! than once at a time.
86//!
87//! In addition:
88//!
89//!   - `ResultSet` represents, as a rust lifetime, all of the steps
90//!     of one execution of a statement. (*Ideally, it would be an
91//!     Iterator over `ResultRow`s, but the `Iterator::next()`
92//!     function has no lifetime parameter.*) Use of mutable
93//!     references ensures that its lifetime is subsumed by the
94//!     statement lifetime.  Its destructor resets the statement.
95//!
96//!   - `ResultRow` is a lifetime for access to the columns of one row.
97//!
98
99use enum_primitive::FromPrimitive;
100use libc::{c_int, c_char};
101use std::ffi as std_ffi;
102use std::mem;
103use std::ptr;
104use std::slice;
105use std::str;
106use std::ffi::CStr;
107use std::rc::Rc;
108use time::Duration;
109
110use self::SqliteOk::SQLITE_OK;
111use self::Step::{SQLITE_ROW, SQLITE_DONE};
112
113pub use super::{
114    SqliteError,
115    SqliteErrorCode,
116    SqliteResult,
117};
118
119pub use super::ColumnType;
120pub use super::ColumnType::SQLITE_NULL;
121
122use ffi; // TODO: move to sqlite3-sys crate
123
124
125/// Successful result
126///
127/// Use `SQLITE_OK as c_int` to decode return values from mod ffi.
128/// See SqliteResult, SqliteError for typical return code handling.
129enum_from_primitive! {
130    #[derive(Debug, PartialEq, Eq, Copy, Clone)]
131    #[allow(non_camel_case_types)]
132    #[allow(missing_docs)]
133    pub enum SqliteOk {
134        SQLITE_OK = 0
135    }
136}
137
138enum_from_primitive! {
139    #[derive(Debug, PartialEq, Eq)]
140    #[allow(non_camel_case_types)]
141    // TODO: use, test this
142    enum SqliteLogLevel {
143        SQLITE_NOTICE    = 27,
144        SQLITE_WARNING   = 28,
145    }
146}
147
148struct Database {
149    // not pub so that nothing outside this module
150    // interferes with the lifetime
151    handle: *mut ffi::sqlite3,
152}
153impl Drop for Database {
154    /// Release resources associated with connection.
155    ///
156    /// # Failure
157    ///
158    /// Fails if "the database connection is associated with
159    /// unfinalized prepared statements or unfinished sqlite3_backup
160    /// objects"[1] which the Rust memory model ensures is impossible
161    /// (barring bugs in the use of unsafe blocks in the implementation
162    /// of this library).
163    ///
164    /// [1]: http://www.sqlite.org/c3ref/close.html
165    fn drop(&mut self) {
166        // sqlite3_close_v2 is for gced languages.
167        let ok = unsafe { ffi::sqlite3_close(self.handle) };
168        assert_eq!(ok, SQLITE_OK as c_int);
169    }
170}
171
172/// A connection to a sqlite3 database.
173pub struct DatabaseConnection {
174    db: Rc<Database>,
175    
176    // whether to copy errmsg() to error detail
177    detailed: bool
178}
179
180
181
182/// Authorization to connect to database.
183pub trait Access {
184    /// Open a database connection.
185    ///
186    /// Whether or not an error occurs, allocate a handle and update
187    /// db to point to it.  return `SQLITE_OK as c_int` or set the
188    /// `errmsg` of the db handle and return a relevant result code.
189    fn open(self, db: *mut *mut ffi::sqlite3) -> c_int;
190}
191
192
193// why isn't this in std::option?
194fn maybe<T>(choice: bool, x: T) -> Option<T> {
195    if choice { Some(x) } else { None }
196}
197
198use std::ffi::NulError;
199impl From<NulError> for SqliteError {
200    fn from(_: NulError) -> SqliteError {
201        SqliteError{
202            kind: SqliteErrorCode::SQLITE_MISUSE,
203            desc: "Sql string contained an internal 0 byte",
204            detail: None
205        }
206    }
207}
208
209impl DatabaseConnection {
210    /// Given explicit access to a database, attempt to connect to it.
211    ///
212    /// Note `SqliteError` code is accompanied by (copy) of `sqlite3_errmsg()`.
213    pub fn new<A: Access>(access: A) -> SqliteResult<DatabaseConnection> {
214        let mut db = ptr::null_mut();
215        let result = access.open(&mut db);
216        match decode_result(result, "sqlite3_open_v2", Some(db)) {
217            Ok(()) => Ok(DatabaseConnection {
218                db: Rc::new(Database { handle: db}), 
219                detailed: true,
220            }),
221            Err(err) => {
222                // "Whether or not an error occurs when it is opened,
223                // resources associated with the database connection
224                // handle should be released by passing it to
225                // sqlite3_close() when it is no longer required."
226                unsafe { ffi::sqlite3_close(db) };
227
228                Err(err)
229            }
230        }
231    }
232
233    /// Opt out of copies of error message details.
234    pub fn ignore_detail(&mut self) {
235        self.detailed = false;
236    }
237
238
239    /// Create connection to an in-memory database.
240    ///
241    ///  - TODO: integrate sqlite3_errmsg()
242    pub fn in_memory() -> SqliteResult<DatabaseConnection> {
243        struct InMemory;
244        impl Access for InMemory {
245            fn open(self, db: *mut *mut ffi::sqlite3) -> c_int {
246                let c_memory = str_charstar(":memory:");
247                unsafe { ffi::sqlite3_open(c_memory.as_ptr(), db) }
248            }
249        }
250        DatabaseConnection::new(InMemory)
251    }
252
253    /// Prepare/compile an SQL statement.
254    pub fn prepare<'db:'st, 'st>(&'db self, sql: &str) -> SqliteResult<PreparedStatement> {
255        match self.prepare_with_offset(sql) {
256            Ok((cur, _)) => Ok(cur),
257            Err(e) => Err(e)
258        }
259    }
260
261    /// Prepare/compile an SQL statement and give offset to remaining text.
262    ///
263    /// *TODO: give caller a safe way to use the offset. Perhaps
264    /// return a &'x str?*
265    pub fn prepare_with_offset<'db:'st, 'st>(&'db self, sql: &str)
266                                    -> SqliteResult<(PreparedStatement, usize)> {
267        let mut stmt = ptr::null_mut();
268        let mut tail = ptr::null();
269        let z_sql = str_charstar(sql);
270        let n_byte = sql.len() as c_int;
271        let r = unsafe { ffi::sqlite3_prepare_v2(self.db.handle, z_sql.as_ptr(), n_byte, &mut stmt, &mut tail) };
272        match decode_result(r, "sqlite3_prepare_v2", maybe(self.detailed, self.db.handle)) {
273            Ok(()) => {
274                let offset = tail as usize - z_sql.as_ptr() as usize;
275                Ok((PreparedStatement { stmt: stmt , db: self.db.clone(), detailed: self.detailed }, offset))
276            },
277            Err(code) => Err(code)
278        }
279    }
280
281    /// Return a copy of the latest error message.
282    ///
283    /// Return `""` in case of ill-formed utf-8 or null.
284    ///
285    /// *TODO: represent error state in types: "If a prior API call
286    /// failed but the most recent API call succeeded, the return
287    /// value from sqlite3_errcode() is undefined."*
288    ///
289    /// cf `ffi::sqlite3_errmsg`.
290    pub fn errmsg(&mut self) -> String {
291        DatabaseConnection::_errmsg(self.db.handle)
292    }
293
294    fn _errmsg(db: *mut ffi::sqlite3) -> String {
295        let errmsg = unsafe { ffi::sqlite3_errmsg(db) };
296        // returning Option<String> doesn't seem worthwhile.
297        charstar_str(&(errmsg)).unwrap_or("").to_string()
298    }
299
300    /// One-Step Query Execution Interface
301    ///
302    /// cf [sqlite3_exec][exec]
303    /// [exec]: http://www.sqlite.org/c3ref/exec.html
304    ///
305    ///  - TODO: callback support?
306    ///  - TODO: errmsg support
307    pub fn exec(&mut self, sql: &str) -> SqliteResult<()> {
308        let c_sql = try!(std_ffi::CString::new(sql.as_bytes()));
309        let result = unsafe {
310            ffi::sqlite3_exec(self.db.handle, c_sql.as_ptr(), None,
311                              ptr::null_mut(), ptr::null_mut())
312        };
313        decode_result(result, "sqlite3_exec", maybe(self.detailed, self.db.handle))
314    }
315
316    /// Return the number of database rows that were changed or
317    /// inserted or deleted by the most recently completed SQL
318    /// statement.
319    ///
320    /// cf `sqlite3_changes`.
321    pub fn changes(&self) -> u64 {
322        let dbh = self.db.handle;
323        let count = unsafe { ffi::sqlite3_changes(dbh) };
324        count as u64
325    }
326
327    /// Set a busy timeout and clear any previously set handler.
328    /// If duration is zero or negative, turns off busy handler.
329    pub fn busy_timeout(&mut self, d: Duration) -> SqliteResult<()> {
330        let ms = d.num_milliseconds() as i32;
331        let result = unsafe { ffi::sqlite3_busy_timeout(self.db.handle, ms) };
332        decode_result(result, "sqlite3_busy_timeout", maybe(self.detailed, self.db.handle))
333    }
334
335    /// Return the rowid of the most recent successful INSERT into
336    /// a rowid table or virtual table.
337    ///
338    /// cf `sqlite3_last_insert_rowid`
339    pub fn last_insert_rowid(&self) -> i64 {
340        unsafe { ffi::sqlite3_last_insert_rowid(self.db.handle) }
341    }
342
343    /// Expose the underlying `sqlite3` struct pointer for use
344    /// with the `ffi` module.
345    pub unsafe fn expose(&mut self) -> *mut ffi::sqlite3 {
346        self.db.handle
347    }
348}
349
350
351/// Convert from sqlite3 API utf8 to rust str.
352fn charstar_str<'a>(utf_bytes: &'a *const c_char) -> Option<&'a str> {
353    if *utf_bytes == ptr::null() {
354        return None;
355    }
356    let c_str = unsafe { CStr::from_ptr(*utf_bytes) };
357    
358    Some( unsafe { str::from_utf8_unchecked(c_str.to_bytes()) } )
359}
360
361/// Convenience function to get a CString from a str
362#[inline(always)]
363pub fn str_charstar<'a>(s: &'a str) -> std_ffi::CString {
364    std_ffi::CString::new(s.as_bytes()).unwrap_or(std_ffi::CString::new("").unwrap())
365}
366
367/// A prepared statement.
368pub struct PreparedStatement {
369    db: Rc<Database>,
370    stmt: *mut ffi::sqlite3_stmt,
371    detailed: bool,
372}
373
374impl Drop for PreparedStatement {
375    fn drop(&mut self) {
376        unsafe {
377
378            // We ignore the return code from finalize because:
379
380            // "If If the most recent evaluation of statement S
381            // failed, then sqlite3_finalize(S) returns the
382            // appropriate error code"
383
384            // "The sqlite3_finalize(S) routine can be called at any
385            // point during the life cycle of prepared statement S"
386
387            ffi::sqlite3_finalize(self.stmt);
388        }
389    }
390}
391
392
393/// Type for picking out a bind parameter.
394/// 1-indexed
395pub type ParamIx = u16;
396
397impl PreparedStatement {
398    /// Begin executing a statement.
399    ///
400    /// An sqlite "row" only lasts until the next call to
401    /// `ffi::sqlite3_step()`, so `ResultSet` has a corresponding
402    /// lifetime constraint, which prevents it `ResultSet` from
403    /// implementing the `Iterator` trait. See the `Query` trait
404    /// for and `Iterator` over query results.
405    pub fn execute(&mut self) -> ResultSet {
406        ResultSet { statement: self }
407    }
408}
409
410/// A compiled prepared statement that may take parameters.
411/// **Note:** "The leftmost SQL parameter has an index of 1."[1]
412///
413/// [1]: http://www.sqlite.org/c3ref/bind_blob.html
414impl PreparedStatement {
415
416    /// Opt out of copies of error message details.
417    pub fn ignore_detail(&mut self) {
418        self.detailed = false;
419    }
420
421
422    fn detail_db(&mut self) -> Option<*mut ffi::sqlite3> {
423        if self.detailed {
424            let db = unsafe { ffi::sqlite3_db_handle(self.stmt) };
425            Some(db)
426        } else {
427            None
428        }
429    }
430
431    fn get_detail(&mut self) -> Option<String> {
432        self.detail_db().map(|db| DatabaseConnection::_errmsg(db))
433    }
434
435    /// Bind null to a statement parameter.
436    pub fn bind_null(&mut self, i: ParamIx) -> SqliteResult<()> {
437        let ix = i as c_int;
438        let r = unsafe { ffi::sqlite3_bind_null(self.stmt, ix ) };
439        decode_result(r, "sqlite3_bind_null", self.detail_db())
440    }
441
442    /// Bind an int to a statement parameter.
443    pub fn bind_int(&mut self, i: ParamIx, value: i32) -> SqliteResult<()> {
444        let ix = i as c_int;
445        let r = unsafe { ffi::sqlite3_bind_int(self.stmt, ix, value) };
446        decode_result(r, "sqlite3_bind_int", self.detail_db())
447    }
448
449    /// Bind an int64 to a statement parameter.
450    pub fn bind_int64(&mut self, i: ParamIx, value: i64) -> SqliteResult<()> {
451        let ix = i as c_int;
452        let r = unsafe { ffi::sqlite3_bind_int64(self.stmt, ix, value) };
453        decode_result(r, "sqlite3_bind_int64", self.detail_db())
454    }
455
456    /// Bind a double to a statement parameter.
457    pub fn bind_double(&mut self, i: ParamIx, value: f64) -> SqliteResult<()> {
458        let ix = i as c_int;
459        let r = unsafe { ffi::sqlite3_bind_double(self.stmt, ix, value) };
460        decode_result(r, "sqlite3_bind_double", self.detail_db())
461    }
462
463    /// Bind a (copy of a) str to a statement parameter.
464    ///
465    /// *TODO: support binding without copying strings, blobs*
466    pub fn bind_text(&mut self, i: ParamIx, value: &str) -> SqliteResult<()> {
467        let ix = i as c_int;
468        // SQLITE_TRANSIENT => SQLite makes a copy
469        let transient = unsafe { mem::transmute(-1 as isize) };
470        let c_value = str_charstar(value);
471        let len = value.len() as c_int;
472        let r = unsafe { ffi::sqlite3_bind_text(self.stmt, ix, c_value.as_ptr(), len, transient) };
473        decode_result(r, "sqlite3_bind_text", self.detail_db())
474    }
475
476    /// Bind a (copy of a) byte sequence to a statement parameter.
477    ///
478    /// *TODO: support binding without copying strings, blobs*
479    pub fn bind_blob(&mut self, i: ParamIx, value: &[u8]) -> SqliteResult<()> {
480        let ix = i as c_int;
481        // SQLITE_TRANSIENT => SQLite makes a copy
482        let transient = unsafe { mem::transmute(-1 as isize) };
483        let len = value.len() as c_int;
484        // from &[u8] to &[i8]
485        let val = unsafe { mem::transmute(value.as_ptr()) };
486        let r = unsafe { ffi::sqlite3_bind_blob(self.stmt, ix, val, len, transient) };
487        decode_result(r, "sqlite3_bind_blob", self.detail_db())
488    }
489
490    /// Clear all parameter bindings.
491    pub fn clear_bindings(&mut self) {
492        // We ignore the return value, since no return codes are documented.
493        unsafe { ffi::sqlite3_clear_bindings(self.stmt) };
494    }
495
496    /// Return the number of SQL parameters.
497    /// If parameters of the ?NNN form are used, there may be gaps in the list.
498    pub fn bind_parameter_count(&mut self) -> ParamIx {
499        let count = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
500        count as ParamIx
501    }
502
503    /// Expose the underlying `sqlite3_stmt` struct pointer for use
504    /// with the `ffi` module.
505    pub unsafe fn expose(&mut self) -> *mut ffi::sqlite3_stmt {
506        self.stmt
507    }
508    
509    /// Return the number of database rows that were changed or
510    /// inserted or deleted by this statement if it is the most
511    /// recently run on its database connection.
512    ///
513    /// cf `sqlite3_changes`.
514    pub fn changes(&self) -> u64 {
515        let dbh = self.db.handle;
516        let count = unsafe { ffi::sqlite3_changes(dbh) };
517        count as u64
518    }
519}
520
521
522/// Results of executing a `prepare()`d statement.
523pub struct ResultSet<'res> {
524    statement: &'res mut PreparedStatement,
525}
526
527enum_from_primitive! {
528    #[derive(Debug, PartialEq, Eq)]
529    #[allow(non_camel_case_types)]
530    enum Step {
531        SQLITE_ROW       = 100,
532        SQLITE_DONE      = 101,
533    }
534}
535
536impl<'res> Drop for ResultSet<'res> {
537    fn drop(&mut self) {
538
539        // We ignore the return code from reset because it has already
540        // been reported:
541        //
542        // "If the most recent call to sqlite3_step(S) for the prepared
543        // statement S indicated an error, then sqlite3_reset(S)
544        // returns an appropriate error code."
545        unsafe { ffi::sqlite3_reset(self.statement.stmt) };
546    }
547}
548
549
550impl<'res:'row, 'row> ResultSet<'res> {
551    /// Execute the next step of a prepared statement.
552    pub fn step(&'row mut self) -> SqliteResult<Option<ResultRow<'res, 'row>>> {
553        let result = unsafe { ffi::sqlite3_step(self.statement.stmt) };
554        match Step::from_i32(result) {
555            Some(SQLITE_ROW) => {
556                Ok(Some(ResultRow{ rows: self }))
557            },
558            Some(SQLITE_DONE) => Ok(None),
559            None => Err(error_result(result, "step", self.statement.get_detail()))
560        }
561    }
562}
563
564
565/// Access to columns of a row.
566pub struct ResultRow<'res:'row, 'row> {
567    rows: &'row mut ResultSet<'res>
568}
569
570/// Column index for accessing parts of a row.
571pub type ColIx = u32;
572
573/// Access to one row (step) of a result.
574///
575/// Note "These routines attempt to convert the value where appropriate."[1]
576/// and "The value returned by `sqlite3_column_type()` is only
577/// meaningful if no type conversions have occurred as described
578/// below. After a type conversion, the value returned by
579/// `sqlite3_column_type()` is undefined."[1]
580///
581/// [1]: http://www.sqlite.org/c3ref/column_blob.html
582impl<'res, 'row> ResultRow<'res, 'row> {
583
584    /// cf `sqlite3_column_count`
585    ///
586    /// *TODO: consider returning Option<uint>
587    /// "This routine returns 0 if pStmt is an SQL statement that does
588    /// not return data (for example an UPDATE)."*
589    pub fn column_count(&self) -> ColIx {
590        let stmt = self.rows.statement.stmt;
591        let result = unsafe { ffi::sqlite3_column_count(stmt) };
592        result as ColIx
593    }
594
595    /// Look up a column name and compute some function of it.
596    ///
597    /// Return `default` if there is no column `i`
598    ///
599    /// cf `sqlite_column_name`
600    pub fn with_column_name<T, F: Fn(&str) -> T>(&mut self, i: ColIx, default: T, f: F) -> T {
601        let stmt = self.rows.statement.stmt;
602        let n = i as c_int;
603        let result = unsafe { ffi::sqlite3_column_name(stmt, n) };
604        match charstar_str(&result) {
605            Some(name) => f(name),
606            None => default
607        }
608    }
609
610    /// Look up the type of a column.
611    ///
612    /// Return `SQLITE_NULL` if there is no such `col`.
613    pub fn column_type(&self, col: ColIx) -> ColumnType {
614        let stmt = self.rows.statement.stmt;
615        let i_col = col as c_int;
616        let result = unsafe { ffi::sqlite3_column_type(stmt, i_col) };
617        // fail on out-of-range result instead?
618        ColumnType::from_i32(result).unwrap_or(SQLITE_NULL)
619    }
620
621    /// Get `int` value of a column.
622    pub fn column_int(&self, col: ColIx) -> i32 {
623        let stmt = self.rows.statement.stmt;
624        let i_col = col as c_int;
625        unsafe { ffi::sqlite3_column_int(stmt, i_col) }
626    }
627
628    /// Get `int64` value of a column.
629    pub fn column_int64(&self, col: ColIx) -> i64 {
630        let stmt = self.rows.statement.stmt;
631        let i_col = col as c_int;
632        unsafe { ffi::sqlite3_column_int64(stmt, i_col) }
633    }
634
635    /// Get `f64` (aka double) value of a column.
636    pub fn column_double(&self, col: ColIx) -> f64 {
637        let stmt = self.rows.statement.stmt;
638        let i_col = col as c_int;
639        unsafe { ffi::sqlite3_column_double(stmt, i_col) }
640    }
641
642    /// Get `Option<String>` (aka text) value of a column.
643    pub fn column_text(&self, col: ColIx) -> Option<String> {
644        self.column_str(col).map(|s| s.to_string())
645    }
646
647    /// Get `Option<&str>` (aka text) value of a column.
648    pub fn column_str<'a>(&'a self, col: ColIx) -> Option<&'a str> {
649        self.column_slice(col).and_then(|slice| str::from_utf8(slice).ok() )
650    }
651
652    /// Get `Option<Vec<u8>>` (aka blob) value of a column.
653    pub fn column_blob(&self, col: ColIx) -> Option<Vec<u8>> {
654        self.column_slice(col).map(|bs| bs.to_vec())
655    }
656
657    /// Get `Option<&[u8]>` (aka blob) value of a column.
658    pub fn column_slice<'a>(&'a self, col: ColIx) -> Option<&'a [u8]> {
659        let stmt = self.rows.statement.stmt;
660        let i_col = col as c_int;
661        let bs = unsafe { ffi::sqlite3_column_blob(stmt, i_col) } as *const ::libc::c_uchar;
662        if bs == ptr::null() {
663            return None;
664        }
665        let len = unsafe { ffi::sqlite3_column_bytes(stmt, i_col) } as usize;
666        Some( unsafe { slice::from_raw_parts(bs, len) } )
667    }
668}
669
670
671/// Decode SQLite result as `SqliteResult`.
672///
673/// Note the use of the `Result<T, E>` pattern to distinguish errors in
674/// the type system.
675///
676/// # Panic
677///
678/// Panics if result is not a SQLITE error code.
679pub fn decode_result(
680    result: c_int,
681    desc: &'static str,
682    detail_db: Option<*mut ffi::sqlite3>,
683    ) -> SqliteResult<()> {
684    if result == SQLITE_OK as c_int {
685        Ok(())
686    } else {
687        let detail = detail_db.map(|db| DatabaseConnection::_errmsg(db));
688        Err(error_result(result, desc, detail))
689    }
690}
691
692
693fn error_result(
694    result: c_int,
695    desc: &'static str,
696    detail: Option<String>
697    ) -> SqliteError {
698    SqliteError {
699        kind: SqliteErrorCode::from_i32(result).unwrap(),
700        desc: desc,
701        detail: detail
702    }
703}
704
705
706#[cfg(test)]
707mod test_opening {
708    use super::{DatabaseConnection, SqliteResult};
709    use time::Duration;
710
711    #[test]
712    fn db_construct_typechecks() {
713        assert!(DatabaseConnection::in_memory().is_ok())
714    }
715
716    #[test]
717    fn db_busy_timeout() {
718        fn go() -> SqliteResult<()> {
719            let mut db = try!(DatabaseConnection::in_memory());
720            db.busy_timeout(Duration::seconds(2))
721        }
722        go().unwrap();
723    }
724
725    // TODO: _v2 with flags
726}
727
728
729#[cfg(test)]
730mod tests {
731    use super::{DatabaseConnection, SqliteResult, ResultSet};
732    use std::str;
733
734    #[test]
735    fn stmt_new_types() {
736        fn go() -> SqliteResult<()> {
737            let db = try!(DatabaseConnection::in_memory());
738            let res = db.prepare("select 1 + 1").map( |_s| () );
739            res
740        }
741        go().unwrap();
742    }
743
744
745    fn with_query<T, F>(sql: &str, mut f: F) -> SqliteResult<T>
746        where F: FnMut(&mut ResultSet) -> T
747    {
748        let db = try!(DatabaseConnection::in_memory());
749        let mut s = try!(db.prepare(sql));
750        let mut rows = s.execute();
751        Ok(f(&mut rows))
752    }
753
754    #[test]
755    fn query_two_rows() {
756        fn go() -> SqliteResult<(u32, i32)> {
757            let mut count = 0;
758            let mut sum = 0i32;
759
760            with_query("select 1
761                       union all
762                       select 2", |rows| {
763                loop {
764                    match rows.step() {
765                        Ok(Some(ref mut row)) => {
766                            count += 1;
767                            sum += row.column_int(0);
768                        },
769                        _ => break
770                    }
771                }
772                (count, sum)
773            })
774        }
775        assert_eq!(go(), Ok((2, 3)))
776    }
777
778    #[test]
779    fn query_null_string() {
780        with_query("select null", |rows| {
781            match rows.step() {
782                Ok(Some(ref mut row)) => {
783                    assert_eq!(row.column_text(0), None);
784                }
785                _ => { panic!("Expected a row"); }
786            }
787        }).unwrap();
788    }
789
790    #[test]
791    fn detailed_errors() {
792        let go = || -> SqliteResult<()> {
793            let db = try!(DatabaseConnection::in_memory());
794            try!(db.prepare("select bogus"));
795            Ok( () )
796        };
797        let err = go().err().unwrap();
798        assert_eq!(err.detail(), Some("no such column: bogus".to_string()))
799    }
800
801    #[test]
802    fn no_alloc_errors_db() {
803        let go = || {
804            let mut db = try!(DatabaseConnection::in_memory());
805            db.ignore_detail();
806            try!(db.prepare("select bogus"));
807            Ok( () )
808        };
809        let x: SqliteResult<()> = go();
810        let err = x.err().unwrap();
811        assert_eq!(err.detail(), None)
812    }
813
814    #[test]
815    fn no_alloc_errors_stmt() {
816        let db = DatabaseConnection::in_memory().unwrap();
817        let mut stmt = db.prepare("select 1").unwrap();
818        stmt.ignore_detail();
819        let oops = stmt.bind_text(3, "abc");
820        assert_eq!(oops.err().unwrap().detail(), None)
821    }
822
823    #[test]
824    fn non_utf8_str() {
825        let mut stmt = DatabaseConnection::in_memory().unwrap().prepare("SELECT x'4546FF'").unwrap();
826        let mut rows = stmt.execute();
827        let row = rows.step().unwrap().unwrap();
828        assert_eq!(row.column_str(0), None);
829        assert!(str::from_utf8(&[0x45u8, 0x46, 0xff]).is_err());
830    }
831
832}
833
834// Local Variables:
835// flycheck-rust-crate-root: "lib.rs"
836// End: