This commit is contained in:
gwenn 2017-04-07 19:43:24 +02:00
parent 4e5b64fbca
commit 08f96a678e
17 changed files with 327 additions and 214 deletions

View File

@ -206,10 +206,10 @@ impl<'a, 'b> Backup<'a, 'b> {
}; };
Ok(Backup { Ok(Backup {
phantom_from: PhantomData, phantom_from: PhantomData,
phantom_to: PhantomData, phantom_to: PhantomData,
b: b, b: b,
}) })
} }
/// Gets the progress of the backup as of the last call to `step`. /// Gets the progress of the backup as of the last call to `step`.

View File

@ -92,13 +92,14 @@ impl Connection {
if read_only { 0 } else { 1 }, if read_only { 0 } else { 1 },
&mut blob) &mut blob)
}; };
c.decode_result(rc).map(|_| { c.decode_result(rc)
Blob { .map(|_| {
conn: self, Blob {
blob: blob, conn: self,
pos: 0, blob: blob,
} pos: 0,
}) }
})
} }
} }
@ -159,9 +160,9 @@ impl<'conn> io::Read for Blob<'conn> {
self.conn self.conn
.decode_result(rc) .decode_result(rc)
.map(|_| { .map(|_| {
self.pos += n; self.pos += n;
n as usize n as usize
}) })
.map_err(|err| io::Error::new(io::ErrorKind::Other, err)) .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
} }
} }
@ -189,9 +190,9 @@ impl<'conn> io::Write for Blob<'conn> {
self.conn self.conn
.decode_result(rc) .decode_result(rc)
.map(|_| { .map(|_| {
self.pos += n; self.pos += n;
n as usize n as usize
}) })
.map_err(|err| io::Error::new(io::ErrorKind::Other, err)) .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
} }
@ -266,7 +267,8 @@ mod test {
fn test_blob() { fn test_blob() {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(4, blob.write(b"Clob").unwrap()); assert_eq!(4, blob.write(b"Clob").unwrap());
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10 assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10 assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
@ -274,7 +276,8 @@ mod test {
blob.reopen(rowid).unwrap(); blob.reopen(rowid).unwrap();
blob.close().unwrap(); blob.close().unwrap();
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true).unwrap(); blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
.unwrap();
let mut bytes = [0u8; 5]; let mut bytes = [0u8; 5];
assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"Clob5"); assert_eq!(&bytes, b"Clob5");
@ -314,7 +317,8 @@ mod test {
fn test_blob_in_bufreader() { fn test_blob_in_bufreader() {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap()); assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
blob.reopen(rowid).unwrap(); blob.reopen(rowid).unwrap();
@ -338,7 +342,8 @@ mod test {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
{ {
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob); let mut writer = BufWriter::new(blob);
// trying to write too much and then flush should fail // trying to write too much and then flush should fail
@ -357,7 +362,8 @@ mod test {
} }
{ {
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob); let mut writer = BufWriter::new(blob);
// trying to write_all too much should fail // trying to write_all too much should fail

View File

@ -280,7 +280,8 @@ mod test {
#[test] #[test]
fn test_connection_close() { fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap(); let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap(); conn.prepare_cached("SELECT * FROM sqlite_master;")
.unwrap();
conn.close().expect("connection not closed"); conn.close().expect("connection not closed");
} }

View File

@ -137,18 +137,14 @@ impl error::Error for Error {
match *self { match *self {
Error::SqliteFailure(ref err, None) => err.description(), Error::SqliteFailure(ref err, None) => err.description(),
Error::SqliteFailure(_, Some(ref s)) => s, Error::SqliteFailure(_, Some(ref s)) => s,
Error::SqliteSingleThreadedMode => { Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
"SQLite was compiled or configured for single-threaded use only"
}
Error::FromSqlConversionFailure(_, _, ref err) => err.description(), Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type", Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(), Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name", Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(), Error::NulError(ref err) => err.description(),
Error::InvalidPath(_) => "invalid path", Error::InvalidPath(_) => "invalid path",
Error::ExecuteReturnedResults => { Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
"execute returned results - did you mean to call query?"
}
Error::QueryReturnedNoRows => "query returned no rows", Error::QueryReturnedNoRows => "query returned no rows",
Error::InvalidColumnIndex(_) => "invalid column index", Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name", Error::InvalidColumnName(_) => "invalid column name",

View File

@ -119,9 +119,13 @@ unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint // if we're on the bundled version (since it's at least 3.17.0) and the normal constraint
// error code if not. // error code if not.
#[cfg(feature = "bundled")] #[cfg(feature = "bundled")]
fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT_FUNCTION } fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT_FUNCTION
}
#[cfg(not(feature = "bundled"))] #[cfg(not(feature = "bundled"))]
fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT } fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT
}
match *err { match *err {
Error::SqliteFailure(ref err, ref s) => { Error::SqliteFailure(ref err, ref s) => {
@ -154,7 +158,8 @@ impl<'a> ValueRef<'a> {
let s = CStr::from_ptr(text as *const c_char); let s = CStr::from_ptr(text as *const c_char);
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_value_text returned invalid UTF-8"); let s = s.to_str()
.expect("sqlite3_value_text returned invalid UTF-8");
ValueRef::Text(s) ValueRef::Text(s)
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {
@ -204,14 +209,17 @@ impl<'a> Context<'a> {
let arg = self.args[idx]; let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) }; let value = unsafe { ValueRef::from_value(arg) };
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => { FromSqlError::InvalidType => {
Error::InvalidFunctionParameterType(idx, value.data_type()) Error::InvalidFunctionParameterType(idx, value.data_type())
} }
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i), FromSqlError::OutOfRange(i) => {
FromSqlError::Other(err) => { Error::IntegralValueOutOfRange(idx as c_int,
i)
}
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err) Error::FromSqlConversionFailure(idx, value.data_type(), err)
} }
}) })
} }
/// Sets the auxilliary data associated with a particular parameter. See /// Sets the auxilliary data associated with a particular parameter. See
@ -304,7 +312,9 @@ impl Connection {
where F: FnMut(&Context) -> Result<T>, where F: FnMut(&Context) -> Result<T>,
T: ToSql T: ToSql
{ {
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) self.db
.borrow_mut()
.create_scalar_function(fn_name, n_arg, deterministic, x_func)
} }
/// Attach a user-defined aggregate function to this database connection. /// Attach a user-defined aggregate function to this database connection.
@ -720,17 +730,16 @@ mod test {
#[test] #[test]
fn test_sum() { fn test_sum() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.create_aggregate_function("my_sum", 1, true, Sum).unwrap(); db.create_aggregate_function("my_sum", 1, true, Sum)
.unwrap();
// sum should return NULL when given no columns (contrast with count below) // sum should return NULL when given no columns (contrast with count below)
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)) let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)).unwrap();
.unwrap();
assert!(result.is_none()); assert!(result.is_none());
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)) let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
.unwrap();
assert_eq!(4, result); assert_eq!(4, result);
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \ let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
@ -743,7 +752,8 @@ mod test {
#[test] #[test]
fn test_count() { fn test_count() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.create_aggregate_function("my_count", -1, true, Count).unwrap(); db.create_aggregate_function("my_count", -1, true, Count)
.unwrap();
// count should return 0 when given no columns (contrast with sum above) // count should return 0 when given no columns (contrast with sum above)
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
@ -751,8 +761,7 @@ mod test {
assert_eq!(result, 0); assert_eq!(result, 0);
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)) let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
.unwrap();
assert_eq!(2, result); assert_eq!(2, result);
} }
} }

View File

@ -222,12 +222,12 @@ impl Connection {
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> { pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
let c_path = try!(path_to_cstring(path.as_ref())); let c_path = try!(path_to_cstring(path.as_ref()));
InnerConnection::open_with_flags(&c_path, flags).map(|db| { InnerConnection::open_with_flags(&c_path, flags).map(|db| {
Connection { Connection {
db: RefCell::new(db), db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: Some(path.as_ref().to_path_buf()), path: Some(path.as_ref().to_path_buf()),
} }
}) })
} }
/// Open a new connection to an in-memory SQLite database. /// Open a new connection to an in-memory SQLite database.
@ -241,12 +241,12 @@ impl Connection {
pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> { pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> {
let c_memory = try!(str_to_cstring(":memory:")); let c_memory = try!(str_to_cstring(":memory:"));
InnerConnection::open_with_flags(&c_memory, flags).map(|db| { InnerConnection::open_with_flags(&c_memory, flags).map(|db| {
Connection { Connection {
db: RefCell::new(db), db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: None, path: None,
} }
}) })
} }
/// Convenience method to run multiple SQL statements (that cannot take any parameters). /// Convenience method to run multiple SQL statements (that cannot take any parameters).
@ -295,7 +295,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<c_int> { pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) self.prepare(sql)
.and_then(|mut stmt| stmt.execute(params))
} }
/// Convenience method to prepare and execute a single SQL statement with named parameter(s). /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
@ -317,7 +318,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> { pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) self.prepare(sql)
.and_then(|mut stmt| stmt.execute_named(params))
} }
/// Get the SQLite rowid of the most recent successful INSERT. /// Get the SQLite rowid of the most recent successful INSERT.
@ -406,7 +408,9 @@ impl Connection {
let mut stmt = try!(self.prepare(sql)); let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params)); let mut rows = try!(stmt.query(params));
rows.get_expected_row().map_err(E::from).and_then(|r| f(&r)) rows.get_expected_row()
.map_err(E::from)
.and_then(|r| f(&r))
} }
/// Convenience method to execute a query that is expected to return a single row. /// Convenience method to execute a query that is expected to return a single row.
@ -534,7 +538,9 @@ impl Connection {
dylib_path: P, dylib_path: P,
entry_point: Option<&str>) entry_point: Option<&str>)
-> Result<()> { -> Result<()> {
self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) self.db
.borrow_mut()
.load_extension(dylib_path.as_ref(), entry_point)
} }
/// Get access to the underlying SQLite database connection handle. /// Get access to the underlying SQLite database connection handle.
@ -849,7 +855,8 @@ impl InnerConnection {
&mut c_stmt, &mut c_stmt,
ptr::null_mut()) ptr::null_mut())
}; };
self.decode_result(r).map(|_| Statement::new(conn, RawStatement::new(c_stmt))) self.decode_result(r)
.map(|_| Statement::new(conn, RawStatement::new(c_stmt)))
} }
fn changes(&mut self) -> c_int { fn changes(&mut self) -> c_int {
@ -999,12 +1006,15 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(1, assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap()); db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
.unwrap());
assert_eq!(1, assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap()); db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
.unwrap());
assert_eq!(3i32, assert_eq!(3i32,
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
} }
#[test] #[test]
@ -1061,7 +1071,8 @@ mod test {
assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1); assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1); assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1);
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC").unwrap(); let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
.unwrap();
{ {
let mut rows = query.query(&[&4i32]).unwrap(); let mut rows = query.query(&[&4i32]).unwrap();
let mut v = Vec::<i32>::new(); let mut v = Vec::<i32>::new();
@ -1146,8 +1157,10 @@ mod test {
#[test] #[test]
fn test_last_insert_rowid() { fn test_last_insert_rowid() {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap(); .unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES")
.unwrap();
assert_eq!(db.last_insert_rowid(), 1); assert_eq!(db.last_insert_rowid(), 1);

View File

@ -25,7 +25,8 @@ impl<'conn> LoadExtensionGuard<'conn> {
/// Attempt to enable loading extensions. Loading extensions will be disabled when this /// Attempt to enable loading extensions. Loading extensions will be disabled when this
/// guard goes out of scope. Cannot be meaningfully nested. /// guard goes out of scope. Cannot be meaningfully nested.
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> { pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> {
conn.load_extension_enable().map(|_| LoadExtensionGuard { conn: conn }) conn.load_extension_enable()
.map(|_| LoadExtensionGuard { conn: conn })
} }
} }

View File

@ -28,22 +28,23 @@ impl<'stmt> Rows<'stmt> {
/// "streaming iterator". For a more natural interface, consider using `query_map` /// "streaming iterator". For a more natural interface, consider using `query_map`
/// or `query_and_then` instead, which return types that implement `Iterator`. /// or `query_and_then` instead, which return types that implement `Iterator`.
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> { pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt.and_then(|stmt| match stmt.step() { self.stmt
Ok(true) => { .and_then(|stmt| match stmt.step() {
Some(Ok(Row { Ok(true) => {
stmt: stmt, Some(Ok(Row {
phantom: PhantomData, stmt: stmt,
})) phantom: PhantomData,
} }))
Ok(false) => { }
Ok(false) => {
self.reset(); self.reset();
None None
} }
Err(err) => { Err(err) => {
self.reset(); self.reset();
Some(Err(err)) Some(Err(err))
} }
}) })
} }
} }
@ -89,10 +90,7 @@ impl<'stmt, T, F> MappedRowsCrateImpl<'stmt, T, F> for MappedRows<'stmt, F>
where F: FnMut(&Row) -> T where F: FnMut(&Row) -> T
{ {
fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> { fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
MappedRows { MappedRows { rows: rows, map: f }
rows: rows,
map: f,
}
} }
} }
@ -103,7 +101,9 @@ impl<'conn, T, F> Iterator for MappedRows<'conn, F>
fn next(&mut self) -> Option<Result<T>> { fn next(&mut self) -> Option<Result<T>> {
let map = &mut self.map; let map = &mut self.map;
self.rows.next().map(|row_result| row_result.map(|row| (map)(&row))) self.rows
.next()
.map(|row_result| row_result.map(|row| (map)(&row)))
} }
} }
@ -124,10 +124,7 @@ impl<'stmt, T, E, F> AndThenRowsCrateImpl<'stmt, T, E, F> for AndThenRows<'stmt,
where F: FnMut(&Row) -> result::Result<T, E> where F: FnMut(&Row) -> result::Result<T, E>
{ {
fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> { fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
AndThenRows { AndThenRows { rows: rows, map: f }
rows: rows,
map: f,
}
} }
} }
@ -139,10 +136,9 @@ impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let map = &mut self.map; let map = &mut self.map;
self.rows.next().map(|row_result| { self.rows
row_result.map_err(E::from) .next()
.and_then(|row| (map)(&row)) .map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
})
} }
} }
@ -182,12 +178,17 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
let value = self.stmt.value_ref(idx); let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()), FromSqlError::InvalidType => {
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), Error::InvalidColumnType(idx,
FromSqlError::Other(err) => { value.data_type())
}
FromSqlError::OutOfRange(i) => {
Error::IntegralValueOutOfRange(idx, i)
}
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err) Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
} }
}) })
} }
/// Return the number of columns in the current row. /// Return the number of columns in the current row.

View File

@ -402,14 +402,19 @@ impl<'conn> Statement<'conn> {
#[cfg(feature = "blob")] #[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(len) => { ToSqlOutput::ZeroBlob(len) => {
return self.conn return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) }); .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
} }
}; };
self.conn.decode_result(match value { self.conn
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) }, .decode_result(match value {
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) }, ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) }, ValueRef::Integer(i) => unsafe {
ValueRef::Text(s) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i)
},
ValueRef::Real(r) => unsafe {
ffi::sqlite3_bind_double(ptr, col, r)
},
ValueRef::Text(s) => unsafe {
let length = s.len(); let length = s.len();
if length > ::std::i32::MAX as usize { if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG ffi::SQLITE_TOOBIG
@ -423,7 +428,7 @@ impl<'conn> Statement<'conn> {
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor) ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
} }
}, },
ValueRef::Blob(b) => unsafe { ValueRef::Blob(b) => unsafe {
let length = b.len(); let length = b.len();
if length > ::std::i32::MAX as usize { if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG ffi::SQLITE_TOOBIG
@ -437,7 +442,7 @@ impl<'conn> Statement<'conn> {
ffi::SQLITE_TRANSIENT()) ffi::SQLITE_TRANSIENT())
} }
}, },
}) })
} }
fn execute_with_bound_parameters(&mut self) -> Result<c_int> { fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
@ -524,7 +529,8 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
}; };
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine. // sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8"); let s = s.to_str()
.expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s) ValueRef::Text(s)
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {
@ -570,15 +576,17 @@ mod test {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(), assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
.unwrap(),
1); 1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(), assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1); 1);
assert_eq!(3i32, assert_eq!(3i32,
db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x", db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)], &[(":x", &0i32)],
|r| r.get(0)) |r| r.get(0))
.unwrap()); .unwrap());
} }
@ -589,13 +597,14 @@ mod test {
INTEGER)"; INTEGER)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
.unwrap();
stmt.execute_named(&[(":name", &"one")]).unwrap(); stmt.execute_named(&[(":name", &"one")]).unwrap();
assert_eq!(1i32, assert_eq!(1i32,
db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name", db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")], &[(":name", &"one")],
|r| r.get(0)) |r| r.get(0))
.unwrap()); .unwrap());
} }
@ -608,7 +617,8 @@ mod test {
"#; "#;
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); let mut stmt = db.prepare("SELECT id FROM test where name = :name")
.unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap(); let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
let id: i32 = rows.next().unwrap().unwrap().get(0); let id: i32 = rows.next().unwrap().unwrap().get(0);
@ -624,7 +634,8 @@ mod test {
"#; "#;
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); let mut stmt = db.prepare("SELECT id FROM test where name = :name")
.unwrap();
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| { let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0); let id: i32 = row.get(0);
2 * id 2 * id
@ -676,7 +687,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)"; let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> = let result: Option<String> =
@ -691,7 +703,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)"; let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap(); stmt.execute_named(&[(":y", &"two")]).unwrap();
@ -704,15 +717,18 @@ mod test {
#[test] #[test]
fn test_insert() { fn test_insert() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap(); .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(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2); assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() { match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (), Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap(); let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
.unwrap();
match multi.insert(&[]).unwrap_err() { match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (), Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
@ -729,9 +745,15 @@ mod test {
") ")
.unwrap(); .unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(), assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1); 1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(), assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1); 1);
} }

View File

@ -100,13 +100,14 @@ impl<'conn> Transaction<'conn> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE", TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
}; };
conn.execute_batch(query).map(move |_| { conn.execute_batch(query)
Transaction { .map(move |_| {
conn: conn, Transaction {
drop_behavior: DropBehavior::Rollback, conn: conn,
committed: false, drop_behavior: DropBehavior::Rollback,
} committed: false,
}) }
})
} }
/// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested /// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
@ -216,15 +217,16 @@ impl<'conn> Savepoint<'conn> {
name: T) name: T)
-> Result<Savepoint> { -> Result<Savepoint> {
let name = name.into(); let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {}", name)).map(|_| { conn.execute_batch(&format!("SAVEPOINT {}", name))
Savepoint { .map(|_| {
conn: conn, Savepoint {
name: name, conn: conn,
depth: depth, name: name,
drop_behavior: DropBehavior::Rollback, depth: depth,
committed: false, drop_behavior: DropBehavior::Rollback,
} committed: false,
}) }
})
} }
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint> { fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint> {
@ -269,7 +271,8 @@ impl<'conn> Savepoint<'conn> {
fn commit_(&mut self) -> Result<()> { fn commit_(&mut self) -> Result<()> {
self.committed = true; self.committed = true;
self.conn.execute_batch(&format!("RELEASE {}", self.name)) self.conn
.execute_batch(&format!("RELEASE {}", self.name))
} }
/// A convenience method which rolls back a savepoint. /// A convenience method which rolls back a savepoint.
@ -279,7 +282,8 @@ impl<'conn> Savepoint<'conn> {
/// Unlike `Transaction`s, savepoints remain active after they have been rolled back, /// Unlike `Transaction`s, savepoints remain active after they have been rolled back,
/// and can be rolled back again or committed. /// and can be rolled back again or committed.
pub fn rollback(&mut self) -> Result<()> { pub fn rollback(&mut self) -> Result<()> {
self.conn.execute_batch(&format!("ROLLBACK TO {}", self.name)) self.conn
.execute_batch(&format!("ROLLBACK TO {}", self.name))
} }
/// Consumes the savepoint, committing or rolling back according to the current setting /// Consumes the savepoint, committing or rolling back according to the current setting
@ -549,7 +553,8 @@ mod test {
} }
fn assert_current_sum(x: i32, conn: &Connection) { fn assert_current_sum(x: i32, conn: &Connection) {
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap(); let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(x, i); assert_eq!(x, i);
} }
} }

View File

@ -19,10 +19,12 @@ impl ToSql for NaiveDate {
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone. /// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
impl FromSql for NaiveDate { impl FromSql for NaiveDate {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") { value
Ok(dt) => Ok(dt), .as_str()
Err(err) => Err(FromSqlError::Other(Box::new(err))), .and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
}) Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
} }
} }
@ -37,17 +39,19 @@ impl ToSql for NaiveTime {
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone. /// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
impl FromSql for NaiveTime { impl FromSql for NaiveTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value
let fmt = match s.len() { .as_str()
5 => "%H:%M", .and_then(|s| {
8 => "%H:%M:%S", let fmt = match s.len() {
_ => "%H:%M:%S%.f", 5 => "%H:%M",
}; 8 => "%H:%M:%S",
match NaiveTime::parse_from_str(s, fmt) { _ => "%H:%M:%S%.f",
Ok(dt) => Ok(dt), };
Err(err) => Err(FromSqlError::Other(Box::new(err))), match NaiveTime::parse_from_str(s, fmt) {
} Ok(dt) => Ok(dt),
}) Err(err) => Err(FromSqlError::Other(Box::new(err))),
}
})
} }
} }
@ -63,18 +67,20 @@ impl ToSql for NaiveDateTime {
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) /// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { .as_str()
"%Y-%m-%dT%H:%M:%S%.f" .and_then(|s| {
} else { let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%d %H:%M:%S%.f" "%Y-%m-%dT%H:%M:%S%.f"
}; } else {
"%Y-%m-%d %H:%M:%S%.f"
};
match NaiveDateTime::parse_from_str(s, fmt) { match NaiveDateTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt), Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
} }
}) })
} }
} }
@ -130,7 +136,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)")
.unwrap();
db db
} }
@ -138,11 +145,14 @@ mod test {
fn test_naive_date() { fn test_naive_date() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd(2016, 2, 23);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23", s); assert_eq!("2016-02-23", s);
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(date, t); assert_eq!(date, t);
} }
@ -150,11 +160,14 @@ mod test {
fn test_naive_time() { fn test_naive_time() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms(23, 56, 4);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("23:56:04", s); assert_eq!("23:56:04", s);
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(time, v); assert_eq!(time, v);
} }
@ -165,15 +178,20 @@ mod test {
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms(23, 56, 4);
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23T23:56:04", s); assert_eq!("2016-02-23T23:56:04", s);
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, v); assert_eq!(dt, v);
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS" db.execute("UPDATE foo set b = datetime(t)", &[])
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); .unwrap(); // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, hms); assert_eq!(dt, hms);
} }
@ -185,12 +203,15 @@ mod test {
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let utc = UTC.from_utc_datetime(&dt); let utc = UTC.from_utc_datetime(&dt);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23T23:56:04.789+00:00", s); assert_eq!("2016-02-23T23:56:04.789+00:00", s);
let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v1); assert_eq!(utc, v1);
let v2: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0)) let v2: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
@ -202,7 +223,8 @@ mod test {
assert_eq!(utc - Duration::milliseconds(789), v3); assert_eq!(utc - Duration::milliseconds(789), v3);
let v4: DateTime<UTC> = let v4: DateTime<UTC> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap(); db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v4); assert_eq!(utc, v4);
} }
@ -214,13 +236,16 @@ mod test {
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let local = Local.from_local_datetime(&dt).single().unwrap(); let local = Local.from_local_datetime(&dt).single().unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", &[&local]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&local])
.unwrap();
// Stored string should be in UTC // Stored string should be in UTC
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert!(s.ends_with("+00:00")); assert!(s.ends_with("+00:00"));
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(local, v); assert_eq!(local, v);
} }
} }

View File

@ -104,9 +104,9 @@ impl FromSql for f64 {
impl FromSql for bool { impl FromSql for bool {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| match i { i64::column_result(value).map(|i| match i {
0 => false, 0 => false,
_ => true, _ => true,
}) })
} }
} }
@ -164,7 +164,9 @@ mod test {
} }
for n in in_range { for n in in_range {
assert_eq!(*n, assert_eq!(*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)).unwrap().into()); db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
.unwrap()
.into());
} }
} }

View File

@ -119,7 +119,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)").unwrap(); db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)")
.unwrap();
db db
} }
@ -128,9 +129,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let v1234 = vec![1u8, 2, 3, 4]; let v1234 = vec![1u8, 2, 3, 4];
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234]).unwrap(); db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, v1234); assert_eq!(v, v1234);
} }
@ -139,9 +142,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let empty = vec![]; let empty = vec![];
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty]).unwrap(); db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, empty); assert_eq!(v, empty);
} }
@ -150,9 +155,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
.unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s); assert_eq!(from, s);
} }
@ -161,9 +168,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()])
.unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s); assert_eq!(from, s);
} }
@ -171,10 +180,12 @@ mod test {
fn test_value() { fn test_value() {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap(); db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
.unwrap();
assert_eq!(10i64, assert_eq!(10i64,
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
.unwrap());
} }
#[test] #[test]
@ -184,10 +195,13 @@ mod test {
let s = Some("hello, world!"); let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]); let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap(); .unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
.unwrap();
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap(); let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
.unwrap();
let mut rows = stmt.query(&[]).unwrap(); let mut rows = stmt.query(&[]).unwrap();
{ {
@ -232,9 +246,15 @@ mod test {
assert_eq!("text", row.get_checked::<i32, String>(1).unwrap()); assert_eq!("text", row.get_checked::<i32, String>(1).unwrap());
assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap()); assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap());
assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON); assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON);
assert!(row.get_checked::<i32, Option<c_int>>(4).unwrap().is_none()); assert!(row.get_checked::<i32, Option<c_int>>(4)
assert!(row.get_checked::<i32, Option<c_double>>(4).unwrap().is_none()); .unwrap()
assert!(row.get_checked::<i32, Option<String>>(4).unwrap().is_none()); .is_none());
assert!(row.get_checked::<i32, Option<c_double>>(4)
.unwrap()
.is_none());
assert!(row.get_checked::<i32, Option<String>>(4)
.unwrap()
.is_none());
// check some invalid types // check some invalid types

View File

@ -32,7 +32,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)")
.unwrap();
db db
} }
@ -46,9 +47,11 @@ mod test {
&[&data, &json.as_bytes()]) &[&data, &json.as_bytes()])
.unwrap(); .unwrap();
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, t); assert_eq!(data, t);
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, b); assert_eq!(data, b);
} }
} }

View File

@ -7,17 +7,22 @@ const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
impl ToSql for time::Timespec { impl ToSql for time::Timespec {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
let time_string = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string(); let time_string = time::at_utc(*self)
.strftime(SQLITE_DATETIME_FMT)
.unwrap()
.to_string();
Ok(ToSqlOutput::from(time_string)) Ok(ToSqlOutput::from(time_string))
} }
} }
impl FromSql for time::Timespec { impl FromSql for time::Timespec {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) { value
Ok(tm) => Ok(tm.to_timespec()), .as_str()
Err(err) => Err(FromSqlError::Other(Box::new(err))), .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
}) Ok(tm) => Ok(tm.to_timespec()),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
} }
} }
@ -28,7 +33,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
.unwrap();
db db
} }
@ -40,9 +46,11 @@ mod test {
sec: 10_000, sec: 10_000,
nsec: 0, nsec: 0,
}; };
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
.unwrap();
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, ts); assert_eq!(from, ts);
} }
} }

View File

@ -32,12 +32,12 @@ impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
impl<'a> ToSql for ToSqlOutput<'a> { impl<'a> ToSql for ToSqlOutput<'a> {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(match *self { Ok(match *self {
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v), ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)), ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
#[cfg(feature = "blob")] #[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i), ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
}) })
} }
} }

View File

@ -13,5 +13,6 @@ pub fn version_number() -> i32 {
/// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html). /// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html).
pub fn version() -> &'static str { pub fn version() -> &'static str {
let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) }; let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
cstr.to_str().expect("SQLite version string is not valid UTF8 ?!") cstr.to_str()
.expect("SQLite version string is not valid UTF8 ?!")
} }