mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 09:09:19 +08:00
commit
ebf98b4241
@ -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();
|
||||||
|
12
src/blob.rs
12
src/blob.rs
@ -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<()> {
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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()"),
|
||||||
|
74
src/lib.rs
74
src/lib.rs
@ -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,29 +1536,30 @@ 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,
|
c_sql.as_ptr(),
|
||||||
c_sql.as_ptr(),
|
None,
|
||||||
None,
|
ptr::null_mut(),
|
||||||
ptr::null_mut(),
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/row.rs
11
src/row.rs
@ -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())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)")
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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))
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,34 +207,45 @@ 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!(
|
||||||
(i128::MIN, "min".to_owned()),
|
res,
|
||||||
(-2, "neg two".to_owned()),
|
&[
|
||||||
(-1, "neg one".to_owned()),
|
(i128::MIN, "min".to_owned()),
|
||||||
(0, "zero".to_owned()),
|
(-2, "neg two".to_owned()),
|
||||||
(1, "pos one".to_owned()),
|
(-1, "neg one".to_owned()),
|
||||||
(2, "pos two".to_owned()),
|
(0, "zero".to_owned()),
|
||||||
(i128::MAX, "max".to_owned()),
|
(1, "pos one".to_owned()),
|
||||||
]);
|
(2, "pos two".to_owned()),
|
||||||
|
(i128::MAX, "max".to_owned()),
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user