Merge remote-tracking branch 'jgallagher/master' into vtab

This commit is contained in:
gwenn 2017-09-11 19:45:22 +02:00
commit c842e789bd
29 changed files with 576 additions and 430 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
libsqlite3-sys/sqlite3 linguist-vendored

View File

@ -18,3 +18,7 @@ rusqlite contributors
* [Omar Ferrer](https://github.com/chamakits) * [Omar Ferrer](https://github.com/chamakits)
* [Lee Jenkins](https://github.com/reddraggone9) * [Lee Jenkins](https://github.com/reddraggone9)
* [miedzinski](https://github.com/miedzinski) * [miedzinski](https://github.com/miedzinski)
* [aidanhs](https://github.com/aidanhs)
* [Steven Fackler](https://github.com/sfackler)
* [Davide Aversa](https://github.com/THeK3nger)
* [mcgoo](https://github.com/mcgoo)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rusqlite" name = "rusqlite"
version = "0.11.0" version = "0.12.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
@ -30,11 +30,11 @@ vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
csvtab = ["csv"] csvtab = ["csv"]
[dependencies] [dependencies]
time = "0.1" time = "0.1.0"
bitflags = "0.7" bitflags = "0.9"
lru-cache = "0.1" lru-cache = "0.1"
chrono = { version = "0.3", optional = true } chrono = { version = "0.3", optional = true }
serde_json = { version = "0.9", optional = true } serde_json = { version = "1.0", optional = true }
[dev-dependencies] [dev-dependencies]
tempdir = "0.3" tempdir = "0.3"

View File

@ -1,3 +1,10 @@
# Version 0.12.0 (2017-05-29)
* Defines HAVE\_USLEEP when building with a bundled SQLite (#263).
* Updates dependencies to their latest versions, particularly serde to 1.0.
* Adds support for vcpkg on Windows.
* Adds `ToSql` impls for `str` and `[u8]`.
# Version 0.11.0 (2017-04-06) # Version 0.11.0 (2017-04-06)
* Avoid publicly exporting SQLite constants multiple times from libsqlite3-sys. * Avoid publicly exporting SQLite constants multiple times from libsqlite3-sys.

View File

@ -94,17 +94,27 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
## Notes on building rusqlite and libsqlite3-sys ## Notes on building rusqlite and libsqlite3-sys
`libsqlite3-sys` is a separate crate from `rusqlite` that provides the Rust `libsqlite3-sys` is a separate crate from `rusqlite` that provides the Rust
declarations for SQLite's C API. By default, `libsqlite3-sys` attempts to use declarations for SQLite's C API. By default, `libsqlite3-sys` attempts to find a SQLite library that already exists on your system using pkg-config, or a
pkg-config to find a SQLite library that already exists on your system. You can [Vcpkg](https://github.com/Microsoft/vcpkg) installation for MSVC ABI builds.
adjust this behavior in a couple of ways:
You can adjust this behavior in a number of ways:
* If you use the `bundled` feature, `libsqlite3-sys` will use the * If you use the `bundled` feature, `libsqlite3-sys` will use the
[gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and [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 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` 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. 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:
```
[dependencies.rusqlite]
version = "0.11.0"
features = ["bundled"]
```
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite * You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
library. library.
* Installing the sqlite3 development packages will usually be all that is required, but
the build helpers for [pkg-config](https://github.com/alexcrichton/pkg-config-rs)
and [vcpkg](https://github.com/mcgoo/vcpkg-rs) have some additional configuration
options. The default when using vcpkg is to dynamically link. `vcpkg install sqlite3:x64-windows` will install the required library.
### Binding generation ### Binding generation

View File

@ -1,6 +1,12 @@
environment: environment:
TARGET: 1.15.0-x86_64-pc-windows-gnu matrix:
- TARGET: 1.15.0-x86_64-pc-windows-gnu
MSYS2_BITS: 64 MSYS2_BITS: 64
- TARGET: 1.15.0-x86_64-pc-windows-msvc
VCPKG_DEFAULT_TRIPLET: x64-windows
- TARGET: nightly-x86_64-pc-windows-msvc
VCPKG_DEFAULT_TRIPLET: x64-windows-static
RUSTFLAGS: -Ctarget-feature=+crt-static
install: install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe"
- rust-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - rust-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
@ -9,11 +15,18 @@ install:
- rustc -V - rustc -V
- cargo -V - cargo -V
- ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-dll-win64-x64-3170000.zip' # download SQLite dll (useful only when the `bundled` feature is not set) - ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-dll-win64-x64-3170000.zip' # download SQLite dll (useful only when the `bundled` feature is not set)
- cmd: 7z e sqlite-dll-win64-x64-3170000.zip -y > nul - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-dll-win64-x64-3170000.zip -y > nul
- ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-amalgamation-3170000.zip' # download SQLite headers (useful only when the `bundled` feature is not set) - ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-amalgamation-3170000.zip' # download SQLite headers (useful only when the `bundled` feature is not set)
- cmd: 7z e sqlite-amalgamation-3170000.zip -y > nul - if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-amalgamation-3170000.zip -y > nul
- SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set) - if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set)
- SET SQLITE3_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite headers have been downloaded (useful only when the `bundled` feature is not set) - if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite headers have been downloaded (useful only when the `bundled` feature is not set)
# install vcpkg and the sqlite3 package
- if defined VCPKG_DEFAULT_TRIPLET git clone https://github.com/Microsoft/vcpkg c:\projects\vcpkg
- if defined VCPKG_DEFAULT_TRIPLET c:\projects\vcpkg\bootstrap-vcpkg.bat
- if defined VCPKG_DEFAULT_TRIPLET set VCPKG_ROOT=c:\projects\vcpkg
- if defined VCPKG_DEFAULT_TRIPLET %VCPKG_ROOT%\vcpkg.exe install sqlite3
- if defined VCPKG_DEFAULT_TRIPLET appveyor DownloadFile http://releases.llvm.org/4.0.0/LLVM-4.0.0-win64.exe
- if defined VCPKG_DEFAULT_TRIPLET LLVM-4.0.0-win64.exe /S
build: false build: false

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.8.0" version = "0.8.1"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
description = "Native bindings to the libsqlite3 library" description = "Native bindings to the libsqlite3 library"
@ -13,16 +13,19 @@ categories = ["database", "external-ffi-bindings"]
[features] [features]
default = ["min_sqlite_version_3_6_8"] default = ["min_sqlite_version_3_6_8"]
bundled = ["gcc"] bundled = ["gcc"]
buildtime_bindgen = ["bindgen", "pkg-config"] buildtime_bindgen = ["bindgen", "pkg-config", "vcpkg"]
min_sqlite_version_3_6_8 = ["pkg-config"] min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_6_11 = ["pkg-config"] min_sqlite_version_3_6_11 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_6_23 = ["pkg-config"] min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_3 = ["pkg-config"] min_sqlite_version_3_7_3 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_4 = ["pkg-config"] min_sqlite_version_3_7_4 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_7 = ["pkg-config"] min_sqlite_version_3_7_7 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_16 = ["pkg-config"] min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
[build-dependencies] [build-dependencies]
bindgen = { version = "0.21", optional = true } bindgen = { version = "0.23", optional = true }
pkg-config = { version = "0.3", optional = true } pkg-config = { version = "0.3", optional = true }
gcc = { version = "0.3", optional = true } gcc = { version = "0.3", optional = true }
[target.'cfg(target_env = "msvc")'.build-dependencies]
vcpkg = { version = "0.2", optional = true }

View File

@ -34,6 +34,7 @@ mod build {
.flag("-DSQLITE_SOUNDEX") .flag("-DSQLITE_SOUNDEX")
.flag("-DSQLITE_THREADSAFE=1") .flag("-DSQLITE_THREADSAFE=1")
.flag("-DSQLITE_USE_URI") .flag("-DSQLITE_USE_URI")
.flag("-DHAVE_USLEEP=1")
.compile("libsqlite3.a"); .compile("libsqlite3.a");
} }
} }
@ -42,6 +43,9 @@ mod build {
mod build { mod build {
extern crate pkg_config; extern crate pkg_config;
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
extern crate vcpkg;
use std::env; use std::env;
pub enum HeaderLocation { pub enum HeaderLocation {
@ -79,6 +83,10 @@ mod build {
return HeaderLocation::FromEnvironment; return HeaderLocation::FromEnvironment;
} }
if let Some(header) = try_vcpkg() {
return header;
}
// See if pkg-config can do everything for us. // See if pkg-config can do everything for us.
match pkg_config::Config::new().print_system_libs(false).probe("sqlite3") { match pkg_config::Config::new().print_system_libs(false).probe("sqlite3") {
Ok(mut lib) => { Ok(mut lib) => {
@ -100,6 +108,23 @@ mod build {
} }
} }
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
fn try_vcpkg() -> Option<HeaderLocation> {
// See if vcpkg can find it.
if let Ok(mut lib) = vcpkg::Config::new().probe("sqlite3") {
if let Some(mut header) = lib.include_paths.pop() {
header.push("sqlite3.h");
return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
}
}
None
}
#[cfg(not(all(feature = "vcpkg", target_env = "msvc")))]
fn try_vcpkg() -> Option<HeaderLocation> {
None
}
#[cfg(not(feature = "buildtime_bindgen"))] #[cfg(not(feature = "buildtime_bindgen"))]
mod bindings { mod bindings {
use super::HeaderLocation; use super::HeaderLocation;
@ -142,7 +167,7 @@ mod build {
mod bindings { mod bindings {
extern crate bindgen; extern crate bindgen;
use self::bindgen::chooser::{TypeChooser, IntKind}; use self::bindgen::callbacks::{ParseCallbacks, IntKind};
use super::HeaderLocation; use super::HeaderLocation;
use std::env; use std::env;
@ -153,7 +178,7 @@ mod build {
#[derive(Debug)] #[derive(Debug)]
struct SqliteTypeChooser; struct SqliteTypeChooser;
impl TypeChooser for SqliteTypeChooser { impl ParseCallbacks for SqliteTypeChooser {
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> { fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 { if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
Some(IntKind::I32) Some(IntKind::I32)
@ -169,7 +194,7 @@ mod build {
let mut output = Vec::new(); let mut output = Vec::new();
bindgen::builder() bindgen::builder()
.header(header.clone()) .header(header.clone())
.type_chooser(Box::new(SqliteTypeChooser)) .parse_callbacks(Box::new(SqliteTypeChooser))
.generate() .generate()
.expect(&format!("could not run bindgen on header {}", header)) .expect(&format!("could not run bindgen on header {}", header))
.write(Box::new(&mut output)) .write(Box::new(&mut output))

View File

@ -64,29 +64,29 @@ pub struct Error {
impl Error { impl Error {
pub fn new(result_code: c_int) -> Error { pub fn new(result_code: c_int) -> Error {
let code = match result_code & 0xff { let code = match result_code & 0xff {
SQLITE_INTERNAL => ErrorCode::InternalMalfunction, super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
SQLITE_PERM => ErrorCode::PermissionDenied, super::SQLITE_PERM => ErrorCode::PermissionDenied,
SQLITE_ABORT => ErrorCode::OperationAborted, super::SQLITE_ABORT => ErrorCode::OperationAborted,
SQLITE_BUSY => ErrorCode::DatabaseBusy, super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
SQLITE_LOCKED => ErrorCode::DatabaseLocked, super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
SQLITE_NOMEM => ErrorCode::OutOfMemory, super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
SQLITE_READONLY => ErrorCode::ReadOnly, super::SQLITE_READONLY => ErrorCode::ReadOnly,
SQLITE_INTERRUPT => ErrorCode::OperationInterrupted, super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
SQLITE_IOERR => ErrorCode::SystemIOFailure, super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt, super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
SQLITE_NOTFOUND => ErrorCode::NotFound, super::SQLITE_NOTFOUND => ErrorCode::NotFound,
SQLITE_FULL => ErrorCode::DiskFull, super::SQLITE_FULL => ErrorCode::DiskFull,
SQLITE_CANTOPEN => ErrorCode::CannotOpen, super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed, super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
SQLITE_SCHEMA => ErrorCode::SchemaChanged, super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
SQLITE_TOOBIG => ErrorCode::TooBig, super::SQLITE_TOOBIG => ErrorCode::TooBig,
SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation, super::SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
SQLITE_MISMATCH => ErrorCode::TypeMismatch, super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
SQLITE_MISUSE => ErrorCode::APIMisuse, super::SQLITE_MISUSE => ErrorCode::APIMisuse,
SQLITE_NOLFS => ErrorCode::NoLargeFileSupport, super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied, super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
SQLITE_RANGE => ErrorCode::ParameterOutOfRange, super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
SQLITE_NOTADB => ErrorCode::NotADatabase, super::SQLITE_NOTADB => ErrorCode::NotADatabase,
_ => ErrorCode::Unknown, _ => ErrorCode::Unknown,
}; };
@ -114,146 +114,100 @@ impl error::Error for Error {
// in the current version of SQLite. We repeat them here so we don't have to worry about which // 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. // version of SQLite added which constants, and we only use them to implement code_to_str below.
const SQLITE_OK : c_int = 0;
const SQLITE_ERROR : c_int = 1;
const SQLITE_INTERNAL : c_int = 2;
const SQLITE_PERM : c_int = 3;
const SQLITE_ABORT : c_int = 4;
const SQLITE_BUSY : c_int = 5;
const SQLITE_LOCKED : c_int = 6;
const SQLITE_NOMEM : c_int = 7;
const SQLITE_READONLY : c_int = 8;
const SQLITE_INTERRUPT : c_int = 9;
const SQLITE_IOERR : c_int = 10;
const SQLITE_CORRUPT : c_int = 11;
const SQLITE_NOTFOUND : c_int = 12;
const SQLITE_FULL : c_int = 13;
const SQLITE_CANTOPEN : c_int = 14;
const SQLITE_PROTOCOL : c_int = 15;
const SQLITE_EMPTY : c_int = 16;
const SQLITE_SCHEMA : c_int = 17;
const SQLITE_TOOBIG : c_int = 18;
const SQLITE_CONSTRAINT: c_int = 19;
const SQLITE_MISMATCH : c_int = 20;
const SQLITE_MISUSE : c_int = 21;
const SQLITE_NOLFS : c_int = 22;
const SQLITE_AUTH : c_int = 23;
const SQLITE_FORMAT : c_int = 24;
const SQLITE_RANGE : c_int = 25;
const SQLITE_NOTADB : c_int = 26;
const SQLITE_NOTICE : c_int = 27; const SQLITE_NOTICE : c_int = 27;
const SQLITE_WARNING : c_int = 28; const SQLITE_WARNING : c_int = 28;
const SQLITE_ROW : c_int = 100;
const SQLITE_DONE : c_int = 101;
// Extended result codes. // Extended result codes.
const SQLITE_IOERR_READ : c_int = (SQLITE_IOERR | (1<<8)); const SQLITE_IOERR_SHMOPEN : c_int = (super::SQLITE_IOERR | (18<<8));
const SQLITE_IOERR_SHORT_READ : c_int = (SQLITE_IOERR | (2<<8)); const SQLITE_IOERR_SHMSIZE : c_int = (super::SQLITE_IOERR | (19<<8));
const SQLITE_IOERR_WRITE : c_int = (SQLITE_IOERR | (3<<8)); const SQLITE_IOERR_SHMLOCK : c_int = (super::SQLITE_IOERR | (20<<8));
const SQLITE_IOERR_FSYNC : c_int = (SQLITE_IOERR | (4<<8)); const SQLITE_IOERR_SHMMAP : c_int = (super::SQLITE_IOERR | (21<<8));
const SQLITE_IOERR_DIR_FSYNC : c_int = (SQLITE_IOERR | (5<<8)); const SQLITE_IOERR_SEEK : c_int = (super::SQLITE_IOERR | (22<<8));
const SQLITE_IOERR_TRUNCATE : c_int = (SQLITE_IOERR | (6<<8)); const SQLITE_IOERR_DELETE_NOENT : c_int = (super::SQLITE_IOERR | (23<<8));
const SQLITE_IOERR_FSTAT : c_int = (SQLITE_IOERR | (7<<8)); const SQLITE_IOERR_MMAP : c_int = (super::SQLITE_IOERR | (24<<8));
const SQLITE_IOERR_UNLOCK : c_int = (SQLITE_IOERR | (8<<8)); const SQLITE_IOERR_GETTEMPPATH : c_int = (super::SQLITE_IOERR | (25<<8));
const SQLITE_IOERR_RDLOCK : c_int = (SQLITE_IOERR | (9<<8)); const SQLITE_IOERR_CONVPATH : c_int = (super::SQLITE_IOERR | (26<<8));
const SQLITE_IOERR_DELETE : c_int = (SQLITE_IOERR | (10<<8)); const SQLITE_IOERR_VNODE : c_int = (super::SQLITE_IOERR | (27<<8));
const SQLITE_IOERR_BLOCKED : c_int = (SQLITE_IOERR | (11<<8)); const SQLITE_LOCKED_SHAREDCACHE : c_int = (super::SQLITE_LOCKED | (1<<8));
const SQLITE_IOERR_NOMEM : c_int = (SQLITE_IOERR | (12<<8)); const SQLITE_BUSY_RECOVERY : c_int = (super::SQLITE_BUSY | (1<<8));
const SQLITE_IOERR_ACCESS : c_int = (SQLITE_IOERR | (13<<8)); const SQLITE_BUSY_SNAPSHOT : c_int = (super::SQLITE_BUSY | (2<<8));
const SQLITE_IOERR_CHECKRESERVEDLOCK : c_int = (SQLITE_IOERR | (14<<8)); const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (super::SQLITE_CANTOPEN | (1<<8));
const SQLITE_IOERR_LOCK : c_int = (SQLITE_IOERR | (15<<8)); const SQLITE_CANTOPEN_ISDIR : c_int = (super::SQLITE_CANTOPEN | (2<<8));
const SQLITE_IOERR_CLOSE : c_int = (SQLITE_IOERR | (16<<8)); const SQLITE_CANTOPEN_FULLPATH : c_int = (super::SQLITE_CANTOPEN | (3<<8));
const SQLITE_IOERR_DIR_CLOSE : c_int = (SQLITE_IOERR | (17<<8)); const SQLITE_CANTOPEN_CONVPATH : c_int = (super::SQLITE_CANTOPEN | (4<<8));
const SQLITE_IOERR_SHMOPEN : c_int = (SQLITE_IOERR | (18<<8)); const SQLITE_CORRUPT_VTAB : c_int = (super::SQLITE_CORRUPT | (1<<8));
const SQLITE_IOERR_SHMSIZE : c_int = (SQLITE_IOERR | (19<<8)); const SQLITE_READONLY_RECOVERY : c_int = (super::SQLITE_READONLY | (1<<8));
const SQLITE_IOERR_SHMLOCK : c_int = (SQLITE_IOERR | (20<<8)); const SQLITE_READONLY_CANTLOCK : c_int = (super::SQLITE_READONLY | (2<<8));
const SQLITE_IOERR_SHMMAP : c_int = (SQLITE_IOERR | (21<<8)); const SQLITE_READONLY_ROLLBACK : c_int = (super::SQLITE_READONLY | (3<<8));
const SQLITE_IOERR_SEEK : c_int = (SQLITE_IOERR | (22<<8)); const SQLITE_READONLY_DBMOVED : c_int = (super::SQLITE_READONLY | (4<<8));
const SQLITE_IOERR_DELETE_NOENT : c_int = (SQLITE_IOERR | (23<<8)); const SQLITE_ABORT_ROLLBACK : c_int = (super::SQLITE_ABORT | (2<<8));
const SQLITE_IOERR_MMAP : c_int = (SQLITE_IOERR | (24<<8)); const SQLITE_CONSTRAINT_CHECK : c_int = (super::SQLITE_CONSTRAINT | (1<<8));
const SQLITE_IOERR_GETTEMPPATH : c_int = (SQLITE_IOERR | (25<<8)); const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (super::SQLITE_CONSTRAINT | (2<<8));
const SQLITE_IOERR_CONVPATH : c_int = (SQLITE_IOERR | (26<<8)); const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (super::SQLITE_CONSTRAINT | (3<<8));
const SQLITE_IOERR_VNODE : c_int = (SQLITE_IOERR | (27<<8)); const SQLITE_CONSTRAINT_FUNCTION : c_int = (super::SQLITE_CONSTRAINT | (4<<8));
const SQLITE_LOCKED_SHAREDCACHE : c_int = (SQLITE_LOCKED | (1<<8)); const SQLITE_CONSTRAINT_NOTNULL : c_int = (super::SQLITE_CONSTRAINT | (5<<8));
const SQLITE_BUSY_RECOVERY : c_int = (SQLITE_BUSY | (1<<8)); const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (super::SQLITE_CONSTRAINT | (6<<8));
const SQLITE_BUSY_SNAPSHOT : c_int = (SQLITE_BUSY | (2<<8)); const SQLITE_CONSTRAINT_TRIGGER : c_int = (super::SQLITE_CONSTRAINT | (7<<8));
const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (SQLITE_CANTOPEN | (1<<8)); const SQLITE_CONSTRAINT_UNIQUE : c_int = (super::SQLITE_CONSTRAINT | (8<<8));
const SQLITE_CANTOPEN_ISDIR : c_int = (SQLITE_CANTOPEN | (2<<8)); const SQLITE_CONSTRAINT_VTAB : c_int = (super::SQLITE_CONSTRAINT | (9<<8));
const SQLITE_CANTOPEN_FULLPATH : c_int = (SQLITE_CANTOPEN | (3<<8)); const SQLITE_CONSTRAINT_ROWID : c_int = (super::SQLITE_CONSTRAINT |(10<<8));
const SQLITE_CANTOPEN_CONVPATH : c_int = (SQLITE_CANTOPEN | (4<<8));
const SQLITE_CORRUPT_VTAB : c_int = (SQLITE_CORRUPT | (1<<8));
const SQLITE_READONLY_RECOVERY : c_int = (SQLITE_READONLY | (1<<8));
const SQLITE_READONLY_CANTLOCK : c_int = (SQLITE_READONLY | (2<<8));
const SQLITE_READONLY_ROLLBACK : c_int = (SQLITE_READONLY | (3<<8));
const SQLITE_READONLY_DBMOVED : c_int = (SQLITE_READONLY | (4<<8));
const SQLITE_ABORT_ROLLBACK : c_int = (SQLITE_ABORT | (2<<8));
const SQLITE_CONSTRAINT_CHECK : c_int = (SQLITE_CONSTRAINT | (1<<8));
const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (SQLITE_CONSTRAINT | (2<<8));
const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (SQLITE_CONSTRAINT | (3<<8));
const SQLITE_CONSTRAINT_FUNCTION : c_int = (SQLITE_CONSTRAINT | (4<<8));
const SQLITE_CONSTRAINT_NOTNULL : c_int = (SQLITE_CONSTRAINT | (5<<8));
const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (SQLITE_CONSTRAINT | (6<<8));
const SQLITE_CONSTRAINT_TRIGGER : c_int = (SQLITE_CONSTRAINT | (7<<8));
const SQLITE_CONSTRAINT_UNIQUE : c_int = (SQLITE_CONSTRAINT | (8<<8));
const SQLITE_CONSTRAINT_VTAB : c_int = (SQLITE_CONSTRAINT | (9<<8));
const SQLITE_CONSTRAINT_ROWID : c_int = (SQLITE_CONSTRAINT |(10<<8));
const SQLITE_NOTICE_RECOVER_WAL : c_int = (SQLITE_NOTICE | (1<<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_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8)); const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8));
const SQLITE_AUTH_USER : c_int = (SQLITE_AUTH | (1<<8)); const SQLITE_AUTH_USER : c_int = (super::SQLITE_AUTH | (1<<8));
pub fn code_to_str(code: c_int) -> &'static str { pub fn code_to_str(code: c_int) -> &'static str {
match code { match code {
SQLITE_OK => "Successful result", super::SQLITE_OK => "Successful result",
SQLITE_ERROR => "SQL error or missing database", super::SQLITE_ERROR => "SQL error or missing database",
SQLITE_INTERNAL => "Internal logic error in SQLite", super::SQLITE_INTERNAL => "Internal logic error in SQLite",
SQLITE_PERM => "Access permission denied", super::SQLITE_PERM => "Access permission denied",
SQLITE_ABORT => "Callback routine requested an abort", super::SQLITE_ABORT => "Callback routine requested an abort",
SQLITE_BUSY => "The database file is locked", super::SQLITE_BUSY => "The database file is locked",
SQLITE_LOCKED => "A table in the database is locked", super::SQLITE_LOCKED => "A table in the database is locked",
SQLITE_NOMEM => "A malloc() failed", super::SQLITE_NOMEM => "A malloc() failed",
SQLITE_READONLY => "Attempt to write a readonly database", super::SQLITE_READONLY => "Attempt to write a readonly database",
SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()", super::SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()",
SQLITE_IOERR => "Some kind of disk I/O error occurred", super::SQLITE_IOERR => "Some kind of disk I/O error occurred",
SQLITE_CORRUPT => "The database disk image is malformed", super::SQLITE_CORRUPT => "The database disk image is malformed",
SQLITE_NOTFOUND => "Unknown opcode in sqlite3_file_control()", super::SQLITE_NOTFOUND => "Unknown opcode in sqlite3_file_control()",
SQLITE_FULL => "Insertion failed because database is full", super::SQLITE_FULL => "Insertion failed because database is full",
SQLITE_CANTOPEN => "Unable to open the database file", super::SQLITE_CANTOPEN => "Unable to open the database file",
SQLITE_PROTOCOL => "Database lock protocol error", super::SQLITE_PROTOCOL => "Database lock protocol error",
SQLITE_EMPTY => "Database is empty", super::SQLITE_EMPTY => "Database is empty",
SQLITE_SCHEMA => "The database schema changed", super::SQLITE_SCHEMA => "The database schema changed",
SQLITE_TOOBIG => "String or BLOB exceeds size limit", super::SQLITE_TOOBIG => "String or BLOB exceeds size limit",
SQLITE_CONSTRAINT=> "Abort due to constraint violation", super::SQLITE_CONSTRAINT=> "Abort due to constraint violation",
SQLITE_MISMATCH => "Data type mismatch", super::SQLITE_MISMATCH => "Data type mismatch",
SQLITE_MISUSE => "Library used incorrectly", super::SQLITE_MISUSE => "Library used incorrectly",
SQLITE_NOLFS => "Uses OS features not supported on host", super::SQLITE_NOLFS => "Uses OS features not supported on host",
SQLITE_AUTH => "Authorization denied", super::SQLITE_AUTH => "Authorization denied",
SQLITE_FORMAT => "Auxiliary database format error", super::SQLITE_FORMAT => "Auxiliary database format error",
SQLITE_RANGE => "2nd parameter to sqlite3_bind out of range", super::SQLITE_RANGE => "2nd parameter to sqlite3_bind out of range",
SQLITE_NOTADB => "File opened that is not a database file", super::SQLITE_NOTADB => "File opened that is not a database file",
SQLITE_NOTICE => "Notifications from sqlite3_log()", SQLITE_NOTICE => "Notifications from sqlite3_log()",
SQLITE_WARNING => "Warnings from sqlite3_log()", SQLITE_WARNING => "Warnings from sqlite3_log()",
SQLITE_ROW => "sqlite3_step() has another row ready", super::SQLITE_ROW => "sqlite3_step() has another row ready",
SQLITE_DONE => "sqlite3_step() has finished executing", super::SQLITE_DONE => "sqlite3_step() has finished executing",
SQLITE_IOERR_READ => "Error reading from disk", super::SQLITE_IOERR_READ => "Error reading from disk",
SQLITE_IOERR_SHORT_READ => "Unable to obtain number of requested bytes (file truncated?)", super::SQLITE_IOERR_SHORT_READ => "Unable to obtain number of requested bytes (file truncated?)",
SQLITE_IOERR_WRITE => "Error writing to disk", super::SQLITE_IOERR_WRITE => "Error writing to disk",
SQLITE_IOERR_FSYNC => "Error flushing data to persistent storage (fsync)", super::SQLITE_IOERR_FSYNC => "Error flushing data to persistent storage (fsync)",
SQLITE_IOERR_DIR_FSYNC => "Error calling fsync on a directory", super::SQLITE_IOERR_DIR_FSYNC => "Error calling fsync on a directory",
SQLITE_IOERR_TRUNCATE => "Error attempting to truncate file", super::SQLITE_IOERR_TRUNCATE => "Error attempting to truncate file",
SQLITE_IOERR_FSTAT => "Error invoking fstat to get file metadata", super::SQLITE_IOERR_FSTAT => "Error invoking fstat to get file metadata",
SQLITE_IOERR_UNLOCK => "I/O error within xUnlock of a VFS object", super::SQLITE_IOERR_UNLOCK => "I/O error within xUnlock of a VFS object",
SQLITE_IOERR_RDLOCK => "I/O error within xLock of a VFS object (trying to obtain a read lock)", super::SQLITE_IOERR_RDLOCK => "I/O error within xLock of a VFS object (trying to obtain a read lock)",
SQLITE_IOERR_DELETE => "I/O error within xDelete of a VFS object", super::SQLITE_IOERR_DELETE => "I/O error within xDelete of a VFS object",
SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED", // no longer used super::SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED", // no longer used
SQLITE_IOERR_NOMEM => "Out of memory in I/O layer", super::SQLITE_IOERR_NOMEM => "Out of memory in I/O layer",
SQLITE_IOERR_ACCESS => "I/O error within xAccess of a VFS object", super::SQLITE_IOERR_ACCESS => "I/O error within xAccess of a VFS object",
SQLITE_IOERR_CHECKRESERVEDLOCK => "I/O error within then xCheckReservedLock method", super::SQLITE_IOERR_CHECKRESERVEDLOCK => "I/O error within then xCheckReservedLock method",
SQLITE_IOERR_LOCK => "I/O error in the advisory file locking layer", super::SQLITE_IOERR_LOCK => "I/O error in the advisory file locking layer",
SQLITE_IOERR_CLOSE => "I/O error within the xClose method", super::SQLITE_IOERR_CLOSE => "I/O error within the xClose method",
SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE", // no longer used super::SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE", // no longer used
SQLITE_IOERR_SHMOPEN => "I/O error within the xShmMap method (trying to open a new shared-memory segment)", SQLITE_IOERR_SHMOPEN => "I/O error within the xShmMap method (trying to open a new shared-memory segment)",
SQLITE_IOERR_SHMSIZE => "I/O error within the xShmMap method (trying to resize an existing shared-memory segment)", SQLITE_IOERR_SHMSIZE => "I/O error within the xShmMap method (trying to resize an existing shared-memory segment)",
SQLITE_IOERR_SHMLOCK => "SQLITE_IOERR_SHMLOCK", // no longer used SQLITE_IOERR_SHMLOCK => "SQLITE_IOERR_SHMLOCK", // no longer used

View File

@ -19,26 +19,26 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
#[repr(C)] #[repr(C)]
pub enum Limit { pub enum Limit {
/// The maximum size of any string or BLOB or table row, in bytes. /// The maximum size of any string or BLOB or table row, in bytes.
SQLITE_LIMIT_LENGTH = 0, SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize,
/// The maximum length of an SQL statement, in bytes. /// The maximum length of an SQL statement, in bytes.
SQLITE_LIMIT_SQL_LENGTH = 1, SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize,
/// The maximum number of columns in a table definition or in the result set of a SELECT /// The maximum number of columns in a table definition or in the result set of a SELECT
/// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause. /// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause.
SQLITE_LIMIT_COLUMN = 2, SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize,
/// The maximum depth of the parse tree on any expression. /// The maximum depth of the parse tree on any expression.
SQLITE_LIMIT_EXPR_DEPTH = 3, SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize,
/// The maximum number of terms in a compound SELECT statement. /// The maximum number of terms in a compound SELECT statement.
SQLITE_LIMIT_COMPOUND_SELECT = 4, SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize,
/// The maximum number of instructions in a virtual machine program used to implement an SQL statement. /// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
SQLITE_LIMIT_VDBE_OP = 5, SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
/// The maximum number of arguments on a function. /// The maximum number of arguments on a function.
SQLITE_LIMIT_FUNCTION_ARG = 6, SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize,
/// The maximum number of attached databases. /// The maximum number of attached databases.
SQLITE_LIMIT_ATTACHED = 7, SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize,
/// The maximum length of the pattern argument to the LIKE or GLOB operators. /// The maximum length of the pattern argument to the LIKE or GLOB operators.
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8, SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize,
/// The maximum index number of any parameter in an SQL statement. /// The maximum index number of any parameter in an SQL statement.
SQLITE_LIMIT_VARIABLE_NUMBER = 9, SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
/// The maximum depth of recursion for triggers. /// The maximum depth of recursion for triggers.
SQLITE_LIMIT_TRIGGER_DEPTH = 10, SQLITE_LIMIT_TRIGGER_DEPTH = 10,
/// The maximum number of auxiliary worker threads that a single prepared statement may start. /// The maximum number of auxiliary worker threads that a single prepared statement may start.

View File

@ -92,7 +92,8 @@ impl Connection {
if read_only { 0 } else { 1 }, if read_only { 0 } else { 1 },
&mut blob) &mut blob)
}; };
c.decode_result(rc).map(|_| { c.decode_result(rc)
.map(|_| {
Blob { Blob {
conn: self, conn: self,
blob: blob, blob: blob,
@ -266,7 +267,8 @@ mod test {
fn test_blob() { fn test_blob() {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(4, blob.write(b"Clob").unwrap()); assert_eq!(4, blob.write(b"Clob").unwrap());
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10 assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10 assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
@ -274,7 +276,8 @@ mod test {
blob.reopen(rowid).unwrap(); blob.reopen(rowid).unwrap();
blob.close().unwrap(); blob.close().unwrap();
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true).unwrap(); blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
.unwrap();
let mut bytes = [0u8; 5]; let mut bytes = [0u8; 5];
assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"Clob5"); assert_eq!(&bytes, b"Clob5");
@ -314,7 +317,8 @@ mod test {
fn test_blob_in_bufreader() { fn test_blob_in_bufreader() {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap()); assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
blob.reopen(rowid).unwrap(); blob.reopen(rowid).unwrap();
@ -338,7 +342,8 @@ mod test {
let (db, rowid) = db_with_test_blob().unwrap(); let (db, rowid) = db_with_test_blob().unwrap();
{ {
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob); let mut writer = BufWriter::new(blob);
// trying to write too much and then flush should fail // trying to write too much and then flush should fail
@ -357,7 +362,8 @@ mod test {
} }
{ {
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap(); let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob); let mut writer = BufWriter::new(blob);
// trying to write_all too much should fail // trying to write_all too much should fail

View File

@ -280,7 +280,8 @@ mod test {
#[test] #[test]
fn test_connection_close() { fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap(); 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"); conn.close().expect("connection not closed");
} }

View File

@ -145,18 +145,14 @@ impl error::Error for Error {
match *self { match *self {
Error::SqliteFailure(ref err, None) => err.description(), Error::SqliteFailure(ref err, None) => err.description(),
Error::SqliteFailure(_, Some(ref s)) => s, Error::SqliteFailure(_, Some(ref s)) => s,
Error::SqliteSingleThreadedMode => { Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
"SQLite was compiled or configured for single-threaded use only"
}
Error::FromSqlConversionFailure(_, _, ref err) => err.description(), Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type", Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(), Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name", Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(), Error::NulError(ref err) => err.description(),
Error::InvalidPath(_) => "invalid path", Error::InvalidPath(_) => "invalid path",
Error::ExecuteReturnedResults => { Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
"execute returned results - did you mean to call query?"
}
Error::QueryReturnedNoRows => "query returned no rows", Error::QueryReturnedNoRows => "query returned no rows",
Error::InvalidColumnIndex(_) => "invalid column index", Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name", Error::InvalidColumnName(_) => "invalid column name",

View File

@ -119,9 +119,13 @@ pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint // if we're on the bundled version (since it's at least 3.17.0) and the normal constraint
// error code if not. // error code if not.
#[cfg(feature = "bundled")] #[cfg(feature = "bundled")]
fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT_FUNCTION } fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT_FUNCTION
}
#[cfg(not(feature = "bundled"))] #[cfg(not(feature = "bundled"))]
fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT } fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT
}
match *err { match *err {
Error::SqliteFailure(ref err, ref s) => { Error::SqliteFailure(ref err, ref s) => {
@ -154,19 +158,24 @@ impl<'a> ValueRef<'a> {
let s = CStr::from_ptr(text as *const c_char); let s = CStr::from_ptr(text as *const c_char);
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_value_text returned invalid UTF-8"); let s = s.to_str()
.expect("sqlite3_value_text returned invalid UTF-8");
ValueRef::Text(s) ValueRef::Text(s)
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {
let blob = ffi::sqlite3_value_blob(value); let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value));
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB value type with NULL data");
let len = ffi::sqlite3_value_bytes(value);
assert!(len >= 0, assert!(len >= 0,
"unexpected negative return from sqlite3_value_bytes"); "unexpected negative return from sqlite3_value_bytes");
if len > 0 {
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB value type with NULL data");
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize)) 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
// is a NULL pointer.
ValueRef::Blob(&[])
}
} }
_ => unreachable!("sqlite3_value_type returned invalid value"), _ => unreachable!("sqlite3_value_type returned invalid value"),
} }
@ -207,7 +216,10 @@ impl<'a> Context<'a> {
FromSqlError::InvalidType => { FromSqlError::InvalidType => {
Error::InvalidFunctionParameterType(idx, value.data_type()) Error::InvalidFunctionParameterType(idx, value.data_type())
} }
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i), FromSqlError::OutOfRange(i) => {
Error::IntegralValueOutOfRange(idx as c_int,
i)
}
FromSqlError::Other(err) => { FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err) Error::FromSqlConversionFailure(idx, value.data_type(), err)
} }
@ -304,7 +316,9 @@ impl Connection {
where F: FnMut(&Context) -> Result<T>, where F: FnMut(&Context) -> Result<T>,
T: ToSql T: ToSql
{ {
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) self.db
.borrow_mut()
.create_scalar_function(fn_name, n_arg, deterministic, x_func)
} }
/// Attach a user-defined aggregate function to this database connection. /// Attach a user-defined aggregate function to this database connection.
@ -720,17 +734,16 @@ mod test {
#[test] #[test]
fn test_sum() { fn test_sum() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.create_aggregate_function("my_sum", 1, true, Sum).unwrap(); db.create_aggregate_function("my_sum", 1, true, Sum)
.unwrap();
// sum should return NULL when given no columns (contrast with count below) // sum should return NULL when given no columns (contrast with count below)
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)) let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)).unwrap();
.unwrap();
assert!(result.is_none()); assert!(result.is_none());
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)) let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
.unwrap();
assert_eq!(4, result); assert_eq!(4, result);
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \ let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
@ -743,7 +756,8 @@ mod test {
#[test] #[test]
fn test_count() { fn test_count() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.create_aggregate_function("my_count", -1, true, Count).unwrap(); db.create_aggregate_function("my_count", -1, true, Count)
.unwrap();
// count should return 0 when given no columns (contrast with sum above) // count should return 0 when given no columns (contrast with sum above)
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)"; let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
@ -751,8 +765,7 @@ mod test {
assert_eq!(result, 0); assert_eq!(result, 0);
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)) let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
.unwrap();
assert_eq!(2, result); assert_eq!(2, result);
} }
} }

View File

@ -297,7 +297,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<c_int> { pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<c_int> {
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). /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
@ -319,7 +320,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails. /// underlying SQLite call fails.
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> { pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> {
self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) self.prepare(sql)
.and_then(|mut stmt| stmt.execute_named(params))
} }
/// Get the SQLite rowid of the most recent successful INSERT. /// Get the SQLite rowid of the most recent successful INSERT.
@ -408,7 +410,9 @@ impl Connection {
let mut stmt = try!(self.prepare(sql)); let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params)); 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. /// Convenience method to execute a query that is expected to return a single row.
@ -536,7 +540,9 @@ impl Connection {
dylib_path: P, dylib_path: P,
entry_point: Option<&str>) entry_point: Option<&str>)
-> Result<()> { -> Result<()> {
self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) self.db
.borrow_mut()
.load_extension(dylib_path.as_ref(), entry_point)
} }
/// Get access to the underlying SQLite database connection handle. /// Get access to the underlying SQLite database connection handle.
@ -580,16 +586,16 @@ bitflags! {
#[doc = "Flags for opening SQLite database connections."] #[doc = "Flags for opening SQLite database connections."]
#[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."] #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
#[repr(C)] #[repr(C)]
pub flags OpenFlags: ::std::os::raw::c_int { pub struct OpenFlags: ::std::os::raw::c_int {
const SQLITE_OPEN_READ_ONLY = 0x00000001, const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY;
const SQLITE_OPEN_READ_WRITE = 0x00000002, const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE;
const SQLITE_OPEN_CREATE = 0x00000004, const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
const SQLITE_OPEN_URI = 0x00000040, const SQLITE_OPEN_URI = 0x00000040;
const SQLITE_OPEN_MEMORY = 0x00000080, const SQLITE_OPEN_MEMORY = 0x00000080;
const SQLITE_OPEN_NO_MUTEX = 0x00008000, const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX;
const SQLITE_OPEN_FULL_MUTEX = 0x00010000, const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX;
const SQLITE_OPEN_SHARED_CACHE = 0x00020000, const SQLITE_OPEN_SHARED_CACHE = 0x00020000;
const SQLITE_OPEN_PRIVATE_CACHE = 0x00040000, const SQLITE_OPEN_PRIVATE_CACHE = 0x00040000;
} }
} }
@ -851,7 +857,8 @@ impl InnerConnection {
&mut c_stmt, &mut c_stmt,
ptr::null_mut()) ptr::null_mut())
}; };
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) -> c_int { fn changes(&mut self) -> c_int {
@ -1001,12 +1008,15 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(1, assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap()); db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
.unwrap());
assert_eq!(1, assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap()); db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
.unwrap());
assert_eq!(3i32, assert_eq!(3i32,
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
} }
#[test] #[test]
@ -1063,7 +1073,8 @@ mod test {
assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1); assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[&3i32]).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").unwrap(); let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
.unwrap();
{ {
let mut rows = query.query(&[&4i32]).unwrap(); let mut rows = query.query(&[&4i32]).unwrap();
let mut v = Vec::<i32>::new(); let mut v = Vec::<i32>::new();
@ -1148,8 +1159,10 @@ mod test {
#[test] #[test]
fn test_last_insert_rowid() { fn test_last_insert_rowid() {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap(); .unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES")
.unwrap();
assert_eq!(db.last_insert_rowid(), 1); assert_eq!(db.last_insert_rowid(), 1);

View File

@ -25,7 +25,8 @@ impl<'conn> LoadExtensionGuard<'conn> {
/// Attempt to enable loading extensions. Loading extensions will be disabled when this /// Attempt to enable loading extensions. Loading extensions will be disabled when this
/// guard goes out of scope. Cannot be meaningfully nested. /// guard goes out of scope. Cannot be meaningfully nested.
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> { pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> {
conn.load_extension_enable().map(|_| LoadExtensionGuard { conn: conn }) conn.load_extension_enable()
.map(|_| LoadExtensionGuard { conn: conn })
} }
} }

View File

@ -28,7 +28,8 @@ impl<'stmt> Rows<'stmt> {
/// "streaming iterator". For a more natural interface, consider using `query_map` /// "streaming iterator". For a more natural interface, consider using `query_map`
/// or `query_and_then` instead, which return types that implement `Iterator`. /// or `query_and_then` instead, which return types that implement `Iterator`.
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> { pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt.and_then(|stmt| match stmt.step() { self.stmt
.and_then(|stmt| match stmt.step() {
Ok(true) => { Ok(true) => {
Some(Ok(Row { Some(Ok(Row {
stmt: stmt, stmt: stmt,
@ -89,10 +90,7 @@ impl<'stmt, T, F> MappedRowsCrateImpl<'stmt, T, F> for MappedRows<'stmt, F>
where F: FnMut(&Row) -> T where F: FnMut(&Row) -> T
{ {
fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> { fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
MappedRows { MappedRows { rows: rows, map: f }
rows: rows,
map: f,
}
} }
} }
@ -103,7 +101,9 @@ impl<'conn, T, F> Iterator for MappedRows<'conn, F>
fn next(&mut self) -> Option<Result<T>> { fn next(&mut self) -> Option<Result<T>> {
let map = &mut self.map; let map = &mut self.map;
self.rows.next().map(|row_result| row_result.map(|row| (map)(&row))) self.rows
.next()
.map(|row_result| row_result.map(|row| (map)(&row)))
} }
} }
@ -124,10 +124,7 @@ impl<'stmt, T, E, F> AndThenRowsCrateImpl<'stmt, T, E, F> for AndThenRows<'stmt,
where F: FnMut(&Row) -> result::Result<T, E> where F: FnMut(&Row) -> result::Result<T, E>
{ {
fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> { fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
AndThenRows { AndThenRows { rows: rows, map: f }
rows: rows,
map: f,
}
} }
} }
@ -139,10 +136,9 @@ impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let map = &mut self.map; let map = &mut self.map;
self.rows.next().map(|row_result| { self.rows
row_result.map_err(E::from) .next()
.and_then(|row| (map)(&row)) .map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
})
} }
} }
@ -182,8 +178,13 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
let value = self.stmt.value_ref(idx); let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()), FromSqlError::InvalidType => {
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), Error::InvalidColumnType(idx,
value.data_type())
}
FromSqlError::OutOfRange(i) => {
Error::IntegralValueOutOfRange(idx, i)
}
FromSqlError::Other(err) => { FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err) Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
} }

View File

@ -405,10 +405,15 @@ impl<'conn> Statement<'conn> {
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) }); .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
} }
}; };
self.conn.decode_result(match value { self.conn
.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) }, ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) }, ValueRef::Integer(i) => unsafe {
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) }, ffi::sqlite3_bind_int64(ptr, col, i)
},
ValueRef::Real(r) => unsafe {
ffi::sqlite3_bind_double(ptr, col, r)
},
ValueRef::Text(s) => unsafe { ValueRef::Text(s) => unsafe {
let length = s.len(); let length = s.len();
if length > ::std::i32::MAX as usize { if length > ::std::i32::MAX as usize {
@ -524,7 +529,8 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
}; };
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine. // sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_column_text returned invalid UTF-8"); let s = s.to_str()
.expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s) ValueRef::Text(s)
} }
ffi::SQLITE_BLOB => { ffi::SQLITE_BLOB => {
@ -570,9 +576,11 @@ mod test {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(), assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
.unwrap(),
1); 1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(), assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1); 1);
assert_eq!(3i32, assert_eq!(3i32,
@ -589,7 +597,8 @@ mod test {
INTEGER)"; INTEGER)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
.unwrap();
stmt.execute_named(&[(":name", &"one")]).unwrap(); stmt.execute_named(&[(":name", &"one")]).unwrap();
assert_eq!(1i32, assert_eq!(1i32,
@ -608,7 +617,8 @@ mod test {
"#; "#;
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); let mut stmt = db.prepare("SELECT id FROM test where name = :name")
.unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap(); let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
let id: i32 = rows.next().unwrap().unwrap().get(0); let id: i32 = rows.next().unwrap().unwrap().get(0);
@ -624,7 +634,8 @@ mod test {
"#; "#;
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap(); 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); let id: i32 = row.get(0);
2 * id 2 * id
@ -676,7 +687,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)"; let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> = let result: Option<String> =
@ -691,7 +703,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)"; let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap(); stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap(); stmt.execute_named(&[(":y", &"two")]).unwrap();
@ -704,15 +717,18 @@ mod test {
#[test] #[test]
fn test_insert() { fn test_insert() {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap(); db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap(); .unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
.unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1); assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2); assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() { match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (), Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap(); let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
.unwrap();
match multi.insert(&[]).unwrap_err() { match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (), Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
@ -729,9 +745,15 @@ mod test {
") ")
.unwrap(); .unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(), assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1); 1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(), assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1); 1);
} }

View File

@ -100,7 +100,8 @@ impl<'conn> Transaction<'conn> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE", TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
}; };
conn.execute_batch(query).map(move |_| { conn.execute_batch(query)
.map(move |_| {
Transaction { Transaction {
conn: conn, conn: conn,
drop_behavior: DropBehavior::Rollback, drop_behavior: DropBehavior::Rollback,
@ -216,7 +217,8 @@ impl<'conn> Savepoint<'conn> {
name: T) name: T)
-> Result<Savepoint> { -> Result<Savepoint> {
let name = name.into(); let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {}", name)).map(|_| { conn.execute_batch(&format!("SAVEPOINT {}", name))
.map(|_| {
Savepoint { Savepoint {
conn: conn, conn: conn,
name: name, name: name,
@ -269,7 +271,8 @@ impl<'conn> Savepoint<'conn> {
fn commit_(&mut self) -> Result<()> { fn commit_(&mut self) -> Result<()> {
self.committed = true; self.committed = true;
self.conn.execute_batch(&format!("RELEASE {}", self.name)) self.conn
.execute_batch(&format!("RELEASE {}", self.name))
} }
/// A convenience method which rolls back a savepoint. /// A convenience method which rolls back a savepoint.
@ -279,7 +282,8 @@ impl<'conn> Savepoint<'conn> {
/// Unlike `Transaction`s, savepoints remain active after they have been rolled back, /// Unlike `Transaction`s, savepoints remain active after they have been rolled back,
/// and can be rolled back again or committed. /// and can be rolled back again or committed.
pub fn rollback(&mut self) -> Result<()> { pub fn rollback(&mut self) -> Result<()> {
self.conn.execute_batch(&format!("ROLLBACK TO {}", self.name)) self.conn
.execute_batch(&format!("ROLLBACK TO {}", self.name))
} }
/// Consumes the savepoint, committing or rolling back according to the current setting /// Consumes the savepoint, committing or rolling back according to the current setting
@ -549,7 +553,8 @@ mod test {
} }
fn assert_current_sum(x: i32, conn: &Connection) { fn assert_current_sum(x: i32, conn: &Connection) {
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap(); let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(x, i); assert_eq!(x, i);
} }
} }

View File

@ -19,7 +19,9 @@ impl ToSql for NaiveDate {
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone. /// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
impl FromSql for NaiveDate { impl FromSql for NaiveDate {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") { value
.as_str()
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
Ok(dt) => Ok(dt), Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
}) })
@ -37,7 +39,9 @@ impl ToSql for NaiveTime {
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone. /// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
impl FromSql for NaiveTime { impl FromSql for NaiveTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value
.as_str()
.and_then(|s| {
let fmt = match s.len() { let fmt = match s.len() {
5 => "%H:%M", 5 => "%H:%M",
8 => "%H:%M:%S", 8 => "%H:%M:%S",
@ -63,7 +67,9 @@ impl ToSql for NaiveDateTime {
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported) /// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
impl FromSql for NaiveDateTime { impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| { value
.as_str()
.and_then(|s| {
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%dT%H:%M:%S%.f" "%Y-%m-%dT%H:%M:%S%.f"
} else { } else {
@ -130,7 +136,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)")
.unwrap();
db db
} }
@ -138,11 +145,14 @@ mod test {
fn test_naive_date() { fn test_naive_date() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let date = NaiveDate::from_ymd(2016, 2, 23); let date = NaiveDate::from_ymd(2016, 2, 23);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23", s); assert_eq!("2016-02-23", s);
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(date, t); assert_eq!(date, t);
} }
@ -150,11 +160,14 @@ mod test {
fn test_naive_time() { fn test_naive_time() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms(23, 56, 4);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("23:56:04", s); assert_eq!("23:56:04", s);
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(time, v); assert_eq!(time, v);
} }
@ -165,15 +178,20 @@ mod test {
let time = NaiveTime::from_hms(23, 56, 4); let time = NaiveTime::from_hms(23, 56, 4);
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23T23:56:04", s); assert_eq!("2016-02-23T23:56:04", s);
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, v); assert_eq!(dt, v);
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS" db.execute("UPDATE foo set b = datetime(t)", &[])
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); .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); assert_eq!(dt, hms);
} }
@ -185,12 +203,15 @@ mod test {
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let utc = UTC.from_utc_datetime(&dt); let utc = UTC.from_utc_datetime(&dt);
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); 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); 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)).unwrap(); let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v1); 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))
@ -202,7 +223,8 @@ mod test {
assert_eq!(utc - Duration::milliseconds(789), v3); assert_eq!(utc - Duration::milliseconds(789), v3);
let v4: DateTime<UTC> = let v4: DateTime<UTC> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap(); db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v4); assert_eq!(utc, v4);
} }
@ -214,13 +236,16 @@ mod test {
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let local = Local.from_local_datetime(&dt).single().unwrap(); let local = Local.from_local_datetime(&dt).single().unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", &[&local]).unwrap(); db.execute("INSERT INTO foo (t) VALUES (?)", &[&local])
.unwrap();
// Stored string should be in UTC // Stored string should be in UTC
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert!(s.ends_with("+00:00")); assert!(s.ends_with("+00:00"));
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(local, v); assert_eq!(local, v);
} }
} }

View File

@ -164,7 +164,9 @@ mod test {
} }
for n in in_range { for n in in_range {
assert_eq!(*n, assert_eq!(*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)).unwrap().into()); db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
.unwrap()
.into());
} }
} }

View File

@ -119,7 +119,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)").unwrap(); db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)")
.unwrap();
db db
} }
@ -128,9 +129,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let v1234 = vec![1u8, 2, 3, 4]; let v1234 = vec![1u8, 2, 3, 4];
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234]).unwrap(); db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, v1234); assert_eq!(v, v1234);
} }
@ -139,9 +142,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let empty = vec![]; let empty = vec![];
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty]).unwrap(); db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, empty); assert_eq!(v, empty);
} }
@ -150,9 +155,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!"; 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)).unwrap(); let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s); assert_eq!(from, s);
} }
@ -161,9 +168,11 @@ mod test {
let db = checked_memory_handle(); let db = checked_memory_handle();
let s = "hello, world!"; let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap(); 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)).unwrap(); let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s); assert_eq!(from, s);
} }
@ -171,10 +180,12 @@ mod test {
fn test_value() { fn test_value() {
let db = checked_memory_handle(); let db = checked_memory_handle();
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap(); db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
.unwrap();
assert_eq!(10i64, assert_eq!(10i64,
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
.unwrap());
} }
#[test] #[test]
@ -184,10 +195,13 @@ mod test {
let s = Some("hello, world!"); let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]); let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap(); db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap(); .unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
.unwrap();
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap(); let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
.unwrap();
let mut rows = stmt.query(&[]).unwrap(); let mut rows = stmt.query(&[]).unwrap();
{ {
@ -232,9 +246,15 @@ mod test {
assert_eq!("text", row.get_checked::<i32, String>(1).unwrap()); assert_eq!("text", row.get_checked::<i32, String>(1).unwrap());
assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap()); assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap());
assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON); assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON);
assert!(row.get_checked::<i32, Option<c_int>>(4).unwrap().is_none()); assert!(row.get_checked::<i32, Option<c_int>>(4)
assert!(row.get_checked::<i32, Option<c_double>>(4).unwrap().is_none()); .unwrap()
assert!(row.get_checked::<i32, Option<String>>(4).unwrap().is_none()); .is_none());
assert!(row.get_checked::<i32, Option<c_double>>(4)
.unwrap()
.is_none());
assert!(row.get_checked::<i32, Option<String>>(4)
.unwrap()
.is_none());
// check some invalid types // check some invalid types

View File

@ -32,7 +32,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)")
.unwrap();
db db
} }
@ -46,9 +47,11 @@ mod test {
&[&data, &json.as_bytes()]) &[&data, &json.as_bytes()])
.unwrap(); .unwrap();
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, t); assert_eq!(data, t);
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, b); assert_eq!(data, b);
} }
} }

View File

@ -7,14 +7,19 @@ const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
impl ToSql for time::Timespec { impl ToSql for time::Timespec {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
let time_string = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string(); let time_string = time::at_utc(*self)
.strftime(SQLITE_DATETIME_FMT)
.unwrap()
.to_string();
Ok(ToSqlOutput::from(time_string)) Ok(ToSqlOutput::from(time_string))
} }
} }
impl FromSql for time::Timespec { impl FromSql for time::Timespec {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) { value
.as_str()
.and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
Ok(tm) => Ok(tm.to_timespec()), Ok(tm) => Ok(tm.to_timespec()),
Err(err) => Err(FromSqlError::Other(Box::new(err))), Err(err) => Err(FromSqlError::Other(Box::new(err))),
}) })
@ -28,7 +33,8 @@ mod test {
fn checked_memory_handle() -> Connection { fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap(); let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)").unwrap(); db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
.unwrap();
db db
} }
@ -40,9 +46,11 @@ mod test {
sec: 10_000, sec: 10_000,
nsec: 0, nsec: 0,
}; };
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)).unwrap(); let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, ts); assert_eq!(from, ts);
} }
} }

View File

@ -94,12 +94,24 @@ impl ToSql for String {
} }
} }
impl ToSql for str {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Vec<u8> { impl ToSql for Vec<u8> {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self.as_slice())) Ok(ToSqlOutput::from(self.as_slice()))
} }
} }
impl ToSql for [u8] {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Value { impl ToSql for Value {
fn to_sql(&self) -> Result<ToSqlOutput> { fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self)) Ok(ToSqlOutput::from(self))

View File

@ -13,5 +13,6 @@ pub fn version_number() -> i32 {
/// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html). /// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html).
pub fn version() -> &'static str { pub fn version() -> &'static str {
let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) }; let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
cstr.to_str().expect("SQLite version string is not valid UTF8 ?!") cstr.to_str()
.expect("SQLite version string is not valid UTF8 ?!")
} }

View File

@ -56,13 +56,13 @@ pub trait VTab<C: VTabCursor<Self>>: Sized {
bitflags! { bitflags! {
#[doc = "Index constraint operator."] #[doc = "Index constraint operator."]
#[repr(C)] #[repr(C)]
pub flags IndexConstraintOp: ::std::os::raw::c_uchar { pub struct IndexConstraintOp: ::std::os::raw::c_uchar {
const SQLITE_INDEX_CONSTRAINT_EQ = 2, const SQLITE_INDEX_CONSTRAINT_EQ = 2;
const SQLITE_INDEX_CONSTRAINT_GT = 4, const SQLITE_INDEX_CONSTRAINT_GT = 4;
const SQLITE_INDEX_CONSTRAINT_LE = 8, const SQLITE_INDEX_CONSTRAINT_LE = 8;
const SQLITE_INDEX_CONSTRAINT_LT = 16, const SQLITE_INDEX_CONSTRAINT_LT = 16;
const SQLITE_INDEX_CONSTRAINT_GE = 32, const SQLITE_INDEX_CONSTRAINT_GE = 32;
const SQLITE_INDEX_CONSTRAINT_MATCH = 64, const SQLITE_INDEX_CONSTRAINT_MATCH = 64;
} }
} }
@ -96,7 +96,7 @@ impl IndexInfo {
/// if `argv_index` > 0, constraint is part of argv to xFilter /// if `argv_index` > 0, constraint is part of argv to xFilter
pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage { pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage {
let mut constraint_usages = unsafe { let constraint_usages = unsafe {
slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize) slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
}; };
IndexConstraintUsage(&mut constraint_usages[constraint_idx]) IndexConstraintUsage(&mut constraint_usages[constraint_idx])

View File

@ -37,17 +37,17 @@ const SERIES_COLUMN_STEP: c_int = 3;
bitflags! { bitflags! {
#[repr(C)] #[repr(C)]
flags QueryPlanFlags: ::std::os::raw::c_int { struct QueryPlanFlags: ::std::os::raw::c_int {
// start = $value -- constraint exists // start = $value -- constraint exists
const START = 1, const START = 1;
// stop = $value -- constraint exists // stop = $value -- constraint exists
const STOP = 2, const STOP = 2;
// step = $value -- constraint exists // step = $value -- constraint exists
const STEP = 4, const STEP = 4;
// output in descending order // output in descending order
const DESC = 8, const DESC = 8;
// Both start and stop // Both start and stop
const BOTH = START.bits | STOP.bits, const BOTH = START.bits | STOP.bits;
} }
} }