This commit is contained in:
gwenn 2018-10-28 08:51:02 +01:00
parent 03561e36fb
commit 1598d4bc30
16 changed files with 177 additions and 111 deletions

View File

@ -405,7 +405,8 @@ mod test {
DatabaseName::Attached("my_attached"), DatabaseName::Attached("my_attached"),
&mut dst, &mut dst,
DatabaseName::Main, DatabaseName::Main,
).unwrap(); )
.unwrap();
backup.step(-1).unwrap(); backup.step(-1).unwrap();
} }
@ -422,7 +423,8 @@ mod test {
DatabaseName::Attached("my_attached"), DatabaseName::Attached("my_attached"),
&mut dst, &mut dst,
DatabaseName::Main, DatabaseName::Main,
).unwrap(); )
.unwrap();
backup backup
.run_to_completion(5, Duration::from_millis(250), None) .run_to_completion(5, Duration::from_millis(250), None)
.unwrap(); .unwrap();

View File

@ -28,7 +28,8 @@
//! db.execute( //! db.execute(
//! "INSERT INTO test (content) VALUES (ZEROBLOB(10))", //! "INSERT INTO test (content) VALUES (ZEROBLOB(10))",
//! NO_PARAMS, //! NO_PARAMS,
//! ).unwrap(); //! )
//! .unwrap();
//! //!
//! let rowid = db.last_insert_rowid(); //! let rowid = db.last_insert_rowid();
//! let mut blob = db //! let mut blob = db
@ -36,7 +37,8 @@
//! .unwrap(); //! .unwrap();
//! //!
//! // Make sure to test that the number of bytes written matches what you expect; //! // Make sure to test that the number of bytes written matches what you expect;
//! // if you try to write too much, the data will be truncated to the size of the BLOB. //! // if you try to write too much, the data will be truncated to the size of the
//! // BLOB.
//! let bytes_written = blob.write(b"01234567").unwrap(); //! let bytes_written = blob.write(b"01234567").unwrap();
//! assert_eq!(bytes_written, 8); //! assert_eq!(bytes_written, 8);
//! //!
@ -171,7 +173,8 @@ impl<'conn> io::Read for Blob<'conn> {
.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))
} }
} }
@ -199,7 +202,8 @@ impl<'conn> io::Write for Blob<'conn> {
.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))
} }
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {

View File

@ -127,7 +127,8 @@ mod test {
let _ = db2 let _ = db2
.query_row("PRAGMA schema_version", NO_PARAMS, |row| { .query_row("PRAGMA schema_version", NO_PARAMS, |row| {
row.get_checked::<_, i32>(0) row.get_checked::<_, i32>(0)
}).expect("unexpected error"); })
.expect("unexpected error");
child.join().unwrap(); child.join().unwrap();
} }
@ -165,7 +166,8 @@ mod test {
let _ = db2 let _ = db2
.query_row("PRAGMA schema_version", NO_PARAMS, |row| { .query_row("PRAGMA schema_version", NO_PARAMS, |row| {
row.get_checked::<_, i32>(0) row.get_checked::<_, i32>(0)
}).expect("unexpected error"); })
.expect("unexpected error");
assert_eq!(CALLED.load(Ordering::Relaxed), true); assert_eq!(CALLED.load(Ordering::Relaxed), true);
child.join().unwrap(); child.join().unwrap();

View File

@ -269,7 +269,8 @@ mod test {
CREATE TABLE foo (x INT); CREATE TABLE foo (x INT);
INSERT INTO foo VALUES (1); INSERT INTO foo VALUES (1);
"#, "#,
).unwrap(); )
.unwrap();
let sql = "SELECT * FROM foo"; let sql = "SELECT * FROM foo";
@ -290,7 +291,8 @@ mod test {
ALTER TABLE foo ADD COLUMN y INT; ALTER TABLE foo ADD COLUMN y INT;
UPDATE foo SET y = 2; UPDATE foo SET y = 2;
"#, "#,
).unwrap(); )
.unwrap();
{ {
let mut stmt = db.prepare_cached(sql).unwrap(); let mut stmt = db.prepare_cached(sql).unwrap();

View File

@ -47,7 +47,8 @@
//! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')", //! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')",
//! NO_PARAMS, //! NO_PARAMS,
//! |row| row.get(0), //! |row| row.get(0),
//! ).unwrap(); //! )
//! .unwrap();
//! //!
//! assert!(is_match); //! assert!(is_match);
//! } //! }
@ -540,8 +541,10 @@ mod test {
let is_match = { let is_match = {
let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap()); let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap());
let text = ctx.get_raw(1).as_str().map_err(|e| let text = ctx
Error::UserFunctionError(e.into()))?; .get_raw(1)
.as_str()
.map_err(|e| Error::UserFunctionError(e.into()))?;
re.is_match(text) re.is_match(text)
}; };
@ -563,7 +566,8 @@ mod test {
INSERT INTO foo VALUES ('lXsi'); INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX'); INSERT INTO foo VALUES ('lisX');
END;", END;",
).unwrap(); )
.unwrap();
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary) db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary)
.unwrap(); .unwrap();
@ -593,7 +597,8 @@ mod test {
INSERT INTO foo VALUES ('lXsi'); INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX'); INSERT INTO foo VALUES ('lisX');
END;", END;",
).unwrap(); )
.unwrap();
// This implementation of a regexp scalar function uses a captured HashMap // This implementation of a regexp scalar function uses a captured HashMap
// to keep cached regular expressions around (even across multiple queries) // to keep cached regular expressions around (even across multiple queries)
@ -617,7 +622,8 @@ mod test {
let text = try!(ctx.get::<String>(1)); let text = try!(ctx.get::<String>(1));
Ok(regex.is_match(&text)) Ok(regex.is_match(&text))
}).unwrap(); })
.unwrap();
let result: Result<bool> = let result: Result<bool> =
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", NO_PARAMS, |r| { db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", NO_PARAMS, |r| {
@ -647,7 +653,8 @@ mod test {
} }
Ok(ret) Ok(ret)
}).unwrap(); })
.unwrap();
for &(expected, query) in &[ for &(expected, query) in &[
("", "SELECT my_concat()"), ("", "SELECT my_concat()"),

View File

@ -28,7 +28,8 @@
//! data BLOB //! data BLOB
//! )", //! )",
//! NO_PARAMS, //! NO_PARAMS,
//! ).unwrap(); //! )
//! .unwrap();
//! let me = Person { //! let me = Person {
//! id: 0, //! id: 0,
//! name: "Steven".to_string(), //! name: "Steven".to_string(),
@ -39,7 +40,8 @@
//! "INSERT INTO person (name, time_created, data) //! "INSERT INTO person (name, time_created, data)
//! VALUES (?1, ?2, ?3)", //! VALUES (?1, ?2, ?3)",
//! &[&me.name as &ToSql, &me.time_created, &me.data], //! &[&me.name as &ToSql, &me.time_created, &me.data],
//! ).unwrap(); //! )
//! .unwrap();
//! //!
//! let mut stmt = conn //! let mut stmt = conn
//! .prepare("SELECT id, name, time_created, data FROM person") //! .prepare("SELECT id, name, time_created, data FROM person")
@ -50,7 +52,8 @@
//! name: row.get(1), //! name: row.get(1),
//! time_created: row.get(2), //! time_created: row.get(2),
//! data: row.get(3), //! data: row.get(3),
//! }).unwrap(); //! })
//! .unwrap();
//! //!
//! for person in person_iter { //! for person in person_iter {
//! println!("Found person {:?}", person.unwrap()); //! println!("Found person {:?}", person.unwrap());
@ -83,7 +86,7 @@ use std::ptr;
use std::result; use std::result;
use std::str; use std::str;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT}; use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::{Once, ONCE_INIT, Arc, Mutex}; use std::sync::{Arc, Mutex, Once, ONCE_INIT};
use cache::StatementCache; use cache::StatementCache;
use error::{error_from_handle, error_from_sqlite_code}; use error::{error_from_handle, error_from_sqlite_code};
@ -576,8 +579,8 @@ impl Connection {
self.db.borrow().db() self.db.borrow().db()
} }
/// Get access to a handle that can be used to interrupt long running queries /// Get access to a handle that can be used to interrupt long running
/// from another thread. /// queries from another thread.
pub fn get_interrupt_handle(&self) -> InterruptHandle { pub fn get_interrupt_handle(&self) -> InterruptHandle {
self.db.borrow().get_interrupt_handle() self.db.borrow().get_interrupt_handle()
} }
@ -892,8 +895,10 @@ impl InnerConnection {
} }
self.remove_hooks(); self.remove_hooks();
let mut shared_handle = self.interrupt_lock.lock().unwrap(); let mut shared_handle = self.interrupt_lock.lock().unwrap();
assert!(!shared_handle.is_null(), assert!(
"Bug: Somehow interrupt_lock was cleared before the DB was closed"); !shared_handle.is_null(),
"Bug: Somehow interrupt_lock was cleared before the DB was closed"
);
unsafe { unsafe {
let r = ffi::sqlite3_close(self.db); let r = ffi::sqlite3_close(self.db);
// Need to use _raw because _guard has a reference out, and // Need to use _raw because _guard has a reference out, and
@ -1116,7 +1121,8 @@ mod test {
" "
BEGIN; CREATE TABLE foo(x INTEGER); BEGIN; CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(42); END;", INSERT INTO foo VALUES(42); END;",
).expect("create temp db"); )
.expect("create temp db");
let mut db1 = Connection::open(&path).unwrap(); let mut db1 = Connection::open(&path).unwrap();
let mut db2 = Connection::open(&path).unwrap(); let mut db2 = Connection::open(&path).unwrap();
@ -1530,19 +1536,21 @@ mod test {
db.execute_batch("CREATE TABLE dummy(id)").unwrap(); db.execute_batch("CREATE TABLE dummy(id)").unwrap();
let interrupt_handle = db.get_interrupt_handle(); let interrupt_handle = db.get_interrupt_handle();
// generate an arbitrary query which will be very slow to execute. // generate an arbitrary query which will be very slow to execute.
let sql = format!("{};", let sql = format!(
(0..100_000).into_iter() "{};",
(0..100_000)
.into_iter()
.map(|i| format!("INSERT INTO dummy(id) VALUES({})", i)) .map(|i| format!("INSERT INTO dummy(id) VALUES({})", i))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(";\n")); .join(";\n")
);
// Do this on the main thread to minimize the amount of time spent // Do this on the main thread to minimize the amount of time spent
// when interrupt won't do anything (because we haven't started // when interrupt won't do anything (because we haven't started
// executing the query). // executing the query).
let c_sql = str_to_cstring(&sql).unwrap(); let c_sql = str_to_cstring(&sql).unwrap();
let joiner = thread::spawn(move || { let joiner = thread::spawn(move || unsafe {
unsafe {
let raw_db = db.db.borrow().db; let raw_db = db.db.borrow().db;
let r = ffi::sqlite3_exec( let r = ffi::sqlite3_exec(
raw_db, raw_db,
@ -1552,7 +1560,6 @@ mod test {
ptr::null_mut(), ptr::null_mut(),
); );
db.decode_result(r) db.decode_result(r)
}
}); });
// Try a few times to make sure we don't catch it too early. // Try a few times to make sure we don't catch it too early.
@ -1603,7 +1610,12 @@ mod test {
let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)").unwrap(); let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)").unwrap();
for (i, v) in vals.iter().enumerate() { for (i, v) in vals.iter().enumerate() {
let i_to_insert = i as i64; let i_to_insert = i as i64;
assert_eq!(insert_stmt.execute(&[&i_to_insert as &dyn ToSql, &v]).unwrap(), 1); assert_eq!(
insert_stmt
.execute(&[&i_to_insert as &dyn ToSql, &v])
.unwrap(),
1
);
} }
let mut query = db.prepare("SELECT i, x FROM foo").unwrap(); let mut query = db.prepare("SELECT i, x FROM foo").unwrap();
@ -1729,7 +1741,8 @@ mod test {
let results: CustomResult<Vec<String>> = query let results: CustomResult<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| { .query_and_then(NO_PARAMS, |row| {
row.get_checked(1).map_err(CustomError::Sqlite) row.get_checked(1).map_err(CustomError::Sqlite)
}).unwrap() })
.unwrap()
.collect(); .collect();
assert_eq!(results.unwrap().concat(), "hello, world!"); assert_eq!(results.unwrap().concat(), "hello, world!");
@ -1751,7 +1764,8 @@ mod test {
let bad_type: CustomResult<Vec<f64>> = query let bad_type: CustomResult<Vec<f64>> = query
.query_and_then(NO_PARAMS, |row| { .query_and_then(NO_PARAMS, |row| {
row.get_checked(1).map_err(CustomError::Sqlite) row.get_checked(1).map_err(CustomError::Sqlite)
}).unwrap() })
.unwrap()
.collect(); .collect();
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
@ -1762,7 +1776,8 @@ mod test {
let bad_idx: CustomResult<Vec<String>> = query let bad_idx: CustomResult<Vec<String>> = query
.query_and_then(NO_PARAMS, |row| { .query_and_then(NO_PARAMS, |row| {
row.get_checked(3).map_err(CustomError::Sqlite) row.get_checked(3).map_err(CustomError::Sqlite)
}).unwrap() })
.unwrap()
.collect(); .collect();
match bad_idx.unwrap_err() { match bad_idx.unwrap_err() {
@ -1846,7 +1861,8 @@ mod test {
db.query_row("SELECT * FROM foo", NO_PARAMS, |r| { db.query_row("SELECT * FROM foo", NO_PARAMS, |r| {
assert_eq!(2, r.column_count()) assert_eq!(2, r.column_count())
}).unwrap(); })
.unwrap();
} }
} }
} }

View File

@ -141,10 +141,9 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// ///
/// * If the underlying SQLite column type is not a valid type as a /// * If the underlying SQLite column type is not a valid type as a
/// source for `T` /// source for `T`
/// * If the underlying SQLite integral value is /// * If the underlying SQLite integral value is outside the range
/// outside the range representable by `T` /// representable by `T`
/// * If `idx` is outside the range of columns in the /// * If `idx` is outside the range of columns in the returned query
/// returned query
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T { pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
self.get_checked(idx).unwrap() self.get_checked(idx).unwrap()
} }
@ -175,9 +174,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err) Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
} }
#[cfg(feature = "i128_blob")] #[cfg(feature = "i128_blob")]
FromSqlError::InvalidI128Size(_) => { FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
Error::InvalidColumnType(idx, value.data_type())
}
}) })
} }

View File

@ -697,7 +697,8 @@ mod test {
"SELECT SUM(x) FROM foo WHERE x > :x", "SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)], &[(":x", &0i32)],
|r| r.get(0) |r| r.get(0)
).unwrap() )
.unwrap()
); );
} }
@ -719,7 +720,8 @@ mod test {
"SELECT COUNT(*) FROM test WHERE name = :name", "SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")], &[(":name", &"one")],
|r| r.get(0) |r| r.get(0)
).unwrap() )
.unwrap()
); );
} }
@ -757,7 +759,8 @@ mod test {
.query_map_named(&[(":name", &"one")], |row| { .query_map_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0); let id: i32 = row.get(0);
2 * id 2 * id
}).unwrap(); })
.unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap(); let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id); assert_eq!(2, doubled_id);
@ -784,7 +787,8 @@ mod test {
} else { } else {
Err(Error::SqliteSingleThreadedMode) Err(Error::SqliteSingleThreadedMode)
} }
}).unwrap(); })
.unwrap();
// first row should be Ok // first row should be Ok
let doubled_id: i32 = rows.next().unwrap().unwrap(); let doubled_id: i32 = rows.next().unwrap().unwrap();
@ -812,7 +816,8 @@ mod test {
let result: Option<String> = db let result: Option<String> = db
.query_row("SELECT y FROM test WHERE x = 'one'", NO_PARAMS, |row| { .query_row("SELECT y FROM test WHERE x = 'one'", NO_PARAMS, |row| {
row.get(0) row.get(0)
}).unwrap(); })
.unwrap();
assert!(result.is_none()); assert!(result.is_none());
} }
@ -831,7 +836,8 @@ mod test {
let result: String = db let result: String = db
.query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| { .query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| {
row.get(0) row.get(0)
}).unwrap(); })
.unwrap();
assert_eq!(result, "one"); assert_eq!(result, "one");
} }
@ -867,7 +873,8 @@ mod test {
CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER); CREATE TABLE bar(x INTEGER);
", ",
).unwrap(); )
.unwrap();
assert_eq!( assert_eq!(
db.prepare("INSERT INTO foo VALUES (10)") db.prepare("INSERT INTO foo VALUES (10)")

View File

@ -236,7 +236,8 @@ mod test {
let v4: DateTime<Utc> = db let v4: DateTime<Utc> = db
.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", NO_PARAMS, |r| { .query_row("SELECT '2016-02-23 23:56:04.789+00:00'", NO_PARAMS, |r| {
r.get(0) r.get(0)
}).unwrap(); })
.unwrap();
assert_eq!(utc, v4); assert_eq!(utc, v4);
} }

View File

@ -28,8 +28,9 @@ impl fmt::Display for FromSqlError {
FromSqlError::InvalidType => write!(f, "Invalid type"), FromSqlError::InvalidType => write!(f, "Invalid type"),
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i), FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
#[cfg(feature = "i128_blob")] #[cfg(feature = "i128_blob")]
FromSqlError::InvalidI128Size(s) => FromSqlError::InvalidI128Size(s) => {
write!(f, "Cannot read 128bit value out of {} byte blob", s), write!(f, "Cannot read 128bit value out of {} byte blob", s)
}
FromSqlError::Other(ref err) => err.fmt(f), FromSqlError::Other(ref err) => err.fmt(f),
} }
} }
@ -41,8 +42,7 @@ impl Error for FromSqlError {
FromSqlError::InvalidType => "invalid type", FromSqlError::InvalidType => "invalid type",
FromSqlError::OutOfRange(_) => "value out of range", FromSqlError::OutOfRange(_) => "value out of range",
#[cfg(feature = "i128_blob")] #[cfg(feature = "i128_blob")]
FromSqlError::InvalidI128Size(_) => FromSqlError::InvalidI128Size(_) => "unexpected blob size for 128bit value",
"unexpected blob size for 128bit value",
FromSqlError::Other(ref err) => err.description(), FromSqlError::Other(ref err) => err.description(),
} }
} }

View File

@ -46,7 +46,8 @@
//! } //! }
//! } //! }
//! //!
//! # // Prevent this doc test from being wrapped in a `fn main()` so that it will compile. //! # // Prevent this doc test from being wrapped in a `fn main()` so that it
//! # // will compile.
//! # fn main() {} //! # fn main() {}
//! ``` //! ```
//! //!
@ -239,7 +240,8 @@ mod test {
db.execute( db.execute(
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)", "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
NO_PARAMS, NO_PARAMS,
).unwrap(); )
.unwrap();
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap(); let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
let mut rows = stmt.query(NO_PARAMS).unwrap(); let mut rows = stmt.query(NO_PARAMS).unwrap();
@ -354,7 +356,8 @@ mod test {
db.execute( db.execute(
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)", "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
NO_PARAMS, NO_PARAMS,
).unwrap(); )
.unwrap();
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap(); let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
let mut rows = stmt.query(NO_PARAMS).unwrap(); let mut rows = stmt.query(NO_PARAMS).unwrap();

View File

@ -20,7 +20,8 @@ impl FromSql for Value {
ValueRef::Text(s) => serde_json::from_str(s), ValueRef::Text(s) => serde_json::from_str(s),
ValueRef::Blob(b) => serde_json::from_slice(b), ValueRef::Blob(b) => serde_json::from_slice(b),
_ => return Err(FromSqlError::InvalidType), _ => return Err(FromSqlError::InvalidType),
}.map_err(|err| FromSqlError::Other(Box::new(err))) }
.map_err(|err| FromSqlError::Other(Box::new(err)))
} }
} }
@ -46,7 +47,8 @@ mod test {
db.execute( db.execute(
"INSERT INTO foo (t, b) VALUES (?, ?)", "INSERT INTO foo (t, b) VALUES (?, ?)",
&[&data as &ToSql, &json.as_bytes()], &[&data as &ToSql, &json.as_bytes()],
).unwrap(); )
.unwrap();
let t: serde_json::Value = db let t: serde_json::Value = db
.query_row("SELECT t FROM foo", NO_PARAMS, |r| r.get(0)) .query_row("SELECT t FROM foo", NO_PARAMS, |r| r.get(0))

View File

@ -25,7 +25,8 @@ impl FromSql for time::Timespec {
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY) time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
.or_else(|_| Err(FromSqlError::Other(Box::new(err)))) .or_else(|_| Err(FromSqlError::Other(Box::new(err))))
}) })
}).map(|tm| tm.to_timespec()) })
.map(|tm| tm.to_timespec())
} }
} }

View File

@ -207,27 +207,37 @@ mod test {
#[cfg(feature = "i128_blob")] #[cfg(feature = "i128_blob")]
#[test] #[test]
fn test_i128() { fn test_i128() {
use {Connection, NO_PARAMS};
use std::i128; use std::i128;
use {Connection, NO_PARAMS};
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)").unwrap(); db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
db.execute(" .unwrap();
db.execute(
"
INSERT INTO foo(i128, desc) VALUES INSERT INTO foo(i128, desc) VALUES
(?, 'zero'), (?, 'zero'),
(?, 'neg one'), (?, 'neg two'), (?, 'neg one'), (?, 'neg two'),
(?, 'pos one'), (?, 'pos two'), (?, 'pos one'), (?, 'pos two'),
(?, 'min'), (?, 'max')", (?, 'min'), (?, 'max')",
&[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX] &[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
).unwrap(); )
.unwrap();
let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC").unwrap(); let mut stmt = db
.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")
.unwrap();
let res = stmt.query_map( let res = stmt
NO_PARAMS, .query_map(NO_PARAMS, |row| {
|row| (row.get::<_, i128>(0), row.get::<_, String>(1)) (row.get::<_, i128>(0), row.get::<_, String>(1))
).unwrap().collect::<Result<Vec<_>, _>>().unwrap(); })
.unwrap()
.collect::<Result<Vec<_>, _>>()
.unwrap();
assert_eq!(res, &[ assert_eq!(
res,
&[
(i128::MIN, "min".to_owned()), (i128::MIN, "min".to_owned()),
(-2, "neg two".to_owned()), (-2, "neg two".to_owned()),
(-1, "neg one".to_owned()), (-1, "neg one".to_owned()),
@ -235,6 +245,7 @@ mod test {
(1, "pos one".to_owned()), (1, "pos one".to_owned()),
(2, "pos two".to_owned()), (2, "pos two".to_owned()),
(i128::MAX, "max".to_owned()), (i128::MAX, "max".to_owned()),
]); ]
);
} }
} }

View File

@ -379,7 +379,8 @@ mod test {
.prepare( .prepare(
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \ "SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
v1.rowid < v2.rowid", v1.rowid < v2.rowid",
).unwrap(); )
.unwrap();
let mut rows = s.query(NO_PARAMS).unwrap(); let mut rows = s.query(NO_PARAMS).unwrap();
let row = rows.next().unwrap().unwrap(); let row = rows.next().unwrap().unwrap();

View File

@ -2,9 +2,11 @@
//! //!
//! Follow these steps to create your own virtual table: //! Follow these steps to create your own virtual table:
//! 1. Write implemenation of `VTab` and `VTabCursor` traits. //! 1. Write implemenation of `VTab` and `VTabCursor` traits.
//! 2. Create an instance of the `Module` structure specialized for `VTab` impl. from step 1. //! 2. Create an instance of the `Module` structure specialized for `VTab` impl.
//! from step 1.
//! 3. Register your `Module` structure using `Connection.create_module`. //! 3. Register your `Module` structure using `Connection.create_module`.
//! 4. Run a `CREATE VIRTUAL TABLE` command that specifies the new module in the `USING` clause. //! 4. Run a `CREATE VIRTUAL TABLE` command that specifies the new module in the
//! `USING` clause.
//! //!
//! (See [SQLite doc](http://sqlite.org/vtab.html)) //! (See [SQLite doc](http://sqlite.org/vtab.html))
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
@ -108,8 +110,9 @@ pub fn read_only_module<T: CreateVTab>(version: c_int) -> Module<T> {
/// ///
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations). /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
pub fn eponymous_only_module<T: VTab>(version: c_int) -> Module<T> { pub fn eponymous_only_module<T: VTab>(version: c_int) -> Module<T> {
// A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method // A virtual table is eponymous if its xCreate method is the exact same function
// For eponymous-only virtual tables, the xCreate method is NULL // as the xConnect method For eponymous-only virtual tables, the xCreate
// method is NULL
let ffi_module = ffi::sqlite3_module { let ffi_module = ffi::sqlite3_module {
iVersion: version, iVersion: version,
xCreate: None, xCreate: None,
@ -152,10 +155,11 @@ impl VTabConnection {
/// ///
/// # Warning /// # Warning
/// ///
/// You should not need to use this function. If you do need to, please [open an issue /// You should not need to use this function. If you do need to, please
/// on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe /// [open an issue on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe
/// your use case. This function is unsafe because it gives you raw access to the SQLite /// your use case. This function is unsafe because it gives you raw access
/// connection, and what you do with it could impact the safety of this `Connection`. /// to the SQLite connection, and what you do with it could impact the
/// safety of this `Connection`.
pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 { pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
self.0 self.0
} }
@ -200,9 +204,10 @@ pub trait VTab: Sized {
/// ///
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html)) /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
pub trait CreateVTab: VTab { pub trait CreateVTab: VTab {
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement. /// Create a new instance of a virtual table in response to a CREATE VIRTUAL
/// The `db` parameter is a pointer to the SQLite database connection that is executing /// TABLE statement. The `db` parameter is a pointer to the SQLite
/// the CREATE VIRTUAL TABLE statement. /// database connection that is executing the CREATE VIRTUAL TABLE
/// statement.
/// ///
/// Call `connect` by default. /// Call `connect` by default.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method)) /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method))
@ -214,7 +219,8 @@ pub trait CreateVTab: VTab {
Self::connect(db, aux, args) Self::connect(db, aux, args)
} }
/// Destroy the underlying table implementation. This method undoes the work of `create`. /// Destroy the underlying table implementation. This method undoes the work
/// of `create`.
/// ///
/// Do nothing by default. /// Do nothing by default.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xdestroy_method)) /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xdestroy_method))
@ -236,7 +242,8 @@ bitflags! {
} }
} }
/// Pass information into and receive the reply from the `VTab.best_index` method. /// Pass information into and receive the reply from the `VTab.best_index`
/// method.
/// ///
/// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html)) /// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html))
pub struct IndexInfo(*mut ffi::sqlite3_index_info); pub struct IndexInfo(*mut ffi::sqlite3_index_info);
@ -409,8 +416,8 @@ pub trait VTabCursor: Sized {
/// Advance cursor to the next row of a result set initiated by `filter`. /// Advance cursor to the next row of a result set initiated by `filter`.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method)) /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method))
fn next(&mut self) -> Result<()>; fn next(&mut self) -> Result<()>;
/// Must return `false` if the cursor currently points to a valid row of data, /// Must return `false` if the cursor currently points to a valid row of
/// or `true` otherwise. /// data, or `true` otherwise.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method)) /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method))
fn eof(&self) -> bool; fn eof(&self) -> bool;
/// Find the value for the `i`-th column of the current row. /// Find the value for the `i`-th column of the current row.
@ -436,7 +443,8 @@ impl Context {
// TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html) // TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html)
} }
/// Wrapper to `VTabCursor.filter` arguments, the values requested by `VTab.best_index`. /// Wrapper to `VTabCursor.filter` arguments, the values requested by
/// `VTab.best_index`.
pub struct Values<'a> { pub struct Values<'a> {
args: &'a [*mut ffi::sqlite3_value], args: &'a [*mut ffi::sqlite3_value],
} }
@ -894,7 +902,8 @@ where
} }
} }
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`. /// Virtual table cursors can set an error message by assigning a string to
/// `zErrMsg`.
unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int { unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
match result { match result {
@ -912,7 +921,8 @@ unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<
} }
} }
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`. /// Virtual tables methods can set an error message by assigning a string to
/// `zErrMsg`.
unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
if !(*vtab).zErrMsg.is_null() { if !(*vtab).zErrMsg.is_null() {
ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void); ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
@ -920,8 +930,8 @@ unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
(*vtab).zErrMsg = mprintf(err_msg); (*vtab).zErrMsg = mprintf(err_msg);
} }
/// To raise an error, the `column` method should use this method to set the error message /// To raise an error, the `column` method should use this method to set the
/// and return the error code. /// error message and return the error code.
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int { unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
match result { match result {