mirror of
https://github.com/isar/rusqlite.git
synced 2025-09-16 20:52:19 +08:00
Merge remote-tracking branch 'jgallagher/master' into vtab
This commit is contained in:
392
src/lib.rs
392
src/lib.rs
@@ -60,75 +60,76 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use std::default::Default;
|
||||
use std::convert;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::cell::RefCell;
|
||||
use std::convert;
|
||||
use std::default::Default;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ptr;
|
||||
use std::result;
|
||||
use std::str;
|
||||
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
|
||||
use std::os::raw::{c_int, c_char};
|
||||
|
||||
use types::{ToSql, ValueRef};
|
||||
use error::{error_from_sqlite_code, error_from_handle};
|
||||
use raw_statement::RawStatement;
|
||||
use cache::StatementCache;
|
||||
use error::{error_from_handle, error_from_sqlite_code};
|
||||
use raw_statement::RawStatement;
|
||||
use types::{ToSql, ValueRef};
|
||||
|
||||
pub use statement::Statement;
|
||||
|
||||
pub use row::{Row, Rows, MappedRows, AndThenRows, RowIndex};
|
||||
pub use row::{AndThenRows, MappedRows, Row, RowIndex, Rows};
|
||||
|
||||
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
|
||||
#[allow(deprecated)]
|
||||
pub use transaction::{SqliteTransaction, SqliteTransactionBehavior};
|
||||
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
|
||||
|
||||
pub use error::Error;
|
||||
#[allow(deprecated)]
|
||||
pub use error::SqliteError;
|
||||
pub use error::Error;
|
||||
pub use ffi::ErrorCode;
|
||||
|
||||
pub use cache::CachedStatement;
|
||||
pub use version::*;
|
||||
|
||||
#[cfg(feature = "hooks")]
|
||||
pub use hooks::*;
|
||||
#[cfg(feature = "load_extension")]
|
||||
#[allow(deprecated)]
|
||||
pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard};
|
||||
pub use load_extension_guard::{LoadExtensionGuard, SqliteLoadExtensionGuard};
|
||||
|
||||
pub mod types;
|
||||
mod version;
|
||||
mod transaction;
|
||||
#[cfg(feature = "backup")]
|
||||
pub mod backup;
|
||||
#[cfg(feature = "blob")]
|
||||
pub mod blob;
|
||||
mod busy;
|
||||
mod cache;
|
||||
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||
mod context;
|
||||
mod error;
|
||||
#[cfg(feature = "functions")]
|
||||
pub mod functions;
|
||||
#[cfg(feature = "hooks")]
|
||||
mod hooks;
|
||||
#[cfg(feature = "limits")]
|
||||
pub mod limits;
|
||||
#[cfg(feature = "load_extension")]
|
||||
mod load_extension_guard;
|
||||
mod raw_statement;
|
||||
mod row;
|
||||
mod statement;
|
||||
#[cfg(feature = "load_extension")]
|
||||
mod load_extension_guard;
|
||||
#[cfg(feature = "trace")]
|
||||
pub mod trace;
|
||||
#[cfg(feature = "backup")]
|
||||
pub mod backup;
|
||||
#[cfg(feature = "functions")]
|
||||
pub mod functions;
|
||||
#[cfg(feature = "blob")]
|
||||
pub mod blob;
|
||||
#[cfg(feature = "limits")]
|
||||
pub mod limits;
|
||||
#[cfg(feature = "hooks")]
|
||||
mod hooks;
|
||||
#[cfg(feature = "hooks")]
|
||||
pub use hooks::*;
|
||||
mod transaction;
|
||||
pub mod types;
|
||||
mod unlock_notify;
|
||||
mod busy;
|
||||
mod version;
|
||||
#[cfg(feature = "vtab")]
|
||||
pub mod vtab;
|
||||
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||
mod context;
|
||||
|
||||
// Number of cached prepared statements we'll hold on to.
|
||||
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
||||
@@ -155,7 +156,7 @@ fn path_to_cstring(p: &Path) -> Result<CString> {
|
||||
}
|
||||
|
||||
/// Name for a database within a SQLite connection.
|
||||
#[derive(Copy,Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DatabaseName<'a> {
|
||||
/// The main database.
|
||||
Main,
|
||||
@@ -172,7 +173,7 @@ pub enum DatabaseName<'a> {
|
||||
#[cfg(any(feature = "backup", feature = "blob"))]
|
||||
impl<'a> DatabaseName<'a> {
|
||||
fn to_cstring(&self) -> Result<CString> {
|
||||
use self::DatabaseName::{Main, Temp, Attached};
|
||||
use self::DatabaseName::{Attached, Main, Temp};
|
||||
match *self {
|
||||
Main => str_to_cstring("main"),
|
||||
Temp => str_to_cstring("temp"),
|
||||
@@ -236,13 +237,11 @@ impl Connection {
|
||||
/// underlying SQLite open call fails.
|
||||
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
|
||||
let c_path = try!(path_to_cstring(path.as_ref()));
|
||||
InnerConnection::open_with_flags(&c_path, flags).map(|db| {
|
||||
Connection {
|
||||
db: RefCell::new(db),
|
||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||
path: Some(path.as_ref().to_path_buf()),
|
||||
}
|
||||
})
|
||||
InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection {
|
||||
db: RefCell::new(db),
|
||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||
path: Some(path.as_ref().to_path_buf()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Open a new connection to an in-memory SQLite database.
|
||||
@@ -255,13 +254,11 @@ impl Connection {
|
||||
/// Will return `Err` if the underlying SQLite open call fails.
|
||||
pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> {
|
||||
let c_memory = try!(str_to_cstring(":memory:"));
|
||||
InnerConnection::open_with_flags(&c_memory, flags).map(|db| {
|
||||
Connection {
|
||||
db: RefCell::new(db),
|
||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||
path: None,
|
||||
}
|
||||
})
|
||||
InnerConnection::open_with_flags(&c_memory, flags).map(|db| Connection {
|
||||
db: RefCell::new(db),
|
||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||
path: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convenience method to run multiple SQL statements (that cannot take any parameters).
|
||||
@@ -310,8 +307,7 @@ impl Connection {
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<usize> {
|
||||
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).
|
||||
@@ -365,7 +361,8 @@ impl Connection {
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
|
||||
where F: FnOnce(&Row) -> T
|
||||
where
|
||||
F: FnOnce(&Row) -> T,
|
||||
{
|
||||
let mut stmt = try!(self.prepare(sql));
|
||||
stmt.query_row(params, f)
|
||||
@@ -381,7 +378,8 @@ impl Connection {
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
|
||||
where F: FnOnce(&Row) -> T
|
||||
where
|
||||
F: FnOnce(&Row) -> T,
|
||||
{
|
||||
let mut stmt = try!(self.prepare(sql));
|
||||
let mut rows = try!(stmt.query_named(params));
|
||||
@@ -412,20 +410,20 @@ impl Connection {
|
||||
///
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn query_row_and_then<T, E, F>(&self,
|
||||
sql: &str,
|
||||
params: &[&ToSql],
|
||||
f: F)
|
||||
-> result::Result<T, E>
|
||||
where F: FnOnce(&Row) -> result::Result<T, E>,
|
||||
E: convert::From<Error>
|
||||
pub fn query_row_and_then<T, E, F>(
|
||||
&self,
|
||||
sql: &str,
|
||||
params: &[&ToSql],
|
||||
f: F,
|
||||
) -> result::Result<T, E>
|
||||
where
|
||||
F: FnOnce(&Row) -> result::Result<T, E>,
|
||||
E: convert::From<Error>,
|
||||
{
|
||||
let mut stmt = try!(self.prepare(sql));
|
||||
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.
|
||||
@@ -449,7 +447,8 @@ impl Connection {
|
||||
/// does exactly the same thing.
|
||||
#[deprecated(since = "0.1.0", note = "Use query_row instead")]
|
||||
pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
|
||||
where F: FnOnce(&Row) -> T
|
||||
where
|
||||
F: FnOnce(&Row) -> T,
|
||||
{
|
||||
self.query_row(sql, params, f)
|
||||
}
|
||||
@@ -549,10 +548,11 @@ impl Connection {
|
||||
///
|
||||
/// Will return `Err` if the underlying SQLite call fails.
|
||||
#[cfg(feature = "load_extension")]
|
||||
pub fn load_extension<P: AsRef<Path>>(&self,
|
||||
dylib_path: P,
|
||||
entry_point: Option<&str>)
|
||||
-> Result<()> {
|
||||
pub fn load_extension<P: AsRef<Path>>(
|
||||
&self,
|
||||
dylib_path: P,
|
||||
entry_point: Option<&str>,
|
||||
) -> Result<()> {
|
||||
self.db
|
||||
.borrow_mut()
|
||||
.load_extension(dylib_path.as_ref(), entry_point)
|
||||
@@ -601,6 +601,12 @@ impl fmt::Debug for Connection {
|
||||
|
||||
struct InnerConnection {
|
||||
db: *mut ffi::sqlite3,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_commit_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
}
|
||||
|
||||
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
|
||||
@@ -626,8 +632,10 @@ bitflags! {
|
||||
|
||||
impl Default for OpenFlags {
|
||||
fn default() -> OpenFlags {
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE |
|
||||
OpenFlags::SQLITE_OPEN_NO_MUTEX | OpenFlags::SQLITE_OPEN_URI
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE
|
||||
| OpenFlags::SQLITE_OPEN_CREATE
|
||||
| OpenFlags::SQLITE_OPEN_NO_MUTEX
|
||||
| OpenFlags::SQLITE_OPEN_URI
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,9 +682,11 @@ fn ensure_valid_sqlite_version() {
|
||||
let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
|
||||
let runtime_major = version_number / 1_000_000;
|
||||
if buildtime_major != runtime_major {
|
||||
panic!("rusqlite was built against SQLite {} but is running with SQLite {}",
|
||||
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
|
||||
version());
|
||||
panic!(
|
||||
"rusqlite was built against SQLite {} but is running with SQLite {}",
|
||||
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
|
||||
version()
|
||||
);
|
||||
}
|
||||
|
||||
if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
|
||||
@@ -686,14 +696,16 @@ fn ensure_valid_sqlite_version() {
|
||||
// Check that the runtime version number is compatible with the version number we found at
|
||||
// build-time.
|
||||
if version_number < ffi::SQLITE_VERSION_NUMBER {
|
||||
panic!("\
|
||||
panic!(
|
||||
"\
|
||||
rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
|
||||
* Recompile rusqlite and link against the SQLite version you are using at runtime, or
|
||||
* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
|
||||
means you're sure everything will work correctly even though the runtime version is older than
|
||||
the version we found at build time.",
|
||||
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
|
||||
version());
|
||||
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
|
||||
version()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -759,6 +771,20 @@ To fix this, either:
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
#[cfg(not(feature = "hooks"))]
|
||||
fn new(db: *mut ffi::sqlite3) -> InnerConnection {
|
||||
InnerConnection { db }
|
||||
}
|
||||
#[cfg(feature = "hooks")]
|
||||
fn new(db: *mut ffi::sqlite3) -> InnerConnection {
|
||||
InnerConnection {
|
||||
db,
|
||||
free_commit_hook: None,
|
||||
free_rollback_hook: None,
|
||||
free_update_hook: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
|
||||
ensure_valid_sqlite_version();
|
||||
ensure_safe_sqlite_threading_mode()?;
|
||||
@@ -767,9 +793,15 @@ impl InnerConnection {
|
||||
// wasn't added until version 3.7.3.
|
||||
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
|
||||
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
|
||||
debug_assert_eq!(1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, 0x40);
|
||||
debug_assert_eq!(
|
||||
1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits,
|
||||
0x40
|
||||
);
|
||||
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
|
||||
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
|
||||
return Err(Error::SqliteFailure(
|
||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
@@ -796,7 +828,7 @@ impl InnerConnection {
|
||||
// attempt to turn on extended results code; don't fail if we can't.
|
||||
ffi::sqlite3_extended_result_codes(db, 1);
|
||||
|
||||
Ok(InnerConnection { db })
|
||||
Ok(InnerConnection::new(db))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,11 +862,13 @@ impl InnerConnection {
|
||||
fn execute_batch(&mut self, sql: &str) -> Result<()> {
|
||||
let c_sql = try!(str_to_cstring(sql));
|
||||
unsafe {
|
||||
let r = ffi::sqlite3_exec(self.db(),
|
||||
c_sql.as_ptr(),
|
||||
None,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut());
|
||||
let r = ffi::sqlite3_exec(
|
||||
self.db(),
|
||||
c_sql.as_ptr(),
|
||||
None,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
self.decode_result(r)
|
||||
}
|
||||
}
|
||||
@@ -852,10 +886,12 @@ impl InnerConnection {
|
||||
let mut errmsg: *mut c_char = mem::uninitialized();
|
||||
let r = if let Some(entry_point) = entry_point {
|
||||
let c_entry = try!(str_to_cstring(entry_point));
|
||||
ffi::sqlite3_load_extension(self.db,
|
||||
dylib_str.as_ptr(),
|
||||
c_entry.as_ptr(),
|
||||
&mut errmsg)
|
||||
ffi::sqlite3_load_extension(
|
||||
self.db,
|
||||
dylib_str.as_ptr(),
|
||||
c_entry.as_ptr(),
|
||||
&mut errmsg,
|
||||
)
|
||||
} else {
|
||||
ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg)
|
||||
};
|
||||
@@ -910,9 +946,8 @@ impl InnerConnection {
|
||||
)
|
||||
}
|
||||
};
|
||||
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) -> usize {
|
||||
@@ -939,8 +974,7 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "hooks"))]
|
||||
fn remove_hooks(&mut self) {
|
||||
}
|
||||
fn remove_hooks(&mut self) {}
|
||||
}
|
||||
|
||||
impl Drop for InnerConnection {
|
||||
@@ -966,18 +1000,16 @@ pub type SqliteStatement<'conn> = Statement<'conn>;
|
||||
#[deprecated(since = "0.6.0", note = "Use Rows instead")]
|
||||
pub type SqliteRows<'stmt> = Rows<'stmt>;
|
||||
|
||||
|
||||
/// Old name for `Row`. `SqliteRow` is deprecated.
|
||||
#[deprecated(since = "0.6.0", note = "Use Row instead")]
|
||||
pub type SqliteRow<'a, 'stmt> = Row<'a, 'stmt>;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
use self::tempdir::TempDir;
|
||||
pub use super::*;
|
||||
use ffi;
|
||||
use self::tempdir::TempDir;
|
||||
pub use std::error::Error as StdError;
|
||||
pub use std::fmt;
|
||||
|
||||
@@ -999,10 +1031,13 @@ mod test {
|
||||
let tmp = TempDir::new("locked").unwrap();
|
||||
let path = tmp.path().join("transactions.db3");
|
||||
|
||||
Connection::open(&path).expect("create temp db").execute_batch("
|
||||
Connection::open(&path)
|
||||
.expect("create temp db")
|
||||
.execute_batch(
|
||||
"
|
||||
BEGIN; CREATE TABLE foo(x INTEGER);
|
||||
INSERT INTO foo VALUES(42); END;")
|
||||
.expect("create temp db");
|
||||
INSERT INTO foo VALUES(42); END;",
|
||||
).expect("create temp db");
|
||||
|
||||
let mut db1 = Connection::open(&path).unwrap();
|
||||
let mut db2 = Connection::open(&path).unwrap();
|
||||
@@ -1015,8 +1050,12 @@ mod test {
|
||||
let tx2 = db2.transaction().unwrap();
|
||||
|
||||
// SELECT first makes sqlite lock with a shared lock
|
||||
let _ = tx1.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ()).unwrap();
|
||||
let _ = tx2.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ()).unwrap();
|
||||
let _ = tx1
|
||||
.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
|
||||
.unwrap();
|
||||
let _ = tx2
|
||||
.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
|
||||
.unwrap();
|
||||
|
||||
tx1.execute("INSERT INTO foo VALUES(?1)", &[&1]).unwrap();
|
||||
let _ = tx2.execute("INSERT INTO foo VALUES(?1)", &[&2]);
|
||||
@@ -1025,12 +1064,15 @@ mod test {
|
||||
let _ = tx2.commit();
|
||||
}
|
||||
|
||||
let _ = db1.transaction().expect("commit should have closed transaction");
|
||||
let _ = db2.transaction().expect("commit should have closed transaction");
|
||||
let _ = db1
|
||||
.transaction()
|
||||
.expect("commit should have closed transaction");
|
||||
let _ = db2
|
||||
.transaction()
|
||||
.expect("commit should have closed transaction");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_persistence() {
|
||||
let temp_dir = TempDir::new("test_open_file").unwrap();
|
||||
let path = temp_dir.path().join("test.db3");
|
||||
@@ -1066,20 +1108,22 @@ mod test {
|
||||
// force the DB to be busy by preparing a statement; this must be done at the FFI
|
||||
// level to allow us to call .close() without dropping the prepared statement first.
|
||||
let raw_stmt = {
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::os::raw::c_int;
|
||||
use super::str_to_cstring;
|
||||
use std::mem;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
let raw_db = db.db.borrow_mut().db;
|
||||
let sql = "SELECT 1";
|
||||
let mut raw_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
|
||||
let rc = unsafe {
|
||||
ffi::sqlite3_prepare_v2(raw_db,
|
||||
str_to_cstring(sql).unwrap().as_ptr(),
|
||||
(sql.len() + 1) as c_int,
|
||||
&mut raw_stmt,
|
||||
ptr::null_mut())
|
||||
ffi::sqlite3_prepare_v2(
|
||||
raw_db,
|
||||
str_to_cstring(sql).unwrap().as_ptr(),
|
||||
(sql.len() + 1) as c_int,
|
||||
&mut raw_stmt,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
assert_eq!(rc, ffi::SQLITE_OK);
|
||||
raw_stmt
|
||||
@@ -1098,15 +1142,16 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_open_with_flags() {
|
||||
for bad_flags in &[OpenFlags::empty(),
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE] {
|
||||
for bad_flags in &[
|
||||
OpenFlags::empty(),
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
|
||||
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE,
|
||||
] {
|
||||
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_execute_batch() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1118,7 +1163,8 @@ mod test {
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3").unwrap();
|
||||
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")
|
||||
.unwrap();
|
||||
|
||||
assert!(db.execute_batch("INVALID SQL").is_err());
|
||||
}
|
||||
@@ -1128,16 +1174,22 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
|
||||
|
||||
assert_eq!(1,
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
|
||||
.unwrap());
|
||||
assert_eq!(1,
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
1,
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
1,
|
||||
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(3i32,
|
||||
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
3i32,
|
||||
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1194,7 +1246,8 @@ mod test {
|
||||
assert_eq!(insert_stmt.execute(&[&2i32]).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")
|
||||
let mut query = db
|
||||
.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
|
||||
.unwrap();
|
||||
{
|
||||
let mut rows = query.query(&[&4i32]).unwrap();
|
||||
@@ -1220,7 +1273,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_map() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1233,15 +1285,13 @@ mod test {
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||
let results: Result<Vec<String>> = query.query_map(&[], |row| row.get(1))
|
||||
.unwrap()
|
||||
.collect();
|
||||
let results: Result<Vec<String>> =
|
||||
query.query_map(&[], |row| row.get(1)).unwrap().collect();
|
||||
|
||||
assert_eq!(results.unwrap().concat(), "hello, world!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_row() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1253,9 +1303,11 @@ mod test {
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
assert_eq!(10i64,
|
||||
db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
10i64,
|
||||
db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0));
|
||||
match result.unwrap_err() {
|
||||
@@ -1282,8 +1334,7 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
|
||||
.unwrap();
|
||||
db.execute_batch("INSERT INTO foo DEFAULT VALUES")
|
||||
.unwrap();
|
||||
db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap();
|
||||
|
||||
assert_eq!(db.last_insert_rowid(), 1);
|
||||
|
||||
@@ -1297,8 +1348,10 @@ mod test {
|
||||
#[test]
|
||||
fn test_is_autocommit() {
|
||||
let db = checked_memory_handle();
|
||||
assert!(db.is_autocommit(),
|
||||
"autocommit expected to be active by default");
|
||||
assert!(
|
||||
db.is_autocommit(),
|
||||
"autocommit expected to be active by default"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1403,7 +1456,6 @@ mod test {
|
||||
type CustomResult<T> = ::std::result::Result<T, CustomError>;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1416,8 +1468,8 @@ mod test {
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||
let results: Result<Vec<String>> = query.query_and_then(&[],
|
||||
|row| row.get_checked(1))
|
||||
let results: Result<Vec<String>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(1))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
@@ -1425,7 +1477,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then_fails() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1438,7 +1489,8 @@ mod test {
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||
let bad_type: Result<Vec<f64>> = query.query_and_then(&[], |row| row.get_checked(1))
|
||||
let bad_type: Result<Vec<f64>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(1))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
@@ -1447,7 +1499,8 @@ mod test {
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
let bad_idx: Result<Vec<String>> = query.query_and_then(&[], |row| row.get_checked(3))
|
||||
let bad_idx: Result<Vec<String>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(3))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
@@ -1458,7 +1511,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then_custom_error() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1471,18 +1523,15 @@ mod test {
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||
let results: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
|
||||
row.get_checked(1)
|
||||
.map_err(CustomError::Sqlite)
|
||||
})
|
||||
.unwrap()
|
||||
let results: CustomResult<Vec<String>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
assert_eq!(results.unwrap().concat(), "hello, world!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then_custom_error_fails() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1495,11 +1544,9 @@ mod test {
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||
let bad_type: CustomResult<Vec<f64>> = query.query_and_then(&[], |row| {
|
||||
row.get_checked(1)
|
||||
.map_err(CustomError::Sqlite)
|
||||
})
|
||||
.unwrap()
|
||||
let bad_type: CustomResult<Vec<f64>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
match bad_type.unwrap_err() {
|
||||
@@ -1507,11 +1554,9 @@ mod test {
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
let bad_idx: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
|
||||
row.get_checked(3)
|
||||
.map_err(CustomError::Sqlite)
|
||||
})
|
||||
.unwrap()
|
||||
let bad_idx: CustomResult<Vec<String>> = query
|
||||
.query_and_then(&[], |row| row.get_checked(3).map_err(CustomError::Sqlite))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
match bad_idx.unwrap_err() {
|
||||
@@ -1519,10 +1564,9 @@ mod test {
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
let non_sqlite_err: CustomResult<Vec<String>> = query.query_and_then(&[], |_| {
|
||||
Err(CustomError::SomeError)
|
||||
})
|
||||
.unwrap()
|
||||
let non_sqlite_err: CustomResult<Vec<String>> = query
|
||||
.query_and_then(&[], |_| Err(CustomError::SomeError))
|
||||
.unwrap()
|
||||
.collect();
|
||||
|
||||
match non_sqlite_err.unwrap_err() {
|
||||
@@ -1532,7 +1576,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_row_and_then_custom_error() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1550,7 +1593,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_row_and_then_custom_error_fails() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1578,9 +1620,8 @@ mod test {
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
let non_sqlite_err: CustomResult<String> = db.query_row_and_then(query, &[], |_| {
|
||||
Err(CustomError::SomeError)
|
||||
});
|
||||
let non_sqlite_err: CustomResult<String> =
|
||||
db.query_row_and_then(query, &[], |_| Err(CustomError::SomeError));
|
||||
|
||||
match non_sqlite_err.unwrap_err() {
|
||||
CustomError::SomeError => (),
|
||||
@@ -1589,7 +1630,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_dynamic() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@@ -1598,7 +1638,9 @@ mod test {
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
db.query_row("SELECT * FROM foo", &[], |r| assert_eq!(2, r.column_count())).unwrap();
|
||||
db.query_row("SELECT * FROM foo", &[], |r| {
|
||||
assert_eq!(2, r.column_count())
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user