1use 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; enum_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 enum SqliteLogLevel {
143 SQLITE_NOTICE = 27,
144 SQLITE_WARNING = 28,
145 }
146}
147
148struct Database {
149 handle: *mut ffi::sqlite3,
152}
153impl Drop for Database {
154 fn drop(&mut self) {
166 let ok = unsafe { ffi::sqlite3_close(self.handle) };
168 assert_eq!(ok, SQLITE_OK as c_int);
169 }
170}
171
172pub struct DatabaseConnection {
174 db: Rc<Database>,
175
176 detailed: bool
178}
179
180
181
182pub trait Access {
184 fn open(self, db: *mut *mut ffi::sqlite3) -> c_int;
190}
191
192
193fn 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 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 unsafe { ffi::sqlite3_close(db) };
227
228 Err(err)
229 }
230 }
231 }
232
233 pub fn ignore_detail(&mut self) {
235 self.detailed = false;
236 }
237
238
239 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 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 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 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 charstar_str(&(errmsg)).unwrap_or("").to_string()
298 }
299
300 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 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 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 pub fn last_insert_rowid(&self) -> i64 {
340 unsafe { ffi::sqlite3_last_insert_rowid(self.db.handle) }
341 }
342
343 pub unsafe fn expose(&mut self) -> *mut ffi::sqlite3 {
346 self.db.handle
347 }
348}
349
350
351fn 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#[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
367pub 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 ffi::sqlite3_finalize(self.stmt);
388 }
389 }
390}
391
392
393pub type ParamIx = u16;
396
397impl PreparedStatement {
398 pub fn execute(&mut self) -> ResultSet {
406 ResultSet { statement: self }
407 }
408}
409
410impl PreparedStatement {
415
416 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 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 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 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 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 pub fn bind_text(&mut self, i: ParamIx, value: &str) -> SqliteResult<()> {
467 let ix = i as c_int;
468 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 pub fn bind_blob(&mut self, i: ParamIx, value: &[u8]) -> SqliteResult<()> {
480 let ix = i as c_int;
481 let transient = unsafe { mem::transmute(-1 as isize) };
483 let len = value.len() as c_int;
484 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 pub fn clear_bindings(&mut self) {
492 unsafe { ffi::sqlite3_clear_bindings(self.stmt) };
494 }
495
496 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 pub unsafe fn expose(&mut self) -> *mut ffi::sqlite3_stmt {
506 self.stmt
507 }
508
509 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
522pub 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 unsafe { ffi::sqlite3_reset(self.statement.stmt) };
546 }
547}
548
549
550impl<'res:'row, 'row> ResultSet<'res> {
551 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
565pub struct ResultRow<'res:'row, 'row> {
567 rows: &'row mut ResultSet<'res>
568}
569
570pub type ColIx = u32;
572
573impl<'res, 'row> ResultRow<'res, 'row> {
583
584 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 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 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 ColumnType::from_i32(result).unwrap_or(SQLITE_NULL)
619 }
620
621 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 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 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 pub fn column_text(&self, col: ColIx) -> Option<String> {
644 self.column_str(col).map(|s| s.to_string())
645 }
646
647 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 pub fn column_blob(&self, col: ColIx) -> Option<Vec<u8>> {
654 self.column_slice(col).map(|bs| bs.to_vec())
655 }
656
657 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
671pub 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 }
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