diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..cca970f --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +rusqlite contributors (sorted alphabetically) +============================================= + +* [John Gallagher](https://github.com/jgallagher) +* [Marcus Klaas de Vries](https://github.com/marcusklaas) diff --git a/Cargo.toml b/Cargo.toml index 1aca7a3..1e20b79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusqlite" -version = "0.0.5" +version = "0.0.6" authors = ["John Gallagher "] description = "Ergonomic wrapper for SQLite" homepage = "https://github.com/jgallagher/rusqlite" diff --git a/Changelog.md b/Changelog.md index 8bd5da6..a907810 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# Version 0.0.6 (2014-01-10) + +* Updates to track latest rustc changes (1.0.0-alpha). +* Add `query_row_safe`, a `SqliteResult`-returning variant of `query_row`. + # Version 0.0.5 (2014-01-07) * Updates to track latest rustc changes (closure syntax). diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 3a39c47..62213a9 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -42,7 +42,7 @@ pub const SQLITE_NULL : c_int = 5; pub type SqliteDestructor = extern "C" fn(*mut c_void); pub fn SQLITE_TRANSIENT() -> SqliteDestructor { - unsafe { mem::transmute(-1i) } + unsafe { mem::transmute(-1is) } } pub fn code_to_str(code: c_int) -> &'static str { diff --git a/src/lib.rs b/src/lib.rs index a1bf52a..bc802f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ //! an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). //! //! ```rust +//! #![allow(unstable)] //! extern crate rusqlite; //! extern crate time; //! @@ -43,11 +44,12 @@ //! time_created: row.get(2), //! data: row.get(3) //! }; -//! println!("Found person {}", person); +//! println!("Found person {:?}", person); //! } //! } //! ``` #![feature(unsafe_destructor)] +#![allow(unstable)] extern crate libc; @@ -96,6 +98,12 @@ pub struct SqliteError { pub message: String, } +impl fmt::String for SqliteError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "SqliteError( code: {}, message: {} )", self.code, self.message) + } +} + impl SqliteError { fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> SqliteError { let message = if db.is_null() { @@ -210,7 +218,7 @@ impl SqliteConnection { /// } /// } /// ``` - pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult { + pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult { self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) } @@ -251,6 +259,35 @@ impl SqliteConnection { f(rows.next().expect("Query did not return a row").unwrap()) } + /// Convenience method to execute a query that is expected to return a single row. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{SqliteResult,SqliteConnection}; + /// fn preferred_locale(conn: &SqliteConnection) -> SqliteResult { + /// conn.query_row_safe("SELECT value FROM preferences WHERE name='locale'", &[], |row| { + /// row.get(0) + /// }) + /// } + /// ``` + /// + /// If the query returns more than one row, all rows except the first are ignored. + pub fn query_row_safe(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult + where F: FnOnce(SqliteRow) -> T { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query(params)); + + if let Some(row) = rows.next() { + row.map(f) + } else { + Err(SqliteError{ + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + }) + } + } + /// Prepare a SQL statement for execution. /// /// ## Example @@ -280,7 +317,7 @@ impl SqliteConnection { self.db.borrow_mut().decode_result(code) } - fn changes(&self) -> uint { + fn changes(&self) -> c_int { self.db.borrow_mut().changes() } } @@ -390,8 +427,8 @@ impl InnerSqliteConnection { }) } - fn changes(&mut self) -> uint { - unsafe{ ffi::sqlite3_changes(self.db) as uint } + fn changes(&mut self) -> c_int { + unsafe{ ffi::sqlite3_changes(self.db) } } } @@ -432,7 +469,7 @@ impl<'conn> SqliteStatement<'conn> { /// Ok(()) /// } /// ``` - pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult { + pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult { self.reset_if_needed(); unsafe { @@ -511,7 +548,7 @@ impl<'conn> SqliteStatement<'conn> { impl<'conn> fmt::Show for SqliteStatement<'conn> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Statement( conn: {}, stmt: {} )", self.conn, self.stmt) + write!(f, "Statement( conn: {:?}, stmt: {:?} )", self.conn, self.stmt) } } @@ -760,6 +797,33 @@ mod test { } } + #[test] + fn test_query_row_safe() { + let db = checked_memory_handle(); + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(1); + INSERT INTO foo VALUES(2); + INSERT INTO foo VALUES(3); + INSERT INTO foo VALUES(4); + END;"; + db.execute_batch(sql).unwrap(); + + assert_eq!(10i64, db.query_row_safe("SELECT SUM(x) FROM foo", &[], |r| { + r.get::(0) + }).unwrap()); + + let result = db.query_row_safe("SELECT x FROM foo WHERE x > 5", &[], |r| r.get::(0)); + let error = result.unwrap_err(); + + assert!(error.code == ffi::SQLITE_NOTICE); + assert!(error.message.as_slice() == "Query did not return a row"); + + let bad_query_result = db.query_row_safe("NOT A PROPER QUERY; test123", &[], |_| ()); + + assert!(bad_query_result.is_err()); + } + #[test] fn test_prepare_failures() { let db = checked_memory_handle(); @@ -796,7 +860,7 @@ mod test { assert_eq!(db.last_insert_rowid(), 1); let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap(); - for _ in range(0i, 9) { + for _ in range(0i32, 9) { stmt.execute(&[]).unwrap(); } assert_eq!(db.last_insert_rowid(), 10); diff --git a/src/types.rs b/src/types.rs index c4cbe47..b63c03a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -136,9 +136,15 @@ impl ToSql for Option { /// ## Example /// /// ```rust,no_run +/// #![allow(unstable)] +/// # extern crate libc; +/// # extern crate rusqlite; /// # use rusqlite::{SqliteConnection, SqliteResult}; /// # use rusqlite::types::{Null}; -/// fn insert_null(conn: &SqliteConnection) -> SqliteResult { +/// # use libc::{c_int}; +/// fn main() { +/// } +/// fn insert_null(conn: &SqliteConnection) -> SqliteResult { /// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null]) /// } /// ``` @@ -186,7 +192,7 @@ impl FromSql for Vec { let c_blob = ffi::sqlite3_column_blob(stmt, col); let len = ffi::sqlite3_column_bytes(stmt, col); - assert!(len >= 0); let len = len as uint; + assert!(len >= 0); let len = len as usize; Ok(Vec::from_raw_buf(mem::transmute(c_blob), len)) }