Roll convenient.rs into statement.rs.

This commit is contained in:
John Gallagher 2017-03-08 11:26:25 -05:00
parent 5bd7cb37c7
commit 6bd8516511
3 changed files with 112 additions and 129 deletions

View File

@ -1,121 +0,0 @@
use {Error, Result, Row, Statement};
use types::ToSql;
use statement::StatementCrateImpl;
impl<'conn> Statement<'conn> {
/// Execute an INSERT and return the ROWID.
///
/// # Note
///
/// This function is a convenience wrapper around `execute()` intended for queries that
/// insert a single item. It is possible to misuse this function in a way that it cannot
/// detect, such as by calling it on a statement which _updates_ a single item rather than
/// inserting one. Please don't do that.
///
/// # Failure
///
/// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
let changes = try!(self.execute(params));
match changes {
1 => Ok(self.last_insert_rowid()),
_ => Err(Error::StatementChangedRows(changes)),
}
}
/// Return `true` if a query in the SQL statement it executes returns one or more rows
/// and `false` if the SQL returns an empty set.
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
let mut rows = try!(self.query(params));
let exists = {
match rows.next() {
Some(_) => true,
None => false,
}
};
Ok(exists)
}
/// Convenience method to execute a query that is expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut rows = try!(self.query(params));
rows.get_expected_row().map(|r| f(&r))
}
}
#[cfg(test)]
mod test {
use {Connection, Error, Result};
#[test]
fn test_insert() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap();
match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
}
}
#[test]
fn test_insert_different_tables() {
// Test for https://github.com/jgallagher/rusqlite/issues/171
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r"
CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER);
")
.unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
}
#[test]
fn test_exists() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(1);
INSERT INTO foo VALUES(2);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[&1i32]).unwrap());
assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[&0i32]).unwrap());
}
#[test]
fn test_query_row() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap());
}
}

View File

@ -104,7 +104,6 @@ mod version;
mod transaction; mod transaction;
mod cache; mod cache;
mod error; mod error;
mod convenient;
mod raw_statement; mod raw_statement;
mod statement; mod statement;
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]

View File

@ -106,6 +106,26 @@ impl<'conn> Statement<'conn> {
self.execute_with_bound_parameters() self.execute_with_bound_parameters()
} }
/// Execute an INSERT and return the ROWID.
///
/// # Note
///
/// This function is a convenience wrapper around `execute()` intended for queries that
/// insert a single item. It is possible to misuse this function in a way that it cannot
/// detect, such as by calling it on a statement which _updates_ a single item rather than
/// inserting one. Please don't do that.
///
/// # Failure
///
/// Will return `Err` if no row is inserted or many rows are inserted.
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
let changes = try!(self.execute(params));
match changes {
1 => Ok(self.conn.last_insert_rowid()),
_ => Err(Error::StatementChangedRows(changes)),
}
}
/// Execute the prepared statement, returning a handle to the resulting rows. /// Execute the prepared statement, returning a handle to the resulting rows.
/// ///
/// Due to lifetime restricts, the rows handle returned by `query` does not /// Due to lifetime restricts, the rows handle returned by `query` does not
@ -309,6 +329,34 @@ impl<'conn> Statement<'conn> {
}) })
} }
/// Return `true` if a query in the SQL statement it executes returns one or more rows
/// and `false` if the SQL returns an empty set.
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
let mut rows = try!(self.query(params));
let exists = {
match rows.next() {
Some(_) => true,
None => false,
}
};
Ok(exists)
}
/// Convenience method to execute a query that is expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are ignored.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
{
let mut rows = try!(self.query(params));
rows.get_expected_row().map(|r| f(&r))
}
/// Consumes the statement. /// Consumes the statement.
/// ///
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors /// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors
@ -458,7 +506,6 @@ impl<'conn> Drop for Statement<'conn> {
// once pub(crate) is stable. // once pub(crate) is stable.
pub trait StatementCrateImpl<'conn> { pub trait StatementCrateImpl<'conn> {
fn new(conn: &'conn Connection, stmt: RawStatement) -> Self; fn new(conn: &'conn Connection, stmt: RawStatement) -> Self;
fn last_insert_rowid(&self) -> i64;
fn value_ref(&self, col: c_int) -> ValueRef; fn value_ref(&self, col: c_int) -> ValueRef;
fn step(&self) -> Result<bool>; fn step(&self) -> Result<bool>;
fn reset(&self) -> c_int; fn reset(&self) -> c_int;
@ -472,10 +519,6 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
} }
} }
fn last_insert_rowid(&self) -> i64 {
self.conn.last_insert_rowid()
}
fn value_ref(&self, col: c_int) -> ValueRef { fn value_ref(&self, col: c_int) -> ValueRef {
let raw = unsafe { self.stmt.ptr() }; let raw = unsafe { self.stmt.ptr() };
@ -533,8 +576,7 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use Connection; use {Connection, Error, Result};
use error::Error;
#[test] #[test]
fn test_execute_named() { fn test_execute_named() {
@ -671,4 +713,67 @@ mod test {
.unwrap(); .unwrap();
assert_eq!(result, "one"); assert_eq!(result, "one");
} }
#[test]
fn test_insert() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap();
match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
}
}
#[test]
fn test_insert_different_tables() {
// Test for https://github.com/jgallagher/rusqlite/issues/171
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r"
CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER);
")
.unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(),
1);
}
#[test]
fn test_exists() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(1);
INSERT INTO foo VALUES(2);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
assert!(stmt.exists(&[&1i32]).unwrap());
assert!(stmt.exists(&[&2i32]).unwrap());
assert!(!stmt.exists(&[&0i32]).unwrap());
}
#[test]
fn test_query_row() {
let db = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
CREATE TABLE foo(x INTEGER, y INTEGER);
INSERT INTO foo VALUES(1, 3);
INSERT INTO foo VALUES(2, 4);
END;";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
assert_eq!(3i64, y.unwrap());
}
} }