mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
commit
ebf98b4241
@ -405,7 +405,8 @@ mod test {
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
backup.step(-1).unwrap();
|
||||
}
|
||||
|
||||
@ -422,7 +423,8 @@ mod test {
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
backup
|
||||
.run_to_completion(5, Duration::from_millis(250), None)
|
||||
.unwrap();
|
||||
|
12
src/blob.rs
12
src/blob.rs
@ -28,7 +28,8 @@
|
||||
//! db.execute(
|
||||
//! "INSERT INTO test (content) VALUES (ZEROBLOB(10))",
|
||||
//! NO_PARAMS,
|
||||
//! ).unwrap();
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let rowid = db.last_insert_rowid();
|
||||
//! let mut blob = db
|
||||
@ -36,7 +37,8 @@
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! // 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();
|
||||
//! assert_eq!(bytes_written, 8);
|
||||
//!
|
||||
@ -171,7 +173,8 @@ impl<'conn> io::Read for Blob<'conn> {
|
||||
.map(|_| {
|
||||
self.pos += n;
|
||||
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(|_| {
|
||||
self.pos += n;
|
||||
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<()> {
|
||||
|
@ -127,7 +127,8 @@ mod test {
|
||||
let _ = db2
|
||||
.query_row("PRAGMA schema_version", NO_PARAMS, |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
})
|
||||
.expect("unexpected error");
|
||||
|
||||
child.join().unwrap();
|
||||
}
|
||||
@ -165,7 +166,8 @@ mod test {
|
||||
let _ = db2
|
||||
.query_row("PRAGMA schema_version", NO_PARAMS, |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
})
|
||||
.expect("unexpected error");
|
||||
assert_eq!(CALLED.load(Ordering::Relaxed), true);
|
||||
|
||||
child.join().unwrap();
|
||||
|
@ -269,7 +269,8 @@ mod test {
|
||||
CREATE TABLE foo (x INT);
|
||||
INSERT INTO foo VALUES (1);
|
||||
"#,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sql = "SELECT * FROM foo";
|
||||
|
||||
@ -290,7 +291,8 @@ mod test {
|
||||
ALTER TABLE foo ADD COLUMN y INT;
|
||||
UPDATE foo SET y = 2;
|
||||
"#,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
|
@ -47,7 +47,8 @@
|
||||
//! "SELECT regexp('[aeiou]*', 'aaaaeeeiii')",
|
||||
//! NO_PARAMS,
|
||||
//! |row| row.get(0),
|
||||
//! ).unwrap();
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! assert!(is_match);
|
||||
//! }
|
||||
@ -540,8 +541,10 @@ mod test {
|
||||
let is_match = {
|
||||
let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap());
|
||||
|
||||
let text = ctx.get_raw(1).as_str().map_err(|e|
|
||||
Error::UserFunctionError(e.into()))?;
|
||||
let text = ctx
|
||||
.get_raw(1)
|
||||
.as_str()
|
||||
.map_err(|e| Error::UserFunctionError(e.into()))?;
|
||||
|
||||
re.is_match(text)
|
||||
};
|
||||
@ -563,7 +566,8 @@ mod test {
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary)
|
||||
.unwrap();
|
||||
|
||||
@ -593,7 +597,8 @@ mod test {
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// This implementation of a regexp scalar function uses a captured HashMap
|
||||
// to keep cached regular expressions around (even across multiple queries)
|
||||
@ -617,7 +622,8 @@ mod test {
|
||||
|
||||
let text = try!(ctx.get::<String>(1));
|
||||
Ok(regex.is_match(&text))
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let result: Result<bool> =
|
||||
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", NO_PARAMS, |r| {
|
||||
@ -647,7 +653,8 @@ mod test {
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for &(expected, query) in &[
|
||||
("", "SELECT my_concat()"),
|
||||
|
56
src/lib.rs
56
src/lib.rs
@ -28,7 +28,8 @@
|
||||
//! data BLOB
|
||||
//! )",
|
||||
//! NO_PARAMS,
|
||||
//! ).unwrap();
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//! let me = Person {
|
||||
//! id: 0,
|
||||
//! name: "Steven".to_string(),
|
||||
@ -39,7 +40,8 @@
|
||||
//! "INSERT INTO person (name, time_created, data)
|
||||
//! VALUES (?1, ?2, ?3)",
|
||||
//! &[&me.name as &ToSql, &me.time_created, &me.data],
|
||||
//! ).unwrap();
|
||||
//! )
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! let mut stmt = conn
|
||||
//! .prepare("SELECT id, name, time_created, data FROM person")
|
||||
@ -50,7 +52,8 @@
|
||||
//! name: row.get(1),
|
||||
//! time_created: row.get(2),
|
||||
//! data: row.get(3),
|
||||
//! }).unwrap();
|
||||
//! })
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! for person in person_iter {
|
||||
//! println!("Found person {:?}", person.unwrap());
|
||||
@ -83,7 +86,7 @@ use std::ptr;
|
||||
use std::result;
|
||||
use std::str;
|
||||
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 error::{error_from_handle, error_from_sqlite_code};
|
||||
@ -576,8 +579,8 @@ impl Connection {
|
||||
self.db.borrow().db()
|
||||
}
|
||||
|
||||
/// Get access to a handle that can be used to interrupt long running queries
|
||||
/// from another thread.
|
||||
/// Get access to a handle that can be used to interrupt long running
|
||||
/// queries from another thread.
|
||||
pub fn get_interrupt_handle(&self) -> InterruptHandle {
|
||||
self.db.borrow().get_interrupt_handle()
|
||||
}
|
||||
@ -892,8 +895,10 @@ impl InnerConnection {
|
||||
}
|
||||
self.remove_hooks();
|
||||
let mut shared_handle = self.interrupt_lock.lock().unwrap();
|
||||
assert!(!shared_handle.is_null(),
|
||||
"Bug: Somehow interrupt_lock was cleared before the DB was closed");
|
||||
assert!(
|
||||
!shared_handle.is_null(),
|
||||
"Bug: Somehow interrupt_lock was cleared before the DB was closed"
|
||||
);
|
||||
unsafe {
|
||||
let r = ffi::sqlite3_close(self.db);
|
||||
// Need to use _raw because _guard has a reference out, and
|
||||
@ -1116,7 +1121,8 @@ mod test {
|
||||
"
|
||||
BEGIN; CREATE TABLE foo(x INTEGER);
|
||||
INSERT INTO foo VALUES(42); END;",
|
||||
).expect("create temp db");
|
||||
)
|
||||
.expect("create temp db");
|
||||
|
||||
let mut db1 = 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();
|
||||
let interrupt_handle = db.get_interrupt_handle();
|
||||
// generate an arbitrary query which will be very slow to execute.
|
||||
let sql = format!("{};",
|
||||
(0..100_000).into_iter()
|
||||
let sql = format!(
|
||||
"{};",
|
||||
(0..100_000)
|
||||
.into_iter()
|
||||
.map(|i| format!("INSERT INTO dummy(id) VALUES({})", i))
|
||||
.collect::<Vec<_>>()
|
||||
.join(";\n"));
|
||||
.join(";\n")
|
||||
);
|
||||
|
||||
// Do this on the main thread to minimize the amount of time spent
|
||||
// when interrupt won't do anything (because we haven't started
|
||||
// executing the query).
|
||||
let c_sql = str_to_cstring(&sql).unwrap();
|
||||
|
||||
let joiner = thread::spawn(move || {
|
||||
unsafe {
|
||||
let joiner = thread::spawn(move || unsafe {
|
||||
let raw_db = db.db.borrow().db;
|
||||
let r = ffi::sqlite3_exec(
|
||||
raw_db,
|
||||
@ -1552,7 +1560,6 @@ mod test {
|
||||
ptr::null_mut(),
|
||||
);
|
||||
db.decode_result(r)
|
||||
}
|
||||
});
|
||||
|
||||
// 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();
|
||||
for (i, v) in vals.iter().enumerate() {
|
||||
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();
|
||||
@ -1729,7 +1741,8 @@ mod test {
|
||||
let results: CustomResult<Vec<String>> = query
|
||||
.query_and_then(NO_PARAMS, |row| {
|
||||
row.get_checked(1).map_err(CustomError::Sqlite)
|
||||
}).unwrap()
|
||||
})
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
assert_eq!(results.unwrap().concat(), "hello, world!");
|
||||
@ -1751,7 +1764,8 @@ mod test {
|
||||
let bad_type: CustomResult<Vec<f64>> = query
|
||||
.query_and_then(NO_PARAMS, |row| {
|
||||
row.get_checked(1).map_err(CustomError::Sqlite)
|
||||
}).unwrap()
|
||||
})
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
match bad_type.unwrap_err() {
|
||||
@ -1762,7 +1776,8 @@ mod test {
|
||||
let bad_idx: CustomResult<Vec<String>> = query
|
||||
.query_and_then(NO_PARAMS, |row| {
|
||||
row.get_checked(3).map_err(CustomError::Sqlite)
|
||||
}).unwrap()
|
||||
})
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
match bad_idx.unwrap_err() {
|
||||
@ -1846,7 +1861,8 @@ mod test {
|
||||
|
||||
db.query_row("SELECT * FROM foo", NO_PARAMS, |r| {
|
||||
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
|
||||
/// source for `T`
|
||||
/// * If the underlying SQLite integral value is
|
||||
/// outside the range representable by `T`
|
||||
/// * If `idx` is outside the range of columns in the
|
||||
/// returned query
|
||||
/// * If the underlying SQLite integral value is outside the range
|
||||
/// representable by `T`
|
||||
/// * If `idx` is outside the range of columns in the returned query
|
||||
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
||||
self.get_checked(idx).unwrap()
|
||||
}
|
||||
@ -175,9 +174,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) => {
|
||||
Error::InvalidColumnType(idx, value.data_type())
|
||||
}
|
||||
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -697,7 +697,8 @@ mod test {
|
||||
"SELECT SUM(x) FROM foo WHERE x > :x",
|
||||
&[(":x", &0i32)],
|
||||
|r| r.get(0)
|
||||
).unwrap()
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@ -719,7 +720,8 @@ mod test {
|
||||
"SELECT COUNT(*) FROM test WHERE name = :name",
|
||||
&[(":name", &"one")],
|
||||
|r| r.get(0)
|
||||
).unwrap()
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@ -757,7 +759,8 @@ mod test {
|
||||
.query_map_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
2 * id
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(2, doubled_id);
|
||||
@ -784,7 +787,8 @@ mod test {
|
||||
} else {
|
||||
Err(Error::SqliteSingleThreadedMode)
|
||||
}
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// first row should be Ok
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
@ -812,7 +816,8 @@ mod test {
|
||||
let result: Option<String> = db
|
||||
.query_row("SELECT y FROM test WHERE x = 'one'", NO_PARAMS, |row| {
|
||||
row.get(0)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
@ -831,7 +836,8 @@ mod test {
|
||||
let result: String = db
|
||||
.query_row("SELECT x FROM test WHERE y = 'two'", NO_PARAMS, |row| {
|
||||
row.get(0)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(result, "one");
|
||||
}
|
||||
|
||||
@ -867,7 +873,8 @@ mod test {
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
CREATE TABLE bar(x INTEGER);
|
||||
",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
db.prepare("INSERT INTO foo VALUES (10)")
|
||||
|
@ -236,7 +236,8 @@ mod test {
|
||||
let v4: DateTime<Utc> = db
|
||||
.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", NO_PARAMS, |r| {
|
||||
r.get(0)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(utc, v4);
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,9 @@ impl fmt::Display for FromSqlError {
|
||||
FromSqlError::InvalidType => write!(f, "Invalid type"),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(s) =>
|
||||
write!(f, "Cannot read 128bit value out of {} byte blob", s),
|
||||
FromSqlError::InvalidI128Size(s) => {
|
||||
write!(f, "Cannot read 128bit value out of {} byte blob", s)
|
||||
}
|
||||
FromSqlError::Other(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
@ -41,8 +42,7 @@ impl Error for FromSqlError {
|
||||
FromSqlError::InvalidType => "invalid type",
|
||||
FromSqlError::OutOfRange(_) => "value out of range",
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) =>
|
||||
"unexpected blob size for 128bit value",
|
||||
FromSqlError::InvalidI128Size(_) => "unexpected blob size for 128bit value",
|
||||
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() {}
|
||||
//! ```
|
||||
//!
|
||||
@ -239,7 +240,8 @@ mod test {
|
||||
db.execute(
|
||||
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
NO_PARAMS,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
|
||||
let mut rows = stmt.query(NO_PARAMS).unwrap();
|
||||
@ -354,7 +356,8 @@ mod test {
|
||||
db.execute(
|
||||
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
NO_PARAMS,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").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::Blob(b) => serde_json::from_slice(b),
|
||||
_ => 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(
|
||||
"INSERT INTO foo (t, b) VALUES (?, ?)",
|
||||
&[&data as &ToSql, &json.as_bytes()],
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let t: serde_json::Value = db
|
||||
.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)
|
||||
.or_else(|_| Err(FromSqlError::Other(Box::new(err))))
|
||||
})
|
||||
}).map(|tm| tm.to_timespec())
|
||||
})
|
||||
.map(|tm| tm.to_timespec())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,27 +207,37 @@ mod test {
|
||||
#[cfg(feature = "i128_blob")]
|
||||
#[test]
|
||||
fn test_i128() {
|
||||
use {Connection, NO_PARAMS};
|
||||
use std::i128;
|
||||
use {Connection, NO_PARAMS};
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)").unwrap();
|
||||
db.execute("
|
||||
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
|
||||
.unwrap();
|
||||
db.execute(
|
||||
"
|
||||
INSERT INTO foo(i128, desc) VALUES
|
||||
(?, 'zero'),
|
||||
(?, 'neg one'), (?, 'neg two'),
|
||||
(?, 'pos one'), (?, 'pos two'),
|
||||
(?, 'min'), (?, 'max')",
|
||||
&[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX]
|
||||
).unwrap();
|
||||
&[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
|
||||
)
|
||||
.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(
|
||||
NO_PARAMS,
|
||||
|row| (row.get::<_, i128>(0), row.get::<_, String>(1))
|
||||
).unwrap().collect::<Result<Vec<_>, _>>().unwrap();
|
||||
let res = stmt
|
||||
.query_map(NO_PARAMS, |row| {
|
||||
(row.get::<_, i128>(0), row.get::<_, String>(1))
|
||||
})
|
||||
.unwrap()
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res, &[
|
||||
assert_eq!(
|
||||
res,
|
||||
&[
|
||||
(i128::MIN, "min".to_owned()),
|
||||
(-2, "neg two".to_owned()),
|
||||
(-1, "neg one".to_owned()),
|
||||
@ -235,6 +245,7 @@ mod test {
|
||||
(1, "pos one".to_owned()),
|
||||
(2, "pos two".to_owned()),
|
||||
(i128::MAX, "max".to_owned()),
|
||||
]);
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +379,8 @@ mod test {
|
||||
.prepare(
|
||||
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
|
||||
v1.rowid < v2.rowid",
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut rows = s.query(NO_PARAMS).unwrap();
|
||||
let row = rows.next().unwrap().unwrap();
|
||||
|
@ -2,9 +2,11 @@
|
||||
//!
|
||||
//! Follow these steps to create your own virtual table:
|
||||
//! 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`.
|
||||
//! 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))
|
||||
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).
|
||||
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
|
||||
// For eponymous-only virtual tables, the xCreate method is NULL
|
||||
// A virtual table is eponymous if its xCreate method is the exact same function
|
||||
// as the xConnect method For eponymous-only virtual tables, the xCreate
|
||||
// method is NULL
|
||||
let ffi_module = ffi::sqlite3_module {
|
||||
iVersion: version,
|
||||
xCreate: None,
|
||||
@ -152,10 +155,11 @@ impl VTabConnection {
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// You should not need to use this function. If you do need to, please [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
|
||||
/// connection, and what you do with it could impact the safety of this `Connection`.
|
||||
/// You should not need to use this function. If you do need to, please
|
||||
/// [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 connection, and what you do with it could impact the
|
||||
/// safety of this `Connection`.
|
||||
pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
|
||||
self.0
|
||||
}
|
||||
@ -200,9 +204,10 @@ pub trait VTab: Sized {
|
||||
///
|
||||
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
|
||||
pub trait CreateVTab: VTab {
|
||||
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement.
|
||||
/// The `db` parameter is a pointer to the SQLite database connection that is executing
|
||||
/// the CREATE VIRTUAL TABLE statement.
|
||||
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL
|
||||
/// TABLE statement. The `db` parameter is a pointer to the SQLite
|
||||
/// database connection that is executing the CREATE VIRTUAL TABLE
|
||||
/// statement.
|
||||
///
|
||||
/// Call `connect` by default.
|
||||
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method))
|
||||
@ -214,7 +219,8 @@ pub trait CreateVTab: VTab {
|
||||
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.
|
||||
/// (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))
|
||||
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`.
|
||||
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method))
|
||||
fn next(&mut self) -> Result<()>;
|
||||
/// Must return `false` if the cursor currently points to a valid row of data,
|
||||
/// or `true` otherwise.
|
||||
/// Must return `false` if the cursor currently points to a valid row of
|
||||
/// data, or `true` otherwise.
|
||||
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method))
|
||||
fn eof(&self) -> bool;
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
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 {
|
||||
use std::error::Error as StdError;
|
||||
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) {
|
||||
if !(*vtab).zErrMsg.is_null() {
|
||||
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);
|
||||
}
|
||||
|
||||
/// To raise an error, the `column` method should use this method to set the error message
|
||||
/// and return the error code.
|
||||
/// To raise an error, the `column` method should use this method to set the
|
||||
/// error message and return the error code.
|
||||
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
|
||||
use std::error::Error as StdError;
|
||||
match result {
|
||||
|
Loading…
Reference in New Issue
Block a user