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

This commit is contained in:
gwenn 2018-01-20 10:08:40 +01:00
commit 96103b0662
30 changed files with 731 additions and 483 deletions

1
.gitattributes vendored Normal file
View File

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

View File

@ -1,4 +1,5 @@
sudo: false
dist: trusty
language: rust
@ -13,12 +14,10 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-precise-3.9
- ubuntu-toolchain-r-test
packages: # recommanded versions for rust-bindgen
- llvm-3.9-dev
- libclang-3.9-dev
- libsqlcipher-dev
env: # specify the clang path for rust-bindgen
- LIBCLANG_PATH=/usr/lib/llvm-3.9/lib
@ -26,6 +25,7 @@ env: # specify the clang path for rust-bindgen
script:
- cargo build
- cargo build --features bundled
- cargo build --features sqlcipher
- cargo test
- cargo test --features backup
- cargo test --features blob
@ -37,6 +37,7 @@ script:
- cargo test --features chrono
- cargo test --features serde_json
- 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 buildtime_bindgen"
- cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled"

View File

@ -18,3 +18,17 @@ rusqlite contributors
* [Omar Ferrer](https://github.com/chamakits)
* [Lee Jenkins](https://github.com/reddraggone9)
* [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)

View File

@ -1,10 +1,10 @@
[package]
name = "rusqlite"
version = "0.11.0"
version = "0.13.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
description = "Ergonomic wrapper for SQLite"
repository = "https://github.com/jgallagher/rusqlite"
documentation = "http://jgallagher.github.io/rusqlite/rusqlite/index.html"
documentation = "http://docs.rs/rusqlite/"
readme = "README.md"
keywords = ["sqlite", "database", "ffi"]
license = "MIT"
@ -27,22 +27,23 @@ bundled = ["libsqlite3-sys/bundled"]
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
limits = []
hooks = []
sqlcipher = ["libsqlite3-sys/sqlcipher"]
[dependencies]
time = "0.1.0"
bitflags = "0.7"
lru-cache = "0.1.0"
chrono = { version = "0.3", optional = true }
serde_json = { version = "0.9", optional = true }
bitflags = "1.0"
lru-cache = "0.1"
chrono = { version = "0.4", optional = true }
serde_json = { version = "1.0", optional = true }
[dev-dependencies]
tempdir = "0.3.4"
tempdir = "0.3"
lazy_static = "0.2"
regex = "0.2"
[dependencies.libsqlite3-sys]
path = "libsqlite3-sys"
version = "0.8"
version = "0.9"
[[test]]
name = "config_log"
@ -50,3 +51,9 @@ harness = false
[[test]]
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"

View File

@ -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)
* Avoid publicly exporting SQLite constants multiple times from libsqlite3-sys.

View File

@ -5,7 +5,7 @@
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
[API documentation](http://jgallagher.github.io/rusqlite/rusqlite/index.html).
[API documentation](http://docs.rs/rusqlite/).
```rust
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
`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.
* `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
`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
pkg-config to find a SQLite library that already exists on your system. You can
adjust this behavior in a couple of ways:
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
[Vcpkg](https://github.com/Microsoft/vcpkg) installation for MSVC ABI builds.
You can adjust this behavior in a number of ways:
* If you use the `bundled` feature, `libsqlite3-sys` will use the
[gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and
link against that. This source is embedded in the `libsqlite3-sys` crate and
is currently SQLite 3.17.0 (as of `rusqlite` 0.10.1 / `libsqlite3-sys`
0.7.1). This is probably the simplest solution to any build problems.
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
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

View File

@ -1,6 +1,13 @@
environment:
TARGET: 1.15.0-x86_64-pc-windows-gnu
matrix:
- TARGET: 1.21.0-x86_64-pc-windows-gnu
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:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe"
- rust-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
@ -9,11 +16,18 @@ install:
- rustc -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)
- 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)
- cmd: 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)
- 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 7z e sqlite-amalgamation-3170000.zip -y > nul
- 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)
- 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

View File

@ -1,27 +1,31 @@
[package]
name = "libsqlite3-sys"
version = "0.8.0"
version = "0.9.1"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
repository = "https://github.com/jgallagher/rusqlite"
description = "Native bindings to the libsqlite3 library"
license = "MIT"
links = "sqlite3"
build = "build.rs"
keywords = ["sqlite", "database", "ffi"]
categories = ["database", "external-ffi-bindings"]
keywords = ["sqlite", "sqlcipher", "ffi"]
categories = ["external-ffi-bindings"]
[features]
default = ["min_sqlite_version_3_6_8"]
bundled = ["gcc"]
buildtime_bindgen = ["bindgen", "pkg-config"]
min_sqlite_version_3_6_8 = ["pkg-config"]
min_sqlite_version_3_6_11 = ["pkg-config"]
min_sqlite_version_3_6_23 = ["pkg-config"]
min_sqlite_version_3_7_3 = ["pkg-config"]
min_sqlite_version_3_7_4 = ["pkg-config"]
min_sqlite_version_3_7_16 = ["pkg-config"]
bundled = ["cc"]
buildtime_bindgen = ["bindgen", "pkg-config", "vcpkg"]
sqlcipher = []
min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_6_11 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_3 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_4 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
[build-dependencies]
bindgen = { version = "0.21", optional = true }
bindgen = { version = "0.31", 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 }

View File

@ -4,17 +4,21 @@ fn main() {
#[cfg(feature = "bundled")]
mod build {
extern crate gcc;
extern crate cc;
use std::{env, fs};
use std::path::Path;
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_path = Path::new(&out_dir).join("bindgen.rs");
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
.expect("Could not copy bindings to output directory");
gcc::Config::new()
cc::Build::new()
.file("sqlite3/sqlite3.c")
.flag("-DSQLITE_CORE")
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
@ -34,6 +38,7 @@ mod build {
.flag("-DSQLITE_SOUNDEX")
.flag("-DSQLITE_THREADSAFE=1")
.flag("-DSQLITE_USE_URI")
.flag("-DHAVE_USLEEP=1")
.compile("libsqlite3.a");
}
}
@ -42,6 +47,9 @@ mod build {
mod build {
extern crate pkg_config;
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
extern crate vcpkg;
use std::env;
pub enum HeaderLocation {
@ -54,8 +62,9 @@ mod build {
fn from(header: HeaderLocation) -> String {
match header {
HeaderLocation::FromEnvironment => {
let mut header = env::var("SQLITE3_INCLUDE_DIR")
.expect("SQLITE3_INCLUDE_DIR must be set if SQLITE3_LIB_DIR is set");
let prefix = env_prefix();
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix))
.expect(&format!("{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", prefix, prefix));
header.push_str("/sqlite3.h");
header
}
@ -72,15 +81,21 @@ mod build {
// Prints the necessary cargo link commands and returns the path to the header.
fn find_sqlite() -> HeaderLocation {
let link_lib = link_lib();
// Allow users to specify where to find SQLite.
if let Ok(dir) = env::var("SQLITE3_LIB_DIR") {
println!("cargo:rustc-link-lib=sqlite3");
if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) {
println!("cargo:rustc-link-lib={}", link_lib);
println!("cargo:rustc-link-search={}", dir);
return HeaderLocation::FromEnvironment;
}
if let Some(header) = try_vcpkg() {
return header;
}
// 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) => {
if let Some(mut header) = lib.include_paths.pop() {
header.push("sqlite3.h");
@ -94,12 +109,45 @@ mod build {
// 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
// https://github.com/jgallagher/rusqlite/issues/207.
println!("cargo:rustc-link-lib=sqlite3");
println!("cargo:rustc-link-lib={}", link_lib);
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"))]
mod bindings {
use super::HeaderLocation;
@ -139,7 +187,7 @@ mod build {
mod bindings {
extern crate bindgen;
use self::bindgen::chooser::{TypeChooser, IntKind};
use self::bindgen::callbacks::{ParseCallbacks, IntKind};
use super::HeaderLocation;
use std::env;
@ -150,7 +198,7 @@ mod build {
#[derive(Debug)]
struct SqliteTypeChooser;
impl TypeChooser for SqliteTypeChooser {
impl ParseCallbacks for SqliteTypeChooser {
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
Some(IntKind::I32)
@ -166,7 +214,7 @@ mod build {
let mut output = Vec::new();
bindgen::builder()
.header(header.clone())
.type_chooser(Box::new(SqliteTypeChooser))
.parse_callbacks(Box::new(SqliteTypeChooser))
.generate()
.expect(&format!("could not run bindgen on header {}", header))
.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
// 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.
if !output.contains("pub const SQLITE_DETERMINISTIC:") {
if !output.contains("pub const SQLITE_DETERMINISTIC") {
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
}

View File

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

View File

@ -18,26 +18,26 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
#[repr(C)]
pub enum Limit {
/// 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.
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
/// 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.
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.
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.
SQLITE_LIMIT_VDBE_OP = 5,
SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
/// 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.
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.
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.
SQLITE_LIMIT_VARIABLE_NUMBER = 9,
SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
/// The maximum depth of recursion for triggers.
SQLITE_LIMIT_TRIGGER_DEPTH = 10,
/// The maximum number of auxiliary worker threads that a single prepared statement may start.

View File

@ -91,7 +91,8 @@ impl Connection {
if read_only { 0 } else { 1 },
&mut blob)
};
c.decode_result(rc).map(|_| {
c.decode_result(rc)
.map(|_| {
Blob {
conn: self,
blob: blob,
@ -265,7 +266,8 @@ mod test {
fn test_blob() {
let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(4, blob.write(b"Clob").unwrap());
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
@ -273,7 +275,8 @@ mod test {
blob.reopen(rowid).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];
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
assert_eq!(&bytes, b"Clob5");
@ -313,7 +316,8 @@ mod test {
fn test_blob_in_bufreader() {
let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
blob.reopen(rowid).unwrap();
@ -337,7 +341,8 @@ mod test {
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);
// 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);
// trying to write_all too much should fail

View File

@ -280,7 +280,8 @@ mod test {
#[test]
fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;")
.unwrap();
conn.close().expect("connection not closed");
}

View File

@ -74,6 +74,9 @@ pub enum Error {
#[cfg(feature = "functions")]
#[allow(dead_code)]
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 {
@ -128,6 +131,7 @@ impl fmt::Display for Error {
}
#[cfg(feature = "functions")]
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 {
Error::SqliteFailure(ref err, None) => err.description(),
Error::SqliteFailure(_, Some(ref s)) => s,
Error::SqliteSingleThreadedMode => {
"SQLite was compiled or configured for single-threaded use only"
}
Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(),
Error::InvalidPath(_) => "invalid path",
Error::ExecuteReturnedResults => {
"execute returned results - did you mean to call query?"
}
Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
Error::QueryReturnedNoRows => "query returned no rows",
Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name",
@ -159,13 +159,13 @@ impl error::Error for Error {
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
#[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => err.description(),
Error::ToSqlConversionFailure(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::SqliteFailure(ref err, _) => Some(err),
Error::FromSqlConversionFailure(_, _, ref err) => Some(&**err),
Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err),
@ -185,6 +185,9 @@ impl error::Error for Error {
#[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => Some(&**err),
Error::FromSqlConversionFailure(_, _, ref err) |
Error::ToSqlConversionFailure(ref err) => Some(&**err),
}
}
}

View File

@ -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
// error code if not.
#[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"))]
fn constraint_error_code() -> i32 { ffi::SQLITE_CONSTRAINT }
fn constraint_error_code() -> i32 {
ffi::SQLITE_CONSTRAINT
}
match *err {
Error::SqliteFailure(ref err, ref s) => {
@ -154,19 +158,24 @@ impl<'a> ValueRef<'a> {
let s = CStr::from_ptr(text as *const c_char);
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str().expect("sqlite3_value_text returned invalid UTF-8");
let s = s.to_str()
.expect("sqlite3_value_text returned invalid UTF-8");
ValueRef::Text(s)
}
ffi::SQLITE_BLOB => {
let blob = ffi::sqlite3_value_blob(value);
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB value type with NULL data");
let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value));
let len = ffi::sqlite3_value_bytes(value);
assert!(len >= 0,
"unexpected negative return from sqlite3_value_bytes");
if len > 0 {
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB value type with NULL data");
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"),
}
@ -207,7 +216,10 @@ impl<'a> Context<'a> {
FromSqlError::InvalidType => {
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) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
@ -304,7 +316,9 @@ impl Connection {
where F: FnMut(&Context) -> Result<T>,
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.
@ -720,17 +734,16 @@ mod test {
#[test]
fn test_sum() {
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)
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))
.unwrap();
let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)).unwrap();
assert!(result.is_none());
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))
.unwrap();
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
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 \
@ -743,7 +756,8 @@ mod test {
#[test]
fn test_count() {
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)
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);
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))
.unwrap();
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
assert_eq!(2, result);
}
}

View File

@ -82,7 +82,7 @@ use cache::StatementCache;
pub use statement::Statement;
use statement::StatementCrateImpl;
pub use row::{Row, Rows, MappedRows, AndThenRows};
pub use row::{Row, Rows, MappedRows, AndThenRows, RowIndex};
use row::RowsCrateImpl;
#[allow(deprecated)]
@ -146,7 +146,7 @@ fn str_to_cstring(s: &str) -> 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)
}
@ -189,6 +189,12 @@ pub struct Connection {
unsafe impl Send for Connection {}
impl Drop for Connection {
fn drop(&mut self) {
self.flush_prepared_statement_cache();
}
}
impl Connection {
/// 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
/// underlying SQLite call fails.
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).
@ -321,7 +328,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn 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.
@ -410,7 +418,9 @@ impl Connection {
let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params));
rows.get_expected_row().map_err(E::from).and_then(|r| f(&r))
rows.get_expected_row()
.map_err(E::from)
.and_then(|r| f(&r))
}
/// Convenience method to execute a query that is expected to return a single row.
@ -538,7 +548,9 @@ impl Connection {
dylib_path: P,
entry_point: Option<&str>)
-> 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.
@ -582,22 +594,23 @@ bitflags! {
#[doc = "Flags for opening SQLite database connections."]
#[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
#[repr(C)]
pub flags OpenFlags: ::std::os::raw::c_int {
const SQLITE_OPEN_READ_ONLY = 0x00000001,
const SQLITE_OPEN_READ_WRITE = 0x00000002,
const SQLITE_OPEN_CREATE = 0x00000004,
const SQLITE_OPEN_URI = 0x00000040,
const SQLITE_OPEN_MEMORY = 0x00000080,
const SQLITE_OPEN_NO_MUTEX = 0x00008000,
const SQLITE_OPEN_FULL_MUTEX = 0x00010000,
const SQLITE_OPEN_SHARED_CACHE = 0x00020000,
const SQLITE_OPEN_PRIVATE_CACHE = 0x00040000,
pub struct OpenFlags: ::std::os::raw::c_int {
const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY;
const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE;
const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
const SQLITE_OPEN_URI = 0x0000_0040;
const SQLITE_OPEN_MEMORY = 0x0000_0080;
const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX;
const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX;
const SQLITE_OPEN_SHARED_CACHE = 0x0002_0000;
const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
}
}
impl Default for 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();
// Check our hard floor.
if version_number < 3006008 {
if version_number < 3_006_008 {
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
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization
// above.
if version_number() >= 3007000 {
if version_number() >= 3_007_000 {
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
let is_singlethreaded = unsafe {
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
// wasn't added until version 3.7.3.
debug_assert!(1 << SQLITE_OPEN_READ_ONLY.bits == 0x02);
debug_assert!(1 << 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_ONLY.bits, 0x02);
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
debug_assert_eq!(1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, 0x40);
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
}
@ -857,7 +870,8 @@ impl InnerConnection {
&mut c_stmt,
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 {
@ -872,7 +886,15 @@ impl InnerConnection {
impl Drop for InnerConnection {
#[allow(unused_must_use)]
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]
fn test_open_with_flags() {
for bad_flags in &[OpenFlags::empty(),
SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE,
SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] {
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE] {
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
}
}
@ -1011,12 +1033,15 @@ mod test {
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap());
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
.unwrap());
assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap());
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
.unwrap());
assert_eq!(3i32,
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
}
#[test]
@ -1073,7 +1098,8 @@ mod test {
assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1);
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC").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 v = Vec::<i32>::new();
@ -1158,8 +1184,10 @@ mod test {
#[test]
fn test_last_insert_rowid() {
let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)").unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
.unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES")
.unwrap();
assert_eq!(db.last_insert_rowid(), 1);

View File

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

View File

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

View File

@ -368,7 +368,7 @@ impl<'conn> Statement<'conn> {
}
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 {}",
self.stmt.bind_parameter_count(),
params.len());
@ -405,10 +405,15 @@ impl<'conn> Statement<'conn> {
.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::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col, r) },
ValueRef::Integer(i) => unsafe {
ffi::sqlite3_bind_int64(ptr, col, i)
},
ValueRef::Real(r) => unsafe {
ffi::sqlite3_bind_double(ptr, col, r)
},
ValueRef::Text(s) => unsafe {
let length = s.len();
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.
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)
}
ffi::SQLITE_BLOB => {
@ -570,9 +576,11 @@ mod test {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
.unwrap(),
1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1);
assert_eq!(3i32,
@ -589,7 +597,8 @@ mod test {
INTEGER)";
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();
assert_eq!(1i32,
@ -608,7 +617,8 @@ mod test {
"#;
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 id: i32 = rows.next().unwrap().unwrap().get(0);
@ -624,7 +634,8 @@ mod test {
"#;
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 id: i32 = row.get(0);
2 * id
@ -676,7 +687,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> =
@ -691,7 +703,8 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap();
@ -704,15 +717,18 @@ mod test {
#[test]
fn test_insert() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
.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(&[&2i32]).unwrap(), 2);
match stmt.insert(&[&1i32]).unwrap_err() {
Error::StatementChangedRows(0) => (),
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() {
Error::StatementChangedRows(2) => (),
err => panic!("Unexpected error {}", err),
@ -729,9 +745,15 @@ mod test {
")
.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);
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);
}

View File

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

View File

@ -3,7 +3,7 @@ extern crate chrono;
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 types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
@ -19,7 +19,9 @@ impl ToSql for NaiveDate {
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
impl FromSql for NaiveDate {
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),
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.
impl FromSql for NaiveTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value.as_str().and_then(|s| {
value
.as_str()
.and_then(|s| {
let fmt = match s.len() {
5 => "%H:%M",
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)
impl FromSql for NaiveDateTime {
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' {
"%Y-%m-%dT%H:%M:%S%.f"
} 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").
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
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>.
impl FromSql for DateTime<UTC> {
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
impl FromSql for DateTime<Utc> {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
{
// 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) {
return Ok(dt.with_timezone(&UTC));
return Ok(dt.with_timezone(&Utc));
}
}
// 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> {
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))
}
}
@ -125,12 +131,13 @@ impl FromSql for DateTime<Local> {
#[cfg(test)]
mod test {
use Connection;
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, UTC,
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
Duration};
fn checked_memory_handle() -> Connection {
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
}
@ -138,11 +145,14 @@ mod test {
fn test_naive_date() {
let db = checked_memory_handle();
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);
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);
}
@ -150,11 +160,14 @@ mod test {
fn test_naive_time() {
let db = checked_memory_handle();
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);
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);
}
@ -165,15 +178,20 @@ mod test {
let time = NaiveTime::from_hms(23, 56, 4);
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);
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);
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap();
db.execute("UPDATE foo set b = datetime(t)", &[])
.unwrap(); // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, hms);
}
@ -183,26 +201,30 @@ mod test {
let date = NaiveDate::from_ymd(2016, 2, 23);
let time = NaiveTime::from_hms_milli(23, 56, 4, 789);
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);
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);
let v2: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
let v2: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v2);
let v3: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc - Duration::milliseconds(789), v3);
let v4: DateTime<UTC> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap();
let v4: DateTime<Utc> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v4);
}
@ -214,13 +236,16 @@ mod test {
let dt = NaiveDateTime::new(date, time);
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
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"));
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);
}
}

View File

@ -61,12 +61,24 @@ pub trait FromSql: Sized {
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(
($t:ident) => (
impl FromSql for $t {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
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))
} else {
Ok(i as $t)
@ -80,7 +92,6 @@ macro_rules! from_sql_integral(
from_sql_integral!(i8);
from_sql_integral!(i16);
from_sql_integral!(i32);
from_sql_integral!(isize);
from_sql_integral!(u8);
from_sql_integral!(u16);
from_sql_integral!(u32);
@ -164,7 +175,9 @@ mod test {
}
for n in in_range {
assert_eq!(*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)).unwrap().into());
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
.unwrap()
.into());
}
}

View File

@ -119,7 +119,8 @@ mod test {
fn checked_memory_handle() -> Connection {
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
}
@ -128,9 +129,11 @@ mod test {
let db = checked_memory_handle();
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);
}
@ -139,9 +142,11 @@ mod test {
let db = checked_memory_handle();
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);
}
@ -150,9 +155,11 @@ mod test {
let db = checked_memory_handle();
let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
.unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s);
}
@ -161,9 +168,11 @@ mod test {
let db = checked_memory_handle();
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);
}
@ -171,10 +180,12 @@ mod test {
fn test_value() {
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,
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]
@ -184,10 +195,13 @@ mod test {
let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap();
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
.unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
.unwrap();
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap();
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
.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!(1, row.get_checked::<i32, c_int>(2).unwrap());
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_double>>(4).unwrap().is_none());
assert!(row.get_checked::<i32, Option<String>>(4).unwrap().is_none());
assert!(row.get_checked::<i32, Option<c_int>>(4)
.unwrap()
.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

View File

@ -32,7 +32,8 @@ mod test {
fn checked_memory_handle() -> Connection {
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
}
@ -46,9 +47,11 @@ mod test {
&[&data, &json.as_bytes()])
.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);
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);
}
}

View File

@ -3,18 +3,23 @@ extern crate time;
use Result;
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 {
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))
}
}
impl FromSql for time::Timespec {
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()),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
@ -28,7 +33,8 @@ mod test {
fn checked_memory_handle() -> Connection {
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
}
@ -36,13 +42,27 @@ mod test {
fn test_timespec() {
let db = checked_memory_handle();
let ts = time::Timespec {
sec: 10_000,
nsec: 0,
};
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
let mut ts_vec = vec![];
ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286
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);
}
}
}

View File

@ -84,7 +84,7 @@ impl<'a, T: ?Sized> ToSql for &'a T
where &'a T: Into<ToSqlOutput<'a>>
{
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> {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self.as_slice()))
}
}
impl ToSql for [u8] {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Value {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(ToSqlOutput::from(self))

View File

@ -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(
($t:ty) => (
impl From<$t> for 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!(i16);
from_i64!(i32);
from_i64!(isize);
from_i64!(u8);
from_i64!(u16);
from_i64!(u32);

View File

@ -3,15 +3,16 @@ use std::ffi::CStr;
/// 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 {
unsafe { ffi::sqlite3_libversion_number() }
}
/// 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 {
let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
cstr.to_str().expect("SQLite version string is not valid UTF8 ?!")
cstr.to_str()
.expect("SQLite version string is not valid UTF8 ?!")
}

View File

@ -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.
#[cfg(feature = "trace")]