mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-22 16:29:20 +08:00
Merge remote-tracking branch 'jgallagher/master' into vtab
This commit is contained in:
commit
fc37b324b8
@ -68,7 +68,7 @@ newer SQLite version; see details below.
|
||||
### Optional Features
|
||||
|
||||
Rusqlite provides several features that are behind [Cargo
|
||||
features](http://doc.crates.io/manifest.html#the-features-section). They are:
|
||||
features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are:
|
||||
|
||||
* [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html)
|
||||
allows loading dynamic library-based SQLite extensions.
|
||||
@ -105,11 +105,11 @@ You can adjust this behavior in a number of ways:
|
||||
* If you use the `bundled` feature, `libsqlite3-sys` will use the
|
||||
[gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and
|
||||
link against that. This source is embedded in the `libsqlite3-sys` crate and
|
||||
is currently SQLite 3.17.0 (as of `rusqlite` 0.10.1 / `libsqlite3-sys`
|
||||
0.7.1). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
|
||||
is currently SQLite 3.24.0 (as of `rusqlite` 0.14.0 / `libsqlite3-sys`
|
||||
0.9.3). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
|
||||
```
|
||||
[dependencies.rusqlite]
|
||||
version = "0.11.0"
|
||||
version = "0.14.0"
|
||||
features = ["bundled"]
|
||||
```
|
||||
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
|
||||
|
@ -1,8 +1,8 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: 1.25.0-x86_64-pc-windows-gnu
|
||||
- TARGET: 1.27.2-x86_64-pc-windows-gnu
|
||||
MSYS2_BITS: 64
|
||||
- TARGET: 1.25.0-x86_64-pc-windows-msvc
|
||||
- TARGET: 1.27.2-x86_64-pc-windows-msvc
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows
|
||||
VCPKGRS_DYNAMIC: 1
|
||||
- TARGET: nightly-x86_64-pc-windows-msvc
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
|
||||
repository = "https://github.com/jgallagher/rusqlite"
|
||||
description = "Native bindings to the libsqlite3 library"
|
||||
|
@ -43,6 +43,8 @@ mod build {
|
||||
cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
|
||||
}
|
||||
cfg.compile("libsqlite3.a");
|
||||
|
||||
println!("cargo:lib_dir={}", out_dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
124
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
124
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
@ -1,10 +1,10 @@
|
||||
/* automatically generated by rust-bindgen */
|
||||
|
||||
pub const __GNUC_VA_LIST: i32 = 1;
|
||||
pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.22.0\0";
|
||||
pub const SQLITE_VERSION_NUMBER: i32 = 3022000;
|
||||
pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.24.0\0";
|
||||
pub const SQLITE_VERSION_NUMBER: i32 = 3024000;
|
||||
pub const SQLITE_SOURCE_ID: &'static [u8; 85usize] =
|
||||
b"2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2171d\0";
|
||||
b"2018-06-04 19:24:41 c7ee0833225bfd8c5ec2f9bf62b97c4e04d03bd9566366d5221ac8fb199a87ca\0";
|
||||
pub const SQLITE_OK: i32 = 0;
|
||||
pub const SQLITE_ERROR: i32 = 1;
|
||||
pub const SQLITE_INTERNAL: i32 = 2;
|
||||
@ -70,6 +70,7 @@ pub const SQLITE_IOERR_BEGIN_ATOMIC: i32 = 7434;
|
||||
pub const SQLITE_IOERR_COMMIT_ATOMIC: i32 = 7690;
|
||||
pub const SQLITE_IOERR_ROLLBACK_ATOMIC: i32 = 7946;
|
||||
pub const SQLITE_LOCKED_SHAREDCACHE: i32 = 262;
|
||||
pub const SQLITE_LOCKED_VTAB: i32 = 518;
|
||||
pub const SQLITE_BUSY_RECOVERY: i32 = 261;
|
||||
pub const SQLITE_BUSY_SNAPSHOT: i32 = 517;
|
||||
pub const SQLITE_CANTOPEN_NOTEMPDIR: i32 = 270;
|
||||
@ -77,6 +78,7 @@ pub const SQLITE_CANTOPEN_ISDIR: i32 = 526;
|
||||
pub const SQLITE_CANTOPEN_FULLPATH: i32 = 782;
|
||||
pub const SQLITE_CANTOPEN_CONVPATH: i32 = 1038;
|
||||
pub const SQLITE_CORRUPT_VTAB: i32 = 267;
|
||||
pub const SQLITE_CORRUPT_SEQUENCE: i32 = 523;
|
||||
pub const SQLITE_READONLY_RECOVERY: i32 = 264;
|
||||
pub const SQLITE_READONLY_CANTLOCK: i32 = 520;
|
||||
pub const SQLITE_READONLY_ROLLBACK: i32 = 776;
|
||||
@ -174,6 +176,7 @@ pub const SQLITE_FCNTL_PDB: i32 = 30;
|
||||
pub const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: i32 = 31;
|
||||
pub const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: i32 = 32;
|
||||
pub const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: i32 = 33;
|
||||
pub const SQLITE_FCNTL_LOCK_TIMEOUT: i32 = 34;
|
||||
pub const SQLITE_GET_LOCKPROXYFILE: i32 = 2;
|
||||
pub const SQLITE_SET_LOCKPROXYFILE: i32 = 3;
|
||||
pub const SQLITE_LAST_ERRNO: i32 = 4;
|
||||
@ -211,6 +214,7 @@ pub const SQLITE_CONFIG_PCACHE_HDRSZ: i32 = 24;
|
||||
pub const SQLITE_CONFIG_PMASZ: i32 = 25;
|
||||
pub const SQLITE_CONFIG_STMTJRNL_SPILL: i32 = 26;
|
||||
pub const SQLITE_CONFIG_SMALL_MALLOC: i32 = 27;
|
||||
pub const SQLITE_CONFIG_SORTERREF_SIZE: i32 = 28;
|
||||
pub const SQLITE_DBCONFIG_MAINDBNAME: i32 = 1000;
|
||||
pub const SQLITE_DBCONFIG_LOOKASIDE: i32 = 1001;
|
||||
pub const SQLITE_DBCONFIG_ENABLE_FKEY: i32 = 1002;
|
||||
@ -220,7 +224,8 @@ pub const SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: i32 = 1005;
|
||||
pub const SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: i32 = 1006;
|
||||
pub const SQLITE_DBCONFIG_ENABLE_QPSG: i32 = 1007;
|
||||
pub const SQLITE_DBCONFIG_TRIGGER_EQP: i32 = 1008;
|
||||
pub const SQLITE_DBCONFIG_MAX: i32 = 1008;
|
||||
pub const SQLITE_DBCONFIG_RESET_DATABASE: i32 = 1009;
|
||||
pub const SQLITE_DBCONFIG_MAX: i32 = 1009;
|
||||
pub const SQLITE_DENY: i32 = 1;
|
||||
pub const SQLITE_IGNORE: i32 = 2;
|
||||
pub const SQLITE_CREATE_INDEX: i32 = 1;
|
||||
@ -287,6 +292,8 @@ pub const SQLITE_UTF16: i32 = 4;
|
||||
pub const SQLITE_ANY: i32 = 5;
|
||||
pub const SQLITE_UTF16_ALIGNED: i32 = 8;
|
||||
pub const SQLITE_DETERMINISTIC: i32 = 2048;
|
||||
pub const SQLITE_WIN32_DATA_DIRECTORY_TYPE: i32 = 1;
|
||||
pub const SQLITE_WIN32_TEMP_DIRECTORY_TYPE: i32 = 2;
|
||||
pub const SQLITE_INDEX_SCAN_UNIQUE: i32 = 1;
|
||||
pub const SQLITE_INDEX_CONSTRAINT_EQ: i32 = 2;
|
||||
pub const SQLITE_INDEX_CONSTRAINT_GT: i32 = 4;
|
||||
@ -365,7 +372,8 @@ pub const SQLITE_DBSTATUS_CACHE_MISS: i32 = 8;
|
||||
pub const SQLITE_DBSTATUS_CACHE_WRITE: i32 = 9;
|
||||
pub const SQLITE_DBSTATUS_DEFERRED_FKS: i32 = 10;
|
||||
pub const SQLITE_DBSTATUS_CACHE_USED_SHARED: i32 = 11;
|
||||
pub const SQLITE_DBSTATUS_MAX: i32 = 11;
|
||||
pub const SQLITE_DBSTATUS_CACHE_SPILL: i32 = 12;
|
||||
pub const SQLITE_DBSTATUS_MAX: i32 = 12;
|
||||
pub const SQLITE_STMTSTATUS_FULLSCAN_STEP: i32 = 1;
|
||||
pub const SQLITE_STMTSTATUS_SORT: i32 = 2;
|
||||
pub const SQLITE_STMTSTATUS_AUTOINDEX: i32 = 3;
|
||||
@ -387,6 +395,10 @@ pub const SQLITE_SCANSTAT_EST: i32 = 2;
|
||||
pub const SQLITE_SCANSTAT_NAME: i32 = 3;
|
||||
pub const SQLITE_SCANSTAT_EXPLAIN: i32 = 4;
|
||||
pub const SQLITE_SCANSTAT_SELECTID: i32 = 5;
|
||||
pub const SQLITE_SERIALIZE_NOCOPY: i32 = 1;
|
||||
pub const SQLITE_DESERIALIZE_FREEONCLOSE: i32 = 1;
|
||||
pub const SQLITE_DESERIALIZE_RESIZEABLE: i32 = 2;
|
||||
pub const SQLITE_DESERIALIZE_READONLY: i32 = 4;
|
||||
pub const NOT_WITHIN: i32 = 0;
|
||||
pub const PARTLY_WITHIN: i32 = 1;
|
||||
pub const FULLY_WITHIN: i32 = 2;
|
||||
@ -2255,6 +2267,24 @@ extern "C" {
|
||||
#[link_name = "\u{1}sqlite3_data_directory"]
|
||||
pub static mut sqlite3_data_directory: *mut ::std::os::raw::c_char;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_win32_set_directory(
|
||||
type_: ::std::os::raw::c_ulong,
|
||||
zValue: *mut ::std::os::raw::c_void,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_win32_set_directory8(
|
||||
type_: ::std::os::raw::c_ulong,
|
||||
zValue: *const ::std::os::raw::c_char,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_win32_set_directory16(
|
||||
type_: ::std::os::raw::c_ulong,
|
||||
zValue: *const ::std::os::raw::c_void,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_get_autocommit(arg1: *mut sqlite3) -> ::std::os::raw::c_int;
|
||||
}
|
||||
@ -3412,6 +3442,72 @@ extern "C" {
|
||||
extern "C" {
|
||||
pub fn sqlite3_test_control(op: ::std::os::raw::c_int, ...) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_keyword_count() -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_keyword_name(
|
||||
arg1: ::std::os::raw::c_int,
|
||||
arg2: *mut *const ::std::os::raw::c_char,
|
||||
arg3: *mut ::std::os::raw::c_int,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_keyword_check(
|
||||
arg1: *const ::std::os::raw::c_char,
|
||||
arg2: ::std::os::raw::c_int,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct sqlite3_str {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_new(arg1: *mut sqlite3) -> *mut sqlite3_str;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_finish(arg1: *mut sqlite3_str) -> *mut ::std::os::raw::c_char;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_appendf(arg1: *mut sqlite3_str, zFormat: *const ::std::os::raw::c_char, ...);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_vappendf(
|
||||
arg1: *mut sqlite3_str,
|
||||
zFormat: *const ::std::os::raw::c_char,
|
||||
arg2: *mut __va_list_tag,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_append(
|
||||
arg1: *mut sqlite3_str,
|
||||
zIn: *const ::std::os::raw::c_char,
|
||||
N: ::std::os::raw::c_int,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_appendall(arg1: *mut sqlite3_str, zIn: *const ::std::os::raw::c_char);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_appendchar(
|
||||
arg1: *mut sqlite3_str,
|
||||
N: ::std::os::raw::c_int,
|
||||
C: ::std::os::raw::c_char,
|
||||
);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_reset(arg1: *mut sqlite3_str);
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_errcode(arg1: *mut sqlite3_str) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_length(arg1: *mut sqlite3_str) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_str_value(arg1: *mut sqlite3_str) -> *mut ::std::os::raw::c_char;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_status(
|
||||
op: ::std::os::raw::c_int,
|
||||
@ -4070,6 +4166,24 @@ extern "C" {
|
||||
zDb: *const ::std::os::raw::c_char,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_serialize(
|
||||
db: *mut sqlite3,
|
||||
zSchema: *const ::std::os::raw::c_char,
|
||||
piSize: *mut sqlite3_int64,
|
||||
mFlags: ::std::os::raw::c_uint,
|
||||
) -> *mut ::std::os::raw::c_uchar;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn sqlite3_deserialize(
|
||||
db: *mut sqlite3,
|
||||
zSchema: *const ::std::os::raw::c_char,
|
||||
pData: *mut ::std::os::raw::c_uchar,
|
||||
szDb: sqlite3_int64,
|
||||
szBuf: sqlite3_int64,
|
||||
mFlags: ::std::os::raw::c_uint,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}
|
||||
pub type sqlite3_rtree_dbl = f64;
|
||||
extern "C" {
|
||||
pub fn sqlite3_rtree_geometry_callback(
|
||||
|
12848
libsqlite3-sys/sqlite3/sqlite3.c
vendored
12848
libsqlite3-sys/sqlite3/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
799
libsqlite3-sys/sqlite3/sqlite3.h
vendored
799
libsqlite3-sys/sqlite3/sqlite3.h
vendored
File diff suppressed because it is too large
Load Diff
34
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
34
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
@ -295,6 +295,21 @@ struct sqlite3_api_routines {
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -563,8 +578,23 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqltie3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqltie3_api->vtab_collation
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
@ -4,7 +4,7 @@ cd $SCRIPT_DIR
|
||||
SQLITE3_LIB_DIR=$SCRIPT_DIR/sqlite3
|
||||
|
||||
# Download and extract amalgamation
|
||||
SQLITE=sqlite-amalgamation-3220000
|
||||
SQLITE=sqlite-amalgamation-3240000
|
||||
curl -O http://sqlite.org/2018/$SQLITE.zip
|
||||
unzip -p $SQLITE.zip $SQLITE/sqlite3.c > $SQLITE3_LIB_DIR/sqlite3.c
|
||||
unzip -p $SQLITE.zip $SQLITE/sqlite3.h > $SQLITE3_LIB_DIR/sqlite3.h
|
||||
|
160
src/busy.rs
Normal file
160
src/busy.rs
Normal file
@ -0,0 +1,160 @@
|
||||
///! 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 ffi;
|
||||
use {Connection, InnerConnection, Result};
|
||||
|
||||
impl Connection {
|
||||
/// Set a busy handler that sleeps for a specified amount of time when a table is locked.
|
||||
/// The handler will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
|
||||
///
|
||||
/// Calling this routine with an argument equal to zero turns off all busy handlers.
|
||||
//
|
||||
/// There can only be a single busy handler for a particular database connection at any given moment.
|
||||
/// If another busy handler was defined (using `busy_handler`) prior to calling this routine, that other busy handler is cleared.
|
||||
pub fn busy_timeout(&self, timeout: Duration) -> Result<()> {
|
||||
let ms = timeout
|
||||
.as_secs()
|
||||
.checked_mul(1000)
|
||||
.and_then(|t| t.checked_add(timeout.subsec_millis().into()))
|
||||
.expect("too big");
|
||||
self.db.borrow_mut().busy_timeout(ms as i32)
|
||||
}
|
||||
|
||||
/// Register a callback to handle `SQLITE_BUSY` errors.
|
||||
///
|
||||
/// If the busy callback is `None`, then `SQLITE_BUSY is returned immediately upon encountering the lock.`
|
||||
/// The argument to the busy handler callback is the number of times that the busy handler has been invoked previously for the same locking event.
|
||||
/// If the busy callback returns `false`, then no additional attempts are made to access the database and `SQLITE_BUSY` is returned to the application.
|
||||
/// If the callback returns `true`, then another attempt is made to access the database and the cycle repeats.
|
||||
///
|
||||
/// There can only be a single busy handler defined for each database connection.
|
||||
/// Setting a new busy handler clears any previously set handler.
|
||||
/// Note that calling `busy_timeout()` or evaluating `PRAGMA busy_timeout=N` will change the busy handler and thus clear any previously set busy handler.
|
||||
pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
|
||||
unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
|
||||
let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);
|
||||
if handler_fn(count) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
let mut c = self.db.borrow_mut();
|
||||
let r = match callback {
|
||||
Some(f) => unsafe {
|
||||
ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), mem::transmute(f))
|
||||
},
|
||||
None => unsafe { ffi::sqlite3_busy_handler(c.db(), None, ptr::null_mut()) },
|
||||
};
|
||||
c.decode_result(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerConnection {
|
||||
fn busy_timeout(&mut self, timeout: c_int) -> Result<()> {
|
||||
let r = unsafe { ffi::sqlite3_busy_timeout(self.db, timeout) };
|
||||
self.decode_result(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate tempdir;
|
||||
use self::tempdir::TempDir;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::sync_channel;
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
|
||||
use {Connection, Error, ErrorCode, TransactionBehavior};
|
||||
|
||||
#[test]
|
||||
fn test_default_busy() {
|
||||
let temp_dir = TempDir::new("test_default_busy").unwrap();
|
||||
let path = temp_dir.path().join("test.db3");
|
||||
|
||||
let mut db1 = Connection::open(&path).unwrap();
|
||||
let tx1 = db1
|
||||
.transaction_with_behavior(TransactionBehavior::Exclusive)
|
||||
.unwrap();
|
||||
let db2 = Connection::open(&path).unwrap();
|
||||
let r = db2.query_row("PRAGMA schema_version", &[], |_| unreachable!());
|
||||
match r.unwrap_err() {
|
||||
Error::SqliteFailure(err, _) => {
|
||||
assert_eq!(err.code, ErrorCode::DatabaseBusy);
|
||||
}
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
tx1.rollback().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_busy_timeout() {
|
||||
let temp_dir = TempDir::new("test_busy_timeout").unwrap();
|
||||
let path = temp_dir.path().join("test.db3");
|
||||
|
||||
let db2 = Connection::open(&path).unwrap();
|
||||
db2.busy_timeout(Duration::from_secs(1)).unwrap();
|
||||
|
||||
let (rx, tx) = sync_channel(0);
|
||||
let child = thread::spawn(move || {
|
||||
let mut db1 = Connection::open(&path).unwrap();
|
||||
let tx1 = db1
|
||||
.transaction_with_behavior(TransactionBehavior::Exclusive)
|
||||
.unwrap();
|
||||
rx.send(1).unwrap();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
tx1.rollback().unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(tx.recv().unwrap(), 1);
|
||||
let _ =
|
||||
db2.query_row("PRAGMA schema_version", &[], |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
|
||||
child.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_busy_handler() {
|
||||
lazy_static! {
|
||||
static ref CALLED: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
fn busy_handler(_: i32) -> bool {
|
||||
CALLED.store(true, Ordering::Relaxed);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
true
|
||||
}
|
||||
|
||||
let temp_dir = TempDir::new("test_busy_handler").unwrap();
|
||||
let path = temp_dir.path().join("test.db3");
|
||||
|
||||
let db2 = Connection::open(&path).unwrap();
|
||||
db2.busy_handler(Some(busy_handler)).unwrap();
|
||||
|
||||
let (rx, tx) = sync_channel(0);
|
||||
let child = thread::spawn(move || {
|
||||
let mut db1 = Connection::open(&path).unwrap();
|
||||
let tx1 = db1
|
||||
.transaction_with_behavior(TransactionBehavior::Exclusive)
|
||||
.unwrap();
|
||||
rx.send(1).unwrap();
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
tx1.rollback().unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(tx.recv().unwrap(), 1);
|
||||
let _ =
|
||||
db2.query_row("PRAGMA schema_version", &[], |row| {
|
||||
row.get_checked::<_, i32>(0)
|
||||
}).expect("unexpected error");
|
||||
assert_eq!(CALLED.load(Ordering::Relaxed), true);
|
||||
|
||||
child.join().unwrap();
|
||||
}
|
||||
}
|
38
src/lib.rs
38
src/lib.rs
@ -56,7 +56,7 @@ extern crate libsqlite3_sys as ffi;
|
||||
extern crate lru_cache;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(all(test, feature = "trace"), feature = "vtab"))]
|
||||
#[cfg(any(test, feature = "vtab"))]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
@ -126,6 +126,7 @@ mod hooks;
|
||||
#[cfg(feature = "hooks")]
|
||||
pub use hooks::*;
|
||||
mod unlock_notify;
|
||||
mod busy;
|
||||
#[cfg(feature = "vtab")]
|
||||
pub mod vtab;
|
||||
#[cfg(any(feature = "functions", feature = "vtab"))]
|
||||
@ -994,6 +995,41 @@ mod test {
|
||||
Connection::open_in_memory().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_concurrent_transactions_busy_commit() {
|
||||
let tmp = TempDir::new("locked").unwrap();
|
||||
let path = tmp.path().join("transactions.db3");
|
||||
|
||||
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");
|
||||
|
||||
let mut db1 = Connection::open(&path).unwrap();
|
||||
let mut db2 = Connection::open(&path).unwrap();
|
||||
|
||||
db1.execute_batch("PRAGMA busy_timeout = 0;").unwrap();
|
||||
db2.execute_batch("PRAGMA busy_timeout = 0;").unwrap();
|
||||
|
||||
{
|
||||
let tx1 = db1.transaction().unwrap();
|
||||
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();
|
||||
|
||||
tx1.execute("INSERT INTO foo VALUES(?1)", &[&1]).unwrap();
|
||||
let _ = tx2.execute("INSERT INTO foo VALUES(?1)", &[&2]);
|
||||
|
||||
let _ = tx1.commit();
|
||||
let _ = tx2.commit();
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -167,8 +167,9 @@ impl<'conn> Transaction<'conn> {
|
||||
}
|
||||
|
||||
fn commit_(&mut self) -> Result<()> {
|
||||
self.conn.execute_batch("COMMIT")?;
|
||||
self.committed = true;
|
||||
self.conn.execute_batch("COMMIT")
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A convenience method which consumes and rolls back a transaction.
|
||||
@ -177,8 +178,9 @@ impl<'conn> Transaction<'conn> {
|
||||
}
|
||||
|
||||
fn rollback_(&mut self) -> Result<()> {
|
||||
self.conn.execute_batch("ROLLBACK")?;
|
||||
self.committed = true;
|
||||
self.conn.execute_batch("ROLLBACK")
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consumes the transaction, committing or rolling back according to the current setting
|
||||
@ -195,7 +197,7 @@ impl<'conn> Transaction<'conn> {
|
||||
return Ok(());
|
||||
}
|
||||
match self.drop_behavior() {
|
||||
DropBehavior::Commit => self.commit_(),
|
||||
DropBehavior::Commit => self.commit_().or_else(|_| self.rollback_()),
|
||||
DropBehavior::Rollback => self.rollback_(),
|
||||
DropBehavior::Ignore => Ok(()),
|
||||
DropBehavior::Panic => panic!("Transaction dropped unexpectedly."),
|
||||
@ -277,9 +279,10 @@ impl<'conn> Savepoint<'conn> {
|
||||
}
|
||||
|
||||
fn commit_(&mut self) -> Result<()> {
|
||||
self.committed = true;
|
||||
self.conn
|
||||
.execute_batch(&format!("RELEASE {}", self.name))
|
||||
.execute_batch(&format!("RELEASE {}", self.name))?;
|
||||
self.committed = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A convenience method which rolls back a savepoint.
|
||||
@ -307,7 +310,7 @@ impl<'conn> Savepoint<'conn> {
|
||||
return Ok(());
|
||||
}
|
||||
match self.drop_behavior() {
|
||||
DropBehavior::Commit => self.commit_(),
|
||||
DropBehavior::Commit => self.commit_().or_else(|_| self.rollback()),
|
||||
DropBehavior::Rollback => self.rollback(),
|
||||
DropBehavior::Ignore => Ok(()),
|
||||
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use super::{Null, Value, ValueRef};
|
||||
#[cfg(feature = "array")]
|
||||
use vtab::array::Array;
|
||||
@ -156,6 +157,12 @@ impl<T: ToSql> ToSql for Option<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for Cow<'a, str> {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ToSql;
|
||||
@ -172,4 +179,16 @@ mod test {
|
||||
is_to_sql::<u16>();
|
||||
is_to_sql::<u32>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cow_str() {
|
||||
use std::borrow::Cow;
|
||||
let s = "str";
|
||||
let cow = Cow::Borrowed(s);
|
||||
let r = cow.to_sql();
|
||||
assert!(r.is_ok());
|
||||
let cow = Cow::Owned::<str>(String::from(s));
|
||||
let r = cow.to_sql();
|
||||
assert!(r.is_ok());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user