aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-09-16 17:44:17 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-09-16 17:44:17 +0000
commit76138a0f926b3de7ba53abc60bd8db1a56a7845d (patch)
tree7627b35b901cea37bfd0a90396c150eddd1e526b
parent6a985204e25980f852b4ae2c50f7d1ebfa46667d (diff)
parent29fb7839dc59489b05bcb8af61578b28ae8d6ccd (diff)
downloadrusqlite-76138a0f926b3de7ba53abc60bd8db1a56a7845d.tar.gz
Upgrade rust/crates/rusqlite to 0.24.0 am: b5372b7e69 am: 29fb7839dc
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/rusqlite/+/1407248 Change-Id: Ide267a39d56283cd8599f770a5c8785de1390d5d
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/main.yml13
-rw-r--r--Cargo.toml17
-rw-r--r--Cargo.toml.orig18
-rw-r--r--METADATA6
-rw-r--r--README.md20
-rw-r--r--benches/exec.rs17
-rw-r--r--codecov.yml8
-rw-r--r--src/backup.rs4
-rw-r--r--src/busy.rs2
-rw-r--r--src/cache.rs2
-rw-r--r--src/collation.rs2
-rw-r--r--src/column.rs9
-rw-r--r--src/config.rs26
-rw-r--r--src/context.rs2
-rw-r--r--src/error.rs1
-rw-r--r--src/functions.rs13
-rw-r--r--src/hooks.rs4
-rw-r--r--src/inner_connection.rs35
-rw-r--r--src/lib.rs52
-rw-r--r--src/pragma.rs11
-rw-r--r--src/raw_statement.rs12
-rw-r--r--src/row.rs230
-rw-r--r--src/session.rs13
-rw-r--r--src/statement.rs27
-rw-r--r--src/trace.rs17
-rw-r--r--src/transaction.rs6
-rw-r--r--src/types/from_sql.rs6
-rw-r--r--src/types/mod.rs73
-rw-r--r--src/types/time.rs59
-rw-r--r--src/types/to_sql.rs1
-rw-r--r--src/types/value.rs1
-rw-r--r--src/types/value_ref.rs1
-rw-r--r--src/vtab/array.rs18
-rw-r--r--src/vtab/csvtab.rs19
-rw-r--r--src/vtab/mod.rs68
-rw-r--r--src/vtab/series.rs28
-rw-r--r--tests/vtab.rs12
38 files changed, 639 insertions, 216 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 2220414..8625996 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "65ef2224b6c36ee7d298331ee45349844a8bed80"
+ "sha1": "79ab6894f0f6f651515ab6eac6043685befe9bd8"
}
}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index d64ee25..5e93ceb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -54,6 +54,14 @@ jobs:
command: test
args: --features 'bundled-full session buildtime_bindgen' --all-targets --workspace
+ - name: "cargo test --doc --features 'bundled-full session buildtime_bindgen'"
+ # TODO: clang is installed on these -- but `bindgen` can't find it...
+ if: matrix.platform.os != 'windows-latest'
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --features 'bundled-full session buildtime_bindgen' --doc --workspace
+
- name: "cargo test --features bundled-full"
uses: actions-rs/cargo@v1
with:
@@ -85,6 +93,11 @@ jobs:
RUSTFLAGS: -Zsanitizer=address
RUSTDOCFLAGS: -Zsanitizer=address
ASAN_OPTIONS: 'detect_stack_use_after_return=1'
+ # Work around https://github.com/rust-lang/rust/issues/59125 by
+ # disabling backtraces. In an ideal world we'd probably suppress the
+ # leak sanitization, but we don't care about backtraces here, so long
+ # as the other tests have them.
+ RUST_BACKTRACE: '0'
run: cargo -Z build-std test --features 'bundled-full session buildtime_bindgen with-asan' --target x86_64-unknown-linux-gnu
# Ensure clippy doesn't complain.
diff --git a/Cargo.toml b/Cargo.toml
index 3add3fe..6842cfa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
[package]
edition = "2018"
name = "rusqlite"
-version = "0.23.1"
+version = "0.24.0"
authors = ["The rusqlite developers"]
description = "Ergonomic wrapper for SQLite"
documentation = "http://docs.rs/rusqlite/"
@@ -25,7 +25,7 @@ repository = "https://github.com/rusqlite/rusqlite"
[package.metadata.docs.rs]
all-features = false
default-target = "x86_64-unknown-linux-gnu"
-features = ["backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "trace", "url", "vtab", "window", "modern_sqlite"]
+features = ["backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "time", "trace", "url", "vtab", "window", "modern_sqlite", "column_decltype"]
no-default-features = true
[package.metadata.playground]
@@ -48,6 +48,10 @@ name = "vtab"
[[bench]]
name = "cache"
harness = false
+
+[[bench]]
+name = "exec"
+harness = false
[dependencies.bitflags]
version = "1.0"
@@ -75,7 +79,7 @@ version = "1.0"
optional = true
[dependencies.libsqlite3-sys]
-version = "0.18.0"
+version = "0.20.0"
[dependencies.lru-cache]
version = "0.1"
@@ -91,7 +95,8 @@ optional = true
version = "1.3"
[dependencies.time]
-version = "0.1.0"
+version = "0.2"
+optional = true
[dependencies.url]
version = "2.0"
@@ -128,9 +133,10 @@ backup = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
blob = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
bundled = ["libsqlite3-sys/bundled", "modern_sqlite"]
-bundled-full = ["array", "backup", "blob", "bundled", "chrono", "collation", "csvtab", "extra_check", "functions", "hooks", "i128_blob", "limits", "load_extension", "serde_json", "series", "trace", "unlock_notify", "url", "uuid", "vtab", "window"]
+bundled-full = ["array", "backup", "blob", "bundled", "chrono", "collation", "column_decltype", "csvtab", "extra_check", "functions", "hooks", "i128_blob", "limits", "load_extension", "serde_json", "series", "trace", "unlock_notify", "url", "uuid", "vtab", "window"]
bundled-windows = ["libsqlite3-sys/bundled-windows"]
collation = []
+column_decltype = []
csvtab = ["csv", "vtab"]
extra_check = []
functions = ["libsqlite3-sys/min_sqlite_version_3_7_7"]
@@ -146,6 +152,7 @@ sqlcipher = ["libsqlite3-sys/sqlcipher"]
trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
unlock_notify = ["libsqlite3-sys/unlock_notify"]
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7", "lazy_static"]
+wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"]
window = ["functions"]
with-asan = ["libsqlite3-sys/with-asan"]
[badges.appveyor]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index eef2a27..6394b09 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "rusqlite"
-version = "0.23.1"
+version = "0.24.0"
authors = ["The rusqlite developers"]
edition = "2018"
description = "Ergonomic wrapper for SQLite"
@@ -59,6 +59,8 @@ in_gecko = ["modern_sqlite", "libsqlite3-sys/in_gecko"]
bundled-windows = ["libsqlite3-sys/bundled-windows"]
# Build bundled sqlite with -fsanitize=address
with-asan = ["libsqlite3-sys/with-asan"]
+column_decltype = []
+wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"]
# Helper feature for enabling both `bundled` and most non-build-related optional
# features or dependencies. This is useful for running tests / clippy / etc. New
@@ -71,6 +73,7 @@ bundled-full = [
"bundled",
"chrono",
"collation",
+ "column_decltype",
"csvtab",
"extra_check",
"functions",
@@ -80,6 +83,9 @@ bundled-full = [
"load_extension",
"serde_json",
"series",
+ # time v0.2 does not work with tarpaulin v0.14.0. See time-rs/time#265.
+ # Re-enable when time v0.3 is released with the fix.
+ # "time",
"trace",
"unlock_notify",
"url",
@@ -89,7 +95,7 @@ bundled-full = [
]
[dependencies]
-time = "0.1.0"
+time = { version = "0.2", optional = true }
bitflags = "1.0"
lru-cache = "0.1"
chrono = { version = "0.4", optional = true }
@@ -117,7 +123,7 @@ bencher = "0.1"
[dependencies.libsqlite3-sys]
path = "libsqlite3-sys"
-version = "0.18.0"
+version = "0.20.0"
[[test]]
name = "config_log"
@@ -133,8 +139,12 @@ name = "vtab"
name = "cache"
harness = false
+[[bench]]
+name = "exec"
+harness = false
+
[package.metadata.docs.rs]
-features = [ "backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "trace", "url", "vtab", "window", "modern_sqlite" ]
+features = [ "backup", "blob", "chrono", "collation", "functions", "limits", "load_extension", "serde_json", "time", "trace", "url", "vtab", "window", "modern_sqlite", "column_decltype" ]
all-features = false
no-default-features = true
default-target = "x86_64-unknown-linux-gnu"
diff --git a/METADATA b/METADATA
index c341c90..64ff2c1 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: ARCHIVE
value: "https://static.crates.io/crates/rusqlite/rusqlite-0.23.1.crate"
}
- version: "0.23.1"
+ version: "0.24.0"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 6
- day: 11
+ month: 8
+ day: 22
}
}
diff --git a/README.md b/README.md
index 40bffdd..300c2b3 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
[![Build Status](https://github.com/rusqlite/rusqlite/workflows/CI/badge.svg)](https://github.com/rusqlite/rusqlite/actions)
[![dependency status](https://deps.rs/repo/github/rusqlite/rusqlite/status.svg)](https://deps.rs/repo/github/rusqlite/rusqlite)
[![Latest Version](https://img.shields.io/crates/v/rusqlite.svg)](https://crates.io/crates/rusqlite)
+[![Gitter](https://badges.gitter.im/rusqlite.svg)](https://gitter.im/rusqlite/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Docs](https://docs.rs/rusqlite/badge.svg)](https://docs.rs/rusqlite)
[![codecov](https://codecov.io/gh/rusqlite/rusqlite/branch/master/graph/badge.svg)](https://codecov.io/gh/rusqlite/rusqlite)
@@ -90,6 +91,9 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
+* `time` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
+ and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
+ `time::OffsetDateTime` type from the [`time` crate](https://crates.io/crates/time).
* `url` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
`Url` type from the [`url` crate](https://crates.io/crates/url).
@@ -97,7 +101,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature is mutually exclusive with `bundled`.
* `hooks` for [Commit, Rollback](http://sqlite.org/c3ref/commit_hook.html) and [Data Change](http://sqlite.org/c3ref/update_hook.html) notification callbacks.
* `unlock_notify` for [Unlock](https://sqlite.org/unlock_notify.html) notification.
-* `vtab` for [virtual table](https://sqlite.org/vtab.html) support (allows you to write virtual table implemntations in Rust). Currently, only read-only virtual tables are supported.
+* `vtab` for [virtual table](https://sqlite.org/vtab.html) support (allows you to write virtual table implementations in Rust). Currently, only read-only virtual tables are supported.
* [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust.
* [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function.
* `i128_blob` allows storing values of type `i128` type in SQLite databases. Internally, the data is stored as a 16 byte big-endian blob, with the most significant bit flipped, which allows ordering and comparison between different blobs storing i128s to work as expected.
@@ -108,18 +112,18 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
`libsqlite3-sys` is a separate crate from `rusqlite` that provides the Rust
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.
+[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
[cc](https://crates.io/crates/cc) crate to compile SQLite from source and
link against that. This source is embedded in the `libsqlite3-sys` crate and
- is currently SQLite 3.30.1 (as of `rusqlite` 0.21.0 / `libsqlite3-sys`
- 0.17.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
+ is currently SQLite 3.33.0 (as of `rusqlite` 0.24.0 / `libsqlite3-sys`
+ 0.20.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
```toml
[dependencies.rusqlite]
- version = "0.21.0"
+ version = "0.24.0"
features = ["bundled"]
```
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
@@ -130,7 +134,7 @@ You can adjust this behavior in a number of ways:
options. The default when using vcpkg is to dynamically link,
which must be enabled by setting `VCPKGRS_DYNAMIC=1` environment variable before build.
`vcpkg install sqlite3:x64-windows` will install the required library.
-
+
### Binding generation
We use [bindgen](https://crates.io/crates/bindgen) to generate the Rust
@@ -192,6 +196,10 @@ instead.
Rusqlite is the product of hard work by a number of people. A list is available
here: https://github.com/rusqlite/rusqlite/graphs/contributors
+## Community
+
+Currently there's a gitter channel set up for rusqlite [here](https://gitter.im/rusqlite/community).
+
## License
Rusqlite is available under the MIT license. See the LICENSE file for more info.
diff --git a/benches/exec.rs b/benches/exec.rs
new file mode 100644
index 0000000..360a98b
--- /dev/null
+++ b/benches/exec.rs
@@ -0,0 +1,17 @@
+use bencher::{benchmark_group, benchmark_main, Bencher};
+use rusqlite::{Connection, NO_PARAMS};
+
+fn bench_execute(b: &mut Bencher) {
+ let db = Connection::open_in_memory().unwrap();
+ let sql = "PRAGMA user_version=1";
+ b.iter(|| db.execute(sql, NO_PARAMS).unwrap());
+}
+
+fn bench_execute_batch(b: &mut Bencher) {
+ let db = Connection::open_in_memory().unwrap();
+ let sql = "PRAGMA user_version=1";
+ b.iter(|| db.execute_batch(sql).unwrap());
+}
+
+benchmark_group!(exec_benches, bench_execute, bench_execute_batch);
+benchmark_main!(exec_benches);
diff --git a/codecov.yml b/codecov.yml
index 9811dde..7a4789e 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,3 +1,11 @@
ignore:
- "libsqlite3-sys/bindgen-bindings"
- "libsqlite3-sys/sqlite3"
+coverage:
+ status:
+ project:
+ default:
+ informational: true
+ patch:
+ default:
+ informational: true
diff --git a/src/backup.rs b/src/backup.rs
index e095146..4654907 100644
--- a/src/backup.rs
+++ b/src/backup.rs
@@ -165,8 +165,8 @@ pub struct Progress {
/// `feature = "backup"` A handle to an online backup.
pub struct Backup<'a, 'b> {
- phantom_from: PhantomData<&'a ()>,
- phantom_to: PhantomData<&'b ()>,
+ phantom_from: PhantomData<&'a Connection>,
+ phantom_to: PhantomData<&'b Connection>,
b: *mut ffi::sqlite3_backup,
}
diff --git a/src/busy.rs b/src/busy.rs
index 223df78..b87504a 100644
--- a/src/busy.rs
+++ b/src/busy.rs
@@ -60,7 +60,7 @@ impl Connection {
let mut c = self.db.borrow_mut();
let r = match callback {
Some(f) => unsafe {
- ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), mem::transmute(f))
+ ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), f as *mut c_void)
},
None => unsafe { ffi::sqlite3_busy_handler(c.db(), None, ptr::null_mut()) },
};
diff --git a/src/cache.rs b/src/cache.rs
index f85c3ab..67ecd24 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -84,7 +84,7 @@ impl Drop for CachedStatement<'_> {
#[allow(unused_must_use)]
fn drop(&mut self) {
if let Some(stmt) = self.stmt.take() {
- self.cache.cache_stmt(stmt.into());
+ self.cache.cache_stmt(unsafe { stmt.into_raw() });
}
}
}
diff --git a/src/collation.rs b/src/collation.rs
index 25b8458..1168b75 100644
--- a/src/collation.rs
+++ b/src/collation.rs
@@ -132,7 +132,7 @@ impl InnerConnection {
let r = unsafe {
ffi::sqlite3_collation_needed(
self.db(),
- mem::transmute(x_coll_needed),
+ x_coll_needed as *mut c_void,
Some(collation_needed_callback),
)
};
diff --git a/src/column.rs b/src/column.rs
index 36ba9c8..4f6daac 100644
--- a/src/column.rs
+++ b/src/column.rs
@@ -39,7 +39,7 @@ impl Statement<'_> {
self.stmt.column_count()
}
- pub(crate) fn column_name_unwrap(&self, col: usize) -> &str {
+ pub(super) fn column_name_unwrap(&self, col: usize) -> &str {
// Just panic if the bounds are wrong for now, we never call this
// without checking first.
self.column_name(col).expect("Column out of bounds")
@@ -86,6 +86,7 @@ impl Statement<'_> {
}
/// Returns a slice describing the columns of the result of the query.
+ #[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Vec<Column> {
let n = self.column_count();
let mut cols = Vec::with_capacity(n as usize);
@@ -123,6 +124,7 @@ impl<'stmt> Rows<'stmt> {
}
/// Returns a slice describing the columns of the Rows.
+ #[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Option<Vec<Column>> {
self.stmt.map(Statement::columns)
}
@@ -150,6 +152,7 @@ impl<'stmt> Row<'stmt> {
}
/// Returns a slice describing the columns of the Row.
+ #[cfg(feature = "column_decltype")]
pub fn columns(&self) -> Vec<Column> {
self.stmt.columns()
}
@@ -157,11 +160,13 @@ impl<'stmt> Row<'stmt> {
#[cfg(test)]
mod test {
- use super::Column;
use crate::Connection;
#[test]
+ #[cfg(feature = "column_decltype")]
fn test_columns() {
+ use super::Column;
+
let db = Connection::open_in_memory().unwrap();
let query = db.prepare("SELECT * FROM sqlite_master").unwrap();
let columns = query.columns();
diff --git a/src/config.rs b/src/config.rs
index 074fed0..797069e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -6,33 +6,55 @@ use crate::ffi;
use crate::{Connection, Result};
/// Database Connection Configuration Options
+/// See [Database Connection Configuration Options](https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html) for details.
#[repr(i32)]
#[allow(non_snake_case, non_camel_case_types)]
#[non_exhaustive]
pub enum DbConfig {
//SQLITE_DBCONFIG_MAINDBNAME = 1000, /* const char* */
//SQLITE_DBCONFIG_LOOKASIDE = 1001, /* void* int int */
+ /// Enable or disable the enforcement of foreign key constraints.
SQLITE_DBCONFIG_ENABLE_FKEY = 1002,
+ /// Enable or disable triggers.
SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003,
+ /// Enable or disable the fts3_tokenizer() function which is part of the
+ /// FTS3 full-text search engine extension.
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // 3.12.0
//SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005,
+ /// In WAL mode, enable or disable the checkpoint operation before closing
+ /// the connection.
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006, // 3.16.2
- SQLITE_DBCONFIG_ENABLE_QPSG = 1007, // 3.20.0
- SQLITE_DBCONFIG_TRIGGER_EQP = 1008, // 3.22.0
+ /// Activates or deactivates the query planner stability guarantee (QPSG).
+ SQLITE_DBCONFIG_ENABLE_QPSG = 1007, // 3.20.0
+ /// Includes or excludes output for any operations performed by trigger
+ /// programs from the output of EXPLAIN QUERY PLAN commands.
+ SQLITE_DBCONFIG_TRIGGER_EQP = 1008, // 3.22.0
//SQLITE_DBCONFIG_RESET_DATABASE = 1009,
+ /// Activates or deactivates the "defensive" flag for a database connection.
SQLITE_DBCONFIG_DEFENSIVE = 1010, // 3.26.0
+ /// Activates or deactivates the "writable_schema" flag.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011, // 3.28.0
+ /// Activates or deactivates the legacy behavior of the ALTER TABLE RENAME
+ /// command.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012, // 3.29
+ /// Activates or deactivates the legacy double-quoted string literal
+ /// misfeature for DML statements only.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_DQS_DML = 1013, // 3.29.0
+ /// Activates or deactivates the legacy double-quoted string literal
+ /// misfeature for DDL statements.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_DQS_DDL = 1014, // 3.29.0
+ /// Enable or disable views.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_ENABLE_VIEW = 1015, // 3.30.0
+ /// Activates or deactivates the legacy file format flag.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_LEGACY_FILE_FORMAT = 1016, // 3.31.0
+ /// Tells SQLite to assume that database schemas (the contents of the
+ /// sqlite_master tables) are untainted by malicious content.
#[cfg(feature = "modern_sqlite")]
SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017, // 3.31.0
}
diff --git a/src/context.rs b/src/context.rs
index ad0a3ad..b7e8bc8 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -12,7 +12,7 @@ use crate::types::{ToSqlOutput, ValueRef};
#[cfg(feature = "array")]
use crate::vtab::array::{free_array, ARRAY_TYPE};
-pub(crate) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<'_>) {
+pub(super) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<'_>) {
let value = match *result {
ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
diff --git a/src/error.rs b/src/error.rs
index 35efaf1..c05f8cc 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -94,6 +94,7 @@ pub enum Error {
#[allow(dead_code)]
ModuleError(String),
+ /// An unwinding panic occurs in an UDF (user-defined function).
#[cfg(feature = "functions")]
UnwindingPanic,
diff --git a/src/functions.rs b/src/functions.rs
index b364f21..3531391 100644
--- a/src/functions.rs
+++ b/src/functions.rs
@@ -267,17 +267,26 @@ where
}
bitflags::bitflags! {
- #[doc = "Function Flags."]
- #[doc = "See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html) for details."]
+ /// Function Flags.
+ /// See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html)
+ /// and [Function Flags](https://sqlite.org/c3ref/c_deterministic.html) for details.
#[repr(C)]
pub struct FunctionFlags: ::std::os::raw::c_int {
+ /// Specifies UTF-8 as the text encoding this SQL function prefers for its parameters.
const SQLITE_UTF8 = ffi::SQLITE_UTF8;
+ /// Specifies UTF-16 using little-endian byte order as the text encoding this SQL function prefers for its parameters.
const SQLITE_UTF16LE = ffi::SQLITE_UTF16LE;
+ /// Specifies UTF-16 using big-endian byte order as the text encoding this SQL function prefers for its parameters.
const SQLITE_UTF16BE = ffi::SQLITE_UTF16BE;
+ /// Specifies UTF-16 using native byte order as the text encoding this SQL function prefers for its parameters.
const SQLITE_UTF16 = ffi::SQLITE_UTF16;
+ /// Means that the function always gives the same output when the input parameters are the same.
const SQLITE_DETERMINISTIC = ffi::SQLITE_DETERMINISTIC;
+ /// Means that the function may only be invoked from top-level SQL.
const SQLITE_DIRECTONLY = 0x0000_0008_0000; // 3.30.0
+ /// Indicates to SQLite that a function may call `sqlite3_value_subtype()` to inspect the sub-types of its arguments.
const SQLITE_SUBTYPE = 0x0000_0010_0000; // 3.30.0
+ /// Means that the function is unlikely to cause problems even if misused.
const SQLITE_INNOCUOUS = 0x0000_0020_0000; // 3.31.0
}
}
diff --git a/src/hooks.rs b/src/hooks.rs
index ed556f5..53dc041 100644
--- a/src/hooks.rs
+++ b/src/hooks.rs
@@ -14,9 +14,13 @@ use crate::{Connection, InnerConnection};
#[repr(i32)]
#[non_exhaustive]
pub enum Action {
+ /// Unsupported / unexpected action
UNKNOWN = -1,
+ /// DELETE command
SQLITE_DELETE = ffi::SQLITE_DELETE,
+ /// INSERT command
SQLITE_INSERT = ffi::SQLITE_INSERT,
+ /// UPDATE command
SQLITE_UPDATE = ffi::SQLITE_UPDATE,
}
diff --git a/src/inner_connection.rs b/src/inner_connection.rs
index 95b2d82..dd786fe 100644
--- a/src/inner_connection.rs
+++ b/src/inner_connection.rs
@@ -106,6 +106,10 @@ impl InnerConnection {
return Err(e);
}
+
+ // attempt to turn on extended results code; don't fail if we can't.
+ ffi::sqlite3_extended_result_codes(db, 1);
+
let r = ffi::sqlite3_busy_timeout(db, 5000);
if r != ffi::SQLITE_OK {
let e = error_from_handle(db, r);
@@ -113,9 +117,6 @@ impl InnerConnection {
return Err(e);
}
- // attempt to turn on extended results code; don't fail if we can't.
- ffi::sqlite3_extended_result_codes(db, 1);
-
Ok(InnerConnection::new(db, true))
}
}
@@ -170,21 +171,6 @@ impl InnerConnection {
}
}
- pub fn execute_batch(&mut self, sql: &str) -> Result<()> {
- // use CString instead of SmallCString because it's probably big.
- let c_sql = std::ffi::CString::new(sql)?;
- unsafe {
- let r = ffi::sqlite3_exec(
- self.db(),
- c_sql.as_ptr(),
- None,
- ptr::null_mut(),
- ptr::null_mut(),
- );
- self.decode_result(r)
- }
- }
-
#[cfg(feature = "load_extension")]
pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) };
@@ -261,8 +247,17 @@ impl InnerConnection {
// comment) then *ppStmt is set to NULL.
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
let c_tail: *const c_char = c_tail;
- // TODO ignore spaces, comments, ... at the end
- let tail = !c_tail.is_null() && unsafe { c_tail != c_sql.offset(len as isize) };
+ let tail = if c_tail.is_null() {
+ 0
+ } else {
+ // TODO nightly feature ptr_offset_from #41079
+ let n = (c_tail as isize) - (c_sql as isize);
+ if n <= 0 || n >= len as isize {
+ 0
+ } else {
+ n as usize
+ }
+ };
Ok(Statement::new(conn, unsafe {
RawStatement::new(c_stmt, tail)
}))
diff --git a/src/lib.rs b/src/lib.rs
index 70dd54e..53f1773 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,13 +3,11 @@
//!
//! ```rust
//! use rusqlite::{params, Connection, Result};
-//! use time::Timespec;
//!
//! #[derive(Debug)]
//! struct Person {
//! id: i32,
//! name: String,
-//! time_created: Timespec,
//! data: Option<Vec<u8>>,
//! }
//!
@@ -20,7 +18,6 @@
//! "CREATE TABLE person (
//! id INTEGER PRIMARY KEY,
//! name TEXT NOT NULL,
-//! time_created TEXT NOT NULL,
//! data BLOB
//! )",
//! params![],
@@ -28,22 +25,19 @@
//! let me = Person {
//! id: 0,
//! name: "Steven".to_string(),
-//! time_created: time::get_time(),
//! data: None,
//! };
//! conn.execute(
-//! "INSERT INTO person (name, time_created, data)
-//! VALUES (?1, ?2, ?3)",
-//! params![me.name, me.time_created, me.data],
+//! "INSERT INTO person (name, data) VALUES (?1, ?2)",
+//! params![me.name, me.data],
//! )?;
//!
-//! let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person")?;
+//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
//! let person_iter = stmt.query_map(params![], |row| {
//! Ok(Person {
//! id: row.get(0)?,
//! name: row.get(1)?,
-//! time_created: row.get(2)?,
-//! data: row.get(3)?,
+//! data: row.get(2)?,
//! })
//! })?;
//!
@@ -53,7 +47,7 @@
//! Ok(())
//! }
//! ```
-#![allow(unknown_lints)]
+#![warn(missing_docs)]
pub use libsqlite3_sys as ffi;
@@ -83,7 +77,7 @@ pub use crate::ffi::ErrorCode;
pub use crate::hooks::Action;
#[cfg(feature = "load_extension")]
pub use crate::load_extension_guard::LoadExtensionGuard;
-pub use crate::row::{AndThenRows, MappedRows, Row, RowIndex, Rows};
+pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
pub use crate::statement::{Statement, StatementStatus};
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
pub use crate::types::ToSql;
@@ -435,8 +429,6 @@ impl Connection {
/// Convenience method to run multiple SQL statements (that cannot take any
/// parameters).
///
- /// Uses [sqlite3_exec](http://www.sqlite.org/c3ref/exec.html) under the hood.
- ///
/// ## Example
///
/// ```rust,no_run
@@ -456,7 +448,20 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
/// or if the underlying SQLite call fails.
pub fn execute_batch(&self, sql: &str) -> Result<()> {
- self.db.borrow_mut().execute_batch(sql)
+ let mut sql = sql;
+ while !sql.is_empty() {
+ let stmt = self.prepare(sql)?;
+ if !stmt.stmt.is_null() && stmt.step()? && cfg!(feature = "extra_check") {
+ // Some PRAGMA may return rows
+ return Err(Error::ExecuteReturnedResults);
+ }
+ let tail = stmt.stmt.tail();
+ if tail == 0 || tail >= sql.len() {
+ break;
+ }
+ sql = &sql[tail..];
+ }
+ Ok(())
}
/// Convenience method to prepare and execute a single SQL statement.
@@ -806,19 +811,32 @@ impl fmt::Debug for Connection {
}
bitflags::bitflags! {
- #[doc = "Flags for opening SQLite database connections."]
- #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
+ /// Flags for opening SQLite database connections.
+ /// See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details.
#[repr(C)]
pub struct OpenFlags: ::std::os::raw::c_int {
+ /// The database is opened in read-only mode.
+ /// If the database does not already exist, an error is returned.
const SQLITE_OPEN_READ_ONLY = ffi::SQLITE_OPEN_READONLY;
+ /// The database is opened for reading and writing if possible,
+ /// or reading only if the file is write protected by the operating system.
+ /// In either case the database must already exist, otherwise an error is returned.
const SQLITE_OPEN_READ_WRITE = ffi::SQLITE_OPEN_READWRITE;
+ /// The database is created if it does not already exist
const SQLITE_OPEN_CREATE = ffi::SQLITE_OPEN_CREATE;
+ /// The filename can be interpreted as a URI if this flag is set.
const SQLITE_OPEN_URI = 0x0000_0040;
+ /// The database will be opened as an in-memory database.
const SQLITE_OPEN_MEMORY = 0x0000_0080;
+ /// The new database connection will use the "multi-thread" threading mode.
const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX;
+ /// The new database connection will use the "serialized" threading mode.
const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX;
+ /// The database is opened shared cache enabled.
const SQLITE_OPEN_SHARED_CACHE = 0x0002_0000;
+ /// The database is opened shared cache disabled.
const SQLITE_OPEN_PRIVATE_CACHE = 0x0004_0000;
+ /// The database filename is not allowed to be a symbolic link.
const SQLITE_OPEN_NOFOLLOW = 0x0100_0000;
}
}
diff --git a/src/pragma.rs b/src/pragma.rs
index b115f8f..4855154 100644
--- a/src/pragma.rs
+++ b/src/pragma.rs
@@ -430,4 +430,15 @@ mod test {
sql.push_string_literal("value'; --");
assert_eq!("'value''; --'", sql.as_str());
}
+
+ #[test]
+ fn locking_mode() {
+ let db = Connection::open_in_memory().unwrap();
+ let r = db.pragma_update(None, "locking_mode", &"exclusive");
+ if cfg!(feature = "extra_check") {
+ r.unwrap_err();
+ } else {
+ r.unwrap();
+ }
+ }
}
diff --git a/src/raw_statement.rs b/src/raw_statement.rs
index 893ac8b..c02dcd9 100644
--- a/src/raw_statement.rs
+++ b/src/raw_statement.rs
@@ -12,7 +12,7 @@ use std::sync::Arc;
#[derive(Debug)]
pub struct RawStatement {
ptr: *mut ffi::sqlite3_stmt,
- tail: bool,
+ tail: usize,
// Cached indices of named parameters, computed on the fly.
cache: crate::util::ParamIndexCache,
// Cached SQL (trimmed) that we use as the key when we're in the statement
@@ -29,7 +29,7 @@ pub struct RawStatement {
}
impl RawStatement {
- pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: bool) -> RawStatement {
+ pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt, tail: usize) -> RawStatement {
RawStatement {
ptr: stmt,
tail,
@@ -63,6 +63,7 @@ impl RawStatement {
unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) }
}
+ #[cfg(feature = "column_decltype")]
pub fn column_decltype(&self, idx: usize) -> Option<&CStr> {
unsafe {
let decltype = ffi::sqlite3_column_decltype(self.ptr, idx as c_int);
@@ -152,7 +153,7 @@ impl RawStatement {
r
}
- #[cfg(feature = "modern_sqlite")] // 3.7.4
+ #[cfg(all(feature = "extra_check", feature = "modern_sqlite"))] // 3.7.4
pub fn readonly(&self) -> bool {
unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
}
@@ -167,7 +168,12 @@ impl RawStatement {
unsafe { ffi::sqlite3_stmt_status(self.ptr, status as i32, reset as i32) }
}
+ #[cfg(feature = "extra_check")]
pub fn has_tail(&self) -> bool {
+ self.tail != 0
+ }
+
+ pub fn tail(&self) -> usize {
self.tail
}
}
diff --git a/src/row.rs b/src/row.rs
index c45f38e..3e536d9 100644
--- a/src/row.rs
+++ b/src/row.rs
@@ -35,6 +35,17 @@ impl<'stmt> Rows<'stmt> {
Ok((*self).get())
}
+ /// Map over this `Rows`, converting it to a [`Map`], which
+ /// implements `FallibleIterator`.
+ /// ```rust,no_run
+ /// use fallible_iterator::FallibleIterator;
+ /// # use rusqlite::{Result, Statement, NO_PARAMS};
+ /// fn query(stmt: &mut Statement) -> Result<Vec<i64>> {
+ /// let rows = stmt.query(NO_PARAMS)?;
+ /// rows.map(|r| r.get(0)).collect()
+ /// }
+ /// ```
+ // FIXME Hide FallibleStreamingIterator::map
pub fn map<F, B>(self, f: F) -> Map<'stmt, F>
where
F: FnMut(&Row<'_>) -> Result<B>,
@@ -84,6 +95,7 @@ impl Drop for Rows<'_> {
}
}
+/// `F` is used to tranform the _streaming_ iterator into a _fallible_ iterator.
pub struct Map<'stmt, F> {
rows: Rows<'stmt>,
f: F,
@@ -105,6 +117,8 @@ where
}
/// An iterator over the mapped resulting rows of a query.
+///
+/// `F` is used to tranform the _streaming_ iterator into a _standard_ iterator.
pub struct MappedRows<'stmt, F> {
rows: Rows<'stmt>,
map: F,
@@ -166,6 +180,24 @@ where
}
}
+/// `FallibleStreamingIterator` differs from the standard library's `Iterator`
+/// in two ways:
+/// * each call to `next` (sqlite3_step) can fail.
+/// * returned `Row` is valid until `next` is called again or `Statement` is
+/// reset or finalized.
+///
+/// While these iterators cannot be used with Rust `for` loops, `while let`
+/// loops offer a similar level of ergonomics:
+/// ```rust,no_run
+/// # use rusqlite::{Result, Statement, NO_PARAMS};
+/// fn query(stmt: &mut Statement) -> Result<()> {
+/// let mut rows = stmt.query(NO_PARAMS)?;
+/// while let Some(row) = rows.next()? {
+/// // scan columns value
+/// }
+/// Ok(())
+/// }
+/// ```
impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
type Error = Error;
type Item = Row<'stmt>;
@@ -334,3 +366,201 @@ impl RowIndex for &'_ str {
stmt.column_index(*self)
}
}
+
+macro_rules! tuple_try_from_row {
+ ($($field:ident),*) => {
+ impl<'a, $($field,)*> convert::TryFrom<&'a Row<'a>> for ($($field,)*) where $($field: FromSql,)* {
+ type Error = crate::Error;
+
+ // we end with index += 1, which rustc warns about
+ // unused_variables and unused_mut are allowed for ()
+ #[allow(unused_assignments, unused_variables, unused_mut)]
+ fn try_from(row: &'a Row<'a>) -> Result<Self> {
+ let mut index = 0;
+ $(
+ #[allow(non_snake_case)]
+ let $field = row.get::<_, $field>(index)?;
+ index += 1;
+ )*
+ Ok(($($field,)*))
+ }
+ }
+ }
+}
+
+macro_rules! tuples_try_from_row {
+ () => {
+ // not very useful, but maybe some other macro users will find this helpful
+ tuple_try_from_row!();
+ };
+ ($first:ident $(, $remaining:ident)*) => {
+ tuple_try_from_row!($first $(, $remaining)*);
+ tuples_try_from_row!($($remaining),*);
+ };
+}
+
+tuples_try_from_row!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
+
+#[cfg(test)]
+mod tests {
+ #![allow(clippy::redundant_closure)] // false positives due to lifetime issues; clippy issue #5594
+
+ #[test]
+ fn test_try_from_row_for_tuple_1() {
+ use crate::{Connection, ToSql};
+ use std::convert::TryFrom;
+
+ let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
+ conn.execute(
+ "CREATE TABLE test (a INTEGER)",
+ std::iter::empty::<&dyn ToSql>(),
+ )
+ .expect("failed to create table");
+ conn.execute(
+ "INSERT INTO test VALUES (42)",
+ std::iter::empty::<&dyn ToSql>(),
+ )
+ .expect("failed to insert value");
+ let val = conn
+ .query_row(
+ "SELECT a FROM test",
+ std::iter::empty::<&dyn ToSql>(),
+ |row| <(u32,)>::try_from(row),
+ )
+ .expect("failed to query row");
+ assert_eq!(val, (42,));
+ let fail = conn.query_row(
+ "SELECT a FROM test",
+ std::iter::empty::<&dyn ToSql>(),
+ |row| <(u32, u32)>::try_from(row),
+ );
+ assert!(fail.is_err());
+ }
+
+ #[test]
+ fn test_try_from_row_for_tuple_2() {
+ use crate::{Connection, ToSql};
+ use std::convert::TryFrom;
+
+ let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
+ conn.execute(
+ "CREATE TABLE test (a INTEGER, b INTEGER)",
+ std::iter::empty::<&dyn ToSql>(),
+ )
+ .expect("failed to create table");
+ conn.execute(
+ "INSERT INTO test VALUES (42, 47)",
+ std::iter::empty::<&dyn ToSql>(),
+ )
+ .expect("failed to insert value");
+ let val = conn
+ .query_row(
+ "SELECT a, b FROM test",
+ std::iter::empty::<&dyn ToSql>(),
+ |row| <(u32, u32)>::try_from(row),
+ )
+ .expect("failed to query row");
+ assert_eq!(val, (42, 47));
+ let fail = conn.query_row(
+ "SELECT a, b FROM test",
+ std::iter::empty::<&dyn ToSql>(),
+ |row| <(u32, u32, u32)>::try_from(row),
+ );
+ assert!(fail.is_err());
+ }
+
+ #[test]
+ fn test_try_from_row_for_tuple_16() {
+ use crate::{Connection, ToSql};
+ use std::convert::TryFrom;
+
+ let create_table = "CREATE TABLE test (
+ a INTEGER,
+ b INTEGER,
+ c INTEGER,
+ d INTEGER,
+ e INTEGER,
+ f INTEGER,
+ g INTEGER,
+ h INTEGER,
+ i INTEGER,
+ j INTEGER,
+ k INTEGER,
+ l INTEGER,
+ m INTEGER,
+ n INTEGER,
+ o INTEGER,
+ p INTEGER
+ )";
+
+ let insert_values = "INSERT INTO test VALUES (
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15
+ )";
+
+ type BigTuple = (
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ u32,
+ );
+
+ let conn = Connection::open_in_memory().expect("failed to create in-memoory database");
+ conn.execute(create_table, std::iter::empty::<&dyn ToSql>())
+ .expect("failed to create table");
+ conn.execute(insert_values, std::iter::empty::<&dyn ToSql>())
+ .expect("failed to insert value");
+ let val = conn
+ .query_row(
+ "SELECT * FROM test",
+ std::iter::empty::<&dyn ToSql>(),
+ |row| BigTuple::try_from(row),
+ )
+ .expect("failed to query row");
+ // Debug is not implemented for tuples of 16
+ assert_eq!(val.0, 0);
+ assert_eq!(val.1, 1);
+ assert_eq!(val.2, 2);
+ assert_eq!(val.3, 3);
+ assert_eq!(val.4, 4);
+ assert_eq!(val.5, 5);
+ assert_eq!(val.6, 6);
+ assert_eq!(val.7, 7);
+ assert_eq!(val.8, 8);
+ assert_eq!(val.9, 9);
+ assert_eq!(val.10, 10);
+ assert_eq!(val.11, 11);
+ assert_eq!(val.12, 12);
+ assert_eq!(val.13, 13);
+ assert_eq!(val.14, 14);
+ assert_eq!(val.15, 15);
+
+ // We don't test one bigger because it's unimplemented
+ }
+}
diff --git a/src/session.rs b/src/session.rs
index b4782b2..97ae3a5 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -22,7 +22,7 @@ use crate::{errmsg_to_string, str_to_cstring, Connection, DatabaseName, Result};
/// `feature = "session"` An instance of this object is a session that can be
/// used to record changes to a database.
pub struct Session<'conn> {
- phantom: PhantomData<&'conn ()>,
+ phantom: PhantomData<&'conn Connection>,
s: *mut ffi::sqlite3_session,
filter: Option<Box<dyn Fn(&str) -> bool>>,
}
@@ -299,7 +299,7 @@ impl Drop for Changeset {
/// `feature = "session"` Cursor for iterating over the elements of a changeset
/// or patchset.
pub struct ChangesetIter<'changeset> {
- phantom: PhantomData<&'changeset ()>,
+ phantom: PhantomData<&'changeset Changeset>,
it: *mut ffi::sqlite3_changeset_iter,
item: Option<ChangesetItem>,
}
@@ -356,18 +356,22 @@ pub struct Operation<'item> {
}
impl Operation<'_> {
+ /// Returns the table name.
pub fn table_name(&self) -> &str {
self.table_name
}
+ /// Returns the number of columns in table
pub fn number_of_columns(&self) -> i32 {
self.number_of_columns
}
+ /// Returns the action code.
pub fn code(&self) -> Action {
self.code
}
+ /// Returns `true` for an 'indirect' change.
pub fn indirect(&self) -> bool {
self.indirect
}
@@ -488,6 +492,7 @@ pub struct Changegroup {
}
impl Changegroup {
+ /// Create a new change group.
pub fn new() -> Result<Self> {
let mut cg = ptr::null_mut();
check!(unsafe { ffi::sqlite3changegroup_new(&mut cg) });
@@ -620,6 +625,8 @@ impl Connection {
}
/// `feature = "session"` Constants passed to the conflict handler
+/// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_CONFLICT) for details.
+#[allow(missing_docs)]
#[repr(i32)]
#[derive(Debug, PartialEq)]
#[non_exhaustive]
@@ -645,6 +652,8 @@ impl From<i32> for ConflictType {
}
/// `feature = "session"` Constants returned by the conflict handler
+/// See [here](https://sqlite.org/session.html#SQLITE_CHANGESET_ABORT) for details.
+#[allow(missing_docs)]
#[repr(i32)]
#[derive(Debug, PartialEq)]
#[non_exhaustive]
diff --git a/src/statement.rs b/src/statement.rs
index cd05ef6..648a9b7 100644
--- a/src/statement.rs
+++ b/src/statement.rs
@@ -219,6 +219,8 @@ impl Statement<'_> {
/// Ok(names)
/// }
/// ```
+ /// `f` is used to tranform the _streaming_ iterator into a _standard_
+ /// iterator.
///
/// ## Failure
///
@@ -256,6 +258,8 @@ impl Statement<'_> {
/// Ok(names)
/// }
/// ```
+ /// `f` is used to tranform the _streaming_ iterator into a _standard_
+ /// iterator.
///
/// ## Failure
///
@@ -450,7 +454,7 @@ impl Statement<'_> {
self.bind_parameter(&p, index)?;
}
if index != expected {
- Err(Error::InvalidParameterCount(expected, index))
+ Err(Error::InvalidParameterCount(index, expected))
} else {
Ok(())
}
@@ -618,7 +622,7 @@ impl Statement<'_> {
}
fn finalize_(&mut self) -> Result<()> {
- let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), false) };
+ let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), 0) };
mem::swap(&mut stmt, &mut self.stmt);
self.conn.decode_result(stmt.finalize())
}
@@ -642,7 +646,7 @@ impl Statement<'_> {
#[inline]
fn check_update(&self) -> Result<()> {
// sqlite3_column_count works for DML but not for DDL (ie ALTER)
- if self.column_count() > 0 || self.stmt.readonly() {
+ if self.column_count() > 0 && self.stmt.readonly() {
return Err(Error::ExecuteReturnedResults);
}
Ok(())
@@ -698,11 +702,12 @@ impl Statement<'_> {
pub(crate) fn check_no_tail(&self) -> Result<()> {
Ok(())
}
-}
-impl Into<RawStatement> for Statement<'_> {
- fn into(mut self) -> RawStatement {
- let mut stmt = unsafe { RawStatement::new(ptr::null_mut(), false) };
+ /// Safety: This is unsafe, because using `sqlite3_stmt` after the
+ /// connection has closed is illegal, but `RawStatement` does not enforce
+ /// this, as it loses our protective `'conn` lifetime bound.
+ pub(crate) unsafe fn into_raw(mut self) -> RawStatement {
+ let mut stmt = RawStatement::new(ptr::null_mut(), 0);
mem::swap(&mut stmt, &mut self.stmt);
stmt
}
@@ -731,11 +736,11 @@ impl Drop for Statement<'_> {
}
impl Statement<'_> {
- pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> {
+ pub(super) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> {
Statement { conn, stmt }
}
- pub(crate) fn value_ref(&self, col: usize) -> ValueRef<'_> {
+ pub(super) fn value_ref(&self, col: usize) -> ValueRef<'_> {
let raw = unsafe { self.stmt.ptr() };
match self.stmt.column_type(col) {
@@ -791,7 +796,7 @@ impl Statement<'_> {
}
}
- pub(crate) fn step(&self) -> Result<bool> {
+ pub(super) fn step(&self) -> Result<bool> {
match self.stmt.step() {
ffi::SQLITE_ROW => Ok(true),
ffi::SQLITE_DONE => Ok(false),
@@ -799,7 +804,7 @@ impl Statement<'_> {
}
}
- pub(crate) fn reset(&self) -> c_int {
+ pub(super) fn reset(&self) -> c_int {
self.stmt.reset()
}
}
diff --git a/src/trace.rs b/src/trace.rs
index 39ef69f..76e0969 100644
--- a/src/trace.rs
+++ b/src/trace.rs
@@ -35,14 +35,11 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
}
let rc = match callback {
- Some(f) => {
- let p_arg: *mut c_void = mem::transmute(f);
- ffi::sqlite3_config(
- ffi::SQLITE_CONFIG_LOG,
- log_callback as extern "C" fn(_, _, _),
- p_arg,
- )
- }
+ Some(f) => ffi::sqlite3_config(
+ ffi::SQLITE_CONFIG_LOG,
+ log_callback as extern "C" fn(_, _, _),
+ f as *mut c_void,
+ ),
None => {
let nullptr: *mut c_void = ptr::null_mut();
ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr)
@@ -83,7 +80,7 @@ impl Connection {
let c = self.db.borrow_mut();
match trace_fn {
Some(f) => unsafe {
- ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f));
+ ffi::sqlite3_trace(c.db(), Some(trace_callback), f as *mut c_void);
},
None => unsafe {
ffi::sqlite3_trace(c.db(), None, ptr::null_mut());
@@ -117,7 +114,7 @@ impl Connection {
let c = self.db.borrow_mut();
match profile_fn {
Some(f) => unsafe {
- ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f))
+ ffi::sqlite3_profile(c.db(), Some(profile_callback), f as *mut c_void)
},
None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
};
diff --git a/src/transaction.rs b/src/transaction.rs
index e1b806e..5e649b7 100644
--- a/src/transaction.rs
+++ b/src/transaction.rs
@@ -6,8 +6,14 @@ use std::ops::Deref;
#[derive(Copy, Clone)]
#[non_exhaustive]
pub enum TransactionBehavior {
+ /// DEFERRED means that the transaction does not actually start until the
+ /// database is first accessed.
Deferred,
+ /// IMMEDIATE cause the database connection to start a new write
+ /// immediately, without waiting for a writes statement.
Immediate,
+ /// EXCLUSIVE prevents other database connections from reading the database
+ /// while the transaction is underway.
Exclusive,
}
diff --git a/src/types/from_sql.rs b/src/types/from_sql.rs
index e8eadc5..3fe74b4 100644
--- a/src/types/from_sql.rs
+++ b/src/types/from_sql.rs
@@ -86,6 +86,7 @@ pub type FromSqlResult<T> = Result<T, FromSqlError>;
/// fetching values as i64 and then doing the interpretation themselves or by
/// defining a newtype and implementing `FromSql`/`ToSql` for it.
pub trait FromSql: Sized {
+ /// Converts SQLite value into Rust value.
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
}
@@ -142,10 +143,7 @@ impl FromSql for f64 {
impl FromSql for bool {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
- i64::column_result(value).map(|i| match i {
- 0 => false,
- _ => true,
- })
+ i64::column_result(value).map(|i| !matches!(i, 0))
}
}
diff --git a/src/types/mod.rs b/src/types/mod.rs
index d79ff82..2d163cf 100644
--- a/src/types/mod.rs
+++ b/src/types/mod.rs
@@ -10,40 +10,39 @@
//! * Strings (`String` and `&str`)
//! * Blobs (`Vec<u8>` and `&[u8]`)
//!
-//! Additionally, because it is such a common data type, implementations are
-//! provided for `time::Timespec` that use the RFC 3339 date/time format,
+//! Additionally, if the `time` feature is enabled, implementations are
+//! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format,
//! `"%Y-%m-%dT%H:%M:%S.%fZ"`, to store time values as strings. These values
//! can be parsed by SQLite's builtin
//! [datetime](https://www.sqlite.org/lang_datefunc.html) functions. If you
-//! want different storage for timespecs, you can use a newtype. For example, to
-//! store timespecs as `f64`s:
-//!
-//! ```rust
-//! use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
-//! use rusqlite::Result;
-//!
-//! pub struct TimespecSql(pub time::Timespec);
-//!
-//! impl FromSql for TimespecSql {
-//! fn column_result(value: ValueRef) -> FromSqlResult<Self> {
-//! f64::column_result(value).map(|as_f64| {
-//! TimespecSql(time::Timespec {
-//! sec: as_f64.trunc() as i64,
-//! nsec: (as_f64.fract() * 1.0e9) as i32,
-//! })
-//! })
-//! }
-//! }
-//!
-//! impl ToSql for TimespecSql {
-//! fn to_sql(&self) -> Result<ToSqlOutput> {
-//! let TimespecSql(ts) = *self;
-//! let as_f64 = ts.sec as f64 + (ts.nsec as f64) / 1.0e9;
-//! Ok(as_f64.into())
-//! }
-//! }
-//! ```
+//! want different storage for datetimes, you can use a newtype.
//!
+#![cfg_attr(feature = "time", doc = r##"
+For example, to store datetimes as `i64`s counting the number of seconds since
+the Unix epoch:
+
+```
+use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
+use rusqlite::Result;
+
+pub struct DateTimeSql(pub time::OffsetDateTime);
+
+impl FromSql for DateTimeSql {
+ fn column_result(value: ValueRef) -> FromSqlResult<Self> {
+ i64::column_result(value).map(|as_i64| {
+ DateTimeSql(time::OffsetDateTime::from_unix_timestamp(as_i64))
+ })
+ }
+}
+
+impl ToSql for DateTimeSql {
+ fn to_sql(&self) -> Result<ToSqlOutput> {
+ Ok(self.0.timestamp().into())
+ }
+}
+```
+
+"##)]
//! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T`
//! implements `ToSql` or `FromSql` for the cases where you want to know if a
//! value was NULL (which gets translated to `None`).
@@ -60,6 +59,7 @@ mod chrono;
mod from_sql;
#[cfg(feature = "serde_json")]
mod serde_json;
+#[cfg(feature = "time")]
mod time;
mod to_sql;
#[cfg(feature = "url")]
@@ -82,12 +82,19 @@ mod value_ref;
#[derive(Copy, Clone)]
pub struct Null;
+/// SQLite data types.
+/// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
+ /// NULL
Null,
+ /// 64-bit signed integer
Integer,
+ /// 64-bit IEEE floating point number
Real,
+ /// String
Text,
+ /// BLOB
Blob,
}
@@ -266,8 +273,9 @@ mod test {
assert!(is_invalid_column_type(
row.get::<_, String>(0).err().unwrap()
));
+ #[cfg(feature = "time")]
assert!(is_invalid_column_type(
- row.get::<_, time::Timespec>(0).err().unwrap()
+ row.get::<_, time::OffsetDateTime>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get::<_, Option<c_int>>(0).err().unwrap()
@@ -328,8 +336,9 @@ mod test {
assert!(is_invalid_column_type(
row.get::<_, Vec<u8>>(4).err().unwrap()
));
+ #[cfg(feature = "time")]
assert!(is_invalid_column_type(
- row.get::<_, time::Timespec>(4).err().unwrap()
+ row.get::<_, time::OffsetDateTime>(4).err().unwrap()
));
}
diff --git a/src/types/time.rs b/src/types/time.rs
index 097b22a..8589167 100644
--- a/src/types/time.rs
+++ b/src/types/time.rs
@@ -1,40 +1,40 @@
+//! `ToSql` and `FromSql` implementation for [`time::OffsetDateTime`].
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use crate::Result;
+use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
const CURRENT_TIMESTAMP_FMT: &str = "%Y-%m-%d %H:%M:%S";
-const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
-const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
+const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%NZ";
+const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%N %z";
-impl ToSql for time::Timespec {
+impl ToSql for OffsetDateTime {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
- let time_string = time::at_utc(*self)
- .strftime(SQLITE_DATETIME_FMT)
- .unwrap()
- .to_string();
+ let time_string = self.to_offset(UtcOffset::UTC).format(SQLITE_DATETIME_FMT);
Ok(ToSqlOutput::from(time_string))
}
}
-impl FromSql for time::Timespec {
+impl FromSql for OffsetDateTime {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
- value
- .as_str()
- .and_then(|s| {
- match s.len() {
- 19 => time::strptime(s, CURRENT_TIMESTAMP_FMT),
- _ => time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| {
- time::strptime(s, SQLITE_DATETIME_FMT_LEGACY).or_else(|_| Err(err))
+ value.as_str().and_then(|s| {
+ match s.len() {
+ 19 => PrimitiveDateTime::parse(s, CURRENT_TIMESTAMP_FMT).map(|d| d.assume_utc()),
+ _ => PrimitiveDateTime::parse(s, SQLITE_DATETIME_FMT)
+ .map(|d| d.assume_utc())
+ .or_else(|err| {
+ OffsetDateTime::parse(s, SQLITE_DATETIME_FMT_LEGACY).map_err(|_| err)
}),
- }
- .or_else(|err| Err(FromSqlError::Other(Box::new(err))))
- })
- .map(|tm| tm.to_timespec())
+ }
+ .map_err(|err| FromSqlError::Other(Box::new(err)))
+ })
}
}
#[cfg(test)]
mod test {
use crate::{Connection, Result, NO_PARAMS};
+ use std::time::Duration;
+ use time::OffsetDateTime;
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@@ -44,22 +44,25 @@ mod test {
}
#[test]
- fn test_timespec() {
+ fn test_offset_date_time() {
let db = checked_memory_handle();
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(1_500_391_124, 1_000_000)); //July 18, 2017
- ts_vec.push(time::Timespec::new(2_000_000_000, 2_000_000)); //May 18, 2033
- ts_vec.push(time::Timespec::new(3_000_000_000, 999_999_999)); //January 24, 2065
- ts_vec.push(time::Timespec::new(10_000_000_000, 0)); //November 20, 2286
+ let make_datetime =
+ |secs, nanos| OffsetDateTime::from_unix_timestamp(secs) + Duration::from_nanos(nanos);
+
+ ts_vec.push(make_datetime(10_000, 0)); //January 1, 1970 2:46:40 AM
+ ts_vec.push(make_datetime(10_000, 1000)); //January 1, 1970 2:46:40 AM (and one microsecond)
+ ts_vec.push(make_datetime(1_500_391_124, 1_000_000)); //July 18, 2017
+ ts_vec.push(make_datetime(2_000_000_000, 2_000_000)); //May 18, 2033
+ ts_vec.push(make_datetime(3_000_000_000, 999_999_999)); //January 24, 2065
+ ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286
for ts in ts_vec {
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
- let from: time::Timespec = db
+ let from: OffsetDateTime = db
.query_row("SELECT t FROM foo", NO_PARAMS, |r| r.get(0))
.unwrap();
@@ -72,7 +75,7 @@ mod test {
#[test]
fn test_sqlite_functions() {
let db = checked_memory_handle();
- let result: Result<time::Timespec> =
+ let result: Result<OffsetDateTime> =
db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
}
diff --git a/src/types/to_sql.rs b/src/types/to_sql.rs
index bec53f8..937c0f8 100644
--- a/src/types/to_sql.rs
+++ b/src/types/to_sql.rs
@@ -87,6 +87,7 @@ impl ToSql for ToSqlOutput<'_> {
/// A trait for types that can be converted into SQLite values.
pub trait ToSql {
+ /// Converts Rust value to SQLite value
fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
}
diff --git a/src/types/value.rs b/src/types/value.rs
index 332d78b..64dc203 100644
--- a/src/types/value.rs
+++ b/src/types/value.rs
@@ -109,6 +109,7 @@ where
}
impl Value {
+ /// Returns SQLite fundamental datatype.
pub fn data_type(&self) -> Type {
match *self {
Value::Null => Type::Null,
diff --git a/src/types/value_ref.rs b/src/types/value_ref.rs
index 80d2457..2f32434 100644
--- a/src/types/value_ref.rs
+++ b/src/types/value_ref.rs
@@ -20,6 +20,7 @@ pub enum ValueRef<'a> {
}
impl ValueRef<'_> {
+ /// Returns SQLite fundamental datatype.
pub fn data_type(&self) -> Type {
match *self {
ValueRef::Null => Type::Null,
diff --git a/src/vtab/array.rs b/src/vtab/array.rs
index 1ade815..644b468 100644
--- a/src/vtab/array.rs
+++ b/src/vtab/array.rs
@@ -27,6 +27,7 @@
//! ```
use std::default::Default;
+use std::marker::PhantomData;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc;
@@ -46,6 +47,7 @@ pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
let _: Array = Rc::from_raw(p as *const Vec<Value>);
}
+/// Array parameter / pointer
pub type Array = Rc<Vec<Value>>;
impl ToSql for Array {
@@ -71,9 +73,9 @@ struct ArrayTab {
base: ffi::sqlite3_vtab,
}
-unsafe impl VTab for ArrayTab {
+unsafe impl<'vtab> VTab<'vtab> for ArrayTab {
type Aux = ();
- type Cursor = ArrayTabCursor;
+ type Cursor = ArrayTabCursor<'vtab>;
fn connect(
_: &mut VTabConnection,
@@ -117,28 +119,30 @@ unsafe impl VTab for ArrayTab {
Ok(())
}
- fn open(&self) -> Result<ArrayTabCursor> {
+ fn open(&self) -> Result<ArrayTabCursor<'_>> {
Ok(ArrayTabCursor::new())
}
}
/// A cursor for the Array virtual table
#[repr(C)]
-struct ArrayTabCursor {
+struct ArrayTabCursor<'vtab> {
/// Base class. Must be first
base: ffi::sqlite3_vtab_cursor,
/// The rowid
row_id: i64,
/// Pointer to the array of values ("pointer")
ptr: Option<Array>,
+ phantom: PhantomData<&'vtab ArrayTab>,
}
-impl ArrayTabCursor {
- fn new() -> ArrayTabCursor {
+impl ArrayTabCursor<'_> {
+ fn new<'vtab>() -> ArrayTabCursor<'vtab> {
ArrayTabCursor {
base: ffi::sqlite3_vtab_cursor::default(),
row_id: 0,
ptr: None,
+ phantom: PhantomData,
}
}
@@ -149,7 +153,7 @@ impl ArrayTabCursor {
}
}
}
-unsafe impl VTabCursor for ArrayTabCursor {
+unsafe impl VTabCursor for ArrayTabCursor<'_> {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
if idx_num > 0 {
self.ptr = args.get_array(0)?;
diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs
index 0d8a4c5..79ec5da 100644
--- a/src/vtab/csvtab.rs
+++ b/src/vtab/csvtab.rs
@@ -22,6 +22,7 @@
//! }
//! ```
use std::fs::File;
+use std::marker::PhantomData;
use std::os::raw::c_int;
use std::path::Path;
use std::str;
@@ -95,9 +96,9 @@ impl CSVTab {
}
}
-unsafe impl VTab for CSVTab {
+unsafe impl<'vtab> VTab<'vtab> for CSVTab {
type Aux = ();
- type Cursor = CSVTabCursor;
+ type Cursor = CSVTabCursor<'vtab>;
fn connect(
_: &mut VTabConnection,
@@ -258,16 +259,16 @@ unsafe impl VTab for CSVTab {
Ok(())
}
- fn open(&self) -> Result<CSVTabCursor> {
+ fn open(&self) -> Result<CSVTabCursor<'_>> {
Ok(CSVTabCursor::new(self.reader()?))
}
}
-impl CreateVTab for CSVTab {}
+impl CreateVTab<'_> for CSVTab {}
/// A cursor for the CSV virtual table
#[repr(C)]
-struct CSVTabCursor {
+struct CSVTabCursor<'vtab> {
/// Base class. Must be first
base: ffi::sqlite3_vtab_cursor,
/// The CSV reader object
@@ -277,16 +278,18 @@ struct CSVTabCursor {
/// Values of the current row
cols: csv::StringRecord,
eof: bool,
+ phantom: PhantomData<&'vtab CSVTab>,
}
-impl CSVTabCursor {
- fn new(reader: csv::Reader<File>) -> CSVTabCursor {
+impl CSVTabCursor<'_> {
+ fn new<'vtab>(reader: csv::Reader<File>) -> CSVTabCursor<'vtab> {
CSVTabCursor {
base: ffi::sqlite3_vtab_cursor::default(),
reader,
row_number: 0,
cols: csv::StringRecord::new(),
eof: false,
+ phantom: PhantomData,
}
}
@@ -296,7 +299,7 @@ impl CSVTabCursor {
}
}
-unsafe impl VTabCursor for CSVTabCursor {
+unsafe impl VTabCursor for CSVTabCursor<'_> {
// Only a full table scan is supported. So `filter` simply rewinds to
// the beginning.
fn filter(
diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs
index 5b47c65..dc3bda6 100644
--- a/src/vtab/mod.rs
+++ b/src/vtab/mod.rs
@@ -61,13 +61,13 @@ use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
///
/// (See [SQLite doc](https://sqlite.org/c3ref/module.html))
#[repr(transparent)]
-pub struct Module<T: VTab> {
+pub struct Module<'vtab, T: VTab<'vtab>> {
base: ffi::sqlite3_module,
- phantom: PhantomData<T>,
+ phantom: PhantomData<&'vtab T>,
}
-unsafe impl<T: VTab> Send for Module<T> {}
-unsafe impl<T: VTab> Sync for Module<T> {}
+unsafe impl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {}
+unsafe impl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
union ModuleZeroHack {
bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()],
@@ -87,7 +87,7 @@ const ZERO_MODULE: ffi::sqlite3_module = unsafe {
/// `feature = "vtab"` Create a read-only virtual table implementation.
///
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
-pub fn read_only_module<T: CreateVTab>() -> &'static Module<T> {
+pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> {
// The xConnect and xCreate methods do the same thing, but they must be
// different so that the virtual table is not an eponymous virtual table.
&Module {
@@ -118,14 +118,14 @@ pub fn read_only_module<T: CreateVTab>() -> &'static Module<T> {
xRollbackTo: None,
..ZERO_MODULE
},
- phantom: PhantomData::<T>,
+ phantom: PhantomData::<&'vtab T>,
}
}
/// `feature = "vtab"` Create an eponymous only virtual table implementation.
///
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
-pub fn eponymous_only_module<T: VTab>() -> &'static Module<T> {
+pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> {
// A virtual table is eponymous if its xCreate method is the exact same function
// as the xConnect method For eponymous-only virtual tables, the xCreate
// method is NULL
@@ -157,7 +157,7 @@ pub fn eponymous_only_module<T: VTab>() -> &'static Module<T> {
xRollbackTo: None,
..ZERO_MODULE
},
- phantom: PhantomData::<T>,
+ phantom: PhantomData::<&'vtab T>,
}
}
@@ -204,8 +204,10 @@ impl VTabConnection {
/// ```
///
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
-pub unsafe trait VTab: Sized {
+pub unsafe trait VTab<'vtab>: Sized {
+ /// Client data passed to `Connection::create_module`.
type Aux;
+ /// Specific cursor implementation
type Cursor: VTabCursor;
/// Establish a new connection to an existing virtual table.
@@ -223,13 +225,13 @@ pub unsafe trait VTab: Sized {
/// Create a new cursor used for accessing a virtual table.
/// (See [SQLite doc](https://sqlite.org/vtab.html#the_xopen_method))
- fn open(&self) -> Result<Self::Cursor>;
+ fn open(&'vtab self) -> Result<Self::Cursor>;
}
/// `feature = "vtab"` Non-eponymous virtual table instance trait.
///
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
-pub trait CreateVTab: VTab {
+pub trait CreateVTab<'vtab>: VTab<'vtab> {
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL
/// TABLE statement. The `db` parameter is a pointer to the SQLite
/// database connection that is executing the CREATE VIRTUAL TABLE
@@ -256,8 +258,9 @@ pub trait CreateVTab: VTab {
}
/// `feature = "vtab"` Index constraint operator.
+/// See [Virtual Table Constraint Operator Codes](https://sqlite.org/c3ref/c_index_constraint_eq.html) for details.
#[derive(Debug, PartialEq)]
-#[allow(non_snake_case, non_camel_case_types)]
+#[allow(non_snake_case, non_camel_case_types, missing_docs)]
#[non_exhaustive]
pub enum IndexConstraintOp {
SQLITE_INDEX_CONSTRAINT_EQ,
@@ -329,6 +332,7 @@ impl IndexInfo {
unsafe { (*self.0).nOrderBy as usize }
}
+ /// Information about what parameters to pass to `VTabCursor.filter`.
pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_> {
let constraint_usages = unsafe {
slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
@@ -495,6 +499,7 @@ pub unsafe trait VTabCursor: Sized {
pub struct Context(*mut ffi::sqlite3_context);
impl Context {
+ /// Set current cell value
pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
let t = value.to_sql()?;
unsafe { set_result(self.0, &t) };
@@ -511,14 +516,17 @@ pub struct Values<'a> {
}
impl Values<'_> {
+ /// Returns the number of values.
pub fn len(&self) -> usize {
self.args.len()
}
+ /// Returns `true` if there is no value.
pub fn is_empty(&self) -> bool {
self.args.is_empty()
}
+ /// Returns value at `idx`
pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) };
@@ -542,7 +550,7 @@ impl Values<'_> {
// `sqlite3_value_type` returns `SQLITE_NULL` for pointer.
// So it seems not possible to enhance `ValueRef::from_value`.
#[cfg(feature = "array")]
- pub(crate) fn get_array(&self, idx: usize) -> Result<Option<array::Array>> {
+ fn get_array(&self, idx: usize) -> Result<Option<array::Array>> {
use crate::types::Value;
let arg = self.args[idx];
let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
@@ -558,6 +566,7 @@ impl Values<'_> {
}
}
+ /// Turns `Values` into an iterator.
pub fn iter(&self) -> ValueIter<'_> {
ValueIter {
iter: self.args.iter(),
@@ -574,6 +583,7 @@ impl<'a> IntoIterator for &'a Values<'a> {
}
}
+/// `Values` iterator.
pub struct ValueIter<'a> {
iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
}
@@ -597,10 +607,10 @@ impl Connection {
///
/// Step 3 of [Creating New Virtual Table
/// Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
- pub fn create_module<T: VTab>(
+ pub fn create_module<'vtab, T: VTab<'vtab>>(
&self,
module_name: &str,
- module: &'static Module<T>,
+ module: &'static Module<'vtab, T>,
aux: Option<T::Aux>,
) -> Result<()> {
self.db.borrow_mut().create_module(module_name, module, aux)
@@ -608,10 +618,10 @@ impl Connection {
}
impl InnerConnection {
- fn create_module<T: VTab>(
+ fn create_module<'vtab, T: VTab<'vtab>>(
&mut self,
module_name: &str,
- module: &'static Module<T>,
+ module: &'static Module<'vtab, T>,
aux: Option<T::Aux>,
) -> Result<()> {
let c_name = str_to_cstring(module_name)?;
@@ -693,7 +703,7 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(p as *mut T);
}
-unsafe extern "C" fn rust_create<T>(
+unsafe extern "C" fn rust_create<'vtab, T>(
db: *mut ffi::sqlite3,
aux: *mut c_void,
argc: c_int,
@@ -702,7 +712,7 @@ unsafe extern "C" fn rust_create<T>(
err_msg: *mut *mut c_char,
) -> c_int
where
- T: CreateVTab,
+ T: CreateVTab<'vtab>,
{
use std::ffi::CStr;
@@ -745,7 +755,7 @@ where
}
}
-unsafe extern "C" fn rust_connect<T>(
+unsafe extern "C" fn rust_connect<'vtab, T>(
db: *mut ffi::sqlite3,
aux: *mut c_void,
argc: c_int,
@@ -754,7 +764,7 @@ unsafe extern "C" fn rust_connect<T>(
err_msg: *mut *mut c_char,
) -> c_int
where
- T: VTab,
+ T: VTab<'vtab>,
{
use std::ffi::CStr;
@@ -797,12 +807,12 @@ where
}
}
-unsafe extern "C" fn rust_best_index<T>(
+unsafe extern "C" fn rust_best_index<'vtab, T>(
vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info,
) -> c_int
where
- T: VTab,
+ T: VTab<'vtab>,
{
let vt = vtab as *mut T;
let mut idx_info = IndexInfo(info);
@@ -821,9 +831,9 @@ where
}
}
-unsafe extern "C" fn rust_disconnect<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
+unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
where
- T: VTab,
+ T: VTab<'vtab>,
{
if vtab.is_null() {
return ffi::SQLITE_OK;
@@ -833,9 +843,9 @@ where
ffi::SQLITE_OK
}
-unsafe extern "C" fn rust_destroy<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
+unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
where
- T: CreateVTab,
+ T: CreateVTab<'vtab>,
{
if vtab.is_null() {
return ffi::SQLITE_OK;
@@ -859,12 +869,12 @@ where
}
}
-unsafe extern "C" fn rust_open<T>(
+unsafe extern "C" fn rust_open<'vtab, T: 'vtab>(
vtab: *mut ffi::sqlite3_vtab,
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
) -> c_int
where
- T: VTab,
+ T: VTab<'vtab>,
{
let vt = vtab as *mut T;
match (*vt).open() {
diff --git a/src/vtab/series.rs b/src/vtab/series.rs
index dfc8e69..ed67f16 100644
--- a/src/vtab/series.rs
+++ b/src/vtab/series.rs
@@ -4,6 +4,7 @@
//! "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c):
//! https://www.sqlite.org/series.html
use std::default::Default;
+use std::marker::PhantomData;
use std::os::raw::c_int;
use crate::ffi;
@@ -49,9 +50,9 @@ struct SeriesTab {
base: ffi::sqlite3_vtab,
}
-unsafe impl VTab for SeriesTab {
+unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
type Aux = ();
- type Cursor = SeriesTabCursor;
+ type Cursor = SeriesTabCursor<'vtab>;
fn connect(
_: &mut VTabConnection,
@@ -151,15 +152,14 @@ unsafe impl VTab for SeriesTab {
Ok(())
}
- fn open(&self) -> Result<SeriesTabCursor> {
+ fn open(&self) -> Result<SeriesTabCursor<'_>> {
Ok(SeriesTabCursor::new())
}
}
/// A cursor for the Series virtual table
-#[derive(Default)]
#[repr(C)]
-struct SeriesTabCursor {
+struct SeriesTabCursor<'vtab> {
/// Base class. Must be first
base: ffi::sqlite3_vtab_cursor,
/// True to count down rather than up
@@ -174,14 +174,24 @@ struct SeriesTabCursor {
max_value: i64,
/// Increment ("step")
step: i64,
+ phantom: PhantomData<&'vtab SeriesTab>,
}
-impl SeriesTabCursor {
- fn new() -> SeriesTabCursor {
- SeriesTabCursor::default()
+impl SeriesTabCursor<'_> {
+ fn new<'vtab>() -> SeriesTabCursor<'vtab> {
+ SeriesTabCursor {
+ base: ffi::sqlite3_vtab_cursor::default(),
+ is_desc: false,
+ row_id: 0,
+ value: 0,
+ min_value: 0,
+ max_value: 0,
+ step: 0,
+ phantom: PhantomData,
+ }
}
}
-unsafe impl VTabCursor for SeriesTabCursor {
+unsafe impl VTabCursor for SeriesTabCursor<'_> {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values<'_>) -> Result<()> {
let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
let mut i = 0;
diff --git a/tests/vtab.rs b/tests/vtab.rs
index d42bac6..4b31574 100644
--- a/tests/vtab.rs
+++ b/tests/vtab.rs
@@ -9,6 +9,7 @@ fn test_dummy_module() {
VTabConnection, VTabCursor, Values,
};
use rusqlite::{version_number, Connection, Result};
+ use std::marker::PhantomData;
use std::os::raw::c_int;
let module = eponymous_only_module::<DummyTab>();
@@ -19,9 +20,9 @@ fn test_dummy_module() {
base: sqlite3_vtab,
}
- unsafe impl VTab for DummyTab {
+ unsafe impl<'vtab> VTab<'vtab> for DummyTab {
type Aux = ();
- type Cursor = DummyTabCursor;
+ type Cursor = DummyTabCursor<'vtab>;
fn connect(
_: &mut VTabConnection,
@@ -39,21 +40,22 @@ fn test_dummy_module() {
Ok(())
}
- fn open(&self) -> Result<DummyTabCursor> {
+ fn open(&'vtab self) -> Result<DummyTabCursor<'vtab>> {
Ok(DummyTabCursor::default())
}
}
#[derive(Default)]
#[repr(C)]
- struct DummyTabCursor {
+ struct DummyTabCursor<'vtab> {
/// Base class. Must be first
base: sqlite3_vtab_cursor,
/// The rowid
row_id: i64,
+ phantom: PhantomData<&'vtab DummyTab>,
}
- unsafe impl VTabCursor for DummyTabCursor {
+ unsafe impl VTabCursor for DummyTabCursor<'_> {
fn filter(
&mut self,
_idx_num: c_int,