Merge pull request #11 from jgallagher/rust-1.0.0-alpha-changes

Rust 1.0.0 alpha changes, add `query_row_safe`
This commit is contained in:
John Gallagher 2015-01-10 21:24:36 -06:00
commit befe94494e
6 changed files with 92 additions and 12 deletions

5
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,5 @@
rusqlite contributors (sorted alphabetically)
=============================================
* [John Gallagher](https://github.com/jgallagher)
* [Marcus Klaas de Vries](https://github.com/marcusklaas)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rusqlite" name = "rusqlite"
version = "0.0.5" version = "0.0.6"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
homepage = "https://github.com/jgallagher/rusqlite" homepage = "https://github.com/jgallagher/rusqlite"

View File

@ -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) # Version 0.0.5 (2014-01-07)
* Updates to track latest rustc changes (closure syntax). * Updates to track latest rustc changes (closure syntax).

View File

@ -42,7 +42,7 @@ pub const SQLITE_NULL : c_int = 5;
pub type SqliteDestructor = extern "C" fn(*mut c_void); pub type SqliteDestructor = extern "C" fn(*mut c_void);
pub fn SQLITE_TRANSIENT() -> SqliteDestructor { pub fn SQLITE_TRANSIENT() -> SqliteDestructor {
unsafe { mem::transmute(-1i) } unsafe { mem::transmute(-1is) }
} }
pub fn code_to_str(code: c_int) -> &'static str { pub fn code_to_str(code: c_int) -> &'static str {

View File

@ -2,6 +2,7 @@
//! an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). //! an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres).
//! //!
//! ```rust //! ```rust
//! #![allow(unstable)]
//! extern crate rusqlite; //! extern crate rusqlite;
//! extern crate time; //! extern crate time;
//! //!
@ -43,11 +44,12 @@
//! time_created: row.get(2), //! time_created: row.get(2),
//! data: row.get(3) //! data: row.get(3)
//! }; //! };
//! println!("Found person {}", person); //! println!("Found person {:?}", person);
//! } //! }
//! } //! }
//! ``` //! ```
#![feature(unsafe_destructor)] #![feature(unsafe_destructor)]
#![allow(unstable)]
extern crate libc; extern crate libc;
@ -96,6 +98,12 @@ pub struct SqliteError {
pub message: String, 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 { impl SqliteError {
fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> SqliteError { fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> SqliteError {
let message = if db.is_null() { let message = if db.is_null() {
@ -210,7 +218,7 @@ impl SqliteConnection {
/// } /// }
/// } /// }
/// ``` /// ```
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult<uint> { pub fn execute(&self, sql: &str, params: &[&ToSql]) -> SqliteResult<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) 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()) 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<String> {
/// 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<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult<T>
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. /// Prepare a SQL statement for execution.
/// ///
/// ## Example /// ## Example
@ -280,7 +317,7 @@ impl SqliteConnection {
self.db.borrow_mut().decode_result(code) self.db.borrow_mut().decode_result(code)
} }
fn changes(&self) -> uint { fn changes(&self) -> c_int {
self.db.borrow_mut().changes() self.db.borrow_mut().changes()
} }
} }
@ -390,8 +427,8 @@ impl InnerSqliteConnection {
}) })
} }
fn changes(&mut self) -> uint { fn changes(&mut self) -> c_int {
unsafe{ ffi::sqlite3_changes(self.db) as uint } unsafe{ ffi::sqlite3_changes(self.db) }
} }
} }
@ -432,7 +469,7 @@ impl<'conn> SqliteStatement<'conn> {
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult<uint> { pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult<c_int> {
self.reset_if_needed(); self.reset_if_needed();
unsafe { unsafe {
@ -511,7 +548,7 @@ impl<'conn> SqliteStatement<'conn> {
impl<'conn> fmt::Show for SqliteStatement<'conn> { impl<'conn> fmt::Show for SqliteStatement<'conn> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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::<i64>(0)
}).unwrap());
let result = db.query_row_safe("SELECT x FROM foo WHERE x > 5", &[], |r| r.get::<i64>(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] #[test]
fn test_prepare_failures() { fn test_prepare_failures() {
let db = checked_memory_handle(); let db = checked_memory_handle();
@ -796,7 +860,7 @@ mod test {
assert_eq!(db.last_insert_rowid(), 1); assert_eq!(db.last_insert_rowid(), 1);
let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap(); let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap();
for _ in range(0i, 9) { for _ in range(0i32, 9) {
stmt.execute(&[]).unwrap(); stmt.execute(&[]).unwrap();
} }
assert_eq!(db.last_insert_rowid(), 10); assert_eq!(db.last_insert_rowid(), 10);

View File

@ -136,9 +136,15 @@ impl<T: ToSql> ToSql for Option<T> {
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// #![allow(unstable)]
/// # extern crate libc;
/// # extern crate rusqlite;
/// # use rusqlite::{SqliteConnection, SqliteResult}; /// # use rusqlite::{SqliteConnection, SqliteResult};
/// # use rusqlite::types::{Null}; /// # use rusqlite::types::{Null};
/// fn insert_null(conn: &SqliteConnection) -> SqliteResult<uint> { /// # use libc::{c_int};
/// fn main() {
/// }
/// fn insert_null(conn: &SqliteConnection) -> SqliteResult<c_int> {
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null]) /// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
/// } /// }
/// ``` /// ```
@ -186,7 +192,7 @@ impl FromSql for Vec<u8> {
let c_blob = ffi::sqlite3_column_blob(stmt, col); let c_blob = ffi::sqlite3_column_blob(stmt, col);
let len = ffi::sqlite3_column_bytes(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)) Ok(Vec::from_raw_buf(mem::transmute(c_blob), len))
} }