mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-22 07:09:20 +08:00
commit
557af7627c
@ -5,8 +5,8 @@ fn main() {
|
||||
#[cfg(feature = "bundled")]
|
||||
mod build {
|
||||
extern crate cc;
|
||||
use std::{env, fs};
|
||||
use std::path::Path;
|
||||
use std::{env, fs};
|
||||
|
||||
pub fn main() {
|
||||
if cfg!(feature = "sqlcipher") {
|
||||
@ -68,8 +68,10 @@ mod build {
|
||||
match header {
|
||||
HeaderLocation::FromEnvironment => {
|
||||
let prefix = env_prefix();
|
||||
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix))
|
||||
.expect(&format!("{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", prefix, prefix));
|
||||
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!(
|
||||
"{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
|
||||
prefix, prefix
|
||||
));
|
||||
header.push_str("/sqlite3.h");
|
||||
header
|
||||
}
|
||||
@ -90,7 +92,7 @@ mod build {
|
||||
|
||||
println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix());
|
||||
println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix());
|
||||
if cfg!(target_os="windows") {
|
||||
if cfg!(target_os = "windows") {
|
||||
println!("cargo:rerun-if-env-changed=PATH");
|
||||
}
|
||||
// Allow users to specify where to find SQLite.
|
||||
@ -105,7 +107,10 @@ mod build {
|
||||
}
|
||||
|
||||
// See if pkg-config can do everything for us.
|
||||
match pkg_config::Config::new().print_system_libs(false).probe(link_lib) {
|
||||
match pkg_config::Config::new()
|
||||
.print_system_libs(false)
|
||||
.probe(link_lib)
|
||||
{
|
||||
Ok(mut lib) => {
|
||||
if let Some(mut header) = lib.include_paths.pop() {
|
||||
header.push("sqlite3.h");
|
||||
@ -162,10 +167,9 @@ mod build {
|
||||
mod bindings {
|
||||
use super::HeaderLocation;
|
||||
|
||||
use std::{env, fs};
|
||||
use std::path::Path;
|
||||
use std::{env, fs};
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
|
||||
"bindgen-bindings/bindgen_3.6.8.rs",
|
||||
|
||||
@ -197,12 +201,12 @@ mod build {
|
||||
mod bindings {
|
||||
extern crate bindgen;
|
||||
|
||||
use self::bindgen::callbacks::{ParseCallbacks, IntKind};
|
||||
use self::bindgen::callbacks::{IntKind, ParseCallbacks};
|
||||
use super::HeaderLocation;
|
||||
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -249,7 +253,8 @@ mod build {
|
||||
.open(path.clone())
|
||||
.expect(&format!("Could not write to {:?}", path));
|
||||
|
||||
file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
|
||||
file.write_all(output.as_bytes())
|
||||
.expect(&format!("Could not write to {:?}", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::os::raw::c_int;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
/// Error Codes
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@ -64,30 +64,30 @@ pub struct Error {
|
||||
impl Error {
|
||||
pub fn new(result_code: c_int) -> Error {
|
||||
let code = match result_code & 0xff {
|
||||
super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
|
||||
super::SQLITE_PERM => ErrorCode::PermissionDenied,
|
||||
super::SQLITE_ABORT => ErrorCode::OperationAborted,
|
||||
super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
|
||||
super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
|
||||
super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
|
||||
super::SQLITE_READONLY => ErrorCode::ReadOnly,
|
||||
super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
|
||||
super::SQLITE_PERM => ErrorCode::PermissionDenied,
|
||||
super::SQLITE_ABORT => ErrorCode::OperationAborted,
|
||||
super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
|
||||
super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
|
||||
super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
|
||||
super::SQLITE_READONLY => ErrorCode::ReadOnly,
|
||||
super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
|
||||
super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
|
||||
super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
|
||||
super::SQLITE_NOTFOUND => ErrorCode::NotFound,
|
||||
super::SQLITE_FULL => ErrorCode::DiskFull,
|
||||
super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
|
||||
super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
|
||||
super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
|
||||
super::SQLITE_TOOBIG => ErrorCode::TooBig,
|
||||
super::SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
|
||||
super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
|
||||
super::SQLITE_MISUSE => ErrorCode::APIMisuse,
|
||||
super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
|
||||
super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
|
||||
super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
|
||||
super::SQLITE_NOTADB => ErrorCode::NotADatabase,
|
||||
_ => ErrorCode::Unknown,
|
||||
super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
|
||||
super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
|
||||
super::SQLITE_NOTFOUND => ErrorCode::NotFound,
|
||||
super::SQLITE_FULL => ErrorCode::DiskFull,
|
||||
super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
|
||||
super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
|
||||
super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
|
||||
super::SQLITE_TOOBIG => ErrorCode::TooBig,
|
||||
super::SQLITE_CONSTRAINT => ErrorCode::ConstraintViolation,
|
||||
super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
|
||||
super::SQLITE_MISUSE => ErrorCode::APIMisuse,
|
||||
super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
|
||||
super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
|
||||
super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
|
||||
super::SQLITE_NOTADB => ErrorCode::NotADatabase,
|
||||
_ => ErrorCode::Unknown,
|
||||
};
|
||||
|
||||
Error {
|
||||
@ -99,7 +99,12 @@ impl Error {
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Error code {}: {}", self.extended_code, code_to_str(self.extended_code))
|
||||
write!(
|
||||
f,
|
||||
"Error code {}: {}",
|
||||
self.extended_code,
|
||||
code_to_str(self.extended_code)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,48 +119,48 @@ impl error::Error for Error {
|
||||
// in the current version of SQLite. We repeat them here so we don't have to worry about which
|
||||
// version of SQLite added which constants, and we only use them to implement code_to_str below.
|
||||
|
||||
const SQLITE_NOTICE : c_int = 27;
|
||||
const SQLITE_WARNING : c_int = 28;
|
||||
const SQLITE_NOTICE: c_int = 27;
|
||||
const SQLITE_WARNING: c_int = 28;
|
||||
|
||||
// Extended result codes.
|
||||
|
||||
const SQLITE_IOERR_SHMOPEN : c_int = (super::SQLITE_IOERR | (18<<8));
|
||||
const SQLITE_IOERR_SHMSIZE : c_int = (super::SQLITE_IOERR | (19<<8));
|
||||
const SQLITE_IOERR_SHMLOCK : c_int = (super::SQLITE_IOERR | (20<<8));
|
||||
const SQLITE_IOERR_SHMMAP : c_int = (super::SQLITE_IOERR | (21<<8));
|
||||
const SQLITE_IOERR_SEEK : c_int = (super::SQLITE_IOERR | (22<<8));
|
||||
const SQLITE_IOERR_DELETE_NOENT : c_int = (super::SQLITE_IOERR | (23<<8));
|
||||
const SQLITE_IOERR_MMAP : c_int = (super::SQLITE_IOERR | (24<<8));
|
||||
const SQLITE_IOERR_GETTEMPPATH : c_int = (super::SQLITE_IOERR | (25<<8));
|
||||
const SQLITE_IOERR_CONVPATH : c_int = (super::SQLITE_IOERR | (26<<8));
|
||||
const SQLITE_IOERR_VNODE : c_int = (super::SQLITE_IOERR | (27<<8));
|
||||
const SQLITE_LOCKED_SHAREDCACHE : c_int = (super::SQLITE_LOCKED | (1<<8));
|
||||
const SQLITE_BUSY_RECOVERY : c_int = (super::SQLITE_BUSY | (1<<8));
|
||||
const SQLITE_BUSY_SNAPSHOT : c_int = (super::SQLITE_BUSY | (2<<8));
|
||||
const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (super::SQLITE_CANTOPEN | (1<<8));
|
||||
const SQLITE_CANTOPEN_ISDIR : c_int = (super::SQLITE_CANTOPEN | (2<<8));
|
||||
const SQLITE_CANTOPEN_FULLPATH : c_int = (super::SQLITE_CANTOPEN | (3<<8));
|
||||
const SQLITE_CANTOPEN_CONVPATH : c_int = (super::SQLITE_CANTOPEN | (4<<8));
|
||||
const SQLITE_CORRUPT_VTAB : c_int = (super::SQLITE_CORRUPT | (1<<8));
|
||||
const SQLITE_READONLY_RECOVERY : c_int = (super::SQLITE_READONLY | (1<<8));
|
||||
const SQLITE_READONLY_CANTLOCK : c_int = (super::SQLITE_READONLY | (2<<8));
|
||||
const SQLITE_READONLY_ROLLBACK : c_int = (super::SQLITE_READONLY | (3<<8));
|
||||
const SQLITE_READONLY_DBMOVED : c_int = (super::SQLITE_READONLY | (4<<8));
|
||||
const SQLITE_ABORT_ROLLBACK : c_int = (super::SQLITE_ABORT | (2<<8));
|
||||
const SQLITE_CONSTRAINT_CHECK : c_int = (super::SQLITE_CONSTRAINT | (1<<8));
|
||||
const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (super::SQLITE_CONSTRAINT | (2<<8));
|
||||
const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (super::SQLITE_CONSTRAINT | (3<<8));
|
||||
const SQLITE_CONSTRAINT_FUNCTION : c_int = (super::SQLITE_CONSTRAINT | (4<<8));
|
||||
const SQLITE_CONSTRAINT_NOTNULL : c_int = (super::SQLITE_CONSTRAINT | (5<<8));
|
||||
const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (super::SQLITE_CONSTRAINT | (6<<8));
|
||||
const SQLITE_CONSTRAINT_TRIGGER : c_int = (super::SQLITE_CONSTRAINT | (7<<8));
|
||||
const SQLITE_CONSTRAINT_UNIQUE : c_int = (super::SQLITE_CONSTRAINT | (8<<8));
|
||||
const SQLITE_CONSTRAINT_VTAB : c_int = (super::SQLITE_CONSTRAINT | (9<<8));
|
||||
const SQLITE_CONSTRAINT_ROWID : c_int = (super::SQLITE_CONSTRAINT |(10<<8));
|
||||
const SQLITE_NOTICE_RECOVER_WAL : c_int = (SQLITE_NOTICE | (1<<8));
|
||||
const SQLITE_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
|
||||
const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8));
|
||||
const SQLITE_AUTH_USER : c_int = (super::SQLITE_AUTH | (1<<8));
|
||||
const SQLITE_IOERR_SHMOPEN: c_int = (super::SQLITE_IOERR | (18 << 8));
|
||||
const SQLITE_IOERR_SHMSIZE: c_int = (super::SQLITE_IOERR | (19 << 8));
|
||||
const SQLITE_IOERR_SHMLOCK: c_int = (super::SQLITE_IOERR | (20 << 8));
|
||||
const SQLITE_IOERR_SHMMAP: c_int = (super::SQLITE_IOERR | (21 << 8));
|
||||
const SQLITE_IOERR_SEEK: c_int = (super::SQLITE_IOERR | (22 << 8));
|
||||
const SQLITE_IOERR_DELETE_NOENT: c_int = (super::SQLITE_IOERR | (23 << 8));
|
||||
const SQLITE_IOERR_MMAP: c_int = (super::SQLITE_IOERR | (24 << 8));
|
||||
const SQLITE_IOERR_GETTEMPPATH: c_int = (super::SQLITE_IOERR | (25 << 8));
|
||||
const SQLITE_IOERR_CONVPATH: c_int = (super::SQLITE_IOERR | (26 << 8));
|
||||
const SQLITE_IOERR_VNODE: c_int = (super::SQLITE_IOERR | (27 << 8));
|
||||
const SQLITE_LOCKED_SHAREDCACHE: c_int = (super::SQLITE_LOCKED | (1 << 8));
|
||||
const SQLITE_BUSY_RECOVERY: c_int = (super::SQLITE_BUSY | (1 << 8));
|
||||
const SQLITE_BUSY_SNAPSHOT: c_int = (super::SQLITE_BUSY | (2 << 8));
|
||||
const SQLITE_CANTOPEN_NOTEMPDIR: c_int = (super::SQLITE_CANTOPEN | (1 << 8));
|
||||
const SQLITE_CANTOPEN_ISDIR: c_int = (super::SQLITE_CANTOPEN | (2 << 8));
|
||||
const SQLITE_CANTOPEN_FULLPATH: c_int = (super::SQLITE_CANTOPEN | (3 << 8));
|
||||
const SQLITE_CANTOPEN_CONVPATH: c_int = (super::SQLITE_CANTOPEN | (4 << 8));
|
||||
const SQLITE_CORRUPT_VTAB: c_int = (super::SQLITE_CORRUPT | (1 << 8));
|
||||
const SQLITE_READONLY_RECOVERY: c_int = (super::SQLITE_READONLY | (1 << 8));
|
||||
const SQLITE_READONLY_CANTLOCK: c_int = (super::SQLITE_READONLY | (2 << 8));
|
||||
const SQLITE_READONLY_ROLLBACK: c_int = (super::SQLITE_READONLY | (3 << 8));
|
||||
const SQLITE_READONLY_DBMOVED: c_int = (super::SQLITE_READONLY | (4 << 8));
|
||||
const SQLITE_ABORT_ROLLBACK: c_int = (super::SQLITE_ABORT | (2 << 8));
|
||||
const SQLITE_CONSTRAINT_CHECK: c_int = (super::SQLITE_CONSTRAINT | (1 << 8));
|
||||
const SQLITE_CONSTRAINT_COMMITHOOK: c_int = (super::SQLITE_CONSTRAINT | (2 << 8));
|
||||
const SQLITE_CONSTRAINT_FOREIGNKEY: c_int = (super::SQLITE_CONSTRAINT | (3 << 8));
|
||||
const SQLITE_CONSTRAINT_FUNCTION: c_int = (super::SQLITE_CONSTRAINT | (4 << 8));
|
||||
const SQLITE_CONSTRAINT_NOTNULL: c_int = (super::SQLITE_CONSTRAINT | (5 << 8));
|
||||
const SQLITE_CONSTRAINT_PRIMARYKEY: c_int = (super::SQLITE_CONSTRAINT | (6 << 8));
|
||||
const SQLITE_CONSTRAINT_TRIGGER: c_int = (super::SQLITE_CONSTRAINT | (7 << 8));
|
||||
const SQLITE_CONSTRAINT_UNIQUE: c_int = (super::SQLITE_CONSTRAINT | (8 << 8));
|
||||
const SQLITE_CONSTRAINT_VTAB: c_int = (super::SQLITE_CONSTRAINT | (9 << 8));
|
||||
const SQLITE_CONSTRAINT_ROWID: c_int = (super::SQLITE_CONSTRAINT | (10 << 8));
|
||||
const SQLITE_NOTICE_RECOVER_WAL: c_int = (SQLITE_NOTICE | (1 << 8));
|
||||
const SQLITE_NOTICE_RECOVER_ROLLBACK: c_int = (SQLITE_NOTICE | (2 << 8));
|
||||
const SQLITE_WARNING_AUTOINDEX: c_int = (SQLITE_WARNING | (1 << 8));
|
||||
const SQLITE_AUTH_USER: c_int = (super::SQLITE_AUTH | (1 << 8));
|
||||
|
||||
pub fn code_to_str(code: c_int) -> &'static str {
|
||||
match code {
|
||||
|
166
src/backup.rs
166
src/backup.rs
@ -36,8 +36,8 @@ use std::time::Duration;
|
||||
|
||||
use ffi;
|
||||
|
||||
use {DatabaseName, Connection, Result};
|
||||
use error::{error_from_sqlite_code, error_from_handle};
|
||||
use error::{error_from_handle, error_from_sqlite_code};
|
||||
use {Connection, DatabaseName, Result};
|
||||
|
||||
impl Connection {
|
||||
/// Back up the `name` database to the given destination path.
|
||||
@ -52,14 +52,20 @@ impl Connection {
|
||||
///
|
||||
/// Will return `Err` if the destination path cannot be opened
|
||||
/// or if the backup fails.
|
||||
pub fn backup<P: AsRef<Path>>(&self,
|
||||
name: DatabaseName,
|
||||
dst_path: P,
|
||||
progress: Option<fn(Progress)>)
|
||||
-> Result<()> {
|
||||
use self::StepResult::{More, Done, Busy, Locked};
|
||||
pub fn backup<P: AsRef<Path>>(
|
||||
&self,
|
||||
name: DatabaseName,
|
||||
dst_path: P,
|
||||
progress: Option<fn(Progress)>,
|
||||
) -> Result<()> {
|
||||
use self::StepResult::{Busy, Done, Locked, More};
|
||||
let mut dst = try!(Connection::open(dst_path));
|
||||
let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main));
|
||||
let backup = try!(Backup::new_with_names(
|
||||
self,
|
||||
name,
|
||||
&mut dst,
|
||||
DatabaseName::Main
|
||||
));
|
||||
|
||||
let mut r = More;
|
||||
while r == More {
|
||||
@ -89,12 +95,13 @@ impl Connection {
|
||||
///
|
||||
/// Will return `Err` if the destination path cannot be opened
|
||||
/// or if the restore fails.
|
||||
pub fn restore<P: AsRef<Path>>(&mut self,
|
||||
name: DatabaseName,
|
||||
src_path: P,
|
||||
progress: Option<fn(Progress)>)
|
||||
-> Result<()> {
|
||||
use self::StepResult::{More, Done, Busy, Locked};
|
||||
pub fn restore<P: AsRef<Path>>(
|
||||
&mut self,
|
||||
name: DatabaseName,
|
||||
src_path: P,
|
||||
progress: Option<fn(Progress)>,
|
||||
) -> Result<()> {
|
||||
use self::StepResult::{Busy, Done, Locked, More};
|
||||
let src = try!(Connection::open(src_path));
|
||||
let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name));
|
||||
|
||||
@ -124,7 +131,7 @@ impl Connection {
|
||||
}
|
||||
|
||||
/// Possible successful results of calling `Backup::step`.
|
||||
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum StepResult {
|
||||
/// The backup is complete.
|
||||
Done,
|
||||
@ -146,7 +153,7 @@ pub enum StepResult {
|
||||
/// backup is as of the last call to `step` - if the source database is
|
||||
/// modified after a call to `step`, the progress value will become outdated
|
||||
/// and potentially incorrect.
|
||||
#[derive(Copy,Clone,Debug)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Progress {
|
||||
/// Number of pages in the source database that still need to be backed up.
|
||||
pub remaining: c_int,
|
||||
@ -184,21 +191,24 @@ impl<'a, 'b> Backup<'a, 'b> {
|
||||
///
|
||||
/// Will return `Err` if the underlying `sqlite3_backup_init` call returns
|
||||
/// `NULL`.
|
||||
pub fn new_with_names(from: &'a Connection,
|
||||
from_name: DatabaseName,
|
||||
to: &'b mut Connection,
|
||||
to_name: DatabaseName)
|
||||
-> Result<Backup<'a, 'b>> {
|
||||
pub fn new_with_names(
|
||||
from: &'a Connection,
|
||||
from_name: DatabaseName,
|
||||
to: &'b mut Connection,
|
||||
to_name: DatabaseName,
|
||||
) -> Result<Backup<'a, 'b>> {
|
||||
let to_name = try!(to_name.to_cstring());
|
||||
let from_name = try!(from_name.to_cstring());
|
||||
|
||||
let to_db = to.db.borrow_mut().db;
|
||||
|
||||
let b = unsafe {
|
||||
let b = ffi::sqlite3_backup_init(to_db,
|
||||
to_name.as_ptr(),
|
||||
from.db.borrow_mut().db,
|
||||
from_name.as_ptr());
|
||||
let b = ffi::sqlite3_backup_init(
|
||||
to_db,
|
||||
to_name.as_ptr(),
|
||||
from.db.borrow_mut().db,
|
||||
from_name.as_ptr(),
|
||||
);
|
||||
if b.is_null() {
|
||||
return Err(error_from_handle(to_db, ffi::sqlite3_errcode(to_db)));
|
||||
}
|
||||
@ -206,10 +216,10 @@ impl<'a, 'b> Backup<'a, 'b> {
|
||||
};
|
||||
|
||||
Ok(Backup {
|
||||
phantom_from: PhantomData,
|
||||
phantom_to: PhantomData,
|
||||
b,
|
||||
})
|
||||
phantom_from: PhantomData,
|
||||
phantom_to: PhantomData,
|
||||
b,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the progress of the backup as of the last call to `step`.
|
||||
@ -235,7 +245,7 @@ impl<'a, 'b> Backup<'a, 'b> {
|
||||
/// `LOCKED` are transient errors and are therefore returned as possible
|
||||
/// `Ok` values.
|
||||
pub fn step(&self, num_pages: c_int) -> Result<StepResult> {
|
||||
use self::StepResult::{Done, More, Busy, Locked};
|
||||
use self::StepResult::{Busy, Done, Locked, More};
|
||||
|
||||
let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) };
|
||||
match rc {
|
||||
@ -262,12 +272,13 @@ impl<'a, 'b> Backup<'a, 'b> {
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if any of the calls to `step` return `Err`.
|
||||
pub fn run_to_completion(&self,
|
||||
pages_per_step: c_int,
|
||||
pause_between_pages: Duration,
|
||||
progress: Option<fn(Progress)>)
|
||||
-> Result<()> {
|
||||
use self::StepResult::{Done, More, Busy, Locked};
|
||||
pub fn run_to_completion(
|
||||
&self,
|
||||
pages_per_step: c_int,
|
||||
pause_between_pages: Duration,
|
||||
progress: Option<fn(Progress)>,
|
||||
) -> Result<()> {
|
||||
use self::StepResult::{Busy, Done, Locked, More};
|
||||
|
||||
assert!(pages_per_step > 0, "pages_per_step must be positive");
|
||||
|
||||
@ -292,12 +303,11 @@ impl<'a, 'b> Drop for Backup<'a, 'b> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, DatabaseName};
|
||||
use std::time::Duration;
|
||||
use super::Backup;
|
||||
use std::time::Duration;
|
||||
use {Connection, DatabaseName};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_backup() {
|
||||
let src = Connection::open_in_memory().unwrap();
|
||||
let sql = "BEGIN;
|
||||
@ -313,22 +323,27 @@ mod test {
|
||||
backup.step(-1).unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42, the_answer);
|
||||
|
||||
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
|
||||
|
||||
{
|
||||
let backup = Backup::new(&src, &mut dst).unwrap();
|
||||
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
|
||||
backup
|
||||
.run_to_completion(5, Duration::from_millis(250), None)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42 + 43, the_answer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_backup_temp() {
|
||||
let src = Connection::open_in_memory().unwrap();
|
||||
let sql = "BEGIN;
|
||||
@ -340,34 +355,35 @@ mod test {
|
||||
let mut dst = Connection::open_in_memory().unwrap();
|
||||
|
||||
{
|
||||
let backup = Backup::new_with_names(&src,
|
||||
DatabaseName::Temp,
|
||||
&mut dst,
|
||||
DatabaseName::Main)
|
||||
.unwrap();
|
||||
let backup =
|
||||
Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
|
||||
.unwrap();
|
||||
backup.step(-1).unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42, the_answer);
|
||||
|
||||
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
|
||||
|
||||
{
|
||||
let backup = Backup::new_with_names(&src,
|
||||
DatabaseName::Temp,
|
||||
&mut dst,
|
||||
DatabaseName::Main)
|
||||
.unwrap();
|
||||
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
|
||||
let backup =
|
||||
Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
|
||||
.unwrap();
|
||||
backup
|
||||
.run_to_completion(5, Duration::from_millis(250), None)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42 + 43, the_answer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_backup_attached() {
|
||||
let src = Connection::open_in_memory().unwrap();
|
||||
let sql = "ATTACH DATABASE ':memory:' AS my_attached;
|
||||
@ -380,29 +396,37 @@ mod test {
|
||||
let mut dst = Connection::open_in_memory().unwrap();
|
||||
|
||||
{
|
||||
let backup = Backup::new_with_names(&src,
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main)
|
||||
.unwrap();
|
||||
let backup = Backup::new_with_names(
|
||||
&src,
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main,
|
||||
).unwrap();
|
||||
backup.step(-1).unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42, the_answer);
|
||||
|
||||
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
|
||||
|
||||
{
|
||||
let backup = Backup::new_with_names(&src,
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main)
|
||||
.unwrap();
|
||||
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
|
||||
let backup = Backup::new_with_names(
|
||||
&src,
|
||||
DatabaseName::Attached("my_attached"),
|
||||
&mut dst,
|
||||
DatabaseName::Main,
|
||||
).unwrap();
|
||||
backup
|
||||
.run_to_completion(5, Duration::from_millis(250), None)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let the_answer: i64 = dst
|
||||
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(42 + 43, the_answer);
|
||||
}
|
||||
}
|
||||
|
107
src/blob.rs
107
src/blob.rs
@ -48,13 +48,13 @@
|
||||
//! assert_eq!(blob.size(), 64);
|
||||
//! }
|
||||
//! ```
|
||||
use std::io;
|
||||
use std::cmp::min;
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
|
||||
use super::ffi;
|
||||
use super::types::{ToSql, ToSqlOutput};
|
||||
use {Result, Connection, DatabaseName};
|
||||
use {Connection, DatabaseName, Result};
|
||||
|
||||
/// Handle to an open BLOB.
|
||||
pub struct Blob<'conn> {
|
||||
@ -70,35 +70,35 @@ impl Connection {
|
||||
///
|
||||
/// Will return `Err` if `db`/`table`/`column` cannot be converted to a C-compatible string
|
||||
/// or if the underlying SQLite BLOB open call fails.
|
||||
pub fn blob_open<'a>(&'a self,
|
||||
db: DatabaseName,
|
||||
table: &str,
|
||||
column: &str,
|
||||
row_id: i64,
|
||||
read_only: bool)
|
||||
-> Result<Blob<'a>> {
|
||||
pub fn blob_open<'a>(
|
||||
&'a self,
|
||||
db: DatabaseName,
|
||||
table: &str,
|
||||
column: &str,
|
||||
row_id: i64,
|
||||
read_only: bool,
|
||||
) -> Result<Blob<'a>> {
|
||||
let mut c = self.db.borrow_mut();
|
||||
let mut blob = ptr::null_mut();
|
||||
let db = try!(db.to_cstring());
|
||||
let table = try!(super::str_to_cstring(table));
|
||||
let column = try!(super::str_to_cstring(column));
|
||||
let rc = unsafe {
|
||||
ffi::sqlite3_blob_open(c.db(),
|
||||
db.as_ptr(),
|
||||
table.as_ptr(),
|
||||
column.as_ptr(),
|
||||
row_id,
|
||||
if read_only { 0 } else { 1 },
|
||||
&mut blob)
|
||||
ffi::sqlite3_blob_open(
|
||||
c.db(),
|
||||
db.as_ptr(),
|
||||
table.as_ptr(),
|
||||
column.as_ptr(),
|
||||
row_id,
|
||||
if read_only { 0 } else { 1 },
|
||||
&mut blob,
|
||||
)
|
||||
};
|
||||
c.decode_result(rc)
|
||||
.map(|_| {
|
||||
Blob {
|
||||
conn: self,
|
||||
blob,
|
||||
pos: 0,
|
||||
}
|
||||
})
|
||||
c.decode_result(rc).map(|_| Blob {
|
||||
conn: self,
|
||||
blob,
|
||||
pos: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,15 +154,13 @@ impl<'conn> io::Read for Blob<'conn> {
|
||||
if n <= 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let rc =
|
||||
unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
|
||||
let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
|
||||
self.conn
|
||||
.decode_result(rc)
|
||||
.map(|_| {
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
self.pos += n;
|
||||
n as usize
|
||||
}).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,16 +181,13 @@ impl<'conn> io::Write for Blob<'conn> {
|
||||
if n <= 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let rc = unsafe {
|
||||
ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos)
|
||||
};
|
||||
let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
|
||||
self.conn
|
||||
.decode_result(rc)
|
||||
.map(|_| {
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
self.pos += n;
|
||||
n as usize
|
||||
}).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
@ -210,11 +205,15 @@ impl<'conn> io::Seek for Blob<'conn> {
|
||||
};
|
||||
|
||||
if pos < 0 {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"invalid seek to negative position"))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid seek to negative position",
|
||||
))
|
||||
} else if pos > i64::from(self.size()) {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||
"invalid seek to position past end of blob"))
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"invalid seek to position past end of blob",
|
||||
))
|
||||
} else {
|
||||
self.pos = pos as i32;
|
||||
Ok(pos as u64)
|
||||
@ -235,7 +234,7 @@ impl<'conn> Drop for Blob<'conn> {
|
||||
/// incremental BLOB I/O routines.
|
||||
///
|
||||
/// A negative value for the zeroblob results in a zero-length BLOB.
|
||||
#[derive(Copy,Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ZeroBlob(pub i32);
|
||||
|
||||
impl ToSql for ZeroBlob {
|
||||
@ -247,10 +246,9 @@ impl ToSql for ZeroBlob {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::io::{BufReader, BufRead, BufWriter, Read, Write, Seek, SeekFrom};
|
||||
use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
|
||||
use {Connection, DatabaseName, Result};
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn db_with_test_blob() -> Result<(Connection, i64)> {
|
||||
let db = try!(Connection::open_in_memory());
|
||||
let sql = "BEGIN;
|
||||
@ -266,7 +264,8 @@ mod test {
|
||||
fn test_blob() {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let mut blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
assert_eq!(4, blob.write(b"Clob").unwrap());
|
||||
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
|
||||
@ -275,7 +274,8 @@ mod test {
|
||||
blob.reopen(rowid).unwrap();
|
||||
blob.close().unwrap();
|
||||
|
||||
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
|
||||
blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, true)
|
||||
.unwrap();
|
||||
let mut bytes = [0u8; 5];
|
||||
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
|
||||
@ -316,7 +316,8 @@ mod test {
|
||||
fn test_blob_in_bufreader() {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let mut blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
|
||||
|
||||
@ -341,7 +342,8 @@ mod test {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
{
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut writer = BufWriter::new(blob);
|
||||
|
||||
@ -353,7 +355,8 @@ mod test {
|
||||
|
||||
{
|
||||
// ... but it should've written the first 10 bytes
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let mut blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut bytes = [0u8; 10];
|
||||
assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
|
||||
@ -361,7 +364,8 @@ mod test {
|
||||
}
|
||||
|
||||
{
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut writer = BufWriter::new(blob);
|
||||
|
||||
@ -372,7 +376,8 @@ mod test {
|
||||
|
||||
{
|
||||
// ... but it should've written the first 10 bytes
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
let mut blob = db
|
||||
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut bytes = [0u8; 10];
|
||||
assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
|
||||
|
12
src/busy.rs
12
src/busy.rs
@ -1,8 +1,8 @@
|
||||
///! Busy handler (when the database is locked)
|
||||
use std::time::Duration;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
use ffi;
|
||||
use {Connection, InnerConnection, Result};
|
||||
@ -67,8 +67,8 @@ mod test {
|
||||
use self::tempdir::TempDir;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::sync_channel;
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use {Connection, Error, ErrorCode, TransactionBehavior};
|
||||
|
||||
@ -113,8 +113,8 @@ mod test {
|
||||
});
|
||||
|
||||
assert_eq!(tx.recv().unwrap(), 1);
|
||||
let _ =
|
||||
db2.query_row("PRAGMA schema_version", &[], |row| {
|
||||
let _ = db2
|
||||
.query_row("PRAGMA schema_version", &[], |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
|
||||
@ -151,8 +151,8 @@ mod test {
|
||||
});
|
||||
|
||||
assert_eq!(tx.recv().unwrap(), 1);
|
||||
let _ =
|
||||
db2.query_row("PRAGMA schema_version", &[], |row| {
|
||||
let _ = db2
|
||||
.query_row("PRAGMA schema_version", &[], |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
assert_eq!(CALLED.load(Ordering::Relaxed), true);
|
||||
|
66
src/cache.rs
66
src/cache.rs
@ -1,10 +1,10 @@
|
||||
//! Prepared statements cache for faster execution.
|
||||
|
||||
use lru_cache::LruCache;
|
||||
use raw_statement::RawStatement;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use lru_cache::LruCache;
|
||||
use {Result, Connection, Statement};
|
||||
use raw_statement::RawStatement;
|
||||
use {Connection, Result, Statement};
|
||||
|
||||
impl Connection {
|
||||
/// Prepare a SQL statement for execution, returning a previously prepared (but
|
||||
@ -119,10 +119,11 @@ impl StatementCache {
|
||||
//
|
||||
// Will return `Err` if no cached statement can be found and the underlying SQLite prepare
|
||||
// call fails.
|
||||
fn get<'conn>(&'conn self,
|
||||
conn: &'conn Connection,
|
||||
sql: &str)
|
||||
-> Result<CachedStatement<'conn>> {
|
||||
fn get<'conn>(
|
||||
&'conn self,
|
||||
conn: &'conn Connection,
|
||||
sql: &str,
|
||||
) -> Result<CachedStatement<'conn>> {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
let stmt = match cache.remove(sql.trim()) {
|
||||
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
|
||||
@ -135,7 +136,9 @@ impl StatementCache {
|
||||
fn cache_stmt(&self, stmt: RawStatement) {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
stmt.clear_bindings();
|
||||
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).trim().to_string();
|
||||
let sql = String::from_utf8_lossy(stmt.sql().to_bytes())
|
||||
.trim()
|
||||
.to_string();
|
||||
cache.insert(sql, stmt);
|
||||
}
|
||||
|
||||
@ -147,8 +150,8 @@ impl StatementCache {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use super::StatementCache;
|
||||
use Connection;
|
||||
|
||||
impl StatementCache {
|
||||
fn clear(&self) {
|
||||
@ -242,46 +245,51 @@ mod test {
|
||||
#[test]
|
||||
fn test_ddl() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch(r#"
|
||||
db.execute_batch(
|
||||
r#"
|
||||
CREATE TABLE foo (x INT);
|
||||
INSERT INTO foo VALUES (1);
|
||||
"#)
|
||||
.unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let sql = "SELECT * FROM foo";
|
||||
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(1i32,
|
||||
stmt.query_map::<i32, _>(&[], |r| r.get(0))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
1i32,
|
||||
stmt.query_map::<i32, _>(&[], |r| r.get(0))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
db.execute_batch(r#"
|
||||
db.execute_batch(
|
||||
r#"
|
||||
ALTER TABLE foo ADD COLUMN y INT;
|
||||
UPDATE foo SET y = 2;
|
||||
"#)
|
||||
.unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!((1i32, 2i32),
|
||||
stmt.query_map(&[], |r| (r.get(0), r.get(1)))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
(1i32, 2i32),
|
||||
stmt.query_map(&[], |r| (r.get(0), r.get(1)))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_close() {
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
conn.prepare_cached("SELECT * FROM sqlite_master;")
|
||||
.unwrap();
|
||||
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
|
||||
|
||||
conn.close().expect("connection not closed");
|
||||
}
|
||||
|
58
src/error.rs
58
src/error.rs
@ -1,10 +1,10 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::os::raw::c_int;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
use std::os::raw::c_int;
|
||||
use {ffi, errmsg_to_string};
|
||||
use types::Type;
|
||||
use {errmsg_to_string, ffi};
|
||||
|
||||
/// Old name for `Error`. `SqliteError` is deprecated.
|
||||
#[deprecated(since = "0.6.0", note = "Use Error instead")]
|
||||
@ -99,17 +99,15 @@ impl fmt::Display for Error {
|
||||
match *self {
|
||||
Error::SqliteFailure(ref err, None) => err.fmt(f),
|
||||
Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
|
||||
Error::SqliteSingleThreadedMode => {
|
||||
write!(f,
|
||||
"SQLite was compiled or configured for single-threaded use only")
|
||||
}
|
||||
Error::FromSqlConversionFailure(i, ref t, ref err) => {
|
||||
write!(f,
|
||||
"Conversion error from type {} at index: {}, {}",
|
||||
t,
|
||||
i,
|
||||
err)
|
||||
}
|
||||
Error::SqliteSingleThreadedMode => write!(
|
||||
f,
|
||||
"SQLite was compiled or configured for single-threaded use only"
|
||||
),
|
||||
Error::FromSqlConversionFailure(i, ref t, ref err) => write!(
|
||||
f,
|
||||
"Conversion error from type {} at index: {}, {}",
|
||||
t, i, err
|
||||
),
|
||||
Error::IntegralValueOutOfRange(col, val) => {
|
||||
write!(f, "Integer {} out of range at index {}", val, col)
|
||||
}
|
||||
@ -145,14 +143,18 @@ impl error::Error for Error {
|
||||
match *self {
|
||||
Error::SqliteFailure(ref err, None) => err.description(),
|
||||
Error::SqliteFailure(_, Some(ref s)) => s,
|
||||
Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
|
||||
Error::SqliteSingleThreadedMode => {
|
||||
"SQLite was compiled or configured for single-threaded use only"
|
||||
}
|
||||
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
|
||||
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
|
||||
Error::Utf8Error(ref err) => err.description(),
|
||||
Error::InvalidParameterName(_) => "invalid parameter name",
|
||||
Error::NulError(ref err) => err.description(),
|
||||
Error::InvalidPath(_) => "invalid path",
|
||||
Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
|
||||
Error::ExecuteReturnedResults => {
|
||||
"execute returned results - did you mean to call query?"
|
||||
}
|
||||
Error::QueryReturnedNoRows => "query returned no rows",
|
||||
Error::InvalidColumnIndex(_) => "invalid column index",
|
||||
Error::InvalidColumnName(_) => "invalid column name",
|
||||
@ -174,17 +176,17 @@ impl error::Error for Error {
|
||||
Error::Utf8Error(ref err) => Some(err),
|
||||
Error::NulError(ref err) => Some(err),
|
||||
|
||||
Error::IntegralValueOutOfRange(_, _) |
|
||||
Error::SqliteSingleThreadedMode |
|
||||
Error::InvalidParameterName(_) |
|
||||
Error::ExecuteReturnedResults |
|
||||
Error::QueryReturnedNoRows |
|
||||
Error::InvalidColumnIndex(_) |
|
||||
Error::InvalidColumnName(_) |
|
||||
Error::InvalidColumnType(_, _) |
|
||||
Error::InvalidPath(_) |
|
||||
Error::StatementChangedRows(_) |
|
||||
Error::InvalidQuery => None,
|
||||
Error::IntegralValueOutOfRange(_, _)
|
||||
| Error::SqliteSingleThreadedMode
|
||||
| Error::InvalidParameterName(_)
|
||||
| Error::ExecuteReturnedResults
|
||||
| Error::QueryReturnedNoRows
|
||||
| Error::InvalidColumnIndex(_)
|
||||
| Error::InvalidColumnName(_)
|
||||
| Error::InvalidColumnType(_, _)
|
||||
| Error::InvalidPath(_)
|
||||
| Error::StatementChangedRows(_)
|
||||
| Error::InvalidQuery => None,
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::InvalidFunctionParameterType(_, _) => None,
|
||||
@ -192,8 +194,8 @@ impl error::Error for Error {
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UserFunctionError(ref err) => Some(&**err),
|
||||
|
||||
Error::FromSqlConversionFailure(_, _, ref err) |
|
||||
Error::ToSqlConversionFailure(ref err) => Some(&**err),
|
||||
Error::FromSqlConversionFailure(_, _, ref err)
|
||||
| Error::ToSqlConversionFailure(ref err) => Some(&**err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
356
src/functions.rs
356
src/functions.rs
@ -51,17 +51,17 @@
|
||||
//! ```
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::os::raw::{c_int, c_char, c_void};
|
||||
|
||||
use ffi;
|
||||
use ffi::sqlite3_context;
|
||||
use ffi::sqlite3_value;
|
||||
|
||||
use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
|
||||
use types::{FromSql, FromSqlError, ToSql, ToSqlOutput, ValueRef};
|
||||
|
||||
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
||||
use {str_to_cstring, Connection, Error, InnerConnection, Result};
|
||||
|
||||
fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
|
||||
let value = match *result {
|
||||
@ -103,10 +103,12 @@ fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_result_zeroblob(ctx, 0)
|
||||
} else {
|
||||
ffi::sqlite3_result_blob(ctx,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT());
|
||||
ffi::sqlite3_result_blob(
|
||||
ctx,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT(),
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -152,23 +154,33 @@ impl<'a> ValueRef<'a> {
|
||||
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
|
||||
ffi::SQLITE_TEXT => {
|
||||
let text = ffi::sqlite3_value_text(value);
|
||||
assert!(!text.is_null(),
|
||||
"unexpected SQLITE_TEXT value type with NULL data");
|
||||
assert!(
|
||||
!text.is_null(),
|
||||
"unexpected SQLITE_TEXT value type with NULL data"
|
||||
);
|
||||
let s = CStr::from_ptr(text as *const c_char);
|
||||
|
||||
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
|
||||
let s = s.to_str()
|
||||
let s = s
|
||||
.to_str()
|
||||
.expect("sqlite3_value_text returned invalid UTF-8");
|
||||
ValueRef::Text(s)
|
||||
}
|
||||
ffi::SQLITE_BLOB => {
|
||||
let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value));
|
||||
let (blob, len) = (
|
||||
ffi::sqlite3_value_blob(value),
|
||||
ffi::sqlite3_value_bytes(value),
|
||||
);
|
||||
|
||||
assert!(len >= 0,
|
||||
"unexpected negative return from sqlite3_value_bytes");
|
||||
assert!(
|
||||
len >= 0,
|
||||
"unexpected negative return from sqlite3_value_bytes"
|
||||
);
|
||||
if len > 0 {
|
||||
assert!(!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB value type with NULL data");
|
||||
assert!(
|
||||
!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB value type with NULL data"
|
||||
);
|
||||
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
||||
} else {
|
||||
// The return value from sqlite3_value_blob() for a zero-length BLOB
|
||||
@ -212,17 +224,14 @@ impl<'a> Context<'a> {
|
||||
let arg = self.args[idx];
|
||||
let value = unsafe { ValueRef::from_value(arg) };
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
FromSqlError::InvalidType => {
|
||||
FromSqlError::InvalidType => {
|
||||
Error::InvalidFunctionParameterType(idx, value.data_type())
|
||||
}
|
||||
FromSqlError::OutOfRange(i) => {
|
||||
Error::IntegralValueOutOfRange(idx,
|
||||
i)
|
||||
}
|
||||
FromSqlError::Other(err) => {
|
||||
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx, value.data_type(), err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the auxilliary data associated with a particular parameter. See
|
||||
@ -231,10 +240,12 @@ impl<'a> Context<'a> {
|
||||
pub fn set_aux<T>(&self, arg: c_int, value: T) {
|
||||
let boxed = Box::into_raw(Box::new(value));
|
||||
unsafe {
|
||||
ffi::sqlite3_set_auxdata(self.ctx,
|
||||
arg,
|
||||
boxed as *mut c_void,
|
||||
Some(free_boxed_value::<T>))
|
||||
ffi::sqlite3_set_auxdata(
|
||||
self.ctx,
|
||||
arg,
|
||||
boxed as *mut c_void,
|
||||
Some(free_boxed_value::<T>),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@ -248,7 +259,11 @@ impl<'a> Context<'a> {
|
||||
/// types must be identical.
|
||||
pub unsafe fn get_aux<T>(&self, arg: c_int) -> Option<&T> {
|
||||
let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T;
|
||||
if p.is_null() { None } else { Some(&*p) }
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +272,8 @@ impl<'a> Context<'a> {
|
||||
/// `A` is the type of the aggregation context and `T` is the type of the final result.
|
||||
/// Implementations should be stateless.
|
||||
pub trait Aggregate<A, T>
|
||||
where T: ToSql
|
||||
where
|
||||
T: ToSql,
|
||||
{
|
||||
/// Initializes the aggregation context. Will be called prior to the first call
|
||||
/// to `step()` to set up the context for an invocation of the function. (Note:
|
||||
@ -306,14 +322,16 @@ impl Connection {
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return Err if the function could not be attached to the connection.
|
||||
pub fn create_scalar_function<F, T>(&self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
x_func: F)
|
||||
-> Result<()>
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql
|
||||
pub fn create_scalar_function<F, T>(
|
||||
&self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
x_func: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql,
|
||||
{
|
||||
self.db
|
||||
.borrow_mut()
|
||||
@ -325,14 +343,16 @@ impl Connection {
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return Err if the function could not be attached to the connection.
|
||||
pub fn create_aggregate_function<A, D, T>(&self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
aggr: D)
|
||||
-> Result<()>
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToSql
|
||||
pub fn create_aggregate_function<A, D, T>(
|
||||
&self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
aggr: D,
|
||||
) -> Result<()>
|
||||
where
|
||||
D: Aggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
self.db
|
||||
.borrow_mut()
|
||||
@ -353,20 +373,24 @@ impl Connection {
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
fn create_scalar_function<F, T>(&mut self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
x_func: F)
|
||||
-> Result<()>
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql
|
||||
fn create_scalar_function<F, T>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
x_func: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql,
|
||||
{
|
||||
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value)
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql
|
||||
unsafe extern "C" fn call_boxed_closure<F, T>(
|
||||
ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value,
|
||||
) where
|
||||
F: FnMut(&Context) -> Result<T>,
|
||||
T: ToSql,
|
||||
{
|
||||
let ctx = Context {
|
||||
ctx,
|
||||
@ -392,31 +416,36 @@ impl InnerConnection {
|
||||
flags |= ffi::SQLITE_DETERMINISTIC;
|
||||
}
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_create_function_v2(self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
flags,
|
||||
boxed_f as *mut c_void,
|
||||
Some(call_boxed_closure::<F, T>),
|
||||
None,
|
||||
None,
|
||||
Some(free_boxed_value::<F>))
|
||||
ffi::sqlite3_create_function_v2(
|
||||
self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
flags,
|
||||
boxed_f as *mut c_void,
|
||||
Some(call_boxed_closure::<F, T>),
|
||||
None,
|
||||
None,
|
||||
Some(free_boxed_value::<F>),
|
||||
)
|
||||
};
|
||||
self.decode_result(r)
|
||||
}
|
||||
|
||||
fn create_aggregate_function<A, D, T>(&mut self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
aggr: D)
|
||||
-> Result<()>
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToSql
|
||||
fn create_aggregate_function<A, D, T>(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
n_arg: c_int,
|
||||
deterministic: bool,
|
||||
aggr: D,
|
||||
) -> Result<()>
|
||||
where
|
||||
D: Aggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
|
||||
bytes: usize)
|
||||
-> Option<*mut *mut A> {
|
||||
unsafe fn aggregate_context<A>(
|
||||
ctx: *mut sqlite3_context,
|
||||
bytes: usize,
|
||||
) -> Option<*mut *mut A> {
|
||||
let pac = ffi::sqlite3_aggregate_context(ctx, bytes as c_int) as *mut *mut A;
|
||||
if pac.is_null() {
|
||||
return None;
|
||||
@ -424,15 +453,19 @@ impl InnerConnection {
|
||||
Some(pac)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value)
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToSql
|
||||
unsafe extern "C" fn call_boxed_step<A, D, T>(
|
||||
ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value,
|
||||
) where
|
||||
D: Aggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
|
||||
assert!(!boxed_aggr.is_null(),
|
||||
"Internal error - null aggregate pointer");
|
||||
assert!(
|
||||
!boxed_aggr.is_null(),
|
||||
"Internal error - null aggregate pointer"
|
||||
);
|
||||
|
||||
let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
|
||||
Some(pac) => pac,
|
||||
@ -458,12 +491,15 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToSql
|
||||
where
|
||||
D: Aggregate<A, T>,
|
||||
T: ToSql,
|
||||
{
|
||||
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
|
||||
assert!(!boxed_aggr.is_null(),
|
||||
"Internal error - null aggregate pointer");
|
||||
assert!(
|
||||
!boxed_aggr.is_null(),
|
||||
"Internal error - null aggregate pointer"
|
||||
);
|
||||
|
||||
// Within the xFinal callback, it is customary to set N=0 in calls to
|
||||
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
|
||||
@ -495,15 +531,17 @@ impl InnerConnection {
|
||||
flags |= ffi::SQLITE_DETERMINISTIC;
|
||||
}
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_create_function_v2(self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
flags,
|
||||
boxed_aggr as *mut c_void,
|
||||
None,
|
||||
Some(call_boxed_step::<A, D, T>),
|
||||
Some(call_boxed_final::<A, D, T>),
|
||||
Some(free_boxed_value::<D>))
|
||||
ffi::sqlite3_create_function_v2(
|
||||
self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
flags,
|
||||
boxed_aggr as *mut c_void,
|
||||
None,
|
||||
Some(call_boxed_step::<A, D, T>),
|
||||
Some(call_boxed_final::<A, D, T>),
|
||||
Some(free_boxed_value::<D>),
|
||||
)
|
||||
};
|
||||
self.decode_result(r)
|
||||
}
|
||||
@ -511,15 +549,17 @@ impl InnerConnection {
|
||||
fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> Result<()> {
|
||||
let c_name = try!(str_to_cstring(fn_name));
|
||||
let r = unsafe {
|
||||
ffi::sqlite3_create_function_v2(self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
ffi::SQLITE_UTF8,
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None)
|
||||
ffi::sqlite3_create_function_v2(
|
||||
self.db(),
|
||||
c_name.as_ptr(),
|
||||
n_arg,
|
||||
ffi::SQLITE_UTF8,
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
self.decode_result(r)
|
||||
}
|
||||
@ -529,13 +569,13 @@ impl InnerConnection {
|
||||
mod test {
|
||||
extern crate regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_double;
|
||||
use self::regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::f64::EPSILON;
|
||||
use std::os::raw::c_double;
|
||||
|
||||
use {Connection, Error, Result};
|
||||
use functions::{Aggregate, Context};
|
||||
use {Connection, Error, Result};
|
||||
|
||||
fn half(ctx: &Context) -> Result<c_double> {
|
||||
assert!(ctx.len() == 1, "called with unexpected number of arguments");
|
||||
@ -597,41 +637,44 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_function_regexp_with_auxilliary() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("BEGIN;
|
||||
CREATE TABLE foo (x string);
|
||||
INSERT INTO foo VALUES ('lisa');
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;").unwrap();
|
||||
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary).unwrap();
|
||||
db.execute_batch(
|
||||
"BEGIN;
|
||||
CREATE TABLE foo (x string);
|
||||
INSERT INTO foo VALUES ('lisa');
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;",
|
||||
).unwrap();
|
||||
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary)
|
||||
.unwrap();
|
||||
|
||||
let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
|
||||
&[],
|
||||
|r| r.get(0));
|
||||
let result: Result<bool> =
|
||||
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
|
||||
|
||||
assert_eq!(true, result.unwrap());
|
||||
|
||||
let result: Result<i64> =
|
||||
db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
|
||||
&[],
|
||||
|r| r.get(0));
|
||||
let result: Result<i64> = db.query_row(
|
||||
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
|
||||
&[],
|
||||
|r| r.get(0),
|
||||
);
|
||||
|
||||
assert_eq!(2, result.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_function_regexp_with_hashmap_cache() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("BEGIN;
|
||||
CREATE TABLE foo (x string);
|
||||
INSERT INTO foo VALUES ('lisa');
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;").unwrap();
|
||||
db.execute_batch(
|
||||
"BEGIN;
|
||||
CREATE TABLE foo (x string);
|
||||
INSERT INTO foo VALUES ('lisa');
|
||||
INSERT INTO foo VALUES ('lXsi');
|
||||
INSERT INTO foo VALUES ('lisX');
|
||||
END;",
|
||||
).unwrap();
|
||||
|
||||
// This implementation of a regexp scalar function uses a captured HashMap
|
||||
// to keep cached regular expressions around (even across multiple queries)
|
||||
@ -646,12 +689,10 @@ mod test {
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
match entry {
|
||||
Occupied(occ) => occ.into_mut(),
|
||||
Vacant(vac) => {
|
||||
match Regex::new(®ex_s) {
|
||||
Ok(r) => vac.insert(r),
|
||||
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
|
||||
}
|
||||
}
|
||||
Vacant(vac) => match Regex::new(®ex_s) {
|
||||
Ok(r) => vac.insert(r),
|
||||
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -659,16 +700,16 @@ mod test {
|
||||
Ok(regex.is_match(&text))
|
||||
}).unwrap();
|
||||
|
||||
let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
|
||||
&[],
|
||||
|r| r.get(0));
|
||||
let result: Result<bool> =
|
||||
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
|
||||
|
||||
assert_eq!(true, result.unwrap());
|
||||
|
||||
let result: Result<i64> =
|
||||
db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
|
||||
&[],
|
||||
|r| r.get(0));
|
||||
let result: Result<i64> = db.query_row(
|
||||
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
|
||||
&[],
|
||||
|r| r.get(0),
|
||||
);
|
||||
|
||||
assert_eq!(2, result.unwrap());
|
||||
}
|
||||
@ -677,21 +718,21 @@ mod test {
|
||||
fn test_varargs_function() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.create_scalar_function("my_concat", -1, true, |ctx| {
|
||||
let mut ret = String::new();
|
||||
let mut ret = String::new();
|
||||
|
||||
for idx in 0..ctx.len() {
|
||||
let s = try!(ctx.get::<String>(idx));
|
||||
ret.push_str(&s);
|
||||
}
|
||||
for idx in 0..ctx.len() {
|
||||
let s = try!(ctx.get::<String>(idx));
|
||||
ret.push_str(&s);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
})
|
||||
.unwrap();
|
||||
Ok(ret)
|
||||
}).unwrap();
|
||||
|
||||
for &(expected, query) in
|
||||
&[("", "SELECT my_concat()"),
|
||||
("onetwo", "SELECT my_concat('one', 'two')"),
|
||||
("abc", "SELECT my_concat('a', 'b', 'c')")] {
|
||||
for &(expected, query) in &[
|
||||
("", "SELECT my_concat()"),
|
||||
("onetwo", "SELECT my_concat('one', 'two')"),
|
||||
("abc", "SELECT my_concat('a', 'b', 'c')"),
|
||||
] {
|
||||
let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
@ -747,7 +788,8 @@ mod test {
|
||||
|
||||
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
|
||||
2, 1)";
|
||||
let result: (i64, i64) = db.query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
|
||||
let result: (i64, i64) = db
|
||||
.query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
|
||||
.unwrap();
|
||||
assert_eq!((4, 2), result);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Commit, Data Change and Rollback Notification Callbacks
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::os::raw::{c_int, c_char, c_void};
|
||||
|
||||
use ffi;
|
||||
|
||||
|
379
src/lib.rs
379
src/lib.rs
@ -60,71 +60,72 @@ 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, c_void};
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
// Number of cached prepared statements we'll hold on to.
|
||||
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
||||
@ -151,7 +152,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,
|
||||
@ -168,7 +169,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"),
|
||||
@ -232,13 +233,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.
|
||||
@ -251,13 +250,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).
|
||||
@ -306,8 +303,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).
|
||||
@ -361,7 +357,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)
|
||||
@ -377,7 +374,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));
|
||||
@ -408,20 +406,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.
|
||||
@ -445,7 +443,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)
|
||||
}
|
||||
@ -545,10 +544,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)
|
||||
@ -598,11 +598,11 @@ impl fmt::Debug for Connection {
|
||||
struct InnerConnection {
|
||||
db: *mut ffi::sqlite3,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_commit_hook: Option<fn(*mut c_void)>,
|
||||
free_commit_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_rollback_hook: Option<fn(*mut c_void)>,
|
||||
free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
#[cfg(feature = "hooks")]
|
||||
free_update_hook: Option<fn(*mut c_void)>,
|
||||
free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
|
||||
}
|
||||
|
||||
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
|
||||
@ -628,8 +628,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
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,9 +678,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) {
|
||||
@ -688,14 +692,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()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -767,7 +773,12 @@ impl InnerConnection {
|
||||
}
|
||||
#[cfg(feature = "hooks")]
|
||||
fn new(db: *mut ffi::sqlite3) -> InnerConnection {
|
||||
InnerConnection { db, free_commit_hook: None, free_rollback_hook: None, free_update_hook: None }
|
||||
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> {
|
||||
@ -778,9 +789,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 {
|
||||
@ -841,11 +858,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)
|
||||
}
|
||||
}
|
||||
@ -863,10 +882,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)
|
||||
};
|
||||
@ -921,9 +942,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 {
|
||||
@ -950,8 +970,7 @@ impl InnerConnection {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "hooks"))]
|
||||
fn remove_hooks(&mut self) {
|
||||
}
|
||||
fn remove_hooks(&mut self) {}
|
||||
}
|
||||
|
||||
impl Drop for InnerConnection {
|
||||
@ -977,18 +996,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;
|
||||
|
||||
@ -1010,10 +1027,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();
|
||||
@ -1026,8 +1046,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]);
|
||||
@ -1036,12 +1060,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");
|
||||
@ -1077,20 +1104,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
|
||||
@ -1109,15 +1138,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;
|
||||
@ -1129,7 +1159,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());
|
||||
}
|
||||
@ -1139,16 +1170,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]
|
||||
@ -1205,7 +1242,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();
|
||||
@ -1231,7 +1269,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_map() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@ -1244,15 +1281,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;
|
||||
@ -1264,9 +1299,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() {
|
||||
@ -1293,8 +1330,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);
|
||||
|
||||
@ -1308,8 +1344,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]
|
||||
@ -1414,7 +1452,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;
|
||||
@ -1427,8 +1464,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();
|
||||
|
||||
@ -1436,7 +1473,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then_fails() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@ -1449,7 +1485,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();
|
||||
|
||||
@ -1458,7 +1495,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();
|
||||
|
||||
@ -1469,7 +1507,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_query_and_then_custom_error() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@ -1482,18 +1519,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;
|
||||
@ -1506,11 +1540,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() {
|
||||
@ -1518,11 +1550,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() {
|
||||
@ -1530,10 +1560,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() {
|
||||
@ -1543,7 +1572,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;
|
||||
@ -1561,7 +1589,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;
|
||||
@ -1589,9 +1616,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 => (),
|
||||
@ -1600,7 +1626,6 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
fn test_dynamic() {
|
||||
let db = checked_memory_handle();
|
||||
let sql = "BEGIN;
|
||||
@ -1609,7 +1634,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use {Result, Connection};
|
||||
use {Connection, Result};
|
||||
|
||||
/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated.
|
||||
#[deprecated(since = "0.6.0", note = "Use LoadExtensionGuard instead")]
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::os::raw::c_int;
|
||||
use super::ffi;
|
||||
use super::unlock_notify;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_int;
|
||||
use std::ptr;
|
||||
|
||||
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
|
||||
#[derive(Debug)]
|
||||
|
62
src/row.rs
62
src/row.rs
@ -1,7 +1,7 @@
|
||||
use std::{convert, result};
|
||||
use std::marker::PhantomData;
|
||||
use std::{convert, result};
|
||||
|
||||
use super::{Statement, Error, Result};
|
||||
use super::{Error, Result, Statement};
|
||||
use types::{FromSql, FromSqlError};
|
||||
|
||||
/// An handle for the resulting rows of a query.
|
||||
@ -28,23 +28,20 @@ impl<'stmt> Rows<'stmt> {
|
||||
/// or `query_and_then` instead, which return types that implement `Iterator`.
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator
|
||||
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
|
||||
self.stmt
|
||||
.and_then(|stmt| match stmt.step() {
|
||||
Ok(true) => {
|
||||
Some(Ok(Row {
|
||||
stmt,
|
||||
phantom: PhantomData,
|
||||
}))
|
||||
}
|
||||
Ok(false) => {
|
||||
self.reset();
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
self.reset();
|
||||
Some(Err(err))
|
||||
}
|
||||
})
|
||||
self.stmt.and_then(|stmt| match stmt.step() {
|
||||
Ok(true) => Some(Ok(Row {
|
||||
stmt,
|
||||
phantom: PhantomData,
|
||||
})),
|
||||
Ok(false) => {
|
||||
self.reset();
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
self.reset();
|
||||
Some(Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +71,8 @@ pub struct MappedRows<'stmt, F> {
|
||||
}
|
||||
|
||||
impl<'stmt, T, F> MappedRows<'stmt, F>
|
||||
where F: FnMut(&Row) -> T
|
||||
where
|
||||
F: FnMut(&Row) -> T,
|
||||
{
|
||||
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
|
||||
MappedRows { rows, map: f }
|
||||
@ -82,7 +80,8 @@ impl<'stmt, T, F> MappedRows<'stmt, F>
|
||||
}
|
||||
|
||||
impl<'conn, T, F> Iterator for MappedRows<'conn, F>
|
||||
where F: FnMut(&Row) -> T
|
||||
where
|
||||
F: FnMut(&Row) -> T,
|
||||
{
|
||||
type Item = Result<T>;
|
||||
|
||||
@ -102,7 +101,8 @@ pub struct AndThenRows<'stmt, F> {
|
||||
}
|
||||
|
||||
impl<'stmt, T, E, F> AndThenRows<'stmt, F>
|
||||
where F: FnMut(&Row) -> result::Result<T, E>
|
||||
where
|
||||
F: FnMut(&Row) -> result::Result<T, E>,
|
||||
{
|
||||
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
|
||||
AndThenRows { rows, map: f }
|
||||
@ -110,8 +110,9 @@ impl<'stmt, T, E, F> AndThenRows<'stmt, F>
|
||||
}
|
||||
|
||||
impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
where
|
||||
E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>,
|
||||
{
|
||||
type Item = result::Result<T, E>;
|
||||
|
||||
@ -159,17 +160,12 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
let idx = try!(idx.idx(self.stmt));
|
||||
let value = self.stmt.value_ref(idx);
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
FromSqlError::InvalidType => {
|
||||
Error::InvalidColumnType(idx,
|
||||
value.data_type())
|
||||
}
|
||||
FromSqlError::OutOfRange(i) => {
|
||||
Error::IntegralValueOutOfRange(idx, i)
|
||||
}
|
||||
FromSqlError::Other(err) => {
|
||||
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the number of columns in the current row.
|
||||
|
268
src/statement.rs
268
src/statement.rs
@ -1,11 +1,13 @@
|
||||
use std::{convert, fmt, mem, ptr, result, str};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::slice::from_raw_parts;
|
||||
use std::{convert, fmt, mem, ptr, result, str};
|
||||
|
||||
use super::ffi;
|
||||
use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows};
|
||||
use super::str_to_cstring;
|
||||
use super::{
|
||||
AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef,
|
||||
};
|
||||
use types::{ToSql, ToSqlOutput};
|
||||
|
||||
/// A prepared statement.
|
||||
@ -211,7 +213,8 @@ impl<'conn> Statement<'conn> {
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
|
||||
where F: FnMut(&Row) -> T
|
||||
where
|
||||
F: FnMut(&Row) -> T,
|
||||
{
|
||||
let rows = self.query(params)?;
|
||||
Ok(MappedRows::new(rows, f))
|
||||
@ -243,11 +246,13 @@ impl<'conn> Statement<'conn> {
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_map_named<'a, T, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<MappedRows<'a, F>>
|
||||
where F: FnMut(&Row) -> T
|
||||
pub fn query_map_named<'a, T, F>(
|
||||
&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F,
|
||||
) -> Result<MappedRows<'a, F>>
|
||||
where
|
||||
F: FnMut(&Row) -> T,
|
||||
{
|
||||
let rows = self.query_named(params)?;
|
||||
Ok(MappedRows::new(rows, f))
|
||||
@ -260,12 +265,14 @@ impl<'conn> Statement<'conn> {
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_and_then<'a, T, E, F>(&'a mut self,
|
||||
params: &[&ToSql],
|
||||
f: F)
|
||||
-> Result<AndThenRows<'a, F>>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
pub fn query_and_then<'a, T, E, F>(
|
||||
&'a mut self,
|
||||
params: &[&ToSql],
|
||||
f: F,
|
||||
) -> Result<AndThenRows<'a, F>>
|
||||
where
|
||||
E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>,
|
||||
{
|
||||
let rows = self.query(params)?;
|
||||
Ok(AndThenRows::new(rows, f))
|
||||
@ -306,12 +313,14 @@ impl<'conn> Statement<'conn> {
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<AndThenRows<'a, F>>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
pub fn query_and_then_named<'a, T, E, F>(
|
||||
&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F,
|
||||
) -> Result<AndThenRows<'a, F>>
|
||||
where
|
||||
E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>,
|
||||
{
|
||||
let rows = self.query_named(params)?;
|
||||
Ok(AndThenRows::new(rows, f))
|
||||
@ -338,7 +347,8 @@ impl<'conn> Statement<'conn> {
|
||||
///
|
||||
/// Will return `Err` if the underlying SQLite call fails.
|
||||
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
|
||||
where F: FnOnce(&Row) -> T
|
||||
where
|
||||
F: FnOnce(&Row) -> T,
|
||||
{
|
||||
let mut rows = try!(self.query(params));
|
||||
|
||||
@ -369,10 +379,13 @@ impl<'conn> Statement<'conn> {
|
||||
}
|
||||
|
||||
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
||||
assert_eq!(params.len(), self.stmt.bind_parameter_count(),
|
||||
"incorrect number of parameters to query(): expected {}, got {}",
|
||||
self.stmt.bind_parameter_count(),
|
||||
params.len());
|
||||
assert_eq!(
|
||||
params.len(),
|
||||
self.stmt.bind_parameter_count(),
|
||||
"incorrect number of parameters to query(): expected {}, got {}",
|
||||
self.stmt.bind_parameter_count(),
|
||||
params.len()
|
||||
);
|
||||
|
||||
for (i, p) in params.iter().enumerate() {
|
||||
try!(self.bind_parameter(*p, i + 1));
|
||||
@ -402,20 +415,16 @@ impl<'conn> Statement<'conn> {
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(len) => {
|
||||
return self.conn
|
||||
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
|
||||
return self
|
||||
.conn
|
||||
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
|
||||
}
|
||||
};
|
||||
self.conn
|
||||
.decode_result(match value {
|
||||
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) },
|
||||
ValueRef::Integer(i) => unsafe {
|
||||
ffi::sqlite3_bind_int64(ptr, col as c_int, i)
|
||||
},
|
||||
ValueRef::Real(r) => unsafe {
|
||||
ffi::sqlite3_bind_double(ptr, col as c_int, r)
|
||||
},
|
||||
ValueRef::Text(s) => unsafe {
|
||||
self.conn.decode_result(match value {
|
||||
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) },
|
||||
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col as c_int, i) },
|
||||
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col as c_int, r) },
|
||||
ValueRef::Text(s) => unsafe {
|
||||
let length = s.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::SQLITE_TOOBIG
|
||||
@ -426,24 +435,32 @@ impl<'conn> Statement<'conn> {
|
||||
} else {
|
||||
ffi::SQLITE_STATIC()
|
||||
};
|
||||
ffi::sqlite3_bind_text(ptr, col as c_int, c_str.as_ptr(), length as c_int, destructor)
|
||||
ffi::sqlite3_bind_text(
|
||||
ptr,
|
||||
col as c_int,
|
||||
c_str.as_ptr(),
|
||||
length as c_int,
|
||||
destructor,
|
||||
)
|
||||
}
|
||||
},
|
||||
ValueRef::Blob(b) => unsafe {
|
||||
ValueRef::Blob(b) => unsafe {
|
||||
let length = b.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::SQLITE_TOOBIG
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_bind_zeroblob(ptr, col as c_int, 0)
|
||||
} else {
|
||||
ffi::sqlite3_bind_blob(ptr,
|
||||
col as c_int,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
ffi::sqlite3_bind_blob(
|
||||
ptr,
|
||||
col as c_int,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT(),
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn execute_with_bound_parameters(&mut self) -> Result<usize> {
|
||||
@ -485,7 +502,9 @@ impl<'conn> Statement<'conn> {
|
||||
#[cfg(feature = "bundled")]
|
||||
pub fn expanded_sql(&self) -> Option<&str> {
|
||||
unsafe {
|
||||
self.stmt.expanded_sql().map(|s| str::from_utf8_unchecked(s.to_bytes()))
|
||||
self.stmt
|
||||
.expanded_sql()
|
||||
.map(|s| str::from_utf8_unchecked(s.to_bytes()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -518,10 +537,7 @@ impl<'conn> Drop for Statement<'conn> {
|
||||
|
||||
impl<'conn> Statement<'conn> {
|
||||
pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement {
|
||||
Statement {
|
||||
conn,
|
||||
stmt,
|
||||
}
|
||||
Statement { conn, stmt }
|
||||
}
|
||||
|
||||
pub(crate) fn value_ref(&self, col: usize) -> ValueRef {
|
||||
@ -532,30 +548,42 @@ impl<'conn> Statement<'conn> {
|
||||
ffi::SQLITE_INTEGER => {
|
||||
ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col as c_int) })
|
||||
}
|
||||
ffi::SQLITE_FLOAT => ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) }),
|
||||
ffi::SQLITE_FLOAT => {
|
||||
ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) })
|
||||
}
|
||||
ffi::SQLITE_TEXT => {
|
||||
let s = unsafe {
|
||||
let text = ffi::sqlite3_column_text(raw, col as c_int);
|
||||
assert!(!text.is_null(),
|
||||
"unexpected SQLITE_TEXT column type with NULL data");
|
||||
assert!(
|
||||
!text.is_null(),
|
||||
"unexpected SQLITE_TEXT column type with NULL data"
|
||||
);
|
||||
CStr::from_ptr(text as *const c_char)
|
||||
};
|
||||
|
||||
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
|
||||
let s = s.to_str()
|
||||
let s = s
|
||||
.to_str()
|
||||
.expect("sqlite3_column_text returned invalid UTF-8");
|
||||
ValueRef::Text(s)
|
||||
}
|
||||
ffi::SQLITE_BLOB => {
|
||||
let (blob, len) = unsafe {
|
||||
(ffi::sqlite3_column_blob(raw, col as c_int), ffi::sqlite3_column_bytes(raw, col as c_int))
|
||||
(
|
||||
ffi::sqlite3_column_blob(raw, col as c_int),
|
||||
ffi::sqlite3_column_bytes(raw, col as c_int),
|
||||
)
|
||||
};
|
||||
|
||||
assert!(len >= 0,
|
||||
"unexpected negative return from sqlite3_column_bytes");
|
||||
assert!(
|
||||
len >= 0,
|
||||
"unexpected negative return from sqlite3_column_bytes"
|
||||
);
|
||||
if len > 0 {
|
||||
assert!(!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB column type with NULL data");
|
||||
assert!(
|
||||
!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB column type with NULL data"
|
||||
);
|
||||
ValueRef::Blob(unsafe { from_raw_parts(blob as *const u8, len as usize) })
|
||||
} else {
|
||||
// The return value from sqlite3_column_blob() for a zero-length BLOB
|
||||
@ -589,18 +617,25 @@ mod test {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
|
||||
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(
|
||||
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
|
||||
assert_eq!(3i32,
|
||||
db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x",
|
||||
&[(":x", &0i32)],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
3i32,
|
||||
db.query_row_named::<i32, _>(
|
||||
"SELECT SUM(x) FROM foo WHERE x > :x",
|
||||
&[(":x", &0i32)],
|
||||
|r| r.get(0)
|
||||
).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -610,15 +645,19 @@ mod test {
|
||||
INTEGER)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
|
||||
let mut stmt = db
|
||||
.prepare("INSERT INTO test (name) VALUES (:name)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
assert_eq!(1i32,
|
||||
db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name",
|
||||
&[(":name", &"one")],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
1i32,
|
||||
db.query_row_named::<i32, _>(
|
||||
"SELECT COUNT(*) FROM test WHERE name = :name",
|
||||
&[(":name", &"one")],
|
||||
|r| r.get(0)
|
||||
).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -630,7 +669,8 @@ mod test {
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
|
||||
let mut stmt = db
|
||||
.prepare("SELECT id FROM test where name = :name")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
@ -647,13 +687,14 @@ mod test {
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
|
||||
let mut stmt = db
|
||||
.prepare("SELECT id FROM test where name = :name")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
|
||||
let mut rows = stmt
|
||||
.query_map_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
2 * id
|
||||
})
|
||||
.unwrap();
|
||||
}).unwrap();
|
||||
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(2, doubled_id);
|
||||
@ -661,7 +702,6 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_query_and_then_named() {
|
||||
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
@ -670,17 +710,18 @@ mod test {
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
|
||||
let mut stmt = db
|
||||
.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
|
||||
let mut rows = stmt
|
||||
.query_and_then_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
if id == 1 {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::SqliteSingleThreadedMode)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
}).unwrap();
|
||||
|
||||
// first row should be Ok
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
@ -700,13 +741,14 @@ mod test {
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
let mut stmt = db
|
||||
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
|
||||
let result: Option<String> =
|
||||
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
let result: Option<String> = db
|
||||
.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
@ -716,14 +758,15 @@ mod test {
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
let mut stmt = db
|
||||
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
stmt.execute_named(&[(":y", &"two")]).unwrap();
|
||||
|
||||
let result: String =
|
||||
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
let result: String = db
|
||||
.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(result, "one");
|
||||
}
|
||||
|
||||
@ -732,7 +775,8 @@ mod test {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
|
||||
.unwrap();
|
||||
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
|
||||
let mut stmt = db
|
||||
.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
|
||||
.unwrap();
|
||||
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
|
||||
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
|
||||
@ -740,7 +784,8 @@ mod test {
|
||||
Error::StatementChangedRows(0) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
|
||||
let mut multi = db
|
||||
.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
|
||||
.unwrap();
|
||||
match multi.insert(&[]).unwrap_err() {
|
||||
Error::StatementChangedRows(2) => (),
|
||||
@ -752,22 +797,27 @@ mod test {
|
||||
fn test_insert_different_tables() {
|
||||
// Test for https://github.com/jgallagher/rusqlite/issues/171
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch(r"
|
||||
db.execute_batch(
|
||||
r"
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
CREATE TABLE bar(x INTEGER);
|
||||
")
|
||||
.unwrap();
|
||||
",
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(
|
||||
db.prepare("INSERT INTO foo VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
db.prepare("INSERT INTO bar VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
26
src/trace.rs
26
src/trace.rs
@ -1,14 +1,14 @@
|
||||
//! Tracing and profiling functions. Error and warning log.
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::ffi;
|
||||
use {Result, Connection};
|
||||
use error::error_from_sqlite_code;
|
||||
use {Connection, Result};
|
||||
|
||||
/// Set up the process-wide SQLite error logging callback.
|
||||
/// This function is marked unsafe for two reasons:
|
||||
@ -33,9 +33,11 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
|
||||
let rc = match callback {
|
||||
Some(f) => {
|
||||
let p_arg: *mut c_void = mem::transmute(f);
|
||||
ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG,
|
||||
log_callback as extern "C" fn(_, _, _),
|
||||
p_arg)
|
||||
ffi::sqlite3_config(
|
||||
ffi::SQLITE_CONFIG_LOG,
|
||||
log_callback as extern "C" fn(_, _, _),
|
||||
p_arg,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
let nullptr: *mut c_void = ptr::null_mut();
|
||||
@ -90,16 +92,20 @@ impl Connection {
|
||||
/// There can only be a single profiler defined for each database connection.
|
||||
/// Setting a new profiler clears the old one.
|
||||
pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
|
||||
unsafe extern "C" fn profile_callback(p_arg: *mut c_void,
|
||||
z_sql: *const c_char,
|
||||
nanoseconds: u64) {
|
||||
unsafe extern "C" fn profile_callback(
|
||||
p_arg: *mut c_void,
|
||||
z_sql: *const c_char,
|
||||
nanoseconds: u64,
|
||||
) {
|
||||
let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
|
||||
let c_slice = CStr::from_ptr(z_sql).to_bytes();
|
||||
let s = String::from_utf8_lossy(c_slice);
|
||||
const NANOS_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
|
||||
(nanoseconds % NANOS_PER_SEC) as u32);
|
||||
let duration = Duration::new(
|
||||
nanoseconds / NANOS_PER_SEC,
|
||||
(nanoseconds % NANOS_PER_SEC) as u32,
|
||||
);
|
||||
profile_fn(&s, duration);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::ops::Deref;
|
||||
use {Result, Connection};
|
||||
use {Connection, Result};
|
||||
|
||||
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated.
|
||||
#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")]
|
||||
@ -7,7 +7,7 @@ pub type SqliteTransactionBehavior = TransactionBehavior;
|
||||
|
||||
/// Options for transaction behavior. See [BEGIN
|
||||
/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details.
|
||||
#[derive(Copy,Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TransactionBehavior {
|
||||
Deferred,
|
||||
Immediate,
|
||||
@ -15,7 +15,7 @@ pub enum TransactionBehavior {
|
||||
}
|
||||
|
||||
/// Options for how a Transaction or Savepoint should behave when it is dropped.
|
||||
#[derive(Copy,Clone,PartialEq,Eq)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum DropBehavior {
|
||||
/// Roll back the changes. This is the default.
|
||||
Rollback,
|
||||
@ -105,13 +105,10 @@ impl<'conn> Transaction<'conn> {
|
||||
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
|
||||
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
|
||||
};
|
||||
conn.execute_batch(query)
|
||||
.map(move |_| {
|
||||
Transaction {
|
||||
conn,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
}
|
||||
})
|
||||
conn.execute_batch(query).map(move |_| Transaction {
|
||||
conn,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
})
|
||||
}
|
||||
|
||||
/// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
|
||||
@ -217,20 +214,19 @@ impl<'conn> Drop for Transaction<'conn> {
|
||||
}
|
||||
|
||||
impl<'conn> Savepoint<'conn> {
|
||||
fn with_depth_and_name<T: Into<String>>(conn: &Connection,
|
||||
depth: u32,
|
||||
name: T)
|
||||
-> Result<Savepoint> {
|
||||
fn with_depth_and_name<T: Into<String>>(
|
||||
conn: &Connection,
|
||||
depth: u32,
|
||||
name: T,
|
||||
) -> Result<Savepoint> {
|
||||
let name = name.into();
|
||||
conn.execute_batch(&format!("SAVEPOINT {}", name))
|
||||
.map(|_| {
|
||||
Savepoint {
|
||||
conn,
|
||||
name,
|
||||
depth,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
}
|
||||
.map(|_| Savepoint {
|
||||
conn,
|
||||
name,
|
||||
depth,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
})
|
||||
}
|
||||
|
||||
@ -275,8 +271,7 @@ impl<'conn> Savepoint<'conn> {
|
||||
}
|
||||
|
||||
fn commit_(&mut self) -> Result<()> {
|
||||
self.conn
|
||||
.execute_batch(&format!("RELEASE {}", self.name))?;
|
||||
self.conn.execute_batch(&format!("RELEASE {}", self.name))?;
|
||||
self.committed = true;
|
||||
Ok(())
|
||||
}
|
||||
@ -365,9 +360,10 @@ impl Connection {
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if the underlying SQLite call fails.
|
||||
pub fn transaction_with_behavior(&mut self,
|
||||
behavior: TransactionBehavior)
|
||||
-> Result<Transaction> {
|
||||
pub fn transaction_with_behavior(
|
||||
&mut self,
|
||||
behavior: TransactionBehavior,
|
||||
) -> Result<Transaction> {
|
||||
Transaction::new(self, behavior)
|
||||
}
|
||||
|
||||
@ -413,8 +409,8 @@ impl Connection {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use super::DropBehavior;
|
||||
use Connection;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
@ -437,9 +433,11 @@ mod test {
|
||||
}
|
||||
{
|
||||
let tx = db.transaction().unwrap();
|
||||
assert_eq!(2i32,
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
2i32,
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,9 +462,11 @@ mod test {
|
||||
}
|
||||
{
|
||||
let tx = db.transaction().unwrap();
|
||||
assert_eq!(6i32,
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
6i32,
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,7 +560,8 @@ mod test {
|
||||
}
|
||||
|
||||
fn assert_current_sum(x: i32, conn: &Connection) {
|
||||
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
let i = conn
|
||||
.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(x, i);
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ extern crate chrono;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, Utc, Local};
|
||||
use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
|
||||
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
use Result;
|
||||
|
||||
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
|
||||
impl ToSql for NaiveDate {
|
||||
@ -22,9 +22,9 @@ impl FromSql for NaiveDate {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
})
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,19 +39,17 @@ impl ToSql for NaiveTime {
|
||||
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
|
||||
impl FromSql for NaiveTime {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| {
|
||||
let fmt = match s.len() {
|
||||
5 => "%H:%M",
|
||||
8 => "%H:%M:%S",
|
||||
_ => "%H:%M:%S%.f",
|
||||
};
|
||||
match NaiveTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
value.as_str().and_then(|s| {
|
||||
let fmt = match s.len() {
|
||||
5 => "%H:%M",
|
||||
8 => "%H:%M:%S",
|
||||
_ => "%H:%M:%S%.f",
|
||||
};
|
||||
match NaiveTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,20 +65,18 @@ impl ToSql for NaiveDateTime {
|
||||
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
|
||||
impl FromSql for NaiveDateTime {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| {
|
||||
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
|
||||
"%Y-%m-%dT%H:%M:%S%.f"
|
||||
} else {
|
||||
"%Y-%m-%d %H:%M:%S%.f"
|
||||
};
|
||||
value.as_str().and_then(|s| {
|
||||
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
|
||||
"%Y-%m-%dT%H:%M:%S%.f"
|
||||
} else {
|
||||
"%Y-%m-%d %H:%M:%S%.f"
|
||||
};
|
||||
|
||||
match NaiveDateTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
match NaiveDateTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,9 +126,10 @@ impl FromSql for DateTime<Local> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::chrono::{
|
||||
DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
|
||||
};
|
||||
use Connection;
|
||||
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
|
||||
Duration};
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
@ -148,10 +145,12 @@ mod test {
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let s: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23", s);
|
||||
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let t: NaiveDate = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(date, t);
|
||||
}
|
||||
@ -163,10 +162,12 @@ mod test {
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let s: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("23:56:04", s);
|
||||
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let v: NaiveTime = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(time, v);
|
||||
}
|
||||
@ -181,16 +182,18 @@ mod test {
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let s: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23T23:56:04", s);
|
||||
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let v: NaiveDateTime = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(dt, v);
|
||||
|
||||
db.execute("UPDATE foo set b = datetime(t)", &[])
|
||||
.unwrap(); // "YYYY-MM-DD HH:MM:SS"
|
||||
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS"
|
||||
let hms: NaiveDateTime = db
|
||||
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(dt, hms);
|
||||
}
|
||||
@ -206,25 +209,29 @@ mod test {
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let s: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23T23:56:04.789+00:00", s);
|
||||
|
||||
let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let v1: DateTime<Utc> = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc, v1);
|
||||
|
||||
let v2: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
|
||||
let v2: DateTime<Utc> = db
|
||||
.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc, v2);
|
||||
|
||||
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
|
||||
let v3: DateTime<Utc> = db
|
||||
.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc - Duration::milliseconds(789), v3);
|
||||
|
||||
let v4: DateTime<Utc> =
|
||||
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
let v4: DateTime<Utc> = db
|
||||
.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc, v4);
|
||||
}
|
||||
|
||||
@ -240,11 +247,13 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
// Stored string should be in UTC
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let s: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert!(s.ends_with("+00:00"));
|
||||
|
||||
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let v: DateTime<Local> = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(local, v);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{ValueRef, Value};
|
||||
use super::{Value, ValueRef};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
@ -35,12 +35,11 @@ impl Error for FromSqlError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="clippy", allow(match_same_arms))]
|
||||
#[cfg_attr(feature = "clippy", allow(match_same_arms))]
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
FromSqlError::Other(ref err) => err.cause(),
|
||||
FromSqlError::InvalidType |
|
||||
FromSqlError::OutOfRange(_) => None,
|
||||
FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,9 +114,9 @@ impl FromSql for f64 {
|
||||
impl FromSql for bool {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
i64::column_result(value).map(|i| match i {
|
||||
0 => false,
|
||||
_ => true,
|
||||
})
|
||||
0 => false,
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,8 +149,8 @@ impl FromSql for Value {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Error};
|
||||
use super::FromSql;
|
||||
use {Connection, Error};
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
Connection::open_in_memory().unwrap()
|
||||
@ -162,10 +161,12 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
|
||||
where T: Into<i64> + FromSql + ::std::fmt::Debug
|
||||
where
|
||||
T: Into<i64> + FromSql + ::std::fmt::Debug,
|
||||
{
|
||||
for n in out_of_range {
|
||||
let err = db.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
|
||||
let err = db
|
||||
.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
|
||||
.unwrap()
|
||||
.unwrap_err();
|
||||
match err {
|
||||
@ -174,18 +175,22 @@ mod test {
|
||||
}
|
||||
}
|
||||
for n in in_range {
|
||||
assert_eq!(*n,
|
||||
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
|
||||
.unwrap()
|
||||
.into());
|
||||
assert_eq!(
|
||||
*n,
|
||||
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
|
||||
.unwrap()
|
||||
.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
|
||||
check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
|
||||
check_ranges::<i32>(&db,
|
||||
&[-2147483649, 2147483648],
|
||||
&[-2147483648, -1, 0, 1, 2147483647]);
|
||||
check_ranges::<i32>(
|
||||
&db,
|
||||
&[-2147483649, 2147483648],
|
||||
&[-2147483648, -1, 0, 1, 2147483647],
|
||||
);
|
||||
check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
|
||||
check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
|
||||
check_ranges::<u32>(&db, &[-2, -1, 4294967296], &[0, 1, 4294967295]);
|
||||
|
194
src/types/mod.rs
194
src/types/mod.rs
@ -59,15 +59,15 @@ pub use self::value_ref::ValueRef;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod value;
|
||||
mod value_ref;
|
||||
mod from_sql;
|
||||
mod to_sql;
|
||||
mod time;
|
||||
#[cfg(feature = "chrono")]
|
||||
mod chrono;
|
||||
mod from_sql;
|
||||
#[cfg(feature = "serde_json")]
|
||||
mod serde_json;
|
||||
mod time;
|
||||
mod to_sql;
|
||||
mod value;
|
||||
mod value_ref;
|
||||
|
||||
/// Empty struct that can be used to fill in a query parameter as `NULL`.
|
||||
///
|
||||
@ -83,10 +83,10 @@ mod serde_json;
|
||||
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Copy,Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Null;
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Type {
|
||||
Null,
|
||||
Integer,
|
||||
@ -111,11 +111,11 @@ impl fmt::Display for Type {
|
||||
mod test {
|
||||
extern crate time;
|
||||
|
||||
use super::Value;
|
||||
use std::f64::EPSILON;
|
||||
use std::os::raw::{c_double, c_int};
|
||||
use Connection;
|
||||
use Error;
|
||||
use std::os::raw::{c_int, c_double};
|
||||
use std::f64::EPSILON;
|
||||
use super::Value;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
@ -132,7 +132,8 @@ mod test {
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
|
||||
.unwrap();
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
let v: Vec<u8> = db
|
||||
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(v, v1234);
|
||||
}
|
||||
@ -145,7 +146,8 @@ mod test {
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
|
||||
.unwrap();
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
let v: Vec<u8> = db
|
||||
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(v, empty);
|
||||
}
|
||||
@ -155,10 +157,10 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
let s = "hello, world!";
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
|
||||
.unwrap();
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
|
||||
|
||||
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let from: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(from, s);
|
||||
}
|
||||
@ -171,7 +173,8 @@ mod test {
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()])
|
||||
.unwrap();
|
||||
|
||||
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let from: String = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(from, s);
|
||||
}
|
||||
@ -183,9 +186,11 @@ mod test {
|
||||
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(10i64,
|
||||
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
assert_eq!(
|
||||
10i64,
|
||||
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -195,12 +200,11 @@ mod test {
|
||||
let s = Some("hello, world!");
|
||||
let b = Some(vec![1u8, 2, 3, 4]);
|
||||
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
|
||||
.unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
|
||||
.unwrap();
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
|
||||
let mut stmt = db
|
||||
.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query(&[]).unwrap();
|
||||
|
||||
@ -232,9 +236,10 @@ mod test {
|
||||
|
||||
let db = checked_memory_handle();
|
||||
|
||||
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
&[])
|
||||
.unwrap();
|
||||
db.execute(
|
||||
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
&[],
|
||||
).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
|
||||
let mut rows = stmt.query(&[]).unwrap();
|
||||
@ -246,53 +251,99 @@ mod test {
|
||||
assert_eq!("text", row.get_checked::<_, String>(1).unwrap());
|
||||
assert_eq!(1, row.get_checked::<_, c_int>(2).unwrap());
|
||||
assert!((1.5 - row.get_checked::<_, c_double>(3).unwrap()).abs() < EPSILON);
|
||||
assert!(row.get_checked::<_, Option<c_int>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
assert!(row.get_checked::<_, Option<c_double>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
assert!(row.get_checked::<_, Option<String>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
assert!(row.get_checked::<_, Option<c_int>>(4).unwrap().is_none());
|
||||
assert!(row.get_checked::<_, Option<c_double>>(4).unwrap().is_none());
|
||||
assert!(row.get_checked::<_, Option<String>>(4).unwrap().is_none());
|
||||
|
||||
// check some invalid types
|
||||
|
||||
// 0 is actually a blob (Vec<u8>)
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, i64>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, String>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, time::Timespec>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(0).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_int>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_int>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, i64>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_double>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, String>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, time::Timespec>(0).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Option<c_int>>(0).err().unwrap()
|
||||
));
|
||||
|
||||
// 1 is actually a text (String)
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, i64>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(1).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_int>(1).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, i64>(1).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_double>(1).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Vec<u8>>(1).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Option<c_int>>(1).err().unwrap()
|
||||
));
|
||||
|
||||
// 2 is actually an integer
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, String>(2).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(2).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Option<String>>(2).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, String>(2).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Vec<u8>>(2).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Option<String>>(2).err().unwrap()
|
||||
));
|
||||
|
||||
// 3 is actually a float (c_double)
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, i64>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, String>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(3).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_int>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, i64>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, String>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Vec<u8>>(3).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Option<c_int>>(3).err().unwrap()
|
||||
));
|
||||
|
||||
// 4 is actually NULL
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, i64>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, String>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(row.get_checked::<_, time::Timespec>(4).err().unwrap()));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_int>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, i64>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, c_double>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, String>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, Vec<u8>>(4).err().unwrap()
|
||||
));
|
||||
assert!(is_invalid_column_type(
|
||||
row.get_checked::<_, time::Timespec>(4).err().unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -300,18 +351,23 @@ mod test {
|
||||
use super::Value;
|
||||
let db = checked_memory_handle();
|
||||
|
||||
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
&[])
|
||||
.unwrap();
|
||||
db.execute(
|
||||
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||
&[],
|
||||
).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
|
||||
let mut rows = stmt.query(&[]).unwrap();
|
||||
|
||||
let row = rows.next().unwrap().unwrap();
|
||||
assert_eq!(Value::Blob(vec![1, 2]),
|
||||
row.get_checked::<_, Value>(0).unwrap());
|
||||
assert_eq!(Value::Text(String::from("text")),
|
||||
row.get_checked::<_, Value>(1).unwrap());
|
||||
assert_eq!(
|
||||
Value::Blob(vec![1, 2]),
|
||||
row.get_checked::<_, Value>(0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Value::Text(String::from("text")),
|
||||
row.get_checked::<_, Value>(1).unwrap()
|
||||
);
|
||||
assert_eq!(Value::Integer(1), row.get_checked::<_, Value>(2).unwrap());
|
||||
match row.get_checked::<_, Value>(3).unwrap() {
|
||||
Value::Real(val) => assert!((1.5 - val).abs() < EPSILON),
|
||||
|
@ -3,8 +3,8 @@ extern crate serde_json;
|
||||
|
||||
use self::serde_json::Value;
|
||||
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
use Result;
|
||||
|
||||
/// Serialize JSON `Value` to text.
|
||||
impl ToSql for Value {
|
||||
@ -17,18 +17,17 @@ impl ToSql for Value {
|
||||
impl FromSql for Value {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
match 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)))
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use super::serde_json;
|
||||
use Connection;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
@ -43,14 +42,17 @@ mod test {
|
||||
|
||||
let json = r#"{"foo": 13, "bar": "baz"}"#;
|
||||
let data: serde_json::Value = serde_json::from_str(json).unwrap();
|
||||
db.execute("INSERT INTO foo (t, b) VALUES (?, ?)",
|
||||
&[&data, &json.as_bytes()])
|
||||
.unwrap();
|
||||
db.execute(
|
||||
"INSERT INTO foo (t, b) VALUES (?, ?)",
|
||||
&[&data, &json.as_bytes()],
|
||||
).unwrap();
|
||||
|
||||
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let t: serde_json::Value = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(data, t);
|
||||
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
let b: serde_json::Value = db
|
||||
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(data, b);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
extern crate time;
|
||||
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
use Result;
|
||||
|
||||
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
|
||||
const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
|
||||
@ -21,18 +21,18 @@ impl FromSql for time::Timespec {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| {
|
||||
time::strptime(s, SQLITE_DATETIME_FMT)
|
||||
.or_else(|err| {
|
||||
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
|
||||
.or_else(|_| Err(FromSqlError::Other(Box::new(err))))})})
|
||||
.map(|tm| tm.to_timespec())
|
||||
time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| {
|
||||
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
|
||||
.or_else(|_| Err(FromSqlError::Other(Box::new(err))))
|
||||
})
|
||||
}).map(|tm| tm.to_timespec())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use super::time;
|
||||
use Connection;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
@ -47,25 +47,23 @@ mod test {
|
||||
|
||||
let mut ts_vec = vec![];
|
||||
|
||||
ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
|
||||
ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
|
||||
ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
|
||||
ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
|
||||
ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
|
||||
ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286
|
||||
ts_vec.push(time::Timespec::new(10_000, 0)); //January 1, 1970 2:46:40 AM
|
||||
ts_vec.push(time::Timespec::new(10_000, 1000)); //January 1, 1970 2:46:40 AM (and one microsecond)
|
||||
ts_vec.push(time::Timespec::new(1500391124, 1_000_000)); //July 18, 2017
|
||||
ts_vec.push(time::Timespec::new(2000000000, 2_000_000)); //May 18, 2033
|
||||
ts_vec.push(time::Timespec::new(3000000000, 999_999_999)); //January 24, 2065
|
||||
ts_vec.push(time::Timespec::new(10000000000, 0)); //November 20, 2286
|
||||
|
||||
for ts in ts_vec {
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
|
||||
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
|
||||
.unwrap();
|
||||
|
||||
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
let from: time::Timespec = db
|
||||
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
|
||||
db.execute("DELETE FROM foo", &[]).unwrap();
|
||||
|
||||
assert_eq!(from, ts);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use std::borrow::Cow;
|
||||
use super::{Null, Value, ValueRef};
|
||||
use std::borrow::Cow;
|
||||
use Result;
|
||||
|
||||
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ToSqlOutput<'a> {
|
||||
/// A borrowed SQLite-representable value.
|
||||
Borrowed(ValueRef<'a>),
|
||||
@ -19,7 +19,8 @@ pub enum ToSqlOutput<'a> {
|
||||
// Generically allow any type that can be converted into a ValueRef
|
||||
// to be converted into a ToSqlOutput as well.
|
||||
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
|
||||
where &'a T: Into<ValueRef<'a>>
|
||||
where
|
||||
&'a T: Into<ValueRef<'a>>,
|
||||
{
|
||||
fn from(t: &'a T) -> Self {
|
||||
ToSqlOutput::Borrowed(t.into())
|
||||
@ -55,12 +56,12 @@ from_value!(Vec<u8>);
|
||||
impl<'a> ToSql for ToSqlOutput<'a> {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(match *self {
|
||||
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
|
||||
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
|
||||
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
|
||||
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +105,8 @@ to_sql_self!(u32);
|
||||
to_sql_self!(f64);
|
||||
|
||||
impl<'a, T: ?Sized> ToSql for &'a T
|
||||
where &'a T: Into<ToSqlOutput<'a>>
|
||||
where
|
||||
&'a T: Into<ToSqlOutput<'a>>,
|
||||
{
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok((*self).into())
|
||||
|
@ -4,7 +4,7 @@ use super::{Null, Type};
|
||||
/// dictated by SQLite (not by the caller).
|
||||
///
|
||||
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
/// The value is a `NULL` value.
|
||||
Null,
|
||||
@ -31,7 +31,9 @@ impl From<bool> for Value {
|
||||
}
|
||||
|
||||
impl From<isize> for Value {
|
||||
fn from(i: isize) -> Value { Value::Integer(i as i64) }
|
||||
fn from(i: isize) -> Value {
|
||||
Value::Integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_i64(
|
||||
|
@ -1,11 +1,11 @@
|
||||
use super::{Type, Value};
|
||||
use types::{FromSqlError, FromSqlResult};
|
||||
use super::{Value, Type};
|
||||
|
||||
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
|
||||
/// memory backing this value is owned by SQLite.
|
||||
///
|
||||
/// See [`Value`](enum.Value.html) for an owning dynamic type value.
|
||||
#[derive(Copy,Clone,Debug,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ValueRef<'a> {
|
||||
/// The value is a `NULL` value.
|
||||
Null,
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! [Unlock Notification](http://sqlite.org/unlock_notify.html)
|
||||
|
||||
#[cfg(feature = "unlock_notify")]
|
||||
use std::sync::{Condvar, Mutex};
|
||||
use std::os::raw::c_int;
|
||||
#[cfg(feature = "unlock_notify")]
|
||||
use std::os::raw::c_void;
|
||||
#[cfg(feature = "unlock_notify")]
|
||||
use std::sync::{Condvar, Mutex};
|
||||
|
||||
use ffi;
|
||||
|
||||
@ -49,10 +49,9 @@ unsafe extern "C" fn unlock_notify_cb(ap_arg: *mut *mut c_void, n_arg: c_int) {
|
||||
|
||||
#[cfg(feature = "unlock_notify")]
|
||||
pub fn is_locked(db: *mut ffi::sqlite3, rc: c_int) -> bool {
|
||||
rc == ffi::SQLITE_LOCKED_SHAREDCACHE || (rc & 0xFF) == ffi::SQLITE_LOCKED && unsafe {
|
||||
ffi::sqlite3_extended_errcode(db)
|
||||
}
|
||||
== ffi::SQLITE_LOCKED_SHAREDCACHE
|
||||
rc == ffi::SQLITE_LOCKED_SHAREDCACHE
|
||||
|| (rc & 0xFF) == ffi::SQLITE_LOCKED
|
||||
&& unsafe { ffi::sqlite3_extended_errcode(db) } == ffi::SQLITE_LOCKED_SHAREDCACHE
|
||||
}
|
||||
|
||||
/// This function assumes that an SQLite API call (either `sqlite3_prepare_v2()`
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Ensure we reject connections when SQLite is in single-threaded mode, as it
|
||||
//! would violate safety if multiple Rust threads tried to use connections.
|
||||
|
||||
extern crate rusqlite;
|
||||
extern crate libsqlite3_sys as ffi;
|
||||
extern crate rusqlite;
|
||||
|
||||
use rusqlite::Connection;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user