mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-26 19:41:37 +08:00
Merge remote-tracking branch 'jgallagher/master' into hooks
This commit is contained in:
commit
96103b0662
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
libsqlite3-sys/sqlite3/* linguist-vendored
|
@ -1,4 +1,5 @@
|
|||||||
sudo: false
|
sudo: false
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
language: rust
|
language: rust
|
||||||
|
|
||||||
@ -13,12 +14,10 @@ matrix:
|
|||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
|
||||||
- llvm-toolchain-precise-3.9
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages: # recommanded versions for rust-bindgen
|
packages: # recommanded versions for rust-bindgen
|
||||||
- llvm-3.9-dev
|
- llvm-3.9-dev
|
||||||
- libclang-3.9-dev
|
- libclang-3.9-dev
|
||||||
|
- libsqlcipher-dev
|
||||||
|
|
||||||
env: # specify the clang path for rust-bindgen
|
env: # specify the clang path for rust-bindgen
|
||||||
- LIBCLANG_PATH=/usr/lib/llvm-3.9/lib
|
- LIBCLANG_PATH=/usr/lib/llvm-3.9/lib
|
||||||
@ -26,6 +25,7 @@ env: # specify the clang path for rust-bindgen
|
|||||||
script:
|
script:
|
||||||
- cargo build
|
- cargo build
|
||||||
- cargo build --features bundled
|
- cargo build --features bundled
|
||||||
|
- cargo build --features sqlcipher
|
||||||
- cargo test
|
- cargo test
|
||||||
- cargo test --features backup
|
- cargo test --features backup
|
||||||
- cargo test --features blob
|
- cargo test --features blob
|
||||||
@ -37,6 +37,7 @@ script:
|
|||||||
- cargo test --features chrono
|
- cargo test --features chrono
|
||||||
- cargo test --features serde_json
|
- cargo test --features serde_json
|
||||||
- cargo test --features bundled
|
- cargo test --features bundled
|
||||||
|
- cargo test --features sqlcipher
|
||||||
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace"
|
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace"
|
||||||
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
|
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
|
||||||
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled"
|
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled"
|
||||||
|
@ -18,3 +18,17 @@ 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)
|
||||||
|
* [Kelvin Ly](https://github.com/cactorium)
|
||||||
|
* [king6cong](https://github.com/king6cong)
|
||||||
|
* [Andy Russell](https://github.com/euclio)
|
||||||
|
* [Tristan Lostroh](https://github.com/tl8roy)
|
||||||
|
* [Ossi Herrala](https://github.com/oherrala)
|
||||||
|
* [Vojtech Cima](https://github.com/vojtechcima)
|
||||||
|
* [Louis Garczynski](https://github.com/lgarczyn)
|
||||||
|
* [twistedfall](https://github.com/twistedfall)
|
||||||
|
* [Nick Fitzgerald](https://github.com/fitzgen)
|
||||||
|
* [Lorenzo Villani](https://github.com/lvillani)
|
||||||
|
23
Cargo.toml
23
Cargo.toml
@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.11.0"
|
version = "0.13.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"
|
||||||
documentation = "http://jgallagher.github.io/rusqlite/rusqlite/index.html"
|
documentation = "http://docs.rs/rusqlite/"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["sqlite", "database", "ffi"]
|
keywords = ["sqlite", "database", "ffi"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -27,22 +27,23 @@ bundled = ["libsqlite3-sys/bundled"]
|
|||||||
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
|
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
|
||||||
limits = []
|
limits = []
|
||||||
hooks = []
|
hooks = []
|
||||||
|
sqlcipher = ["libsqlite3-sys/sqlcipher"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1.0"
|
time = "0.1.0"
|
||||||
bitflags = "0.7"
|
bitflags = "1.0"
|
||||||
lru-cache = "0.1.0"
|
lru-cache = "0.1"
|
||||||
chrono = { version = "0.3", optional = true }
|
chrono = { version = "0.4", optional = true }
|
||||||
serde_json = { version = "0.9", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempdir = "0.3.4"
|
tempdir = "0.3"
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
regex = "0.2"
|
regex = "0.2"
|
||||||
|
|
||||||
[dependencies.libsqlite3-sys]
|
[dependencies.libsqlite3-sys]
|
||||||
path = "libsqlite3-sys"
|
path = "libsqlite3-sys"
|
||||||
version = "0.8"
|
version = "0.9"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "config_log"
|
name = "config_log"
|
||||||
@ -50,3 +51,9 @@ harness = false
|
|||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "deny_single_threaded_sqlite_config"
|
name = "deny_single_threaded_sqlite_config"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ]
|
||||||
|
all-features = false
|
||||||
|
no-default-features = true
|
||||||
|
default-target = "x86_64-unknown-linux-gnu"
|
||||||
|
17
Changelog.md
17
Changelog.md
@ -1,3 +1,20 @@
|
|||||||
|
# Version 0.13.0 (2017-11-13)
|
||||||
|
|
||||||
|
* Added ToSqlConversionFailure case to Error enum.
|
||||||
|
* Now depends on chrono 0.4, bitflats 1.0, and (optionally) cc 1.0 / bindgen 0.31.
|
||||||
|
* The ToSql/FromSql implementations for time::Timespec now include
|
||||||
|
and expect fractional seconds and timezone in the serialized string.
|
||||||
|
* The RowIndex type used in Row::get is now publicly exported.
|
||||||
|
* New `sqlcipher` feature allows linking against SQLCipher instead of SQLite.
|
||||||
|
* Doc link in README now point to docs.rs.
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
21
README.md
21
README.md
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose
|
Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose
|
||||||
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full
|
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full
|
||||||
[API documentation](http://jgallagher.github.io/rusqlite/rusqlite/index.html).
|
[API documentation](http://docs.rs/rusqlite/).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
extern crate rusqlite;
|
extern crate rusqlite;
|
||||||
@ -90,21 +90,32 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
|
|||||||
and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the
|
and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the
|
||||||
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
||||||
* `bundled` uses a bundled version of sqlite3. This is a good option for cases where linking to sqlite3 is complicated, such as Windows.
|
* `bundled` uses a bundled version of sqlite3. This is a good option for cases where linking to sqlite3 is complicated, such as Windows.
|
||||||
|
* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature is mutually exclusive with `bundled`.
|
||||||
|
|
||||||
## 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
|
||||||
|
|
||||||
|
24
appveyor.yml
24
appveyor.yml
@ -1,6 +1,13 @@
|
|||||||
environment:
|
environment:
|
||||||
TARGET: 1.15.0-x86_64-pc-windows-gnu
|
matrix:
|
||||||
|
- TARGET: 1.21.0-x86_64-pc-windows-gnu
|
||||||
MSYS2_BITS: 64
|
MSYS2_BITS: 64
|
||||||
|
- TARGET: 1.21.0-x86_64-pc-windows-msvc
|
||||||
|
VCPKG_DEFAULT_TRIPLET: x64-windows
|
||||||
|
VCPKGRS_DYNAMIC: 1
|
||||||
|
- 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 +16,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
|
||||||
|
|
||||||
|
@ -1,27 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.8.0"
|
version = "0.9.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"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
links = "sqlite3"
|
links = "sqlite3"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
keywords = ["sqlite", "database", "ffi"]
|
keywords = ["sqlite", "sqlcipher", "ffi"]
|
||||||
categories = ["database", "external-ffi-bindings"]
|
categories = ["external-ffi-bindings"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["min_sqlite_version_3_6_8"]
|
default = ["min_sqlite_version_3_6_8"]
|
||||||
bundled = ["gcc"]
|
bundled = ["cc"]
|
||||||
buildtime_bindgen = ["bindgen", "pkg-config"]
|
buildtime_bindgen = ["bindgen", "pkg-config", "vcpkg"]
|
||||||
min_sqlite_version_3_6_8 = ["pkg-config"]
|
sqlcipher = []
|
||||||
min_sqlite_version_3_6_11 = ["pkg-config"]
|
min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"]
|
||||||
min_sqlite_version_3_6_23 = ["pkg-config"]
|
min_sqlite_version_3_6_11 = ["pkg-config", "vcpkg"]
|
||||||
min_sqlite_version_3_7_3 = ["pkg-config"]
|
min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"]
|
||||||
min_sqlite_version_3_7_4 = ["pkg-config"]
|
min_sqlite_version_3_7_3 = ["pkg-config", "vcpkg"]
|
||||||
min_sqlite_version_3_7_16 = ["pkg-config"]
|
min_sqlite_version_3_7_4 = ["pkg-config", "vcpkg"]
|
||||||
|
min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = { version = "0.21", optional = true }
|
bindgen = { version = "0.31", optional = true }
|
||||||
pkg-config = { version = "0.3", optional = true }
|
pkg-config = { version = "0.3", optional = true }
|
||||||
gcc = { version = "0.3", optional = true }
|
cc = { version = "1.0", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_env = "msvc")'.build-dependencies]
|
||||||
|
vcpkg = { version = "0.2", optional = true }
|
||||||
|
@ -4,17 +4,21 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(feature = "bundled")]
|
#[cfg(feature = "bundled")]
|
||||||
mod build {
|
mod build {
|
||||||
extern crate gcc;
|
extern crate cc;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
if cfg!(feature = "sqlcipher") {
|
||||||
|
panic!("Builds with bundled SQLCipher are not supported");
|
||||||
|
}
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
||||||
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
|
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
|
||||||
.expect("Could not copy bindings to output directory");
|
.expect("Could not copy bindings to output directory");
|
||||||
|
|
||||||
gcc::Config::new()
|
cc::Build::new()
|
||||||
.file("sqlite3/sqlite3.c")
|
.file("sqlite3/sqlite3.c")
|
||||||
.flag("-DSQLITE_CORE")
|
.flag("-DSQLITE_CORE")
|
||||||
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
|
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
|
||||||
@ -34,6 +38,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 +47,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 {
|
||||||
@ -54,8 +62,9 @@ mod build {
|
|||||||
fn from(header: HeaderLocation) -> String {
|
fn from(header: HeaderLocation) -> String {
|
||||||
match header {
|
match header {
|
||||||
HeaderLocation::FromEnvironment => {
|
HeaderLocation::FromEnvironment => {
|
||||||
let mut header = env::var("SQLITE3_INCLUDE_DIR")
|
let prefix = env_prefix();
|
||||||
.expect("SQLITE3_INCLUDE_DIR must be set if SQLITE3_LIB_DIR is set");
|
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix))
|
||||||
|
.expect(&format!("{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", prefix, prefix));
|
||||||
header.push_str("/sqlite3.h");
|
header.push_str("/sqlite3.h");
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
@ -72,15 +81,21 @@ mod build {
|
|||||||
|
|
||||||
// Prints the necessary cargo link commands and returns the path to the header.
|
// Prints the necessary cargo link commands and returns the path to the header.
|
||||||
fn find_sqlite() -> HeaderLocation {
|
fn find_sqlite() -> HeaderLocation {
|
||||||
|
let link_lib = link_lib();
|
||||||
|
|
||||||
// Allow users to specify where to find SQLite.
|
// Allow users to specify where to find SQLite.
|
||||||
if let Ok(dir) = env::var("SQLITE3_LIB_DIR") {
|
if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) {
|
||||||
println!("cargo:rustc-link-lib=sqlite3");
|
println!("cargo:rustc-link-lib={}", link_lib);
|
||||||
println!("cargo:rustc-link-search={}", dir);
|
println!("cargo:rustc-link-search={}", dir);
|
||||||
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(link_lib) {
|
||||||
Ok(mut lib) => {
|
Ok(mut lib) => {
|
||||||
if let Some(mut header) = lib.include_paths.pop() {
|
if let Some(mut header) = lib.include_paths.pop() {
|
||||||
header.push("sqlite3.h");
|
header.push("sqlite3.h");
|
||||||
@ -94,12 +109,45 @@ mod build {
|
|||||||
// request and hope that the library exists on the system paths. We used to
|
// request and hope that the library exists on the system paths. We used to
|
||||||
// output /usr/lib explicitly, but that can introduce other linking problems; see
|
// output /usr/lib explicitly, but that can introduce other linking problems; see
|
||||||
// https://github.com/jgallagher/rusqlite/issues/207.
|
// https://github.com/jgallagher/rusqlite/issues/207.
|
||||||
println!("cargo:rustc-link-lib=sqlite3");
|
println!("cargo:rustc-link-lib={}", link_lib);
|
||||||
HeaderLocation::Wrapper
|
HeaderLocation::Wrapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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(link_lib()) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
fn env_prefix() -> &'static str {
|
||||||
|
if cfg!(feature = "sqlcipher") {
|
||||||
|
"SQLCIPHER"
|
||||||
|
} else {
|
||||||
|
"SQLITE3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link_lib() -> &'static str {
|
||||||
|
if cfg!(feature = "sqlcipher") {
|
||||||
|
"sqlcipher"
|
||||||
|
} else {
|
||||||
|
"sqlite3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "buildtime_bindgen"))]
|
#[cfg(not(feature = "buildtime_bindgen"))]
|
||||||
mod bindings {
|
mod bindings {
|
||||||
use super::HeaderLocation;
|
use super::HeaderLocation;
|
||||||
@ -139,7 +187,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;
|
||||||
@ -150,7 +198,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)
|
||||||
@ -166,7 +214,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))
|
||||||
@ -177,7 +225,7 @@ mod build {
|
|||||||
// was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
|
// was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
|
||||||
// don't want to not build just because this flag is missing (e.g., if we're linking against
|
// don't want to not build just because this flag is missing (e.g., if we're linking against
|
||||||
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
|
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
|
||||||
if !output.contains("pub const SQLITE_DETERMINISTIC:") {
|
if !output.contains("pub const SQLITE_DETERMINISTIC") {
|
||||||
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
|
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -18,26 +18,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.
|
||||||
|
18
src/blob.rs
18
src/blob.rs
@ -91,7 +91,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,
|
||||||
@ -265,7 +266,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
|
||||||
@ -273,7 +275,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");
|
||||||
@ -313,7 +316,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();
|
||||||
@ -337,7 +341,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
|
||||||
@ -356,7 +361,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
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
17
src/error.rs
17
src/error.rs
@ -74,6 +74,9 @@ pub enum Error {
|
|||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
UserFunctionError(Box<error::Error + Send + Sync>),
|
UserFunctionError(Box<error::Error + Send + Sync>),
|
||||||
|
|
||||||
|
/// Error available for the implementors of the `ToSql` trait.
|
||||||
|
ToSqlConversionFailure(Box<error::Error + Send + Sync>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<str::Utf8Error> for Error {
|
impl From<str::Utf8Error> for Error {
|
||||||
@ -128,6 +131,7 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => err.fmt(f),
|
Error::UserFunctionError(ref err) => err.fmt(f),
|
||||||
|
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,18 +141,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",
|
||||||
@ -159,13 +159,13 @@ impl error::Error for Error {
|
|||||||
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
|
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => err.description(),
|
Error::UserFunctionError(ref err) => err.description(),
|
||||||
|
Error::ToSqlConversionFailure(ref err) => err.description(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cause(&self) -> Option<&error::Error> {
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::SqliteFailure(ref err, _) => Some(err),
|
Error::SqliteFailure(ref err, _) => Some(err),
|
||||||
Error::FromSqlConversionFailure(_, _, ref err) => Some(&**err),
|
|
||||||
Error::Utf8Error(ref err) => Some(err),
|
Error::Utf8Error(ref err) => Some(err),
|
||||||
Error::NulError(ref err) => Some(err),
|
Error::NulError(ref err) => Some(err),
|
||||||
|
|
||||||
@ -185,6 +185,9 @@ impl error::Error for Error {
|
|||||||
|
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::UserFunctionError(ref err) => Some(&**err),
|
Error::UserFunctionError(ref err) => Some(&**err),
|
||||||
|
|
||||||
|
Error::FromSqlConversionFailure(_, _, ref err) |
|
||||||
|
Error::ToSqlConversionFailure(ref err) => Some(&**err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,13 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
92
src/lib.rs
92
src/lib.rs
@ -82,7 +82,7 @@ use cache::StatementCache;
|
|||||||
pub use statement::Statement;
|
pub use statement::Statement;
|
||||||
use statement::StatementCrateImpl;
|
use statement::StatementCrateImpl;
|
||||||
|
|
||||||
pub use row::{Row, Rows, MappedRows, AndThenRows};
|
pub use row::{Row, Rows, MappedRows, AndThenRows, RowIndex};
|
||||||
use row::RowsCrateImpl;
|
use row::RowsCrateImpl;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@ -146,7 +146,7 @@ fn str_to_cstring(s: &str) -> Result<CString> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path_to_cstring(p: &Path) -> Result<CString> {
|
fn path_to_cstring(p: &Path) -> Result<CString> {
|
||||||
let s = try!(p.to_str().ok_or(Error::InvalidPath(p.to_owned())));
|
let s = try!(p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned())));
|
||||||
str_to_cstring(s)
|
str_to_cstring(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +189,12 @@ pub struct Connection {
|
|||||||
|
|
||||||
unsafe impl Send for Connection {}
|
unsafe impl Send for Connection {}
|
||||||
|
|
||||||
|
impl Drop for Connection {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.flush_prepared_statement_cache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
/// Open a new connection to a SQLite database.
|
/// Open a new connection to a SQLite database.
|
||||||
///
|
///
|
||||||
@ -299,7 +305,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).
|
||||||
@ -321,7 +328,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.
|
||||||
@ -410,7 +418,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.
|
||||||
@ -538,7 +548,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.
|
||||||
@ -582,22 +594,23 @@ 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 = 0x0000_0040;
|
||||||
const SQLITE_OPEN_MEMORY = 0x00000080,
|
const SQLITE_OPEN_MEMORY = 0x0000_0080;
|
||||||
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 = 0x0002_0000;
|
||||||
const SQLITE_OPEN_PRIVATE_CACHE = 0x00040000,
|
const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for OpenFlags {
|
impl Default for OpenFlags {
|
||||||
fn default() -> OpenFlags {
|
fn default() -> OpenFlags {
|
||||||
SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NO_MUTEX | SQLITE_OPEN_URI
|
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE |
|
||||||
|
OpenFlags::SQLITE_OPEN_NO_MUTEX | OpenFlags::SQLITE_OPEN_URI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,7 +649,7 @@ fn ensure_valid_sqlite_version() {
|
|||||||
let version_number = version_number();
|
let version_number = version_number();
|
||||||
|
|
||||||
// Check our hard floor.
|
// Check our hard floor.
|
||||||
if version_number < 3006008 {
|
if version_number < 3_006_008 {
|
||||||
panic!("rusqlite requires SQLite 3.6.8 or newer");
|
panic!("rusqlite requires SQLite 3.6.8 or newer");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,7 +702,7 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
|||||||
// will fail if someone else has already initialized SQLite even if they initialized it
|
// will fail if someone else has already initialized SQLite even if they initialized it
|
||||||
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization
|
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization
|
||||||
// above.
|
// above.
|
||||||
if version_number() >= 3007000 {
|
if version_number() >= 3_007_000 {
|
||||||
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
|
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
|
||||||
let is_singlethreaded = unsafe {
|
let is_singlethreaded = unsafe {
|
||||||
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
|
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
|
||||||
@ -735,9 +748,9 @@ impl InnerConnection {
|
|||||||
|
|
||||||
// Replicate the check for sane open flags from SQLite, because the check in SQLite itself
|
// Replicate the check for sane open flags from SQLite, because the check in SQLite itself
|
||||||
// wasn't added until version 3.7.3.
|
// wasn't added until version 3.7.3.
|
||||||
debug_assert!(1 << SQLITE_OPEN_READ_ONLY.bits == 0x02);
|
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
|
||||||
debug_assert!(1 << SQLITE_OPEN_READ_WRITE.bits == 0x04);
|
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
|
||||||
debug_assert!(1 << (SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE).bits == 0x40);
|
debug_assert_eq!(1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, 0x40);
|
||||||
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
|
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
|
||||||
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
|
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
|
||||||
}
|
}
|
||||||
@ -857,7 +870,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 {
|
||||||
@ -872,7 +886,15 @@ impl InnerConnection {
|
|||||||
impl Drop for InnerConnection {
|
impl Drop for InnerConnection {
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.close();
|
use std::thread::panicking;
|
||||||
|
|
||||||
|
if let Err(e) = self.close() {
|
||||||
|
if panicking() {
|
||||||
|
eprintln!("Error while closing SQLite connection: {:?}", e);
|
||||||
|
} else {
|
||||||
|
panic!("Error while closing SQLite connection: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,8 +1003,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_open_with_flags() {
|
fn test_open_with_flags() {
|
||||||
for bad_flags in &[OpenFlags::empty(),
|
for bad_flags in &[OpenFlags::empty(),
|
||||||
SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE,
|
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
|
||||||
SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] {
|
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE] {
|
||||||
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
|
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1011,12 +1033,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]
|
||||||
@ -1073,7 +1098,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();
|
||||||
@ -1158,8 +1184,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);
|
||||||
|
|
||||||
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
src/row.rs
33
src/row.rs
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
||||||
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
|
assert_eq!(params.len() as c_int, self.stmt.bind_parameter_count(),
|
||||||
"incorrect number of parameters to query(): expected {}, got {}",
|
"incorrect number of parameters to query(): expected {}, got {}",
|
||||||
self.stmt.bind_parameter_count(),
|
self.stmt.bind_parameter_count(),
|
||||||
params.len());
|
params.len());
|
||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ extern crate chrono;
|
|||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
|
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, Utc, Local};
|
||||||
|
|
||||||
use Result;
|
use Result;
|
||||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||||
@ -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 {
|
||||||
@ -81,12 +87,12 @@ impl FromSql for NaiveDateTime {
|
|||||||
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
|
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
|
||||||
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
|
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
|
||||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
Ok(ToSqlOutput::from(self.with_timezone(&UTC).to_rfc3339()))
|
Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>.
|
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
|
||||||
impl FromSql for DateTime<UTC> {
|
impl FromSql for DateTime<Utc> {
|
||||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||||
{
|
{
|
||||||
// Try to parse value as rfc3339 first.
|
// Try to parse value as rfc3339 first.
|
||||||
@ -105,19 +111,19 @@ impl FromSql for DateTime<UTC> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
|
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
|
||||||
return Ok(dt.with_timezone(&UTC));
|
return Ok(dt.with_timezone(&Utc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couldn't parse as rfc3339 - fall back to NaiveDateTime.
|
// Couldn't parse as rfc3339 - fall back to NaiveDateTime.
|
||||||
NaiveDateTime::column_result(value).map(|dt| UTC.from_utc_datetime(&dt))
|
NaiveDateTime::column_result(value).map(|dt| Utc.from_utc_datetime(&dt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<Local>.
|
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Local>`.
|
||||||
impl FromSql for DateTime<Local> {
|
impl FromSql for DateTime<Local> {
|
||||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||||
let utc_dt = try!(DateTime::<UTC>::column_result(value));
|
let utc_dt = try!(DateTime::<Utc>::column_result(value));
|
||||||
Ok(utc_dt.with_timezone(&Local))
|
Ok(utc_dt.with_timezone(&Local))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,12 +131,13 @@ impl FromSql for DateTime<Local> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use Connection;
|
use Connection;
|
||||||
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC,
|
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
|
||||||
Duration};
|
Duration};
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,26 +201,30 @@ mod test {
|
|||||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
let date = NaiveDate::from_ymd(2016, 2, 23);
|
||||||
let time = NaiveTime::from_hms_milli(23, 56, 4, 789);
|
let time = NaiveTime::from_hms_milli(23, 56, 4, 789);
|
||||||
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))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(utc, v2);
|
assert_eq!(utc, v2);
|
||||||
|
|
||||||
let v3: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
|
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,12 +61,24 @@ pub trait FromSql: Sized {
|
|||||||
fn column_result(value: ValueRef) -> FromSqlResult<Self>;
|
fn column_result(value: ValueRef) -> FromSqlResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromSql for isize {
|
||||||
|
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||||
|
i64::column_result(value).and_then(|i| {
|
||||||
|
if i < isize::min_value() as i64 || i > isize::max_value() as i64 {
|
||||||
|
Err(FromSqlError::OutOfRange(i))
|
||||||
|
} else {
|
||||||
|
Ok(i as isize)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! from_sql_integral(
|
macro_rules! from_sql_integral(
|
||||||
($t:ident) => (
|
($t:ident) => (
|
||||||
impl FromSql for $t {
|
impl FromSql for $t {
|
||||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||||
i64::column_result(value).and_then(|i| {
|
i64::column_result(value).and_then(|i| {
|
||||||
if i < $t::min_value() as i64 || i > $t::max_value() as i64 {
|
if i < i64::from($t::min_value()) || i > i64::from($t::max_value()) {
|
||||||
Err(FromSqlError::OutOfRange(i))
|
Err(FromSqlError::OutOfRange(i))
|
||||||
} else {
|
} else {
|
||||||
Ok(i as $t)
|
Ok(i as $t)
|
||||||
@ -80,7 +92,6 @@ macro_rules! from_sql_integral(
|
|||||||
from_sql_integral!(i8);
|
from_sql_integral!(i8);
|
||||||
from_sql_integral!(i16);
|
from_sql_integral!(i16);
|
||||||
from_sql_integral!(i32);
|
from_sql_integral!(i32);
|
||||||
from_sql_integral!(isize);
|
|
||||||
from_sql_integral!(u8);
|
from_sql_integral!(u8);
|
||||||
from_sql_integral!(u16);
|
from_sql_integral!(u16);
|
||||||
from_sql_integral!(u32);
|
from_sql_integral!(u32);
|
||||||
@ -164,7 +175,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,23 @@ extern crate time;
|
|||||||
use Result;
|
use Result;
|
||||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||||
|
|
||||||
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,13 +42,27 @@ mod test {
|
|||||||
fn test_timespec() {
|
fn test_timespec() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
|
|
||||||
let ts = time::Timespec {
|
let mut ts_vec = vec![];
|
||||||
sec: 10_000,
|
|
||||||
nsec: 0,
|
ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
|
||||||
};
|
ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
|
ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
|
||||||
|
ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
|
||||||
|
ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
|
||||||
|
ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286
|
||||||
|
|
||||||
|
for ts in ts_vec {
|
||||||
|
|
||||||
|
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
db.execute("DELETE FROM foo", &[]).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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ impl<'a, T: ?Sized> ToSql for &'a T
|
|||||||
where &'a T: Into<ToSqlOutput<'a>>
|
where &'a T: Into<ToSqlOutput<'a>>
|
||||||
{
|
{
|
||||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||||
Ok(ToSqlOutput::from((*self).into()))
|
Ok((*self).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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))
|
||||||
|
@ -30,11 +30,15 @@ impl From<bool> for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<isize> for Value {
|
||||||
|
fn from(i: isize) -> Value { Value::Integer(i as i64) }
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! from_i64(
|
macro_rules! from_i64(
|
||||||
($t:ty) => (
|
($t:ty) => (
|
||||||
impl From<$t> for Value {
|
impl From<$t> for Value {
|
||||||
fn from(i: $t) -> Value {
|
fn from(i: $t) -> Value {
|
||||||
Value::Integer(i as i64)
|
Value::Integer(i64::from(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -43,7 +47,6 @@ macro_rules! from_i64(
|
|||||||
from_i64!(i8);
|
from_i64!(i8);
|
||||||
from_i64!(i16);
|
from_i64!(i16);
|
||||||
from_i64!(i32);
|
from_i64!(i32);
|
||||||
from_i64!(isize);
|
|
||||||
from_i64!(u8);
|
from_i64!(u8);
|
||||||
from_i64!(u16);
|
from_i64!(u16);
|
||||||
from_i64!(u32);
|
from_i64!(u32);
|
||||||
|
@ -3,15 +3,16 @@ use std::ffi::CStr;
|
|||||||
|
|
||||||
/// Returns the SQLite version as an integer; e.g., `3016002` for version 3.16.2.
|
/// Returns the SQLite version as an integer; e.g., `3016002` for version 3.16.2.
|
||||||
///
|
///
|
||||||
/// See [sqlite3_libversion_number()](https://www.sqlite.org/c3ref/libversion.html).
|
/// See [`sqlite3_libversion_number()`](https://www.sqlite.org/c3ref/libversion.html).
|
||||||
pub fn version_number() -> i32 {
|
pub fn version_number() -> i32 {
|
||||||
unsafe { ffi::sqlite3_libversion_number() }
|
unsafe { ffi::sqlite3_libversion_number() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
|
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
|
||||||
///
|
///
|
||||||
/// 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 ?!")
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! This file contains unit tests for rusqlite::trace::config_log. This function affects
|
//! This file contains unit tests for `rusqlite::trace::config_log`. This function affects
|
||||||
//! SQLite process-wide and so is not safe to run as a normal #[test] in the library.
|
//! SQLite process-wide and so is not safe to run as a normal #[test] in the library.
|
||||||
|
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
|
Loading…
Reference in New Issue
Block a user