diff --git a/Cargo.lock b/Cargo.lock index 12d0f49..2ae7e78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1048,12 +1048,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1313,7 +1307,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash 0.1.5", + "foldhash", ] [[package]] @@ -1321,17 +1315,14 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash 0.2.0", -] [[package]] name = "hashlink" -version = "0.11.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.15.5", ] [[package]] @@ -1957,9 +1948,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.37.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ "cc", "pkg-config", @@ -3329,16 +3320,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rsqlite-vfs" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a1f2315036ef6b1fbacd1972e8ee7688030b0a2121edfc2a6550febd41574d" -dependencies = [ - "hashbrown 0.16.1", - "thiserror 2.0.18", -] - [[package]] name = "ruma" version = "0.14.1" @@ -3516,7 +3497,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.37.99" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags 2.11.0", "fallible-iterator", @@ -3524,7 +3507,6 @@ dependencies = [ "hashlink", "libsqlite3-sys", "smallvec", - "sqlite-wasm-rs", ] [[package]] @@ -3995,18 +3977,6 @@ dependencies = [ "der", ] -[[package]] -name = "sqlite-wasm-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4206ed3a67690b9c29b77d728f6acc3ce78f16bf846d83c94f76400320181b" -dependencies = [ - "cc", - "js-sys", - "rsqlite-vfs", - "wasm-bindgen", -] - [[package]] name = "sse-codec" version = "0.3.2" @@ -4048,7 +4018,6 @@ dependencies = [ "portable-pty", "pulldown-cmark", "reqwest 0.13.2", - "rusqlite", "rust-embed", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 1a02d0c..00d6bd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,9 +37,3 @@ matrix-sdk = { version = "0.16.0", default-features = false, features = [ pulldown-cmark = { version = "0.13.1", default-features = false, features = [ "html", ] } - -[patch.crates-io] -# Patch rusqlite 0.37.x (used by matrix-sdk-sqlite) with a local fork that requires -# libsqlite3-sys 0.37.0 instead of 0.35.0, enabling a single unified libsqlite3-sys -# 0.37.0 in the dependency graph with the "bundled" feature for static builds. -rusqlite = { path = "vendor/rusqlite" } diff --git a/server/Cargo.toml b/server/Cargo.toml index e82f327..4ba1230 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -32,10 +32,7 @@ matrix-sdk = { workspace = true } pulldown-cmark = { workspace = true } # Force bundled SQLite so static musl builds don't need a system libsqlite3 -libsqlite3-sys = { version = "0.37.0", features = ["bundled"] } -# Enable fallible_uint feature to restore u64/usize ToSql/FromSql impls needed -# by matrix-sdk-sqlite (removed in rusqlite 0.38+ without this feature flag) -rusqlite = { version = "0.37.99", features = ["fallible_uint"] } +libsqlite3-sys = { version = "0.35.0", features = ["bundled"] } wait-timeout = "0.2.1" [dev-dependencies] diff --git a/vendor/rusqlite/.cargo-ok b/vendor/rusqlite/.cargo-ok deleted file mode 100644 index 5f8b795..0000000 --- a/vendor/rusqlite/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -{"v":1} \ No newline at end of file diff --git a/vendor/rusqlite/.cargo_vcs_info.json b/vendor/rusqlite/.cargo_vcs_info.json deleted file mode 100644 index 9b66cf4..0000000 --- a/vendor/rusqlite/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "2a1790a69107cd03dae85d501dcbdb11c5b32ef3" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/vendor/rusqlite/.gitignore b/vendor/rusqlite/.gitignore deleted file mode 100644 index 5f0a3e1..0000000 --- a/vendor/rusqlite/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target/ -/doc/ -Cargo.lock diff --git a/vendor/rusqlite/Cargo.toml b/vendor/rusqlite/Cargo.toml deleted file mode 100644 index 3366c42..0000000 --- a/vendor/rusqlite/Cargo.toml +++ /dev/null @@ -1,350 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -name = "rusqlite" -version = "0.37.99" -authors = ["The rusqlite developers"] -build = false -exclude = [ - "/.github/*", - "/.gitattributes", - "/appveyor.yml", - "/Changelog.md", - "/clippy.toml", - "/codecov.yml", - "**/*.sh", -] -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Ergonomic wrapper for SQLite" -documentation = "https://docs.rs/rusqlite/" -readme = "README.md" -keywords = [ - "sqlite", - "database", - "ffi", -] -categories = ["database"] -license = "MIT" -repository = "https://github.com/rusqlite/rusqlite" - -[package.metadata.docs.rs] -features = [ - "modern-full", - "rusqlite-macros", -] -all-features = false -no-default-features = false -default-target = "x86_64-unknown-linux-gnu" -rustdoc-args = [ - "--cfg", - "docsrs", -] - -[package.metadata.playground] -features = ["bundled-full"] -all-features = false - -[badges.appveyor] -repository = "rusqlite/rusqlite" - -[badges.codecov] -repository = "rusqlite/rusqlite" - -[badges.maintenance] -status = "actively-developed" - -[features] -array = [ - "vtab", - "pointer", -] -backup = [] -blob = [] -buildtime_bindgen = [ - "libsqlite3-sys/buildtime_bindgen", - "sqlite-wasm-rs/bindgen", -] -bundled = [ - "libsqlite3-sys/bundled", - "modern_sqlite", -] -bundled-full = [ - "modern-full", - "bundled", -] -bundled-sqlcipher = [ - "libsqlite3-sys/bundled-sqlcipher", - "bundled", -] -bundled-sqlcipher-vendored-openssl = [ - "libsqlite3-sys/bundled-sqlcipher-vendored-openssl", - "bundled-sqlcipher", -] -bundled-windows = ["libsqlite3-sys/bundled-windows"] -cache = ["hashlink"] -collation = [] -column_decltype = [] -column_metadata = ["libsqlite3-sys/column_metadata"] -csvtab = [ - "csv", - "vtab", -] -default = ["cache"] -extra_check = [] -fallible_uint = [] -functions = [] -hooks = [] -i128_blob = [] -in_gecko = [ - "modern_sqlite", - "libsqlite3-sys/in_gecko", -] -limits = [] -load_extension = [] -loadable_extension = ["libsqlite3-sys/loadable_extension"] -modern-full = [ - "array", - "backup", - "blob", - "modern_sqlite", - "chrono", - "collation", - "column_metadata", - "column_decltype", - "csvtab", - "extra_check", - "functions", - "hooks", - "i128_blob", - "jiff", - "limits", - "load_extension", - "serde_json", - "serialize", - "series", - "time", - "trace", - "unlock_notify", - "url", - "uuid", - "vtab", - "window", -] -modern_sqlite = ["libsqlite3-sys/bundled_bindings"] -pointer = [] -preupdate_hook = [ - "libsqlite3-sys/preupdate_hook", - "hooks", -] -serialize = [] -series = ["vtab"] -session = [ - "libsqlite3-sys/session", - "hooks", -] -sqlcipher = ["libsqlite3-sys/sqlcipher"] -trace = [] -unlock_notify = ["libsqlite3-sys/unlock_notify"] -vtab = [] -wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"] -window = ["functions"] -with-asan = ["libsqlite3-sys/with-asan"] - -[lib] -name = "rusqlite" -path = "src/lib.rs" - -[[example]] -name = "load_extension" -path = "examples/load_extension.rs" -required-features = [ - "load_extension", - "bundled", - "functions", - "trace", -] - -[[example]] -name = "loadable_extension" -crate-type = ["cdylib"] -path = "examples/loadable_extension.rs" -required-features = [ - "loadable_extension", - "functions", - "trace", -] - -[[example]] -name = "owning_rows" -path = "examples/owning_rows.rs" - -[[example]] -name = "owning_statement" -path = "examples/owning_statement.rs" - -[[example]] -name = "persons" -path = "examples/persons/main.rs" - -[[test]] -name = "auto_ext" -path = "tests/auto_ext.rs" - -[[test]] -name = "config_log" -path = "tests/config_log.rs" -harness = false - -[[test]] -name = "deny_single_threaded_sqlite_config" -path = "tests/deny_single_threaded_sqlite_config.rs" - -[[test]] -name = "vtab" -path = "tests/vtab.rs" - -[[bench]] -name = "cache" -path = "benches/cache.rs" -harness = false - -[[bench]] -name = "exec" -path = "benches/exec.rs" -harness = false - -[dependencies.bitflags] -version = "2.6.0" - -[dependencies.chrono] -version = "0.4.42" -features = ["clock"] -optional = true -default-features = false - -[dependencies.csv] -version = "1.1" -optional = true - -[dependencies.fallible-iterator] -version = "0.3" - -[dependencies.fallible-streaming-iterator] -version = "0.1" - -[dependencies.hashlink] -version = "0.11" -optional = true - -[dependencies.jiff] -version = "0.2" -features = ["std"] -optional = true -default-features = false - -[dependencies.rusqlite-macros] -version = "0.4.2" -optional = true - -[dependencies.serde_json] -version = "1.0" -optional = true - -[dependencies.smallvec] -version = "1.6.1" - -[dependencies.time] -version = "0.3.47" -features = [ - "formatting", - "macros", - "parsing", -] -optional = true - -[dependencies.url] -version = "2.1" -optional = true - -[dependencies.uuid] -version = "1.0" -optional = true - -[dev-dependencies.bencher] -version = "0.1" - -[dev-dependencies.doc-comment] -version = "0.3" - -[dev-dependencies.regex] -version = "1.5.5" - -[dev-dependencies.self_cell] -version = "1.1.0" - -[dev-dependencies.tempfile] -version = "3.1.0" - -[dev-dependencies.unicase] -version = "2.6.0" - -[dev-dependencies.uuid] -version = "1.0" -features = ["v4"] - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies.chrono] -version = "0.4.42" -features = ["wasmbind"] -optional = true -default-features = false - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies.jiff] -version = "0.2" -features = ["js"] -optional = true -default-features = false - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies.sqlite-wasm-rs] -version = "0.5.1" -default-features = false - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies.time] -version = "0.3.47" -features = ["wasm-bindgen"] -optional = true - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies.uuid] -version = "1.0" -features = ["js"] -optional = true - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies.getrandom] -version = "0.4" -features = ["wasm_js"] - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies.uuid] -version = "1.0" -features = ["js"] - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies.wasm-bindgen] -version = "0.2.104" - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test] -version = "0.3.54" - -[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies.libsqlite3-sys] -version = "0.37.0" diff --git a/vendor/rusqlite/Cargo.toml.orig b/vendor/rusqlite/Cargo.toml.orig deleted file mode 100644 index fadcd62..0000000 --- a/vendor/rusqlite/Cargo.toml.orig +++ /dev/null @@ -1,242 +0,0 @@ -[package] -name = "rusqlite" -# Note: Update version in README.md when you change this. -version = "0.37.99" -authors = ["The rusqlite developers"] -edition = "2021" -description = "Ergonomic wrapper for SQLite" -repository = "https://github.com/rusqlite/rusqlite" -documentation = "https://docs.rs/rusqlite/" -readme = "README.md" -keywords = ["sqlite", "database", "ffi"] -license = "MIT" -categories = ["database"] - -exclude = [ - "/.github/*", - "/.gitattributes", - "/appveyor.yml", - "/Changelog.md", - "/clippy.toml", - "/codecov.yml", - "**/*.sh", -] - -[badges] -appveyor = { repository = "rusqlite/rusqlite" } -codecov = { repository = "rusqlite/rusqlite" } -maintenance = { status = "actively-developed" } - -[lib] -name = "rusqlite" - -[workspace] -members = ["libsqlite3-sys"] - -[features] -# if not SQLITE_OMIT_LOAD_EXTENSION -load_extension = [] -# hot-backup interface -backup = [] -# if not SQLITE_OMIT_INCRBLOB -# sqlite3_blob -blob = [] -# Prepared statements cache by connection (like https://www.sqlite.org/tclsqlite.html#cache) -cache = ["hashlink"] -# sqlite3_create_collation_v2 -collation = [] -# sqlite3_create_function_v2 -functions = [] -# sqlite3_log / sqlite3_trace_v2 -trace = [] -# Use bundled SQLite sources (instead of the one provided by your OS / distribution) -bundled = ["libsqlite3-sys/bundled", "modern_sqlite"] -# Use SQLCipher instead of SQLite -bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"] -bundled-sqlcipher-vendored-openssl = [ - "libsqlite3-sys/bundled-sqlcipher-vendored-openssl", - "bundled-sqlcipher", -] -buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen", "sqlite-wasm-rs/bindgen"] -# sqlite3_limit -limits = [] -# Used to generate a cdylib -loadable_extension = ["libsqlite3-sys/loadable_extension"] -# sqlite3_commit_hook, sqlite3_rollback_hook, ... -hooks = [] -# if SQLITE_ENABLE_PREUPDATE_HOOK -preupdate_hook = ["libsqlite3-sys/preupdate_hook", "hooks"] -# u64, usize, NonZeroU64, NonZeroUsize -fallible_uint = [] -i128_blob = [] -sqlcipher = ["libsqlite3-sys/sqlcipher"] -# SQLITE_ENABLE_UNLOCK_NOTIFY -unlock_notify = ["libsqlite3-sys/unlock_notify"] -# if not SQLITE_OMIT_VIRTUALTABLE -# sqlite3_vtab -vtab = [] -csvtab = ["csv", "vtab"] -# Port of Carray() table-valued function -array = ["vtab", "pointer"] -# if SQLITE_ENABLE_SESSION -# session extension -session = ["libsqlite3-sys/session", "hooks"] -# if not SQLITE_OMIT_WINDOWFUNC -# sqlite3_create_window_function -window = ["functions"] -# Port of generate_series table-valued function -series = ["vtab"] -# check for invalid query. -extra_check = [] -# ]3.34.1, last] -modern_sqlite = ["libsqlite3-sys/bundled_bindings"] -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"] -# if SQLITE_ENABLE_COLUMN_METADATA -column_metadata = ["libsqlite3-sys/column_metadata"] -# if not SQLITE_OMIT_DECLTYPE -column_decltype = [] -wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"] -# if not SQLITE_OMIT_DESERIALIZE -serialize = [] -# pointer passing interfaces: 3.20.0 -pointer = [] - -# Helper feature for enabling most non-build-related optional features -# or dependencies (except `session`). This is useful for running tests / clippy -# / etc. New features and optional dependencies that don't conflict with anything -# else should be added here. -modern-full = [ - "array", - "backup", - "blob", - "modern_sqlite", - "chrono", - "collation", - "column_metadata", - "column_decltype", - "csvtab", - "extra_check", - "functions", - "hooks", - "i128_blob", - "jiff", - "limits", - "load_extension", - "serde_json", - "serialize", - "series", - "time", - "trace", - "unlock_notify", - "url", - "uuid", - "vtab", - "window", -] - -bundled-full = ["modern-full", "bundled"] -default = ["cache"] - -[dependencies] -# Jiff Date/Time/Timestamp persistence -jiff = { version = "0.2", optional = true, default-features = false, features = [ - "std", -] } -# Date/Time/Timestamp persistence -time = { version = "0.3.47", features = [ - "formatting", - "macros", - "parsing", -], optional = true } -bitflags = "2.6.0" -# LRU cache of statement -hashlink = { version = "0.11", optional = true } -# Chrono Date/Time/Timestamp persistence -chrono = { version = "0.4.42", optional = true, default-features = false, features = [ - "clock", -] } -# JSON persistence -serde_json = { version = "1.0", optional = true } -# Virtual table -csv = { version = "1.1", optional = true } -# Url persistence -url = { version = "2.1", optional = true } -fallible-iterator = "0.3" -fallible-streaming-iterator = "0.1" -# Uuid persistence -uuid = { version = "1.0", optional = true } -smallvec = "1.6.1" -# WIP comptime checks -rusqlite-macros = { path = "rusqlite-macros", version = "0.4.2", optional = true } - -[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dependencies] -libsqlite3-sys = { path = "libsqlite3-sys", version = "0.37.0" } - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] -sqlite-wasm-rs = { version = "0.5.1", default-features = false } -chrono = { version = "0.4.42", optional = true, default-features = false, features = ["wasmbind"] } -jiff = { version = "0.2", optional = true, default-features = false, features = ["js"] } -time = { version = "0.3.47", optional = true, features = ["wasm-bindgen"] } -uuid = { version = "1.0", optional = true, features = ["js"] } - -[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies] -# Something is dependent on them, we use feature to override it. -uuid = { version = "1.0", features = ["js"] } -getrandom = { version = "0.4", features = ["wasm_js"] } -wasm-bindgen-test = "0.3.54" -wasm-bindgen = "0.2.104" - -[dev-dependencies] -doc-comment = "0.3" -tempfile = "3.1.0" -regex = "1.5.5" -uuid = { version = "1.0", features = ["v4"] } -unicase = "2.6.0" -self_cell = "1.1.0" -# Use `bencher` over criterion because it builds much faster, -# and we don't have many benchmarks -bencher = "0.1" - -[[test]] -name = "auto_ext" - -[[test]] -name = "config_log" -harness = false - -[[test]] -name = "deny_single_threaded_sqlite_config" - -[[test]] -name = "vtab" - -[[bench]] -name = "cache" -harness = false - -[[bench]] -name = "exec" -harness = false - -[[example]] -name = "loadable_extension" -crate-type = ["cdylib"] -required-features = ["loadable_extension", "functions", "trace"] - -[[example]] -name = "load_extension" -required-features = ["load_extension", "bundled", "functions", "trace"] - -[package.metadata.docs.rs] -features = ["modern-full", "rusqlite-macros"] -all-features = false -no-default-features = false -default-target = "x86_64-unknown-linux-gnu" -rustdoc-args = ["--cfg", "docsrs"] - -[package.metadata.playground] -features = ["bundled-full"] -all-features = false diff --git a/vendor/rusqlite/LICENSE b/vendor/rusqlite/LICENSE deleted file mode 100644 index 08c660f..0000000 --- a/vendor/rusqlite/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 The rusqlite developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/rusqlite/README.md b/vendor/rusqlite/README.md deleted file mode 100644 index 76822b3..0000000 --- a/vendor/rusqlite/README.md +++ /dev/null @@ -1,259 +0,0 @@ -# Rusqlite - -[![Latest Version](https://img.shields.io/crates/v/rusqlite.svg)](https://crates.io/crates/rusqlite) -[![Documentation](https://docs.rs/rusqlite/badge.svg)](https://docs.rs/rusqlite) -[![Build Status (GitHub)](https://github.com/rusqlite/rusqlite/workflows/CI/badge.svg)](https://github.com/rusqlite/rusqlite/actions) -[![Build Status (AppVeyor)](https://ci.appveyor.com/api/projects/status/github/rusqlite/rusqlite?branch=master&svg=true)](https://ci.appveyor.com/project/rusqlite/rusqlite) -[![Code Coverage](https://codecov.io/gh/rusqlite/rusqlite/branch/master/graph/badge.svg)](https://codecov.io/gh/rusqlite/rusqlite) -[![Dependency Status](https://deps.rs/repo/github/rusqlite/rusqlite/status.svg)](https://deps.rs/repo/github/rusqlite/rusqlite) -[![Discord Chat](https://img.shields.io/discord/927966344266256434.svg?logo=discord)](https://discord.gg/nFYfGPB8g4) - -Rusqlite is an ergonomic wrapper for using SQLite from Rust. - -Historically, the API was based on the one from [`rust-postgres`](https://github.com/sfackler/rust-postgres). However, the two have diverged in many ways, and no compatibility between the two is intended. - -## Usage - -In your Cargo.toml: - -```toml -[dependencies] -# `bundled` causes us to automatically compile and link in an up to date -# version of SQLite for you. This avoids many common build issues, and -# avoids depending on the version of SQLite on the users system (or your -# system), which may be old or missing. It's the right choice for most -# programs that control their own SQLite databases. -# -# That said, it's not ideal for all scenarios and in particular, generic -# libraries built around `rusqlite` should probably not enable it, which -# is why it is not a default feature -- it could become hard to disable. -rusqlite = { version = "0.39.0", features = ["bundled"] } -``` - -Simple example usage: - -```rust -use rusqlite::{Connection, Result}; - -#[derive(Debug)] -struct Person { - id: i32, - name: String, - data: Option>, -} - -fn main() -> Result<()> { - let conn = Connection::open_in_memory()?; - - conn.execute( - "CREATE TABLE person ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - data BLOB - )", - (), // empty list of parameters. - )?; - let me = Person { - id: 0, - name: "Steven".to_string(), - data: None, - }; - conn.execute( - "INSERT INTO person (name, data) VALUES (?1, ?2)", - (&me.name, &me.data), - )?; - - let mut stmt = conn.prepare("SELECT id, name, data FROM person")?; - let person_iter = stmt.query_map([], |row| { - Ok(Person { - id: row.get(0)?, - name: row.get(1)?, - data: row.get(2)?, - }) - })?; - - for person in person_iter { - println!("Found person {:?}", person.unwrap()); - } - Ok(()) -} -``` - -### Supported SQLite Versions - -The base `rusqlite` package supports SQLite version 3.34.1 or newer. If you need -support for older versions, please file an issue. Some cargo features require a -newer SQLite version; see details below. - -### Optional Features - -Rusqlite provides several features that are behind [Cargo -features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are: - -* [`load_extension`](https://docs.rs/rusqlite/~0/rusqlite/struct.LoadExtensionGuard.html) - allows loading dynamic library-based SQLite extensions. -* `loadable_extension` to program [loadable extension](https://sqlite.org/loadext.html) in Rust. -* [`backup`](https://docs.rs/rusqlite/~0/rusqlite/backup/index.html) - allows use of SQLite's online backup API. -* [`functions`](https://docs.rs/rusqlite/~0/rusqlite/functions/index.html) - allows you to load Rust closures into SQLite connections for use in queries. -* `window` for [window function](https://www.sqlite.org/windowfunctions.html) support (`fun(...) OVER ...`). (Implies `functions`.) -* [`trace`](https://docs.rs/rusqlite/~0/rusqlite/trace/index.html) - allows hooks into SQLite's tracing and profiling APIs. -* [`blob`](https://docs.rs/rusqlite/~0/rusqlite/blob/index.html) - gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. -* [`limits`](https://docs.rs/rusqlite/~0/rusqlite/struct.Connection.html#method.limit) - allows you to set and retrieve SQLite's per connection limits. -* `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). -* `chrono` 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 various - types from the [`chrono` crate](https://crates.io/crates/chrono). -* `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 various - types from the [`time` crate](https://crates.io/crates/time). -* `jiff` 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 [`jiff` crate](https://crates.io/crates/jiff). -* `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). -* `bundled` uses a bundled version of SQLite. This is a good option for cases where linking to SQLite is complicated, such as Windows. -* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature overrides `bundled`. -* `bundled-sqlcipher` uses a bundled version of SQLCipher. This searches for and links against a system-installed crypto library to provide the crypto implementation. -* `bundled-sqlcipher-vendored-openssl` allows using bundled-sqlcipher with a vendored version of OpenSSL (via the `openssl-sys` crate) as the crypto provider. - - As the name implies this depends on the `bundled-sqlcipher` feature, and automatically turns it on. - - If turned on, this uses the [`openssl-sys`](https://crates.io/crates/openssl-sys) crate, with the `vendored` feature enabled in order to build and bundle the OpenSSL crypto library. -* `hooks` for [Commit, Rollback](http://sqlite.org/c3ref/commit_hook.html) and [Data Change](http://sqlite.org/c3ref/update_hook.html) notification callbacks. -* `preupdate_hook` for [preupdate](https://sqlite.org/c3ref/preupdate_count.html) notification callbacks. (Implies `hooks`.) -* `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 implementations in Rust). Currently, only read-only virtual tables are supported. -* `series` exposes [`generate_series(...)`](https://www.sqlite.org/series.html) Table-Valued Function. (Implies `vtab`.) -* [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust. (Implies `vtab`.) -* [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function. (Implies `vtab`.) -* `fallible_uint` allows storing values of type `u64`, `usize`, `NonZeroU64`, `NonZeroUsize` but only if <= `i64::MAX`. -* `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. -* `uuid` allows storing and retrieving `Uuid` values from the [`uuid`](https://docs.rs/uuid/) crate using blobs. -* [`session`](https://sqlite.org/sessionintro.html), Session module extension. Requires `buildtime_bindgen` feature. (Implies `hooks`.) -* `extra_check` fails when a query passed to `execute` is readonly and has a column count > 0. -* `column_decltype` provides `columns()` method for Statements and Rows; omit if linking to a version of SQLite/SQLCipher compiled with `-DSQLITE_OMIT_DECLTYPE`. -* `collation` exposes [`sqlite3_create_collation_v2`](https://sqlite.org/c3ref/create_collation.html). -* `serialize` exposes [`sqlite3_serialize`](http://sqlite.org/c3ref/serialize.html) (3.23.0). -* `rusqlite-macros` enables the use of the [`prepare_and_bind`](https://docs.rs/rusqlite/~0/rusqlite/macro.prepare_and_bind.html) - and [`prepare_cached_and_bind`](https://docs.rs/rusqlite/~0/rusqlite/macro.prepare_cached_and_bind.html) - procedural macros, which allow capturing identifiers in SQL statements. - - -## 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 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`, `bundled-sqlcipher`, or `bundled-sqlcipher-vendored-openssl` features, `libsqlite3-sys` will use the - [cc](https://crates.io/crates/cc) crate to compile SQLite or SQLCipher from source and - link against that. This source is embedded in the `libsqlite3-sys` crate and - is currently SQLite 3.51.3 (as of `rusqlite` 0.39.0 / `libsqlite3-sys` - 0.37.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.39.0" - features = ["bundled"] - ``` -* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE3_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.) -* When using `bundled-sqlcipher` (and not also using `bundled-sqlcipher-vendored-openssl`), `libsqlite3-sys` will need to - link against crypto libraries on the system. If the build script can find a `libcrypto` from OpenSSL or LibreSSL (it will consult `OPENSSL_LIB_DIR`/`OPENSSL_INCLUDE_DIR` and `OPENSSL_DIR` environment variables), it will use that. If building on and for Macs, and none of those variables are set, it will use the system's SecurityFramework instead. - -* When linking against a SQLite (or SQLCipher) library already on the system (so *not* using any of the `bundled` features), you can set the `SQLITE3_LIB_DIR` (or `SQLCIPHER_LIB_DIR`) environment variable to point to a directory containing the library. You can also set the `SQLITE3_INCLUDE_DIR` (or `SQLCIPHER_INCLUDE_DIR`) variable to point to the directory containing `sqlite3.h`. -* 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, - which must be enabled by setting `VCPKGRS_DYNAMIC=1` environment variable before build. - `vcpkg install sqlite3:x64-windows` will install the required library. -* When linking against a SQLite (or SQLCipher) library already on the system, you can set the `SQLITE3_STATIC` (or `SQLCIPHER_STATIC`) environment variable to 1 to request that the library be statically instead of dynamically linked. - - -### Binding generation - -We use [bindgen](https://crates.io/crates/bindgen) to generate the Rust -declarations from SQLite's C header file. `bindgen` -[recommends](https://github.com/servo/rust-bindgen#library-usage-with-buildrs) -running this as part of the build process of libraries that used this. We tried -this briefly (`rusqlite` 0.10.0, specifically), but it had some annoyances: - -* The build time for `libsqlite3-sys` (and therefore `rusqlite`) increased - dramatically. -* Running `bindgen` requires a relatively-recent version of Clang, which many - systems do not have installed by default. -* Running `bindgen` also requires the SQLite header file to be present. - -As of `rusqlite` 0.10.1, we avoid running `bindgen` at build-time by shipping -pregenerated bindings for several versions of SQLite. When compiling -`rusqlite`, we use your selected Cargo features to pick the bindings for the -minimum SQLite version that supports your chosen features. If you are using -`libsqlite3-sys` directly, you can use the same features to choose which -pregenerated bindings are chosen: - -* `min_sqlite_version_3_34_1` - SQLite 3.34.1 bindings (this is the default) - -If you use any of the `bundled` features, you will get pregenerated bindings for the -bundled version of SQLite/SQLCipher. If you need other specific pregenerated binding -versions, please file an issue. If you want to run `bindgen` at buildtime to -produce your own bindings, use the `buildtime_bindgen` Cargo feature. - -If you enable the `modern_sqlite` feature, we'll use the bindings we would have -included with the bundled build. You generally should have `buildtime_bindgen` -enabled if you turn this on, as otherwise you'll need to keep the version of -SQLite you link with in sync with what rusqlite would have bundled, (usually the -most recent release of SQLite). Failing to do this will cause a runtime error. - -## Contributing - -Rusqlite has many features, and many of them impact the build configuration in -incompatible ways. This is unfortunate, and makes testing changes hard. - -To help here: you generally should ensure that you run tests/lint for -`--features bundled`, and `--features "bundled-full session buildtime_bindgen"`. - -If running bindgen is problematic for you, `--features bundled-full` enables -bundled and all features which don't require binding generation, and can be used -instead. - -### Checklist - -- Run `cargo fmt` to ensure your Rust code is correctly formatted. -- Ensure `cargo clippy --workspace --features bundled` passes without warnings. -- Ensure `cargo clippy --workspace --features "bundled-full session buildtime_bindgen"` passes without warnings. -- Ensure `cargo test --workspace --features bundled` reports no failures. -- Ensure `cargo test --workspace --features "bundled-full session buildtime_bindgen"` reports no failures. - -## Author - -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 - -Feel free to join the [Rusqlite Discord Server](https://discord.gg/nFYfGPB8g4) to discuss or get help with `rusqlite` or `libsqlite3-sys`. - -## License - -Rusqlite and libsqlite3-sys are available under the MIT license. See the LICENSE file for more info. - -### Licenses of Bundled Software - -Depending on the set of enabled cargo `features`, rusqlite and libsqlite3-sys will also bundle other libraries, which have their own licensing terms: - -- If `--features=bundled-sqlcipher` is enabled, the vendored source of [SQLcipher](https://github.com/sqlcipher/sqlcipher) will be compiled and statically linked in. SQLcipher is distributed under a BSD-style license, as described [here](libsqlite3-sys/sqlcipher/LICENSE). - -- If `--features=bundled` is enabled, the vendored source of SQLite will be compiled and linked in. SQLite is in the public domain, as described [here](https://www.sqlite.org/copyright.html). - -Both of these are quite permissive, have no bearing on the license of the code in `rusqlite` or `libsqlite3-sys` themselves, and can be entirely ignored if you do not use the feature in question. - -## Minimum supported Rust version (MSRV) - -Latest stable Rust version at the time of release. It might compile with older versions. diff --git a/vendor/rusqlite/benches/cache.rs b/vendor/rusqlite/benches/cache.rs deleted file mode 100644 index dd3683e..0000000 --- a/vendor/rusqlite/benches/cache.rs +++ /dev/null @@ -1,18 +0,0 @@ -use bencher::{benchmark_group, benchmark_main, Bencher}; -use rusqlite::Connection; - -fn bench_no_cache(b: &mut Bencher) { - let db = Connection::open_in_memory().unwrap(); - db.set_prepared_statement_cache_capacity(0); - let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71"; - b.iter(|| db.prepare(sql).unwrap()); -} - -fn bench_cache(b: &mut Bencher) { - let db = Connection::open_in_memory().unwrap(); - let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71"; - b.iter(|| db.prepare_cached(sql).unwrap()); -} - -benchmark_group!(cache_benches, bench_no_cache, bench_cache); -benchmark_main!(cache_benches); diff --git a/vendor/rusqlite/benches/exec.rs b/vendor/rusqlite/benches/exec.rs deleted file mode 100644 index b95cb35..0000000 --- a/vendor/rusqlite/benches/exec.rs +++ /dev/null @@ -1,17 +0,0 @@ -use bencher::{benchmark_group, benchmark_main, Bencher}; -use rusqlite::Connection; - -fn bench_execute(b: &mut Bencher) { - let db = Connection::open_in_memory().unwrap(); - let sql = "PRAGMA user_version=1"; - b.iter(|| db.execute(sql, []).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/vendor/rusqlite/bindings.md b/vendor/rusqlite/bindings.md deleted file mode 100644 index 3e03374..0000000 --- a/vendor/rusqlite/bindings.md +++ /dev/null @@ -1,405 +0,0 @@ -# List of SQLite functions supported - -- [ ] `sqlite3_version` -- [X] `sqlite3_libversion` -- [ ] `sqlite3_sourceid` -- [X] `sqlite3_libversion_number` - -- [ ] `sqlite3_compileoption_used` -- [ ] `sqlite3_compileoption_get` - -- [X] `sqlite3_threadsafe` (internal use only) - -- [X] `sqlite3_close` -- [ ] `sqlite3_close_v2` - -- [ ] `sqlite3_exec` - -- [ ] `sqlite3_initialize` -- [ ] `sqlite3_shutdown` -- [ ] `sqlite3_os_init` -- [ ] `sqlite3_os_end` - -- [ ] `sqlite3_config` (partially, `fn` callback for SQLITE_CONFIG_LOG) (cannot be used by a loadable extension) -- [X] `sqlite3_db_config` - -- [X] `sqlite3_extended_result_codes` (not public, internal use only) - -- [X] `sqlite3_last_insert_rowid` -- [ ] `sqlite3_set_last_insert_rowid` - -- [X] `sqlite3_changes` -- [X] `sqlite3_changes64` -- [X] `sqlite3_total_changes` -- [X] `sqlite3_total_changes64` - -- [X] `sqlite3_interrupt` -- [X] `sqlite3_is_interrupted` - -- [ ] `sqlite3_complete` - -- [X] `sqlite3_busy_handler` (`fn` callback) -- [X] `sqlite3_busy_timeout` - -- [ ] `sqlite3_get_table` - -- [ ] `sqlite3_mprintf` -- [ ] `sqlite3_vmprintf` -- [ ] `sqlite3_snprintf` -- [ ] `sqlite3_vsnprintf` - -- [ ] `sqlite3_malloc` -- [X] `sqlite3_malloc64` (not public, internal use only) -- [ ] `sqlite3_realloc` -- [ ] `sqlite3_realloc64` -- [X] `sqlite3_free` (not public, internal use only) -- [ ] `sqlite3_msize` - -- [ ] `sqlite3_memory_used` -- [ ] `sqlite3_memory_highwater` - -- [ ] `sqlite3_randomness` - -- [X] `sqlite3_set_authorizer` (`FnMut` callback, reference kept) -- [X] `sqlite3_trace` deprecated (`fn` callback) -- [X] `sqlite3_profile` deprecated (`fn` callback) -- [X] `sqlite3_trace_v2` (`fn` callback, no context data) -- [X] `sqlite3_progress_handler` (`FnMut` callback, reference kept) - -- [ ] `sqlite3_open` -- [X] `sqlite3_open_v2` -- [ ] `sqlite3_uri_parameter` -- [ ] `sqlite3_uri_boolean` -- [ ] `sqlite3_uri_int64` -- [ ] `sqlite3_uri_key` - -- [ ] `sqlite3_filename_database` -- [ ] `sqlite3_filename_journal` -- [ ] `sqlite3_filename_wal` -- [ ] `sqlite3_database_file_object` -- [ ] `sqlite3_create_filename` -- [ ] `sqlite3_free_filename` - -- [X] `sqlite3_errcode` -- [X] `sqlite3_extended_errcode` -- [X] `sqlite3_errmsg` (not public, internal use only) -- [X] `sqlite3_errstr` (not public, internal use only) -- [X] `sqlite3_error_offset` - -- [X] `sqlite3_limit` - -- [ ] `sqlite3_prepare` -- [X] `sqlite3_prepare_v2` -- [X] `sqlite3_prepare_v3` - -- [X] `sqlite3_sql` (not public, internal use only) -- [X] `sqlite3_expanded_sql` -- [ ] `sqlite3_normalized_sql` - -- [X] `sqlite3_stmt_readonly` -- [X] `sqlite3_stmt_isexplain` -- [ ] `sqlite3_stmt_explain` -- [X] `sqlite3_stmt_busy` - -- [ ] `sqlite3_bind_blob` -- [X] `sqlite3_bind_blob64` -- [X] `sqlite3_bind_double` -- [ ] `sqlite3_bind_int` -- [X] `sqlite3_bind_int64` -- [X] `sqlite3_bind_null` -- [ ] `sqlite3_bind_text` -- [X] `sqlite3_bind_text64` -- [ ] `sqlite3_bind_value` -- [X] `sqlite3_bind_pointer` -- [X] `sqlite3_bind_zeroblob` -- [ ] `sqlite3_bind_zeroblob64` - -- [X] `sqlite3_bind_parameter_count` -- [X] `sqlite3_bind_parameter_name` -- [X] `sqlite3_bind_parameter_index` -- [X] `sqlite3_clear_bindings` - -- [X] `sqlite3_column_count` -- [ ] `sqlite3_data_count` -- [X] `sqlite3_column_name` -- [X] `sqlite3_column_database_name` -- [X] `sqlite3_column_table_name` -- [X] `sqlite3_column_origin_name` -- [X] `sqlite3_column_decltype` - -- [X] `sqlite3_step` - -- [X] `sqlite3_column_blob` -- [X] `sqlite3_column_double` -- [ ] `sqlite3_column_int` -- [X] `sqlite3_column_int64` -- [X] `sqlite3_column_text` -- [X] `sqlite3_column_value` (not public, internal use only) -- [X] `sqlite3_column_bytes` (not public, internal use only) -- [X] `sqlite3_column_type` - -- [X] `sqlite3_finalize` -- [X] `sqlite3_reset` (not public, internal use only) - -- [ ] `sqlite3_create_function` -- [X] `sqlite3_create_function_v2` (Boxed callback, destroyed by SQLite) -- [X] `sqlite3_create_window_function` (Boxed callback, destroyed by SQLite) - -- [X] `sqlite3_value_blob` -- [X] `sqlite3_value_double` -- [ ] `sqlite3_value_int` -- [X] `sqlite3_value_int64` -- [X] `sqlite3_value_pointer` -- [X] `sqlite3_value_text` -- [X] `sqlite3_value_bytes` (not public, internal use only) -- [X] `sqlite3_value_type` -- [ ] `sqlite3_value_numeric_type` -- [X] `sqlite3_value_nochange` -- [ ] `sqlite3_value_frombind` -- [ ] `sqlite3_value_encoding` -- [X] `sqlite3_value_subtype` - -- [ ] `sqlite3_value_dup` -- [ ] `sqlite3_value_free` - -- [X] `sqlite3_aggregate_context` (not public, internal use only) -- [X] `sqlite3_user_data` (not public, internal use only) -- [X] `sqlite3_context_db_handle` (Connection ref) -- [X] `sqlite3_get_auxdata` -- [X] `sqlite3_set_auxdata` -- [ ] `sqlite3_get_clientdata` -- [ ] `sqlite3_set_clientdata` - -- [ ] `sqlite3_result_blob` -- [X] `sqlite3_result_blob64` -- [X] `sqlite3_result_double` -- [X] `sqlite3_result_error` -- [X] `sqlite3_result_error_toobig` -- [X] `sqlite3_result_error_nomem` -- [X] `sqlite3_result_error_code` -- [ ] `sqlite3_result_int` -- [X] `sqlite3_result_int64` -- [X] `sqlite3_result_null` -- [ ] `sqlite3_result_text` -- [X] `sqlite3_result_text64` -- [X] `sqlite3_result_value` -- [X] `sqlite3_result_pointer` -- [X] `sqlite3_result_zeroblob` -- [ ] `sqlite3_result_zeroblob64` -- [X] `sqlite3_result_subtype` - -- [ ] `sqlite3_create_collation` -- [X] `sqlite3_create_collation_v2` (Boxed callback, destroyed by SQLite) -- [X] `sqlite3_collation_needed` (`fn` callback) - -- [ ] `sqlite3_sleep` - -- [X] `sqlite3_get_autocommit` - -- [X] `sqlite3_db_handle` (not public, internal use only, Connection ref) -- [X] `sqlite3_db_name` -- [X] `sqlite3_db_filename` -- [X] `sqlite3_db_readonly` -- [X] `sqlite3_txn_state` -- [X] `sqlite3_next_stmt` (not public, internal use only) - -- [X] `sqlite3_commit_hook` (`FnMut` callback, reference kept) -- [X] `sqlite3_rollback_hook` (`FnMut` callback, reference kept) -- [ ] `sqlite3_autovacuum_pages` -- [X] `sqlite3_update_hook` (`FnMut` callback, reference kept) - -- [ ] `sqlite3_enable_shared_cache` -- [ ] `sqlite3_release_memory` -- [X] `sqlite3_db_release_memory` -- [ ] `sqlite3_soft_heap_limit64` -- [ ] `sqlite3_hard_heap_limit64` - -- [X] `sqlite3_table_column_metadata` - -- [X] `sqlite3_load_extension` -- [X] `sqlite3_enable_load_extension` (cannot be used by a loadable extension) -- [X] `sqlite3_auto_extension` (`fn` callbak with Connection ref) -- [X] `sqlite3_cancel_auto_extension` -- [X] `sqlite3_reset_auto_extension` - -- [ ] `sqlite3_create_module` -- [X] `sqlite3_create_module_v2` -- [ ] `sqlite3_drop_modules` -- [X] `sqlite3_declare_vtab` -- [ ] `sqlite3_overload_function` - -- [X] `sqlite3_blob_open` -- [X] `sqlite3_blob_reopen` -- [X] `sqlite3_blob_close` -- [X] `sqlite3_blob_bytes` -- [X] `sqlite3_blob_read` -- [X] `sqlite3_blob_write` - -- [ ] `sqlite3_vfs_find` -- [ ] `sqlite3_vfs_register` -- [ ] `sqlite3_vfs_unregister` - -- [ ] `sqlite3_mutex_alloc` -- [ ] `sqlite3_mutex_free` -- [ ] `sqlite3_mutex_enter` -- [ ] `sqlite3_mutex_try` -- [ ] `sqlite3_mutex_leave` -- [ ] `sqlite3_mutex_held` -- [ ] `sqlite3_mutex_notheld` -- [ ] `sqlite3_db_mutex` - -- [X] `sqlite3_file_control` (not public, internal use only) -- [ ] `sqlite3_test_control` - -- [ ] `sqlite3_keyword_count` -- [ ] `sqlite3_keyword_name` -- [ ] `sqlite3_keyword_check` - -- [ ] `sqlite3_str_new` -- [ ] `sqlite3_str_finish` -- [ ] `sqlite3_str_append` -- [ ] `sqlite3_str_reset` -- [ ] `sqlite3_str_errcode` -- [ ] `sqlite3_str_length` -- [ ] `sqlite3_str_value` - -- [ ] `sqlite3_status` -- [ ] `sqlite3_status64` -- [ ] `sqlite3_db_status` -- [X] `sqlite3_stmt_status` - -- [X] `sqlite3_backup_init` -- [X] `sqlite3_backup_step` -- [X] `sqlite3_backup_finish` -- [X] `sqlite3_backup_remaining` -- [X] `sqlite3_backup_pagecount` - -- [X] `sqlite3_unlock_notify` (`fn` callback, internal use only) - -- [ ] `sqlite3_stricmp` -- [ ] `sqlite3_strnicmp` -- [ ] `sqlite3_strglob` -- [ ] `sqlite3_strlike` - -- [X] `sqlite3_log` - -- [X] `sqlite3_wal_hook` (`fn` callback with Connection ref) -- [ ] `sqlite3_wal_autocheckpoint` -- [X] `sqlite3_wal_checkpoint` -- [X] `sqlite3_wal_checkpoint_v2` - -- [X] `sqlite3_vtab_config` -- [X] `sqlite3_vtab_on_conflict` -- [X] `sqlite3_vtab_nochange` -- [X] `sqlite3_vtab_collation` -- [X] `sqlite3_vtab_distinct` -- [X] `sqlite3_vtab_in` -- [X] `sqlite3_vtab_in_first` -- [X] `sqlite3_vtab_in_next` -- [X] `sqlite3_vtab_rhs_value` - -- [ ] `sqlite3_stmt_scanstatus` -- [ ] `sqlite3_stmt_scanstatus_v2` -- [ ] `sqlite3_stmt_scanstatus_reset` - -- [X] `sqlite3_db_cacheflush` - -- [X] `sqlite3_preupdate_hook` (`FnMut` callback with Connection ref, reference kept) (cannot be used by a loadable extension) -- [X] `sqlite3_preupdate_old` -- [X] `sqlite3_preupdate_count` -- [X] `sqlite3_preupdate_depth` -- [X] `sqlite3_preupdate_new` -- [ ] `sqlite3_preupdate_blobwrite` - -- [ ] `sqlite3_system_errno` - -- [ ] `sqlite3_snapshot_get` -- [ ] `sqlite3_snapshot_open` -- [ ] `sqlite3_snapshot_free` -- [ ] `sqlite3_snapshot_cmp` -- [ ] `sqlite3_snapshot_recover` - -- [X] `sqlite3_serialize` -- [X] `sqlite3_deserialize` - -- [ ] `sqlite3_rtree_geometry_callback` -- [ ] `sqlite3_rtree_query_callback` - -- [X] `sqlite3session_create` -- [X] `sqlite3session_delete` -- [ ] `sqlite3session_object_config` -- [X] `sqlite3session_enable` -- [X] `sqlite3session_indirect` -- [X] `sqlite3session_attach` -- [X] `sqlite3session_table_filter` (Boxed callback, reference kept) -- [X] `sqlite3session_changeset` -- [ ] `sqlite3session_changeset_size` -- [X] `sqlite3session_diff` -- [X] `sqlite3session_patchset` -- [X] `sqlite3session_isempty` -- [ ] `sqlite3session_memory_used` -- [X] `sqlite3changeset_start` -- [ ] `sqlite3changeset_start_v2` -- [X] `sqlite3changeset_next` -- [X] `sqlite3changeset_op` -- [X] `sqlite3changeset_pk` -- [X] `sqlite3changeset_old` -- [X] `sqlite3changeset_new` -- [X] `sqlite3changeset_conflict` -- [X] `sqlite3changeset_fk_conflicts` -- [X] `sqlite3changeset_finalize` -- [X] `sqlite3changeset_invert` -- [X] `sqlite3changeset_concat` -- [ ] `sqlite3changeset_upgrade` -- [X] `sqlite3changegroup_new` -- [ ] `sqlite3changegroup_schema` -- [X] `sqlite3changegroup_add` -- [ ] `sqlite3changegroup_add_change` -- [X] `sqlite3changegroup_output` -- [X] `sqlite3changegroup_delete` -- [X] `sqlite3changeset_apply` -- [ ] `sqlite3changeset_apply_v2` -- [ ] `sqlite3rebaser_create` -- [ ] `sqlite3rebaser_configure` -- [ ] `sqlite3rebaser_rebase` -- [ ] `sqlite3rebaser_delete` -- [X] `sqlite3changeset_apply_strm` -- [ ] `sqlite3changeset_apply_v2_strm` -- [X] `sqlite3changeset_concat_strm` -- [X] `sqlite3changeset_invert_strm` -- [X] `sqlite3changeset_start_strm` -- [ ] `sqlite3changeset_start_v2_strm` -- [X] `sqlite3session_changeset_strm` -- [X] `sqlite3session_patchset_strm` -- [X] `sqlite3changegroup_add_strm` -- [X] `sqlite3changegroup_add_strm` -- [X] `sqlite3changegroup_output_strm` -- [ ] `sqlite3rebaser_rebase_strm` -- [ ] `sqlite3session_config` - -## List of virtual table methods supported - -- [X] `xCreate` -- [X] `xConnect` -- [X] `xBestIndex` -- [X] `xDisconnect` -- [X] `xDestroy` -- [X] `xOpen` -- [X] `xClose` -- [X] `xFilter` -- [X] `xNext` -- [X] `xEof` -- [X] `xColumn` -- [X] `xRowid` -- [X] `xUpdate` -- [X] `xBegin` -- [X] `xSync` -- [X] `xCommit` -- [X] `xRollback` -- [ ] `xFindFunction` -- [ ] `xRename` -- [ ] `xSavepoint` -- [ ] `xRelease` -- [ ] `xRollbackTo` -- [ ] `xShadowName` -- [ ] `xIntegrity` diff --git a/vendor/rusqlite/examples/load_extension.rs b/vendor/rusqlite/examples/load_extension.rs deleted file mode 100644 index eee07de..0000000 --- a/vendor/rusqlite/examples/load_extension.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Ensure `loadable_extension.rs` works. - -use rusqlite::{Connection, Result}; -use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; - -fn main() -> Result<()> { - let db = Connection::open_in_memory()?; - - unsafe { - db.load_extension_enable()?; - db.load_extension( - format!("target/debug/examples/{DLL_PREFIX}loadable_extension{DLL_SUFFIX}"), - None::<&str>, - )?; - db.load_extension_disable()?; - } - - let str = db.query_row("SELECT rusqlite_test_function()", [], |row| { - row.get::<_, String>(0) - })?; - assert_eq!(&str, "Rusqlite extension loaded correctly!"); - Ok(()) -} diff --git a/vendor/rusqlite/examples/loadable_extension.rs b/vendor/rusqlite/examples/loadable_extension.rs deleted file mode 100644 index 84999a5..0000000 --- a/vendor/rusqlite/examples/loadable_extension.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Adaptation of https://sqlite.org/loadext.html#programming_loadable_extensions -//! -//! # build -//! ```sh -//! cargo build --example loadable_extension --features "loadable_extension functions trace" -//! ``` -//! -//! # test -//! ```sh -//! sqlite> .log on -//! sqlite> .load target/debug/examples/libloadable_extension.so -//! (28) Rusqlite extension initialized -//! sqlite> SELECT rusqlite_test_function(); -//! Rusqlite extension loaded correctly! -//! ``` -use std::os::raw::{c_char, c_int}; - -use rusqlite::ffi; -use rusqlite::functions::FunctionFlags; -use rusqlite::types::{ToSqlOutput, Value}; -use rusqlite::{Connection, Result}; - -/// Entry point for SQLite to load the extension. -/// See on this function's name and usage. -/// # Safety -/// This function is called by SQLite and must be safe to call. -#[no_mangle] -pub unsafe extern "C" fn sqlite3_extension_init( - db: *mut ffi::sqlite3, - pz_err_msg: *mut *mut c_char, - p_api: *mut ffi::sqlite3_api_routines, -) -> c_int { - Connection::extension_init2(db, pz_err_msg, p_api, extension_init) -} - -fn extension_init(db: Connection) -> Result { - db.create_scalar_function( - c"rusqlite_test_function", - 0, - FunctionFlags::SQLITE_DETERMINISTIC, - |_ctx| { - Ok(ToSqlOutput::Owned(Value::Text( - "Rusqlite extension loaded correctly!".to_string(), - ))) - }, - )?; - rusqlite::trace::log(ffi::SQLITE_WARNING, "Rusqlite extension initialized"); - Ok(false) -} diff --git a/vendor/rusqlite/examples/owning_rows.rs b/vendor/rusqlite/examples/owning_rows.rs deleted file mode 100644 index 1e42212..0000000 --- a/vendor/rusqlite/examples/owning_rows.rs +++ /dev/null @@ -1,27 +0,0 @@ -extern crate rusqlite; - -use rusqlite::{CachedStatement, Connection, Result, Rows}; -use self_cell::{self_cell, MutBorrow}; - -type RowsRef<'a> = Rows<'a>; - -self_cell!( - struct OwningRows<'conn> { - owner: MutBorrow>, - #[covariant] - dependent: RowsRef, - } -); - -fn main() -> Result<()> { - let conn = Connection::open_in_memory()?; - let stmt = conn.prepare_cached("SELECT 1")?; - let mut or = OwningRows::try_new(MutBorrow::new(stmt), |s| s.borrow_mut().query([]))?; - or.with_dependent_mut(|_stmt, rows| -> Result<()> { - while let Some(row) = rows.next()? { - assert_eq!(Ok(1), row.get(0)); - } - Ok(()) - })?; - Ok(()) -} diff --git a/vendor/rusqlite/examples/owning_statement.rs b/vendor/rusqlite/examples/owning_statement.rs deleted file mode 100644 index 21ee1d6..0000000 --- a/vendor/rusqlite/examples/owning_statement.rs +++ /dev/null @@ -1,30 +0,0 @@ -extern crate rusqlite; -use rusqlite::{CachedStatement, Connection, Result, Rows}; -use self_cell::{self_cell, MutBorrow}; - -type CachedStatementRef<'a> = CachedStatement<'a>; - -// Caveat: single statement at a time for one connection. -// But if you need multiple statements, you can still create your own struct -// with multiple fields (one for each statement). -self_cell!( - struct OwningStatement { - owner: MutBorrow, - #[covariant] - dependent: CachedStatementRef, - } -); - -fn main() -> Result<()> { - let conn = Connection::open_in_memory()?; - - let mut os = OwningStatement::try_new(MutBorrow::new(conn), |s| { - s.borrow_mut().prepare_cached("SELECT 1") - })?; - - let mut rows = os.with_dependent_mut(|_conn, stmt| -> Result> { stmt.query([]) })?; - while let Some(row) = rows.next()? { - assert_eq!(Ok(1), row.get(0)); - } - Ok(()) -} diff --git a/vendor/rusqlite/examples/persons/README.md b/vendor/rusqlite/examples/persons/README.md deleted file mode 100644 index f649031..0000000 --- a/vendor/rusqlite/examples/persons/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Persons example - -## Run - -``` -$ cargo run --example persons -``` - -## Run (wasm32-wasi) - -### Requisites - -- [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) -- [wasmtime](https://wasmtime.dev/) - -``` -# Set to wasi-sdk directory -$ export WASI_SDK_PATH=`` -$ export CC_wasm32_wasi="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot" -# Build -$ cargo build --example persons --target wasm32-wasi --release --features bundled -# Run -$ wasmtime target/wasm32-wasi/release/examples/persons.wasm -Found persons: -ID: 1, Name: Steven -ID: 2, Name: John -ID: 3, Name: Alex -``` - -## Run (wasm32-unknown-unknown) - -### Requisites - -- [emscripten](https://emscripten.org/docs/getting_started/downloads.html) -- [wasm-bindgen-cli](https://github.com/wasm-bindgen/wasm-bindgen) - -``` -# Build -$ cargo build --example persons --target wasm32-unknown-unknown --release -# Bindgen -$ wasm-bindgen target/wasm32-unknown-unknown/release/examples/persons.wasm --out-dir target/pkg --nodejs -# Run -$ node target/pkg/persons.js -Found persons: -ID: 1, Name: Steven -ID: 2, Name: John -ID: 3, Name: Alex -``` diff --git a/vendor/rusqlite/examples/persons/main.rs b/vendor/rusqlite/examples/persons/main.rs deleted file mode 100644 index 6faeddd..0000000 --- a/vendor/rusqlite/examples/persons/main.rs +++ /dev/null @@ -1,57 +0,0 @@ -use rusqlite::{Connection, Result}; -#[cfg(all(target_family = "wasm", target_os = "unknown"))] -use wasm_bindgen::prelude::wasm_bindgen; - -#[cfg(all(target_family = "wasm", target_os = "unknown"))] -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - -#[cfg(all(target_family = "wasm", target_os = "unknown"))] -macro_rules! println { - ($($t:tt)*) => (log(&format_args!($($t)*).to_string())) -} - -struct Person { - id: i32, - name: String, -} - -#[cfg_attr(all(target_family = "wasm", target_os = "unknown"), wasm_bindgen(main))] -fn main() -> Result<()> { - let conn = Connection::open_in_memory()?; - - conn.execute( - "CREATE TABLE IF NOT EXISTS persons ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL - )", - (), // empty list of parameters. - )?; - - conn.execute( - "INSERT INTO persons (name) VALUES (?1), (?2), (?3)", - ["Steven", "John", "Alex"].map(|n| n.to_string()), - )?; - - let mut stmt = conn.prepare("SELECT id, name FROM persons")?; - let rows = stmt.query_map([], |row| { - Ok(Person { - id: row.get(0)?, - name: row.get(1)?, - }) - })?; - - println!("Found persons:"); - - for person in rows { - match person { - Ok(p) => println!("ID: {}, Name: {}", p.id, p.name), - Err(e) => eprintln!("Error: {e:?}"), - } - } - - Ok(()) -} diff --git a/vendor/rusqlite/src/auto_extension.rs b/vendor/rusqlite/src/auto_extension.rs deleted file mode 100644 index e3b7e22..0000000 --- a/vendor/rusqlite/src/auto_extension.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Automatic extension loading -use super::ffi; -use crate::error::{check, to_sqlite_error}; -use crate::{Connection, Error, Result}; -use std::ffi::{c_char, c_int}; -use std::panic::catch_unwind; - -/// Automatic extension initialization routine -pub type AutoExtension = fn(Connection) -> Result<()>; - -/// Raw automatic extension initialization routine -pub type RawAutoExtension = unsafe extern "C" fn( - db: *mut ffi::sqlite3, - pz_err_msg: *mut *mut c_char, - _: *const ffi::sqlite3_api_routines, -) -> c_int; - -/// Bridge between `RawAutoExtension` and `AutoExtension` -/// -/// # Safety -/// * Opening a database from an auto-extension handler will lead to -/// an endless recursion of the auto-handler triggering itself -/// indirectly for each newly-opened database. -/// * Results are undefined if the given db is closed by an auto-extension. -/// * The list of auto-extensions should not be manipulated from an auto-extension. -pub unsafe fn init_auto_extension( - db: *mut ffi::sqlite3, - pz_err_msg: *mut *mut c_char, - ax: AutoExtension, -) -> c_int { - let r = catch_unwind(|| { - let c = Connection::from_handle(db); - c.and_then(ax) - }) - .unwrap_or_else(|_| Err(Error::UnwindingPanic)); - match r { - Err(e) => to_sqlite_error(&e, pz_err_msg), - _ => ffi::SQLITE_OK, - } -} - -/// Register au auto-extension -/// -/// # Safety -/// * Opening a database from an auto-extension handler will lead to -/// an endless recursion of the auto-handler triggering itself -/// indirectly for each newly-opened database. -/// * Results are undefined if the given db is closed by an auto-extension. -/// * The list of auto-extensions should not be manipulated from an auto-extension. -pub unsafe fn register_auto_extension(ax: RawAutoExtension) -> Result<()> { - check(ffi::sqlite3_auto_extension(Some(ax))) -} - -/// Unregister the initialization routine -pub fn cancel_auto_extension(ax: RawAutoExtension) -> bool { - unsafe { ffi::sqlite3_cancel_auto_extension(Some(ax)) == 1 } -} - -/// Disable all automatic extensions previously registered -pub fn reset_auto_extension() { - unsafe { ffi::sqlite3_reset_auto_extension() } -} diff --git a/vendor/rusqlite/src/backup.rs b/vendor/rusqlite/src/backup.rs deleted file mode 100644 index 7413e20..0000000 --- a/vendor/rusqlite/src/backup.rs +++ /dev/null @@ -1,442 +0,0 @@ -//! Online SQLite backup API. -//! -//! Alternatively, you can create a backup with a simple -//! [`VACUUM INTO `](https://sqlite.org/lang_vacuum.html#vacuuminto). -//! -//! To create a [`Backup`], you must have two distinct [`Connection`]s - one -//! for the source (which can be used while the backup is running) and one for -//! the destination (which cannot). A [`Backup`] handle exposes three methods: -//! [`step`](Backup::step) will attempt to back up a specified number of pages, -//! [`progress`](Backup::progress) gets the current progress of the backup as of -//! the last call to [`step`](Backup::step), and -//! [`run_to_completion`](Backup::run_to_completion) will attempt to back up the -//! entire source database, allowing you to specify how many pages are backed up -//! at a time and how long the thread should sleep between chunks of pages. -//! -//! The following example is equivalent to "Example 2: Online Backup of a -//! Running Database" from [SQLite's Online Backup API -//! documentation](https://www.sqlite.org/backup.html). -//! -//! ```rust,no_run -//! # use rusqlite::{backup, Connection, Result}; -//! # use std::path::Path; -//! # use std::time; -//! -//! fn backup_db>( -//! src: &Connection, -//! dst: P, -//! progress: fn(backup::Progress), -//! ) -> Result<()> { -//! let mut dst = Connection::open(dst)?; -//! let backup = backup::Backup::new(src, &mut dst)?; -//! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) -//! } -//! ``` - -use std::marker::PhantomData; -use std::path::Path; -use std::ptr; - -use std::ffi::c_int; -use std::thread; -use std::time::Duration; - -use crate::ffi; - -use crate::error::error_from_handle; -use crate::{Connection, Name, Result, MAIN_DB}; - -impl Connection { - /// Back up the `name` database to the given - /// destination path. - /// - /// If `progress` is not `None`, it will be called periodically - /// until the backup completes. - /// - /// For more fine-grained control over the backup process (e.g., - /// to sleep periodically during the backup or to back up to an - /// already-open database connection), see the `backup` module. - /// - /// # Failure - /// - /// Will return `Err` if the destination path cannot be opened - /// or if the backup fails. - pub fn backup>( - &self, - name: N, - dst_path: P, - progress: Option, - ) -> Result<()> { - use self::StepResult::{Busy, Done, Locked, More}; - let mut dst = Self::open(dst_path)?; - let backup = Backup::new_with_names(self, name, &mut dst, MAIN_DB)?; - - let mut r = More; - while r == More { - r = backup.step(100)?; - if let Some(f) = progress { - f(backup.progress()); - } - } - - match r { - Done => Ok(()), - Busy => Err(unsafe { error_from_handle(ptr::null_mut(), ffi::SQLITE_BUSY) }), - Locked => Err(unsafe { error_from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED) }), - More => unreachable!(), - } - } - - /// Restore the given source path into the - /// `name` database. If `progress` is not `None`, it will be - /// called periodically until the restore completes. - /// - /// For more fine-grained control over the restore process (e.g., - /// to sleep periodically during the restore or to restore from an - /// already-open database connection), see the `backup` module. - /// - /// # Failure - /// - /// Will return `Err` if the destination path cannot be opened - /// or if the restore fails. - pub fn restore, F: Fn(Progress)>( - &mut self, - name: N, - src_path: P, - progress: Option, - ) -> Result<()> { - use self::StepResult::{Busy, Done, Locked, More}; - let src = Self::open(src_path)?; - let restore = Backup::new_with_names(&src, MAIN_DB, self, name)?; - - let mut r = More; - let mut busy_count = 0_i32; - 'restore_loop: while r == More || r == Busy { - r = restore.step(100)?; - if let Some(ref f) = progress { - f(restore.progress()); - } - if r == Busy { - busy_count += 1; - if busy_count >= 3 { - break 'restore_loop; - } - thread::sleep(Duration::from_millis(100)); - } - } - - match r { - Done => Ok(()), - Busy => Err(unsafe { error_from_handle(ptr::null_mut(), ffi::SQLITE_BUSY) }), - Locked => Err(unsafe { error_from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED) }), - More => unreachable!(), - } - } -} - -/// Possible successful results of calling -/// [`Backup::step`]. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum StepResult { - /// The backup is complete. - Done, - - /// The step was successful but there are still more pages that need to be - /// backed up. - More, - - /// The step failed because appropriate locks could not be acquired. This is - /// not a fatal error - the step can be retried. - Busy, - - /// The step failed because the source connection was writing to the - /// database. This is not a fatal error - the step can be retried. - Locked, -} - -/// Struct specifying the progress of a backup. -/// -/// The percentage completion can be calculated as `(pagecount - remaining) / -/// pagecount`. The progress of a backup is as of the last call to -/// [`step`](Backup::step) - if the source database is modified after a call to -/// [`step`](Backup::step), the progress value will become outdated and -/// potentially incorrect. -#[derive(Copy, Clone, Debug)] -pub struct Progress { - /// Number of pages in the source database that still need to be backed up. - pub remaining: c_int, - /// Total number of pages in the source database. - pub pagecount: c_int, -} - -/// A handle to an online backup. -pub struct Backup<'a, 'b> { - phantom_from: PhantomData<&'a Connection>, - to: &'b Connection, - b: *mut ffi::sqlite3_backup, -} - -impl Backup<'_, '_> { - /// Attempt to create a new handle that will allow backups from `from` to - /// `to`. Note that `to` is a `&mut` - this is because SQLite forbids any - /// API calls on the destination of a backup while the backup is taking - /// place. - /// - /// # Failure - /// - /// Will return `Err` if the underlying `sqlite3_backup_init` call returns - /// `NULL`. - #[inline] - pub fn new<'a, 'b>(from: &'a Connection, to: &'b mut Connection) -> Result> { - Backup::new_with_names(from, MAIN_DB, to, MAIN_DB) - } - - /// Attempt to create a new handle that will allow backups from the - /// `from_name` database of `from` to the `to_name` database of `to`. Note - /// that `to` is a `&mut` - this is because SQLite forbids any API calls on - /// the destination of a backup while the backup is taking place. - /// - /// # Failure - /// - /// Will return `Err` if the underlying `sqlite3_backup_init` call returns - /// `NULL`. - pub fn new_with_names<'a, 'b, F: Name, T: Name>( - from: &'a Connection, - from_name: F, - to: &'b mut Connection, - to_name: T, - ) -> Result> { - let to_name = to_name.as_cstr()?; - let from_name = from_name.as_cstr()?; - - let to_db = to.db.borrow_mut().db; - - let b = unsafe { - let b = ffi::sqlite3_backup_init( - to_db, - to_name.as_ptr(), - from.db.borrow_mut().db, - from_name.as_ptr(), - ); - if b.is_null() { - return Err(error_from_handle(to_db, ffi::sqlite3_errcode(to_db))); - } - b - }; - - Ok(Backup { - phantom_from: PhantomData, - to, - b, - }) - } - - /// Gets the progress of the backup as of the last call to - /// [`step`](Backup::step). - #[inline] - #[must_use] - pub fn progress(&self) -> Progress { - unsafe { - Progress { - remaining: ffi::sqlite3_backup_remaining(self.b), - pagecount: ffi::sqlite3_backup_pagecount(self.b), - } - } - } - - /// Attempts to back up the given number of pages. If `num_pages` is - /// negative, will attempt to back up all remaining pages. This will hold a - /// lock on the source database for the duration, so it is probably not - /// what you want for databases that are currently active (see - /// [`run_to_completion`](Backup::run_to_completion) for a better - /// alternative). - /// - /// # Failure - /// - /// Will return `Err` if the underlying `sqlite3_backup_step` call returns - /// an error code other than `DONE`, `OK`, `BUSY`, or `LOCKED`. `BUSY` and - /// `LOCKED` are transient errors and are therefore returned as possible - /// `Ok` values. - #[inline] - pub fn step(&self, num_pages: c_int) -> Result { - use self::StepResult::{Busy, Done, Locked, More}; - - let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) }; - match rc { - ffi::SQLITE_DONE => Ok(Done), - ffi::SQLITE_OK => Ok(More), - ffi::SQLITE_BUSY => Ok(Busy), - ffi::SQLITE_LOCKED => Ok(Locked), - _ => self.to.decode_result(rc).map(|_| More), - } - } - - /// Attempts to run the entire backup. Will call - /// [`step(pages_per_step)`](Backup::step) as many times as necessary, - /// sleeping for `pause_between_pages` between each call to give the - /// source database time to process any pending queries. This is a - /// direct implementation of "Example 2: Online Backup of a Running - /// Database" from [SQLite's Online Backup API documentation](https://www.sqlite.org/backup.html). - /// - /// If `progress` is not `None`, it will be called after each step with the - /// current progress of the backup. Note that is possible the progress may - /// not change if the step returns `Busy` or `Locked` even though the - /// backup is still running. - /// - /// # Failure - /// - /// Will return `Err` if any of the calls to [`step`](Backup::step) return - /// `Err`. - pub fn run_to_completion( - &self, - pages_per_step: c_int, - pause_between_pages: Duration, - progress: Option, - ) -> Result<()> { - use self::StepResult::{Busy, Done, Locked, More}; - - assert!(pages_per_step > 0, "pages_per_step must be positive"); - - loop { - let r = self.step(pages_per_step)?; - if let Some(progress) = progress { - progress(self.progress()); - } - match r { - More | Busy | Locked => thread::sleep(pause_between_pages), - Done => return Ok(()), - } - } - } -} - -impl Drop for Backup<'_, '_> { - #[inline] - fn drop(&mut self) { - unsafe { ffi::sqlite3_backup_finish(self.b) }; - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::{Backup, Progress}; - use crate::{Connection, Result, MAIN_DB, TEMP_DB}; - use std::time::Duration; - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn backup_to_path() -> Result<()> { - let src = Connection::open_in_memory()?; - src.execute_batch("CREATE TABLE foo AS SELECT 42 AS x")?; - - let temp_dir = tempfile::tempdir().unwrap(); - let path = temp_dir.path().join("test.db3"); - - fn progress(_: Progress) {} - - src.backup(MAIN_DB, path.as_path(), Some(progress))?; - - let mut dst = Connection::open_in_memory()?; - dst.restore(MAIN_DB, path, Some(progress))?; - - Ok(()) - } - - #[test] - fn test_backup() -> Result<()> { - let src = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(42); - END;"; - src.execute_batch(sql)?; - - let mut dst = Connection::open_in_memory()?; - - { - let backup = Backup::new(&src, &mut dst)?; - backup.step(-1)?; - } - - assert_eq!(42, dst.one_column::("SELECT x FROM foo", [])?); - - src.execute_batch("INSERT INTO foo VALUES(43)")?; - - { - let backup = Backup::new(&src, &mut dst)?; - backup.run_to_completion(5, Duration::from_millis(250), None)?; - } - - let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo", [])?; - assert_eq!(42 + 43, the_answer); - Ok(()) - } - - #[test] - fn test_backup_temp() -> Result<()> { - let src = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TEMPORARY TABLE foo(x INTEGER); - INSERT INTO foo VALUES(42); - END;"; - src.execute_batch(sql)?; - - let mut dst = Connection::open_in_memory()?; - - { - let backup = Backup::new_with_names(&src, TEMP_DB, &mut dst, MAIN_DB)?; - backup.step(-1)?; - } - - assert_eq!(42, dst.one_column::("SELECT x FROM foo", [])?); - - src.execute_batch("INSERT INTO foo VALUES(43)")?; - - { - let backup = Backup::new_with_names(&src, TEMP_DB, &mut dst, MAIN_DB)?; - backup.run_to_completion(5, Duration::from_millis(250), None)?; - } - - let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo", [])?; - assert_eq!(42 + 43, the_answer); - Ok(()) - } - - #[test] - fn test_backup_attached() -> Result<()> { - let src = Connection::open_in_memory()?; - let sql = "ATTACH DATABASE ':memory:' AS my_attached; - BEGIN; - CREATE TABLE my_attached.foo(x INTEGER); - INSERT INTO my_attached.foo VALUES(42); - END;"; - src.execute_batch(sql)?; - - let mut dst = Connection::open_in_memory()?; - - { - let backup = Backup::new_with_names(&src, c"my_attached", &mut dst, MAIN_DB)?; - backup.step(-1)?; - } - - assert_eq!(42, dst.one_column::("SELECT x FROM foo", [])?); - - src.execute_batch("INSERT INTO foo VALUES(43)")?; - - { - let backup = Backup::new_with_names(&src, c"my_attached", &mut dst, MAIN_DB)?; - backup.run_to_completion(5, Duration::from_millis(250), None)?; - } - - let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo", [])?; - assert_eq!(42 + 43, the_answer); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/bind.rs b/vendor/rusqlite/src/bind.rs deleted file mode 100644 index f307a7a..0000000 --- a/vendor/rusqlite/src/bind.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{ffi, Error, Result, Statement}; -use std::ffi::CStr; - -mod sealed { - use std::ffi::CStr; - /// This trait exists just to ensure that the only impls of `trait BindIndex` - /// that are allowed are ones in this crate. - pub trait Sealed {} - impl Sealed for usize {} - impl Sealed for &str {} - impl Sealed for &CStr {} -} - -/// A trait implemented by types that can index into parameters of a statement. -/// -/// It is only implemented for `usize` and `&str` and `&CStr`. -pub trait BindIndex: sealed::Sealed { - /// Returns the index of the associated parameter, or `Error` if no such - /// parameter exists. - fn idx(&self, stmt: &Statement<'_>) -> Result; -} - -impl BindIndex for usize { - #[inline] - fn idx(&self, _: &Statement<'_>) -> Result { - // No validation - Ok(*self) - } -} - -impl BindIndex for &'_ str { - fn idx(&self, stmt: &Statement<'_>) -> Result { - match stmt.parameter_index(self)? { - Some(idx) => Ok(idx), - None => Err(Error::InvalidParameterName(self.to_string())), - } - } -} -/// C-string literal to avoid alloc -impl BindIndex for &CStr { - fn idx(&self, stmt: &Statement<'_>) -> Result { - let r = unsafe { ffi::sqlite3_bind_parameter_index(stmt.ptr(), self.as_ptr()) }; - match r { - 0 => Err(Error::InvalidParameterName( - self.to_string_lossy().to_string(), - )), - i => Ok(i as usize), - } - } -} - -#[cfg(test)] -mod test { - use crate::{ffi, Connection, Error, Result}; - - #[test] - fn invalid_name() -> Result<()> { - let db = Connection::open_in_memory()?; - let mut stmt = db.prepare("SELECT 1")?; - let err = stmt.raw_bind_parameter(1, 1).unwrap_err(); - assert_eq!( - err.sqlite_error_code(), - Some(ffi::ErrorCode::ParameterOutOfRange), - ); - let err = stmt.raw_bind_parameter(":p1", 1).unwrap_err(); - assert_eq!(err, Error::InvalidParameterName(":p1".to_owned())); - let err = stmt.raw_bind_parameter(c"x", 1).unwrap_err(); - assert_eq!(err, Error::InvalidParameterName("x".to_owned())); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/blob/mod.rs b/vendor/rusqlite/src/blob/mod.rs deleted file mode 100644 index 6e3e020..0000000 --- a/vendor/rusqlite/src/blob/mod.rs +++ /dev/null @@ -1,564 +0,0 @@ -//! Incremental BLOB I/O. -//! -//! Note that SQLite does not provide API-level access to change the size of a -//! BLOB; that must be performed through SQL statements. -//! -//! There are two choices for how to perform IO on a [`Blob`]. -//! -//! 1. The implementations it provides of the `std::io::Read`, `std::io::Write`, -//! and `std::io::Seek` traits. -//! -//! 2. A positional IO API, e.g. [`Blob::read_at`], [`Blob::write_at`] and -//! similar. -//! -//! Documenting these in order: -//! -//! ## 1. `std::io` trait implementations. -//! -//! `Blob` conforms to `std::io::Read`, `std::io::Write`, and `std::io::Seek`, -//! so it plays nicely with other types that build on these (such as -//! `std::io::BufReader` and `std::io::BufWriter`). However, you must be careful -//! with the size of the blob. For example, when using a `BufWriter`, the -//! `BufWriter` will accept more data than the `Blob` will allow, so make sure -//! to call `flush` and check for errors. (See the unit tests in this module for -//! an example.) -//! -//! ## 2. Positional IO -//! -//! `Blob`s also offer a `pread` / `pwrite`-style positional IO api in the form -//! of [`Blob::read_at`], [`Blob::write_at`], [`Blob::raw_read_at`], -//! [`Blob::read_at_exact`], and [`Blob::raw_read_at_exact`]. -//! -//! These APIs all take the position to read from or write to from as a -//! parameter, instead of using an internal `pos` value. -//! -//! ### Positional IO Read Variants -//! -//! For the `read` functions, there are several functions provided: -//! -//! - [`Blob::read_at`] -//! - [`Blob::raw_read_at`] -//! - [`Blob::read_at_exact`] -//! - [`Blob::raw_read_at_exact`] -//! -//! These can be divided along two axes: raw/not raw, and exact/inexact: -//! -//! 1. Raw/not raw refers to the type of the destination buffer. The raw -//! functions take a `&mut [MaybeUninit]` as the destination buffer, -//! where the "normal" functions take a `&mut [u8]`. -//! -//! Using `MaybeUninit` here can be more efficient in some cases, but is -//! often inconvenient, so both are provided. -//! -//! 2. Exact/inexact refers to whether or not the entire buffer must be -//! filled in order for the call to be considered a success. -//! -//! The "exact" functions require the provided buffer be entirely filled, or -//! they return an error, whereas the "inexact" functions read as much out of -//! the blob as is available, and return how much they were able to read. -//! -//! The inexact functions are preferable if you do not know the size of the -//! blob already, and the exact functions are preferable if you do. -//! -//! ### Comparison to using the `std::io` traits: -//! -//! In general, the positional methods offer the following Pro/Cons compared to -//! using the implementation `std::io::{Read, Write, Seek}` we provide for -//! `Blob`: -//! -//! 1. (Pro) There is no need to first seek to a position in order to perform IO -//! on it as the position is a parameter. -//! -//! 2. (Pro) `Blob`'s positional read functions don't mutate the blob in any -//! way, and take `&self`. No `&mut` access required. -//! -//! 3. (Pro) Positional IO functions return `Err(rusqlite::Error)` on failure, -//! rather than `Err(std::io::Error)`. Returning `rusqlite::Error` is more -//! accurate and convenient. -//! -//! Note that for the `std::io` API, no data is lost however, and it can be -//! recovered with `io_err.downcast::()` (this can be easy -//! to forget, though). -//! -//! 4. (Pro, for now). A `raw` version of the read API exists which can allow -//! reading into a `&mut [MaybeUninit]` buffer, which avoids a potential -//! costly initialization step. (However, `std::io` traits will certainly -//! gain this someday, which is why this is only a "Pro, for now"). -//! -//! 5. (Con) The set of functions is more bare-bones than what is offered in -//! `std::io`, which has a number of adapters, handy algorithms, further -//! traits. -//! -//! 6. (Con) No meaningful interoperability with other crates, so if you need -//! that you must use `std::io`. -//! -//! To generalize: the `std::io` traits are useful because they conform to a -//! standard interface that a lot of code knows how to handle, however that -//! interface is not a perfect fit for [`Blob`], so another small set of -//! functions is provided as well. -//! -//! # Example (`std::io`) -//! -//! ```rust -//! # use rusqlite::blob::ZeroBlob; -//! # use rusqlite::{Connection, MAIN_DB}; -//! # use std::error::Error; -//! # use std::io::{Read, Seek, SeekFrom, Write}; -//! # fn main() -> Result<(), Box> { -//! let db = Connection::open_in_memory()?; -//! db.execute_batch("CREATE TABLE test_table (content BLOB);")?; -//! -//! // Insert a BLOB into the `content` column of `test_table`. Note that the Blob -//! // I/O API provides no way of inserting or resizing BLOBs in the DB -- this -//! // must be done via SQL. -//! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?; -//! -//! // Get the row id off the BLOB we just inserted. -//! let rowid = db.last_insert_rowid(); -//! // Open the BLOB we just inserted for IO. -//! let mut blob = db.blob_open(MAIN_DB, "test_table", "content", rowid, false)?; -//! -//! // Write some data into the blob. Make sure to test that the number of bytes -//! // written matches what you expect; if you try to write too much, the data -//! // will be truncated to the size of the BLOB. -//! let bytes_written = blob.write(b"01234567")?; -//! assert_eq!(bytes_written, 8); -//! -//! // Move back to the start and read into a local buffer. -//! // Same guidance - make sure you check the number of bytes read! -//! blob.seek(SeekFrom::Start(0))?; -//! let mut buf = [0u8; 20]; -//! let bytes_read = blob.read(&mut buf[..])?; -//! assert_eq!(bytes_read, 10); // note we read 10 bytes because the blob has size 10 -//! -//! // Insert another BLOB, this time using a parameter passed in from -//! // rust (potentially with a dynamic size). -//! db.execute( -//! "INSERT INTO test_table (content) VALUES (?1)", -//! [ZeroBlob(64)], -//! )?; -//! -//! // given a new row ID, we can reopen the blob on that row -//! let rowid = db.last_insert_rowid(); -//! blob.reopen(rowid)?; -//! // Just check that the size is right. -//! assert_eq!(blob.len(), 64); -//! # Ok(()) -//! # } -//! ``` -//! -//! # Example (Positional) -//! -//! ```rust -//! # use rusqlite::blob::ZeroBlob; -//! # use rusqlite::{Connection, MAIN_DB}; -//! # use std::error::Error; -//! # fn main() -> Result<(), Box> { -//! let db = Connection::open_in_memory()?; -//! db.execute_batch("CREATE TABLE test_table (content BLOB);")?; -//! // Insert a blob into the `content` column of `test_table`. Note that the Blob -//! // I/O API provides no way of inserting or resizing blobs in the DB -- this -//! // must be done via SQL. -//! db.execute("INSERT INTO test_table (content) VALUES (ZEROBLOB(10))", [])?; -//! // Get the row id off the blob we just inserted. -//! let rowid = db.last_insert_rowid(); -//! // Open the blob we just inserted for IO. -//! let mut blob = db.blob_open(MAIN_DB, "test_table", "content", rowid, false)?; -//! // Write some data into the blob. -//! blob.write_at(b"ABCDEF", 2)?; -//! -//! // Read the whole blob into a local buffer. -//! let mut buf = [0u8; 10]; -//! blob.read_at_exact(&mut buf, 0)?; -//! assert_eq!(&buf, b"\0\0ABCDEF\0\0"); -//! -//! // Insert another blob, this time using a parameter passed in from -//! // rust (potentially with a dynamic size). -//! db.execute( -//! "INSERT INTO test_table (content) VALUES (?1)", -//! [ZeroBlob(64)], -//! )?; -//! -//! // given a new row ID, we can reopen the blob on that row -//! let rowid = db.last_insert_rowid(); -//! blob.reopen(rowid)?; -//! assert_eq!(blob.len(), 64); -//! # Ok(()) -//! # } -//! ``` -use std::cmp::min; -use std::io; -use std::ptr; - -use super::ffi; -use super::types::{ToSql, ToSqlOutput}; -use crate::{Connection, Name, Result}; - -mod pos_io; - -/// Handle to an open BLOB. See -/// [`rusqlite::blob`](crate::blob) documentation for in-depth discussion. -pub struct Blob<'conn> { - conn: &'conn Connection, - blob: *mut ffi::sqlite3_blob, - // used by std::io implementations, - pos: i32, -} - -impl Connection { - /// Open a handle to the BLOB located in `row_id`, - /// `column`, `table` in database `db`. - /// - /// # Failure - /// - /// Will return `Err` if `db`/`table`/`column` cannot be converted to a - /// C-compatible string or if the underlying SQLite BLOB open call - /// fails. - #[inline] - pub fn blob_open( - &self, - db: D, - table: N, - column: N, - row_id: i64, - read_only: bool, - ) -> Result> { - let c = self.db.borrow_mut(); - let mut blob = ptr::null_mut(); - let db = db.as_cstr()?; - let table = table.as_cstr()?; - let column = column.as_cstr()?; - let rc = unsafe { - ffi::sqlite3_blob_open( - c.db(), - db.as_ptr(), - table.as_ptr(), - column.as_ptr(), - row_id, - !read_only as std::ffi::c_int, - &mut blob, - ) - }; - c.decode_result(rc).map(|_| Blob { - conn: self, - blob, - pos: 0, - }) - } -} - -impl Blob<'_> { - /// Move a BLOB handle to a new row. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite BLOB reopen call fails. - #[inline] - pub fn reopen(&mut self, row: i64) -> Result<()> { - let rc = unsafe { ffi::sqlite3_blob_reopen(self.blob, row) }; - if rc != ffi::SQLITE_OK { - return self.conn.decode_result(rc); - } - self.pos = 0; - Ok(()) - } - - /// Return the size in bytes of the BLOB. - #[inline] - #[must_use] - pub fn size(&self) -> i32 { - unsafe { ffi::sqlite3_blob_bytes(self.blob) } - } - - /// Return the current size in bytes of the BLOB. - #[inline] - #[must_use] - pub fn len(&self) -> usize { - self.size().try_into().unwrap() - } - - /// Return true if the BLOB is empty. - #[inline] - #[must_use] - pub fn is_empty(&self) -> bool { - self.size() == 0 - } - - /// Close a BLOB handle. - /// - /// Calling `close` explicitly is not required (the BLOB will be closed - /// when the `Blob` is dropped), but it is available, so you can get any - /// errors that occur. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite close call fails. - #[inline] - pub fn close(mut self) -> Result<()> { - self.close_() - } - - #[inline] - fn close_(&mut self) -> Result<()> { - let rc = unsafe { ffi::sqlite3_blob_close(self.blob) }; - self.blob = ptr::null_mut(); - self.conn.decode_result(rc) - } -} - -impl io::Read for Blob<'_> { - /// Read data from a BLOB incrementally. Will return Ok(0) if the end of - /// the blob has been reached. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite read call fails. - #[inline] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let max_allowed_len = (self.size() - self.pos) as usize; - let n = min(buf.len(), max_allowed_len) as i32; - if n <= 0 { - return Ok(0); - } - let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_mut_ptr().cast(), n, self.pos) }; - self.conn - .decode_result(rc) - .map(|_| { - self.pos += n; - n as usize - }) - .map_err(io::Error::other) - } -} - -impl io::Write for Blob<'_> { - /// Write data into a BLOB incrementally. Will return `Ok(0)` if the end of - /// the blob has been reached; consider using `Write::write_all(buf)` - /// if you want to get an error if the entirety of the buffer cannot be - /// written. - /// - /// This function may only modify the contents of the BLOB; it is not - /// possible to increase the size of a BLOB using this API. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite write call fails. - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let max_allowed_len = (self.size() - self.pos) as usize; - let n = min(buf.len(), max_allowed_len) as i32; - if n <= 0 { - return Ok(0); - } - let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) }; - self.conn - .decode_result(rc) - .map(|_| { - self.pos += n; - n as usize - }) - .map_err(io::Error::other) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl io::Seek for Blob<'_> { - /// Seek to an offset, in bytes, in BLOB. - #[inline] - fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - let pos = match pos { - io::SeekFrom::Start(offset) => offset as i64, - io::SeekFrom::Current(offset) => i64::from(self.pos) + offset, - io::SeekFrom::End(offset) => i64::from(self.size()) + offset, - }; - - if pos < 0 { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid seek to negative position", - )) - } else if pos > i64::from(self.size()) { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "invalid seek to position past end of blob", - )) - } else { - self.pos = pos as i32; - Ok(pos as u64) - } - } -} - -#[expect(unused_must_use)] -impl Drop for Blob<'_> { - #[inline] - fn drop(&mut self) { - self.close_(); - } -} - -/// BLOB of length N that is filled with zeroes. -/// -/// Zeroblobs are intended to serve as placeholders for BLOBs whose content is -/// later written using incremental BLOB I/O routines. -/// -/// A negative value for the zeroblob results in a zero-length BLOB. -#[derive(Copy, Clone)] -pub struct ZeroBlob(pub i32); - -impl ToSql for ZeroBlob { - #[inline] - fn to_sql(&self) -> Result> { - let Self(length) = *self; - Ok(ToSqlOutput::ZeroBlob(length)) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result, MAIN_DB}; - use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write}; - - fn db_with_test_blob() -> Result<(Connection, i64)> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE test (content BLOB); - INSERT INTO test VALUES (ZEROBLOB(10)); - END;"; - db.execute_batch(sql)?; - let rowid = db.last_insert_rowid(); - Ok((db, rowid)) - } - - #[test] - fn test_blob() -> Result<()> { - let (db, rowid) = db_with_test_blob()?; - - let mut blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - assert!(!blob.is_empty()); - assert_eq!(10, blob.len()); - 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 - blob.flush().unwrap(); - - blob.reopen(rowid)?; - blob.close()?; - - blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, true)?; - let mut bytes = [0u8; 5]; - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"Clob5"); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"67890"); - assert_eq!(0, blob.read(&mut bytes[..]).unwrap()); - - blob.seek(SeekFrom::Start(2)).unwrap(); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"ob567"); - - // only first 4 bytes of `bytes` should be read into - blob.seek(SeekFrom::Current(-1)).unwrap(); - assert_eq!(4, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"78907"); - - blob.seek(SeekFrom::End(-6)).unwrap(); - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"56789"); - - blob.reopen(rowid)?; - assert_eq!(5, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(&bytes, b"Clob5"); - - // should not be able to seek negative or past end - blob.seek(SeekFrom::Current(-20)).unwrap_err(); - blob.seek(SeekFrom::End(0)).unwrap(); - blob.seek(SeekFrom::Current(1)).unwrap_err(); - - // write_all should detect when we return Ok(0) because there is no space left, - // and return a write error - blob.reopen(rowid)?; - blob.write_all(b"0123456789x").unwrap_err(); - Ok(()) - } - - #[test] - fn test_blob_in_bufreader() -> Result<()> { - let (db, rowid) = db_with_test_blob()?; - - let mut blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - assert_eq!(8, blob.write(b"one\ntwo\n").unwrap()); - - blob.reopen(rowid)?; - let mut reader = BufReader::new(blob); - - let mut line = String::new(); - assert_eq!(4, reader.read_line(&mut line).unwrap()); - assert_eq!("one\n", line); - - line.truncate(0); - assert_eq!(4, reader.read_line(&mut line).unwrap()); - assert_eq!("two\n", line); - - line.truncate(0); - assert_eq!(2, reader.read_line(&mut line).unwrap()); - assert_eq!("\0\0", line); - Ok(()) - } - - #[test] - fn test_blob_in_bufwriter() -> Result<()> { - let (db, rowid) = db_with_test_blob()?; - - { - let blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - let mut writer = BufWriter::new(blob); - - // trying to write too much and then flush should fail - assert_eq!(8, writer.write(b"01234567").unwrap()); - assert_eq!(8, writer.write(b"01234567").unwrap()); - writer.flush().unwrap_err(); - } - - { - // ... but it should've written the first 10 bytes - let mut blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - let mut bytes = [0u8; 10]; - assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(b"0123456701", &bytes); - } - - { - let blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - let mut writer = BufWriter::new(blob); - - // trying to write_all too much should fail - writer.write_all(b"aaaaaaaaaabbbbb").unwrap(); - writer.flush().unwrap_err(); - } - - { - // ... but it should've written the first 10 bytes - let mut blob = db.blob_open(MAIN_DB, c"test", c"content", rowid, false)?; - let mut bytes = [0u8; 10]; - assert_eq!(10, blob.read(&mut bytes[..]).unwrap()); - assert_eq!(b"aaaaaaaaaa", &bytes); - Ok(()) - } - } - - #[test] - fn zero_blob() -> Result<()> { - use crate::types::ToSql; - let zb = super::ZeroBlob(1); - assert!(zb.to_sql().is_ok()); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/blob/pos_io.rs b/vendor/rusqlite/src/blob/pos_io.rs deleted file mode 100644 index 5fe89a5..0000000 --- a/vendor/rusqlite/src/blob/pos_io.rs +++ /dev/null @@ -1,274 +0,0 @@ -use super::Blob; - -use std::mem::MaybeUninit; -use std::slice::from_raw_parts_mut; - -use crate::ffi; -use crate::{Error, Result}; - -impl Blob<'_> { - /// Write `buf` to `self` starting at `write_start`, returning an error if - /// `write_start + buf.len()` is past the end of the blob. - /// - /// If an error is returned, no data is written. - /// - /// Note: the blob cannot be resized using this function -- that must be - /// done using SQL (for example, an `UPDATE` statement). - /// - /// Note: This is part of the positional I/O API, and thus takes an absolute - /// position write to, instead of using the internal position that can be - /// manipulated by the `std::io` traits. - /// - /// Unlike the similarly named [`FileExt::write_at`][fext_write_at] function - /// (from `std::os::unix`), it's always an error to perform a "short write". - /// - /// [fext_write_at]: https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#tymethod.write_at - #[inline] - pub fn write_at(&mut self, buf: &[u8], write_start: usize) -> Result<()> { - let len = self.len(); - - if buf.len().saturating_add(write_start) > len { - return Err(Error::BlobSizeError); - } - // We know `len` fits in an `i32`, so either: - // - // 1. `buf.len() + write_start` overflows, in which case we'd hit the - // return above (courtesy of `saturating_add`). - // - // 2. `buf.len() + write_start` doesn't overflow but is larger than len, - // in which case ditto. - // - // 3. `buf.len() + write_start` doesn't overflow but is less than len. - // This means that both `buf.len()` and `write_start` can also be - // losslessly converted to i32, since `len` came from an i32. - // Sanity check the above. - debug_assert!(i32::try_from(write_start).is_ok() && i32::try_from(buf.len()).is_ok()); - self.conn.decode_result(unsafe { - ffi::sqlite3_blob_write( - self.blob, - buf.as_ptr().cast(), - buf.len() as i32, - write_start as i32, - ) - }) - } - - /// An alias for `write_at` provided for compatibility with the conceptually - /// equivalent [`std::os::unix::FileExt::write_all_at`][write_all_at] - /// function from libstd: - /// - /// [write_all_at]: https://doc.rust-lang.org/std/os/unix/fs/trait.FileExt.html#method.write_all_at - #[inline] - pub fn write_all_at(&mut self, buf: &[u8], write_start: usize) -> Result<()> { - self.write_at(buf, write_start) - } - - /// Read as much as possible from `offset` to `offset + buf.len()` out of - /// `self`, writing into `buf`. On success, returns the number of bytes - /// written. - /// - /// If there's insufficient data in `self`, then the returned value will be - /// less than `buf.len()`. - /// - /// See also [`Blob::raw_read_at`], which can take an uninitialized buffer, - /// or [`Blob::read_at_exact`] which returns an error if the entire `buf` is - /// not read. - /// - /// Note: This is part of the positional I/O API, and thus takes an absolute - /// position to read from, instead of using the internal position that can - /// be manipulated by the `std::io` traits. Consequently, it does not change - /// that value either. - #[inline] - pub fn read_at(&self, buf: &mut [u8], read_start: usize) -> Result { - // Safety: this is safe because `raw_read_at` never stores uninitialized - // data into `as_uninit`. - let as_uninit: &mut [MaybeUninit] = - unsafe { from_raw_parts_mut(buf.as_mut_ptr().cast(), buf.len()) }; - self.raw_read_at(as_uninit, read_start).map(|s| s.len()) - } - - /// Read as much as possible from `offset` to `offset + buf.len()` out of - /// `self`, writing into `buf`. On success, returns the portion of `buf` - /// which was initialized by this call. - /// - /// If there's insufficient data in `self`, then the returned value will be - /// shorter than `buf`. - /// - /// See also [`Blob::read_at`], which takes a `&mut [u8]` buffer instead of - /// a slice of `MaybeUninit`. - /// - /// Note: This is part of the positional I/O API, and thus takes an absolute - /// position to read from, instead of using the internal position that can - /// be manipulated by the `std::io` traits. Consequently, it does not change - /// that value either. - #[inline] - pub fn raw_read_at<'a>( - &self, - buf: &'a mut [MaybeUninit], - read_start: usize, - ) -> Result<&'a mut [u8]> { - let len = self.len(); - - let read_len = match len.checked_sub(read_start) { - None | Some(0) => 0, - Some(v) => v.min(buf.len()), - }; - - if read_len == 0 { - // We could return `Ok(&mut [])`, but it seems confusing that the - // pointers don't match, so fabricate an empty slice of u8 with the - // same base pointer as `buf`. - let empty = unsafe { from_raw_parts_mut(buf.as_mut_ptr().cast::(), 0) }; - return Ok(empty); - } - - // At this point we believe `read_start as i32` is lossless because: - // - // 1. `len as i32` is known to be lossless, since it comes from a SQLite - // api returning an i32. - // - // 2. If we got here, `len.checked_sub(read_start)` was Some (or else - // we'd have hit the `if read_len == 0` early return), so `len` must - // be larger than `read_start`, and so it must fit in i32 as well. - debug_assert!(i32::try_from(read_start).is_ok()); - - // We also believe that `read_start + read_len <= len` because: - // - // 1. This is equivalent to `read_len <= len - read_start` via algebra. - // 2. We know that `read_len` is `min(len - read_start, buf.len())` - // 3. Expanding, this is `min(len - read_start, buf.len()) <= len - read_start`, - // or `min(A, B) <= A` which is clearly true. - // - // Note that this stuff is in debug_assert so no need to use checked_add - // and such -- we'll always panic on overflow in debug builds. - debug_assert!(read_start + read_len <= len); - - // These follow naturally. - debug_assert!(buf.len() >= read_len); - debug_assert!(i32::try_from(buf.len()).is_ok()); - debug_assert!(i32::try_from(read_len).is_ok()); - - unsafe { - self.conn.decode_result(ffi::sqlite3_blob_read( - self.blob, - buf.as_mut_ptr().cast(), - read_len as i32, - read_start as i32, - ))?; - - Ok(from_raw_parts_mut(buf.as_mut_ptr().cast::(), read_len)) - } - } - - /// Equivalent to [`Blob::read_at`], but returns a `BlobSizeError` if `buf` - /// is not fully initialized. - #[inline] - pub fn read_at_exact(&self, buf: &mut [u8], read_start: usize) -> Result<()> { - let n = self.read_at(buf, read_start)?; - if n != buf.len() { - Err(Error::BlobSizeError) - } else { - Ok(()) - } - } - - /// Equivalent to [`Blob::raw_read_at`], but returns a `BlobSizeError` if - /// `buf` is not fully initialized. - #[inline] - pub fn raw_read_at_exact<'a>( - &self, - buf: &'a mut [MaybeUninit], - read_start: usize, - ) -> Result<&'a mut [u8]> { - let buflen = buf.len(); - let initted = self.raw_read_at(buf, read_start)?; - if initted.len() != buflen { - Err(Error::BlobSizeError) - } else { - Ok(initted) - } - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result, MAIN_DB}; - // to ensure we don't modify seek pos - use std::io::Seek as _; - - #[test] - fn test_pos_io() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE test_table(content BLOB);")?; - db.execute("INSERT INTO test_table(content) VALUES (ZEROBLOB(10))", [])?; - - let rowid = db.last_insert_rowid(); - let mut blob = db.blob_open(MAIN_DB, c"test_table", c"content", rowid, false)?; - // modify the seek pos to ensure we aren't using it or modifying it. - blob.seek(std::io::SeekFrom::Start(1)).unwrap(); - - let one2ten: [u8; 10] = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - blob.write_at(&one2ten, 0)?; - - let mut s = [0u8; 10]; - blob.read_at_exact(&mut s, 0)?; - assert_eq!(&s, &one2ten, "write should go through"); - blob.read_at_exact(&mut s, 1).unwrap_err(); - - blob.read_at_exact(&mut s, 0)?; - assert_eq!(&s, &one2ten, "should be unchanged"); - - let mut fives = [0u8; 5]; - blob.read_at_exact(&mut fives, 0)?; - assert_eq!(&fives, &[1u8, 2, 3, 4, 5]); - - blob.read_at_exact(&mut fives, 5)?; - assert_eq!(&fives, &[6u8, 7, 8, 9, 10]); - blob.read_at_exact(&mut fives, 7).unwrap_err(); - blob.read_at_exact(&mut fives, 12).unwrap_err(); - blob.read_at_exact(&mut fives, 10).unwrap_err(); - blob.read_at_exact(&mut fives, i32::MAX as usize) - .unwrap_err(); - blob.read_at_exact(&mut fives, i32::MAX as usize + 1) - .unwrap_err(); - - // zero length writes are fine if in bounds - blob.read_at_exact(&mut [], 10)?; - blob.read_at_exact(&mut [], 0)?; - blob.read_at_exact(&mut [], 5)?; - - blob.write_all_at(&[16, 17, 18, 19, 20], 5)?; - blob.read_at_exact(&mut s, 0)?; - assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]); - - blob.write_at(&[100, 99, 98, 97, 96], 6).unwrap_err(); - blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize) - .unwrap_err(); - blob.write_at(&[100, 99, 98, 97, 96], i32::MAX as usize + 1) - .unwrap_err(); - - blob.read_at_exact(&mut s, 0)?; - assert_eq!(&s, &[1u8, 2, 3, 4, 5, 16, 17, 18, 19, 20]); - - let mut s2: [std::mem::MaybeUninit; 10] = [std::mem::MaybeUninit::uninit(); 10]; - { - let read = blob.raw_read_at_exact(&mut s2, 0)?; - assert_eq!(read, &s); - assert!(std::ptr::eq(read.as_ptr(), s2.as_ptr().cast())); - } - - let mut empty = []; - assert!(std::ptr::eq( - blob.raw_read_at_exact(&mut empty, 0)?.as_ptr(), - empty.as_ptr().cast(), - )); - blob.raw_read_at_exact(&mut s2, 5).unwrap_err(); - - let end_pos = blob.stream_position().unwrap(); - assert_eq!(end_pos, 1); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/busy.rs b/vendor/rusqlite/src/busy.rs deleted file mode 100644 index 639efa6..0000000 --- a/vendor/rusqlite/src/busy.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! Busy handler (when the database is locked) -use std::ffi::{c_int, c_void}; -use std::mem; -use std::panic::catch_unwind; -use std::ptr; -use std::time::Duration; - -use crate::ffi; -use crate::{Connection, InnerConnection, Result}; - -impl Connection { - /// Set a busy handler that sleeps for a specified amount of time when a - /// table is locked. The handler will sleep multiple times until at - /// least "ms" milliseconds of sleeping have accumulated. - /// - /// Calling this routine with an argument equal to zero turns off all busy - /// handlers. - /// - /// There can only be a single busy handler for a particular database - /// connection at any given moment. If another busy handler was defined - /// (using [`busy_handler`](Connection::busy_handler)) prior to calling this - /// routine, that other busy handler is cleared. - /// - /// Newly created connections currently have a default busy timeout of - /// 5000ms, but this may be subject to change. - pub fn busy_timeout(&self, timeout: Duration) -> Result<()> { - let ms: i32 = timeout - .as_secs() - .checked_mul(1000) - .and_then(|t| t.checked_add(timeout.subsec_millis().into())) - .and_then(|t| t.try_into().ok()) - .expect("too big"); - self.db.borrow_mut().busy_timeout(ms) - } - - /// Register a callback to handle `SQLITE_BUSY` errors. - /// - /// If the busy callback is `None`, then `SQLITE_BUSY` is returned - /// immediately upon encountering the lock. The argument to the busy - /// handler callback is the number of times that the - /// busy handler has been invoked previously for the - /// same locking event. If the busy callback returns `false`, then no - /// additional attempts are made to access the - /// database and `SQLITE_BUSY` is returned to the - /// application. If the callback returns `true`, then another attempt - /// is made to access the database and the cycle repeats. - /// - /// There can only be a single busy handler defined for each database - /// connection. Setting a new busy handler clears any previously set - /// handler. Note that calling [`busy_timeout()`](Connection::busy_timeout) - /// or evaluating `PRAGMA busy_timeout=N` will change the busy handler - /// and thus clear any previously set busy handler. - /// - /// Newly created connections default to a - /// [`busy_timeout()`](Connection::busy_timeout) handler with a timeout - /// of 5000ms, although this is subject to change. - pub fn busy_handler(&self, callback: Option bool>) -> Result<()> { - unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int { - let handler_fn: fn(i32) -> bool = mem::transmute(p_arg); - c_int::from(catch_unwind(|| handler_fn(count)).unwrap_or_default()) - } - let c = self.db.borrow_mut(); - c.decode_result(unsafe { - ffi::sqlite3_busy_handler( - c.db(), - callback.as_ref().map(|_| busy_handler_callback as _), - callback.map_or_else(ptr::null_mut, |f| f as *mut c_void), - ) - }) - } -} - -impl InnerConnection { - #[inline] - fn busy_timeout(&mut self, timeout: c_int) -> Result<()> { - let r = unsafe { ffi::sqlite3_busy_timeout(self.db, timeout) }; - self.decode_result(r) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, ErrorCode, Result, TransactionBehavior}; - use std::sync::atomic::{AtomicBool, Ordering}; - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn test_default_busy() -> Result<()> { - let temp_dir = tempfile::tempdir().unwrap(); - let path = temp_dir.path().join("test.db3"); - - let mut db1 = Connection::open(&path)?; - let tx1 = db1.transaction_with_behavior(TransactionBehavior::Exclusive)?; - let db2 = Connection::open(&path)?; - let r: Result<()> = db2.query_row("PRAGMA schema_version", [], |_| unreachable!()); - assert_eq!( - r.unwrap_err().sqlite_error_code(), - Some(ErrorCode::DatabaseBusy) - ); - tx1.rollback() - } - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn test_busy_handler() -> Result<()> { - static CALLED: AtomicBool = AtomicBool::new(false); - fn busy_handler(n: i32) -> bool { - if n > 2 { - false - } else { - CALLED.swap(true, Ordering::Relaxed) - } - } - - let temp_dir = tempfile::tempdir().unwrap(); - let path = temp_dir.path().join("busy-handler.db3"); - - let db1 = Connection::open(&path)?; - db1.execute_batch("CREATE TABLE IF NOT EXISTS t(a)")?; - let db2 = Connection::open(&path)?; - db2.busy_handler(Some(busy_handler))?; - db1.execute_batch("BEGIN EXCLUSIVE")?; - let err = db2.prepare("SELECT * FROM t").unwrap_err(); - assert_eq!(err.sqlite_error_code(), Some(ErrorCode::DatabaseBusy)); - assert!(CALLED.load(Ordering::Relaxed)); - db1.busy_handler(None)?; - Ok(()) - } -} diff --git a/vendor/rusqlite/src/cache.rs b/vendor/rusqlite/src/cache.rs deleted file mode 100644 index 15c08ea..0000000 --- a/vendor/rusqlite/src/cache.rs +++ /dev/null @@ -1,351 +0,0 @@ -//! Prepared statements cache for faster execution. - -use crate::raw_statement::RawStatement; -use crate::{Connection, PrepFlags, Result, Statement}; -use hashlink::LruCache; -use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; -use std::sync::Arc; - -impl Connection { - /// Prepare a SQL statement for execution, returning a previously prepared - /// (but not currently in-use) statement if one is available. The - /// returned statement will be cached for reuse by future calls to - /// [`prepare_cached`](Connection::prepare_cached) once it is dropped. - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert_new_people(conn: &Connection) -> Result<()> { - /// { - /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?; - /// stmt.execute(["Joe Smith"])?; - /// } - /// { - /// // This will return the same underlying SQLite statement handle without - /// // having to prepare it again. - /// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?; - /// stmt.execute(["Bob Jones"])?; - /// } - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn prepare_cached(&self, sql: &str) -> Result> { - self.cache.get(self, sql) - } - - /// Set the maximum number of cached prepared statements this connection - /// will hold. By default, a connection will hold a relatively small - /// number of cached statements. If you need more, or know that you - /// will not use cached statements, you - /// can set the capacity manually using this method. - #[inline] - pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) { - self.cache.set_capacity(capacity); - } - - /// Remove/finalize all prepared statements currently in the cache. - #[inline] - pub fn flush_prepared_statement_cache(&self) { - self.cache.flush(); - } -} - -/// Prepared statements LRU cache. -#[derive(Debug)] -pub struct StatementCache(RefCell, RawStatement>>); - -unsafe impl Send for StatementCache {} - -/// Cacheable statement. -/// -/// Statement will return automatically to the cache by default. -/// If you want the statement to be discarded, call -/// [`discard()`](CachedStatement::discard) on it. -pub struct CachedStatement<'conn> { - stmt: Option>, - cache: &'conn StatementCache, -} - -impl<'conn> Deref for CachedStatement<'conn> { - type Target = Statement<'conn>; - - #[inline] - fn deref(&self) -> &Statement<'conn> { - self.stmt.as_ref().unwrap() - } -} - -impl<'conn> DerefMut for CachedStatement<'conn> { - #[inline] - fn deref_mut(&mut self) -> &mut Statement<'conn> { - self.stmt.as_mut().unwrap() - } -} - -impl Drop for CachedStatement<'_> { - #[inline] - fn drop(&mut self) { - if let Some(stmt) = self.stmt.take() { - self.cache.cache_stmt(unsafe { stmt.into_raw() }); - } - } -} - -impl CachedStatement<'_> { - #[inline] - fn new<'conn>(stmt: Statement<'conn>, cache: &'conn StatementCache) -> CachedStatement<'conn> { - CachedStatement { - stmt: Some(stmt), - cache, - } - } - - /// Discard the statement, preventing it from being returned to its - /// [`Connection`]'s collection of cached statements. - #[inline] - pub fn discard(mut self) { - self.stmt = None; - } -} - -impl StatementCache { - /// Create a statement cache. - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self(RefCell::new(LruCache::new(capacity))) - } - - #[inline] - fn set_capacity(&self, capacity: usize) { - self.0.borrow_mut().set_capacity(capacity); - } - - // Search the cache for a prepared-statement object that implements `sql`. - // If no such prepared-statement can be found, allocate and prepare a new one. - // - // # Failure - // - // Will return `Err` if no cached statement can be found and the underlying - // SQLite prepare call fails. - fn get<'conn>( - &'conn self, - conn: &'conn Connection, - sql: &str, - ) -> Result> { - let trimmed = sql.trim(); - let mut cache = self.0.borrow_mut(); - let stmt = match cache.remove(trimmed) { - Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)), - None => conn.prepare_with_flags(trimmed, PrepFlags::SQLITE_PREPARE_PERSISTENT), - }; - stmt.map(|mut stmt| { - stmt.stmt.set_statement_cache_key(trimmed); - CachedStatement::new(stmt, self) - }) - } - - // Return a statement to the cache. - fn cache_stmt(&self, mut stmt: RawStatement) { - if stmt.is_null() { - return; - } - let mut cache = self.0.borrow_mut(); - stmt.clear_bindings(); - if let Some(sql) = stmt.statement_cache_key() { - cache.insert(sql, stmt); - } else { - debug_assert!( - false, - "bug in statement cache code, statement returned to cache that without key" - ); - } - } - - #[inline] - fn flush(&self) { - let mut cache = self.0.borrow_mut(); - cache.clear(); - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::StatementCache; - use crate::{Connection, Result}; - use fallible_iterator::FallibleIterator; - - impl StatementCache { - fn clear(&self) { - self.0.borrow_mut().clear(); - } - - fn len(&self) -> usize { - self.0.borrow().len() - } - - fn capacity(&self) -> usize { - self.0.borrow().capacity() - } - } - - #[test] - fn test_cache() -> Result<()> { - let db = Connection::open_in_memory()?; - let cache = &db.cache; - let initial_capacity = cache.capacity(); - assert_eq!(0, cache.len()); - assert!(initial_capacity > 0); - - let sql = "PRAGMA schema_version"; - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - - cache.clear(); - assert_eq!(0, cache.len()); - assert_eq!(initial_capacity, cache.capacity()); - Ok(()) - } - - #[test] - fn test_set_capacity() -> Result<()> { - let db = Connection::open_in_memory()?; - let cache = &db.cache; - - let sql = "PRAGMA schema_version"; - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - - db.set_prepared_statement_cache_capacity(0); - assert_eq!(0, cache.len()); - - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(0, cache.len()); - - db.set_prepared_statement_cache_capacity(8); - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - Ok(()) - } - - #[test] - fn test_discard() -> Result<()> { - let db = Connection::open_in_memory()?; - let cache = &db.cache; - - let sql = "PRAGMA schema_version"; - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - stmt.discard(); - } - assert_eq!(0, cache.len()); - Ok(()) - } - - #[test] - fn test_ddl() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch( - r" - CREATE TABLE foo (x INT); - INSERT INTO foo VALUES (1); - ", - )?; - - let sql = "SELECT * FROM foo"; - - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(Ok(Some(1i32)), stmt.query([])?.map(|r| r.get(0)).next()); - } - - db.execute_batch( - r" - ALTER TABLE foo ADD COLUMN y INT; - UPDATE foo SET y = 2; - ", - )?; - - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!( - Ok(Some((1i32, 2i32))), - stmt.query([])?.map(|r| Ok((r.get(0)?, r.get(1)?))).next() - ); - } - Ok(()) - } - - #[test] - fn test_connection_close() -> Result<()> { - let conn = Connection::open_in_memory()?; - conn.prepare_cached("SELECT * FROM sqlite_master;")?; - - conn.close().expect("connection not closed"); - Ok(()) - } - - #[test] - fn test_cache_key() -> Result<()> { - let db = Connection::open_in_memory()?; - let cache = &db.cache; - assert_eq!(0, cache.len()); - - //let sql = " PRAGMA schema_version; -- comment"; - let sql = "PRAGMA schema_version; "; - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - - { - let mut stmt = db.prepare_cached(sql)?; - assert_eq!(0, cache.len()); - assert_eq!(0, stmt.query_row([], |r| r.get::<_, i64>(0))?); - } - assert_eq!(1, cache.len()); - Ok(()) - } - - #[test] - fn test_empty_stmt() -> Result<()> { - let conn = Connection::open_in_memory()?; - conn.prepare_cached("")?; - Ok(()) - } -} diff --git a/vendor/rusqlite/src/collation.rs b/vendor/rusqlite/src/collation.rs deleted file mode 100644 index 6189e78..0000000 --- a/vendor/rusqlite/src/collation.rs +++ /dev/null @@ -1,236 +0,0 @@ -//! Add, remove, or modify a collation -use std::cmp::Ordering; -use std::ffi::{c_char, c_int, c_void, CStr}; -use std::panic::catch_unwind; -use std::ptr; -use std::slice; - -use crate::ffi; -use crate::util::free_boxed_value; -use crate::{Connection, InnerConnection, Name, Result}; - -impl Connection { - /// Add or modify a collation. - #[inline] - pub fn create_collation(&self, collation_name: N, x_compare: C) -> Result<()> - where - C: Fn(&str, &str) -> Ordering + Send + 'static, - { - self.db - .borrow_mut() - .create_collation(collation_name, x_compare) - } - - /// Collation needed callback - #[inline] - pub fn collation_needed(&self, x_coll_needed: fn(&Self, &str) -> Result<()>) -> Result<()> { - self.db.borrow_mut().collation_needed(x_coll_needed) - } - - /// Remove collation. - #[inline] - pub fn remove_collation(&self, collation_name: N) -> Result<()> { - self.db.borrow_mut().remove_collation(collation_name) - } -} - -impl InnerConnection { - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.create_collation("foo", |_, _| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// std::cmp::Ordering::Equal - /// })?; - /// } - /// let value: String = db.query_row( - /// "WITH cte(bar) AS - /// (VALUES ('v1'),('v2'),('v3'),('v4'),('v5')) - /// SELECT DISTINCT bar COLLATE foo FROM cte;", - /// [], - /// |row| row.get(0), - /// )?; - /// assert_eq!(value, "v1"); - /// Ok(()) - /// } - /// ``` - fn create_collation(&mut self, collation_name: N, x_compare: C) -> Result<()> - where - C: Fn(&str, &str) -> Ordering + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure( - arg1: *mut c_void, - arg2: c_int, - arg3: *const c_void, - arg4: c_int, - arg5: *const c_void, - ) -> c_int - where - C: Fn(&str, &str) -> Ordering, - { - let r = catch_unwind(|| { - let boxed_f: *mut C = arg1.cast::(); - assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - let s1 = { - let c_slice = slice::from_raw_parts(arg3.cast::(), arg2 as usize); - String::from_utf8_lossy(c_slice) - }; - let s2 = { - let c_slice = slice::from_raw_parts(arg5.cast::(), arg4 as usize); - String::from_utf8_lossy(c_slice) - }; - (*boxed_f)(s1.as_ref(), s2.as_ref()) - }); - let t = match r { - Err(_) => { - return -1; // FIXME How ? - } - Ok(r) => r, - }; - - match t { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - } - } - - let boxed_f: *mut C = Box::into_raw(Box::new(x_compare)); - let c_name = collation_name.as_cstr()?; - let flags = ffi::SQLITE_UTF8; - let r = unsafe { - ffi::sqlite3_create_collation_v2( - self.db(), - c_name.as_ptr(), - flags, - boxed_f.cast::(), - Some(call_boxed_closure::), - Some(free_boxed_value::), - ) - }; - let res = self.decode_result(r); - // The xDestroy callback is not called if the sqlite3_create_collation_v2() - // function fails. - if res.is_err() { - drop(unsafe { Box::from_raw(boxed_f) }); - } - res - } - - fn collation_needed( - &mut self, - x_coll_needed: fn(&Connection, &str) -> Result<()>, - ) -> Result<()> { - use std::mem; - #[expect(clippy::needless_return)] - unsafe extern "C" fn collation_needed_callback( - arg1: *mut c_void, - arg2: *mut ffi::sqlite3, - e_text_rep: c_int, - arg3: *const c_char, - ) { - use std::str; - - if e_text_rep != ffi::SQLITE_UTF8 { - // TODO: validate - return; - } - - let callback: fn(&Connection, &str) -> Result<()> = mem::transmute(arg1); - let res = catch_unwind(|| { - let conn = Connection::from_handle(arg2).unwrap(); - let collation_name = CStr::from_ptr(arg3) - .to_str() - .expect("illegal collation sequence name"); - callback(&conn, collation_name) - }); - if res.is_err() { - return; // FIXME How ? - } - } - - let r = unsafe { - ffi::sqlite3_collation_needed( - self.db(), - x_coll_needed as *mut c_void, - Some(collation_needed_callback), - ) - }; - self.decode_result(r) - } - - #[inline] - fn remove_collation(&mut self, collation_name: N) -> Result<()> { - let c_name = collation_name.as_cstr()?; - let r = unsafe { - ffi::sqlite3_create_collation_v2( - self.db(), - c_name.as_ptr(), - ffi::SQLITE_UTF8, - ptr::null_mut(), - None, - None, - ) - }; - self.decode_result(r) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result}; - use fallible_streaming_iterator::FallibleStreamingIterator; - use std::cmp::Ordering; - use unicase::UniCase; - - fn unicase_compare(s1: &str, s2: &str) -> Ordering { - UniCase::new(s1).cmp(&UniCase::new(s2)) - } - - #[test] - fn test_unicase() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_collation(c"unicase", unicase_compare)?; - collate(db) - } - - fn collate(db: Connection) -> Result<()> { - db.execute_batch( - "CREATE TABLE foo (bar); - INSERT INTO foo (bar) VALUES ('Maße'); - INSERT INTO foo (bar) VALUES ('MASSE');", - )?; - let mut stmt = db.prepare("SELECT DISTINCT bar COLLATE unicase FROM foo ORDER BY 1")?; - let rows = stmt.query([])?; - assert_eq!(rows.count()?, 1); - Ok(()) - } - - fn collation_needed(db: &Connection, collation_name: &str) -> Result<()> { - if "unicase" == collation_name { - db.create_collation(collation_name, unicase_compare) - } else { - Ok(()) - } - } - - #[test] - fn test_collation_needed() -> Result<()> { - let db = Connection::open_in_memory()?; - db.collation_needed(collation_needed)?; - collate(db) - } - - #[test] - fn remove_collation() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_collation(c"unicase", unicase_compare)?; - db.remove_collation(c"unicase") - } -} diff --git a/vendor/rusqlite/src/column.rs b/vendor/rusqlite/src/column.rs deleted file mode 100644 index 2ca11cb..0000000 --- a/vendor/rusqlite/src/column.rs +++ /dev/null @@ -1,576 +0,0 @@ -use std::ffi::{c_char, CStr}; -use std::ptr; -use std::str; - -use crate::ffi; -use crate::{Connection, Error, Name, Result, Statement}; - -/// Information about a column of a SQLite query. -#[cfg(feature = "column_decltype")] -#[derive(Debug)] -pub struct Column<'stmt> { - name: &'stmt str, - decl_type: Option<&'stmt str>, -} - -#[cfg(feature = "column_decltype")] -impl Column<'_> { - /// Returns the name of the column. - #[inline] - #[must_use] - pub fn name(&self) -> &str { - self.name - } - - /// Returns the type of the column (`None` for expression). - #[inline] - #[must_use] - pub fn decl_type(&self) -> Option<&str> { - self.decl_type - } -} - -/// Metadata about the origin of a column of a SQLite query -#[cfg(feature = "column_metadata")] -#[derive(Debug)] -pub struct ColumnMetadata<'stmt> { - name: &'stmt str, - database_name: Option<&'stmt str>, - table_name: Option<&'stmt str>, - origin_name: Option<&'stmt str>, -} - -#[cfg(feature = "column_metadata")] -impl ColumnMetadata<'_> { - #[inline] - #[must_use] - /// Returns the name of the column in the query results - pub fn name(&self) -> &str { - self.name - } - - #[inline] - #[must_use] - /// Returns the database name from which the column originates - pub fn database_name(&self) -> Option<&str> { - self.database_name - } - - #[inline] - #[must_use] - /// Returns the table name from which the column originates - pub fn table_name(&self) -> Option<&str> { - self.table_name - } - - #[inline] - #[must_use] - /// Returns the column name from which the column originates - pub fn origin_name(&self) -> Option<&str> { - self.origin_name - } -} - -impl Statement<'_> { - /// Get all the column names in the result set of the prepared statement. - /// - /// If associated DB schema can be altered concurrently, you should make - /// sure that current statement has already been stepped once before - /// calling this method. - pub fn column_names(&self) -> Vec<&str> { - let n = self.column_count(); - let mut cols = Vec::with_capacity(n); - for i in 0..n { - let s = self.column_name_unwrap(i); - cols.push(s); - } - cols - } - - /// Return the number of columns in the result set returned by the prepared - /// statement. - /// - /// If associated DB schema can be altered concurrently, you should make - /// sure that current statement has already been stepped once before - /// calling this method. - #[inline] - pub fn column_count(&self) -> usize { - self.stmt.column_count() - } - - /// Check that column name reference lifetime is limited: - /// - /// > The returned string pointer is valid... - /// - /// `column_name` reference can become invalid if `stmt` is reprepared - /// (because of schema change) when `query_row` is called. So we assert - /// that a compilation error happens if this reference is kept alive: - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// let mut stmt = db.prepare("SELECT 1 as x")?; - /// let column_name = stmt.column_name(0)?; - /// let x = stmt.query_row([], |r| r.get::<_, i64>(0))?; // E0502 - /// assert_eq!(1, x); - /// assert_eq!("x", column_name); - /// Ok(()) - /// } - /// ``` - #[inline] - 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") - } - - /// Returns the name assigned to a particular column in the result set - /// returned by the prepared statement. - /// - /// If associated DB schema can be altered concurrently, you should make - /// sure that current statement has already been stepped once before - /// calling this method. - /// - /// ## Failure - /// - /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid - /// column range for this row. - /// - /// # Panics - /// - /// Panics when column name is not valid UTF-8. - #[inline] - pub fn column_name(&self, col: usize) -> Result<&str> { - self.stmt - .column_name(col) - // clippy::or_fun_call (nightly) vs clippy::unnecessary-lazy-evaluations (stable) - .ok_or(Error::InvalidColumnIndex(col)) - .map(|slice| { - slice - .to_str() - .expect("Invalid UTF-8 sequence in column name") - }) - } - - /// Returns the column index in the result set for a given column name. - /// - /// If there is no AS clause then the name of the column is unspecified and - /// may change from one release of SQLite to the next. - /// - /// If associated DB schema can be altered concurrently, you should make - /// sure that current statement has already been stepped once before - /// calling this method. - /// - /// # Failure - /// - /// Will return an `Error::InvalidColumnName` when there is no column with - /// the specified `name`. - #[inline] - pub fn column_index(&self, name: &str) -> Result { - let bytes = name.as_bytes(); - let n = self.column_count(); - for i in 0..n { - // Note: `column_name` is only fallible if `i` is out of bounds, - // which we've already checked. - if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).unwrap().to_bytes()) { - return Ok(i); - } - } - Err(Error::InvalidColumnName(String::from(name))) - } - - /// Returns a slice describing the columns of the result of the query. - /// - /// If associated DB schema can be altered concurrently, you should make - /// sure that current statement has already been stepped once before - /// calling this method. - #[cfg(feature = "column_decltype")] - pub fn columns(&self) -> Vec> { - let n = self.column_count(); - let mut cols = Vec::with_capacity(n); - for i in 0..n { - let name = self.column_name_unwrap(i); - let slice = self.stmt.column_decltype(i); - let decl_type = slice.map(|s| { - s.to_str() - .expect("Invalid UTF-8 sequence in column declaration") - }); - cols.push(Column { name, decl_type }); - } - cols - } - - /// Returns the names of the database, table, and row from which - /// each column of this query's results originate. - /// - /// Computed or otherwise derived columns will have None values for these fields. - #[cfg(feature = "column_metadata")] - pub fn columns_with_metadata(&self) -> Vec> { - let n = self.column_count(); - let mut col_mets = Vec::with_capacity(n); - for i in 0..n { - let name = self.column_name_unwrap(i); - let db_slice = self.stmt.column_database_name(i); - let tbl_slice = self.stmt.column_table_name(i); - let origin_slice = self.stmt.column_origin_name(i); - col_mets.push(ColumnMetadata { - name, - database_name: db_slice.map(|s| { - s.to_str() - .expect("Invalid UTF-8 sequence in column db name") - }), - table_name: tbl_slice.map(|s| { - s.to_str() - .expect("Invalid UTF-8 sequence in column table name") - }), - origin_name: origin_slice.map(|s| { - s.to_str() - .expect("Invalid UTF-8 sequence in column origin name") - }), - }) - } - col_mets - } - - /// Extract metadata of column at specified index - /// - /// Returns: - /// - database name - /// - table name - /// - original column name - /// - declared data type - /// - name of default collation sequence - /// - True if column has a NOT NULL constraint - /// - True if column is part of the PRIMARY KEY - /// - True if column is AUTOINCREMENT - /// - /// See [Connection::column_metadata] - #[cfg(feature = "column_metadata")] - #[expect(clippy::type_complexity)] - pub fn column_metadata( - &self, - col: usize, - ) -> Result< - Option<( - &CStr, - &CStr, - &CStr, - Option<&CStr>, - Option<&CStr>, - bool, - bool, - bool, - )>, - > { - let db_name = self.stmt.column_database_name(col); - let table_name = self.stmt.column_table_name(col); - let origin_name = self.stmt.column_origin_name(col); - if db_name.is_none() || table_name.is_none() || origin_name.is_none() { - return Ok(None); - } - let (data_type, coll_seq, not_null, primary_key, auto_inc) = - self.conn - .column_metadata(db_name, table_name.unwrap(), origin_name.unwrap())?; - Ok(Some(( - db_name.unwrap(), - table_name.unwrap(), - origin_name.unwrap(), - data_type, - coll_seq, - not_null, - primary_key, - auto_inc, - ))) - } -} - -impl Connection { - /// Check if `table_name`.`column_name` exists. - /// - /// `db_name` is main, temp, the name in ATTACH, or `None` to search all databases. - pub fn column_exists( - &self, - db_name: Option, - table_name: N, - column_name: N, - ) -> Result { - self.exists(db_name, table_name, Some(column_name)) - } - - /// Check if `table_name` exists. - /// - /// `db_name` is main, temp, the name in ATTACH, or `None` to search all databases. - pub fn table_exists(&self, db_name: Option, table_name: N) -> Result { - self.exists(db_name, table_name, None) - } - - /// Extract metadata of column at specified index - /// - /// Returns: - /// - declared data type - /// - name of default collation sequence - /// - True if column has a NOT NULL constraint - /// - True if column is part of the PRIMARY KEY - /// - True if column is AUTOINCREMENT - #[expect(clippy::type_complexity)] - pub fn column_metadata( - &self, - db_name: Option, - table_name: N, - column_name: N, - ) -> Result<(Option<&CStr>, Option<&CStr>, bool, bool, bool)> { - let cs = db_name.as_ref().map(N::as_cstr).transpose()?; - let db_name = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - let table_name = table_name.as_cstr()?; - let column_name = column_name.as_cstr()?; - - let mut data_type: *const c_char = ptr::null_mut(); - let mut coll_seq: *const c_char = ptr::null_mut(); - let mut not_null = 0; - let mut primary_key = 0; - let mut auto_inc = 0; - - self.decode_result(unsafe { - ffi::sqlite3_table_column_metadata( - self.handle(), - db_name, - table_name.as_ptr(), - column_name.as_ptr(), - &mut data_type, - &mut coll_seq, - &mut not_null, - &mut primary_key, - &mut auto_inc, - ) - })?; - - Ok(( - if data_type.is_null() { - None - } else { - Some(unsafe { CStr::from_ptr(data_type) }) - }, - if coll_seq.is_null() { - None - } else { - Some(unsafe { CStr::from_ptr(coll_seq) }) - }, - not_null != 0, - primary_key != 0, - auto_inc != 0, - )) - } - - fn exists( - &self, - db_name: Option, - table_name: N, - column_name: Option, - ) -> Result { - let cs = db_name.as_ref().map(N::as_cstr).transpose()?; - let db_name = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - let table_name = table_name.as_cstr()?; - let cn = column_name.as_ref().map(N::as_cstr).transpose()?; - let column_name = cn.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - let r = unsafe { - ffi::sqlite3_table_column_metadata( - self.handle(), - db_name, - table_name.as_ptr(), - column_name, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ) - }; - match r { - ffi::SQLITE_OK => Ok(true), - ffi::SQLITE_ERROR => Ok(false), - _ => self.db.borrow().decode_result(r).map(|_| false), - } - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result}; - - #[test] - #[cfg(feature = "column_decltype")] - fn test_columns() -> Result<()> { - use super::Column; - - let db = Connection::open_in_memory()?; - let query = db.prepare("SELECT * FROM sqlite_master")?; - let columns = query.columns(); - let column_names: Vec<&str> = columns.iter().map(Column::name).collect(); - assert_eq!( - column_names.as_slice(), - &["type", "name", "tbl_name", "rootpage", "sql"] - ); - let column_types: Vec> = columns - .iter() - .map(|col| col.decl_type().map(str::to_lowercase)) - .collect(); - assert_eq!( - &column_types[..3], - &[ - Some("text".to_owned()), - Some("text".to_owned()), - Some("text".to_owned()), - ] - ); - Ok(()) - } - - #[test] - #[cfg(feature = "column_metadata")] - fn test_columns_with_metadata() -> Result<()> { - let db = Connection::open_in_memory()?; - let query = db.prepare("SELECT *, 1 FROM sqlite_master")?; - - let col_mets = query.columns_with_metadata(); - - assert_eq!(col_mets.len(), 6); - - for col in col_mets.iter().take(5) { - assert_eq!(&col.database_name(), &Some("main")); - assert_eq!(&col.table_name(), &Some("sqlite_master")); - } - - assert!(col_mets[5].database_name().is_none()); - assert!(col_mets[5].table_name().is_none()); - assert!(col_mets[5].origin_name().is_none()); - - let col_origins: Vec> = col_mets.iter().map(|col| col.origin_name()).collect(); - - assert_eq!( - &col_origins[..5], - &[ - Some("type"), - Some("name"), - Some("tbl_name"), - Some("rootpage"), - Some("sql"), - ] - ); - - Ok(()) - } - - #[test] - fn test_column_name_in_error() -> Result<()> { - use crate::{types::Type, Error}; - let db = Connection::open_in_memory()?; - db.execute_batch( - "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, NULL); - END;", - )?; - let mut stmt = db.prepare("SELECT x as renamed, y FROM foo")?; - let mut rows = stmt.query([])?; - let row = rows.next()?.unwrap(); - match row.get::<_, String>(0).unwrap_err() { - Error::InvalidColumnType(idx, name, ty) => { - assert_eq!(idx, 0); - assert_eq!(name, "renamed"); - assert_eq!(ty, Type::Integer); - } - e => { - panic!("Unexpected error type: {e:?}"); - } - } - match row.get::<_, String>("y").unwrap_err() { - Error::InvalidColumnType(idx, name, ty) => { - assert_eq!(idx, 1); - assert_eq!(name, "y"); - assert_eq!(ty, Type::Null); - } - e => { - panic!("Unexpected error type: {e:?}"); - } - } - Ok(()) - } - - /// `column_name` reference should stay valid until `stmt` is reprepared (or - /// reset) even if DB schema is altered (SQLite documentation is - /// ambiguous here because it says reference "is valid until (...) the next - /// call to `sqlite3_column_name()` or `sqlite3_column_name16()` on the same - /// column.". We assume that reference is valid if only - /// `sqlite3_column_name()` is used): - #[test] - #[cfg(feature = "modern_sqlite")] - fn test_column_name_reference() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE y (x);")?; - let stmt = db.prepare("SELECT x FROM y;")?; - let column_name = stmt.column_name(0)?; - assert_eq!("x", column_name); - db.execute_batch("ALTER TABLE y RENAME COLUMN x TO z;")?; - // column name is not refreshed until statement is re-prepared - let same_column_name = stmt.column_name(0)?; - assert_eq!(same_column_name, column_name); - Ok(()) - } - - #[test] - #[cfg(feature = "column_metadata")] - fn stmt_column_metadata() -> Result<()> { - let db = Connection::open_in_memory()?; - let query = db.prepare("SELECT *, 1 FROM sqlite_master")?; - let (db_name, table_name, col_name, data_type, coll_seq, not_null, primary_key, auto_inc) = - query.column_metadata(0)?.unwrap(); - assert_eq!(db_name, crate::MAIN_DB); - assert_eq!(table_name, c"sqlite_master"); - assert_eq!(col_name, c"type"); - assert_eq!(data_type, Some(c"TEXT")); - assert_eq!(coll_seq, Some(c"BINARY")); - assert!(!not_null); - assert!(!primary_key); - assert!(!auto_inc); - assert!(query.column_metadata(5)?.is_none()); - Ok(()) - } - - #[test] - fn column_exists() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!(db.column_exists(None, c"sqlite_master", c"type")?); - assert!(db.column_exists(Some(crate::TEMP_DB), c"sqlite_master", c"type")?); - assert!(!db.column_exists(Some(crate::MAIN_DB), c"sqlite_temp_master", c"type")?); - Ok(()) - } - - #[test] - fn table_exists() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!(db.table_exists(None, c"sqlite_master")?); - assert!(db.table_exists(Some(crate::TEMP_DB), c"sqlite_master")?); - assert!(!db.table_exists(Some(crate::MAIN_DB), c"sqlite_temp_master")?); - Ok(()) - } - - #[test] - fn column_metadata() -> Result<()> { - let db = Connection::open_in_memory()?; - let (data_type, coll_seq, not_null, primary_key, auto_inc) = - db.column_metadata(None, c"sqlite_master", c"type")?; - assert_eq!( - data_type.map(|cs| cs.to_str().unwrap().to_ascii_uppercase()), - Some("TEXT".to_owned()) - ); - assert_eq!(coll_seq, Some(c"BINARY")); - assert!(!not_null); - assert!(!primary_key); - assert!(!auto_inc); - assert!(db.column_metadata(None, c"sqlite_master", c"foo").is_err()); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/config.rs b/vendor/rusqlite/src/config.rs deleted file mode 100644 index b7905f0..0000000 --- a/vendor/rusqlite/src/config.rs +++ /dev/null @@ -1,169 +0,0 @@ -//! Configure database connections - -use std::ffi::c_int; - -use crate::error::check; -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)] -#[derive(Copy, Clone, Debug)] -#[expect(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 = ffi::SQLITE_DBCONFIG_ENABLE_FKEY, - /// Enable or disable triggers. - SQLITE_DBCONFIG_ENABLE_TRIGGER = ffi::SQLITE_DBCONFIG_ENABLE_TRIGGER, - /// Enable or disable the `fts3_tokenizer()` function which is part of the - /// FTS3 full-text search engine extension. - SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = ffi::SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, // 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 - /// 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 - /// Activates or deactivates the "reset" flag for a database connection. - /// Run VACUUM with this flag set to reset the database. - SQLITE_DBCONFIG_RESET_DATABASE = 1009, // 3.24.0 - /// Activates or deactivates the "defensive" flag for a database connection. - SQLITE_DBCONFIG_DEFENSIVE = 1010, // 3.26.0 - /// Activates or deactivates the `writable_schema` flag. - SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011, // 3.28.0 - /// Activates or deactivates the legacy behavior of the ALTER TABLE RENAME - /// command. - SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012, // 3.29 - /// Activates or deactivates the legacy double-quoted string literal - /// misfeature for DML statements only. - SQLITE_DBCONFIG_DQS_DML = 1013, // 3.29.0 - /// Activates or deactivates the legacy double-quoted string literal - /// misfeature for DDL statements. - SQLITE_DBCONFIG_DQS_DDL = 1014, // 3.29.0 - /// Enable or disable views. - SQLITE_DBCONFIG_ENABLE_VIEW = 1015, // 3.30.0 - /// Activates or deactivates the legacy file format flag. - 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. - SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017, // 3.31.0 - /// Sets or clears a flag that enables collection of the - /// `sqlite3_stmt_scanstatus_v2()` statistics - #[cfg(feature = "modern_sqlite")] - SQLITE_DBCONFIG_STMT_SCANSTATUS = 1018, // 3.42.0 - /// Changes the default order in which tables and indexes are scanned - #[cfg(feature = "modern_sqlite")] - SQLITE_DBCONFIG_REVERSE_SCANORDER = 1019, // 3.42.0 - /// Enables or disables the ability of the ATTACH DATABASE SQL command - /// to create a new database file if the database filed named in the ATTACH command does not already exist. - #[cfg(feature = "modern_sqlite")] - SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE = 1020, // 3.49.0 - /// Enables or disables the ability of the ATTACH DATABASE SQL command to open a database for writing. - #[cfg(feature = "modern_sqlite")] - SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE = 1021, // 3.49.0 - /// Enables or disables the ability to include comments in SQL text. - #[cfg(feature = "modern_sqlite")] - SQLITE_DBCONFIG_ENABLE_COMMENTS = 1022, // 3.49.0 -} - -impl Connection { - /// Returns the current value of a `config`. - /// - /// - `SQLITE_DBCONFIG_ENABLE_FKEY`: return `false` or `true` to indicate - /// whether FK enforcement is off or on - /// - `SQLITE_DBCONFIG_ENABLE_TRIGGER`: return `false` or `true` to indicate - /// whether triggers are disabled or enabled - /// - `SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER`: return `false` or `true` to - /// indicate whether `fts3_tokenizer` are disabled or enabled - /// - `SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE`: return `false` to indicate - /// checkpoints-on-close are not disabled or `true` if they are - /// - `SQLITE_DBCONFIG_ENABLE_QPSG`: return `false` or `true` to indicate - /// whether the QPSG is disabled or enabled - /// - `SQLITE_DBCONFIG_TRIGGER_EQP`: return `false` to indicate - /// output-for-trigger are not disabled or `true` if it is - #[inline] - pub fn db_config(&self, config: DbConfig) -> Result { - let c = self.db.borrow(); - unsafe { - let mut val = 0; - check(ffi::sqlite3_db_config( - c.db(), - config as c_int, - -1, - &mut val, - ))?; - Ok(val != 0) - } - } - - /// Make configuration changes to a database connection - /// - /// - `SQLITE_DBCONFIG_ENABLE_FKEY`: `false` to disable FK enforcement, - /// `true` to enable FK enforcement - /// - `SQLITE_DBCONFIG_ENABLE_TRIGGER`: `false` to disable triggers, `true` - /// to enable triggers - /// - `SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER`: `false` to disable - /// `fts3_tokenizer()`, `true` to enable `fts3_tokenizer()` - /// - `SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE`: `false` (the default) to enable - /// checkpoints-on-close, `true` to disable them - /// - `SQLITE_DBCONFIG_ENABLE_QPSG`: `false` to disable the QPSG, `true` to - /// enable QPSG - /// - `SQLITE_DBCONFIG_TRIGGER_EQP`: `false` to disable output for trigger - /// programs, `true` to enable it - #[inline] - pub fn set_db_config(&self, config: DbConfig, new_val: bool) -> Result { - let c = self.db.borrow_mut(); - unsafe { - let mut val = 0; - check(ffi::sqlite3_db_config( - c.db(), - config as c_int, - new_val as c_int, - &mut val, - ))?; - Ok(val != 0) - } - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::DbConfig; - use crate::{Connection, Result}; - - #[test] - fn test_db_config() -> Result<()> { - let db = Connection::open_in_memory()?; - - let opposite = !db.db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_FKEY)?; - assert_eq!( - db.set_db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_FKEY, opposite), - Ok(opposite) - ); - assert_eq!( - db.db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_FKEY), - Ok(opposite) - ); - - let opposite = !db.db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_TRIGGER)?; - assert_eq!( - db.set_db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_TRIGGER, opposite), - Ok(opposite) - ); - assert_eq!( - db.db_config(DbConfig::SQLITE_DBCONFIG_ENABLE_TRIGGER), - Ok(opposite) - ); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/context.rs b/vendor/rusqlite/src/context.rs deleted file mode 100644 index ad57f50..0000000 --- a/vendor/rusqlite/src/context.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Code related to `sqlite3_context` common to `functions` and `vtab` modules. - -use crate::ffi::sqlite3_value; -use std::ffi::c_void; - -use crate::ffi; -use crate::ffi::sqlite3_context; - -use crate::str_for_sqlite; -use crate::types::{ToSqlOutput, ValueRef}; - -// This function is inline despite it's size because what's in the ToSqlOutput -// is often known to the compiler, and thus const prop/DCE can substantially -// simplify the function. -#[inline] -pub(super) unsafe fn set_result( - ctx: *mut sqlite3_context, - #[allow(unused_variables)] args: &[*mut sqlite3_value], - result: &ToSqlOutput<'_>, -) { - let value = match *result { - ToSqlOutput::Borrowed(v) => v, - ToSqlOutput::Owned(ref v) => ValueRef::from(v), - - #[cfg(feature = "blob")] - ToSqlOutput::ZeroBlob(len) => { - // TODO sqlite3_result_zeroblob64 // 3.8.11 - return ffi::sqlite3_result_zeroblob(ctx, len); - } - #[cfg(feature = "functions")] - ToSqlOutput::Arg(i) => { - return ffi::sqlite3_result_value(ctx, args[i]); - } - #[cfg(feature = "pointer")] - ToSqlOutput::Pointer(ref p) => { - return ffi::sqlite3_result_pointer(ctx, p.0 as _, p.1.as_ptr(), p.2); - } - }; - - match value { - ValueRef::Null => ffi::sqlite3_result_null(ctx), - ValueRef::Integer(i) => ffi::sqlite3_result_int64(ctx, i), - ValueRef::Real(r) => ffi::sqlite3_result_double(ctx, r), - ValueRef::Text(s) => { - let (c_str, len, destructor) = str_for_sqlite(s); - ffi::sqlite3_result_text64(ctx, c_str, len, destructor, ffi::SQLITE_UTF8 as _); - } - ValueRef::Blob(b) => { - let length = b.len(); - if length == 0 { - ffi::sqlite3_result_zeroblob(ctx, 0); - } else { - ffi::sqlite3_result_blob64( - ctx, - b.as_ptr().cast::(), - length as ffi::sqlite3_uint64, - ffi::SQLITE_TRANSIENT(), - ); - } - } - } -} diff --git a/vendor/rusqlite/src/error.rs b/vendor/rusqlite/src/error.rs deleted file mode 100644 index 944d203..0000000 --- a/vendor/rusqlite/src/error.rs +++ /dev/null @@ -1,514 +0,0 @@ -use crate::types::FromSqlError; -use crate::types::Type; -use crate::{errmsg_to_string, ffi, Result}; -use std::error; -use std::ffi::{c_char, c_int, NulError}; -use std::fmt; -use std::path::PathBuf; -use std::str; - -/// Enum listing possible errors from rusqlite. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// An error from an underlying SQLite call. - SqliteFailure(ffi::Error, Option), - - /// Error reported when attempting to open a connection when SQLite was - /// configured to allow single-threaded use only. - SqliteSingleThreadedMode, - - /// Error when the value of a particular column is requested, but it cannot - /// be converted to the requested Rust type. - FromSqlConversionFailure(usize, Type, Box), - - /// Error when SQLite gives us an integral value outside the range of the - /// requested type (e.g., trying to get the value 1000 into a `u8`). - /// The associated `usize` is the column index, - /// and the associated `i64` is the value returned by SQLite. - IntegralValueOutOfRange(usize, i64), - - /// Error converting a string to UTF-8. - Utf8Error(usize, str::Utf8Error), - - /// Error converting a string to a C-compatible string because it contained - /// an embedded nul. - NulError(NulError), - - /// Error when using SQL named parameters and passing a parameter name not - /// present in the SQL. - InvalidParameterName(String), - - /// Error converting a file path to a string. - InvalidPath(PathBuf), - - /// Error returned when an [`execute`](crate::Connection::execute) call - /// returns rows. - ExecuteReturnedResults, - - /// Error when a query that was expected to return at least one row (e.g., - /// for [`query_row`](crate::Connection::query_row)) did not return any. - QueryReturnedNoRows, - - /// Error when a query that was expected to return only one row (e.g., - /// for [`query_one`](crate::Connection::query_one)) did return more than one. - QueryReturnedMoreThanOneRow, - - /// Error when the value of a particular column is requested, but the index - /// is out of range for the statement. - InvalidColumnIndex(usize), - - /// Error when the value of a named column is requested, but no column - /// matches the name for the statement. - InvalidColumnName(String), - - /// Error when the value of a particular column is requested, but the type - /// of the result in that column cannot be converted to the requested - /// Rust type. - InvalidColumnType(usize, String, Type), - - /// Error when a query that was expected to insert one row did not insert - /// any or insert many. - StatementChangedRows(usize), - - /// Error returned by - /// [`functions::Context::get`](crate::functions::Context::get) when the - /// function argument cannot be converted to the requested type. - #[cfg(feature = "functions")] - InvalidFunctionParameterType(usize, Type), - /// Error returned by [`vtab::Values::get`](crate::vtab::Values::get) when - /// the filter argument cannot be converted to the requested type. - #[cfg(feature = "vtab")] - InvalidFilterParameterType(usize, Type), - - /// An error case available for implementors of custom user functions (e.g., - /// [`create_scalar_function`](crate::Connection::create_scalar_function)). - #[cfg(feature = "functions")] - UserFunctionError(Box), - - /// Error available for the implementors of the - /// [`ToSql`](crate::types::ToSql) trait. - ToSqlConversionFailure(Box), - - /// Error when the SQL is not a `SELECT`, is not read-only. - InvalidQuery, - - /// An error case available for implementors of custom modules (e.g., - /// [`create_module`](crate::Connection::create_module)). - #[cfg(feature = "vtab")] - ModuleError(String), - - /// An unwinding panic occurs in a UDF (user-defined function). - UnwindingPanic, - - /// An error returned when - /// [`Context::get_aux`](crate::functions::Context::get_aux) attempts to - /// retrieve data of a different type than what had been stored using - /// [`Context::set_aux`](crate::functions::Context::set_aux). - #[cfg(feature = "functions")] - GetAuxWrongType, - - /// Error when the SQL contains multiple statements. - MultipleStatement, - /// Error when the number of bound parameters does not match the number of - /// parameters in the query. The first `usize` is how many parameters were - /// given, the 2nd is how many were expected. - InvalidParameterCount(usize, usize), - - /// Returned from various functions in the Blob IO positional API. For - /// example, - /// [`Blob::raw_read_at_exact`](crate::blob::Blob::raw_read_at_exact) will - /// return it if the blob has insufficient data. - #[cfg(feature = "blob")] - BlobSizeError, - /// Error referencing a specific token in the input SQL - #[cfg(feature = "modern_sqlite")] // 3.38.0 - SqlInputError { - /// error code - error: ffi::Error, - /// error message - msg: String, - /// SQL input - sql: String, - /// byte offset of the start of invalid token - offset: c_int, - }, - /// Loadable extension initialization error - #[cfg(feature = "loadable_extension")] - InitError(ffi::InitError), - /// Error when the schema of a particular database is requested, but the index - /// is out of range. - #[cfg(feature = "modern_sqlite")] // 3.39.0 - InvalidDatabaseIndex(usize), -} - -impl PartialEq for Error { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::SqliteFailure(e1, s1), Self::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2, - (Self::SqliteSingleThreadedMode, Self::SqliteSingleThreadedMode) => true, - (Self::IntegralValueOutOfRange(i1, n1), Self::IntegralValueOutOfRange(i2, n2)) => { - i1 == i2 && n1 == n2 - } - (Self::Utf8Error(i1, e1), Self::Utf8Error(i2, e2)) => i1 == i2 && e1 == e2, - (Self::NulError(e1), Self::NulError(e2)) => e1 == e2, - (Self::InvalidParameterName(n1), Self::InvalidParameterName(n2)) => n1 == n2, - (Self::InvalidPath(p1), Self::InvalidPath(p2)) => p1 == p2, - (Self::ExecuteReturnedResults, Self::ExecuteReturnedResults) => true, - (Self::QueryReturnedNoRows, Self::QueryReturnedNoRows) => true, - (Self::QueryReturnedMoreThanOneRow, Self::QueryReturnedMoreThanOneRow) => true, - (Self::InvalidColumnIndex(i1), Self::InvalidColumnIndex(i2)) => i1 == i2, - (Self::InvalidColumnName(n1), Self::InvalidColumnName(n2)) => n1 == n2, - (Self::InvalidColumnType(i1, n1, t1), Self::InvalidColumnType(i2, n2, t2)) => { - i1 == i2 && t1 == t2 && n1 == n2 - } - (Self::StatementChangedRows(n1), Self::StatementChangedRows(n2)) => n1 == n2, - #[cfg(feature = "functions")] - ( - Self::InvalidFunctionParameterType(i1, t1), - Self::InvalidFunctionParameterType(i2, t2), - ) => i1 == i2 && t1 == t2, - #[cfg(feature = "vtab")] - ( - Self::InvalidFilterParameterType(i1, t1), - Self::InvalidFilterParameterType(i2, t2), - ) => i1 == i2 && t1 == t2, - (Self::InvalidQuery, Self::InvalidQuery) => true, - #[cfg(feature = "vtab")] - (Self::ModuleError(s1), Self::ModuleError(s2)) => s1 == s2, - (Self::UnwindingPanic, Self::UnwindingPanic) => true, - #[cfg(feature = "functions")] - (Self::GetAuxWrongType, Self::GetAuxWrongType) => true, - (Self::InvalidParameterCount(i1, n1), Self::InvalidParameterCount(i2, n2)) => { - i1 == i2 && n1 == n2 - } - #[cfg(feature = "blob")] - (Self::BlobSizeError, Self::BlobSizeError) => true, - #[cfg(feature = "modern_sqlite")] - ( - Self::SqlInputError { - error: e1, - msg: m1, - sql: s1, - offset: o1, - }, - Self::SqlInputError { - error: e2, - msg: m2, - sql: s2, - offset: o2, - }, - ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2, - #[cfg(feature = "loadable_extension")] - (Self::InitError(e1), Self::InitError(e2)) => e1 == e2, - #[cfg(feature = "modern_sqlite")] - (Self::InvalidDatabaseIndex(i1), Self::InvalidDatabaseIndex(i2)) => i1 == i2, - (..) => false, - } - } -} - -impl From for Error { - #[cold] - fn from(err: str::Utf8Error) -> Self { - Self::Utf8Error(UNKNOWN_COLUMN, err) - } -} - -impl From for Error { - #[cold] - fn from(err: NulError) -> Self { - Self::NulError(err) - } -} - -const UNKNOWN_COLUMN: usize = usize::MAX; - -/// The conversion isn't precise, but it's convenient to have it -/// to allow use of `get_raw(…).as_…()?` in callbacks that take `Error`. -impl From for Error { - #[cold] - fn from(err: FromSqlError) -> Self { - // The error type requires index and type fields, but they aren't known in this - // context. - match err { - FromSqlError::OutOfRange(val) => Self::IntegralValueOutOfRange(UNKNOWN_COLUMN, val), - FromSqlError::InvalidBlobSize { .. } => { - Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Blob, Box::new(err)) - } - FromSqlError::Other(source) => { - Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, source) - } - _ => Self::FromSqlConversionFailure(UNKNOWN_COLUMN, Type::Null, Box::new(err)), - } - } -} - -#[cfg(feature = "loadable_extension")] -impl From for Error { - #[cold] - fn from(err: ffi::InitError) -> Self { - Self::InitError(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::SqliteFailure(ref err, None) => err.fmt(f), - Self::SqliteFailure(_, Some(ref s)) => write!(f, "{s}"), - Self::SqliteSingleThreadedMode => write!( - f, - "SQLite was compiled or configured for single-threaded use only" - ), - Self::FromSqlConversionFailure(i, ref t, ref err) => { - if i != UNKNOWN_COLUMN { - write!(f, "Conversion error from type {t} at index: {i}, {err}") - } else { - err.fmt(f) - } - } - Self::IntegralValueOutOfRange(col, val) => { - if col != UNKNOWN_COLUMN { - write!(f, "Integer {val} out of range at index {col}") - } else { - write!(f, "Integer {val} out of range") - } - } - Self::Utf8Error(col, ref err) => { - if col != UNKNOWN_COLUMN { - write!(f, "{err} at index {col}") - } else { - err.fmt(f) - } - } - Self::NulError(ref err) => err.fmt(f), - Self::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {name}"), - Self::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()), - Self::ExecuteReturnedResults => { - write!(f, "Execute returned results - did you mean to call query?") - } - Self::QueryReturnedNoRows => write!(f, "Query returned no rows"), - Self::QueryReturnedMoreThanOneRow => write!(f, "Query returned more than one row"), - Self::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"), - Self::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"), - Self::InvalidColumnType(i, ref name, ref t) => { - write!(f, "Invalid column type {t} at index: {i}, name: {name}") - } - Self::InvalidParameterCount(i1, n1) => write!( - f, - "Wrong number of parameters passed to query. Got {i1}, needed {n1}" - ), - Self::StatementChangedRows(i) => write!(f, "Query changed {i} rows"), - - #[cfg(feature = "functions")] - Self::InvalidFunctionParameterType(i, ref t) => { - write!(f, "Invalid function parameter type {t} at index {i}") - } - #[cfg(feature = "vtab")] - Self::InvalidFilterParameterType(i, ref t) => { - write!(f, "Invalid filter parameter type {t} at index {i}") - } - #[cfg(feature = "functions")] - Self::UserFunctionError(ref err) => err.fmt(f), - Self::ToSqlConversionFailure(ref err) => err.fmt(f), - Self::InvalidQuery => write!(f, "Query is not read-only"), - #[cfg(feature = "vtab")] - Self::ModuleError(ref desc) => write!(f, "{desc}"), - Self::UnwindingPanic => write!(f, "unwinding panic"), - #[cfg(feature = "functions")] - Self::GetAuxWrongType => write!(f, "get_aux called with wrong type"), - Self::MultipleStatement => write!(f, "Multiple statements provided"), - #[cfg(feature = "blob")] - Self::BlobSizeError => "Blob size is insufficient".fmt(f), - #[cfg(feature = "modern_sqlite")] - Self::SqlInputError { - ref msg, - offset, - ref sql, - .. - } => write!(f, "{msg} in {sql} at offset {offset}"), - #[cfg(feature = "loadable_extension")] - Self::InitError(ref err) => err.fmt(f), - #[cfg(feature = "modern_sqlite")] - Self::InvalidDatabaseIndex(i) => write!(f, "Invalid database index: {i}"), - } - } -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - Self::SqliteFailure(ref err, _) => Some(err), - Self::Utf8Error(_, ref err) => Some(err), - Self::NulError(ref err) => Some(err), - - Self::IntegralValueOutOfRange(..) - | Self::SqliteSingleThreadedMode - | Self::InvalidParameterName(_) - | Self::ExecuteReturnedResults - | Self::QueryReturnedNoRows - | Self::QueryReturnedMoreThanOneRow - | Self::InvalidColumnIndex(_) - | Self::InvalidColumnName(_) - | Self::InvalidColumnType(..) - | Self::InvalidPath(_) - | Self::InvalidParameterCount(..) - | Self::StatementChangedRows(_) - | Self::InvalidQuery - | Self::MultipleStatement => None, - - #[cfg(feature = "functions")] - Self::InvalidFunctionParameterType(..) => None, - #[cfg(feature = "vtab")] - Self::InvalidFilterParameterType(..) => None, - - #[cfg(feature = "functions")] - Self::UserFunctionError(ref err) => Some(&**err), - - Self::FromSqlConversionFailure(_, _, ref err) - | Self::ToSqlConversionFailure(ref err) => Some(&**err), - - #[cfg(feature = "vtab")] - Self::ModuleError(_) => None, - - Self::UnwindingPanic => None, - - #[cfg(feature = "functions")] - Self::GetAuxWrongType => None, - - #[cfg(feature = "blob")] - Self::BlobSizeError => None, - #[cfg(feature = "modern_sqlite")] - Self::SqlInputError { ref error, .. } => Some(error), - #[cfg(feature = "loadable_extension")] - Self::InitError(ref err) => Some(err), - #[cfg(feature = "modern_sqlite")] - Self::InvalidDatabaseIndex(_) => None, - } - } -} - -impl Error { - /// Returns the underlying SQLite error if this is [`Error::SqliteFailure`]. - #[inline] - #[must_use] - pub fn sqlite_error(&self) -> Option<&ffi::Error> { - match self { - Self::SqliteFailure(error, _) => Some(error), - _ => None, - } - } - - /// Returns the underlying SQLite error code if this is - /// [`Error::SqliteFailure`]. - #[inline] - #[must_use] - pub fn sqlite_error_code(&self) -> Option { - self.sqlite_error().map(|error| error.code) - } -} - -// These are public but not re-exported by lib.rs, so only visible within crate. - -#[cold] -pub fn error_from_sqlite_code(code: c_int, message: Option) -> Error { - Error::SqliteFailure(ffi::Error::new(code), message) -} - -macro_rules! err { - ($code:expr $(,)?) => { - $crate::error::error_from_sqlite_code($code, None) - }; - ($code:expr, $msg:literal $(,)?) => { - $crate::error::error_from_sqlite_code($code, Some(format!($msg))) - }; - ($code:expr, $err:expr $(,)?) => { - $crate::error::error_from_sqlite_code($code, Some(format!($err))) - }; - ($code:expr, $fmt:expr, $($arg:tt)*) => { - $crate::error::error_from_sqlite_code($code, Some(format!($fmt, $($arg)*))) - }; -} - -#[cold] -pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { - error_from_sqlite_code(code, error_msg(db, code)) -} - -unsafe fn error_msg(db: *mut ffi::sqlite3, code: c_int) -> Option { - if db.is_null() || ffi::sqlite3_errcode(db) != code { - let err_str = ffi::sqlite3_errstr(code); - if err_str.is_null() { - None - } else { - Some(errmsg_to_string(err_str)) - } - } else { - Some(errmsg_to_string(ffi::sqlite3_errmsg(db))) - } -} - -pub unsafe fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> { - if code == ffi::SQLITE_OK { - Ok(()) - } else { - Err(error_from_handle(db, code)) - } -} - -#[cold] -#[cfg(not(feature = "modern_sqlite"))] // SQLite >= 3.38.0 -pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error { - error_from_handle(db, code) -} - -#[cold] -#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0 -pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error { - if db.is_null() { - error_from_sqlite_code(code, None) - } else { - let error = ffi::Error::new(code); - let msg = error_msg(db, code); - if ffi::ErrorCode::Unknown == error.code { - let offset = ffi::sqlite3_error_offset(db); - if offset >= 0 { - return Error::SqlInputError { - error, - msg: msg.unwrap_or("error".to_owned()), - sql: sql.to_owned(), - offset, - }; - } - } - Error::SqliteFailure(error, msg) - } -} - -pub fn check(code: c_int) -> Result<()> { - if code != ffi::SQLITE_OK { - Err(error_from_sqlite_code(code, None)) - } else { - Ok(()) - } -} - -/// Transform Rust error to SQLite error (message and code). -/// # Safety -/// This function is unsafe because it uses raw pointer -pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut c_char) -> c_int { - use crate::util::alloc; - match e { - Error::SqliteFailure(err, s) => { - if let Some(s) = s { - *err_msg = alloc(s); - } - err.extended_code - } - err => { - *err_msg = alloc(&err.to_string()); - ffi::SQLITE_ERROR - } - } -} diff --git a/vendor/rusqlite/src/functions.rs b/vendor/rusqlite/src/functions.rs deleted file mode 100644 index 94fdf58..0000000 --- a/vendor/rusqlite/src/functions.rs +++ /dev/null @@ -1,1271 +0,0 @@ -//! Create or redefine SQL functions. -//! -//! # Example -//! -//! Adding a `regexp` function to a connection in which compiled regular -//! expressions are cached in a `HashMap`. For an alternative implementation -//! that uses SQLite's [Function Auxiliary Data](https://www.sqlite.org/c3ref/get_auxdata.html) interface -//! to avoid recompiling regular expressions, see the unit tests for this -//! module. -//! -//! ```rust -//! use regex::Regex; -//! use rusqlite::functions::FunctionFlags; -//! use rusqlite::{Connection, Error, Result}; -//! use std::sync::Arc; -//! type BoxError = Box; -//! -//! fn add_regexp_function(db: &Connection) -> Result<()> { -//! db.create_scalar_function( -//! "regexp", -//! 2, -//! FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, -//! move |ctx| { -//! assert_eq!(ctx.len(), 2, "called with unexpected number of arguments"); -//! let regexp: Arc = ctx.get_or_create_aux(0, |vr| -> Result<_, BoxError> { -//! Ok(Regex::new(vr.as_str()?)?) -//! })?; -//! let is_match = { -//! let text = ctx -//! .get_raw(1) -//! .as_str() -//! .map_err(|e| Error::UserFunctionError(e.into()))?; -//! -//! regexp.is_match(text) -//! }; -//! -//! Ok(is_match) -//! }, -//! ) -//! } -//! -//! fn main() -> Result<()> { -//! let db = Connection::open_in_memory()?; -//! add_regexp_function(&db)?; -//! -//! let is_match: bool = -//! db.query_row("SELECT regexp('[aeiou]*', 'aaaaeeeiii')", [], |row| { -//! row.get(0) -//! })?; -//! -//! assert!(is_match); -//! Ok(()) -//! } -//! ``` -use std::any::Any; -use std::ffi::{c_int, c_uint, c_void}; -use std::marker::PhantomData; -use std::ops::Deref; -use std::panic::{catch_unwind, RefUnwindSafe, UnwindSafe}; -use std::ptr; -use std::slice; -use std::sync::Arc; - -use crate::ffi; -use crate::ffi::sqlite3_context; -use crate::ffi::sqlite3_value; - -use crate::context::set_result; -use crate::types::{FromSql, FromSqlError, ToSql, ToSqlOutput, ValueRef}; -use crate::util::free_boxed_value; -use crate::{str_to_cstring, Connection, Error, InnerConnection, Name, Result}; - -unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { - if let Error::SqliteFailure(ref err, ref s) = *err { - ffi::sqlite3_result_error_code(ctx, err.extended_code); - if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) { - ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1); - } - } else { - ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION); - if let Ok(cstr) = str_to_cstring(&err.to_string()) { - ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1); - } - } -} - -/// Context is a wrapper for the SQLite function -/// evaluation context. -pub struct Context<'a> { - ctx: *mut sqlite3_context, - args: &'a [*mut sqlite3_value], -} - -impl Context<'_> { - /// Returns the number of arguments to the function. - #[inline] - #[must_use] - pub fn len(&self) -> usize { - self.args.len() - } - - /// Returns `true` when there is no argument. - #[inline] - #[must_use] - pub fn is_empty(&self) -> bool { - self.args.is_empty() - } - - /// Returns the `idx`th argument as a `T`. - /// - /// # Failure - /// - /// Will panic if `idx` is greater than or equal to - /// [`self.len()`](Context::len). - /// - /// Will return Err if the underlying SQLite type cannot be converted to a - /// `T`. - pub fn get(&self, idx: usize) -> Result { - let arg = self.args[idx]; - let value = unsafe { ValueRef::from_value(arg) }; - FromSql::column_result(value).map_err(|err| match err { - FromSqlError::InvalidType => { - Error::InvalidFunctionParameterType(idx, value.data_type()) - } - FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), - FromSqlError::Utf8Error(err) => Error::Utf8Error(idx, err), - FromSqlError::Other(err) => { - Error::FromSqlConversionFailure(idx, value.data_type(), err) - } - FromSqlError::InvalidBlobSize { .. } => { - Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err)) - } - }) - } - - /// Return raw pointer at `idx` - /// # Safety - /// This function is unsafe because it uses raw pointer and cast - #[cfg(feature = "pointer")] - pub unsafe fn get_pointer( - &self, - idx: usize, - ptr_type: &'static std::ffi::CStr, - ) -> Option<&T> { - let arg = self.args[idx]; - debug_assert_eq!(unsafe { ffi::sqlite3_value_type(arg) }, ffi::SQLITE_NULL); - unsafe { - ffi::sqlite3_value_pointer(arg, ptr_type.as_ptr()) - .cast::() - .as_ref() - } - } - - /// Returns the `idx`th argument as a `ValueRef`. - /// - /// # Failure - /// - /// Will panic if `idx` is greater than or equal to - /// [`self.len()`](Context::len). - #[inline] - #[must_use] - pub fn get_raw(&self, idx: usize) -> ValueRef<'_> { - let arg = self.args[idx]; - unsafe { ValueRef::from_value(arg) } - } - - /// Returns the `idx`th argument as a `SqlFnArg`. - /// To be used when the SQL function result is one of its arguments. - #[inline] - #[must_use] - pub fn get_arg(&self, idx: usize) -> SqlFnArg { - assert!(idx < self.len()); - SqlFnArg { idx } - } - - /// Returns the subtype of `idx`th argument. - /// - /// # Failure - /// - /// Will panic if `idx` is greater than or equal to - /// [`self.len()`](Context::len). - pub fn get_subtype(&self, idx: usize) -> c_uint { - let arg = self.args[idx]; - unsafe { ffi::sqlite3_value_subtype(arg) } - } - - /// Fetch or insert the auxiliary data associated with a particular - /// parameter. This is intended to be an easier-to-use way of fetching it - /// compared to calling [`get_aux`](Context::get_aux) and - /// [`set_aux`](Context::set_aux) separately. - /// - /// See `https://www.sqlite.org/c3ref/get_auxdata.html` for a discussion of - /// this feature, or the unit tests of this module for an example. - /// - /// # Failure - /// - /// Will panic if `arg` is greater than or equal to - /// [`self.len()`](Context::len). - pub fn get_or_create_aux(&self, arg: c_int, func: F) -> Result> - where - T: Send + Sync + 'static, - E: Into>, - F: FnOnce(ValueRef<'_>) -> Result, - { - if let Some(v) = self.get_aux(arg)? { - Ok(v) - } else { - let vr = self.get_raw(arg as usize); - self.set_aux( - arg, - func(vr).map_err(|e| Error::UserFunctionError(e.into()))?, - ) - } - } - - /// Sets the auxiliary data associated with a particular parameter. See - /// `https://www.sqlite.org/c3ref/get_auxdata.html` for a discussion of - /// this feature, or the unit tests of this module for an example. - /// - /// # Failure - /// - /// Will panic if `arg` is greater than or equal to - /// [`self.len()`](Context::len). - pub fn set_aux(&self, arg: c_int, value: T) -> Result> { - assert!(arg < self.len() as i32); - let orig: Arc = Arc::new(value); - let inner: AuxInner = orig.clone(); - let outer = Box::new(inner); - let raw: *mut AuxInner = Box::into_raw(outer); - unsafe { - ffi::sqlite3_set_auxdata( - self.ctx, - arg, - raw.cast(), - Some(free_boxed_value::), - ); - }; - Ok(orig) - } - - /// Gets the auxiliary data that was associated with a given parameter via - /// [`set_aux`](Context::set_aux). Returns `Ok(None)` if no data has been - /// associated, and Ok(Some(v)) if it has. Returns an error if the - /// requested type does not match. - /// - /// # Failure - /// - /// Will panic if `arg` is greater than or equal to - /// [`self.len()`](Context::len). - pub fn get_aux(&self, arg: c_int) -> Result>> { - assert!(arg < self.len() as i32); - let p = unsafe { ffi::sqlite3_get_auxdata(self.ctx, arg) as *const AuxInner }; - if p.is_null() { - Ok(None) - } else { - let v: AuxInner = AuxInner::clone(unsafe { &*p }); - v.downcast::() - .map(Some) - .map_err(|_| Error::GetAuxWrongType) - } - } - - /// Get the db connection handle via [sqlite3_context_db_handle](https://www.sqlite.org/c3ref/context_db_handle.html) - /// - /// # Safety - /// - /// This function is marked unsafe because there is a potential for other - /// references to the connection to be sent across threads, [see this comment](https://github.com/rusqlite/rusqlite/issues/643#issuecomment-640181213). - pub unsafe fn get_connection(&self) -> Result> { - let handle = ffi::sqlite3_context_db_handle(self.ctx); - Ok(ConnectionRef { - conn: Connection::from_handle(handle)?, - phantom: PhantomData, - }) - } -} - -/// A reference to a connection handle with a lifetime bound to something. -pub struct ConnectionRef<'ctx> { - // comes from Connection::from_handle(sqlite3_context_db_handle(...)) - // and is non-owning - conn: Connection, - phantom: PhantomData<&'ctx Context<'ctx>>, -} - -impl Deref for ConnectionRef<'_> { - type Target = Connection; - - #[inline] - fn deref(&self) -> &Connection { - &self.conn - } -} - -type AuxInner = Arc; - -/// Subtype of an SQL function -pub type SubType = Option; - -/// Result of an SQL function -pub trait SqlFnOutput { - /// Converts Rust value to SQLite value with an optional subtype - fn to_sql(&self) -> Result<(ToSqlOutput<'_>, SubType)>; -} - -impl SqlFnOutput for T { - #[inline] - fn to_sql(&self) -> Result<(ToSqlOutput<'_>, SubType)> { - ToSql::to_sql(self).map(|o| (o, None)) - } -} - -impl SqlFnOutput for (T, SubType) { - fn to_sql(&self) -> Result<(ToSqlOutput<'_>, SubType)> { - ToSql::to_sql(&self.0).map(|o| (o, self.1)) - } -} - -/// n-th arg of an SQL scalar function -pub struct SqlFnArg { - idx: usize, -} -impl ToSql for SqlFnArg { - fn to_sql(&self) -> Result> { - Ok(ToSqlOutput::Arg(self.idx)) - } -} - -unsafe fn sql_result( - ctx: *mut sqlite3_context, - args: &[*mut sqlite3_value], - r: Result, -) { - let t = r.as_ref().map(SqlFnOutput::to_sql); - - match t { - Ok(Ok((ref value, sub_type))) => { - set_result(ctx, args, value); - if let Some(sub_type) = sub_type { - ffi::sqlite3_result_subtype(ctx, sub_type); - } - } - Ok(Err(err)) => report_error(ctx, &err), - Err(err) => report_error(ctx, err), - }; -} - -/// Aggregate is the callback interface for user-defined -/// aggregate function. -/// -/// `A` is the type of the aggregation context and `T` is the type of the final -/// result. Implementations should be stateless. -pub trait Aggregate -where - A: RefUnwindSafe + UnwindSafe, - T: SqlFnOutput, -{ - /// Initializes the aggregation context. Will be called prior to the first - /// call to [`step()`](Aggregate::step) to set up the context for an - /// invocation of the function. (Note: `init()` will not be called if - /// there are no rows.) - fn init(&self, ctx: &mut Context<'_>) -> Result; - - /// "step" function called once for each row in an aggregate group. May be - /// called 0 times if there are no rows. - fn step(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>; - - /// Computes and returns the final result. Will be called exactly once for - /// each invocation of the function. If [`step()`](Aggregate::step) was - /// called at least once, will be given `Some(A)` (the same `A` as was - /// created by [`init`](Aggregate::init) and given to - /// [`step`](Aggregate::step)); if [`step()`](Aggregate::step) was not - /// called (because the function is running against 0 rows), will be - /// given `None`. - /// - /// The passed context will have no arguments. - fn finalize(&self, ctx: &mut Context<'_>, acc: Option) -> Result; -} - -/// `WindowAggregate` is the callback interface for -/// user-defined aggregate window function. -#[cfg(feature = "window")] -pub trait WindowAggregate: Aggregate -where - A: RefUnwindSafe + UnwindSafe, - T: SqlFnOutput, -{ - /// Returns the current value of the aggregate. Unlike xFinal, the - /// implementation should not delete any context. - fn value(&self, acc: Option<&mut A>) -> Result; - - /// Removes a row from the current window. - fn inverse(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>; -} - -bitflags::bitflags! { - /// 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. - #[derive(Clone, Copy, Debug)] - #[repr(C)] - pub struct FunctionFlags: 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; // 3.8.3 - /// 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 subtypes 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 - /// Indicates to SQLite that a function might call `sqlite3_result_subtype()` to cause a subtype to be associated with its result. - const SQLITE_RESULT_SUBTYPE = 0x0000_0100_0000; // 3.45.0 - /// Indicates that the function is an aggregate that internally orders the values provided to the first argument. - const SQLITE_SELFORDER1 = 0x0000_0200_0000; // 3.47.0 - } -} - -impl Default for FunctionFlags { - #[inline] - fn default() -> Self { - Self::SQLITE_UTF8 - } -} - -impl Connection { - /// Attach a user-defined scalar function to - /// this database connection. - /// - /// `fn_name` is the name the function will be accessible from SQL. - /// `n_arg` is the number of arguments to the function. Use `-1` for a - /// variable number. If the function always returns the same value - /// given the same input, `deterministic` should be `true`. - /// - /// The function will remain available until the connection is closed or - /// until it is explicitly removed via - /// [`remove_function`](Connection::remove_function). - /// - /// # Example - /// - /// ```rust - /// # use rusqlite::{Connection, Result}; - /// # use rusqlite::functions::FunctionFlags; - /// fn scalar_function_example(db: Connection) -> Result<()> { - /// db.create_scalar_function( - /// "halve", - /// 1, - /// FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - /// |ctx| { - /// let value = ctx.get::(0)?; - /// Ok(value / 2f64) - /// }, - /// )?; - /// - /// let six_halved: f64 = db.query_row("SELECT halve(6)", [], |r| r.get(0))?; - /// assert_eq!(six_halved, 3f64); - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return Err if the function could not be attached to the connection. - #[inline] - pub fn create_scalar_function( - &self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - x_func: F, - ) -> Result<()> - where - F: Fn(&Context<'_>) -> Result + Send + 'static, - T: SqlFnOutput, - { - self.db - .borrow_mut() - .create_scalar_function(fn_name, n_arg, flags, x_func) - } - - /// Attach a user-defined aggregate function to this - /// database connection. - /// - /// # Failure - /// - /// Will return Err if the function could not be attached to the connection. - #[inline] - pub fn create_aggregate_function( - &self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - aggr: D, - ) -> Result<()> - where - A: RefUnwindSafe + UnwindSafe, - D: Aggregate + 'static, - T: SqlFnOutput, - { - self.db - .borrow_mut() - .create_aggregate_function(fn_name, n_arg, flags, aggr) - } - - /// Attach a user-defined aggregate window function to - /// this database connection. - /// - /// See `https://sqlite.org/windowfunctions.html#udfwinfunc` for more - /// information. - #[cfg(feature = "window")] - #[inline] - pub fn create_window_function( - &self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - aggr: W, - ) -> Result<()> - where - A: RefUnwindSafe + UnwindSafe, - W: WindowAggregate + 'static, - T: SqlFnOutput, - { - self.db - .borrow_mut() - .create_window_function(fn_name, n_arg, flags, aggr) - } - - /// Removes a user-defined function from this - /// database connection. - /// - /// `fn_name` and `n_arg` should match the name and number of arguments - /// given to [`create_scalar_function`](Connection::create_scalar_function) - /// or [`create_aggregate_function`](Connection::create_aggregate_function). - /// - /// # Failure - /// - /// Will return Err if the function could not be removed. - #[inline] - pub fn remove_function(&self, fn_name: N, n_arg: c_int) -> Result<()> { - self.db.borrow_mut().remove_function(fn_name, n_arg) - } -} - -impl InnerConnection { - /// ```compile_fail - /// use rusqlite::{functions::FunctionFlags, Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.create_scalar_function( - /// "test", - /// 0, - /// FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - /// |_| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// Ok(true) - /// }, - /// ); - /// } - /// let result: Result = db.query_row("SELECT test()", [], |r| r.get(0)); - /// assert!(result?); - /// Ok(()) - /// } - /// ``` - fn create_scalar_function( - &mut self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - x_func: F, - ) -> Result<()> - where - F: Fn(&Context<'_>) -> Result + Send + 'static, - T: SqlFnOutput, - { - unsafe extern "C" fn call_boxed_closure( - ctx: *mut sqlite3_context, - argc: c_int, - argv: *mut *mut sqlite3_value, - ) where - F: Fn(&Context<'_>) -> Result, - T: SqlFnOutput, - { - let args = slice::from_raw_parts(argv, argc as usize); - let r = catch_unwind(|| { - let boxed_f: *const F = ffi::sqlite3_user_data(ctx).cast::(); - assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - let ctx = Context { ctx, args }; - (*boxed_f)(&ctx) - }); - let t = match r { - Err(_) => { - report_error(ctx, &Error::UnwindingPanic); - return; - } - Ok(r) => r, - }; - sql_result(ctx, args, t); - } - - let boxed_f: *mut F = Box::into_raw(Box::new(x_func)); - let c_name = fn_name.as_cstr()?; - let r = unsafe { - ffi::sqlite3_create_function_v2( - self.db(), - c_name.as_ptr(), - n_arg, - flags.bits(), - boxed_f.cast::(), - Some(call_boxed_closure::), - None, - None, - Some(free_boxed_value::), - ) - }; - self.decode_result(r) - } - - fn create_aggregate_function( - &mut self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - aggr: D, - ) -> Result<()> - where - A: RefUnwindSafe + UnwindSafe, - D: Aggregate + 'static, - T: SqlFnOutput, - { - let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr)); - let c_name = fn_name.as_cstr()?; - let r = unsafe { - ffi::sqlite3_create_function_v2( - self.db(), - c_name.as_ptr(), - n_arg, - flags.bits(), - boxed_aggr.cast::(), - None, - Some(call_boxed_step::), - Some(call_boxed_final::), - Some(free_boxed_value::), - ) - }; - self.decode_result(r) - } - - #[cfg(feature = "window")] - fn create_window_function( - &mut self, - fn_name: N, - n_arg: c_int, - flags: FunctionFlags, - aggr: W, - ) -> Result<()> - where - A: RefUnwindSafe + UnwindSafe, - W: WindowAggregate + 'static, - T: SqlFnOutput, - { - let boxed_aggr: *mut W = Box::into_raw(Box::new(aggr)); - let c_name = fn_name.as_cstr()?; - let r = unsafe { - ffi::sqlite3_create_window_function( - self.db(), - c_name.as_ptr(), - n_arg, - flags.bits(), - boxed_aggr.cast::(), - Some(call_boxed_step::), - Some(call_boxed_final::), - Some(call_boxed_value::), - Some(call_boxed_inverse::), - Some(free_boxed_value::), - ) - }; - self.decode_result(r) - } - - fn remove_function(&mut self, fn_name: N, n_arg: c_int) -> Result<()> { - let c_name = fn_name.as_cstr()?; - let r = unsafe { - ffi::sqlite3_create_function_v2( - self.db(), - c_name.as_ptr(), - n_arg, - ffi::SQLITE_UTF8, - ptr::null_mut(), - None, - None, - None, - None, - ) - }; - self.decode_result(r) - } -} - -unsafe fn aggregate_context(ctx: *mut sqlite3_context, bytes: usize) -> Option<*mut *mut A> { - let pac = ffi::sqlite3_aggregate_context(ctx, bytes as c_int) as *mut *mut A; - if pac.is_null() { - return None; - } - Some(pac) -} - -unsafe extern "C" fn call_boxed_step( - ctx: *mut sqlite3_context, - argc: c_int, - argv: *mut *mut sqlite3_value, -) where - A: RefUnwindSafe + UnwindSafe, - D: Aggregate, - T: SqlFnOutput, -{ - let Some(pac) = aggregate_context(ctx, size_of::<*mut A>()) else { - ffi::sqlite3_result_error_nomem(ctx); - return; - }; - - let r = catch_unwind(|| { - let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx).cast::(); - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - let mut ctx = Context { - ctx, - args: slice::from_raw_parts(argv, argc as usize), - }; - - #[expect(clippy::unnecessary_cast)] - if (*pac as *mut A).is_null() { - *pac = Box::into_raw(Box::new((*boxed_aggr).init(&mut ctx)?)); - } - - (*boxed_aggr).step(&mut ctx, &mut **pac) - }); - let r = match r { - Err(_) => { - report_error(ctx, &Error::UnwindingPanic); - return; - } - Ok(r) => r, - }; - match r { - Ok(_) => {} - Err(err) => report_error(ctx, &err), - }; -} - -#[cfg(feature = "window")] -unsafe extern "C" fn call_boxed_inverse( - ctx: *mut sqlite3_context, - argc: c_int, - argv: *mut *mut sqlite3_value, -) where - A: RefUnwindSafe + UnwindSafe, - W: WindowAggregate, - T: SqlFnOutput, -{ - let Some(pac) = aggregate_context(ctx, size_of::<*mut A>()) else { - ffi::sqlite3_result_error_nomem(ctx); - return; - }; - - let r = catch_unwind(|| { - let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::(); - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - let mut ctx = Context { - ctx, - args: slice::from_raw_parts(argv, argc as usize), - }; - (*boxed_aggr).inverse(&mut ctx, &mut **pac) - }); - let r = match r { - Err(_) => { - report_error(ctx, &Error::UnwindingPanic); - return; - } - Ok(r) => r, - }; - match r { - Ok(_) => {} - Err(err) => report_error(ctx, &err), - }; -} - -unsafe extern "C" fn call_boxed_final(ctx: *mut sqlite3_context) -where - A: RefUnwindSafe + UnwindSafe, - D: Aggregate, - T: SqlFnOutput, -{ - // Within the xFinal callback, it is customary to set N=0 in calls to - // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. - let a: Option = match aggregate_context(ctx, 0) { - Some(pac) => - { - #[expect(clippy::unnecessary_cast)] - if (*pac as *mut A).is_null() { - None - } else { - let a = Box::from_raw(*pac); - Some(*a) - } - } - None => None, - }; - - let r = catch_unwind(|| { - let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx).cast::(); - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - let mut ctx = Context { ctx, args: &mut [] }; - (*boxed_aggr).finalize(&mut ctx, a) - }); - let t = match r { - Err(_) => { - report_error(ctx, &Error::UnwindingPanic); - return; - } - Ok(r) => r, - }; - sql_result(ctx, &[], t); -} - -#[cfg(feature = "window")] -unsafe extern "C" fn call_boxed_value(ctx: *mut sqlite3_context) -where - A: RefUnwindSafe + UnwindSafe, - W: WindowAggregate, - T: SqlFnOutput, -{ - // Within the xValue callback, it is customary to set N=0 in calls to - // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. - let pac = aggregate_context(ctx, 0).filter(|&pac| { - #[expect(clippy::unnecessary_cast)] - !(*pac as *mut A).is_null() - }); - - let r = catch_unwind(|| { - let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::(); - assert!( - !boxed_aggr.is_null(), - "Internal error - null aggregate pointer" - ); - (*boxed_aggr).value(pac.map(|pac| &mut **pac)) - }); - let t = match r { - Err(_) => { - report_error(ctx, &Error::UnwindingPanic); - return; - } - Ok(r) => r, - }; - sql_result(ctx, &[], t); -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - #[cfg(feature = "window")] - use crate::functions::WindowAggregate; - use crate::functions::{Aggregate, Context, FunctionFlags, SqlFnArg, SubType}; - use crate::{Connection, Error, Result}; - use regex::Regex; - use std::ffi::c_double; - - fn half(ctx: &Context<'_>) -> Result { - assert!(!ctx.is_empty()); - assert_eq!(ctx.len(), 1, "called with unexpected number of arguments"); - assert!(unsafe { - ctx.get_connection() - .as_ref() - .map(::std::ops::Deref::deref) - .is_ok() - }); - let value = ctx.get::(0)?; - Ok(value / 2f64) - } - - #[test] - fn test_function_half() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_scalar_function( - c"half", - 1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - half, - )?; - let result: f64 = db.one_column("SELECT half(6)", [])?; - - assert!((3f64 - result).abs() < f64::EPSILON); - Ok(()) - } - - #[test] - fn test_remove_function() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_scalar_function( - c"half", - 1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - half, - )?; - assert!((3f64 - db.one_column::("SELECT half(6)", [])?).abs() < f64::EPSILON); - - db.remove_function(c"half", 1)?; - db.one_column::("SELECT half(6)", []).unwrap_err(); - Ok(()) - } - - // This implementation of a regexp scalar function uses SQLite's auxiliary data - // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular - // expression multiple times within one query. - fn regexp_with_auxiliary(ctx: &Context<'_>) -> Result { - assert_eq!(ctx.len(), 2, "called with unexpected number of arguments"); - type BoxError = Box; - let regexp: std::sync::Arc = ctx - .get_or_create_aux(0, |vr| -> Result<_, BoxError> { - Ok(Regex::new(vr.as_str()?)?) - })?; - - let is_match = { - let text = ctx - .get_raw(1) - .as_str() - .map_err(|e| Error::UserFunctionError(e.into()))?; - - regexp.is_match(text) - }; - - Ok(is_match) - } - - #[test] - fn test_function_regexp_with_auxiliary() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch( - "BEGIN; - CREATE TABLE foo (x string); - INSERT INTO foo VALUES ('lisa'); - INSERT INTO foo VALUES ('lXsi'); - INSERT INTO foo VALUES ('lisX'); - END;", - )?; - db.create_scalar_function( - c"regexp", - 2, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - regexp_with_auxiliary, - )?; - - assert!(db.one_column::("SELECT regexp('l.s[aeiouy]', 'lisa')", [])?); - - assert_eq!( - 2, - db.one_column::( - "SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1", - [], - )? - ); - Ok(()) - } - - #[test] - fn test_varargs_function() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_scalar_function( - c"my_concat", - -1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - |ctx| { - let mut ret = String::new(); - - for idx in 0..ctx.len() { - let s = ctx.get::(idx)?; - ret.push_str(&s); - } - - Ok(ret) - }, - )?; - - for &(expected, query) in &[ - ("", "SELECT my_concat()"), - ("onetwo", "SELECT my_concat('one', 'two')"), - ("abc", "SELECT my_concat('a', 'b', 'c')"), - ] { - assert_eq!(expected, db.one_column::(query, [])?); - } - Ok(()) - } - - #[test] - fn test_get_aux_type_checking() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_scalar_function(c"example", 2, FunctionFlags::default(), |ctx| { - if !ctx.get::(1)? { - ctx.set_aux::(0, 100)?; - } else { - assert_eq!(ctx.get_aux::(0), Err(Error::GetAuxWrongType)); - assert_eq!(*ctx.get_aux::(0)?.unwrap(), 100); - } - Ok(true) - })?; - - let res: bool = db.query_row( - "SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)", - [], - |r| r.get(0), - )?; - // Doesn't actually matter, we'll assert in the function if there's a problem. - assert!(res); - Ok(()) - } - - struct Sum; - struct Count; - - impl Aggregate> for Sum { - fn init(&self, _: &mut Context<'_>) -> Result { - Ok(0) - } - - fn step(&self, ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> { - *sum += ctx.get::(0)?; - Ok(()) - } - - fn finalize(&self, _: &mut Context<'_>, sum: Option) -> Result> { - Ok(sum) - } - } - - impl Aggregate for Count { - fn init(&self, _: &mut Context<'_>) -> Result { - Ok(0) - } - - fn step(&self, _ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> { - *sum += 1; - Ok(()) - } - - fn finalize(&self, _: &mut Context<'_>, sum: Option) -> Result { - Ok(sum.unwrap_or(0)) - } - } - - #[test] - fn test_sum() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_aggregate_function( - c"my_sum", - 1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - Sum, - )?; - - // 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)"; - assert!(db.one_column::, _>(no_result, [])?.is_none()); - - let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; - assert_eq!(4, db.one_column::(single_sum, [])?); - - let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \ - 2, 1)"; - let result: (i64, i64) = db.query_row(dual_sum, [], |r| Ok((r.get(0)?, r.get(1)?)))?; - assert_eq!((4, 2), result); - Ok(()) - } - - #[test] - fn test_count() -> Result<()> { - let db = Connection::open_in_memory()?; - db.create_aggregate_function( - c"my_count", - -1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - Count, - )?; - - // 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)"; - assert_eq!(db.one_column::(no_result, [])?, 0); - - let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)"; - assert_eq!(2, db.one_column::(single_sum, [])?); - Ok(()) - } - - #[cfg(feature = "window")] - impl WindowAggregate> for Sum { - fn inverse(&self, ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> { - *sum -= ctx.get::(0)?; - Ok(()) - } - - fn value(&self, sum: Option<&mut i64>) -> Result> { - Ok(sum.copied()) - } - } - - #[test] - #[cfg(feature = "window")] - fn test_window() -> Result<()> { - use fallible_iterator::FallibleIterator; - - let db = Connection::open_in_memory()?; - db.create_window_function( - c"sumint", - 1, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC, - Sum, - )?; - db.execute_batch( - "CREATE TABLE t3(x, y); - INSERT INTO t3 VALUES('a', 4), - ('b', 5), - ('c', 3), - ('d', 8), - ('e', 1);", - )?; - - let mut stmt = db.prepare( - "SELECT x, sumint(y) OVER ( - ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING - ) AS sum_y - FROM t3 ORDER BY x;", - )?; - - let results: Vec<(String, i64)> = stmt - .query([])? - .map(|row| Ok((row.get("x")?, row.get("sum_y")?))) - .collect()?; - let expected = vec![ - ("a".to_owned(), 9), - ("b".to_owned(), 12), - ("c".to_owned(), 16), - ("d".to_owned(), 12), - ("e".to_owned(), 9), - ]; - assert_eq!(expected, results); - Ok(()) - } - - #[test] - fn test_sub_type() -> Result<()> { - fn test_getsubtype(ctx: &Context<'_>) -> Result { - Ok(ctx.get_subtype(0) as i32) - } - fn test_setsubtype(ctx: &Context<'_>) -> Result<(SqlFnArg, SubType)> { - use std::ffi::c_uint; - let value = ctx.get_arg(0); - let sub_type = ctx.get::(1)?; - Ok((value, Some(sub_type))) - } - let db = Connection::open_in_memory()?; - db.create_scalar_function( - c"test_getsubtype", - 1, - FunctionFlags::SQLITE_UTF8, - test_getsubtype, - )?; - db.create_scalar_function( - c"test_setsubtype", - 2, - FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_RESULT_SUBTYPE, - test_setsubtype, - )?; - let result: i32 = db.one_column("SELECT test_getsubtype('hello');", [])?; - assert_eq!(0, result); - - let result: i32 = - db.one_column("SELECT test_getsubtype(test_setsubtype('hello',123));", [])?; - assert_eq!(123, result); - - Ok(()) - } - - #[test] - fn test_blob() -> Result<()> { - fn test_len(ctx: &Context<'_>) -> Result { - let blob = ctx.get_raw(0); - Ok(blob - .as_bytes_or_null()? - .map_or(0, |b| b.len().try_into().unwrap())) - } - let db = Connection::open_in_memory()?; - db.create_scalar_function("test_len", 1, FunctionFlags::SQLITE_DETERMINISTIC, test_len)?; - assert_eq!( - 6, - db.one_column::("SELECT test_len(X'53514C697465');", [])? - ); - assert_eq!(0, db.one_column::("SELECT test_len(X'');", [])?); - assert_eq!(0, db.one_column::("SELECT test_len(NULL);", [])?); - Ok(()) - } - - #[test] - #[cfg(feature = "pointer")] - fn test_rc_pointer() -> Result<()> { - use crate::types::ToSqlOutput; - use std::ops::Deref; - use std::rc::Rc; - - const PTR_TYPE: &std::ffi::CStr = c"my_rust_ptr"; - let rc = Rc::new(1); - { - let ptr = ToSqlOutput::from_rc(rc.clone(), PTR_TYPE); - assert_eq!(2, Rc::strong_count(&rc)); - fn myfunc(ctx: &Context<'_>) -> Result> { - let x = unsafe { ctx.get_pointer(0, PTR_TYPE) }; - assert_eq!(x, Some(&1)); - Ok(ToSqlOutput::from_rc(Rc::new(*x.unwrap()), PTR_TYPE)) - } - let db = Connection::open_in_memory()?; - db.create_scalar_function("myfunc", 1, FunctionFlags::SQLITE_DETERMINISTIC, myfunc)?; - let mut stmt = db.prepare("SELECT myfunc(?)")?; - let result = stmt.query_one([ptr], |r| { - unsafe { r.get_pointer::<_, i32>(0, PTR_TYPE) }.map(|opt| opt.cloned()) - })?; - assert_eq!(result.unwrap(), *rc.deref()); - } - assert_eq!(1, Rc::strong_count(&rc)); - Ok(()) - } - - #[test] - #[cfg(feature = "pointer")] - fn test_box_pointer() -> Result<()> { - use crate::types::ToSqlOutput; - - const PTR_TYPE: &std::ffi::CStr = c"my_rust_ptr"; - let value = 1; - let ptr = ToSqlOutput::new_boxed(value, PTR_TYPE); - fn myfunc(ctx: &Context<'_>) -> Result> { - let x = unsafe { ctx.get_pointer(0, PTR_TYPE) }; - assert_eq!(x, Some(&1)); - Ok(ToSqlOutput::new_boxed(*x.unwrap(), PTR_TYPE)) - } - let db = Connection::open_in_memory()?; - db.create_scalar_function("myfunc", 1, FunctionFlags::SQLITE_DETERMINISTIC, myfunc)?; - let mut stmt = db.prepare("SELECT myfunc(?)")?; - let result = stmt.query_one([ptr], |r| { - unsafe { r.get_pointer::<_, i32>(0, PTR_TYPE) }.map(|opt| opt.cloned()) - })?; - assert_eq!(result.unwrap(), value); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/hooks/mod.rs b/vendor/rusqlite/src/hooks/mod.rs deleted file mode 100644 index efa7bce..0000000 --- a/vendor/rusqlite/src/hooks/mod.rs +++ /dev/null @@ -1,1002 +0,0 @@ -//! Commit, Data Change and Rollback Notification Callbacks -#![expect(non_camel_case_types)] - -use std::ffi::{c_char, c_int, c_void, CStr}; -use std::panic::catch_unwind; -use std::ptr; - -use crate::ffi; - -use crate::{error::decode_result_raw, Connection, InnerConnection, Result}; - -#[cfg(feature = "preupdate_hook")] -pub use preupdate_hook::*; - -#[cfg(feature = "preupdate_hook")] -mod preupdate_hook; - -/// Action Codes -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[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, -} - -impl From for Action { - #[inline] - fn from(code: i32) -> Self { - match code { - ffi::SQLITE_DELETE => Self::SQLITE_DELETE, - ffi::SQLITE_INSERT => Self::SQLITE_INSERT, - ffi::SQLITE_UPDATE => Self::SQLITE_UPDATE, - _ => Self::UNKNOWN, - } - } -} - -/// The context received by an authorizer hook. -/// -/// See for more info. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct AuthContext<'c> { - /// The action to be authorized. - pub action: AuthAction<'c>, - - /// The database name, if applicable. - pub database_name: Option<&'c str>, - - /// The inner-most trigger or view responsible for the access attempt. - /// `None` if the access attempt was made by top-level SQL code. - pub accessor: Option<&'c str>, -} - -/// Actions and arguments found within a statement during -/// preparation. -/// -/// See for more info. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[non_exhaustive] -#[allow(missing_docs)] -pub enum AuthAction<'c> { - /// This variant is not normally produced by SQLite. You may encounter it - // if you're using a different version than what's supported by this library. - Unknown { - /// The unknown authorization action code. - code: i32, - /// The third arg to the authorizer callback. - arg1: Option<&'c str>, - /// The fourth arg to the authorizer callback. - arg2: Option<&'c str>, - }, - CreateIndex { - index_name: &'c str, - table_name: &'c str, - }, - CreateTable { - table_name: &'c str, - }, - CreateTempIndex { - index_name: &'c str, - table_name: &'c str, - }, - CreateTempTable { - table_name: &'c str, - }, - CreateTempTrigger { - trigger_name: &'c str, - table_name: &'c str, - }, - CreateTempView { - view_name: &'c str, - }, - CreateTrigger { - trigger_name: &'c str, - table_name: &'c str, - }, - CreateView { - view_name: &'c str, - }, - Delete { - table_name: &'c str, - }, - DropIndex { - index_name: &'c str, - table_name: &'c str, - }, - DropTable { - table_name: &'c str, - }, - DropTempIndex { - index_name: &'c str, - table_name: &'c str, - }, - DropTempTable { - table_name: &'c str, - }, - DropTempTrigger { - trigger_name: &'c str, - table_name: &'c str, - }, - DropTempView { - view_name: &'c str, - }, - DropTrigger { - trigger_name: &'c str, - table_name: &'c str, - }, - DropView { - view_name: &'c str, - }, - Insert { - table_name: &'c str, - }, - Pragma { - pragma_name: &'c str, - /// The pragma value, if present (e.g., `PRAGMA name = value;`). - pragma_value: Option<&'c str>, - }, - Read { - table_name: &'c str, - column_name: &'c str, - }, - Select, - Transaction { - operation: TransactionOperation, - }, - Update { - table_name: &'c str, - column_name: &'c str, - }, - Attach { - filename: &'c str, - }, - Detach { - database_name: &'c str, - }, - AlterTable { - database_name: &'c str, - table_name: &'c str, - }, - Reindex { - index_name: &'c str, - }, - Analyze { - table_name: &'c str, - }, - CreateVtable { - table_name: &'c str, - module_name: &'c str, - }, - DropVtable { - table_name: &'c str, - module_name: &'c str, - }, - Function { - function_name: &'c str, - }, - Savepoint { - operation: TransactionOperation, - savepoint_name: &'c str, - }, - Recursive, -} - -impl<'c> AuthAction<'c> { - fn from_raw(code: i32, arg1: Option<&'c str>, arg2: Option<&'c str>) -> Self { - match (code, arg1, arg2) { - (ffi::SQLITE_CREATE_INDEX, Some(index_name), Some(table_name)) => Self::CreateIndex { - index_name, - table_name, - }, - (ffi::SQLITE_CREATE_TABLE, Some(table_name), _) => Self::CreateTable { table_name }, - (ffi::SQLITE_CREATE_TEMP_INDEX, Some(index_name), Some(table_name)) => { - Self::CreateTempIndex { - index_name, - table_name, - } - } - (ffi::SQLITE_CREATE_TEMP_TABLE, Some(table_name), _) => { - Self::CreateTempTable { table_name } - } - (ffi::SQLITE_CREATE_TEMP_TRIGGER, Some(trigger_name), Some(table_name)) => { - Self::CreateTempTrigger { - trigger_name, - table_name, - } - } - (ffi::SQLITE_CREATE_TEMP_VIEW, Some(view_name), _) => { - Self::CreateTempView { view_name } - } - (ffi::SQLITE_CREATE_TRIGGER, Some(trigger_name), Some(table_name)) => { - Self::CreateTrigger { - trigger_name, - table_name, - } - } - (ffi::SQLITE_CREATE_VIEW, Some(view_name), _) => Self::CreateView { view_name }, - (ffi::SQLITE_DELETE, Some(table_name), None) => Self::Delete { table_name }, - (ffi::SQLITE_DROP_INDEX, Some(index_name), Some(table_name)) => Self::DropIndex { - index_name, - table_name, - }, - (ffi::SQLITE_DROP_TABLE, Some(table_name), _) => Self::DropTable { table_name }, - (ffi::SQLITE_DROP_TEMP_INDEX, Some(index_name), Some(table_name)) => { - Self::DropTempIndex { - index_name, - table_name, - } - } - (ffi::SQLITE_DROP_TEMP_TABLE, Some(table_name), _) => { - Self::DropTempTable { table_name } - } - (ffi::SQLITE_DROP_TEMP_TRIGGER, Some(trigger_name), Some(table_name)) => { - Self::DropTempTrigger { - trigger_name, - table_name, - } - } - (ffi::SQLITE_DROP_TEMP_VIEW, Some(view_name), _) => Self::DropTempView { view_name }, - (ffi::SQLITE_DROP_TRIGGER, Some(trigger_name), Some(table_name)) => Self::DropTrigger { - trigger_name, - table_name, - }, - (ffi::SQLITE_DROP_VIEW, Some(view_name), _) => Self::DropView { view_name }, - (ffi::SQLITE_INSERT, Some(table_name), _) => Self::Insert { table_name }, - (ffi::SQLITE_PRAGMA, Some(pragma_name), pragma_value) => Self::Pragma { - pragma_name, - pragma_value, - }, - (ffi::SQLITE_READ, Some(table_name), Some(column_name)) => Self::Read { - table_name, - column_name, - }, - (ffi::SQLITE_SELECT, ..) => Self::Select, - (ffi::SQLITE_TRANSACTION, Some(operation_str), _) => Self::Transaction { - operation: TransactionOperation::from_str(operation_str), - }, - (ffi::SQLITE_UPDATE, Some(table_name), Some(column_name)) => Self::Update { - table_name, - column_name, - }, - (ffi::SQLITE_ATTACH, Some(filename), _) => Self::Attach { filename }, - (ffi::SQLITE_DETACH, Some(database_name), _) => Self::Detach { database_name }, - (ffi::SQLITE_ALTER_TABLE, Some(database_name), Some(table_name)) => Self::AlterTable { - database_name, - table_name, - }, - (ffi::SQLITE_REINDEX, Some(index_name), _) => Self::Reindex { index_name }, - (ffi::SQLITE_ANALYZE, Some(table_name), _) => Self::Analyze { table_name }, - (ffi::SQLITE_CREATE_VTABLE, Some(table_name), Some(module_name)) => { - Self::CreateVtable { - table_name, - module_name, - } - } - (ffi::SQLITE_DROP_VTABLE, Some(table_name), Some(module_name)) => Self::DropVtable { - table_name, - module_name, - }, - (ffi::SQLITE_FUNCTION, _, Some(function_name)) => Self::Function { function_name }, - (ffi::SQLITE_SAVEPOINT, Some(operation_str), Some(savepoint_name)) => Self::Savepoint { - operation: TransactionOperation::from_str(operation_str), - savepoint_name, - }, - (ffi::SQLITE_RECURSIVE, ..) => Self::Recursive, - (code, arg1, arg2) => Self::Unknown { code, arg1, arg2 }, - } - } -} - -pub(crate) type BoxedAuthorizer = - Box FnMut(AuthContext<'c>) -> Authorization + Send + 'static>; - -/// A transaction operation. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[non_exhaustive] -#[allow(missing_docs)] -pub enum TransactionOperation { - Unknown, - Begin, - Release, - Rollback, -} - -impl TransactionOperation { - fn from_str(op_str: &str) -> Self { - match op_str { - "BEGIN" => Self::Begin, - "RELEASE" => Self::Release, - "ROLLBACK" => Self::Rollback, - _ => Self::Unknown, - } - } -} - -/// [`authorizer`](Connection::authorizer) return code -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub enum Authorization { - /// Authorize the action. - Allow, - /// Don't allow access, but don't trigger an error either. - Ignore, - /// Trigger an error. - Deny, -} - -impl Authorization { - fn into_raw(self) -> c_int { - match self { - Self::Allow => ffi::SQLITE_OK, - Self::Ignore => ffi::SQLITE_IGNORE, - Self::Deny => ffi::SQLITE_DENY, - } - } -} - -impl Connection { - /// Register a callback function to be invoked whenever - /// a transaction is committed. - /// - /// The callback returns `true` to rollback. - #[inline] - pub fn commit_hook(&self, hook: Option) -> Result<()> - where - F: FnMut() -> bool + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().commit_hook(hook); - Ok(()) - } - - /// Register a callback function to be invoked whenever - /// a transaction is rolled back. - #[inline] - pub fn rollback_hook(&self, hook: Option) -> Result<()> - where - F: FnMut() + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().rollback_hook(hook); - Ok(()) - } - - /// Register a callback function to be invoked whenever - /// a row is updated, inserted or deleted in a rowid table. - /// - /// The callback parameters are: - /// - /// - the type of database update (`SQLITE_INSERT`, `SQLITE_UPDATE` or - /// `SQLITE_DELETE`), - /// - the name of the database ("main", "temp", ...), - /// - the name of the table that is updated, - /// - the ROWID of the row that is updated. - #[inline] - pub fn update_hook(&self, hook: Option) -> Result<()> - where - F: FnMut(Action, &str, &str, i64) + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().update_hook(hook); - Ok(()) - } - - /// Register a callback that is invoked each time data is committed to a database in wal mode. - /// - /// A single database handle may have at most a single write-ahead log callback registered at one time. - /// Calling `wal_hook` replaces any previously registered write-ahead log callback. - /// Note that the `sqlite3_wal_autocheckpoint()` interface and the `wal_autocheckpoint` pragma - /// both invoke `sqlite3_wal_hook()` and will overwrite any prior `sqlite3_wal_hook()` settings. - pub fn wal_hook(&self, hook: Option Result<()>>) { - unsafe extern "C" fn wal_hook_callback( - client_data: *mut c_void, - db: *mut ffi::sqlite3, - db_name: *const c_char, - pages: c_int, - ) -> c_int { - let hook_fn: fn(&Wal, c_int) -> Result<()> = std::mem::transmute(client_data); - let wal = Wal { db, db_name }; - catch_unwind(|| match hook_fn(&wal, pages) { - Ok(_) => ffi::SQLITE_OK, - Err(e) => e - .sqlite_error() - .map_or(ffi::SQLITE_ERROR, |x| x.extended_code), - }) - .unwrap_or_default() - } - let c = self.db.borrow_mut(); - unsafe { - ffi::sqlite3_wal_hook( - c.db(), - hook.as_ref().map(|_| wal_hook_callback as _), - hook.map_or_else(ptr::null_mut, |f| f as *mut c_void), - ); - } - } - - /// Register a query progress callback. - /// - /// The parameter `num_ops` is the approximate number of virtual machine - /// instructions that are evaluated between successive invocations of the - /// `handler`. If `num_ops` is less than one then the progress handler - /// is disabled. - /// - /// If the progress callback returns `true`, the operation is interrupted. - pub fn progress_handler(&self, num_ops: c_int, handler: Option) -> Result<()> - where - F: FnMut() -> bool + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().progress_handler(num_ops, handler); - Ok(()) - } - - /// Register an authorizer callback that's invoked - /// as a statement is being prepared. - #[inline] - pub fn authorizer<'c, F>(&self, hook: Option) -> Result<()> - where - F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().authorizer(hook); - Ok(()) - } -} - -/// Checkpoint mode -#[derive(Clone, Copy)] -#[repr(i32)] -#[non_exhaustive] -pub enum CheckpointMode { - /// Do as much as possible w/o blocking - PASSIVE = ffi::SQLITE_CHECKPOINT_PASSIVE, - /// Wait for writers, then checkpoint - FULL = ffi::SQLITE_CHECKPOINT_FULL, - /// Like FULL but wait for readers - RESTART = ffi::SQLITE_CHECKPOINT_RESTART, - /// Like RESTART but also truncate WAL - TRUNCATE = ffi::SQLITE_CHECKPOINT_TRUNCATE, - /// Do no work at all - #[cfg(feature = "modern_sqlite")] // 3.51.0 - NOOP = -1, //ffi::SQLITE_CHECKPOINT_NOOP, -} - -/// Write-Ahead Log -pub struct Wal { - db: *mut ffi::sqlite3, - db_name: *const c_char, -} - -impl Wal { - /// Checkpoint a database - pub fn checkpoint(&self) -> Result<()> { - unsafe { decode_result_raw(self.db, ffi::sqlite3_wal_checkpoint(self.db, self.db_name)) } - } - /// Checkpoint a database - pub fn checkpoint_v2(&self, mode: CheckpointMode) -> Result<(c_int, c_int)> { - let mut n_log = 0; - let mut n_ckpt = 0; - unsafe { - decode_result_raw( - self.db, - ffi::sqlite3_wal_checkpoint_v2( - self.db, - self.db_name, - mode as c_int, - &mut n_log, - &mut n_ckpt, - ), - )? - }; - Ok((n_log, n_ckpt)) - } - - /// Name of the database that was written to - pub fn name(&self) -> &CStr { - unsafe { CStr::from_ptr(self.db_name) } - } -} - -impl InnerConnection { - #[inline] - pub fn remove_hooks(&mut self) { - self.update_hook(None::); - self.commit_hook(None:: bool>); - self.rollback_hook(None::); - self.progress_handler(0, None:: bool>); - self.authorizer(None::) -> Authorization>); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.commit_hook(Some(|| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// true - /// })); - /// } - /// assert!(db - /// .execute_batch( - /// "BEGIN; - /// CREATE TABLE foo (t TEXT); - /// COMMIT;", - /// ) - /// .is_err()); - /// Ok(()) - /// } - /// ``` - fn commit_hook(&mut self, hook: Option) - where - F: FnMut() -> bool + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure(p_arg: *mut c_void) -> c_int - where - F: FnMut() -> bool, - { - let r = catch_unwind(|| { - let boxed_hook: *mut F = p_arg.cast::(); - (*boxed_hook)() - }); - c_int::from(r.unwrap_or_default()) - } - let boxed_hook = hook.map(Box::new); - unsafe { - ffi::sqlite3_commit_hook( - self.db(), - boxed_hook.as_ref().map(|_| call_boxed_closure:: as _), - boxed_hook - .as_ref() - .map_or_else(ptr::null_mut, |h| &**h as *const F as *mut _), - ) - }; - self.commit_hook = boxed_hook.map(|bh| bh as _); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.rollback_hook(Some(|| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// })); - /// } - /// assert!(db - /// .execute_batch( - /// "BEGIN; - /// CREATE TABLE foo (t TEXT); - /// ROLLBACK;", - /// ) - /// .is_err()); - /// Ok(()) - /// } - /// ``` - fn rollback_hook(&mut self, hook: Option) - where - F: FnMut() + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure(p_arg: *mut c_void) - where - F: FnMut(), - { - drop(catch_unwind(|| { - let boxed_hook: *mut F = p_arg.cast::(); - (*boxed_hook)(); - })); - } - - let boxed_hook = hook.map(Box::new); - unsafe { - ffi::sqlite3_rollback_hook( - self.db(), - boxed_hook.as_ref().map(|_| call_boxed_closure:: as _), - boxed_hook - .as_ref() - .map_or_else(ptr::null_mut, |h| &**h as *const F as *mut _), - ) - }; - self.rollback_hook = boxed_hook.map(|bh| bh as _); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.update_hook(Some(|_, _: &str, _: &str, _| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// })); - /// } - /// db.execute_batch("CREATE TABLE foo AS SELECT 1 AS bar;") - /// } - /// ``` - fn update_hook(&mut self, hook: Option) - where - F: FnMut(Action, &str, &str, i64) + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure( - p_arg: *mut c_void, - action_code: c_int, - p_db_name: *const c_char, - p_table_name: *const c_char, - row_id: i64, - ) where - F: FnMut(Action, &str, &str, i64), - { - let action = Action::from(action_code); - drop(catch_unwind(|| { - let boxed_hook: *mut F = p_arg.cast::(); - (*boxed_hook)( - action, - expect_utf8(p_db_name, "database name"), - expect_utf8(p_table_name, "table name"), - row_id, - ); - })); - } - - let boxed_hook = hook.map(Box::new); - unsafe { - ffi::sqlite3_update_hook( - self.db(), - boxed_hook.as_ref().map(|_| call_boxed_closure:: as _), - boxed_hook - .as_ref() - .map_or_else(ptr::null_mut, |h| &**h as *const F as *mut _), - ) - }; - self.update_hook = boxed_hook.map(|bh| bh as _); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.progress_handler( - /// 1, - /// Some(|| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// true - /// }), - /// ); - /// } - /// assert!(db - /// .execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;") - /// .is_err()); - /// Ok(()) - /// } - /// ``` - fn progress_handler(&mut self, num_ops: c_int, handler: Option) - where - F: FnMut() -> bool + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure(p_arg: *mut c_void) -> c_int - where - F: FnMut() -> bool, - { - let r = catch_unwind(|| { - let boxed_handler: *mut F = p_arg.cast::(); - (*boxed_handler)() - }); - c_int::from(r.unwrap_or_default()) - } - - let boxed_handler = handler.map(Box::new); - unsafe { - ffi::sqlite3_progress_handler( - self.db(), - num_ops, - boxed_handler.as_ref().map(|_| call_boxed_closure:: as _), - boxed_handler - .as_ref() - .map_or_else(ptr::null_mut, |h| &**h as *const F as *mut _), - ) - }; - self.progress_handler = boxed_handler.map(|bh| bh as _); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.authorizer(Some(|_: rusqlite::hooks::AuthContext<'_>| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// rusqlite::hooks::Authorization::Deny - /// })); - /// } - /// assert!(db - /// .execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;") - /// .is_err()); - /// Ok(()) - /// } - /// ``` - fn authorizer<'c, F>(&'c mut self, authorizer: Option) - where - F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure<'c, F>( - p_arg: *mut c_void, - action_code: c_int, - param1: *const c_char, - param2: *const c_char, - db_name: *const c_char, - trigger_or_view_name: *const c_char, - ) -> c_int - where - F: FnMut(AuthContext<'c>) -> Authorization + Send + 'static, - { - catch_unwind(|| { - let action = AuthAction::from_raw( - action_code, - expect_optional_utf8(param1, "authorizer param 1"), - expect_optional_utf8(param2, "authorizer param 2"), - ); - let auth_ctx = AuthContext { - action, - database_name: expect_optional_utf8(db_name, "database name"), - accessor: expect_optional_utf8( - trigger_or_view_name, - "accessor (inner-most trigger or view)", - ), - }; - let boxed_hook: *mut F = p_arg.cast::(); - (*boxed_hook)(auth_ctx) - }) - .map_or_else(|_| ffi::SQLITE_ERROR, Authorization::into_raw) - } - - let boxed_authorizer = authorizer.map(Box::new); - - match unsafe { - ffi::sqlite3_set_authorizer( - self.db(), - boxed_authorizer - .as_ref() - .map(|_| call_boxed_closure::<'c, F> as _), - boxed_authorizer - .as_ref() - .map_or_else(ptr::null_mut, |f| &**f as *const F as *mut _), - ) - } { - ffi::SQLITE_OK => { - self.authorizer = boxed_authorizer.map(|ba| ba as _); - } - err_code => { - // The only error that `sqlite3_set_authorizer` returns is `SQLITE_MISUSE` - // when compiled with `ENABLE_API_ARMOR` and the db pointer is invalid. - // This library does not allow constructing a null db ptr, so if this branch - // is hit, something very bad has happened. Panicking instead of returning - // `Result` keeps this hook's API consistent with the others. - panic!("unexpectedly failed to set_authorizer: {}", unsafe { - crate::error::error_from_handle(self.db(), err_code) - }); - } - } - } -} - -unsafe fn expect_utf8<'a>(p_str: *const c_char, description: &'static str) -> &'a str { - expect_optional_utf8(p_str, description) - .unwrap_or_else(|| panic!("received empty {description}")) -} - -unsafe fn expect_optional_utf8<'a>( - p_str: *const c_char, - description: &'static str, -) -> Option<&'a str> { - if p_str.is_null() { - return None; - } - CStr::from_ptr(p_str) - .to_str() - .unwrap_or_else(|_| panic!("received non-utf8 string as {description}")) - .into() -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::Action; - use crate::{Connection, Result, MAIN_DB}; - use std::sync::atomic::{AtomicBool, Ordering}; - - #[test] - fn test_commit_hook() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - db.commit_hook(Some(|| { - CALLED.store(true, Ordering::Relaxed); - false - }))?; - db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_fn_commit_hook() -> Result<()> { - let db = Connection::open_in_memory()?; - - fn hook() -> bool { - true - } - - db.commit_hook(Some(hook))?; - db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;") - .unwrap_err(); - Ok(()) - } - - #[test] - fn test_rollback_hook() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - db.rollback_hook(Some(|| { - CALLED.store(true, Ordering::Relaxed); - }))?; - db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); ROLLBACK;")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_update_hook() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - db.update_hook(Some(|action, db: &str, tbl: &str, row_id| { - assert_eq!(Action::SQLITE_INSERT, action); - assert_eq!("main", db); - assert_eq!("foo", tbl); - assert_eq!(1, row_id); - CALLED.store(true, Ordering::Relaxed); - }))?; - db.execute_batch("CREATE TABLE foo (t TEXT)")?; - db.execute_batch("INSERT INTO foo VALUES ('lisa')")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_progress_handler() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - db.progress_handler( - 1, - Some(|| { - CALLED.store(true, Ordering::Relaxed); - false - }), - )?; - db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_progress_handler_interrupt() -> Result<()> { - let db = Connection::open_in_memory()?; - - fn handler() -> bool { - true - } - - db.progress_handler(1, Some(handler))?; - db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;") - .unwrap_err(); - Ok(()) - } - - #[test] - fn test_authorizer() -> Result<()> { - use super::{AuthAction, AuthContext, Authorization}; - - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo (public TEXT, private TEXT)")?; - - let authorizer = move |ctx: AuthContext<'_>| match ctx.action { - AuthAction::Read { - column_name: "private", - .. - } => Authorization::Ignore, - AuthAction::DropTable { .. } => Authorization::Deny, - AuthAction::Pragma { .. } => panic!("shouldn't be called"), - _ => Authorization::Allow, - }; - - db.authorizer(Some(authorizer))?; - db.execute_batch( - "BEGIN TRANSACTION; INSERT INTO foo VALUES ('pub txt', 'priv txt'); COMMIT;", - )?; - db.query_row_and_then("SELECT * FROM foo", [], |row| -> Result<()> { - assert_eq!(row.get::<_, String>("public")?, "pub txt"); - assert!(row.get::<_, Option>("private")?.is_none()); - Ok(()) - })?; - db.execute_batch("DROP TABLE foo").unwrap_err(); - - db.authorizer(None::) -> Authorization>)?; - db.execute_batch("PRAGMA user_version=1")?; // Disallowed by first authorizer, but it's now removed. - - Ok(()) - } - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn wal_hook() -> Result<()> { - let temp_dir = tempfile::tempdir().unwrap(); - let path = temp_dir.path().join("wal-hook.db3"); - - let db = Connection::open(&path)?; - let journal_mode: String = - db.pragma_update_and_check(None, "journal_mode", "wal", |row| row.get(0))?; - assert_eq!(journal_mode, "wal"); - - static CALLED: AtomicBool = AtomicBool::new(false); - db.wal_hook(Some(|wal, pages| { - assert_eq!(wal.name(), MAIN_DB); - assert!(pages > 0); - CALLED.swap(true, Ordering::Relaxed); - wal.checkpoint() - })); - db.execute_batch("CREATE TABLE x(c);")?; - assert!(CALLED.load(Ordering::Relaxed)); - - db.wal_hook(Some(|wal, pages| { - assert!(pages > 0); - let (log, ckpt) = wal.checkpoint_v2(super::CheckpointMode::TRUNCATE)?; - assert_eq!(log, 0); - assert_eq!(ckpt, 0); - Ok(()) - })); - db.execute_batch("CREATE TABLE y(c);")?; - - db.wal_hook(None); - Ok(()) - } - - #[test] - fn test_non_owning_hooks_cleanup() -> Result<()> { - let conn = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - CALLED.store(false, Ordering::Relaxed); - conn.commit_hook(Some(|| { - CALLED.store(true, Ordering::Relaxed); - false - }))?; - - let non_owning_conn = unsafe { Connection::from_handle(conn.handle()) }?; - drop(non_owning_conn); - - conn.execute_batch("CREATE TABLE test(value)")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/hooks/preupdate_hook.rs b/vendor/rusqlite/src/hooks/preupdate_hook.rs deleted file mode 100644 index d5f946b..0000000 --- a/vendor/rusqlite/src/hooks/preupdate_hook.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::ffi::{c_char, c_int, c_void}; -use std::fmt::Debug; -use std::panic::catch_unwind; -use std::ptr; - -use super::expect_utf8; -use super::Action; -use crate::error::check; -use crate::ffi; -use crate::inner_connection::InnerConnection; -use crate::types::ValueRef; -use crate::Connection; -use crate::Result; - -/// The possible cases for when a PreUpdateHook gets triggered. Allows access to the relevant -/// functions for each case through the contained values. -#[derive(Debug)] -pub enum PreUpdateCase { - /// Pre-update hook was triggered by an insert. - Insert(PreUpdateNewValueAccessor), - /// Pre-update hook was triggered by a delete. - Delete(PreUpdateOldValueAccessor), - /// Pre-update hook was triggered by an update. - Update { - #[allow(missing_docs)] - old_value_accessor: PreUpdateOldValueAccessor, - #[allow(missing_docs)] - new_value_accessor: PreUpdateNewValueAccessor, - }, - /// This variant is not normally produced by SQLite. You may encounter it - /// if you're using a different version than what's supported by this library. - Unknown, -} - -impl From for Action { - fn from(puc: PreUpdateCase) -> Action { - match puc { - PreUpdateCase::Insert(_) => Action::SQLITE_INSERT, - PreUpdateCase::Delete(_) => Action::SQLITE_DELETE, - PreUpdateCase::Update { .. } => Action::SQLITE_UPDATE, - PreUpdateCase::Unknown => Action::UNKNOWN, - } - } -} - -/// An accessor to access the old values of the row being deleted/updated during the preupdate callback. -#[derive(Debug)] -pub struct PreUpdateOldValueAccessor { - db: *mut ffi::sqlite3, - old_row_id: i64, -} - -impl PreUpdateOldValueAccessor { - /// Get the amount of columns in the row being deleted/updated. - pub fn get_column_count(&self) -> i32 { - unsafe { ffi::sqlite3_preupdate_count(self.db) } - } - - /// Get the depth of the query that triggered the preupdate hook. - /// Returns 0 if the preupdate callback was invoked as a result of - /// a direct insert, update, or delete operation; - /// 1 for inserts, updates, or deletes invoked by top-level triggers; - /// 2 for changes resulting from triggers called by top-level triggers; and so forth. - pub fn get_query_depth(&self) -> i32 { - unsafe { ffi::sqlite3_preupdate_depth(self.db) } - } - - /// Get the row id of the row being updated/deleted. - pub fn get_old_row_id(&self) -> i64 { - self.old_row_id - } - - /// Get the value of the row being updated/deleted at the specified index. - pub fn get_old_column_value(&self, i: i32) -> Result> { - let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut(); - unsafe { - check(ffi::sqlite3_preupdate_old(self.db, i, &mut p_value))?; - Ok(ValueRef::from_value(p_value)) - } - } -} - -/// An accessor to access the new values of the row being inserted/updated -/// during the preupdate callback. -#[derive(Debug)] -pub struct PreUpdateNewValueAccessor { - db: *mut ffi::sqlite3, - new_row_id: i64, -} - -impl PreUpdateNewValueAccessor { - /// Get the amount of columns in the row being inserted/updated. - pub fn get_column_count(&self) -> i32 { - unsafe { ffi::sqlite3_preupdate_count(self.db) } - } - - /// Get the depth of the query that triggered the preupdate hook. - /// Returns 0 if the preupdate callback was invoked as a result of - /// a direct insert, update, or delete operation; - /// 1 for inserts, updates, or deletes invoked by top-level triggers; - /// 2 for changes resulting from triggers called by top-level triggers; and so forth. - pub fn get_query_depth(&self) -> i32 { - unsafe { ffi::sqlite3_preupdate_depth(self.db) } - } - - /// Get the row id of the row being inserted/updated. - pub fn get_new_row_id(&self) -> i64 { - self.new_row_id - } - - /// Get the value of the row being updated/deleted at the specified index. - pub fn get_new_column_value(&self, i: i32) -> Result> { - let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut(); - unsafe { - check(ffi::sqlite3_preupdate_new(self.db, i, &mut p_value))?; - Ok(ValueRef::from_value(p_value)) - } - } -} - -impl Connection { - /// Register a callback function to be invoked before - /// a row is updated, inserted or deleted. - /// - /// The callback parameters are: - /// - /// - the name of the database ("main", "temp", ...), - /// - the name of the table that is updated, - /// - a variant of the PreUpdateCase enum which allows access to extra functions depending - /// on whether it's an update, delete or insert. - #[inline] - pub fn preupdate_hook(&self, hook: Option) -> Result<()> - where - F: FnMut(Action, &str, &str, &PreUpdateCase) + Send + 'static, - { - self.db.borrow().check_owned()?; - self.db.borrow_mut().preupdate_hook(hook); - Ok(()) - } -} - -impl InnerConnection { - #[inline] - pub fn remove_preupdate_hook(&mut self) { - self.preupdate_hook(None::); - } - - /// ```compile_fail - /// use rusqlite::{Connection, Result, hooks::PreUpdateCase}; - /// fn main() -> Result<()> { - /// let db = Connection::open_in_memory()?; - /// { - /// let mut called = std::sync::atomic::AtomicBool::new(false); - /// db.preupdate_hook(Some(|action, db: &str, tbl: &str, case: &PreUpdateCase| { - /// called.store(true, std::sync::atomic::Ordering::Relaxed); - /// })); - /// } - /// db.execute_batch("CREATE TABLE foo AS SELECT 1 AS bar;") - /// } - /// ``` - fn preupdate_hook(&mut self, hook: Option) - where - F: FnMut(Action, &str, &str, &PreUpdateCase) + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure( - p_arg: *mut c_void, - sqlite: *mut ffi::sqlite3, - action_code: c_int, - db_name: *const c_char, - tbl_name: *const c_char, - old_row_id: i64, - new_row_id: i64, - ) where - F: FnMut(Action, &str, &str, &PreUpdateCase), - { - let action = Action::from(action_code); - - let preupdate_case = match action { - Action::SQLITE_INSERT => PreUpdateCase::Insert(PreUpdateNewValueAccessor { - db: sqlite, - new_row_id, - }), - Action::SQLITE_DELETE => PreUpdateCase::Delete(PreUpdateOldValueAccessor { - db: sqlite, - old_row_id, - }), - Action::SQLITE_UPDATE => PreUpdateCase::Update { - old_value_accessor: PreUpdateOldValueAccessor { - db: sqlite, - old_row_id, - }, - new_value_accessor: PreUpdateNewValueAccessor { - db: sqlite, - new_row_id, - }, - }, - Action::UNKNOWN => PreUpdateCase::Unknown, - }; - - drop(catch_unwind(|| { - let boxed_hook: *mut F = p_arg.cast::(); - (*boxed_hook)( - action, - expect_utf8(db_name, "database name"), - expect_utf8(tbl_name, "table name"), - &preupdate_case, - ); - })); - } - - let boxed_hook = hook.map(Box::new); - unsafe { - ffi::sqlite3_preupdate_hook( - self.db(), - boxed_hook.as_ref().map(|_| call_boxed_closure:: as _), - boxed_hook - .as_ref() - .map_or_else(ptr::null_mut, |h| &**h as *const F as *mut _), - ) - }; - self.preupdate_hook = boxed_hook.map(|bh| bh as _); - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use std::sync::atomic::{AtomicBool, Ordering}; - - use super::super::Action; - use super::PreUpdateCase; - use crate::{Connection, Result}; - - #[test] - fn test_preupdate_hook_insert() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - - db.preupdate_hook(Some(|action, db: &str, tbl: &str, case: &PreUpdateCase| { - assert_eq!(Action::SQLITE_INSERT, action); - assert_eq!("main", db); - assert_eq!("foo", tbl); - match case { - PreUpdateCase::Insert(accessor) => { - assert_eq!(1, accessor.get_column_count()); - assert_eq!(1, accessor.get_new_row_id()); - assert_eq!(0, accessor.get_query_depth()); - // out of bounds access should return an error - assert!(accessor.get_new_column_value(1).is_err()); - assert_eq!( - "lisa", - accessor.get_new_column_value(0).unwrap().as_str().unwrap() - ); - assert_eq!(0, accessor.get_query_depth()); - } - _ => panic!("wrong preupdate case"), - } - CALLED.store(true, Ordering::Relaxed); - }))?; - db.execute_batch("CREATE TABLE foo (t TEXT)")?; - db.execute_batch("INSERT INTO foo VALUES ('lisa')")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_preupdate_hook_delete() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - - db.execute_batch("CREATE TABLE foo (t TEXT)")?; - db.execute_batch("INSERT INTO foo VALUES ('lisa')")?; - - db.preupdate_hook(Some(|action, db: &str, tbl: &str, case: &PreUpdateCase| { - assert_eq!(Action::SQLITE_DELETE, action); - assert_eq!("main", db); - assert_eq!("foo", tbl); - match case { - PreUpdateCase::Delete(accessor) => { - assert_eq!(1, accessor.get_column_count()); - assert_eq!(1, accessor.get_old_row_id()); - assert_eq!(0, accessor.get_query_depth()); - // out of bounds access should return an error - assert!(accessor.get_old_column_value(1).is_err()); - assert_eq!( - "lisa", - accessor.get_old_column_value(0).unwrap().as_str().unwrap() - ); - assert_eq!(0, accessor.get_query_depth()); - } - _ => panic!("wrong preupdate case"), - } - CALLED.store(true, Ordering::Relaxed); - }))?; - - db.execute_batch("DELETE from foo")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_preupdate_hook_update() -> Result<()> { - let db = Connection::open_in_memory()?; - - static CALLED: AtomicBool = AtomicBool::new(false); - - db.execute_batch("CREATE TABLE foo (t TEXT)")?; - db.execute_batch("INSERT INTO foo VALUES ('lisa')")?; - - db.preupdate_hook(Some(|action, db: &str, tbl: &str, case: &PreUpdateCase| { - assert_eq!(Action::SQLITE_UPDATE, action); - assert_eq!("main", db); - assert_eq!("foo", tbl); - match case { - PreUpdateCase::Update { - old_value_accessor, - new_value_accessor, - } => { - assert_eq!(1, old_value_accessor.get_column_count()); - assert_eq!(1, old_value_accessor.get_old_row_id()); - assert_eq!(0, old_value_accessor.get_query_depth()); - // out of bounds access should return an error - assert!(old_value_accessor.get_old_column_value(1).is_err()); - assert_eq!( - "lisa", - old_value_accessor - .get_old_column_value(0) - .unwrap() - .as_str() - .unwrap() - ); - assert_eq!(0, old_value_accessor.get_query_depth()); - - assert_eq!(1, new_value_accessor.get_column_count()); - assert_eq!(1, new_value_accessor.get_new_row_id()); - assert_eq!(0, new_value_accessor.get_query_depth()); - // out of bounds access should return an error - assert!(new_value_accessor.get_new_column_value(1).is_err()); - assert_eq!( - "janice", - new_value_accessor - .get_new_column_value(0) - .unwrap() - .as_str() - .unwrap() - ); - assert_eq!(0, new_value_accessor.get_query_depth()); - } - _ => panic!("wrong preupdate case"), - } - CALLED.store(true, Ordering::Relaxed); - }))?; - - db.execute_batch("UPDATE foo SET t = 'janice'")?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/inner_connection.rs b/vendor/rusqlite/src/inner_connection.rs deleted file mode 100644 index 0751cfc..0000000 --- a/vendor/rusqlite/src/inner_connection.rs +++ /dev/null @@ -1,446 +0,0 @@ -use std::ffi::{c_char, c_int, CStr}; -#[cfg(feature = "load_extension")] -use std::path::Path; -use std::ptr; -use std::str; -use std::sync::{Arc, Mutex}; - -use super::ffi; -use super::{Connection, InterruptHandle, Name, OpenFlags, PrepFlags, Result}; -use crate::error::{decode_result_raw, error_from_handle, error_with_offset, Error}; -use crate::raw_statement::RawStatement; -use crate::statement::Statement; -use crate::version_number; - -pub struct InnerConnection { - pub db: *mut ffi::sqlite3, - // It's unsafe to call `sqlite3_close` while another thread is performing - // a `sqlite3_interrupt`, and vice versa, so we take this mutex during - // those functions. This protects a copy of the `db` pointer (which is - // cleared on closing), however the main copy, `db`, is unprotected. - // Otherwise, a long-running query would prevent calling interrupt, as - // interrupt would only acquire the lock after the query's completion. - interrupt_lock: Arc>, - #[cfg(feature = "hooks")] - pub commit_hook: Option bool + Send>>, - #[cfg(feature = "hooks")] - pub rollback_hook: Option>, - #[cfg(feature = "hooks")] - #[expect(clippy::type_complexity)] - pub update_hook: Option>, - #[cfg(feature = "hooks")] - pub progress_handler: Option bool + Send>>, - #[cfg(feature = "hooks")] - pub authorizer: Option, - #[cfg(feature = "preupdate_hook")] - #[expect(clippy::type_complexity)] - pub preupdate_hook: Option< - Box, - >, - owned: bool, -} - -unsafe impl Send for InnerConnection {} - -impl InnerConnection { - #[expect(clippy::arc_with_non_send_sync)] // See unsafe impl Send / Sync for InterruptHandle - #[inline] - pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> Self { - Self { - db, - interrupt_lock: Arc::new(Mutex::new(if owned { db } else { ptr::null_mut() })), - #[cfg(feature = "hooks")] - commit_hook: None, - #[cfg(feature = "hooks")] - rollback_hook: None, - #[cfg(feature = "hooks")] - update_hook: None, - #[cfg(feature = "hooks")] - progress_handler: None, - #[cfg(feature = "hooks")] - authorizer: None, - #[cfg(feature = "preupdate_hook")] - preupdate_hook: None, - owned, - } - } - - pub fn open_with_flags( - c_path: &CStr, - mut flags: OpenFlags, - vfs: Option<&CStr>, - ) -> Result { - ensure_safe_sqlite_threading_mode()?; - - let z_vfs = match vfs { - Some(c_vfs) => c_vfs.as_ptr(), - None => ptr::null(), - }; - - // turn on extended results code before opening database to have a better diagnostic if a failure happens - let exrescode = if version_number() >= 3_037_000 { - flags |= OpenFlags::SQLITE_OPEN_EXRESCODE; - true - } else { - false // flag SQLITE_OPEN_EXRESCODE is ignored by SQLite version < 3.37.0 - }; - - unsafe { - let mut db: *mut ffi::sqlite3 = ptr::null_mut(); - let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), z_vfs); - if r != ffi::SQLITE_OK { - let e = if db.is_null() { - err!(r, "{}", c_path.to_string_lossy()) - } else { - let mut e = error_from_handle(db, r); - if let Error::SqliteFailure( - ffi::Error { - code: ffi::ErrorCode::CannotOpen, - .. - }, - Some(msg), - ) = e - { - e = err!(r, "{msg}: {}", c_path.to_string_lossy()); - } - ffi::sqlite3_close(db); - e - }; - - return Err(e); - } - - // attempt to turn on extended results code; don't fail if we can't. - if !exrescode { - 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); - ffi::sqlite3_close(db); - return Err(e); - } - - Ok(Self::new(db, true)) - } - } - - #[inline] - pub fn db(&self) -> *mut ffi::sqlite3 { - self.db - } - - #[inline] - pub fn decode_result(&self, code: c_int) -> Result<()> { - unsafe { decode_result_raw(self.db(), code) } - } - - pub fn close(&mut self) -> Result<()> { - if self.db.is_null() { - return Ok(()); - } - if self.owned { - self.remove_hooks(); - self.remove_preupdate_hook(); - } - let mut shared_handle = self.interrupt_lock.lock().unwrap(); - assert!( - !self.owned || !shared_handle.is_null(), - "Bug: Somehow interrupt_lock was cleared before the DB was closed" - ); - if !self.owned { - self.db = ptr::null_mut(); - return Ok(()); - } - unsafe { - let r = ffi::sqlite3_close(self.db); - // Need to use _raw because _guard has a reference out, and - // decode_result takes &mut self. - let r = decode_result_raw(self.db, r); - if r.is_ok() { - *shared_handle = ptr::null_mut(); - self.db = ptr::null_mut(); - } - r - } - } - - #[inline] - pub fn get_interrupt_handle(&self) -> InterruptHandle { - InterruptHandle { - db_lock: Arc::clone(&self.interrupt_lock), - } - } - - #[inline] - #[cfg(feature = "load_extension")] - pub unsafe fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { - let r = ffi::sqlite3_enable_load_extension(self.db, onoff); - self.decode_result(r) - } - - #[cfg(feature = "load_extension")] - pub unsafe fn load_extension( - &self, - dylib_path: &Path, - entry_point: Option, - ) -> Result<()> { - let dylib_str = super::path_to_cstring(dylib_path)?; - let mut errmsg: *mut c_char = ptr::null_mut(); - let cs = entry_point.as_ref().map(N::as_cstr).transpose()?; - let c_entry = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - let r = ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), c_entry, &mut errmsg); - if r == ffi::SQLITE_OK { - Ok(()) - } else { - let message = super::errmsg_to_string(errmsg); - ffi::sqlite3_free(errmsg.cast::()); - Err(crate::error::error_from_sqlite_code(r, Some(message))) - } - } - - #[inline] - pub fn last_insert_rowid(&self) -> i64 { - unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } - } - - pub fn prepare<'a>( - &mut self, - conn: &'a Connection, - sql: &str, - flags: PrepFlags, - ) -> Result<(Statement<'a>, usize)> { - let mut c_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut(); - let Ok(len) = c_int::try_from(sql.len()) else { - return Err(err!(ffi::SQLITE_TOOBIG)); - }; - let c_sql = sql.as_bytes().as_ptr().cast::(); - let mut c_tail: *const c_char = ptr::null(); - #[cfg(not(feature = "unlock_notify"))] - let r = unsafe { - ffi::sqlite3_prepare_v3( - self.db(), - c_sql, - len, - flags.bits(), - &mut c_stmt, - &mut c_tail, - ) - }; - #[cfg(feature = "unlock_notify")] - let r = unsafe { - use crate::unlock_notify; - let mut rc; - loop { - rc = ffi::sqlite3_prepare_v3( - self.db(), - c_sql, - len, - flags.bits(), - &mut c_stmt, - &mut c_tail, - ); - if !unlock_notify::is_locked(self.db, rc) { - break; - } - rc = unlock_notify::wait_for_unlock_notify(self.db); - if rc != ffi::SQLITE_OK { - break; - } - } - rc - }; - // If there is an error, *ppStmt is set to NULL. - if r != ffi::SQLITE_OK { - return Err(unsafe { error_with_offset(self.db, r, sql) }); - } - // If the input text contains no SQL (if the input is an empty string or a - // comment) then *ppStmt is set to NULL. - let tail = if c_tail.is_null() { - 0 - } else { - 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, - )) - } - - #[inline] - pub fn changes(&self) -> u64 { - #[cfg(not(feature = "modern_sqlite"))] - unsafe { - ffi::sqlite3_changes(self.db()) as u64 - } - #[cfg(feature = "modern_sqlite")] // 3.37.0 - unsafe { - ffi::sqlite3_changes64(self.db()) as u64 - } - } - - #[inline] - pub fn total_changes(&self) -> u64 { - #[cfg(not(feature = "modern_sqlite"))] - unsafe { - ffi::sqlite3_total_changes(self.db()) as u64 - } - #[cfg(feature = "modern_sqlite")] // 3.37.0 - unsafe { - ffi::sqlite3_total_changes64(self.db()) as u64 - } - } - - #[inline] - pub fn is_autocommit(&self) -> bool { - unsafe { get_autocommit(self.db()) } - } - - pub fn is_busy(&self) -> bool { - let db = self.db(); - unsafe { - let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut()); - while !stmt.is_null() { - if ffi::sqlite3_stmt_busy(stmt) != 0 { - return true; - } - stmt = ffi::sqlite3_next_stmt(db, stmt); - } - } - false - } - - pub fn cache_flush(&mut self) -> Result<()> { - crate::error::check(unsafe { ffi::sqlite3_db_cacheflush(self.db()) }) - } - - #[cfg(not(feature = "hooks"))] - #[inline] - fn remove_hooks(&mut self) {} - - #[cfg(not(feature = "preupdate_hook"))] - #[inline] - fn remove_preupdate_hook(&mut self) {} - - pub fn db_readonly(&self, db_name: N) -> Result { - let name = db_name.as_cstr()?; - let r = unsafe { ffi::sqlite3_db_readonly(self.db, name.as_ptr()) }; - match r { - 0 => Ok(false), - 1 => Ok(true), - -1 => Err(err!( - ffi::SQLITE_MISUSE, - "{db_name:?} is not the name of a database" - )), - _ => Err(err!(r, "Unexpected result")), - } - } - - #[cfg(feature = "modern_sqlite")] // 3.37.0 - pub fn txn_state( - &self, - db_name: Option, - ) -> Result { - let cs = db_name.as_ref().map(N::as_cstr).transpose()?; - let name = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - let r = unsafe { ffi::sqlite3_txn_state(self.db, name) }; - match r { - 0 => Ok(super::transaction::TransactionState::None), - 1 => Ok(super::transaction::TransactionState::Read), - 2 => Ok(super::transaction::TransactionState::Write), - -1 => Err(err!( - ffi::SQLITE_MISUSE, - "{db_name:?} is not the name of a valid schema" - )), - _ => Err(err!(r, "Unexpected result")), - } - } - - #[inline] - pub fn release_memory(&self) -> Result<()> { - self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) }) - } - - #[cfg(feature = "modern_sqlite")] // 3.41.0 - pub fn is_interrupted(&self) -> bool { - unsafe { ffi::sqlite3_is_interrupted(self.db) == 1 } - } - - #[cfg(any(feature = "hooks", feature = "preupdate_hook"))] - pub fn check_owned(&self) -> Result<()> { - if !self.owned { - return Err(err!(ffi::SQLITE_MISUSE, "Connection is not owned")); - } - Ok(()) - } -} - -#[inline] -pub(crate) unsafe fn get_autocommit(ptr: *mut ffi::sqlite3) -> bool { - ffi::sqlite3_get_autocommit(ptr) != 0 -} - -#[inline] -pub(crate) unsafe fn db_filename( - _: std::marker::PhantomData<&()>, - ptr: *mut ffi::sqlite3, - db_name: N, -) -> Option<&str> { - let db_name = db_name.as_cstr().unwrap(); - let db_filename = ffi::sqlite3_db_filename(ptr, db_name.as_ptr()); - if db_filename.is_null() { - None - } else { - CStr::from_ptr(db_filename).to_str().ok() - } -} - -impl Drop for InnerConnection { - #[expect(unused_must_use)] - #[inline] - fn drop(&mut self) { - self.close(); - } -} - -// threading mode checks are not necessary (and do not work) on target -// platforms that do not have threading (such as webassembly) -#[cfg(target_arch = "wasm32")] -fn ensure_safe_sqlite_threading_mode() -> Result<()> { - Ok(()) -} - -#[cfg(not(any(target_arch = "wasm32")))] -fn ensure_safe_sqlite_threading_mode() -> Result<()> { - // Ensure SQLite was compiled in threadsafe mode. - if unsafe { ffi::sqlite3_threadsafe() == 0 } { - return Err(Error::SqliteSingleThreadedMode); - } - - // Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, - // but it's possible someone configured it to be in Single-thread mode - // before calling into us. That would mean we're exposing an unsafe API via - // a safe one (in Rust terminology). - // - // We can ask SQLite for a mutex and check for - // the magic value 8. This isn't documented, but it's what SQLite - // returns for its mutex allocation function in Single-thread mode. - const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; - let is_singlethreaded = unsafe { - let mutex_ptr = ffi::sqlite3_mutex_alloc(0); - let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; - ffi::sqlite3_mutex_free(mutex_ptr); - is_singlethreaded - }; - if is_singlethreaded { - Err(Error::SqliteSingleThreadedMode) - } else { - Ok(()) - } -} diff --git a/vendor/rusqlite/src/lib.rs b/vendor/rusqlite/src/lib.rs deleted file mode 100644 index ef599d3..0000000 --- a/vendor/rusqlite/src/lib.rs +++ /dev/null @@ -1,2330 +0,0 @@ -//! Rusqlite is an ergonomic wrapper for using SQLite from Rust. -//! -//! Historically, the API was based on the one from -//! [`rust-postgres`](https://github.com/sfackler/rust-postgres). However, the -//! two have diverged in many ways, and no compatibility between the two is -//! intended. -//! -//! ```rust -//! use rusqlite::{params, Connection, Result}; -//! -//! #[derive(Debug)] -//! struct Person { -//! id: i32, -//! name: String, -//! data: Option>, -//! } -//! -//! fn main() -> Result<()> { -//! let conn = Connection::open_in_memory()?; -//! -//! conn.execute( -//! "CREATE TABLE person ( -//! id INTEGER PRIMARY KEY, -//! name TEXT NOT NULL, -//! data BLOB -//! )", -//! (), // empty list of parameters. -//! )?; -//! let me = Person { -//! id: 0, -//! name: "Steven".to_string(), -//! data: None, -//! }; -//! conn.execute( -//! "INSERT INTO person (name, data) VALUES (?1, ?2)", -//! (&me.name, &me.data), -//! )?; -//! -//! let mut stmt = conn.prepare("SELECT id, name, data FROM person")?; -//! let person_iter = stmt.query_map([], |row| { -//! Ok(Person { -//! id: row.get(0)?, -//! name: row.get(1)?, -//! data: row.get(2)?, -//! }) -//! })?; -//! -//! for person in person_iter { -//! println!("Found person {:?}", person?); -//! } -//! Ok(()) -//! } -//! ``` -#![warn(missing_docs)] -#![cfg_attr(docsrs, feature(doc_cfg))] - -pub use fallible_iterator; -pub use fallible_streaming_iterator; - -#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] -pub use libsqlite3_sys as ffi; -#[cfg(all(target_family = "wasm", target_os = "unknown"))] -pub use sqlite_wasm_rs as ffi; - -use std::cell::RefCell; -use std::default::Default; -use std::ffi::{c_char, c_int, c_uint, CStr, CString}; -use std::fmt; - -use std::path::Path; -use std::result; -use std::str; -use std::sync::{Arc, Mutex}; - -#[cfg(feature = "cache")] -use crate::cache::StatementCache; -use crate::inner_connection::InnerConnection; -use crate::raw_statement::RawStatement; -use crate::types::ValueRef; - -pub use crate::bind::BindIndex; -#[cfg(feature = "cache")] -pub use crate::cache::CachedStatement; -#[cfg(feature = "column_decltype")] -pub use crate::column::Column; -#[cfg(feature = "column_metadata")] -pub use crate::column::ColumnMetadata; -pub use crate::error::{to_sqlite_error, Error}; -pub use crate::ffi::ErrorCode; -#[cfg(feature = "load_extension")] -pub use crate::load_extension_guard::LoadExtensionGuard; -pub use crate::params::{params_from_iter, Params, ParamsFromIter}; -pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows}; -pub use crate::statement::{Statement, StatementStatus}; -#[cfg(feature = "modern_sqlite")] -pub use crate::transaction::TransactionState; -pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; -pub use crate::types::ToSql; -pub use crate::util::Name; -pub use crate::version::*; -#[cfg(feature = "rusqlite-macros")] -#[doc(hidden)] -pub use rusqlite_macros::__bind; - -#[macro_use] -mod error; - -#[cfg(not(feature = "loadable_extension"))] -pub mod auto_extension; -#[cfg(feature = "backup")] -pub mod backup; -mod bind; -#[cfg(feature = "blob")] -pub mod blob; -mod busy; -#[cfg(feature = "cache")] -mod cache; -#[cfg(feature = "collation")] -mod collation; -mod column; -pub mod config; -#[cfg(any(feature = "functions", feature = "vtab"))] -mod context; -#[cfg(feature = "functions")] -pub mod functions; -#[cfg(feature = "hooks")] -pub mod hooks; -mod inner_connection; -#[cfg(feature = "limits")] -pub mod limits; -#[cfg(feature = "load_extension")] -mod load_extension_guard; -mod params; -mod pragma; -mod raw_statement; -mod row; -#[cfg(feature = "serialize")] -pub mod serialize; -#[cfg(feature = "session")] -pub mod session; -mod statement; -#[cfg(feature = "trace")] -pub mod trace; -mod transaction; -pub mod types; -#[cfg(feature = "unlock_notify")] -mod unlock_notify; -mod version; -#[cfg(feature = "vtab")] -pub mod vtab; - -pub(crate) mod util; - -// Actually, only sqlite3_enable_load_extension is disabled (not sqlite3_load_extension) -#[cfg(all(feature = "loadable_extension", feature = "load_extension"))] -compile_error!("feature \"loadable_extension\" and feature \"load_extension\" cannot be enabled at the same time"); - -// Number of cached prepared statements we'll hold on to. -#[cfg(feature = "cache")] -const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16; - -/// A macro making it more convenient to pass longer lists of -/// parameters as a `&[&dyn ToSql]`. -/// -/// # Example -/// -/// ```rust,no_run -/// # use rusqlite::{Result, Connection, params}; -/// -/// struct Person { -/// name: String, -/// age_in_years: u8, -/// data: Option>, -/// } -/// -/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { -/// conn.execute( -/// "INSERT INTO person(name, age_in_years, data) VALUES (?1, ?2, ?3)", -/// params![person.name, person.age_in_years, person.data], -/// )?; -/// Ok(()) -/// } -/// ``` -#[macro_export] -macro_rules! params { - () => { - &[] as &[&dyn $crate::ToSql] - }; - ($($param:expr),+ $(,)?) => { - &[$(&$param as &dyn $crate::ToSql),+] as &[&dyn $crate::ToSql] - }; -} - -/// A macro making it more convenient to pass lists of named parameters -/// as a `&[(&str, &dyn ToSql)]`. -/// -/// # Example -/// -/// ```rust,no_run -/// # use rusqlite::{Result, Connection, named_params}; -/// -/// struct Person { -/// name: String, -/// age_in_years: u8, -/// data: Option>, -/// } -/// -/// fn add_person(conn: &Connection, person: &Person) -> Result<()> { -/// conn.execute( -/// "INSERT INTO person (name, age_in_years, data) -/// VALUES (:name, :age, :data)", -/// named_params! { -/// ":name": person.name, -/// ":age": person.age_in_years, -/// ":data": person.data, -/// }, -/// )?; -/// Ok(()) -/// } -/// ``` -#[macro_export] -macro_rules! named_params { - () => { - &[] as &[(&str, &dyn $crate::ToSql)] - }; - // Note: It's a lot more work to support this as part of the same macro as - // `params!`, unfortunately. - ($($param_name:literal: $param_val:expr),+ $(,)?) => { - &[$(($param_name, &$param_val as &dyn $crate::ToSql)),+] as &[(&str, &dyn $crate::ToSql)] - }; -} - -/// Captured identifiers in SQL -/// -/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not -/// work). -/// * `$x.y` expression does not work. -/// -/// # Example -/// -/// ```rust, no_run -/// # use rusqlite::{prepare_and_bind, Connection, Result, Statement}; -/// -/// fn misc(db: &Connection) -> Result { -/// let name = "Lisa"; -/// let age = 8; -/// let smart = true; -/// Ok(prepare_and_bind!(db, "SELECT $name, @age, :smart;")) -/// } -/// ``` -#[cfg(feature = "rusqlite-macros")] -#[macro_export] -macro_rules! prepare_and_bind { - ($conn:expr, $sql:literal) => {{ - let mut stmt = $conn.prepare($sql)?; - $crate::__bind!(stmt $sql); - stmt - }}; -} - -/// Captured identifiers in SQL -/// -/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not -/// work). -/// * `$x.y` expression does not work. -#[cfg(feature = "rusqlite-macros")] -#[macro_export] -macro_rules! prepare_cached_and_bind { - ($conn:expr, $sql:literal) => {{ - let mut stmt = $conn.prepare_cached($sql)?; - $crate::__bind!(stmt $sql); - stmt - }}; -} - -/// A typedef of the result returned by many methods. -pub type Result = result::Result; - -/// See the [method documentation](#tymethod.optional). -pub trait OptionalExtension { - /// Converts a `Result` into a `Result>`. - /// - /// By default, Rusqlite treats 0 rows being returned from a query that is - /// expected to return 1 row as an error. This method will - /// handle that error, and give you back an `Option` instead. - fn optional(self) -> Result>; -} - -impl OptionalExtension for Result { - fn optional(self) -> Result> { - match self { - Ok(value) => Ok(Some(value)), - Err(Error::QueryReturnedNoRows) => Ok(None), - Err(e) => Err(e), - } - } -} - -unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { - CStr::from_ptr(errmsg).to_string_lossy().into_owned() -} - -#[cfg(any(feature = "functions", feature = "vtab", test))] -fn str_to_cstring(s: &str) -> Result { - Ok(util::SmallCString::new(s)?) -} - -/// Returns `(string ptr, len as c_int, SQLITE_STATIC | SQLITE_TRANSIENT)` -/// normally. -/// The `sqlite3_destructor_type` item is always `SQLITE_TRANSIENT` unless -/// the string was empty (in which case it's `SQLITE_STATIC`, and the ptr is -/// static). -fn str_for_sqlite( - s: &[u8], -) -> ( - *const c_char, - ffi::sqlite3_uint64, - ffi::sqlite3_destructor_type, -) { - let len = s.len(); - let (ptr, dtor_info) = if len != 0 { - (s.as_ptr().cast::(), ffi::SQLITE_TRANSIENT()) - } else { - // Return a pointer guaranteed to live forever - ("".as_ptr().cast::(), ffi::SQLITE_STATIC()) - }; - (ptr, len as ffi::sqlite3_uint64, dtor_info) -} - -#[cfg(unix)] -fn path_to_cstring(p: &Path) -> Result { - use std::os::unix::ffi::OsStrExt; - Ok(CString::new(p.as_os_str().as_bytes())?) -} - -#[cfg(not(unix))] -fn path_to_cstring(p: &Path) -> Result { - let s = p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))?; - Ok(CString::new(s)?) -} - -/// Shorthand for `Main` database. -pub const MAIN_DB: &CStr = c"main"; -/// Shorthand for `Temp` database. -pub const TEMP_DB: &CStr = c"temp"; - -/// A connection to a SQLite database. -pub struct Connection { - db: RefCell, - #[cfg(feature = "cache")] - cache: StatementCache, - transaction_behavior: TransactionBehavior, -} - -unsafe impl Send for Connection {} - -impl Drop for Connection { - #[inline] - fn drop(&mut self) { - #[cfg(feature = "cache")] - self.flush_prepared_statement_cache(); - } -} - -impl Connection { - /// Open a new connection to a SQLite database. If a database does not exist - /// at the path, one is created. - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn open_my_db() -> Result<()> { - /// let path = "./my_db.db3"; - /// let db = Connection::open(path)?; - /// // Use the database somehow... - /// println!("{}", db.is_autocommit()); - /// Ok(()) - /// } - /// ``` - /// - /// # Flags - /// - /// `Connection::open(path)` is equivalent to using - /// [`Connection::open_with_flags`] with the default [`OpenFlags`]. That is, - /// it's equivalent to: - /// - /// ```ignore - /// Connection::open_with_flags( - /// path, - /// OpenFlags::SQLITE_OPEN_READ_WRITE - /// | OpenFlags::SQLITE_OPEN_CREATE - /// | OpenFlags::SQLITE_OPEN_URI - /// | OpenFlags::SQLITE_OPEN_NO_MUTEX, - /// ) - /// ``` - /// - /// These flags have the following effects: - /// - /// - Open the database for both reading or writing. - /// - Create the database if one does not exist at the path. - /// - Allow the filename to be interpreted as a URI (see - /// for details). - /// - Disables the use of a per-connection mutex. - /// - /// Rusqlite enforces thread-safety at compile time, so additional - /// locking is not needed and provides no benefit. (See the - /// documentation on [`OpenFlags::SQLITE_OPEN_FULL_MUTEX`] for some - /// additional discussion about this). - /// - /// Most of these are also the default settings for the C API, although - /// technically the default locking behavior is controlled by the flags used - /// when compiling SQLite -- rather than let it vary, we choose `NO_MUTEX` - /// because it's a fairly clearly the best choice for users of this library. - /// - /// # Failure - /// - /// Will return `Err` if `path` cannot be converted to a C-compatible string - /// or if the underlying SQLite open call fails. - /// - /// # WASM support - /// - /// If you plan to use this connection type on the `wasm32-unknown-unknown` target please - /// make sure to read the following notes: - /// - /// - The database is stored in memory by default. - /// - Persistent VFS (Virtual File Systems) is optional, - /// see for details - #[inline] - pub fn open>(path: P) -> Result { - let flags = OpenFlags::default(); - Self::open_with_flags(path, flags) - } - - /// Open a new connection to an in-memory SQLite database. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite open call fails. - #[inline] - pub fn open_in_memory() -> Result { - let flags = OpenFlags::default(); - Self::open_in_memory_with_flags(flags) - } - - /// Open a new connection to a SQLite database. - /// - /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid - /// flag combinations. - /// - /// # Failure - /// - /// Will return `Err` if `path` cannot be converted to a C-compatible - /// string or if the underlying SQLite open call fails. - #[inline] - pub fn open_with_flags>(path: P, flags: OpenFlags) -> Result { - let c_path = path_to_cstring(path.as_ref())?; - InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Self { - db: RefCell::new(db), - #[cfg(feature = "cache")] - cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - transaction_behavior: TransactionBehavior::Deferred, - }) - } - - /// Open a new connection to a SQLite database using the specific flags and - /// vfs name. - /// - /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid - /// flag combinations. - /// - /// # Failure - /// - /// Will return `Err` if either `path` or `vfs` cannot be converted to a - /// C-compatible string or if the underlying SQLite open call fails. - #[inline] - pub fn open_with_flags_and_vfs, V: Name>( - path: P, - flags: OpenFlags, - vfs: V, - ) -> Result { - let c_path = path_to_cstring(path.as_ref())?; - let c_vfs = vfs.as_cstr()?; - InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Self { - db: RefCell::new(db), - #[cfg(feature = "cache")] - cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - transaction_behavior: TransactionBehavior::Deferred, - }) - } - - /// Open a new connection to an in-memory SQLite database. - /// - /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid - /// flag combinations. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite open call fails. - #[inline] - pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result { - Self::open_with_flags(":memory:", flags) - } - - /// Open a new connection to an in-memory SQLite database using the specific - /// flags and vfs name. - /// - /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid - /// flag combinations. - /// - /// # Failure - /// - /// Will return `Err` if `vfs` cannot be converted to a C-compatible - /// string or if the underlying SQLite open call fails. - #[inline] - pub fn open_in_memory_with_flags_and_vfs(flags: OpenFlags, vfs: V) -> Result { - Self::open_with_flags_and_vfs(":memory:", flags, vfs) - } - - /// Convenience method to run multiple SQL statements (that cannot take any - /// parameters). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn create_tables(conn: &Connection) -> Result<()> { - /// conn.execute_batch( - /// "BEGIN; - /// CREATE TABLE foo(x INTEGER); - /// CREATE TABLE bar(y TEXT); - /// COMMIT;", - /// ) - /// } - /// ``` - /// - /// # Failure - /// - /// 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<()> { - let mut sql = sql; - while !sql.is_empty() { - let (stmt, tail) = self - .db - .borrow_mut() - .prepare(self, sql, PrepFlags::default())?; - if !stmt.stmt.is_null() && stmt.step()? { - // Some PRAGMA may return rows - if false { - return Err(Error::ExecuteReturnedResults); - } - } - if tail == 0 || tail >= sql.len() { - break; - } - sql = &sql[tail..]; - } - Ok(()) - } - - /// Convenience method to prepare and execute a single SQL statement. - /// - /// On success, returns the number of rows that were changed or inserted or - /// deleted (via `sqlite3_changes`). - /// - /// ## Example - /// - /// ### With positional params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection}; - /// fn update_rows(conn: &Connection) { - /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?1", [1i32]) { - /// Ok(updated) => println!("{} rows were updated", updated), - /// Err(err) => println!("update failed: {}", err), - /// } - /// } - /// ``` - /// - /// ### With positional params of varying types - /// - /// ```rust,no_run - /// # use rusqlite::{params, Connection}; - /// fn update_rows(conn: &Connection) { - /// match conn.execute( - /// "UPDATE foo SET bar = 'baz' WHERE qux = ?1 AND quux = ?2", - /// params![1i32, 1.5f64], - /// ) { - /// Ok(updated) => println!("{} rows were updated", updated), - /// Err(err) => println!("update failed: {}", err), - /// } - /// } - /// ``` - /// - /// ### With named params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert(conn: &Connection) -> Result { - /// conn.execute( - /// "INSERT INTO test (name) VALUES (:name)", - /// &[(":name", "one")], - /// ) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn execute(&self, sql: &str, params: P) -> Result { - self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) - } - - /// Returns the path to the database file, if one exists and is known. - /// - /// Returns `Some("")` for a temporary or in-memory database. - /// - /// Note that in some cases [PRAGMA - /// database_list](https://sqlite.org/pragma.html#pragma_database_list) is - /// likely to be more robust. - #[inline] - pub fn path(&self) -> Option<&str> { - unsafe { inner_connection::db_filename(std::marker::PhantomData, self.handle(), MAIN_DB) } - } - - /// Attempts to free as much heap memory as possible from the database - /// connection. - /// - /// This calls [`sqlite3_db_release_memory`](https://www.sqlite.org/c3ref/db_release_memory.html). - #[inline] - pub fn release_memory(&self) -> Result<()> { - self.db.borrow_mut().release_memory() - } - - /// Get the SQLite rowid of the most recent successful INSERT. - /// - /// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under - /// the hood. - #[inline] - pub fn last_insert_rowid(&self) -> i64 { - self.db.borrow_mut().last_insert_rowid() - } - - /// Convenience method to execute a query that is expected to return a - /// single row. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Result, Connection}; - /// fn preferred_locale(conn: &Connection) -> Result { - /// conn.query_row( - /// "SELECT value FROM preferences WHERE name='locale'", - /// [], - /// |row| row.get(0), - /// ) - /// } - /// ``` - /// - /// If the query returns more than one row, all rows except the first are - /// ignored. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call `.optional()` on the result of - /// this to get a `Result>`. - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn query_row(&self, sql: &str, params: P, f: F) -> Result - where - P: Params, - F: FnOnce(&Row<'_>) -> Result, - { - let mut stmt = self.prepare(sql)?; - stmt.query_row(params, f) - } - - /// Convenience method to execute a query that is expected to return exactly - /// one row. - /// - /// Returns `Err(QueryReturnedMoreThanOneRow)` if the query returns more than one row. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call - /// [`.optional()`](crate::OptionalExtension::optional) on the result of - /// this to get a `Result>` (requires that the trait - /// `rusqlite::OptionalExtension` is imported). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - pub fn query_one(&self, sql: &str, params: P, f: F) -> Result - where - P: Params, - F: FnOnce(&Row<'_>) -> Result, - { - let mut stmt = self.prepare(sql)?; - stmt.query_one(params, f) - } - - // https://sqlite.org/tclsqlite.html#onecolumn - #[cfg(test)] - pub(crate) fn one_column(&self, sql: &str, params: P) -> Result - where - T: types::FromSql, - P: Params, - { - self.query_one(sql, params, |r| r.get(0)) - } - - /// Convenience method to execute a query that is expected to return a - /// single row, and execute a mapping via `f` on that returned row with - /// the possibility of failure. The `Result` type of `f` must implement - /// `std::convert::From`. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Result, Connection}; - /// fn preferred_locale(conn: &Connection) -> Result { - /// conn.query_row_and_then( - /// "SELECT value FROM preferences WHERE name='locale'", - /// [], - /// |row| row.get(0), - /// ) - /// } - /// ``` - /// - /// If the query returns more than one row, all rows except the first are - /// ignored. - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn query_row_and_then(&self, sql: &str, params: P, f: F) -> Result - where - P: Params, - F: FnOnce(&Row<'_>) -> Result, - E: From, - { - let mut stmt = self.prepare(sql)?; - let mut rows = stmt.query(params)?; - - rows.get_expected_row().map_err(E::from).and_then(f) - } - - /// Prepare a SQL statement for execution. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn insert_new_people(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?1)")?; - /// stmt.execute(["Joe Smith"])?; - /// stmt.execute(["Bob Jones"])?; - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn prepare(&self, sql: &str) -> Result> { - self.prepare_with_flags(sql, PrepFlags::default()) - } - - /// Prepare a SQL statement for execution. - /// - /// # Failure - /// - /// Will return `Err` if `sql` cannot be converted to a C-compatible string - /// or if the underlying SQLite call fails. - #[inline] - pub fn prepare_with_flags(&self, sql: &str, flags: PrepFlags) -> Result> { - let (stmt, tail) = self.db.borrow_mut().prepare(self, sql, flags)?; - if tail != 0 && !self.prepare(&sql[tail..])?.stmt.is_null() { - Err(Error::MultipleStatement) - } else { - Ok(stmt) - } - } - - /// Close the SQLite connection. - /// - /// This is functionally equivalent to the `Drop` implementation for - /// `Connection` except that on failure, it returns an error and the - /// connection itself (presumably so closing can be attempted again). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[expect(clippy::result_large_err)] - #[inline] - pub fn close(self) -> Result<(), (Self, Error)> { - #[cfg(feature = "cache")] - self.flush_prepared_statement_cache(); - let r = self.db.borrow_mut().close(); - r.map_err(move |err| (self, err)) - } - - /// Enable loading of SQLite extensions from both SQL queries and Rust. - /// - /// You must call [`Connection::load_extension_disable`] when you're - /// finished loading extensions (failure to call it can lead to bad things, - /// see "Safety"), so you should strongly consider using - /// [`LoadExtensionGuard`] instead of this function, automatically disables - /// extension loading when it goes out of scope. - /// - /// # Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn load_my_extension(conn: &Connection) -> Result<()> { - /// // Safety: We fully trust the loaded extension and execute no untrusted SQL - /// // while extension loading is enabled. - /// unsafe { - /// conn.load_extension_enable()?; - /// let r = conn.load_extension("my/trusted/extension", None::<&str>); - /// conn.load_extension_disable()?; - /// r - /// } - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - /// - /// # Safety - /// - /// TLDR: Don't execute any untrusted queries between this call and - /// [`Connection::load_extension_disable`]. - /// - /// Perhaps surprisingly, this function does not only allow the use of - /// [`Connection::load_extension`] from Rust, but it also allows SQL queries - /// to perform [the same operation][loadext]. For example, in the period - /// between `load_extension_enable` and `load_extension_disable`, the - /// following operation will load and call some function in some dynamic - /// library: - /// - /// ```sql - /// SELECT load_extension('why_is_this_possible.dll', 'dubious_func'); - /// ``` - /// - /// This means that while this is enabled a carefully crafted SQL query can - /// be used to escalate a SQL injection attack into code execution. - /// - /// Safely using this function requires that you trust all SQL queries run - /// between when it is called, and when loading is disabled (by - /// [`Connection::load_extension_disable`]). - /// - /// [loadext]: https://www.sqlite.org/lang_corefunc.html#load_extension - #[cfg(feature = "load_extension")] - #[inline] - pub unsafe fn load_extension_enable(&self) -> Result<()> { - self.db.borrow_mut().enable_load_extension(1) - } - - /// Disable loading of SQLite extensions. - /// - /// See [`Connection::load_extension_enable`] for an example. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[cfg(feature = "load_extension")] - #[inline] - pub fn load_extension_disable(&self) -> Result<()> { - // It's always safe to turn off extension loading. - unsafe { self.db.borrow_mut().enable_load_extension(0) } - } - - /// Load the SQLite extension at `dylib_path`. `dylib_path` is passed - /// through to `sqlite3_load_extension`, which may attempt OS-specific - /// modifications if the file cannot be loaded directly (for example - /// converting `"some/ext"` to `"some/ext.so"`, `"some\\ext.dll"`, ...). - /// - /// If `entry_point` is `None`, SQLite will attempt to find the entry point. - /// If it is not `None`, the entry point will be passed through to - /// `sqlite3_load_extension`. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, LoadExtensionGuard}; - /// fn load_my_extension(conn: &Connection) -> Result<()> { - /// // Safety: we don't execute any SQL statements while - /// // extension loading is enabled. - /// let _guard = unsafe { LoadExtensionGuard::new(conn)? }; - /// // Safety: `my_sqlite_extension` is highly trustworthy. - /// unsafe { conn.load_extension("my_sqlite_extension", None::<&str>) } - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - /// - /// # Safety - /// - /// This is equivalent to performing a `dlopen`/`LoadLibrary` on a shared - /// library, and calling a function inside, and thus requires that you trust - /// the library that you're loading. - /// - /// That is to say: to safely use this, the code in the extension must be - /// sound, trusted, correctly use the SQLite APIs, and not contain any - /// memory or thread safety errors. - #[cfg(feature = "load_extension")] - #[inline] - pub unsafe fn load_extension, N: Name>( - &self, - dylib_path: P, - entry_point: Option, - ) -> Result<()> { - self.db - .borrow_mut() - .load_extension(dylib_path.as_ref(), entry_point) - } - - /// Get access to the underlying SQLite database connection handle. - /// - /// # Warning - /// - /// You should not need to use this function. If you do need to, please - /// [open an issue on the rusqlite repository](https://github.com/rusqlite/rusqlite/issues) and describe - /// your use case. - /// - /// # Safety - /// - /// This function is unsafe because it gives you raw access - /// to the SQLite connection, and what you do with it could impact the - /// safety of this `Connection`. - #[inline] - pub unsafe fn handle(&self) -> *mut ffi::sqlite3 { - self.db.borrow().db() - } - - /// Create a `Connection` from a raw handle. - /// - /// The underlying SQLite database connection handle will not be closed when - /// the returned connection is dropped/closed. - /// - /// # Safety - /// - /// This function is unsafe because improper use may impact the Connection. - #[inline] - pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result { - let db = InnerConnection::new(db, false); - Ok(Self { - db: RefCell::new(db), - #[cfg(feature = "cache")] - cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - transaction_behavior: TransactionBehavior::Deferred, - }) - } - - /// Helper to register an SQLite extension written in Rust. - /// For [persistent](https://sqlite.org/loadext.html#persistent_loadable_extensions) extension, - /// `init` should return `Ok(true)`. - /// # Safety - /// * Results are undefined if `init` does not just register features. - #[cfg(feature = "loadable_extension")] - pub unsafe fn extension_init2( - db: *mut ffi::sqlite3, - pz_err_msg: *mut *mut c_char, - p_api: *mut ffi::sqlite3_api_routines, - init: fn(Self) -> Result, - ) -> c_int { - if p_api.is_null() { - return ffi::SQLITE_ERROR; - } - match ffi::rusqlite_extension_init2(p_api) - .map_err(Error::from) - .and(Self::from_handle(db)) - .and_then(init) - { - Err(err) => to_sqlite_error(&err, pz_err_msg), - Ok(true) => ffi::SQLITE_OK_LOAD_PERMANENTLY, - _ => ffi::SQLITE_OK, - } - } - - /// Create a `Connection` from a raw owned handle. - /// - /// The returned connection will attempt to close the inner connection - /// when dropped/closed. This function should only be called on connections - /// owned by the caller. - /// - /// # Safety - /// - /// This function is unsafe because improper use may impact the Connection. - /// In particular, it should only be called on connections created - /// and owned by the caller, e.g. as a result of calling - /// `ffi::sqlite3_open`(). - #[inline] - pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result { - let db = InnerConnection::new(db, true); - Ok(Self { - db: RefCell::new(db), - #[cfg(feature = "cache")] - cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY), - transaction_behavior: TransactionBehavior::Deferred, - }) - } - - /// Get access to a handle that can be used to interrupt long-running - /// queries from another thread. - #[inline] - pub fn get_interrupt_handle(&self) -> InterruptHandle { - self.db.borrow().get_interrupt_handle() - } - - #[inline] - fn decode_result(&self, code: c_int) -> Result<()> { - self.db.borrow().decode_result(code) - } - - /// Return the number of rows modified, inserted or deleted by the most - /// recently completed INSERT, UPDATE or DELETE statement on the database - /// connection. - /// - /// See - #[inline] - pub fn changes(&self) -> u64 { - self.db.borrow().changes() - } - - /// Return the total number of rows modified, inserted or deleted by all - /// completed INSERT, UPDATE or DELETE statements since the database - /// connection was opened, including those executed as part of trigger programs. - /// - /// See - #[inline] - pub fn total_changes(&self) -> u64 { - self.db.borrow().total_changes() - } - - /// Test for auto-commit mode. - /// Autocommit mode is on by default. - #[inline] - pub fn is_autocommit(&self) -> bool { - self.db.borrow().is_autocommit() - } - - /// Determine if all associated prepared statements have been reset. - #[inline] - pub fn is_busy(&self) -> bool { - self.db.borrow().is_busy() - } - - /// Flush caches to disk mid-transaction - pub fn cache_flush(&self) -> Result<()> { - self.db.borrow_mut().cache_flush() - } - - /// Determine if a database is read-only - pub fn is_readonly(&self, db_name: N) -> Result { - self.db.borrow().db_readonly(db_name) - } - - /// Return the schema name for a database connection - /// - /// ## Failure - /// - /// Return an `Error::InvalidDatabaseIndex` if `index` is out of range. - #[cfg(feature = "modern_sqlite")] // 3.39.0 - pub fn db_name(&self, index: usize) -> Result { - unsafe { - let db = self.handle(); - let name = ffi::sqlite3_db_name(db, index as c_int); - if name.is_null() { - Err(Error::InvalidDatabaseIndex(index)) - } else { - Ok(CStr::from_ptr(name).to_str()?.to_owned()) - } - } - } - - /// Determine whether an interrupt is currently in effect - #[cfg(feature = "modern_sqlite")] // 3.41.0 - pub fn is_interrupted(&self) -> bool { - self.db.borrow().is_interrupted() - } -} - -impl fmt::Debug for Connection { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Connection") - .field("path", &self.path()) - .finish() - } -} - -/// Batch fallible iterator -/// -/// # Warning -/// -/// There is no recovery on parsing error, when an invalid statement is found in `sql`, SQLite cannot jump to the next statement. -/// So you should break the loop when an error is raised by the `next` method. -/// -/// ```rust -/// use fallible_iterator::FallibleIterator; -/// use rusqlite::{Batch, Connection, Result}; -/// -/// fn main() -> Result<()> { -/// let conn = Connection::open_in_memory()?; -/// let sql = r" -/// CREATE TABLE tbl1 (col); -/// CREATE TABLE tbl2 (col); -/// "; -/// let mut batch = Batch::new(&conn, sql); -/// while let Some(mut stmt) = batch.next()? { -/// stmt.execute([])?; -/// } -/// Ok(()) -/// } -/// ``` -#[derive(Debug)] -pub struct Batch<'conn, 'sql> { - conn: &'conn Connection, - sql: &'sql str, - tail: usize, -} - -impl<'conn, 'sql> Batch<'conn, 'sql> { - /// Constructor - pub fn new(conn: &'conn Connection, sql: &'sql str) -> Self { - Batch { conn, sql, tail: 0 } - } -} -impl<'conn> fallible_iterator::FallibleIterator for Batch<'conn, '_> { - type Item = Statement<'conn>; - type Error = Error; - - /// Iterates on each batch statements. - /// - /// Returns `Ok(None)` when batch is completed. - fn next(&mut self) -> Result>> { - while self.tail < self.sql.len() { - let sql = &self.sql[self.tail..]; - let (next, tail) = - self.conn - .db - .borrow_mut() - .prepare(self.conn, sql, PrepFlags::default())?; - if tail == 0 { - self.tail = self.sql.len(); - } else { - self.tail += tail; - } - if next.stmt.is_null() { - continue; - } - return Ok(Some(next)); - } - Ok(None) - } -} - -bitflags::bitflags! { - /// Flags for opening SQLite database connections. See - /// [sqlite3_open_v2](https://www.sqlite.org/c3ref/open.html) for details. - /// - /// The default open flags are `SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE - /// | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX`. See [`Connection::open`] for - /// some discussion about these flags. - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - #[repr(C)] - pub struct OpenFlags: 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 = ffi::SQLITE_OPEN_URI; - /// The database will be opened as an in-memory database. - const SQLITE_OPEN_MEMORY = ffi::SQLITE_OPEN_MEMORY; - /// The new database connection will not use a per-connection mutex (the - /// connection will use the "multi-thread" threading mode, in SQLite - /// parlance). - /// - /// This is used by default, as proper `Send`/`Sync` usage (in - /// particular, the fact that [`Connection`] does not implement `Sync`) - /// ensures thread-safety without the need to perform locking around all - /// calls. - const SQLITE_OPEN_NO_MUTEX = ffi::SQLITE_OPEN_NOMUTEX; - /// The new database connection will use a per-connection mutex -- the - /// "serialized" threading mode, in SQLite parlance. - /// - /// # Caveats - /// - /// This flag should probably never be used with `rusqlite`, as we - /// ensure thread-safety statically (we implement [`Send`] and not - /// [`Sync`]). - /// - /// Critically, even if this flag is used, the [`Connection`] is not - /// safe to use across multiple threads simultaneously. To access a - /// database from multiple threads, you should either create multiple - /// connections, one for each thread (if you have very many threads, - /// wrapping the `rusqlite::Connection` in a mutex is also reasonable). - /// - /// This is both because of the additional per-connection state stored - /// by `rusqlite` (for example, the prepared statement cache), and - /// because not all of SQLites functions are fully thread safe, even in - /// serialized/`SQLITE_OPEN_FULLMUTEX` mode. - /// - /// All that said, it's fairly harmless to enable this flag with - /// `rusqlite`, it will just slow things down while providing no - /// benefit. - const SQLITE_OPEN_FULL_MUTEX = ffi::SQLITE_OPEN_FULLMUTEX; - /// The database is opened with shared cache enabled. - /// - /// This is frequently useful for in-memory connections, but note that - /// broadly speaking it's discouraged by SQLite itself, which states - /// "Any use of shared cache is discouraged" in the official - /// [documentation](https://www.sqlite.org/c3ref/enable_shared_cache.html). - 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. (3.31.0) - const SQLITE_OPEN_NOFOLLOW = 0x0100_0000; - /// Extended result codes. (3.37.0) - const SQLITE_OPEN_EXRESCODE = 0x0200_0000; - } -} - -impl Default for OpenFlags { - #[inline] - fn default() -> Self { - // Note: update the `Connection::open` and top-level `OpenFlags` docs if - // you change these. - Self::SQLITE_OPEN_READ_WRITE - | Self::SQLITE_OPEN_CREATE - | Self::SQLITE_OPEN_NO_MUTEX - | Self::SQLITE_OPEN_URI - } -} - -bitflags::bitflags! { - /// Prepare flags. See - /// [sqlite3_prepare_v3](https://sqlite.org/c3ref/c_prepare_dont_log.html) for details. - #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] - #[repr(C)] - pub struct PrepFlags: c_uint { - /// A hint to the query planner that the prepared statement will be retained for a long time and probably reused many times. - const SQLITE_PREPARE_PERSISTENT = 0x01; - /// Causes the SQL compiler to return an error (error code SQLITE_ERROR) if the statement uses any virtual tables. - const SQLITE_PREPARE_NO_VTAB = 0x04; - /// Prevents SQL compiler errors from being sent to the error log. - const SQLITE_PREPARE_DONT_LOG = 0x10; - } -} - -/// Allows interrupting a long-running computation. -pub struct InterruptHandle { - db_lock: Arc>, -} - -unsafe impl Send for InterruptHandle {} -unsafe impl Sync for InterruptHandle {} - -impl InterruptHandle { - /// Interrupt the query currently executing on another thread. This will - /// cause that query to fail with a `SQLITE3_INTERRUPT` error. - pub fn interrupt(&self) { - let db_handle = self.db_lock.lock().unwrap(); - if !db_handle.is_null() { - unsafe { ffi::sqlite3_interrupt(*db_handle) } - } - } -} - -#[cfg(doctest)] -doc_comment::doctest!("../README.md"); - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::*; - use fallible_iterator::FallibleIterator; - use std::error::Error as StdError; - use std::fmt; - - // this function is never called, but is still type checked; in - // particular, calls with specific instantiations will require - // that those types are `Send`. - #[allow(dead_code)] - #[expect(unconditional_recursion, clippy::extra_unused_type_parameters)] - fn ensure_send() { - ensure_send::(); - ensure_send::(); - } - - #[allow(dead_code)] - #[expect(unconditional_recursion, clippy::extra_unused_type_parameters)] - fn ensure_sync() { - ensure_sync::(); - } - - fn checked_memory_handle() -> Connection { - Connection::open_in_memory().unwrap() - } - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn test_concurrent_transactions_busy_commit() -> Result<()> { - use std::time::Duration; - let tmp = tempfile::tempdir().unwrap(); - let path = tmp.path().join("transactions.db3"); - - Connection::open(&path)?.execute_batch( - " - BEGIN; CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(42); END;", - )?; - - let mut db1 = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_WRITE)?; - let mut db2 = Connection::open_with_flags(&path, OpenFlags::SQLITE_OPEN_READ_ONLY)?; - - db1.busy_timeout(Duration::from_millis(0))?; - db2.busy_timeout(Duration::from_millis(0))?; - - { - let tx1 = db1.transaction()?; - let tx2 = db2.transaction()?; - - // SELECT first makes sqlite lock with a shared lock - tx1.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?; - tx2.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?; - - tx1.execute("INSERT INTO foo VALUES(?1)", [1])?; - let _ = tx2.execute("INSERT INTO foo VALUES(?1)", [2]); - - let _ = tx1.commit(); - let _ = tx2.commit(); - } - - let _ = db1 - .transaction() - .expect("commit should have closed transaction"); - let _ = db2 - .transaction() - .expect("commit should have closed transaction"); - Ok(()) - } - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn test_persistence() -> Result<()> { - let temp_dir = tempfile::tempdir().unwrap(); - let path = temp_dir.path().join("test.db3"); - - { - let db = Connection::open(&path)?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(42); - END;"; - db.execute_batch(sql)?; - } - - let path_string = path.to_str().unwrap(); - let db = Connection::open(path_string)?; - - assert_eq!(42, db.one_column::("SELECT x FROM foo", [])?); - Ok(()) - } - - #[test] - fn test_open() { - Connection::open_in_memory().unwrap(); - - let db = checked_memory_handle(); - db.close().unwrap(); - } - - #[cfg_attr( - all(target_family = "wasm", target_os = "unknown"), - ignore = "no filesystem on this platform" - )] - #[test] - fn test_path() -> Result<()> { - let tmp = tempfile::tempdir().unwrap(); - let db = Connection::open("")?; - assert_eq!(Some(""), db.path()); - let db = Connection::open_in_memory()?; - assert_eq!(Some(""), db.path()); - let db = Connection::open("file:dummy.db?mode=memory&cache=shared")?; - assert_eq!(Some(""), db.path()); - let path = tmp.path().join("file.db"); - let db = Connection::open(path)?; - assert!(db.path().is_some_and(|p| p.ends_with("file.db"))); - Ok(()) - } - - #[test] - fn test_open_failure() { - let filename = "no_such_file.db"; - let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY); - let err = result.unwrap_err(); - if let Error::SqliteFailure(e, Some(msg)) = err { - assert_eq!(ErrorCode::CannotOpen, e.code); - assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code); - assert!( - msg.contains(filename), - "error message '{msg}' does not contain '{filename}'" - ); - } else { - panic!("SqliteFailure expected"); - } - } - - #[cfg(unix)] - #[test] - fn test_invalid_unicode_file_names() -> Result<()> { - use std::ffi::OsStr; - use std::fs::File; - use std::os::unix::ffi::OsStrExt; - let temp_dir = tempfile::tempdir().unwrap(); - - let path = temp_dir.path(); - if File::create(path.join(OsStr::from_bytes(&[0xFE]))).is_err() { - // Skip test, filesystem doesn't support invalid Unicode - return Ok(()); - } - let db_path = path.join(OsStr::from_bytes(&[0xFF])); - { - let db = Connection::open(&db_path)?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(42); - END;"; - db.execute_batch(sql)?; - } - - let db = Connection::open(&db_path)?; - - assert_eq!(42, db.one_column::("SELECT x FROM foo", [])?); - Ok(()) - } - - #[test] - fn test_close_retry() -> Result<()> { - let db = Connection::open_in_memory()?; - - // force the DB to be busy by preparing a statement; this must be done at the - // FFI level to allow us to call .close() without dropping the prepared - // statement first. - let raw_stmt = { - use super::str_to_cstring; - use std::ffi::c_int; - use std::ptr; - - let raw_db = db.db.borrow_mut().db; - let sql = "SELECT 1"; - let mut raw_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut(); - let cstring = str_to_cstring(sql)?; - let rc = unsafe { - ffi::sqlite3_prepare_v2( - raw_db, - cstring.as_ptr(), - (sql.len() + 1) as c_int, - &mut raw_stmt, - ptr::null_mut(), - ) - }; - assert_eq!(rc, ffi::SQLITE_OK); - raw_stmt - }; - - // now that we have an open statement, trying (and retrying) to close should - // fail. - let (db, _) = db.close().unwrap_err(); - let (db, _) = db.close().unwrap_err(); - let (db, _) = db.close().unwrap_err(); - - // finalize the open statement so a final close will succeed - assert_eq!(ffi::SQLITE_OK, unsafe { ffi::sqlite3_finalize(raw_stmt) }); - - db.close().unwrap(); - Ok(()) - } - - #[test] - fn test_open_with_flags() { - for bad_flags in &[ - OpenFlags::empty(), - OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE, - OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE, - ] { - Connection::open_in_memory_with_flags(*bad_flags).unwrap_err(); - } - } - - #[test] - fn test_execute_batch() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(1); - INSERT INTO foo VALUES(2); - INSERT INTO foo VALUES(3); - INSERT INTO foo VALUES(4); - END;"; - db.execute_batch(sql)?; - - db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?; - - db.execute_batch("INVALID SQL").unwrap_err(); - - db.execute_batch("PRAGMA locking_mode = EXCLUSIVE")?; - Ok(()) - } - - #[test] - fn test_execute() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER)")?; - - assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [1i32])?); - assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [2i32])?); - - assert_eq!(3, db.one_column::("SELECT SUM(x) FROM foo", [])?); - Ok(()) - } - - #[test] - #[cfg(feature = "extra_check")] - fn test_execute_select_with_no_row() { - let db = checked_memory_handle(); - let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err(); - assert_eq!( - err, - Error::ExecuteReturnedResults, - "Unexpected error: {err}" - ); - } - - #[test] - fn test_execute_select_with_row() { - let db = checked_memory_handle(); - let err = db.execute("SELECT 1", []).unwrap_err(); - assert_eq!(err, Error::ExecuteReturnedResults); - } - - #[test] - fn test_execute_multiple() { - let db = checked_memory_handle(); - let err = db - .execute( - "CREATE TABLE foo(x INTEGER); CREATE TABLE foo(x INTEGER)", - [], - ) - .unwrap_err(); - match err { - Error::MultipleStatement => (), - _ => panic!("Unexpected error: {err}"), - } - db.execute("CREATE TABLE t(c); -- bim", []) - .expect("Tail comment should be ignored"); - } - - #[test] - fn test_prepare_column_names() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - - let stmt = db.prepare("SELECT * FROM foo")?; - assert_eq!(stmt.column_count(), 1); - assert_eq!(stmt.column_names(), vec!["x"]); - - let stmt = db.prepare("SELECT x AS a, x AS b FROM foo")?; - assert_eq!(stmt.column_count(), 2); - assert_eq!(stmt.column_names(), vec!["a", "b"]); - Ok(()) - } - - #[test] - fn test_prepare_execute() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - - let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?; - assert_eq!(insert_stmt.execute([1i32])?, 1); - assert_eq!(insert_stmt.execute([2i32])?, 1); - assert_eq!(insert_stmt.execute([3i32])?, 1); - - assert_eq!(insert_stmt.execute(["hello"])?, 1); - assert_eq!(insert_stmt.execute(["goodbye"])?, 1); - assert_eq!(insert_stmt.execute([types::Null])?, 1); - - let mut update_stmt = db.prepare("UPDATE foo SET x=?1 WHERE x Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - - let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?; - assert_eq!(insert_stmt.execute([1i32])?, 1); - assert_eq!(insert_stmt.execute([2i32])?, 1); - assert_eq!(insert_stmt.execute([3i32])?, 1); - - let mut query = db.prepare("SELECT x FROM foo WHERE x < ?1 ORDER BY x DESC")?; - { - let mut rows = query.query([4i32])?; - let mut v = Vec::::new(); - - while let Some(row) = rows.next()? { - v.push(row.get(0)?); - } - - assert_eq!(v, [3i32, 2, 1]); - } - - { - let mut rows = query.query([3i32])?; - let mut v = Vec::::new(); - - while let Some(row) = rows.next()? { - v.push(row.get(0)?); - } - - assert_eq!(v, [2i32, 1]); - } - Ok(()) - } - - #[test] - fn test_query_map() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - INSERT INTO foo VALUES(3, \", \"); - INSERT INTO foo VALUES(2, \"world\"); - INSERT INTO foo VALUES(1, \"!\"); - END;"; - db.execute_batch(sql)?; - - let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; - let results: Result> = query.query([])?.map(|row| row.get(1)).collect(); - - assert_eq!(results?.concat(), "hello, world!"); - Ok(()) - } - - #[test] - fn test_query_row() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(1); - INSERT INTO foo VALUES(2); - INSERT INTO foo VALUES(3); - INSERT INTO foo VALUES(4); - END;"; - db.execute_batch(sql)?; - - assert_eq!(10, db.one_column::("SELECT SUM(x) FROM foo", [])?); - - let result: Result = db.one_column("SELECT x FROM foo WHERE x > 5", []); - match result.unwrap_err() { - Error::QueryReturnedNoRows => (), - err => panic!("Unexpected error {err}"), - } - - db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(())) - .unwrap_err(); - - db.query_row("SELECT 1; SELECT 2;", [], |_| Ok(())) - .unwrap_err(); - - Ok(()) - } - - #[test] - fn test_optional() -> Result<()> { - let db = Connection::open_in_memory()?; - - let result: Result = db.one_column("SELECT 1 WHERE 0 <> 0", []); - let result = result.optional(); - match result? { - None => (), - _ => panic!("Unexpected result"), - } - - let result: Result = db.one_column("SELECT 1 WHERE 0 == 0", []); - let result = result.optional(); - match result? { - Some(1) => (), - _ => panic!("Unexpected result"), - } - - let bad_query_result: Result = db.one_column("NOT A PROPER QUERY", []); - let bad_query_result = bad_query_result.optional(); - bad_query_result.unwrap_err(); - Ok(()) - } - - #[test] - fn test_pragma_query_row() -> Result<()> { - let db = Connection::open_in_memory()?; - assert_eq!( - "memory", - db.one_column::("PRAGMA journal_mode", [])? - ); - let mode = db.one_column::("PRAGMA journal_mode=off", [])?; - if cfg!(feature = "bundled") { - assert_eq!(mode, "off"); - } else { - // Note: system SQLite on macOS defaults to "off" rather than - // "memory" for the journal mode (which cannot be changed for - // in-memory connections). This seems like it's *probably* legal - // according to the docs below, so we relax this test when not - // bundling: - // - // From https://www.sqlite.org/pragma.html#pragma_journal_mode - // > Note that the journal_mode for an in-memory database is either - // > MEMORY or OFF and can not be changed to a different value. An - // > attempt to change the journal_mode of an in-memory database to - // > any setting other than MEMORY or OFF is ignored. - assert!(mode == "memory" || mode == "off", "Got mode {mode:?}"); - } - - Ok(()) - } - - #[test] - fn test_prepare_failures() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - - let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err(); - assert!(format!("{err}").contains("does_not_exist")); - Ok(()) - } - - #[test] - fn test_last_insert_rowid() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; - db.execute_batch("INSERT INTO foo DEFAULT VALUES")?; - - assert_eq!(db.last_insert_rowid(), 1); - - let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES")?; - for _ in 0i32..9 { - stmt.execute([])?; - } - assert_eq!(db.last_insert_rowid(), 10); - Ok(()) - } - - #[test] - fn test_total_changes() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "CREATE TABLE foo(x INTEGER PRIMARY KEY, value TEXT default '' NOT NULL, - desc TEXT default ''); - CREATE VIEW foo_bar AS SELECT x, desc FROM foo WHERE value = 'bar'; - CREATE TRIGGER INSERT_FOOBAR - INSTEAD OF INSERT - ON foo_bar - BEGIN - INSERT INTO foo VALUES(new.x, 'bar', new.desc); - END;"; - db.execute_batch(sql)?; - let total_changes_before = db.total_changes(); - let changes = db - .prepare("INSERT INTO foo_bar VALUES(null, 'baz');")? - .execute([])?; - let total_changes_after = db.total_changes(); - assert_eq!(changes, 0); - assert_eq!(total_changes_after - total_changes_before, 1); - Ok(()) - } - - #[test] - fn test_is_autocommit() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!( - db.is_autocommit(), - "autocommit expected to be active by default" - ); - Ok(()) - } - - #[test] - fn test_is_busy() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!(!db.is_busy()); - let mut stmt = db.prepare("PRAGMA schema_version")?; - assert!(!db.is_busy()); - { - let mut rows = stmt.query([])?; - assert!(!db.is_busy()); - let row = rows.next()?; - assert!(db.is_busy()); - assert!(row.is_some()); - } - assert!(!db.is_busy()); - Ok(()) - } - - #[test] - fn test_statement_debugging() -> Result<()> { - let db = Connection::open_in_memory()?; - let query = "SELECT 12345"; - let stmt = db.prepare(query)?; - - assert!(format!("{stmt:?}").contains(query)); - Ok(()) - } - - #[test] - fn test_notnull_constraint_error() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x NOT NULL)")?; - - let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []); - - match result.unwrap_err() { - Error::SqliteFailure(err, _) => { - assert_eq!(err.code, ErrorCode::ConstraintViolation); - assert_eq!(err.extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL); - } - err => panic!("Unexpected error {err}"), - } - Ok(()) - } - - #[test] - fn test_version_string() { - let n = version_number(); - let major = n / 1_000_000; - let minor = (n % 1_000_000) / 1_000; - let patch = n % 1_000; - - assert!(version().contains(&format!("{major}.{minor}.{patch}"))); - } - - #[test] - #[cfg(feature = "functions")] - fn test_interrupt() -> Result<()> { - let db = Connection::open_in_memory()?; - - let interrupt_handle = db.get_interrupt_handle(); - - db.create_scalar_function( - "interrupt", - 0, - functions::FunctionFlags::default(), - move |_| { - interrupt_handle.interrupt(); - Ok(0) - }, - )?; - - let mut stmt = - db.prepare("SELECT interrupt() FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)")?; - - let result: Result> = stmt.query([])?.map(|r| r.get(0)).collect(); - - assert_eq!( - result.unwrap_err().sqlite_error_code(), - Some(ErrorCode::OperationInterrupted) - ); - Ok(()) - } - - #[test] - fn test_interrupt_close() { - let db = checked_memory_handle(); - let handle = db.get_interrupt_handle(); - handle.interrupt(); - db.close().unwrap(); - handle.interrupt(); - - // Look at its internals to see if we cleared it out properly. - let db_guard = handle.db_lock.lock().unwrap(); - assert!(db_guard.is_null()); - // It would be nice to test that we properly handle close/interrupt - // running at the same time, but it seems impossible to do with any - // degree of reliability. - } - - #[test] - fn test_get_raw() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(i, x);")?; - let vals = ["foobar", "1234", "qwerty"]; - let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?1, ?2)")?; - for (i, v) in vals.iter().enumerate() { - let i_to_insert = i as i64; - assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1); - } - - let mut query = db.prepare("SELECT i, x FROM foo")?; - let mut rows = query.query([])?; - - while let Some(row) = rows.next()? { - let i = row.get_ref(0)?.as_i64()?; - let expect = vals[i as usize]; - let x = row.get_ref("x")?.as_str()?; - assert_eq!(x, expect); - } - - let mut query = db.prepare("SELECT x FROM foo")?; - let rows = query.query_map([], |row| { - let x = row.get_ref(0)?.as_str()?; // check From for Error - Ok(x[..].to_owned()) - })?; - - for (i, row) in rows.enumerate() { - assert_eq!(row?, vals[i]); - } - Ok(()) - } - - #[test] - fn test_from_handle() -> Result<()> { - let db = Connection::open_in_memory()?; - let handle = unsafe { db.handle() }; - { - let db = unsafe { Connection::from_handle(handle) }?; - db.execute_batch("PRAGMA VACUUM")?; - } - db.close().unwrap(); - Ok(()) - } - - #[test] - fn test_from_handle_owned() -> Result<()> { - let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut(); - let r = unsafe { ffi::sqlite3_open(c":memory:".as_ptr(), &mut handle) }; - assert_eq!(r, ffi::SQLITE_OK); - let db = unsafe { Connection::from_handle_owned(handle) }?; - db.execute_batch("PRAGMA VACUUM")?; - Ok(()) - } - - mod query_and_then_tests { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::*; - - #[derive(Debug)] - enum CustomError { - SomeError, - Sqlite(Error), - } - - impl fmt::Display for CustomError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match *self { - Self::SomeError => write!(f, "my custom error"), - Self::Sqlite(ref se) => write!(f, "my custom error: {se}"), - } - } - } - - impl StdError for CustomError { - fn description(&self) -> &str { - "my custom error" - } - - fn cause(&self) -> Option<&dyn StdError> { - match *self { - Self::SomeError => None, - Self::Sqlite(ref se) => Some(se), - } - } - } - - impl From for CustomError { - fn from(se: Error) -> Self { - Self::Sqlite(se) - } - } - - type CustomResult = Result; - - #[test] - fn test_query_and_then() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - INSERT INTO foo VALUES(3, \", \"); - INSERT INTO foo VALUES(2, \"world\"); - INSERT INTO foo VALUES(1, \"!\"); - END;"; - db.execute_batch(sql)?; - - let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; - let results: Result> = - query.query_and_then([], |row| row.get(1))?.collect(); - - assert_eq!(results?.concat(), "hello, world!"); - Ok(()) - } - - #[test] - fn test_query_and_then_fails() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - INSERT INTO foo VALUES(3, \", \"); - INSERT INTO foo VALUES(2, \"world\"); - INSERT INTO foo VALUES(1, \"!\"); - END;"; - db.execute_batch(sql)?; - - let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; - let bad_type: Result> = query.query_and_then([], |row| row.get(1))?.collect(); - - match bad_type.unwrap_err() { - Error::InvalidColumnType(..) => (), - err => panic!("Unexpected error {err}"), - } - - let bad_idx: Result> = - query.query_and_then([], |row| row.get(3))?.collect(); - - match bad_idx.unwrap_err() { - Error::InvalidColumnIndex(_) => (), - err => panic!("Unexpected error {err}"), - } - Ok(()) - } - - #[test] - fn test_query_and_then_custom_error() -> CustomResult<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - INSERT INTO foo VALUES(3, \", \"); - INSERT INTO foo VALUES(2, \"world\"); - INSERT INTO foo VALUES(1, \"!\"); - END;"; - db.execute_batch(sql)?; - - let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; - let results: CustomResult> = query - .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))? - .collect(); - - assert_eq!(results?.concat(), "hello, world!"); - Ok(()) - } - - #[test] - fn test_query_and_then_custom_error_fails() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - INSERT INTO foo VALUES(3, \", \"); - INSERT INTO foo VALUES(2, \"world\"); - INSERT INTO foo VALUES(1, \"!\"); - END;"; - db.execute_batch(sql)?; - - let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?; - let bad_type: CustomResult> = query - .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))? - .collect(); - - match bad_type.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnType(..)) => (), - err => panic!("Unexpected error {err}"), - } - - let bad_idx: CustomResult> = query - .query_and_then([], |row| row.get(3).map_err(CustomError::Sqlite))? - .collect(); - - match bad_idx.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (), - err => panic!("Unexpected error {err}"), - } - - let non_sqlite_err: CustomResult> = query - .query_and_then([], |_| Err(CustomError::SomeError))? - .collect(); - - match non_sqlite_err.unwrap_err() { - CustomError::SomeError => (), - err => panic!("Unexpected error {err}"), - } - Ok(()) - } - - #[test] - fn test_query_row_and_then_custom_error() -> CustomResult<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - END;"; - db.execute_batch(sql)?; - - let query = "SELECT x, y FROM foo ORDER BY x DESC"; - let results: CustomResult = - db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite)); - - assert_eq!(results?, "hello"); - Ok(()) - } - - #[test] - fn test_query_row_and_then_custom_error_fails() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - END;"; - db.execute_batch(sql)?; - - let query = "SELECT x, y FROM foo ORDER BY x DESC"; - let bad_type: CustomResult = - db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite)); - - match bad_type.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnType(..)) => (), - err => panic!("Unexpected error {err}"), - } - - let bad_idx: CustomResult = - db.query_row_and_then(query, [], |row| row.get(3).map_err(CustomError::Sqlite)); - - match bad_idx.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (), - err => panic!("Unexpected error {err}"), - } - - let non_sqlite_err: CustomResult = - db.query_row_and_then(query, [], |_| Err(CustomError::SomeError)); - - match non_sqlite_err.unwrap_err() { - CustomError::SomeError => (), - err => panic!("Unexpected error {err}"), - } - Ok(()) - } - } - - #[test] - fn test_dynamic() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y TEXT); - INSERT INTO foo VALUES(4, \"hello\"); - END;"; - db.execute_batch(sql)?; - - db.query_row("SELECT * FROM foo", [], |r| { - assert_eq!(2, r.as_ref().column_count()); - Ok(()) - }) - } - #[test] - fn test_dyn_box() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER);")?; - let b: Box = Box::new(5); - db.execute("INSERT INTO foo VALUES(?1)", [b])?; - db.query_row("SELECT x FROM foo", [], |r| { - assert_eq!(5, r.get_unwrap::<_, i32>(0)); - Ok(()) - }) - } - - #[test] - fn test_params() -> Result<()> { - let db = Connection::open_in_memory()?; - db.query_row( - "SELECT - ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, - ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, - ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30, - ?31, ?32, ?33, ?34;", - params![ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, - ], - |r| { - assert_eq!(1, r.get_unwrap::<_, i32>(0)); - Ok(()) - }, - ) - } - - #[test] - fn test_alter_table() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE x(t);")?; - // `execute_batch` should be used but `execute` should also work - db.execute("ALTER TABLE x RENAME TO y;", [])?; - Ok(()) - } - - #[test] - fn test_batch() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r" - CREATE TABLE tbl1 (col); - CREATE TABLE tbl2 (col); - "; - let mut batch = Batch::new(&db, sql); - while let Some(mut stmt) = batch.next()? { - stmt.execute([])?; - } - Ok(()) - } - - #[test] - fn test_invalid_batch() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r" - PRAGMA test1; - PRAGMA test2=?; - PRAGMA test3; - "; - let mut batch = Batch::new(&db, sql); - assert!(batch.next().is_ok()); - assert!(batch.next().is_err()); - assert!(batch.next().is_err()); - assert!(Batch::new(&db, sql).count().is_err()); - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] - fn test_returning() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; - let row_id = - db.one_column::("INSERT INTO foo DEFAULT VALUES RETURNING ROWID", [])?; - assert_eq!(row_id, 1); - Ok(()) - } - - #[test] - fn test_cache_flush() -> Result<()> { - let db = Connection::open_in_memory()?; - db.cache_flush() - } - - #[test] - fn db_readonly() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!(!db.is_readonly(MAIN_DB)?); - Ok(()) - } - - #[test] - #[cfg(feature = "rusqlite-macros")] - fn prepare_and_bind() -> Result<()> { - let db = Connection::open_in_memory()?; - let name = "Lisa"; - let age = 8; - let mut stmt = prepare_and_bind!(db, "SELECT $name, $age;"); - let (v1, v2) = stmt - .raw_query() - .next() - .and_then(|o| o.ok_or(Error::QueryReturnedNoRows)) - .and_then(|r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))?; - assert_eq!((v1.as_str(), v2), (name, age)); - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] - fn test_db_name() -> Result<()> { - let db = Connection::open_in_memory()?; - assert_eq!(db.db_name(0)?, "main"); - assert_eq!(db.db_name(1)?, "temp"); - assert_eq!(db.db_name(2), Err(Error::InvalidDatabaseIndex(2))); - db.execute_batch("ATTACH DATABASE ':memory:' AS xyz;")?; - assert_eq!(db.db_name(2)?, "xyz"); - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] - fn test_is_interrupted() -> Result<()> { - let db = Connection::open_in_memory()?; - assert!(!db.is_interrupted()); - db.get_interrupt_handle().interrupt(); - assert!(db.is_interrupted()); - Ok(()) - } - - #[test] - fn release_memory() -> Result<()> { - let db = Connection::open_in_memory()?; - db.release_memory() - } -} diff --git a/vendor/rusqlite/src/limits.rs b/vendor/rusqlite/src/limits.rs deleted file mode 100644 index 562d76e..0000000 --- a/vendor/rusqlite/src/limits.rs +++ /dev/null @@ -1,182 +0,0 @@ -//! Run-Time Limits - -use crate::{ffi, Connection, Result}; -use std::ffi::c_int; - -/// Run-Time limit categories, for use with [`Connection::limit`] and -/// [`Connection::set_limit`]. -/// -/// See the official documentation for more information: -/// - -/// - -#[derive(Copy, Clone, Debug)] -#[repr(i32)] -#[non_exhaustive] -#[expect(non_camel_case_types)] -pub enum Limit { - /// The maximum size of any string or BLOB or table row, in bytes. - SQLITE_LIMIT_LENGTH = ffi::SQLITE_LIMIT_LENGTH, - /// The maximum length of an SQL statement, in bytes. - SQLITE_LIMIT_SQL_LENGTH = ffi::SQLITE_LIMIT_SQL_LENGTH, - /// 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 = ffi::SQLITE_LIMIT_COLUMN, - /// The maximum depth of the parse tree on any expression. - SQLITE_LIMIT_EXPR_DEPTH = ffi::SQLITE_LIMIT_EXPR_DEPTH, - /// The maximum number of terms in a compound SELECT statement. - SQLITE_LIMIT_COMPOUND_SELECT = ffi::SQLITE_LIMIT_COMPOUND_SELECT, - /// The maximum number of instructions in a virtual machine program used to - /// implement an SQL statement. - SQLITE_LIMIT_VDBE_OP = ffi::SQLITE_LIMIT_VDBE_OP, - /// The maximum number of arguments on a function. - SQLITE_LIMIT_FUNCTION_ARG = ffi::SQLITE_LIMIT_FUNCTION_ARG, - /// The maximum number of attached databases. - SQLITE_LIMIT_ATTACHED = ffi::SQLITE_LIMIT_ATTACHED, - /// The maximum length of the pattern argument to the LIKE or GLOB - /// operators. - SQLITE_LIMIT_LIKE_PATTERN_LENGTH = ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, - /// The maximum index number of any parameter in an SQL statement. - SQLITE_LIMIT_VARIABLE_NUMBER = ffi::SQLITE_LIMIT_VARIABLE_NUMBER, - /// The maximum depth of recursion for triggers. - SQLITE_LIMIT_TRIGGER_DEPTH = ffi::SQLITE_LIMIT_TRIGGER_DEPTH, - /// The maximum number of auxiliary worker threads that a single prepared - /// statement may start. - SQLITE_LIMIT_WORKER_THREADS = ffi::SQLITE_LIMIT_WORKER_THREADS, - /// Only used for testing - #[cfg(test)] - INVALID = -1, -} - -impl Connection { - /// Returns the current value of a [`Limit`]. - #[inline] - pub fn limit(&self, limit: Limit) -> Result { - let c = self.db.borrow(); - let rc = unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }; - if rc < 0 { - return Err(err!(ffi::SQLITE_RANGE, "{limit:?} is invalid")); - } - Ok(rc) - } - - /// Changes the [`Limit`] to `new_val`, returning the prior - /// value of the limit. - #[inline] - pub fn set_limit(&self, limit: Limit, new_val: i32) -> Result { - if new_val < 0 { - return Err(err!(ffi::SQLITE_RANGE, "{new_val} is invalid")); - } - let c = self.db.borrow_mut(); - let rc = unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }; - if rc < 0 { - return Err(err!(ffi::SQLITE_RANGE, "{limit:?} is invalid")); - } - Ok(rc) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::*; - use crate::Result; - - #[test] - fn test_limit_values() { - assert_eq!(Limit::SQLITE_LIMIT_LENGTH as i32, ffi::SQLITE_LIMIT_LENGTH,); - assert_eq!( - Limit::SQLITE_LIMIT_SQL_LENGTH as i32, - ffi::SQLITE_LIMIT_SQL_LENGTH, - ); - assert_eq!(Limit::SQLITE_LIMIT_COLUMN as i32, ffi::SQLITE_LIMIT_COLUMN,); - assert_eq!( - Limit::SQLITE_LIMIT_EXPR_DEPTH as i32, - ffi::SQLITE_LIMIT_EXPR_DEPTH, - ); - assert_eq!( - Limit::SQLITE_LIMIT_COMPOUND_SELECT as i32, - ffi::SQLITE_LIMIT_COMPOUND_SELECT, - ); - assert_eq!( - Limit::SQLITE_LIMIT_VDBE_OP as i32, - ffi::SQLITE_LIMIT_VDBE_OP, - ); - assert_eq!( - Limit::SQLITE_LIMIT_FUNCTION_ARG as i32, - ffi::SQLITE_LIMIT_FUNCTION_ARG, - ); - assert_eq!( - Limit::SQLITE_LIMIT_ATTACHED as i32, - ffi::SQLITE_LIMIT_ATTACHED, - ); - assert_eq!( - Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH as i32, - ffi::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, - ); - assert_eq!( - Limit::SQLITE_LIMIT_VARIABLE_NUMBER as i32, - ffi::SQLITE_LIMIT_VARIABLE_NUMBER, - ); - assert_eq!( - Limit::SQLITE_LIMIT_TRIGGER_DEPTH as i32, - ffi::SQLITE_LIMIT_TRIGGER_DEPTH, - ); - assert_eq!( - Limit::SQLITE_LIMIT_WORKER_THREADS as i32, - ffi::SQLITE_LIMIT_WORKER_THREADS, - ); - } - - #[test] - fn test_limit() -> Result<()> { - let db = Connection::open_in_memory()?; - db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024)?; - assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH)?); - - db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024)?; - assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH)?); - - db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64)?; - assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN)?); - - db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256)?; - assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH)?); - - db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32)?; - assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT)?); - - db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32)?; - assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG)?); - - db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2)?; - assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED)?); - - db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128)?; - assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH)?); - - db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99)?; - assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)?); - - db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32)?; - assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH)?); - - db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2)?; - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS)?); - - // wasm build with DSQLITE_THREADSAFE=0, so limit not working - // see - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - assert_eq!(0, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS)?); - - assert!(db - .set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, -1) - .is_err()); - assert!(db.set_limit(Limit::INVALID, 0).is_err()); - assert!(db.limit(Limit::INVALID).is_err()); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/load_extension_guard.rs b/vendor/rusqlite/src/load_extension_guard.rs deleted file mode 100644 index 25a7973..0000000 --- a/vendor/rusqlite/src/load_extension_guard.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{Connection, Result}; - -/// RAII guard temporarily enabling SQLite extensions to be loaded. -/// -/// ## Example -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result, LoadExtensionGuard}; -/// # use std::path::{Path}; -/// fn load_my_extension(conn: &Connection) -> Result<()> { -/// unsafe { -/// let _guard = LoadExtensionGuard::new(conn)?; -/// conn.load_extension("trusted/sqlite/extension", None::<&str>) -/// } -/// } -/// ``` -pub struct LoadExtensionGuard<'conn> { - conn: &'conn Connection, -} - -impl LoadExtensionGuard<'_> { - /// Attempt to enable loading extensions. Loading extensions will be - /// disabled when this guard goes out of scope. Cannot be meaningfully - /// nested. - /// - /// # Safety - /// - /// You must not run untrusted queries while extension loading is enabled. - /// - /// See the safety comment on [`Connection::load_extension_enable`] for more - /// details. - #[inline] - pub unsafe fn new(conn: &Connection) -> Result> { - conn.load_extension_enable() - .map(|_| LoadExtensionGuard { conn }) - } -} - -#[expect(unused_must_use)] -impl Drop for LoadExtensionGuard<'_> { - #[inline] - fn drop(&mut self) { - self.conn.load_extension_disable(); - } -} diff --git a/vendor/rusqlite/src/params.rs b/vendor/rusqlite/src/params.rs deleted file mode 100644 index e85793b..0000000 --- a/vendor/rusqlite/src/params.rs +++ /dev/null @@ -1,453 +0,0 @@ -use crate::{BindIndex, Result, Statement, ToSql}; - -mod sealed { - /// This trait exists just to ensure that the only impls of `trait Params` - /// that are allowed are ones in this crate. - pub trait Sealed {} -} -use sealed::Sealed; - -/// Trait used for [sets of parameter][params] passed into SQL -/// statements/queries. -/// -/// [params]: https://www.sqlite.org/c3ref/bind_blob.html -/// -/// Note: Currently, this trait can only be implemented inside this crate. -/// Additionally, it's methods (which are `doc(hidden)`) should currently not be -/// considered part of the stable API, although it's possible they will -/// stabilize in the future. -/// -/// # Passing parameters to SQLite -/// -/// Many functions in this library let you pass parameters to SQLite. Doing this -/// lets you avoid any risk of SQL injection, and is simpler than escaping -/// things manually. Aside from deprecated functions and a few helpers, this is -/// indicated by the function taking a generic argument that implements `Params` -/// (this trait). -/// -/// ## Positional parameters -/// -/// For cases where you want to pass a list of parameters where the number of -/// parameters is known at compile time, this can be done in one of the -/// following ways: -/// -/// - For small lists of parameters up to 16 items, they may alternatively be -/// passed as a tuple, as in `thing.query((1, "foo"))`. -/// This is somewhat inconvenient for a single item, since you need a -/// weird-looking trailing comma: `thing.query(("example",))`. That case is -/// perhaps more cleanly expressed as `thing.query(["example"])`. -/// -/// - Using the [`rusqlite::params!`](crate::params!) macro, e.g. -/// `thing.query(rusqlite::params![1, "foo", bar])`. This is mostly useful for -/// heterogeneous lists where the number of parameters greater than 16, or -/// homogeneous lists of parameters where the number of parameters exceeds 32. -/// -/// - For small homogeneous lists of parameters, they can either be passed as: -/// -/// - an array, as in `thing.query([1i32, 2, 3, 4])` or `thing.query(["foo", -/// "bar", "baz"])`. -/// -/// - a reference to an array of references, as in `thing.query(&["foo", -/// "bar", "baz"])` or `thing.query(&[&1i32, &2, &3])`. -/// (Note: in this case we don't implement this for slices for coherence -/// reasons, so it really is only for the "reference to array" types — -/// hence why the number of parameters must be <= 32, or you need to -/// reach for `rusqlite::params!`) -/// -/// Unfortunately, in the current design it's not possible to allow this for -/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like -/// this should instead either use `params!`, an array literal, a `&[&dyn -/// ToSql]` or if none of those work, [`ParamsFromIter`]. -/// -/// - As a slice of `ToSql` trait object references, e.g. `&[&dyn ToSql]`. This -/// is mostly useful for passing parameter lists around as arguments without -/// having every function take a generic `P: Params`. -/// -/// ### Example (positional) -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result, params}; -/// fn update_rows(conn: &Connection) -> Result<()> { -/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)")?; -/// -/// // Using a tuple: -/// stmt.execute((0, "foobar"))?; -/// -/// // Using `rusqlite::params!`: -/// stmt.execute(params![1i32, "blah"])?; -/// -/// // array literal — non-references -/// stmt.execute([2i32, 3i32])?; -/// -/// // array literal — references -/// stmt.execute(["foo", "bar"])?; -/// -/// // Slice literal, references: -/// stmt.execute(&[&2i32, &3i32])?; -/// -/// // Note: The types behind the references don't have to be `Sized` -/// stmt.execute(&["foo", "bar"])?; -/// -/// // However, this doesn't work (see above): -/// // stmt.execute(&[1i32, 2i32])?; -/// Ok(()) -/// } -/// ``` -/// -/// ## Named parameters -/// -/// SQLite lets you name parameters using a number of conventions (":foo", -/// "@foo", "$foo"). You can pass named parameters in to SQLite using rusqlite -/// in a few ways: -/// -/// - Using the [`rusqlite::named_params!`](crate::named_params!) macro, as in -/// `stmt.execute(named_params!{ ":name": "foo", ":age": 99 })`. Similar to -/// the `params` macro, this is most useful for heterogeneous lists of -/// parameters, or lists where the number of parameters exceeds 32. -/// -/// - As a slice of `&[(&str, &dyn ToSql)]`. This is what essentially all of -/// these boil down to in the end, conceptually at least. In theory, you can -/// pass this as `stmt`. -/// -/// - As array references, similar to the positional params. This looks like -/// `thing.query(&[(":foo", &1i32), (":bar", &2i32)])` or -/// `thing.query(&[(":foo", "abc"), (":bar", "def")])`. -/// -/// Note: Unbound named parameters will be left to the value they previously -/// were bound with, falling back to `NULL` for parameters which have never been -/// bound. -/// -/// ### Example (named) -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result, named_params}; -/// fn insert(conn: &Connection) -> Result<()> { -/// let mut stmt = conn.prepare("INSERT INTO test (key, value) VALUES (:key, :val)")?; -/// // Using `rusqlite::params!`: -/// stmt.execute(named_params! { ":key": "one", ":val": 2 })?; -/// // Alternatively: -/// stmt.execute(&[(":key", "three"), (":val", "four")])?; -/// // Or: -/// stmt.execute(&[(":key", &100), (":val", &200)])?; -/// Ok(()) -/// } -/// ``` -/// -/// ## No parameters -/// -/// You can just use an empty tuple or the empty array literal to run a query -/// that accepts no parameters. -/// -/// ### Example (no parameters) -/// -/// The empty tuple: -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result, params}; -/// fn delete_all_users(conn: &Connection) -> Result<()> { -/// // You may also use `()`. -/// conn.execute("DELETE FROM users", ())?; -/// Ok(()) -/// } -/// ``` -/// -/// The empty array: -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result, params}; -/// fn delete_all_users(conn: &Connection) -> Result<()> { -/// // Just use an empty array (e.g. `[]`) for no params. -/// conn.execute("DELETE FROM users", [])?; -/// Ok(()) -/// } -/// ``` -/// -/// ## Dynamic parameter list -/// -/// If you have a number of parameters which is unknown at compile time (for -/// example, building a dynamic query at runtime), you have two choices: -/// -/// - Use a `&[&dyn ToSql]`. This is often annoying to construct if you don't -/// already have this type on-hand. -/// - Use the [`ParamsFromIter`] type. This essentially lets you wrap an -/// iterator some `T: ToSql` with something that implements `Params`. The -/// usage of this looks like `rusqlite::params_from_iter(something)`. -/// -/// A lot of the considerations here are similar either way, so you should see -/// the [`ParamsFromIter`] documentation for more info / examples. -pub trait Params: Sealed { - // XXX not public api, might not need to expose. - // - // Binds the parameters to the statement. It is unlikely calling this - // explicitly will do what you want. Please use `Statement::query` or - // similar directly. - // - // For now, just hide the function in the docs... - #[doc(hidden)] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()>; -} - -// Explicitly impl for empty array. Critically, for `conn.execute([])` to be -// unambiguous, this must be the *only* implementation for an empty array. -// -// This sadly prevents `impl Params for [T; N]`, which -// forces people to use `params![...]` or `rusqlite::params_from_iter` for long -// homogeneous lists of parameters. This is not that big of a deal, but is -// unfortunate, especially because I mostly did it because I wanted a simple -// syntax for no-params that didn't require importing -- the empty tuple fits -// that nicely, but I didn't think of it until much later. -// -// Admittedly, if we did have the generic impl, then we *wouldn't* support the -// empty array literal as a parameter, since the `T` there would fail to be -// inferred. The error message here would probably be quite bad, and so on -// further thought, probably would end up causing *more* surprises, not less. -impl Sealed for [&(dyn ToSql + Send + Sync); 0] {} -impl Params for [&(dyn ToSql + Send + Sync); 0] { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.ensure_parameter_count(0) - } -} - -impl Sealed for &[&dyn ToSql] {} -impl Params for &[&dyn ToSql] { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters(self) - } -} - -impl Sealed for &[(S, T)] {} -impl Params for &[(S, T)] { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters_named(self) - } -} - -// Manual impls for the empty and singleton tuple, although the rest are covered -// by macros. -impl Sealed for () {} -impl Params for () { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.ensure_parameter_count(0) - } -} - -// I'm pretty sure you could tweak the `single_tuple_impl` to accept this. -impl Sealed for (T,) {} -impl Params for (T,) { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.ensure_parameter_count(1)?; - stmt.raw_bind_parameter(1, self.0)?; - Ok(()) - } -} - -macro_rules! single_tuple_impl { - ($count:literal : $(($field:tt $ftype:ident)),* $(,)?) => { - impl<$($ftype,)*> Sealed for ($($ftype,)*) where $($ftype: ToSql,)* {} - impl<$($ftype,)*> Params for ($($ftype,)*) where $($ftype: ToSql,)* { - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.ensure_parameter_count($count)?; - $({ - debug_assert!($field < $count); - stmt.raw_bind_parameter($field + 1, self.$field)?; - })+ - Ok(()) - } - } - } -} - -// We use a macro for the rest, but don't bother with trying to implement it -// in a single invocation (it's possible to do, but my attempts were almost the -// same amount of code as just writing it out this way, and much more dense -- -// it is a more complicated case than the TryFrom macro we have for row->tuple). -// -// Note that going up to 16 (rather than the 12 that the impls in the stdlib -// usually support) is just because we did the same in the `TryFrom` impl. -// I didn't catch that then, but there's no reason to remove it, and it seems -// nice to be consistent here; this way putting data in the database and getting -// data out of the database are more symmetric in a (mostly superficial) sense. -single_tuple_impl!(2: (0 A), (1 B)); -single_tuple_impl!(3: (0 A), (1 B), (2 C)); -single_tuple_impl!(4: (0 A), (1 B), (2 C), (3 D)); -single_tuple_impl!(5: (0 A), (1 B), (2 C), (3 D), (4 E)); -single_tuple_impl!(6: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F)); -single_tuple_impl!(7: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G)); -single_tuple_impl!(8: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H)); -single_tuple_impl!(9: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I)); -single_tuple_impl!(10: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J)); -single_tuple_impl!(11: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K)); -single_tuple_impl!(12: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L)); -single_tuple_impl!(13: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M)); -single_tuple_impl!(14: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N)); -single_tuple_impl!(15: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O)); -single_tuple_impl!(16: (0 A), (1 B), (2 C), (3 D), (4 E), (5 F), (6 G), (7 H), (8 I), (9 J), (10 K), (11 L), (12 M), (13 N), (14 O), (15 P)); - -macro_rules! impl_for_array_ref { - ($($N:literal)+) => {$( - // These are already generic, and there's a shedload of them, so lets - // avoid the compile time hit from making them all inline for now. - impl Sealed for &[&T; $N] {} - impl Params for &[&T; $N] { - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters(self) - } - } - impl Sealed for &[(S, &T); $N] {} - impl Params for &[(S, &T); $N] { - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters_named(self) - } - } - impl Sealed for [T; $N] {} - impl Params for [T; $N] { - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters(&self) - } - } - )+}; -} - -// Following libstd/libcore's (old) lead, implement this for arrays up to `[_; -// 32]`. Note `[_; 0]` is intentionally omitted for coherence reasons, see the -// note above the impl of `[&dyn ToSql; 0]` for more information. -// -// Note that this unfortunately means we can't use const generics here, but I -// don't really think it matters -- users who hit that can use `params!` anyway. -impl_for_array_ref!( - 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 -); - -/// Adapter type which allows any iterator over [`ToSql`] values to implement -/// [`Params`]. -/// -/// This struct is created by the [`params_from_iter`] function. -/// -/// This can be useful if you have something like an `&[String]` (of unknown -/// length), and you want to use them with an API that wants something -/// implementing `Params`. This way, you can avoid having to allocate storage -/// for something like a `&[&dyn ToSql]`. -/// -/// This essentially is only ever actually needed when dynamically generating -/// SQL — static SQL (by definition) has the number of parameters known -/// statically. As dynamically generating SQL is itself pretty advanced, this -/// API is itself for advanced use cases (See "Realistic use case" in the -/// examples). -/// -/// # Example -/// -/// ## Basic usage -/// -/// ```rust,no_run -/// use rusqlite::{params_from_iter, Connection, Result}; -/// use std::collections::BTreeSet; -/// -/// fn query(conn: &Connection, ids: &BTreeSet) -> Result<()> { -/// assert_eq!(ids.len(), 3, "Unrealistic sample code"); -/// -/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?1, ?2, ?3)")?; -/// let _rows = stmt.query(params_from_iter(ids.iter()))?; -/// -/// // use _rows... -/// Ok(()) -/// } -/// ``` -/// -/// ## Realistic use case -/// -/// Here's how you'd use `ParamsFromIter` to call [`Statement::exists`] with a -/// dynamic number of parameters. -/// -/// ```rust,no_run -/// use rusqlite::{Connection, Result}; -/// -/// pub fn any_active_users(conn: &Connection, usernames: &[String]) -> Result { -/// if usernames.is_empty() { -/// return Ok(false); -/// } -/// -/// // Note: `repeat_vars` never returns anything attacker-controlled, so -/// // it's fine to use it in a dynamically-built SQL string. -/// let vars = repeat_vars(usernames.len()); -/// -/// let sql = format!( -/// // In practice this would probably be better as an `EXISTS` query. -/// "SELECT 1 FROM user WHERE is_active AND name IN ({}) LIMIT 1", -/// vars, -/// ); -/// let mut stmt = conn.prepare(&sql)?; -/// stmt.exists(rusqlite::params_from_iter(usernames)) -/// } -/// -/// // Helper function to return a comma-separated sequence of `?`. -/// // - `repeat_vars(0) => panic!(...)` -/// // - `repeat_vars(1) => "?"` -/// // - `repeat_vars(2) => "?,?"` -/// // - `repeat_vars(3) => "?,?,?"` -/// // - ... -/// fn repeat_vars(count: usize) -> String { -/// assert_ne!(count, 0); -/// let mut s = "?,".repeat(count); -/// // Remove trailing comma -/// s.pop(); -/// s -/// } -/// ``` -/// -/// That is fairly complex, and even so would need even more work to be fully -/// production-ready: -/// -/// - production code should ensure `usernames` isn't so large that it will -/// surpass [`conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)`][limits], -/// chunking if too large. (Note that the limits api requires rusqlite to have -/// the "limits" feature). -/// -/// - `repeat_vars` can be implemented in a way that avoids needing to allocate -/// a String. -/// -/// - Etc... -/// -/// [limits]: crate::Connection::limit -/// -/// This complexity reflects the fact that `ParamsFromIter` is mainly intended -/// for advanced use cases — most of the time you should know how many -/// parameters you have statically (and if you don't, you're either doing -/// something tricky, or should take a moment to think about the design). -#[derive(Clone, Debug)] -pub struct ParamsFromIter(I); - -/// Constructor function for a [`ParamsFromIter`]. See its documentation for -/// more. -#[inline] -pub fn params_from_iter(iter: I) -> ParamsFromIter -where - I: IntoIterator, - I::Item: ToSql, -{ - ParamsFromIter(iter) -} - -impl Sealed for ParamsFromIter -where - I: IntoIterator, - I::Item: ToSql, -{ -} - -impl Params for ParamsFromIter -where - I: IntoIterator, - I::Item: ToSql, -{ - #[inline] - fn __bind_in(self, stmt: &mut Statement<'_>) -> Result<()> { - stmt.bind_parameters(self.0) - } -} diff --git a/vendor/rusqlite/src/pragma.rs b/vendor/rusqlite/src/pragma.rs deleted file mode 100644 index 9b25fb5..0000000 --- a/vendor/rusqlite/src/pragma.rs +++ /dev/null @@ -1,426 +0,0 @@ -//! Pragma helpers - -use std::ops::Deref; - -use crate::ffi; -use crate::types::{ToSql, ToSqlOutput, ValueRef}; -use crate::{Connection, Result, Row}; - -pub struct Sql { - buf: String, -} - -impl Sql { - pub fn new() -> Self { - Self { buf: String::new() } - } - - pub fn push_pragma(&mut self, schema_name: Option<&str>, pragma_name: &str) -> Result<()> { - self.push_keyword("PRAGMA")?; - self.push_space(); - if let Some(schema_name) = schema_name { - self.push_schema_name(schema_name); - self.push_dot(); - } - self.push_keyword(pragma_name) - } - - pub fn push_keyword(&mut self, keyword: &str) -> Result<()> { - if !keyword.is_empty() && is_identifier(keyword) { - self.buf.push_str(keyword); - Ok(()) - } else { - Err(err!(ffi::SQLITE_MISUSE, "Invalid keyword \"{keyword}\"")) - } - } - - pub fn push_schema_name(&mut self, schema_name: &str) { - self.push_identifier(schema_name); - } - - pub fn push_identifier(&mut self, s: &str) { - if is_identifier(s) { - self.buf.push_str(s); - } else { - self.wrap_and_escape(s, '"'); - } - } - - pub fn push_value(&mut self, value: &dyn ToSql) -> Result<()> { - let value = value.to_sql()?; - let value = match value { - ToSqlOutput::Borrowed(v) => v, - ToSqlOutput::Owned(ref v) => ValueRef::from(v), - #[cfg(any(feature = "blob", feature = "functions", feature = "pointer"))] - _ => { - return Err(err!(ffi::SQLITE_MISUSE, "Unsupported value \"{value:?}\"")); - } - }; - match value { - ValueRef::Integer(i) => { - self.push_int(i); - } - ValueRef::Real(r) => { - self.push_real(r); - } - ValueRef::Text(s) => { - let s = std::str::from_utf8(s)?; - self.push_string_literal(s); - } - _ => { - return Err(err!(ffi::SQLITE_MISUSE, "Unsupported value \"{value:?}\"")); - } - }; - Ok(()) - } - - pub fn push_string_literal(&mut self, s: &str) { - self.wrap_and_escape(s, '\''); - } - - pub fn push_int(&mut self, i: i64) { - self.buf.push_str(&i.to_string()); - } - - pub fn push_real(&mut self, f: f64) { - self.buf.push_str(&f.to_string()); - } - - pub fn push_space(&mut self) { - self.buf.push(' '); - } - - pub fn push_dot(&mut self) { - self.buf.push('.'); - } - - pub fn push_equal_sign(&mut self) { - self.buf.push('='); - } - - pub fn open_brace(&mut self) { - self.buf.push('('); - } - - pub fn close_brace(&mut self) { - self.buf.push(')'); - } - - pub fn as_str(&self) -> &str { - &self.buf - } - - fn wrap_and_escape(&mut self, s: &str, quote: char) { - self.buf.push(quote); - let chars = s.chars(); - for ch in chars { - // escape `quote` by doubling it - if ch == quote { - self.buf.push(ch); - } - self.buf.push(ch); - } - self.buf.push(quote); - } -} - -impl Deref for Sql { - type Target = str; - - fn deref(&self) -> &str { - self.as_str() - } -} - -impl Connection { - /// Query the current value of `pragma_name`. - /// - /// Some pragmas will return multiple rows/values which cannot be retrieved - /// with this method. - /// - /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: - /// `SELECT user_version FROM pragma_user_version;` - pub fn pragma_query_value( - &self, - schema_name: Option<&str>, - pragma_name: &str, - f: F, - ) -> Result - where - F: FnOnce(&Row<'_>) -> Result, - { - let mut query = Sql::new(); - query.push_pragma(schema_name, pragma_name)?; - self.query_row(&query, [], f) - } - - /// Query the current rows/values of `pragma_name`. - /// - /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: - /// `SELECT * FROM pragma_collation_list;` - pub fn pragma_query( - &self, - schema_name: Option<&str>, - pragma_name: &str, - mut f: F, - ) -> Result<()> - where - F: FnMut(&Row<'_>) -> Result<()>, - { - let mut query = Sql::new(); - query.push_pragma(schema_name, pragma_name)?; - let mut stmt = self.prepare(&query)?; - let mut rows = stmt.query([])?; - while let Some(result_row) = rows.next()? { - let row = result_row; - f(row)?; - } - Ok(()) - } - - /// Query the current value(s) of `pragma_name` associated to - /// `pragma_value`. - /// - /// This method can be used with query-only pragmas which need an argument - /// (e.g. `table_info('one_tbl')`) or pragmas which returns value(s) - /// (e.g. `integrity_check`). - /// - /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20: - /// `SELECT * FROM pragma_table_info(?1);` - pub fn pragma( - &self, - schema_name: Option<&str>, - pragma_name: &str, - pragma_value: V, - mut f: F, - ) -> Result<()> - where - F: FnMut(&Row<'_>) -> Result<()>, - V: ToSql, - { - let mut sql = Sql::new(); - sql.push_pragma(schema_name, pragma_name)?; - // The argument may be either in parentheses - // or it may be separated from the pragma name by an equal sign. - // The two syntaxes yield identical results. - sql.open_brace(); - sql.push_value(&pragma_value)?; - sql.close_brace(); - let mut stmt = self.prepare(&sql)?; - let mut rows = stmt.query([])?; - while let Some(result_row) = rows.next()? { - let row = result_row; - f(row)?; - } - Ok(()) - } - - /// Set a new value to `pragma_name`. - /// - /// Some pragmas will return the updated value which cannot be retrieved - /// with this method. - pub fn pragma_update( - &self, - schema_name: Option<&str>, - pragma_name: &str, - pragma_value: V, - ) -> Result<()> - where - V: ToSql, - { - let mut sql = Sql::new(); - sql.push_pragma(schema_name, pragma_name)?; - // The argument may be either in parentheses - // or it may be separated from the pragma name by an equal sign. - // The two syntaxes yield identical results. - sql.push_equal_sign(); - sql.push_value(&pragma_value)?; - self.execute_batch(&sql) - } - - /// Set a new value to `pragma_name` and return the updated value. - /// - /// Only few pragmas automatically return the updated value. - pub fn pragma_update_and_check( - &self, - schema_name: Option<&str>, - pragma_name: &str, - pragma_value: V, - f: F, - ) -> Result - where - F: FnOnce(&Row<'_>) -> Result, - V: ToSql, - { - let mut sql = Sql::new(); - sql.push_pragma(schema_name, pragma_name)?; - // The argument may be either in parentheses - // or it may be separated from the pragma name by an equal sign. - // The two syntaxes yield identical results. - sql.push_equal_sign(); - sql.push_value(&pragma_value)?; - self.query_row(&sql, [], f) - } -} - -fn is_identifier(s: &str) -> bool { - let chars = s.char_indices(); - for (i, ch) in chars { - if i == 0 { - if !is_identifier_start(ch) { - return false; - } - } else if !is_identifier_continue(ch) { - return false; - } - } - true -} - -fn is_identifier_start(c: char) -> bool { - c.is_ascii_uppercase() || c == '_' || c.is_ascii_lowercase() || c > '\x7F' -} - -fn is_identifier_continue(c: char) -> bool { - c == '$' - || c.is_ascii_digit() - || c.is_ascii_uppercase() - || c == '_' - || c.is_ascii_lowercase() - || c > '\x7F' -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::Sql; - use crate::pragma; - use crate::{Connection, Result}; - - #[test] - fn pragma_query_value() -> Result<()> { - let db = Connection::open_in_memory()?; - let user_version: i32 = db.pragma_query_value(None, "user_version", |row| row.get(0))?; - assert_eq!(0, user_version); - Ok(()) - } - - #[test] - fn pragma_func_query_value() -> Result<()> { - let db = Connection::open_in_memory()?; - let user_version: i32 = - db.one_column("SELECT user_version FROM pragma_user_version", [])?; - assert_eq!(0, user_version); - Ok(()) - } - - #[test] - fn pragma_query_no_schema() -> Result<()> { - let db = Connection::open_in_memory()?; - let mut user_version = -1; - db.pragma_query(None, "user_version", |row| { - user_version = row.get(0)?; - Ok(()) - })?; - assert_eq!(0, user_version); - Ok(()) - } - - #[test] - fn pragma_query_with_schema() -> Result<()> { - let db = Connection::open_in_memory()?; - let mut user_version = -1; - db.pragma_query(Some("main"), "user_version", |row| { - user_version = row.get(0)?; - Ok(()) - })?; - assert_eq!(0, user_version); - Ok(()) - } - - #[test] - fn pragma() -> Result<()> { - let db = Connection::open_in_memory()?; - let mut columns = Vec::new(); - db.pragma(None, "table_info", "sqlite_master", |row| { - let column: String = row.get(1)?; - columns.push(column); - Ok(()) - })?; - assert_eq!(5, columns.len()); - Ok(()) - } - - #[test] - fn pragma_func() -> Result<()> { - let db = Connection::open_in_memory()?; - let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?1)")?; - let mut columns = Vec::new(); - let mut rows = table_info.query(["sqlite_master"])?; - - while let Some(row) = rows.next()? { - let column: String = row.get(1)?; - columns.push(column); - } - assert_eq!(5, columns.len()); - Ok(()) - } - - #[test] - fn pragma_update() -> Result<()> { - let db = Connection::open_in_memory()?; - db.pragma_update(None, "user_version", 1) - } - - #[test] - fn pragma_update_and_check() -> Result<()> { - let db = Connection::open_in_memory()?; - let journal_mode: String = - db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get(0))?; - assert!( - journal_mode == "off" || journal_mode == "memory", - "mode: {journal_mode:?}" - ); - // Sanity checks to ensure the move to a generic `ToSql` wasn't breaking - let mode = - db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get::<_, String>(0))?; - assert!(mode == "off" || mode == "memory", "mode: {mode:?}"); - - let param: &dyn crate::ToSql = &"OFF"; - let mode = - db.pragma_update_and_check(None, "journal_mode", param, |row| row.get::<_, String>(0))?; - assert!(mode == "off" || mode == "memory", "mode: {mode:?}"); - Ok(()) - } - - #[test] - fn is_identifier() { - assert!(pragma::is_identifier("full")); - assert!(pragma::is_identifier("r2d2")); - assert!(!pragma::is_identifier("sp ce")); - assert!(!pragma::is_identifier("semi;colon")); - } - - #[test] - fn double_quote() { - let mut sql = Sql::new(); - sql.push_schema_name(r#"schema";--"#); - assert_eq!(r#""schema"";--""#, sql.as_str()); - } - - #[test] - fn wrap_and_escape() { - let mut sql = Sql::new(); - sql.push_string_literal("value'; --"); - assert_eq!("'value''; --'", sql.as_str()); - } - - #[test] - fn locking_mode() -> Result<()> { - let db = Connection::open_in_memory()?; - db.pragma_update(None, "locking_mode", "exclusive")?; - Ok(()) - } -} diff --git a/vendor/rusqlite/src/raw_statement.rs b/vendor/rusqlite/src/raw_statement.rs deleted file mode 100644 index 73ee67e..0000000 --- a/vendor/rusqlite/src/raw_statement.rs +++ /dev/null @@ -1,282 +0,0 @@ -use super::ffi; -use super::StatementStatus; -use crate::util::ParamIndexCache; -use crate::util::SqliteMallocString; -use std::ffi::{c_int, CStr}; -use std::ptr; -#[cfg(feature = "cache")] -use std::sync::Arc; - -// Private newtype for raw sqlite3_stmts that finalize themselves when dropped. -#[derive(Debug)] -pub struct RawStatement { - ptr: *mut ffi::sqlite3_stmt, - // Cached indices of named parameters, computed on the fly. - cache: ParamIndexCache, - // Cached SQL (trimmed) that we use as the key when we're in the statement - // cache. This is None for statements which didn't come from the statement - // cache. - // - // This is probably the same as `self.sql()` in most cases, but we don't - // care either way -- It's a better cache key as it is anyway since it's the - // actual source we got from rust. - // - // One example of a case where the result of `sqlite_sql` and the value in - // `statement_cache_key` might differ is if the statement has a `tail`. - #[cfg(feature = "cache")] - statement_cache_key: Option>, -} - -impl RawStatement { - #[inline] - pub unsafe fn new(stmt: *mut ffi::sqlite3_stmt) -> Self { - Self { - ptr: stmt, - cache: ParamIndexCache::default(), - #[cfg(feature = "cache")] - statement_cache_key: None, - } - } - - #[inline] - pub fn is_null(&self) -> bool { - self.ptr.is_null() - } - - #[inline] - #[cfg(feature = "cache")] - pub(crate) fn set_statement_cache_key(&mut self, p: impl Into>) { - self.statement_cache_key = Some(p.into()); - } - - #[inline] - #[cfg(feature = "cache")] - pub(crate) fn statement_cache_key(&self) -> Option> { - self.statement_cache_key.clone() - } - - #[inline] - pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { - self.ptr - } - - #[inline] - pub fn column_count(&self) -> usize { - // Note: Can't cache this as it changes if the schema is altered. - unsafe { ffi::sqlite3_column_count(self.ptr) as usize } - } - - #[inline] - pub fn column_type(&self, idx: usize) -> c_int { - unsafe { ffi::sqlite3_column_type(self.ptr, idx as c_int) } - } - - #[inline] - #[cfg(feature = "column_metadata")] - pub fn column_database_name(&self, idx: usize) -> Option<&CStr> { - unsafe { - let db_name = ffi::sqlite3_column_database_name(self.ptr, idx as c_int); - if db_name.is_null() { - None - } else { - Some(CStr::from_ptr(db_name)) - } - } - } - - #[inline] - #[cfg(feature = "column_metadata")] - pub fn column_table_name(&self, idx: usize) -> Option<&CStr> { - unsafe { - let tbl_name = ffi::sqlite3_column_table_name(self.ptr, idx as c_int); - if tbl_name.is_null() { - None - } else { - Some(CStr::from_ptr(tbl_name)) - } - } - } - - #[inline] - #[cfg(feature = "column_metadata")] - pub fn column_origin_name(&self, idx: usize) -> Option<&CStr> { - unsafe { - let origin_name = ffi::sqlite3_column_origin_name(self.ptr, idx as c_int); - if origin_name.is_null() { - None - } else { - Some(CStr::from_ptr(origin_name)) - } - } - } - - #[inline] - #[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); - if decltype.is_null() { - None - } else { - Some(CStr::from_ptr(decltype)) - } - } - } - - #[inline] - pub fn column_name(&self, idx: usize) -> Option<&CStr> { - let idx = idx as c_int; - if idx < 0 || idx >= self.column_count() as c_int { - return None; - } - unsafe { - let ptr = ffi::sqlite3_column_name(self.ptr, idx); - // If ptr is null here, it's an OOM, so there's probably nothing - // meaningful we can do. Just assert instead of returning None. - assert!( - !ptr.is_null(), - "Null pointer from sqlite3_column_name: Out of memory?" - ); - Some(CStr::from_ptr(ptr)) - } - } - - #[inline] - #[cfg(not(feature = "unlock_notify"))] - pub fn step(&self) -> c_int { - unsafe { ffi::sqlite3_step(self.ptr) } - } - - #[cfg(feature = "unlock_notify")] - pub fn step(&self) -> c_int { - use crate::unlock_notify; - let mut db = ptr::null_mut::(); - loop { - unsafe { - let mut rc = ffi::sqlite3_step(self.ptr); - // Bail out early for success and errors unrelated to locking. We - // still need check `is_locked` after this, but checking now lets us - // avoid one or two (admittedly cheap) calls into SQLite that we - // don't need to make. - if (rc & 0xff) != ffi::SQLITE_LOCKED { - break rc; - } - if db.is_null() { - db = ffi::sqlite3_db_handle(self.ptr); - } - if !unlock_notify::is_locked(db, rc) { - break rc; - } - rc = unlock_notify::wait_for_unlock_notify(db); - if rc != ffi::SQLITE_OK { - break rc; - } - self.reset(); - } - } - } - - #[inline] - pub fn reset(&self) -> c_int { - unsafe { ffi::sqlite3_reset(self.ptr) } - } - - #[inline] - pub fn bind_parameter_count(&self) -> usize { - unsafe { ffi::sqlite3_bind_parameter_count(self.ptr) as usize } - } - - #[inline] - pub fn bind_parameter_index(&self, name: &str) -> Option { - self.cache.get_or_insert_with(name, |param_cstr| { - let r = unsafe { ffi::sqlite3_bind_parameter_index(self.ptr, param_cstr.as_ptr()) }; - match r { - 0 => None, - i => Some(i as usize), - } - }) - } - - #[inline] - pub fn bind_parameter_name(&self, index: i32) -> Option<&CStr> { - unsafe { - let name = ffi::sqlite3_bind_parameter_name(self.ptr, index); - if name.is_null() { - None - } else { - Some(CStr::from_ptr(name)) - } - } - } - - #[inline] - pub fn clear_bindings(&mut self) { - unsafe { - ffi::sqlite3_clear_bindings(self.ptr); - } // rc is always SQLITE_OK - } - - #[inline] - pub fn sql(&self) -> Option<&CStr> { - if self.ptr.is_null() { - None - } else { - Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.ptr)) }) - } - } - - #[inline] - pub fn finalize(mut self) -> c_int { - self.finalize_() - } - - #[inline] - fn finalize_(&mut self) -> c_int { - let r = unsafe { ffi::sqlite3_finalize(self.ptr) }; - self.ptr = ptr::null_mut(); - r - } - - // does not work for PRAGMA - #[inline] - pub fn readonly(&self) -> bool { - unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 } - } - - #[inline] - pub(crate) fn expanded_sql(&self) -> Option { - unsafe { expanded_sql(self.ptr) } - } - - #[inline] - pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 { - unsafe { stmt_status(self.ptr, status, reset) } - } - - #[inline] - pub fn is_explain(&self) -> i32 { - unsafe { ffi::sqlite3_stmt_isexplain(self.ptr) } - } - - // TODO sqlite3_normalized_sql (https://sqlite.org/c3ref/expanded_sql.html) // 3.27.0 + SQLITE_ENABLE_NORMALIZE -} - -#[inline] -pub(crate) unsafe fn expanded_sql(ptr: *mut ffi::sqlite3_stmt) -> Option { - SqliteMallocString::from_raw(ffi::sqlite3_expanded_sql(ptr)) -} -#[inline] -pub(crate) unsafe fn stmt_status( - ptr: *mut ffi::sqlite3_stmt, - status: StatementStatus, - reset: bool, -) -> i32 { - assert!(!ptr.is_null()); - ffi::sqlite3_stmt_status(ptr, status as i32, reset as i32) -} - -impl Drop for RawStatement { - fn drop(&mut self) { - self.finalize_(); - } -} diff --git a/vendor/rusqlite/src/row.rs b/vendor/rusqlite/src/row.rs deleted file mode 100644 index 277927e..0000000 --- a/vendor/rusqlite/src/row.rs +++ /dev/null @@ -1,680 +0,0 @@ -use fallible_iterator::FallibleIterator; -use fallible_streaming_iterator::FallibleStreamingIterator; -use std::convert; - -use super::{Error, Result, Statement}; -use crate::types::{FromSql, FromSqlError, ValueRef}; - -/// A handle (lazy fallible streaming iterator) for the resulting rows of a query. -#[must_use = "Rows is lazy and will do nothing unless consumed"] -pub struct Rows<'stmt> { - pub(crate) stmt: Option<&'stmt Statement<'stmt>>, - row: Option>, -} - -impl<'stmt> Rows<'stmt> { - #[inline] - fn reset(&mut self) -> Result<()> { - if let Some(stmt) = self.stmt.take() { - stmt.reset() - } else { - Ok(()) - } - } - - /// Attempt to get the next row from the query. Returns `Ok(Some(Row))` if - /// there is another row, `Err(...)` if there was an error - /// getting the next row, and `Ok(None)` if all rows have been retrieved. - /// - /// ## Note - /// - /// This interface is not compatible with Rust's `Iterator` trait, because - /// the lifetime of the returned row is tied to the lifetime of `self`. - /// This is a fallible "streaming iterator". For a more natural interface, - /// consider using [`query_map`](Statement::query_map) or - /// [`query_and_then`](Statement::query_and_then) instead, which - /// return types that implement `Iterator`. - #[expect(clippy::should_implement_trait)] // cannot implement Iterator - #[inline] - pub fn next(&mut self) -> Result>> { - self.advance()?; - 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}; - /// fn query(stmt: &mut Statement) -> Result> { - /// let rows = stmt.query([])?; - /// rows.map(|r| r.get(0)).collect() - /// } - /// ``` - // FIXME Hide FallibleStreamingIterator::map - #[inline] - pub fn map(self, f: F) -> Map<'stmt, F> - where - F: FnMut(&Row<'_>) -> Result, - { - Map { rows: self, f } - } - - /// Map over this `Rows`, converting it to a [`MappedRows`], which - /// implements `Iterator`. - #[inline] - pub fn mapped(self, f: F) -> MappedRows<'stmt, F> - where - F: FnMut(&Row<'_>) -> Result, - { - MappedRows { rows: self, map: f } - } - - /// Map over this `Rows` with a fallible function, converting it to a - /// [`AndThenRows`], which implements `Iterator` (instead of - /// `FallibleStreamingIterator`). - #[inline] - pub fn and_then(self, f: F) -> AndThenRows<'stmt, F> - where - F: FnMut(&Row<'_>) -> Result, - { - AndThenRows { rows: self, map: f } - } - - /// Give access to the underlying statement - #[must_use] - pub fn as_ref(&self) -> Option<&Statement<'stmt>> { - self.stmt - } -} - -impl<'stmt> Rows<'stmt> { - #[inline] - pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Self { - Rows { - stmt: Some(stmt), - row: None, - } - } - - #[inline] - pub(crate) fn get_expected_row(&mut self) -> Result<&Row<'stmt>> { - match self.next()? { - Some(row) => Ok(row), - None => Err(Error::QueryReturnedNoRows), - } - } -} - -impl Drop for Rows<'_> { - #[expect(unused_must_use)] - #[inline] - fn drop(&mut self) { - self.reset(); - } -} - -/// `F` is used to transform the _streaming_ iterator into a _fallible_ -/// iterator. -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct Map<'stmt, F> { - rows: Rows<'stmt>, - f: F, -} - -impl FallibleIterator for Map<'_, F> -where - F: FnMut(&Row<'_>) -> Result, -{ - type Item = B; - type Error = Error; - - #[inline] - fn next(&mut self) -> Result> { - match self.rows.next()? { - Some(v) => Ok(Some((self.f)(v)?)), - None => Ok(None), - } - } -} - -/// An iterator over the mapped resulting rows of a query. -/// -/// `F` is used to transform the _streaming_ iterator into a _standard_ -/// iterator. -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct MappedRows<'stmt, F> { - rows: Rows<'stmt>, - map: F, -} - -impl Iterator for MappedRows<'_, F> -where - F: FnMut(&Row<'_>) -> Result, -{ - type Item = Result; - - #[inline] - fn next(&mut self) -> Option> { - let map = &mut self.map; - self.rows - .next() - .transpose() - .map(|row_result| row_result.and_then(map)) - } -} - -/// An iterator over the mapped resulting rows of a query, with an Error type -/// unifying with Error. -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct AndThenRows<'stmt, F> { - rows: Rows<'stmt>, - map: F, -} - -impl Iterator for AndThenRows<'_, F> -where - E: From, - F: FnMut(&Row<'_>) -> Result, -{ - type Item = Result; - - #[inline] - fn next(&mut self) -> Option { - let map = &mut self.map; - self.rows - .next() - .transpose() - .map(|row_result| row_result.map_err(E::from).and_then(map)) - } -} - -/// `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}; -/// fn query(stmt: &mut Statement) -> Result<()> { -/// let mut rows = stmt.query([])?; -/// while let Some(row) = rows.next()? { -/// // scan columns value -/// } -/// Ok(()) -/// } -/// ``` -impl<'stmt> FallibleStreamingIterator for Rows<'stmt> { - type Item = Row<'stmt>; - type Error = Error; - - #[inline] - fn advance(&mut self) -> Result<()> { - if let Some(stmt) = self.stmt { - match stmt.step() { - Ok(true) => { - self.row = Some(Row { stmt }); - Ok(()) - } - Ok(false) => { - let r = self.reset(); - self.row = None; - r - } - Err(e) => { - let _ = self.reset(); // prevents infinite loop on error - self.row = None; - Err(e) - } - } - } else { - self.row = None; - Ok(()) - } - } - - #[inline] - fn get(&self) -> Option<&Row<'stmt>> { - self.row.as_ref() - } -} - -/// A single result row of a query. -pub struct Row<'stmt> { - pub(crate) stmt: &'stmt Statement<'stmt>, -} - -impl Row<'_> { - /// Get the value of a particular column of the result row. - /// - /// # Panics - /// - /// Panics if calling [`row.get(idx)`](Row::get) would return an error, - /// including: - /// - /// * If the underlying SQLite column type is not a valid type as a source - /// for `T` - /// * If the underlying SQLite integral value is outside the range - /// representable by `T` - /// * If `idx` is outside the range of columns in the returned query - #[track_caller] - pub fn get_unwrap(&self, idx: I) -> T { - self.get(idx).unwrap() - } - - /// Get the value of a particular column of the result row. - /// - /// ## Failure - /// - /// Returns an `Error::InvalidColumnType` if the underlying SQLite column - /// type is not a valid type as a source for `T`. - /// - /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid - /// column range for this row. - /// - /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column - /// name for this row. - /// - /// If the result type is i128 (which requires the `i128_blob` feature to be - /// enabled), and the underlying SQLite column is a blob whose size is not - /// 16 bytes, `Error::InvalidColumnType` will also be returned. - #[track_caller] - pub fn get(&self, idx: I) -> Result { - let idx = 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, - self.stmt.column_name_unwrap(idx).into(), - value.data_type(), - ), - FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i), - FromSqlError::Utf8Error(err) => Error::Utf8Error(idx, err), - FromSqlError::Other(err) => { - Error::FromSqlConversionFailure(idx, value.data_type(), err) - } - FromSqlError::InvalidBlobSize { .. } => { - Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err)) - } - }) - } - - /// Get the value of a particular column of the result row as a `ValueRef`, - /// allowing data to be read out of a row without copying. - /// - /// This `ValueRef` is valid only as long as this Row, which is enforced by - /// its lifetime. This means that while this method is completely safe, - /// it can be somewhat difficult to use, and most callers will be better - /// served by [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap). - /// - /// ## Failure - /// - /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid - /// column range for this row. - /// - /// Returns an `Error::InvalidColumnName` if `idx` is not a valid column - /// name for this row. - pub fn get_ref(&self, idx: I) -> Result> { - let idx = idx.idx(self.stmt)?; - // Narrowing from `ValueRef<'stmt>` (which `self.stmt.value_ref(idx)` - // returns) to `ValueRef<'a>` is needed because it's only valid until - // the next call to sqlite3_step. - let val_ref = self.stmt.value_ref(idx); - Ok(val_ref) - } - - /// Get the value of a particular column of the result row as a `ValueRef`, - /// allowing data to be read out of a row without copying. - /// - /// This `ValueRef` is valid only as long as this Row, which is enforced by - /// its lifetime. This means that while this method is completely safe, - /// it can be difficult to use, and most callers will be better served by - /// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap). - /// - /// # Panics - /// - /// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an - /// error, including: - /// - /// * If `idx` is outside the range of columns in the returned query. - /// * If `idx` is not a valid column name for this row. - #[track_caller] - pub fn get_ref_unwrap(&self, idx: I) -> ValueRef<'_> { - self.get_ref(idx).unwrap() - } - - /// Return raw pointer at `idx` - /// # Safety - /// This function is unsafe because it uses raw pointer and cast - #[cfg(feature = "pointer")] - pub unsafe fn get_pointer( - &self, - idx: I, - ptr_type: &'static std::ffi::CStr, - ) -> Result> { - let idx = idx.idx(self.stmt)?; - debug_assert_eq!(self.stmt.stmt.column_type(idx), super::ffi::SQLITE_NULL); - let sv = super::ffi::sqlite3_column_value(self.stmt.stmt.ptr(), idx as std::ffi::c_int); - Ok(if sv.is_null() { - None - } else { - super::ffi::sqlite3_value_pointer(sv, ptr_type.as_ptr()) - .cast::() - .as_ref() - }) - } -} - -impl<'stmt> AsRef> for Row<'stmt> { - fn as_ref(&self) -> &Statement<'stmt> { - self.stmt - } -} - -/// Debug `Row` like an ordered `Map, Result<(Type, ValueRef)>>` -/// with column name as key except that for `Type::Blob` only its size is -/// printed (not its content). -impl std::fmt::Debug for Row<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut dm = f.debug_map(); - for c in 0..self.stmt.column_count() { - let name = self.stmt.column_name(c).expect("valid column index"); - dm.key(&name); - let value = self.get_ref(c); - match value { - Ok(value) => { - let dt = value.data_type(); - match value { - ValueRef::Null => { - dm.value(&(dt, ())); - } - ValueRef::Integer(i) => { - dm.value(&(dt, i)); - } - ValueRef::Real(f) => { - dm.value(&(dt, f)); - } - ValueRef::Text(s) => { - dm.value(&(dt, String::from_utf8_lossy(s))); - } - ValueRef::Blob(b) => { - dm.value(&(dt, b.len())); - } - } - } - Err(ref _err) => { - dm.value(&value); - } - } - } - dm.finish() - } -} - -mod sealed { - /// This trait exists just to ensure that the only impls of `trait RowIndex` - /// that are allowed are ones in this crate. - pub trait Sealed {} - impl Sealed for usize {} - impl Sealed for &str {} -} - -/// A trait implemented by types that can index into columns of a row. -/// -/// It is only implemented for `usize` and `&str`. -pub trait RowIndex: sealed::Sealed { - /// Returns the index of the appropriate column, or `Error` if no such - /// column exists. - fn idx(&self, stmt: &Statement<'_>) -> Result; -} - -impl RowIndex for usize { - #[inline] - fn idx(&self, stmt: &Statement<'_>) -> Result { - if *self >= stmt.column_count() { - Err(Error::InvalidColumnIndex(*self)) - } else { - Ok(*self) - } - } -} - -impl RowIndex for &'_ str { - #[inline] - fn idx(&self, stmt: &Statement<'_>) -> Result { - 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 { - let mut index = 0; - $( - #[expect(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 { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result}; - - #[test] - fn test_try_from_row_for_tuple_1() -> Result<()> { - use crate::ToSql; - use std::convert::TryFrom; - - let conn = Connection::open_in_memory()?; - conn.execute( - "CREATE TABLE test (a INTEGER)", - crate::params_from_iter(std::iter::empty::<&dyn ToSql>()), - )?; - conn.execute("INSERT INTO test VALUES (42)", [])?; - let val = conn.query_row("SELECT a FROM test", [], |row| <(u32,)>::try_from(row))?; - assert_eq!(val, (42,)); - let fail = conn.query_row("SELECT a FROM test", [], |row| <(u32, u32)>::try_from(row)); - fail.unwrap_err(); - Ok(()) - } - - #[test] - fn test_try_from_row_for_tuple_2() -> Result<()> { - use std::convert::TryFrom; - - let conn = Connection::open_in_memory()?; - conn.execute("CREATE TABLE test (a INTEGER, b INTEGER)", [])?; - conn.execute("INSERT INTO test VALUES (42, 47)", [])?; - let val = conn.query_row("SELECT a, b FROM test", [], |row| { - <(u32, u32)>::try_from(row) - })?; - assert_eq!(val, (42, 47)); - let fail = conn.query_row("SELECT a, b FROM test", [], |row| { - <(u32, u32, u32)>::try_from(row) - }); - fail.unwrap_err(); - Ok(()) - } - - #[test] - fn test_try_from_row_for_tuple_16() -> Result<()> { - 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()?; - conn.execute(create_table, [])?; - conn.execute(insert_values, [])?; - let val = conn.query_row("SELECT * FROM test", [], |row| BigTuple::try_from(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 - Ok(()) - } - - #[test] - #[cfg(feature = "bundled")] - fn pathological_case() -> Result<()> { - let conn = Connection::open_in_memory()?; - conn.execute_batch( - "CREATE TABLE foo(x); - CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;", - )?; - let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?; - { - let iterator_count = stmt.query_map([], |_| Ok(()))?.count(); - assert_eq!(1, iterator_count); // should be 0 - use fallible_streaming_iterator::FallibleStreamingIterator; - let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0); - assert_eq!(0, fallible_iterator_count); - } - { - let iterator_last = stmt.query_map([], |_| Ok(()))?.last(); - assert!(iterator_last.is_some()); // should be none - use fallible_iterator::FallibleIterator; - let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last(); - assert!(fallible_iterator_last.is_err()); - } - Ok(()) - } - - #[test] - fn as_ref() -> Result<()> { - let conn = Connection::open_in_memory()?; - let mut stmt = conn.prepare("SELECT 'Lisa' as name, 1 as id")?; - let rows = stmt.query([])?; - assert_eq!(rows.as_ref().unwrap().column_count(), 2); - Ok(()) - } - - #[test] - fn debug() -> Result<()> { - let conn = Connection::open_in_memory()?; - let mut stmt = conn.prepare( - "SELECT 'Lisa' as name, 1 as id, 3.14 as pi, X'53514C697465' as blob, NULL as void", - )?; - let mut rows = stmt.query([])?; - let row = rows.next()?.unwrap(); - let s = format!("{row:?}"); - assert_eq!( - s, - r#"{"name": (Text, "Lisa"), "id": (Integer, 1), "pi": (Real, 3.14), "blob": (Blob, 6), "void": (Null, ())}"# - ); - Ok(()) - } - - #[test] - #[cfg(feature = "pointer")] - fn test_pointer() -> Result<()> { - use crate::ffi::fts5_api; - use crate::types::ToSqlOutput; - const PTR_TYPE: &std::ffi::CStr = c"fts5_api_ptr"; - let p_ret: *mut fts5_api = std::ptr::null_mut(); - let ptr = ToSqlOutput::Pointer((&p_ret as *const *mut fts5_api as _, PTR_TYPE, None)); - let db = Connection::open_in_memory()?; - db.query_row("SELECT fts5(?)", [ptr], |_| Ok(()))?; - assert!(!p_ret.is_null()); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/serialize.rs b/vendor/rusqlite/src/serialize.rs deleted file mode 100644 index df04ab9..0000000 --- a/vendor/rusqlite/src/serialize.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Serialize a database. -use std::marker::PhantomData; -use std::ops::Deref; -use std::ptr::NonNull; - -use crate::error::{error_from_handle, error_from_sqlite_code}; -use crate::ffi; -use crate::{Connection, Error, Name, Result}; - -/// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database -pub struct SharedData<'conn> { - phantom: PhantomData<&'conn Connection>, - ptr: NonNull, - sz: usize, -} - -/// Owned serialized database -pub struct OwnedData { - ptr: NonNull, - sz: usize, -} - -impl OwnedData { - /// # Safety - /// - /// Caller must be certain that `ptr` is allocated by `sqlite3_malloc64`. - pub unsafe fn from_raw_nonnull(ptr: NonNull, sz: usize) -> Self { - Self { ptr, sz } - } - - fn into_raw(self) -> (*mut u8, usize) { - let raw = (self.ptr.as_ptr(), self.sz); - std::mem::forget(self); - raw - } -} - -impl Drop for OwnedData { - fn drop(&mut self) { - unsafe { - ffi::sqlite3_free(self.ptr.as_ptr().cast()); - } - } -} - -/// Serialized database -pub enum Data<'conn> { - /// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database - Shared(SharedData<'conn>), - /// Owned serialized database - Owned(OwnedData), -} - -impl Deref for Data<'_> { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - let (ptr, sz) = match self { - Data::Owned(OwnedData { ptr, sz }) => (ptr.as_ptr(), *sz), - Data::Shared(SharedData { ptr, sz, .. }) => (ptr.as_ptr(), *sz), - }; - unsafe { std::slice::from_raw_parts(ptr, sz) } - } -} - -impl Connection { - /// Serialize a database. - pub fn serialize(&self, schema: N) -> Result> { - let schema = schema.as_cstr()?; - let mut sz = 0; - let mut ptr: *mut u8 = unsafe { - ffi::sqlite3_serialize( - self.handle(), - schema.as_ptr(), - &mut sz, - ffi::SQLITE_SERIALIZE_NOCOPY, - ) - }; - Ok(if ptr.is_null() { - ptr = unsafe { ffi::sqlite3_serialize(self.handle(), schema.as_ptr(), &mut sz, 0) }; - if ptr.is_null() { - return Err(unsafe { error_from_handle(self.handle(), ffi::SQLITE_NOMEM) }); - } - Data::Owned(OwnedData { - ptr: NonNull::new(ptr).unwrap(), - sz: sz.try_into().unwrap(), - }) - } else { - // shared buffer - Data::Shared(SharedData { - ptr: NonNull::new(ptr).unwrap(), - sz: sz.try_into().unwrap(), - phantom: PhantomData, - }) - }) - } - - /// Deserialize from stream - pub fn deserialize_read_exact( - &mut self, - schema: N, - mut read: R, - sz: usize, - read_only: bool, - ) -> Result<()> { - let ptr = unsafe { ffi::sqlite3_malloc64(sz.try_into().unwrap()) }.cast::(); - if ptr.is_null() { - return Err(error_from_sqlite_code(ffi::SQLITE_NOMEM, None)); - } - let buf = unsafe { std::slice::from_raw_parts_mut(ptr, sz) }; - read.read_exact(buf).map_err(|e| { - Error::SqliteFailure( - ffi::Error { - code: ffi::ErrorCode::CannotOpen, - extended_code: ffi::SQLITE_IOERR, - }, - Some(format!("{e}")), - ) - })?; - let ptr = NonNull::new(ptr).unwrap(); - let data = unsafe { OwnedData::from_raw_nonnull(ptr, sz) }; - self.deserialize(schema, data, read_only) - } - - /// Deserialize `include_bytes` as a read only database - pub fn deserialize_bytes(&mut self, schema: N, data: &'static [u8]) -> Result<()> { - let sz = data.len().try_into().unwrap(); - self.deserialize_( - schema, - data.as_ptr() as *mut _, - sz, - ffi::SQLITE_DESERIALIZE_READONLY, - ) - } - - /// Deserialize a database. - pub fn deserialize( - &mut self, - schema: N, - data: OwnedData, - read_only: bool, - ) -> Result<()> { - let (data, sz) = data.into_raw(); - let sz = sz.try_into().unwrap(); - let flags = if read_only { - ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_READONLY - } else { - ffi::SQLITE_DESERIALIZE_FREEONCLOSE | ffi::SQLITE_DESERIALIZE_RESIZEABLE - }; - self.deserialize_(schema, data, sz, flags) - /* TODO - if let Some(mxSize) = mxSize { - unsafe { - ffi::sqlite3_file_control( - self.handle(), - schema.as_ptr(), - ffi::SQLITE_FCNTL_SIZE_LIMIT, - &mut mxSize, - ) - }; - }*/ - } - - fn deserialize_( - &mut self, - schema: N, - data: *mut u8, - sz: ffi::sqlite_int64, - flags: std::ffi::c_uint, - ) -> Result<()> { - let schema = schema.as_cstr()?; - let rc = unsafe { - ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags) - }; - if rc != ffi::SQLITE_OK { - return Err(unsafe { error_from_handle(self.handle(), rc) }); - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::*; - use crate::MAIN_DB; - - #[test] - fn serialize() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE x AS SELECT 'data'")?; - let data = db.serialize(MAIN_DB)?; - let Data::Owned(data) = data else { - panic!("expected OwnedData") - }; - assert!(data.sz > 0); - Ok(()) - } - - #[test] - fn deserialize_read_exact() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE x AS SELECT 'data'")?; - let data = db.serialize(MAIN_DB)?; - - let mut dst = Connection::open_in_memory()?; - let read = data.deref(); - dst.deserialize_read_exact(MAIN_DB, read, read.len(), false)?; - dst.execute("DELETE FROM x", [])?; - Ok(()) - } - - #[test] - fn deserialize_bytes() -> Result<()> { - let data = b""; - let mut dst = Connection::open_in_memory()?; - dst.deserialize_bytes(MAIN_DB, data)?; - Ok(()) - } - - #[test] - fn deserialize() -> Result<()> { - let src = Connection::open_in_memory()?; - src.execute_batch("CREATE TABLE x AS SELECT 'data'")?; - let data = src.serialize(MAIN_DB)?; - let Data::Owned(data) = data else { - panic!("expected OwnedData") - }; - - let mut dst = Connection::open_in_memory()?; - dst.deserialize(MAIN_DB, data, false)?; - dst.execute("DELETE FROM x", [])?; - Ok(()) - } -} diff --git a/vendor/rusqlite/src/session.rs b/vendor/rusqlite/src/session.rs deleted file mode 100644 index bf72b70..0000000 --- a/vendor/rusqlite/src/session.rs +++ /dev/null @@ -1,963 +0,0 @@ -//! [Session Extension](https://sqlite.org/sessionintro.html) -#![expect(non_camel_case_types)] - -use std::ffi::{c_char, c_int, c_uchar, c_void, CStr}; -use std::io::{Read, Write}; -use std::marker::PhantomData; -use std::panic::catch_unwind; -use std::ptr; -use std::slice::{from_raw_parts, from_raw_parts_mut}; - -use fallible_streaming_iterator::FallibleStreamingIterator; - -use crate::error::{check, error_from_sqlite_code, Error}; -use crate::ffi; -use crate::hooks::Action; -use crate::types::ValueRef; -use crate::{errmsg_to_string, Connection, Name, Result, MAIN_DB}; - -// https://sqlite.org/session.html - -type Filter = Option bool>>; - -/// 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 Connection>, - s: *mut ffi::sqlite3_session, - filter: Filter, -} - -impl Session<'_> { - /// Create a new session object - #[inline] - pub fn new(db: &Connection) -> Result> { - Session::new_with_name(db, MAIN_DB) - } - - /// Create a new session object - #[inline] - pub fn new_with_name(db: &Connection, name: N) -> Result> { - let name = name.as_cstr()?; - - let db = db.db.borrow_mut().db; - - let mut s: *mut ffi::sqlite3_session = ptr::null_mut(); - check(unsafe { ffi::sqlite3session_create(db, name.as_ptr(), &mut s) })?; - - Ok(Session { - phantom: PhantomData, - s, - filter: None, - }) - } - - /// Set a table filter - pub fn table_filter(&mut self, filter: Option) - where - F: Fn(&str) -> bool + Send + 'static, - { - unsafe extern "C" fn call_boxed_closure( - p_arg: *mut c_void, - tbl_str: *const c_char, - ) -> c_int - where - F: Fn(&str) -> bool, - { - let tbl_name = CStr::from_ptr(tbl_str).to_str(); - c_int::from( - catch_unwind(|| { - let boxed_filter: *mut F = p_arg.cast::(); - (*boxed_filter)(tbl_name.expect("non-utf8 table name")) - }) - .unwrap_or_default(), - ) - } - - match filter { - Some(filter) => { - let boxed_filter = Box::new(filter); - unsafe { - ffi::sqlite3session_table_filter( - self.s, - Some(call_boxed_closure::), - &*boxed_filter as *const F as *mut _, - ); - } - self.filter = Some(boxed_filter); - } - _ => { - unsafe { ffi::sqlite3session_table_filter(self.s, None, ptr::null_mut()) } - self.filter = None; - } - }; - } - - /// Attach a table. `None` means all tables. - pub fn attach(&mut self, table: Option) -> Result<()> { - let cs = table.as_ref().map(N::as_cstr).transpose()?; - let table = cs.as_ref().map(|s| s.as_ptr()).unwrap_or(ptr::null()); - check(unsafe { ffi::sqlite3session_attach(self.s, table) }) - } - - /// Generate a Changeset - pub fn changeset(&mut self) -> Result { - let mut n = 0; - let mut cs: *mut c_void = ptr::null_mut(); - check(unsafe { ffi::sqlite3session_changeset(self.s, &mut n, &mut cs) })?; - Ok(Changeset { cs, n }) - } - - /// Write the set of changes represented by this session to `output`. - #[inline] - pub fn changeset_strm(&mut self, output: &mut dyn Write) -> Result<()> { - let output_ref = &output; - check(unsafe { - ffi::sqlite3session_changeset_strm( - self.s, - Some(x_output), - output_ref as *const &mut dyn Write as *mut c_void, - ) - }) - } - - /// Generate a Patchset - #[inline] - pub fn patchset(&mut self) -> Result { - let mut n = 0; - let mut ps: *mut c_void = ptr::null_mut(); - check(unsafe { ffi::sqlite3session_patchset(self.s, &mut n, &mut ps) })?; - // TODO Validate: same struct - Ok(Changeset { cs: ps, n }) - } - - /// Write the set of patches represented by this session to `output`. - #[inline] - pub fn patchset_strm(&mut self, output: &mut dyn Write) -> Result<()> { - let output_ref = &output; - check(unsafe { - ffi::sqlite3session_patchset_strm( - self.s, - Some(x_output), - output_ref as *const &mut dyn Write as *mut c_void, - ) - }) - } - - /// Load the difference between tables. - pub fn diff(&mut self, from: N, table: N) -> Result<()> { - let from = from.as_cstr()?; - let table = table.as_cstr()?; - let table = table.as_ptr(); - unsafe { - let mut errmsg = ptr::null_mut(); - let r = - ffi::sqlite3session_diff(self.s, from.as_ptr(), table, &mut errmsg as *mut *mut _); - if r != ffi::SQLITE_OK { - let errmsg: *mut c_char = errmsg; - let message = errmsg_to_string(&*errmsg); - ffi::sqlite3_free(errmsg as *mut c_void); - return Err(error_from_sqlite_code(r, Some(message))); - } - } - Ok(()) - } - - /// Test if a changeset has recorded any changes - #[inline] - pub fn is_empty(&self) -> bool { - unsafe { ffi::sqlite3session_isempty(self.s) != 0 } - } - - /// Query the current state of the session - #[inline] - pub fn is_enabled(&self) -> bool { - unsafe { ffi::sqlite3session_enable(self.s, -1) != 0 } - } - - /// Enable or disable the recording of changes - #[inline] - pub fn set_enabled(&mut self, enabled: bool) { - unsafe { - ffi::sqlite3session_enable(self.s, c_int::from(enabled)); - } - } - - /// Query the current state of the indirect flag - #[inline] - pub fn is_indirect(&self) -> bool { - unsafe { ffi::sqlite3session_indirect(self.s, -1) != 0 } - } - - /// Set or clear the indirect change flag - #[inline] - pub fn set_indirect(&mut self, indirect: bool) { - unsafe { - ffi::sqlite3session_indirect(self.s, c_int::from(indirect)); - } - } -} - -impl Drop for Session<'_> { - #[inline] - fn drop(&mut self) { - if self.filter.is_some() { - self.table_filter(None:: bool>); - } - unsafe { ffi::sqlite3session_delete(self.s) }; - } -} - -/// Invert a changeset -#[inline] -pub fn invert_strm(input: &mut dyn Read, output: &mut dyn Write) -> Result<()> { - let input_ref = &input; - let output_ref = &output; - check(unsafe { - ffi::sqlite3changeset_invert_strm( - Some(x_input), - input_ref as *const &mut dyn Read as *mut c_void, - Some(x_output), - output_ref as *const &mut dyn Write as *mut c_void, - ) - }) -} - -/// Combine two changesets -#[inline] -pub fn concat_strm( - input_a: &mut dyn Read, - input_b: &mut dyn Read, - output: &mut dyn Write, -) -> Result<()> { - let input_a_ref = &input_a; - let input_b_ref = &input_b; - let output_ref = &output; - check(unsafe { - ffi::sqlite3changeset_concat_strm( - Some(x_input), - input_a_ref as *const &mut dyn Read as *mut c_void, - Some(x_input), - input_b_ref as *const &mut dyn Read as *mut c_void, - Some(x_output), - output_ref as *const &mut dyn Write as *mut c_void, - ) - }) -} - -/// Changeset or Patchset -pub struct Changeset { - cs: *mut c_void, - n: c_int, -} - -impl Changeset { - /// Invert a changeset - #[inline] - pub fn invert(&self) -> Result { - let mut n = 0; - let mut cs = ptr::null_mut(); - check(unsafe { - ffi::sqlite3changeset_invert(self.n, self.cs, &mut n, &mut cs as *mut *mut _) - })?; - Ok(Changeset { cs, n }) - } - - /// Create an iterator to traverse a changeset - #[inline] - pub fn iter(&self) -> Result> { - let mut it = ptr::null_mut(); - check(unsafe { ffi::sqlite3changeset_start(&mut it as *mut *mut _, self.n, self.cs) })?; - Ok(ChangesetIter { - phantom: PhantomData, - it, - item: None, - }) - } - - /// Concatenate two changeset objects - #[inline] - pub fn concat(a: &Changeset, b: &Changeset) -> Result { - let mut n = 0; - let mut cs = ptr::null_mut(); - check(unsafe { - ffi::sqlite3changeset_concat(a.n, a.cs, b.n, b.cs, &mut n, &mut cs as *mut *mut _) - })?; - Ok(Changeset { cs, n }) - } -} - -impl Drop for Changeset { - #[inline] - fn drop(&mut self) { - unsafe { - ffi::sqlite3_free(self.cs); - } - } -} - -/// Cursor for iterating over the elements of a changeset -/// or patchset. -pub struct ChangesetIter<'changeset> { - phantom: PhantomData<&'changeset Changeset>, - it: *mut ffi::sqlite3_changeset_iter, - item: Option, -} - -impl ChangesetIter<'_> { - /// Create an iterator on `input` - #[inline] - pub fn start_strm<'input>(input: &&'input mut dyn Read) -> Result> { - let mut it = ptr::null_mut(); - check(unsafe { - ffi::sqlite3changeset_start_strm( - &mut it as *mut *mut _, - Some(x_input), - input as *const &mut dyn Read as *mut c_void, - ) - })?; - Ok(ChangesetIter { - phantom: PhantomData, - it, - item: None, - }) - } -} - -impl FallibleStreamingIterator for ChangesetIter<'_> { - type Error = Error; - type Item = ChangesetItem; - - #[inline] - fn advance(&mut self) -> Result<()> { - let rc = unsafe { ffi::sqlite3changeset_next(self.it) }; - match rc { - ffi::SQLITE_ROW => { - self.item = Some(ChangesetItem { it: self.it }); - Ok(()) - } - ffi::SQLITE_DONE => { - self.item = None; - Ok(()) - } - code => Err(error_from_sqlite_code(code, None)), - } - } - - #[inline] - fn get(&self) -> Option<&ChangesetItem> { - self.item.as_ref() - } -} - -/// Operation -pub struct Operation<'item> { - table_name: &'item str, - number_of_columns: i32, - code: Action, - indirect: bool, -} - -impl Operation<'_> { - /// Returns the table name. - #[inline] - pub fn table_name(&self) -> &str { - self.table_name - } - - /// Returns the number of columns in table - #[inline] - pub fn number_of_columns(&self) -> i32 { - self.number_of_columns - } - - /// Returns the action code. - #[inline] - pub fn code(&self) -> Action { - self.code - } - - /// Returns `true` for an 'indirect' change. - #[inline] - pub fn indirect(&self) -> bool { - self.indirect - } -} - -impl Drop for ChangesetIter<'_> { - #[inline] - fn drop(&mut self) { - unsafe { - ffi::sqlite3changeset_finalize(self.it); - } - } -} - -/// An item passed to a conflict-handler by -/// [`Connection::apply`](Connection::apply), or an item generated by -/// [`ChangesetIter::next`](ChangesetIter::next). -// TODO enum ? Delete, Insert, Update, ... -pub struct ChangesetItem { - it: *mut ffi::sqlite3_changeset_iter, -} - -impl ChangesetItem { - /// Obtain conflicting row values - /// - /// May only be called with an `SQLITE_CHANGESET_DATA` or - /// `SQLITE_CHANGESET_CONFLICT` conflict handler callback. - #[inline] - pub fn conflict(&self, col: usize) -> Result> { - unsafe { - let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut(); - check(ffi::sqlite3changeset_conflict( - self.it, - col as i32, - &mut p_value, - ))?; - if p_value.is_null() { - Err(Error::InvalidColumnIndex(col)) - } else { - Ok(ValueRef::from_value(p_value)) - } - } - } - - /// Determine the number of foreign key constraint violations - /// - /// May only be called with an `SQLITE_CHANGESET_FOREIGN_KEY` conflict - /// handler callback. - #[inline] - pub fn fk_conflicts(&self) -> Result { - unsafe { - let mut p_out = 0; - check(ffi::sqlite3changeset_fk_conflicts(self.it, &mut p_out))?; - Ok(p_out) - } - } - - /// Obtain new.* Values - /// - /// May only be called if the type of change is either `SQLITE_UPDATE` or - /// `SQLITE_INSERT`. - #[inline] - pub fn new_value(&self, col: usize) -> Result> { - unsafe { - let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut(); - check(ffi::sqlite3changeset_new(self.it, col as i32, &mut p_value))?; - if p_value.is_null() { - Err(Error::InvalidColumnIndex(col)) - } else { - Ok(ValueRef::from_value(p_value)) - } - } - } - - /// Obtain old.* Values - /// - /// May only be called if the type of change is either `SQLITE_DELETE` or - /// `SQLITE_UPDATE`. - #[inline] - pub fn old_value(&self, col: usize) -> Result> { - unsafe { - let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut(); - check(ffi::sqlite3changeset_old(self.it, col as i32, &mut p_value))?; - if p_value.is_null() { - Err(Error::InvalidColumnIndex(col)) - } else { - Ok(ValueRef::from_value(p_value)) - } - } - } - - /// Obtain the current operation - #[inline] - pub fn op(&self) -> Result> { - let mut number_of_columns = 0; - let mut code = 0; - let mut indirect = 0; - let tab = unsafe { - let mut pz_tab: *const c_char = ptr::null(); - check(ffi::sqlite3changeset_op( - self.it, - &mut pz_tab, - &mut number_of_columns, - &mut code, - &mut indirect, - ))?; - CStr::from_ptr(pz_tab) - }; - let table_name = tab.to_str()?; - Ok(Operation { - table_name, - number_of_columns, - code: Action::from(code), - indirect: indirect != 0, - }) - } - - /// Obtain the primary key definition of a table - #[inline] - pub fn pk(&self) -> Result<&[u8]> { - let mut number_of_columns = 0; - unsafe { - let mut pks: *mut c_uchar = ptr::null_mut(); - check(ffi::sqlite3changeset_pk( - self.it, - &mut pks, - &mut number_of_columns, - ))?; - Ok(from_raw_parts(pks, number_of_columns as usize)) - } - } -} - -/// Used to combine two or more changesets or -/// patchsets -pub struct Changegroup { - cg: *mut ffi::sqlite3_changegroup, -} - -impl Changegroup { - /// Create a new change group. - #[inline] - pub fn new() -> Result { - let mut cg = ptr::null_mut(); - check(unsafe { ffi::sqlite3changegroup_new(&mut cg) })?; - Ok(Changegroup { cg }) - } - - /// Add a changeset - #[inline] - pub fn add(&mut self, cs: &Changeset) -> Result<()> { - check(unsafe { ffi::sqlite3changegroup_add(self.cg, cs.n, cs.cs) }) - } - - /// Add a changeset read from `input` to this change group. - #[inline] - pub fn add_stream(&mut self, input: &mut dyn Read) -> Result<()> { - let input_ref = &input; - check(unsafe { - ffi::sqlite3changegroup_add_strm( - self.cg, - Some(x_input), - input_ref as *const &mut dyn Read as *mut c_void, - ) - }) - } - - /// Obtain a composite Changeset - #[inline] - pub fn output(&mut self) -> Result { - let mut n = 0; - let mut output: *mut c_void = ptr::null_mut(); - check(unsafe { ffi::sqlite3changegroup_output(self.cg, &mut n, &mut output) })?; - Ok(Changeset { cs: output, n }) - } - - /// Write the combined set of changes to `output`. - #[inline] - pub fn output_strm(&mut self, output: &mut dyn Write) -> Result<()> { - let output_ref = &output; - check(unsafe { - ffi::sqlite3changegroup_output_strm( - self.cg, - Some(x_output), - output_ref as *const &mut dyn Write as *mut c_void, - ) - }) - } -} - -impl Drop for Changegroup { - #[inline] - fn drop(&mut self) { - unsafe { - ffi::sqlite3changegroup_delete(self.cg); - } - } -} - -impl Connection { - /// Apply a changeset to a database - pub fn apply(&self, cs: &Changeset, filter: Option, conflict: C) -> Result<()> - where - F: Fn(&str) -> bool + Send + 'static, - C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static, - { - let db = self.db.borrow_mut().db; - - let filtered = filter.is_some(); - let tuple = &mut (filter, conflict); - check(unsafe { - if filtered { - ffi::sqlite3changeset_apply( - db, - cs.n, - cs.cs, - Some(call_filter::), - Some(call_conflict::), - tuple as *mut (Option, C) as *mut c_void, - ) - } else { - ffi::sqlite3changeset_apply( - db, - cs.n, - cs.cs, - None, - Some(call_conflict::), - tuple as *mut (Option, C) as *mut c_void, - ) - } - }) - } - - /// Apply a changeset to a database - pub fn apply_strm( - &self, - input: &mut dyn Read, - filter: Option, - conflict: C, - ) -> Result<()> - where - F: Fn(&str) -> bool + Send + 'static, - C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static, - { - let input_ref = &input; - let db = self.db.borrow_mut().db; - - let filtered = filter.is_some(); - let tuple = &mut (filter, conflict); - check(unsafe { - if filtered { - ffi::sqlite3changeset_apply_strm( - db, - Some(x_input), - input_ref as *const &mut dyn Read as *mut c_void, - Some(call_filter::), - Some(call_conflict::), - tuple as *mut (Option, C) as *mut c_void, - ) - } else { - ffi::sqlite3changeset_apply_strm( - db, - Some(x_input), - input_ref as *const &mut dyn Read as *mut c_void, - None, - Some(call_conflict::), - tuple as *mut (Option, C) as *mut c_void, - ) - } - }) - } -} - -/// 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, Eq)] -#[non_exhaustive] -pub enum ConflictType { - UNKNOWN = -1, - SQLITE_CHANGESET_DATA = ffi::SQLITE_CHANGESET_DATA, - SQLITE_CHANGESET_NOTFOUND = ffi::SQLITE_CHANGESET_NOTFOUND, - SQLITE_CHANGESET_CONFLICT = ffi::SQLITE_CHANGESET_CONFLICT, - SQLITE_CHANGESET_CONSTRAINT = ffi::SQLITE_CHANGESET_CONSTRAINT, - SQLITE_CHANGESET_FOREIGN_KEY = ffi::SQLITE_CHANGESET_FOREIGN_KEY, -} -impl From for ConflictType { - fn from(code: i32) -> ConflictType { - match code { - ffi::SQLITE_CHANGESET_DATA => ConflictType::SQLITE_CHANGESET_DATA, - ffi::SQLITE_CHANGESET_NOTFOUND => ConflictType::SQLITE_CHANGESET_NOTFOUND, - ffi::SQLITE_CHANGESET_CONFLICT => ConflictType::SQLITE_CHANGESET_CONFLICT, - ffi::SQLITE_CHANGESET_CONSTRAINT => ConflictType::SQLITE_CHANGESET_CONSTRAINT, - ffi::SQLITE_CHANGESET_FOREIGN_KEY => ConflictType::SQLITE_CHANGESET_FOREIGN_KEY, - _ => ConflictType::UNKNOWN, - } - } -} - -/// 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, Eq)] -#[non_exhaustive] -pub enum ConflictAction { - SQLITE_CHANGESET_OMIT = ffi::SQLITE_CHANGESET_OMIT, - SQLITE_CHANGESET_REPLACE = ffi::SQLITE_CHANGESET_REPLACE, - SQLITE_CHANGESET_ABORT = ffi::SQLITE_CHANGESET_ABORT, -} - -unsafe extern "C" fn call_filter(p_ctx: *mut c_void, tbl_str: *const c_char) -> c_int -where - F: Fn(&str) -> bool + Send + 'static, - C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static, -{ - let tbl_name = CStr::from_ptr(tbl_str).to_str(); - c_int::from( - catch_unwind(|| { - let tuple: *mut (Option, C) = p_ctx.cast::<(Option, C)>(); - if let Some(ref filter) = (*tuple).0 { - filter(tbl_name.expect("illegal table name")) - } else { - true - } - }) - .unwrap_or_default(), - ) -} - -unsafe extern "C" fn call_conflict( - p_ctx: *mut c_void, - e_conflict: c_int, - p: *mut ffi::sqlite3_changeset_iter, -) -> c_int -where - F: Fn(&str) -> bool + Send + 'static, - C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static, -{ - let conflict_type = ConflictType::from(e_conflict); - let item = ChangesetItem { it: p }; - if let Ok(action) = catch_unwind(|| { - let tuple: *mut (Option, C) = p_ctx.cast::<(Option, C)>(); - (*tuple).1(conflict_type, item) - }) { - action as c_int - } else { - ffi::SQLITE_CHANGESET_ABORT - } -} - -unsafe extern "C" fn x_input(p_in: *mut c_void, data: *mut c_void, len: *mut c_int) -> c_int { - if p_in.is_null() { - return ffi::SQLITE_MISUSE; - } - let bytes: &mut [u8] = from_raw_parts_mut(data as *mut u8, *len as usize); - let input = p_in as *mut &mut dyn Read; - match (*input).read(bytes) { - Ok(n) => { - *len = n as i32; // TODO Validate: n = 0 may not mean the reader will always no longer be able to - // produce bytes. - ffi::SQLITE_OK - } - Err(_) => ffi::SQLITE_IOERR_READ, // TODO check if err is a (ru)sqlite Error => propagate - } -} - -unsafe extern "C" fn x_output(p_out: *mut c_void, data: *const c_void, len: c_int) -> c_int { - if p_out.is_null() { - return ffi::SQLITE_MISUSE; - } - // The sessions module never invokes an xOutput callback with the third - // parameter set to a value less than or equal to zero. - let bytes: &[u8] = from_raw_parts(data as *const u8, len as usize); - let output = p_out as *mut &mut dyn Write; - match (*output).write_all(bytes) { - Ok(_) => ffi::SQLITE_OK, - Err(_) => ffi::SQLITE_IOERR_WRITE, // TODO check if err is a (ru)sqlite Error => propagate - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use fallible_streaming_iterator::FallibleStreamingIterator; - use std::io::Read; - use std::sync::atomic::{AtomicBool, Ordering}; - - use super::{Changeset, ChangesetIter, ConflictAction, ConflictType, Session}; - use crate::hooks::Action; - use crate::{Connection, Result}; - - fn one_changeset_insert() -> Result { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")?; - - let mut session = Session::new(&db)?; - assert!(session.is_empty()); - - session.attach::<&str>(None)?; - db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; - - session.changeset() - } - - fn one_changeset_update() -> Result { - let db = Connection::open_in_memory()?; - db.execute_batch( - "CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL, i INTEGER NOT NULL DEFAULT 0);", - )?; - db.execute_batch("INSERT INTO foo (t) VALUES ('bar');")?; - - let mut session = Session::new(&db)?; - session.attach::<&str>(None)?; - db.execute("UPDATE foo SET i=100 WHERE t='bar';", [])?; - - session.changeset() - } - - fn one_changeset_strm() -> Result> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")?; - - let mut session = Session::new(&db)?; - assert!(session.is_empty()); - - session.attach::<&str>(None)?; - db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; - - let mut output = Vec::new(); - session.changeset_strm(&mut output)?; - Ok(output) - } - - #[test] - fn test_changeset() -> Result<()> { - let changeset = one_changeset_insert()?; - let mut iter = changeset.iter()?; - let item = iter.next()?; - assert!(item.is_some()); - - let item = item.unwrap(); - let op = item.op()?; - assert_eq!("foo", op.table_name()); - assert_eq!(1, op.number_of_columns()); - assert_eq!(Action::SQLITE_INSERT, op.code()); - assert!(!op.indirect()); - - let pk = item.pk()?; - assert_eq!(&[1], pk); - - let new_value = item.new_value(0)?; - assert_eq!(Ok("bar"), new_value.as_str()); - Ok(()) - } - - #[test] - fn test_changeset_strm() -> Result<()> { - let output = one_changeset_strm()?; - assert!(!output.is_empty()); - assert_eq!(14, output.len()); - - let input: &mut dyn Read = &mut output.as_slice(); - let mut iter = ChangesetIter::start_strm(&input)?; - let item = iter.next()?; - assert!(item.is_some()); - Ok(()) - } - - #[test] - fn test_changeset_values() -> Result<()> { - let changeset = one_changeset_update()?; - let mut iter = changeset.iter()?; - let item = iter.next()?.unwrap(); - - let new_value = item.new_value(0); // unchanged - assert_eq!(Err(crate::Error::InvalidColumnIndex(0)), new_value); - let new_value = item.new_value(1)?; // updated - assert_eq!(Ok(100), new_value.as_i64()); - Ok(()) - } - - #[test] - fn test_changeset_apply() -> Result<()> { - let changeset = one_changeset_insert()?; - - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")?; - - static CALLED: AtomicBool = AtomicBool::new(false); - db.apply( - &changeset, - None:: bool>, - |_conflict_type, _item| { - CALLED.store(true, Ordering::Relaxed); - ConflictAction::SQLITE_CHANGESET_OMIT - }, - )?; - - assert!(!CALLED.load(Ordering::Relaxed)); - let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| { - row.get::<_, i32>(0) - })?; - assert_eq!(1, check); - - // conflict expected when same changeset applied again on the same db - db.apply( - &changeset, - None:: bool>, - |conflict_type, item| { - CALLED.store(true, Ordering::Relaxed); - assert_eq!(ConflictType::SQLITE_CHANGESET_CONFLICT, conflict_type); - let conflict = item.conflict(0).unwrap(); - assert_eq!(Ok("bar"), conflict.as_str()); - ConflictAction::SQLITE_CHANGESET_OMIT - }, - )?; - assert!(CALLED.load(Ordering::Relaxed)); - Ok(()) - } - - #[test] - fn test_changeset_apply_strm() -> Result<()> { - let output = one_changeset_strm()?; - - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")?; - - let mut input = output.as_slice(); - db.apply_strm( - &mut input, - None:: bool>, - |_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT, - )?; - - let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| { - row.get::<_, i32>(0) - })?; - assert_eq!(1, check); - Ok(()) - } - - #[test] - fn test_session_empty() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")?; - - let mut session = Session::new(&db)?; - assert!(session.is_empty()); - - session.attach::<&str>(None)?; - db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?; - - assert!(!session.is_empty()); - Ok(()) - } - - #[test] - fn test_session_set_enabled() -> Result<()> { - let db = Connection::open_in_memory()?; - - let mut session = Session::new(&db)?; - assert!(session.is_enabled()); - session.set_enabled(false); - assert!(!session.is_enabled()); - Ok(()) - } - - #[test] - fn test_session_set_indirect() -> Result<()> { - let db = Connection::open_in_memory()?; - - let mut session = Session::new(&db)?; - assert!(!session.is_indirect()); - session.set_indirect(true); - assert!(session.is_indirect()); - Ok(()) - } -} diff --git a/vendor/rusqlite/src/statement.rs b/vendor/rusqlite/src/statement.rs deleted file mode 100644 index 7b89d6c..0000000 --- a/vendor/rusqlite/src/statement.rs +++ /dev/null @@ -1,1396 +0,0 @@ -use std::ffi::{c_int, c_void}; -use std::slice::from_raw_parts; -use std::{fmt, mem, ptr, str}; - -use super::ffi; -use super::str_for_sqlite; -use super::{ - AndThenRows, Connection, Error, MappedRows, Params, RawStatement, Result, Row, Rows, ValueRef, -}; -use crate::bind::BindIndex; -use crate::types::{ToSql, ToSqlOutput}; - -/// A prepared statement. -pub struct Statement<'conn> { - pub(crate) conn: &'conn Connection, - pub(crate) stmt: RawStatement, -} - -impl Statement<'_> { - /// Execute the prepared statement. - /// - /// On success, returns the number of rows that were changed or inserted or - /// deleted (via `sqlite3_changes`). - /// - /// ## Example - /// - /// ### Use with positional parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, params}; - /// fn update_rows(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("UPDATE foo SET bar = ?1 WHERE qux = ?2")?; - /// // For a single parameter, or a parameter where all the values have - /// // the same type, just passing an array is simplest. - /// stmt.execute([2i32])?; - /// // The `rusqlite::params!` macro is mostly useful when the parameters do not - /// // all have the same type, or if there are more than 32 parameters - /// // at once, but it can be used in other cases. - /// stmt.execute(params![1i32])?; - /// // However, it's not required, many cases are fine as: - /// stmt.execute(&[&2i32])?; - /// // Or even: - /// stmt.execute([2i32])?; - /// // If you really want to, this is an option as well. - /// stmt.execute((2i32,))?; - /// Ok(()) - /// } - /// ``` - /// - /// #### Heterogeneous positional parameters - /// - /// ``` - /// use rusqlite::{Connection, Result}; - /// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> { - /// # // no need to do it for real. - /// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] } - /// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?1, ?2, ?3)"; - /// let mut stmt = conn.prepare_cached(query)?; - /// let hash: [u8; 32] = sha256(data); - /// // The easiest way to pass positional parameters of have several - /// // different types is by using a tuple. - /// stmt.execute((path, hash, data))?; - /// // Using the `params!` macro also works, and supports longer parameter lists: - /// stmt.execute(rusqlite::params![path, hash, data])?; - /// Ok(()) - /// } - /// # let c = Connection::open_in_memory().unwrap(); - /// # c.execute_batch("CREATE TABLE files(path TEXT PRIMARY KEY, hash BLOB, data BLOB)").unwrap(); - /// # store_file(&c, "foo/bar.txt", b"bibble").unwrap(); - /// # store_file(&c, "foo/baz.txt", b"bobble").unwrap(); - /// ``` - /// - /// ### Use with named parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, named_params}; - /// fn insert(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("INSERT INTO test (key, value) VALUES (:key, :value)")?; - /// // The `rusqlite::named_params!` macro (like `params!`) is useful for heterogeneous - /// // sets of parameters (where all parameters are not the same type), or for queries - /// // with many (more than 32) statically known parameters. - /// stmt.execute(named_params! { ":key": "one", ":val": 2 })?; - /// // However, named parameters can also be passed like: - /// stmt.execute(&[(":key", "three"), (":val", "four")])?; - /// // Or even: (note that a &T is required for the value type, currently) - /// stmt.execute(&[(":key", &100), (":val", &200)])?; - /// Ok(()) - /// } - /// ``` - /// - /// ### Use without parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, params}; - /// fn delete_all(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("DELETE FROM users")?; - /// stmt.execute([])?; - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails, the executed statement - /// returns rows (in which case `query` should be used instead), or the - /// underlying SQLite call fails. - #[inline] - pub fn execute(&mut self, params: P) -> Result { - params.__bind_in(self)?; - self.execute_with_bound_parameters() - } - - /// Execute an INSERT and return the ROWID. - /// - /// # Note - /// - /// This function is a convenience wrapper around - /// [`execute()`](Statement::execute) intended for queries that insert a - /// single item. It is possible to misuse this function in a way that it - /// cannot detect, such as by calling it on a statement which _updates_ - /// a single item rather than inserting one. Please don't do that. - /// - /// # Failure - /// - /// Will return `Err` if no row is inserted or many rows are inserted. - #[inline] - pub fn insert(&mut self, params: P) -> Result { - let changes = self.execute(params)?; - match changes { - 1 => Ok(self.conn.last_insert_rowid()), - _ => Err(Error::StatementChangedRows(changes)), - } - } - - /// Execute the prepared statement, returning a handle to the resulting - /// rows. - /// - /// Due to lifetime restrictions, the rows handle returned by `query` does - /// not implement the `Iterator` trait. Consider using - /// [`query_map`](Statement::query_map) or - /// [`query_and_then`](Statement::query_and_then) instead, which do. - /// - /// ## Example - /// - /// ### Use without parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = conn.prepare("SELECT name FROM people")?; - /// let mut rows = stmt.query([])?; - /// - /// let mut names = Vec::new(); - /// while let Some(row) = rows.next()? { - /// names.push(row.get(0)?); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// - /// ### Use with positional parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn query(conn: &Connection, name: &str) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?; - /// let mut rows = stmt.query(rusqlite::params![name])?; - /// while let Some(row) = rows.next()? { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - /// - /// Or, equivalently (but without the [`crate::params!`] macro). - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn query(conn: &Connection, name: &str) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?; - /// let mut rows = stmt.query([name])?; - /// while let Some(row) = rows.next()? { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - /// - /// ### Use with named parameters - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn query(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; - /// let mut rows = stmt.query(&[(":name", "one")])?; - /// while let Some(row) = rows.next()? { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - /// - /// Note, the `named_params!` macro is provided for syntactic convenience, - /// and so the above example could also be written as: - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, named_params}; - /// fn query(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?; - /// let mut rows = stmt.query(named_params! { ":name": "one" })?; - /// while let Some(row) = rows.next()? { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - /// - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - #[inline] - pub fn query(&mut self, params: P) -> Result> { - params.__bind_in(self)?; - Ok(Rows::new(self)) - } - - /// Executes the prepared statement and maps a function over the resulting - /// rows, returning an iterator over the mapped function results. - /// - /// `f` is used to transform the _streaming_ iterator into a _standard_ - /// iterator. - /// - /// This is equivalent to `stmt.query(params)?.mapped(f)`. - /// - /// ## Example - /// - /// ### Use with positional params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = conn.prepare("SELECT name FROM people")?; - /// let rows = stmt.query_map([], |row| row.get(0))?; - /// - /// let mut names = Vec::new(); - /// for name_result in rows { - /// names.push(name_result?); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// - /// ### Use with named params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?; - /// let rows = stmt.query_map(&[(":id", &"one")], |row| row.get(0))?; - /// - /// let mut names = Vec::new(); - /// for name_result in rows { - /// names.push(name_result?); - /// } - /// - /// Ok(names) - /// } - /// ``` - /// ## Failure - /// - /// Will return `Err` if binding parameters fails. - pub fn query_map(&mut self, params: P, f: F) -> Result> - where - P: Params, - F: FnMut(&Row<'_>) -> Result, - { - self.query(params).map(|rows| rows.mapped(f)) - } - - /// Executes the prepared statement and maps a function over the resulting - /// rows, where the function returns a `Result` with `Error` type - /// implementing `std::convert::From` (so errors can be unified). - /// - /// This is equivalent to `stmt.query(params)?.and_then(f)`. - /// - /// ## Example - /// - /// ### Use with named params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// struct Person { - /// name: String, - /// }; - /// - /// fn name_to_person(name: String) -> Result { - /// // ... check for valid name - /// Ok(Person { name }) - /// } - /// - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?; - /// let rows = stmt.query_and_then(&[(":id", "one")], |row| name_to_person(row.get(0)?))?; - /// - /// let mut persons = Vec::new(); - /// for person_result in rows { - /// persons.push(person_result?); - /// } - /// - /// Ok(persons) - /// } - /// ``` - /// - /// ### Use with positional params - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn get_names(conn: &Connection) -> Result> { - /// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?1")?; - /// let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?; - /// - /// let mut persons = Vec::new(); - /// for person_result in rows { - /// persons.push(person_result?); - /// } - /// - /// Ok(persons) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if binding parameters fails. - #[inline] - pub fn query_and_then(&mut self, params: P, f: F) -> Result> - where - P: Params, - E: From, - F: FnMut(&Row<'_>) -> Result, - { - self.query(params).map(|rows| rows.and_then(f)) - } - - /// Return `true` if a query in the SQL statement it executes returns one - /// or more rows and `false` if the SQL returns an empty set. - #[inline] - pub fn exists(&mut self, params: P) -> Result { - let mut rows = self.query(params)?; - let exists = rows.next()?.is_some(); - Ok(exists) - } - - /// Convenience method to execute a query that is expected to return a - /// single row. - /// - /// If the query returns more than one row, all rows except the first are - /// ignored. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call - /// [`.optional()`](crate::OptionalExtension::optional) on the result of - /// this to get a `Result>` (requires that the trait - /// `rusqlite::OptionalExtension` is imported). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - pub fn query_row(&mut self, params: P, f: F) -> Result - where - P: Params, - F: FnOnce(&Row<'_>) -> Result, - { - let mut rows = self.query(params)?; - - rows.get_expected_row().and_then(f) - } - - /// Convenience method to execute a query that is expected to return exactly - /// one row. - /// - /// Returns `Err(QueryReturnedMoreThanOneRow)` if the query returns more than one row. - /// - /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the - /// query truly is optional, you can call - /// [`.optional()`](crate::OptionalExtension::optional) on the result of - /// this to get a `Result>` (requires that the trait - /// `rusqlite::OptionalExtension` is imported). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - pub fn query_one(&mut self, params: P, f: F) -> Result - where - P: Params, - F: FnOnce(&Row<'_>) -> Result, - { - let mut rows = self.query(params)?; - let row = rows.get_expected_row().and_then(f)?; - if rows.next()?.is_some() { - return Err(Error::QueryReturnedMoreThanOneRow); - } - Ok(row) - } - - /// Consumes the statement. - /// - /// Functionally equivalent to the `Drop` implementation, but allows - /// callers to see any errors that occur. - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[inline] - pub fn finalize(mut self) -> Result<()> { - self.finalize_() - } - - /// Return the (one-based) index of an SQL parameter given its name. - /// - /// Note that the initial ":" or "$" or "@" or "?" used to specify the - /// parameter is included as part of the name. - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn example(conn: &Connection) -> Result<()> { - /// let stmt = conn.prepare("SELECT * FROM test WHERE name = :example")?; - /// let index = stmt.parameter_index(":example")?; - /// assert_eq!(index, Some(1)); - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return Err if `name` is invalid. Will return Ok(None) if the name - /// is valid but not a bound parameter of this statement. - #[inline] - pub fn parameter_index(&self, name: &str) -> Result> { - Ok(self.stmt.bind_parameter_index(name)) - } - - /// Return the SQL parameter name given its (one-based) index (the inverse - /// of [`Statement::parameter_index`]). - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn example(conn: &Connection) -> Result<()> { - /// let stmt = conn.prepare("SELECT * FROM test WHERE name = :example")?; - /// let index = stmt.parameter_name(1); - /// assert_eq!(index, Some(":example")); - /// Ok(()) - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `None` if the column index is out of bounds or if the - /// parameter is positional. - /// - /// # Panics - /// - /// Panics when parameter name is not valid UTF-8. - #[inline] - pub fn parameter_name(&self, index: usize) -> Option<&'_ str> { - self.stmt.bind_parameter_name(index as i32).map(|name| { - name.to_str() - .expect("Invalid UTF-8 sequence in parameter name") - }) - } - - #[inline] - pub(crate) fn bind_parameters

(&mut self, params: P) -> Result<()> - where - P: IntoIterator, - P::Item: ToSql, - { - let expected = self.stmt.bind_parameter_count(); - let mut index = 0; - for p in params { - index += 1; // The leftmost SQL parameter has an index of 1. - if index > expected { - break; - } - self.bind_parameter(&p, index)?; - } - if index != expected { - Err(Error::InvalidParameterCount(index, expected)) - } else { - Ok(()) - } - } - - #[inline] - pub(crate) fn ensure_parameter_count(&self, n: usize) -> Result<()> { - let count = self.parameter_count(); - if count != n { - Err(Error::InvalidParameterCount(n, count)) - } else { - Ok(()) - } - } - - #[inline] - pub(crate) fn bind_parameters_named( - &mut self, - params: &[(S, T)], - ) -> Result<()> { - for (name, value) in params { - let i = name.idx(self)?; - let ts: &dyn ToSql = &value; - self.bind_parameter(ts, i)?; - } - Ok(()) - } - - /// Return the number of parameters that can be bound to this statement. - #[inline] - pub fn parameter_count(&self) -> usize { - self.stmt.bind_parameter_count() - } - - /// Low level API to directly bind a parameter to a given index. - /// - /// Note that the index is one-based, that is, the first parameter index is - /// 1 and not 0. This is consistent with the SQLite API and the values given - /// to parameters bound as `?NNN`. - /// - /// The valid values for `one_based_col_index` begin at `1`, and end at - /// [`Statement::parameter_count`], inclusive. - /// - /// # Caveats - /// - /// This should not generally be used, but is available for special cases - /// such as: - /// - /// - binding parameters where a gap exists. - /// - binding named and positional parameters in the same query. - /// - separating parameter binding from query execution. - /// - /// In general, statements that have had *any* parameters bound this way - /// should have *all* parameters bound this way, and be queried or executed - /// by [`Statement::raw_query`] or [`Statement::raw_execute`], other usage - /// is unsupported and will likely, probably in surprising ways. - /// - /// That is: Do not mix the "raw" statement functions with the rest of the - /// API, or the results may be surprising, and may even change in future - /// versions without comment. - /// - /// # Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// fn query(conn: &Connection) -> Result<()> { - /// let mut stmt = conn.prepare("SELECT * FROM test WHERE name = :name AND value > ?2")?; - /// stmt.raw_bind_parameter(c":name", "foo")?; - /// stmt.raw_bind_parameter(2, 100)?; - /// let mut rows = stmt.raw_query(); - /// while let Some(row) = rows.next()? { - /// // ... - /// } - /// Ok(()) - /// } - /// ``` - #[inline] - pub fn raw_bind_parameter( - &mut self, - one_based_index: I, - param: T, - ) -> Result<()> { - // This is the same as `bind_parameter` but slightly more ergonomic and - // correctly takes `&mut self`. - self.bind_parameter(¶m, one_based_index.idx(self)?) - } - - /// Low level API to execute a statement given that all parameters were - /// bound explicitly with the [`Statement::raw_bind_parameter`] API. - /// - /// # Caveats - /// - /// Any unbound parameters will have `NULL` as their value. - /// - /// This should not generally be used outside special cases, and - /// functions in the [`Statement::execute`] family should be preferred. - /// - /// # Failure - /// - /// Will return `Err` if the executed statement returns rows (in which case - /// `query` should be used instead), or the underlying SQLite call fails. - #[inline] - pub fn raw_execute(&mut self) -> Result { - self.execute_with_bound_parameters() - } - - /// Low level API to get `Rows` for this query given that all parameters - /// were bound explicitly with the [`Statement::raw_bind_parameter`] API. - /// - /// # Caveats - /// - /// Any unbound parameters will have `NULL` as their value. - /// - /// This should not generally be used outside special cases, and - /// functions in the [`Statement::query`] family should be preferred. - /// - /// Note that if the SQL does not return results, [`Statement::raw_execute`] - /// should be used instead. - #[inline] - pub fn raw_query(&mut self) -> Rows<'_> { - Rows::new(self) - } - - // generic because many of these branches can constant fold away. - fn bind_parameter(&self, param: &P, ndx: usize) -> Result<()> { - let value = param.to_sql()?; - - let ptr = unsafe { self.stmt.ptr() }; - let value = match value { - ToSqlOutput::Borrowed(v) => v, - ToSqlOutput::Owned(ref v) => ValueRef::from(v), - - #[cfg(feature = "blob")] - ToSqlOutput::ZeroBlob(len) => { - // TODO sqlite3_bind_zeroblob64 // 3.8.11 - return self - .conn - .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, ndx as c_int, len) }); - } - #[cfg(feature = "functions")] - ToSqlOutput::Arg(_) => { - return Err(err!(ffi::SQLITE_MISUSE, "Unsupported value \"{value:?}\"")); - } - #[cfg(feature = "pointer")] - ToSqlOutput::Pointer(p) => { - return self.conn.decode_result(unsafe { - ffi::sqlite3_bind_pointer(ptr, ndx as c_int, p.0 as _, p.1.as_ptr(), p.2) - }); - } - }; - self.conn.decode_result(match value { - ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, ndx as c_int) }, - ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, ndx as c_int, i) }, - ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, ndx as c_int, r) }, - ValueRef::Text(s) => unsafe { - let (c_str, len, destructor) = str_for_sqlite(s); - ffi::sqlite3_bind_text64( - ptr, - ndx as c_int, - c_str, - len, - destructor, - ffi::SQLITE_UTF8 as _, - ) - }, - ValueRef::Blob(b) => unsafe { - let length = b.len(); - if length == 0 { - ffi::sqlite3_bind_zeroblob(ptr, ndx as c_int, 0) - } else { - ffi::sqlite3_bind_blob64( - ptr, - ndx as c_int, - b.as_ptr().cast::(), - length as ffi::sqlite3_uint64, - ffi::SQLITE_TRANSIENT(), - ) - } - }, - }) - } - - #[inline] - fn execute_with_bound_parameters(&mut self) -> Result { - self.check_update()?; - let r = self.stmt.step(); - let rr = self.stmt.reset(); - match r { - ffi::SQLITE_DONE => match rr { - ffi::SQLITE_OK => Ok(self.conn.changes() as usize), - _ => Err(self.conn.decode_result(rr).unwrap_err()), - }, - ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), - _ => Err(self.conn.decode_result(r).unwrap_err()), - } - } - - #[inline] - fn finalize_(&mut self) -> Result<()> { - let mut stmt = unsafe { RawStatement::new(ptr::null_mut()) }; - mem::swap(&mut stmt, &mut self.stmt); - self.conn.decode_result(stmt.finalize()) - } - - #[cfg(feature = "extra_check")] - #[inline] - fn check_update(&self) -> Result<()> { - if self.column_count() > 0 && self.stmt.readonly() { - return Err(Error::ExecuteReturnedResults); - } - Ok(()) - } - - #[cfg(not(feature = "extra_check"))] - #[inline] - #[expect(clippy::unnecessary_wraps)] - fn check_update(&self) -> Result<()> { - Ok(()) - } - - /// Returns a string containing the SQL text of prepared statement with - /// bound parameters expanded. - pub fn expanded_sql(&self) -> Option { - self.stmt - .expanded_sql() - .map(|s| s.to_string_lossy().to_string()) - } - - /// Get the value for one of the status counters for this statement. - #[inline] - pub fn get_status(&self, status: StatementStatus) -> i32 { - self.stmt.get_status(status, false) - } - - /// Reset the value of one of the status counters for this statement, - #[inline] - /// returning the value it had before resetting. - pub fn reset_status(&self, status: StatementStatus) -> i32 { - self.stmt.get_status(status, true) - } - - /// Returns 1 if the prepared statement is an EXPLAIN statement, - /// or 2 if the statement is an EXPLAIN QUERY PLAN, - /// or 0 if it is an ordinary statement or a NULL pointer. - #[inline] - pub fn is_explain(&self) -> i32 { - self.stmt.is_explain() - } - - /// Returns true if the statement is read only. - #[inline] - pub fn readonly(&self) -> bool { - self.stmt.readonly() - } - - /// 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. - #[inline] - #[cfg(feature = "cache")] - pub(crate) unsafe fn into_raw(mut self) -> RawStatement { - let mut stmt = RawStatement::new(ptr::null_mut()); - mem::swap(&mut stmt, &mut self.stmt); - stmt - } - - /// Reset all bindings - pub fn clear_bindings(&mut self) { - self.stmt.clear_bindings(); - } - - pub(crate) unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { - self.stmt.ptr() - } -} - -impl fmt::Debug for Statement<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let sql = if self.stmt.is_null() { - Ok("") - } else { - self.stmt.sql().unwrap().to_str() - }; - f.debug_struct("Statement") - .field("conn", self.conn) - .field("stmt", &self.stmt) - .field("sql", &sql) - .finish() - } -} - -impl Drop for Statement<'_> { - #[expect(unused_must_use)] - #[inline] - fn drop(&mut self) { - self.finalize_(); - } -} - -impl Statement<'_> { - #[inline] - pub(super) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> { - Statement { conn, stmt } - } - - pub(super) fn value_ref(&self, col: usize) -> ValueRef<'_> { - let raw = unsafe { self.stmt.ptr() }; - - match self.stmt.column_type(col) { - ffi::SQLITE_NULL => ValueRef::Null, - ffi::SQLITE_INTEGER => { - ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col as c_int) }) - } - ffi::SQLITE_FLOAT => { - ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) }) - } - ffi::SQLITE_TEXT => { - let s = unsafe { - // Quoting from "Using SQLite" book: - // To avoid problems, an application should first extract the desired type using - // a sqlite3_column_xxx() function, and then call the - // appropriate sqlite3_column_bytes() function. - let text = ffi::sqlite3_column_text(raw, col as c_int); - let len = ffi::sqlite3_column_bytes(raw, col as c_int); - assert!( - !text.is_null(), - "unexpected SQLITE_TEXT column type with NULL data" - ); - from_raw_parts(text.cast::(), len as usize) - }; - - ValueRef::Text(s) - } - ffi::SQLITE_BLOB => { - let (blob, len) = unsafe { - ( - ffi::sqlite3_column_blob(raw, col as c_int), - ffi::sqlite3_column_bytes(raw, col as c_int), - ) - }; - - assert!( - len >= 0, - "unexpected negative return from sqlite3_column_bytes" - ); - if len > 0 { - assert!( - !blob.is_null(), - "unexpected SQLITE_BLOB column type with NULL data" - ); - ValueRef::Blob(unsafe { from_raw_parts(blob.cast::(), len as usize) }) - } else { - // The return value from sqlite3_column_blob() for a zero-length BLOB - // is a NULL pointer. - ValueRef::Blob(&[]) - } - } - _ => unreachable!("sqlite3_column_type returned invalid value"), - } - } - - #[inline] - pub(super) fn step(&self) -> Result { - match self.stmt.step() { - ffi::SQLITE_ROW => Ok(true), - ffi::SQLITE_DONE => Ok(false), - code => Err(self.conn.decode_result(code).unwrap_err()), - } - } - - #[inline] - pub(super) fn reset(&self) -> Result<()> { - match self.stmt.reset() { - ffi::SQLITE_OK => Ok(()), - code => Err(self.conn.decode_result(code).unwrap_err()), - } - } -} - -/// Prepared statement status counters. -/// -/// See `https://www.sqlite.org/c3ref/c_stmtstatus_counter.html` -/// for explanations of each. -/// -/// Note that depending on your version of SQLite, all of these -/// may not be available. -#[repr(i32)] -#[derive(Clone, Copy, PartialEq, Eq)] -#[non_exhaustive] -pub enum StatementStatus { - /// Equivalent to `SQLITE_STMTSTATUS_FULLSCAN_STEP` - FullscanStep = 1, - /// Equivalent to `SQLITE_STMTSTATUS_SORT` - Sort = 2, - /// Equivalent to `SQLITE_STMTSTATUS_AUTOINDEX` - AutoIndex = 3, - /// Equivalent to `SQLITE_STMTSTATUS_VM_STEP` - VmStep = 4, - /// Equivalent to `SQLITE_STMTSTATUS_REPREPARE` (3.20.0) - RePrepare = 5, - /// Equivalent to `SQLITE_STMTSTATUS_RUN` (3.20.0) - Run = 6, - /// Equivalent to `SQLITE_STMTSTATUS_FILTER_MISS` - FilterMiss = 7, - /// Equivalent to `SQLITE_STMTSTATUS_FILTER_HIT` - FilterHit = 8, - /// Equivalent to `SQLITE_STMTSTATUS_MEMUSED` (3.20.0) - MemUsed = 99, -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::types::ToSql; - use crate::{params_from_iter, Connection, Error, Result}; - - #[test] - fn test_execute_named() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER)")?; - - assert_eq!( - db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?, - 1 - ); - assert_eq!( - db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])?, - 1 - ); - assert_eq!( - db.execute( - "INSERT INTO foo(x) VALUES (:x)", - crate::named_params! {":x": 3i32} - )?, - 1 - ); - - assert_eq!( - 6i32, - db.query_row::( - "SELECT SUM(x) FROM foo WHERE x > :x", - &[(":x", &0i32)], - |r| r.get(0) - )? - ); - assert_eq!( - 5i32, - db.query_row::( - "SELECT SUM(x) FROM foo WHERE x > :x", - &[(":x", &1i32)], - |r| r.get(0) - )? - ); - Ok(()) - } - - #[test] - fn test_stmt_execute_named() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ - INTEGER)"; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?; - stmt.execute(&[(":name", "one")])?; - stmt.execute(vec![(":name", "one")].as_slice())?; - - let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = :name")?; - assert_eq!( - 2i32, - stmt.query_row::(&[(":name", "one")], |r| r.get(0))? - ); - Ok(()) - } - - #[test] - fn test_query_named() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - "#; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("SELECT id FROM test where name = :name")?; - let mut rows = stmt.query(&[(":name", "one")])?; - let id: Result = rows.next()?.unwrap().get(0); - assert_eq!(Ok(1), id); - Ok(()) - } - - #[test] - fn test_query_map_named() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - "#; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("SELECT id FROM test where name = :name")?; - let mut rows = stmt.query_map(&[(":name", "one")], |row| { - let id: Result = row.get(0); - id.map(|i| 2 * i) - })?; - - let doubled_id: i32 = rows.next().unwrap()?; - assert_eq!(2, doubled_id); - Ok(()) - } - - #[test] - fn test_query_and_then_by_name() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = r#" - CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER); - INSERT INTO test(id, name) VALUES (1, "one"); - INSERT INTO test(id, name) VALUES (2, "one"); - "#; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")?; - let mut rows = stmt.query_and_then(&[(":name", "one")], |row| { - let id: i32 = row.get(0)?; - if id == 1 { - Ok(id) - } else { - Err(Error::SqliteSingleThreadedMode) - } - })?; - - // first row should be Ok - let doubled_id: i32 = rows.next().unwrap()?; - assert_eq!(1, doubled_id); - - // second row should be an `Err` - #[expect(clippy::match_wild_err_arm)] - match rows.next().unwrap() { - Ok(_) => panic!("invalid Ok"), - Err(Error::SqliteSingleThreadedMode) => (), - Err(_) => panic!("invalid Err"), - } - Ok(()) - } - - #[test] - fn test_unbound_parameters_are_null() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "CREATE TABLE test (x TEXT, y TEXT)"; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?; - stmt.execute(&[(":x", "one")])?; - - let result: Option = db.one_column("SELECT y FROM test WHERE x = 'one'", [])?; - assert!(result.is_none()); - Ok(()) - } - - #[test] - fn test_raw_binding() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?; - { - let mut stmt = db.prepare("INSERT INTO test (name, value) VALUES (:name, ?3)")?; - - stmt.raw_bind_parameter(c":name", "example")?; - stmt.raw_bind_parameter(":name", "example")?; - stmt.raw_bind_parameter(3, 50i32)?; - let n = stmt.raw_execute()?; - assert_eq!(n, 1); - } - - { - let mut stmt = db.prepare("SELECT name, value FROM test WHERE value = ?2")?; - stmt.raw_bind_parameter(2, 50)?; - let mut rows = stmt.raw_query(); - { - let row = rows.next()?.unwrap(); - let name: String = row.get(0)?; - assert_eq!(name, "example"); - let value: i32 = row.get(1)?; - assert_eq!(value, 50); - } - assert!(rows.next()?.is_none()); - } - - Ok(()) - } - - #[test] - fn test_unbound_parameters_are_reused() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "CREATE TABLE test (x TEXT, y TEXT)"; - db.execute_batch(sql)?; - - let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?; - stmt.execute(&[(":x", "one")])?; - stmt.execute(&[(c":y", "two")])?; - - let result: String = db.one_column("SELECT x FROM test WHERE y = 'two'", [])?; - assert_eq!(result, "one"); - Ok(()) - } - - #[test] - fn test_insert() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?; - let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?1)")?; - assert_eq!(stmt.insert([1i32])?, 1); - assert_eq!(stmt.insert([2i32])?, 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")?; - match multi.insert([]).unwrap_err() { - Error::StatementChangedRows(2) => (), - err => panic!("Unexpected error {err}"), - } - Ok(()) - } - - #[test] - fn test_insert_different_tables() -> Result<()> { - // Test for https://github.com/rusqlite/rusqlite/issues/171 - let db = Connection::open_in_memory()?; - db.execute_batch( - r" - CREATE TABLE foo(x INTEGER); - CREATE TABLE bar(x INTEGER); - ", - )?; - - assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")?.insert([])?, 1); - assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")?.insert([])?, 1); - Ok(()) - } - - #[test] - fn test_exists() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER); - INSERT INTO foo VALUES(1); - INSERT INTO foo VALUES(2); - END;"; - db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?1")?; - assert!(stmt.exists([1i32])?); - assert!(stmt.exists([2i32])?); - assert!(!stmt.exists([0i32])?); - Ok(()) - } - #[test] - fn test_tuple_params() -> Result<()> { - let db = Connection::open_in_memory()?; - let s = db.query_row("SELECT printf('[%s]', ?1)", ("abc",), |r| { - r.get::<_, String>(0) - })?; - assert_eq!(s, "[abc]"); - let s = db.query_row( - "SELECT printf('%d %s %d', ?1, ?2, ?3)", - (1i32, "abc", 2i32), - |r| r.get::<_, String>(0), - )?; - assert_eq!(s, "1 abc 2"); - let s = db.query_row( - "SELECT printf('%d %s %d %d', ?1, ?2, ?3, ?4)", - (1, "abc", 2i32, 4i64), - |r| r.get::<_, String>(0), - )?; - assert_eq!(s, "1 abc 2 4"); - #[rustfmt::skip] - let bigtup = ( - 0, "a", 1, "b", 2, "c", 3, "d", - 4, "e", 5, "f", 6, "g", 7, "h", - ); - let query = "SELECT printf( - '%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s', - ?1, ?2, ?3, ?4, - ?5, ?6, ?7, ?8, - ?9, ?10, ?11, ?12, - ?13, ?14, ?15, ?16 - )"; - let s = db.query_row(query, bigtup, |r| r.get::<_, String>(0))?; - assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h"); - Ok(()) - } - - #[test] - fn test_query_row() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y INTEGER); - INSERT INTO foo VALUES(1, 3); - INSERT INTO foo VALUES(2, 4); - END;"; - db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?; - let y: Result = stmt.query_row([1i32], |r| r.get(0)); - assert_eq!(3i64, y?); - Ok(()) - } - - #[test] - fn query_one() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo(x INTEGER, y INTEGER);")?; - let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?; - let y: Result = stmt.query_one([1i32], |r| r.get(0)); - assert_eq!(Error::QueryReturnedNoRows, y.unwrap_err()); - db.execute_batch("INSERT INTO foo VALUES(1, 3);")?; - let y: Result = stmt.query_one([1i32], |r| r.get(0)); - assert_eq!(3i64, y?); - db.execute_batch("INSERT INTO foo VALUES(1, 3);")?; - let y: Result = stmt.query_one([1i32], |r| r.get(0)); - assert_eq!(Error::QueryReturnedMoreThanOneRow, y.unwrap_err()); - Ok(()) - } - - #[test] - fn test_query_by_column_name() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y INTEGER); - INSERT INTO foo VALUES(1, 3); - END;"; - db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT y FROM foo")?; - let y: Result = stmt.query_row([], |r| r.get("y")); - assert_eq!(3i64, y?); - Ok(()) - } - - #[test] - fn test_query_by_column_name_ignore_case() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "BEGIN; - CREATE TABLE foo(x INTEGER, y INTEGER); - INSERT INTO foo VALUES(1, 3); - END;"; - db.execute_batch(sql)?; - let mut stmt = db.prepare("SELECT y as Y FROM foo")?; - let y: Result = stmt.query_row([], |r| r.get("y")); - assert_eq!(3i64, y?); - Ok(()) - } - - #[test] - fn test_expanded_sql() -> Result<()> { - let db = Connection::open_in_memory()?; - let stmt = db.prepare("SELECT ?1")?; - stmt.bind_parameter(&1, 1)?; - assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql()); - Ok(()) - } - - #[test] - fn test_bind_parameters() -> Result<()> { - let db = Connection::open_in_memory()?; - // dynamic slice: - db.query_row( - "SELECT ?1, ?2, ?3", - [&1u8 as &dyn ToSql, &"one", &Some("one")], - |row| row.get::<_, u8>(0), - )?; - // existing collection: - let data = vec![1, 2, 3]; - db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { - row.get::<_, u8>(0) - })?; - db.query_row( - "SELECT ?1, ?2, ?3", - params_from_iter(data.as_slice()), - |row| row.get::<_, u8>(0), - )?; - db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data), |row| { - row.get::<_, u8>(0) - })?; - - use std::collections::BTreeSet; - let data: BTreeSet = ["one", "two", "three"] - .iter() - .map(|s| (*s).to_string()) - .collect(); - db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { - row.get::<_, String>(0) - })?; - - let data = [0; 3]; - db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| { - row.get::<_, u8>(0) - })?; - db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| { - row.get::<_, u8>(0) - })?; - Ok(()) - } - - #[test] - fn test_parameter_name() -> Result<()> { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?; - let stmt = db.prepare("INSERT INTO test (name, value) VALUES (:name, ?3)")?; - assert_eq!(stmt.parameter_name(0), None); - assert_eq!(stmt.parameter_name(1), Some(":name")); - assert_eq!(stmt.parameter_name(2), None); - Ok(()) - } - - #[test] - fn test_empty_stmt() -> Result<()> { - let conn = Connection::open_in_memory()?; - let mut stmt = conn.prepare("")?; - assert_eq!(0, stmt.column_count()); - stmt.parameter_index("test")?; - let err = stmt.step().unwrap_err(); - assert_eq!(err.sqlite_error_code(), Some(crate::ErrorCode::ApiMisuse)); - // error msg is different with sqlcipher, so we use assert_ne: - assert_ne!(err.to_string(), "not an error".to_owned()); - stmt.reset()?; // SQLITE_OMIT_AUTORESET = false - stmt.execute([]).unwrap_err(); - Ok(()) - } - - #[test] - fn test_comment_stmt() -> Result<()> { - let conn = Connection::open_in_memory()?; - conn.prepare("/*SELECT 1;*/")?; - Ok(()) - } - - #[test] - fn test_comment_and_sql_stmt() -> Result<()> { - let conn = Connection::open_in_memory()?; - let stmt = conn.prepare("/*...*/ SELECT 1;")?; - assert_eq!(1, stmt.column_count()); - Ok(()) - } - - #[test] - fn test_semi_colon_stmt() -> Result<()> { - let conn = Connection::open_in_memory()?; - let stmt = conn.prepare(";")?; - assert_eq!(0, stmt.column_count()); - Ok(()) - } - - #[test] - fn test_utf16_conversion() -> Result<()> { - let db = Connection::open_in_memory()?; - db.pragma_update(None, "encoding", "UTF-16le")?; - let encoding: String = db.pragma_query_value(None, "encoding", |row| row.get(0))?; - assert_eq!("UTF-16le", encoding); - db.execute_batch("CREATE TABLE foo(x TEXT)")?; - let expected = "テスト"; - db.execute("INSERT INTO foo(x) VALUES (?1)", [&expected])?; - let actual: String = db.one_column("SELECT x FROM foo", [])?; - assert_eq!(expected, actual); - Ok(()) - } - - #[test] - fn test_nul_byte() -> Result<()> { - let db = Connection::open_in_memory()?; - let expected = "a\x00b"; - let actual: String = db.one_column("SELECT ?1", [expected])?; - assert_eq!(expected, actual); - Ok(()) - } - - #[test] - fn is_explain() -> Result<()> { - let db = Connection::open_in_memory()?; - let stmt = db.prepare("SELECT 1;")?; - assert_eq!(0, stmt.is_explain()); - Ok(()) - } - - #[test] - fn readonly() -> Result<()> { - let db = Connection::open_in_memory()?; - let stmt = db.prepare("SELECT 1;")?; - assert!(stmt.readonly()); - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0 - fn test_error_offset() -> Result<()> { - use crate::ffi::ErrorCode; - let db = Connection::open_in_memory()?; - let r = db.execute_batch("SELECT INVALID_FUNCTION;"); - match r.unwrap_err() { - Error::SqlInputError { error, offset, .. } => { - assert_eq!(error.code, ErrorCode::Unknown); - assert_eq!(offset, 7); - } - err => panic!("Unexpected error {err}"), - } - Ok(()) - } -} diff --git a/vendor/rusqlite/src/trace.rs b/vendor/rusqlite/src/trace.rs deleted file mode 100644 index f3e00e3..0000000 --- a/vendor/rusqlite/src/trace.rs +++ /dev/null @@ -1,385 +0,0 @@ -//! Tracing and profiling functions. Error and warning log. - -use std::borrow::Cow; -use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString}; -use std::marker::PhantomData; -use std::mem; -use std::panic::catch_unwind; -use std::ptr; -use std::time::Duration; - -use super::ffi; -use crate::{Connection, StatementStatus, MAIN_DB}; - -/// Set up the process-wide SQLite error logging callback. -/// -/// # Safety -/// -/// This function is marked unsafe for two reasons: -/// -/// * The function is not threadsafe. No other SQLite calls may be made while -/// `config_log` is running, and multiple threads may not call `config_log` -/// simultaneously. -/// * The provided `callback` itself function has two requirements: -/// * It must not invoke any SQLite calls. -/// * It must be threadsafe if SQLite is used in a multithreaded way. -/// -/// cf [The Error And Warning Log](http://sqlite.org/errlog.html). -#[cfg(not(feature = "loadable_extension"))] -pub unsafe fn config_log(callback: Option) -> crate::Result<()> { - extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) { - let s = unsafe { CStr::from_ptr(msg).to_string_lossy() }; - let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; - - drop(catch_unwind(|| callback(err, &s))); - } - - let rc = if let Some(f) = callback { - ffi::sqlite3_config( - ffi::SQLITE_CONFIG_LOG, - log_callback as extern "C" fn(_, _, _), - f as *mut c_void, - ) - } else { - let nullptr: *mut c_void = ptr::null_mut(); - ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr) - }; - - if rc == ffi::SQLITE_OK { - Ok(()) - } else { - Err(crate::error::error_from_sqlite_code(rc, None)) - } -} - -/// Write a message into the error log established by -/// `config_log`. -#[inline] -pub fn log(err_code: c_int, msg: &str) { - let msg = CString::new(msg).expect("SQLite log messages cannot contain embedded zeroes"); - unsafe { - ffi::sqlite3_log(err_code, b"%s\0" as *const _ as *const c_char, msg.as_ptr()); - } -} - -bitflags::bitflags! { - /// Trace event codes - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - #[non_exhaustive] - #[repr(C)] - pub struct TraceEventCodes: c_uint { - /// when a prepared statement first begins running and possibly at other times during the execution - /// of the prepared statement, such as at the start of each trigger subprogram - const SQLITE_TRACE_STMT = ffi::SQLITE_TRACE_STMT; - /// when the statement finishes - const SQLITE_TRACE_PROFILE = ffi::SQLITE_TRACE_PROFILE; - /// whenever a prepared statement generates a single row of result - const SQLITE_TRACE_ROW = ffi::SQLITE_TRACE_ROW; - /// when a database connection closes - const SQLITE_TRACE_CLOSE = ffi::SQLITE_TRACE_CLOSE; - } -} - -/// Trace event -#[non_exhaustive] -pub enum TraceEvent<'s> { - /// when a prepared statement first begins running and possibly at other times during the execution - /// of the prepared statement, such as at the start of each trigger subprogram - Stmt(StmtRef<'s>, &'s str), - /// when the statement finishes - Profile(StmtRef<'s>, Duration), - /// whenever a prepared statement generates a single row of result - Row(StmtRef<'s>), - /// when a database connection closes - Close(ConnRef<'s>), -} - -/// Statement reference -pub struct StmtRef<'s> { - ptr: *mut ffi::sqlite3_stmt, - phantom: PhantomData<&'s ()>, -} - -impl StmtRef<'_> { - fn new(ptr: *mut ffi::sqlite3_stmt) -> Self { - StmtRef { - ptr, - phantom: PhantomData, - } - } - /// SQL text - pub fn sql(&self) -> Cow<'_, str> { - let sql = unsafe { ffi::sqlite3_sql(self.ptr) }; - - if sql.is_null() { - return Cow::default(); - } - - // Safety: sql is a valid pointer to a cstr returned by sqlite3 - unsafe { CStr::from_ptr(sql).to_string_lossy() } - } - /// Expanded SQL text - pub fn expanded_sql(&self) -> Option { - unsafe { - crate::raw_statement::expanded_sql(self.ptr).map(|s| s.to_string_lossy().to_string()) - } - } - /// Get the value for one of the status counters for this statement. - pub fn get_status(&self, status: StatementStatus) -> i32 { - unsafe { crate::raw_statement::stmt_status(self.ptr, status, false) } - } -} - -/// Connection reference -pub struct ConnRef<'s> { - ptr: *mut ffi::sqlite3, - phantom: PhantomData<&'s ()>, -} - -impl ConnRef<'_> { - /// Test for auto-commit mode. - pub fn is_autocommit(&self) -> bool { - unsafe { crate::inner_connection::get_autocommit(self.ptr) } - } - /// the path to the database file, if one exists and is known. - pub fn db_filename(&self) -> Option<&str> { - unsafe { crate::inner_connection::db_filename(self.phantom, self.ptr, MAIN_DB) } - } -} - -impl Connection { - /// Register or clear a callback function that can be - /// used for tracing the execution of SQL statements. - /// - /// Prepared statement placeholders are replaced/logged with their assigned - /// values. There can only be a single tracer defined for each database - /// connection. Setting a new tracer clears the old one. - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - #[deprecated(since = "0.33.0", note = "use trace_v2 instead")] - pub fn trace(&mut self, trace_fn: Option) { - unsafe extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { - let trace_fn: fn(&str) = mem::transmute(p_arg); - let s = CStr::from_ptr(z_sql).to_string_lossy(); - drop(catch_unwind(|| trace_fn(&s))); - } - - let c = self.db.borrow_mut(); - unsafe { - ffi::sqlite3_trace( - c.db(), - trace_fn.as_ref().map(|_| trace_callback as _), - trace_fn.map_or_else(ptr::null_mut, |f| f as *mut c_void), - ); - } - } - - /// Register or clear a callback function that can be - /// used for profiling the execution of SQL statements. - /// - /// There can only be a single profiler defined for each database - /// connection. Setting a new profiler clears the old one. - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - #[deprecated(since = "0.33.0", note = "use trace_v2 instead")] - pub fn profile(&mut self, profile_fn: Option) { - unsafe extern "C" fn profile_callback( - p_arg: *mut c_void, - z_sql: *const c_char, - nanoseconds: u64, - ) { - let profile_fn: fn(&str, Duration) = mem::transmute(p_arg); - let s = CStr::from_ptr(z_sql).to_string_lossy(); - - let duration = Duration::from_nanos(nanoseconds); - drop(catch_unwind(|| profile_fn(&s, duration))); - } - - let c = self.db.borrow_mut(); - unsafe { - ffi::sqlite3_profile( - c.db(), - profile_fn.as_ref().map(|_| profile_callback as _), - profile_fn.map_or_else(ptr::null_mut, |f| f as *mut c_void), - ); - } - } - - /// Register or clear a trace callback function - pub fn trace_v2(&self, mask: TraceEventCodes, trace_fn: Option)>) { - unsafe extern "C" fn trace_callback( - evt: c_uint, - ctx: *mut c_void, - p: *mut c_void, - x: *mut c_void, - ) -> c_int { - let trace_fn: fn(TraceEvent<'_>) = mem::transmute(ctx); - drop(catch_unwind(|| match evt { - ffi::SQLITE_TRACE_STMT => { - let str = CStr::from_ptr(x as *const c_char).to_string_lossy(); - trace_fn(TraceEvent::Stmt( - StmtRef::new(p as *mut ffi::sqlite3_stmt), - &str, - )) - } - ffi::SQLITE_TRACE_PROFILE => { - let ns = *(x as *const i64); - trace_fn(TraceEvent::Profile( - StmtRef::new(p as *mut ffi::sqlite3_stmt), - Duration::from_nanos(u64::try_from(ns).unwrap_or_default()), - )) - } - ffi::SQLITE_TRACE_ROW => { - trace_fn(TraceEvent::Row(StmtRef::new(p as *mut ffi::sqlite3_stmt))) - } - ffi::SQLITE_TRACE_CLOSE => trace_fn(TraceEvent::Close(ConnRef { - ptr: p as *mut ffi::sqlite3, - phantom: PhantomData, - })), - _ => {} - })); - // The integer return value from the callback is currently ignored, though this may change in future releases. - // Callback implementations should return zero to ensure future compatibility. - ffi::SQLITE_OK - } - let c = self.db.borrow_mut(); - unsafe { - ffi::sqlite3_trace_v2( - c.db(), - mask.bits(), - trace_fn.as_ref().map(|_| trace_callback as _), - trace_fn.map_or_else(ptr::null_mut, |f| f as *mut c_void), - ); - } - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - use std::sync::{LazyLock, Mutex}; - use std::time::Duration; - - use super::{TraceEvent, TraceEventCodes}; - use crate::{Connection, Result, MAIN_DB}; - - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - #[test] - #[allow(deprecated)] - fn test_trace() -> Result<()> { - static TRACED_STMTS: LazyLock>> = - LazyLock::new(|| Mutex::new(Vec::new())); - fn tracer(s: &str) { - let mut traced_stmts = TRACED_STMTS.lock().unwrap(); - traced_stmts.push(s.to_owned()); - } - - let mut db = Connection::open_in_memory()?; - db.trace(Some(tracer)); - { - let _ = db.query_row("SELECT ?1", [1i32], |_| Ok(())); - let _ = db.query_row("SELECT ?1", ["hello"], |_| Ok(())); - } - db.trace(None); - { - let _ = db.query_row("SELECT ?1", [2i32], |_| Ok(())); - let _ = db.query_row("SELECT ?1", ["goodbye"], |_| Ok(())); - } - - let traced_stmts = TRACED_STMTS.lock().unwrap(); - assert_eq!(traced_stmts.len(), 2); - assert_eq!(traced_stmts[0], "SELECT 1"); - assert_eq!(traced_stmts[1], "SELECT 'hello'"); - Ok(()) - } - - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - #[test] - #[allow(deprecated)] - fn test_profile() -> Result<()> { - static PROFILED: LazyLock>> = - LazyLock::new(|| Mutex::new(Vec::new())); - fn profiler(s: &str, d: Duration) { - let mut profiled = PROFILED.lock().unwrap(); - profiled.push((s.to_owned(), d)); - } - - let mut db = Connection::open_in_memory()?; - db.profile(Some(profiler)); - db.execute_batch("PRAGMA application_id = 1")?; - db.profile(None); - db.execute_batch("PRAGMA application_id = 2")?; - - let profiled = PROFILED.lock().unwrap(); - assert_eq!(profiled.len(), 1); - assert_eq!(profiled[0].0, "PRAGMA application_id = 1"); - Ok(()) - } - - #[test] - pub fn trace_v2() -> Result<()> { - use std::borrow::Borrow; - use std::cmp::Ordering; - - let db = Connection::open_in_memory()?; - db.trace_v2( - TraceEventCodes::all(), - Some(|e| match e { - TraceEvent::Stmt(s, sql) => { - assert_eq!(s.sql(), sql); - } - TraceEvent::Profile(s, d) => { - assert_eq!(s.get_status(crate::StatementStatus::Sort), 0); - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - assert_eq!(d.cmp(&Duration::ZERO), Ordering::Greater); - // Timers on the web are not very accurate - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - assert!(matches!( - d.cmp(&Duration::ZERO), - Ordering::Equal | Ordering::Greater - )); - } - TraceEvent::Row(s) => { - assert_eq!(s.expanded_sql().as_deref(), Some(s.sql().borrow())); - } - TraceEvent::Close(db) => { - assert!(db.is_autocommit()); - // https://www.sqlite.org/c3ref/db_filename.html - // if database N is a temporary or in-memory database, - // then this function will return either a NULL pointer or an empty string. - assert!(db.db_filename().is_none_or(|s| s.is_empty())); - } - }), - ); - - db.one_column::("PRAGMA user_version", [])?; - drop(db); - - let db = Connection::open_in_memory()?; - db.trace_v2(TraceEventCodes::empty(), None); - Ok(()) - } - - #[test] - #[cfg(feature = "blob")] - pub fn null_sql() -> Result<()> { - let db = Connection::open_in_memory()?; - let sql = "CREATE TABLE test (content BLOB); - INSERT INTO test VALUES (ZEROBLOB(10));"; - db.execute_batch(sql)?; - let rowid = db.last_insert_rowid(); - - db.trace_v2( - TraceEventCodes::SQLITE_TRACE_ROW, - Some(|e| { - if let TraceEvent::Row(s) = e { - assert_eq!(s.sql(), ""); - } - }), - ); - db.blob_open(MAIN_DB, c"test", c"content", rowid, true)?; - - Ok(()) - } -} diff --git a/vendor/rusqlite/src/transaction.rs b/vendor/rusqlite/src/transaction.rs deleted file mode 100644 index febb80b..0000000 --- a/vendor/rusqlite/src/transaction.rs +++ /dev/null @@ -1,826 +0,0 @@ -use crate::{Connection, Result}; -use std::ops::Deref; - -/// Options for transaction behavior. See [BEGIN -/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details. -#[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, -} - -/// Options for how a Transaction or Savepoint should behave when it is dropped. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum DropBehavior { - /// Roll back the changes. This is the default. - Rollback, - - /// Commit the changes. - Commit, - - /// Do not commit or roll back changes - this will leave the transaction or - /// savepoint open, so should be used with care. - Ignore, - - /// Panic. Used to enforce intentional behavior during development. - Panic, -} - -/// Represents a transaction on a database connection. -/// -/// ## Note -/// -/// Transactions will roll back by default. Use `commit` method to explicitly -/// commit the transaction, or use `set_drop_behavior` to change what happens -/// when the transaction is dropped. -/// -/// ## Example -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result}; -/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } -/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } -/// fn perform_queries(conn: &mut Connection) -> Result<()> { -/// let tx = conn.transaction()?; -/// -/// do_queries_part_1(&tx)?; // tx causes rollback if this fails -/// do_queries_part_2(&tx)?; // tx causes rollback if this fails -/// -/// tx.commit() -/// } -/// ``` -#[derive(Debug)] -pub struct Transaction<'conn> { - conn: &'conn Connection, - drop_behavior: DropBehavior, -} - -/// Represents a savepoint on a database connection. -/// -/// ## Note -/// -/// Savepoints will roll back by default. Use `commit` method to explicitly -/// commit the savepoint, or use `set_drop_behavior` to change what happens -/// when the savepoint is dropped. -/// -/// ## Example -/// -/// ```rust,no_run -/// # use rusqlite::{Connection, Result}; -/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } -/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } -/// fn perform_queries(conn: &mut Connection) -> Result<()> { -/// let sp = conn.savepoint()?; -/// -/// do_queries_part_1(&sp)?; // sp causes rollback if this fails -/// do_queries_part_2(&sp)?; // sp causes rollback if this fails -/// -/// sp.commit() -/// } -/// ``` -#[derive(Debug)] -pub struct Savepoint<'conn> { - conn: &'conn Connection, - name: String, - drop_behavior: DropBehavior, - committed: bool, -} - -impl Transaction<'_> { - /// Begin a new transaction. Cannot be nested; see `savepoint` for nested - /// transactions. - /// - /// Even though we don't mutate the connection, we take a `&mut Connection` - /// to prevent nested transactions on the same connection. For cases - /// where this is unacceptable, [`Transaction::new_unchecked`] is available. - #[inline] - pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result> { - Self::new_unchecked(conn, behavior) - } - - /// Begin a new transaction, failing if a transaction is open. - /// - /// If a transaction is already open, this will return an error. Where - /// possible, [`Transaction::new`] should be preferred, as it provides a - /// compile-time guarantee that transactions are not nested. - #[inline] - pub fn new_unchecked( - conn: &Connection, - behavior: TransactionBehavior, - ) -> Result> { - let query = match behavior { - TransactionBehavior::Deferred => "BEGIN DEFERRED", - TransactionBehavior::Immediate => "BEGIN IMMEDIATE", - TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", - }; - conn.execute_batch(query).map(move |()| Transaction { - conn, - drop_behavior: DropBehavior::Rollback, - }) - } - - /// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested - /// transactions. - /// - /// ## Note - /// - /// Just like outer level transactions, savepoint transactions rollback by - /// default. - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// # fn perform_queries_part_1_succeeds(_conn: &Connection) -> bool { true } - /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let mut tx = conn.transaction()?; - /// - /// { - /// let sp = tx.savepoint()?; - /// if perform_queries_part_1_succeeds(&sp) { - /// sp.commit()?; - /// } - /// // otherwise, sp will rollback - /// } - /// - /// tx.commit() - /// } - /// ``` - #[inline] - pub fn savepoint(&mut self) -> Result> { - Savepoint::new_(self.conn) - } - - /// Create a new savepoint with a custom savepoint name. See `savepoint()`. - #[inline] - pub fn savepoint_with_name>(&mut self, name: T) -> Result> { - Savepoint::with_name_(self.conn, name) - } - - /// Get the current setting for what happens to the transaction when it is - /// dropped. - #[inline] - #[must_use] - pub fn drop_behavior(&self) -> DropBehavior { - self.drop_behavior - } - - /// Configure the transaction to perform the specified action when it is - /// dropped. - #[inline] - pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) { - self.drop_behavior = drop_behavior; - } - - /// A convenience method which consumes and commits a transaction. - #[inline] - pub fn commit(mut self) -> Result<()> { - self.commit_() - } - - #[inline] - fn commit_(&mut self) -> Result<()> { - self.conn.execute_batch("COMMIT")?; - Ok(()) - } - - /// A convenience method which consumes and rolls back a transaction. - #[inline] - pub fn rollback(mut self) -> Result<()> { - self.rollback_() - } - - #[inline] - fn rollback_(&mut self) -> Result<()> { - self.conn.execute_batch("ROLLBACK")?; - Ok(()) - } - - /// Consumes the transaction, committing or rolling back according to the - /// current setting (see `drop_behavior`). - /// - /// Functionally equivalent to the `Drop` implementation, but allows - /// callers to see any errors that occur. - #[inline] - pub fn finish(mut self) -> Result<()> { - self.finish_() - } - - #[inline] - fn finish_(&mut self) -> Result<()> { - if self.conn.is_autocommit() { - return Ok(()); - } - match self.drop_behavior() { - DropBehavior::Commit => self.commit_().or_else(|_| self.rollback_()), - DropBehavior::Rollback => self.rollback_(), - DropBehavior::Ignore => Ok(()), - DropBehavior::Panic => panic!("Transaction dropped unexpectedly."), - } - } -} - -impl Deref for Transaction<'_> { - type Target = Connection; - - #[inline] - fn deref(&self) -> &Connection { - self.conn - } -} - -#[expect(unused_must_use)] -impl Drop for Transaction<'_> { - #[inline] - fn drop(&mut self) { - self.finish_(); - } -} - -impl Savepoint<'_> { - #[inline] - fn with_name_>(conn: &Connection, name: T) -> Result> { - let name = name.into(); - conn.execute_batch(&format!("SAVEPOINT {name}")) - .map(|()| Savepoint { - conn, - name, - drop_behavior: DropBehavior::Rollback, - committed: false, - }) - } - - #[inline] - fn new_(conn: &Connection) -> Result> { - Savepoint::with_name_(conn, "_rusqlite_sp") - } - - /// Begin a new savepoint. Can be nested. - #[inline] - pub fn new(conn: &mut Connection) -> Result> { - Savepoint::new_(conn) - } - - /// Begin a new savepoint with a user-provided savepoint name. - #[inline] - pub fn with_name>(conn: &mut Connection, name: T) -> Result> { - Savepoint::with_name_(conn, name) - } - - /// Begin a nested savepoint. - #[inline] - pub fn savepoint(&mut self) -> Result> { - Savepoint::new_(self.conn) - } - - /// Begin a nested savepoint with a user-provided savepoint name. - #[inline] - pub fn savepoint_with_name>(&mut self, name: T) -> Result> { - Savepoint::with_name_(self.conn, name) - } - - /// Get the current setting for what happens to the savepoint when it is - /// dropped. - #[inline] - #[must_use] - pub fn drop_behavior(&self) -> DropBehavior { - self.drop_behavior - } - - /// Configure the savepoint to perform the specified action when it is - /// dropped. - #[inline] - pub fn set_drop_behavior(&mut self, drop_behavior: DropBehavior) { - self.drop_behavior = drop_behavior; - } - - /// A convenience method which consumes and commits a savepoint. - #[inline] - pub fn commit(mut self) -> Result<()> { - self.commit_() - } - - #[inline] - fn commit_(&mut self) -> Result<()> { - self.conn.execute_batch(&format!("RELEASE {}", self.name))?; - self.committed = true; - Ok(()) - } - - /// A convenience method which rolls back a savepoint. - /// - /// ## Note - /// - /// Unlike `Transaction`s, savepoints remain active after they have been - /// rolled back, and can be rolled back again or committed. - #[inline] - pub fn rollback(&mut self) -> Result<()> { - self.conn - .execute_batch(&format!("ROLLBACK TO {}", self.name)) - } - - /// Consumes the savepoint, committing or rolling back according to the - /// current setting (see `drop_behavior`). - /// - /// Functionally equivalent to the `Drop` implementation, but allows - /// callers to see any errors that occur. - #[inline] - pub fn finish(mut self) -> Result<()> { - self.finish_() - } - - #[inline] - fn finish_(&mut self) -> Result<()> { - if self.committed { - return Ok(()); - } - match self.drop_behavior() { - DropBehavior::Commit => self - .commit_() - .or_else(|_| self.rollback().and_then(|()| self.commit_())), - DropBehavior::Rollback => self.rollback().and_then(|()| self.commit_()), - DropBehavior::Ignore => Ok(()), - DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."), - } - } -} - -impl Deref for Savepoint<'_> { - type Target = Connection; - - #[inline] - fn deref(&self) -> &Connection { - self.conn - } -} - -#[expect(unused_must_use)] -impl Drop for Savepoint<'_> { - #[inline] - fn drop(&mut self) { - self.finish_(); - } -} - -/// Transaction state of a database -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[non_exhaustive] -#[cfg(feature = "modern_sqlite")] // 3.37.0 -pub enum TransactionState { - /// Equivalent to `SQLITE_TXN_NONE` - None, - /// Equivalent to `SQLITE_TXN_READ` - Read, - /// Equivalent to `SQLITE_TXN_WRITE` - Write, -} - -impl Connection { - /// Begin a new transaction with the default behavior (DEFERRED). - /// - /// The transaction defaults to rolling back when it is dropped. If you - /// want the transaction to commit, you must call - /// [`commit`](Transaction::commit) or - /// [`set_drop_behavior(DropBehavior::Commit)`](Transaction::set_drop_behavior). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } - /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } - /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let tx = conn.transaction()?; - /// - /// do_queries_part_1(&tx)?; // tx causes rollback if this fails - /// do_queries_part_2(&tx)?; // tx causes rollback if this fails - /// - /// tx.commit() - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[inline] - pub fn transaction(&mut self) -> Result> { - Transaction::new(self, self.transaction_behavior) - } - - /// Begin a new transaction with a specified behavior. - /// - /// See [`transaction`](Connection::transaction). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[inline] - pub fn transaction_with_behavior( - &mut self, - behavior: TransactionBehavior, - ) -> Result> { - Transaction::new(self, behavior) - } - - /// Begin a new transaction with the default behavior (DEFERRED). - /// - /// Attempt to open a nested transaction will result in a SQLite error. - /// `Connection::transaction` prevents this at compile time by taking `&mut - /// self`, but `Connection::unchecked_transaction()` may be used to defer - /// the checking until runtime. - /// - /// See [`Connection::transaction`] and [`Transaction::new_unchecked`] - /// (which can be used if the default transaction behavior is undesirable). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// # use std::rc::Rc; - /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } - /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } - /// fn perform_queries(conn: Rc) -> Result<()> { - /// let tx = conn.unchecked_transaction()?; - /// - /// do_queries_part_1(&tx)?; // tx causes rollback if this fails - /// do_queries_part_2(&tx)?; // tx causes rollback if this fails - /// - /// tx.commit() - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. The specific - /// error returned if transactions are nested is currently unspecified. - pub fn unchecked_transaction(&self) -> Result> { - Transaction::new_unchecked(self, self.transaction_behavior) - } - - /// Begin a new savepoint with the default behavior (DEFERRED). - /// - /// The savepoint defaults to rolling back when it is dropped. If you want - /// the savepoint to commit, you must call [`commit`](Savepoint::commit) or - /// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::set_drop_behavior). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result}; - /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } - /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } - /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// let sp = conn.savepoint()?; - /// - /// do_queries_part_1(&sp)?; // sp causes rollback if this fails - /// do_queries_part_2(&sp)?; // sp causes rollback if this fails - /// - /// sp.commit() - /// } - /// ``` - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[inline] - pub fn savepoint(&mut self) -> Result> { - Savepoint::new(self) - } - - /// Begin a new savepoint with a specified name. - /// - /// See [`savepoint`](Connection::savepoint). - /// - /// # Failure - /// - /// Will return `Err` if the underlying SQLite call fails. - #[inline] - pub fn savepoint_with_name>(&mut self, name: T) -> Result> { - Savepoint::with_name(self, name) - } - - /// Determine the transaction state of a database - #[cfg(feature = "modern_sqlite")] // 3.37.0 - pub fn transaction_state( - &self, - db_name: Option, - ) -> Result { - self.db.borrow().txn_state(db_name) - } - - /// Set the default transaction behavior for the connection. - /// - /// ## Note - /// - /// This will only apply to transactions initiated by [`transaction`](Connection::transaction) - /// or [`unchecked_transaction`](Connection::unchecked_transaction). - /// - /// ## Example - /// - /// ```rust,no_run - /// # use rusqlite::{Connection, Result, TransactionBehavior}; - /// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) } - /// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) } - /// fn perform_queries(conn: &mut Connection) -> Result<()> { - /// conn.set_transaction_behavior(TransactionBehavior::Immediate); - /// - /// let tx = conn.transaction()?; - /// - /// do_queries_part_1(&tx)?; // tx causes rollback if this fails - /// do_queries_part_2(&tx)?; // tx causes rollback if this fails - /// - /// tx.commit() - /// } - /// ``` - pub fn set_transaction_behavior(&mut self, behavior: TransactionBehavior) { - self.transaction_behavior = behavior; - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::DropBehavior; - use crate::{Connection, Error, Result}; - - fn checked_memory_handle() -> Result { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo (x INTEGER)")?; - Ok(db) - } - - #[test] - fn test_drop() -> Result<()> { - let mut db = checked_memory_handle()?; - { - let tx = db.transaction()?; - tx.execute_batch("INSERT INTO foo VALUES(1)")?; - // default: rollback - } - { - let mut tx = db.transaction()?; - tx.execute_batch("INSERT INTO foo VALUES(2)")?; - tx.set_drop_behavior(DropBehavior::Commit) - } - { - let tx = db.transaction()?; - assert_eq!(2, tx.one_column::("SELECT SUM(x) FROM foo", [])?); - } - Ok(()) - } - fn assert_nested_tx_error(e: Error) { - if let Error::SqliteFailure(e, Some(m)) = &e { - assert_eq!(e.extended_code, crate::ffi::SQLITE_ERROR); - // FIXME: Not ideal... - assert_eq!(e.code, crate::ErrorCode::Unknown); - assert!(m.contains("transaction")); - } else { - panic!("Unexpected error type: {e:?}"); - } - } - - #[test] - fn test_unchecked_nesting() -> Result<()> { - let db = checked_memory_handle()?; - - { - let tx = db.unchecked_transaction()?; - let e = tx.unchecked_transaction().unwrap_err(); - assert_nested_tx_error(e); - // default: rollback - } - { - let tx = db.unchecked_transaction()?; - tx.execute_batch("INSERT INTO foo VALUES(1)")?; - // Ensure this doesn't interfere with ongoing transaction - let e = tx.unchecked_transaction().unwrap_err(); - assert_nested_tx_error(e); - - tx.execute_batch("INSERT INTO foo VALUES(1)")?; - tx.commit()?; - } - - assert_eq!(2, db.one_column::("SELECT SUM(x) FROM foo", [])?); - Ok(()) - } - - #[test] - fn test_explicit_rollback_commit() -> Result<()> { - let mut db = checked_memory_handle()?; - { - let mut tx = db.transaction()?; - { - let mut sp = tx.savepoint()?; - sp.execute_batch("INSERT INTO foo VALUES(1)")?; - sp.rollback()?; - sp.execute_batch("INSERT INTO foo VALUES(2)")?; - sp.commit()?; - } - tx.commit()?; - } - { - let tx = db.transaction()?; - tx.execute_batch("INSERT INTO foo VALUES(4)")?; - tx.commit()?; - } - { - let tx = db.transaction()?; - assert_eq!(6, tx.one_column::("SELECT SUM(x) FROM foo", [])?); - } - Ok(()) - } - - #[test] - fn test_savepoint() -> Result<()> { - let mut db = checked_memory_handle()?; - { - let mut tx = db.transaction()?; - tx.execute_batch("INSERT INTO foo VALUES(1)")?; - assert_current_sum(1, &tx)?; - tx.set_drop_behavior(DropBehavior::Commit); - { - let mut sp1 = tx.savepoint()?; - sp1.execute_batch("INSERT INTO foo VALUES(2)")?; - assert_current_sum(3, &sp1)?; - // will roll back sp1 - { - let mut sp2 = sp1.savepoint()?; - sp2.execute_batch("INSERT INTO foo VALUES(4)")?; - assert_current_sum(7, &sp2)?; - // will roll back sp2 - { - let sp3 = sp2.savepoint()?; - sp3.execute_batch("INSERT INTO foo VALUES(8)")?; - assert_current_sum(15, &sp3)?; - sp3.commit()?; - // committed sp3, but will be erased by sp2 rollback - } - assert_current_sum(15, &sp2)?; - } - assert_current_sum(3, &sp1)?; - } - assert_current_sum(1, &tx)?; - } - assert_current_sum(1, &db)?; - Ok(()) - } - - #[test] - fn test_ignore_drop_behavior() -> Result<()> { - let mut db = checked_memory_handle()?; - - let mut tx = db.transaction()?; - { - let mut sp1 = tx.savepoint()?; - insert(1, &sp1)?; - sp1.rollback()?; - insert(2, &sp1)?; - { - let mut sp2 = sp1.savepoint()?; - sp2.set_drop_behavior(DropBehavior::Ignore); - insert(4, &sp2)?; - } - assert_current_sum(6, &sp1)?; - sp1.commit()?; - } - assert_current_sum(6, &tx)?; - Ok(()) - } - - #[test] - fn test_savepoint_drop_behavior_releases() -> Result<()> { - let mut db = checked_memory_handle()?; - - { - let mut sp = db.savepoint()?; - sp.set_drop_behavior(DropBehavior::Commit); - } - assert!(db.is_autocommit()); - { - let mut sp = db.savepoint()?; - sp.set_drop_behavior(DropBehavior::Rollback); - } - assert!(db.is_autocommit()); - - Ok(()) - } - - #[test] - fn test_savepoint_release_error() -> Result<()> { - let mut db = checked_memory_handle()?; - - db.pragma_update(None, "foreign_keys", true)?; - db.execute_batch("CREATE TABLE r(n INTEGER PRIMARY KEY NOT NULL); CREATE TABLE f(n REFERENCES r(n) DEFERRABLE INITIALLY DEFERRED);")?; - { - let mut sp = db.savepoint()?; - sp.execute("INSERT INTO f VALUES (0)", [])?; - sp.set_drop_behavior(DropBehavior::Commit); - } - assert!(db.is_autocommit()); - - Ok(()) - } - - #[test] - fn test_savepoint_names() -> Result<()> { - let mut db = checked_memory_handle()?; - - { - let mut sp1 = db.savepoint_with_name("my_sp")?; - insert(1, &sp1)?; - assert_current_sum(1, &sp1)?; - { - let mut sp2 = sp1.savepoint_with_name("my_sp")?; - sp2.set_drop_behavior(DropBehavior::Commit); - insert(2, &sp2)?; - assert_current_sum(3, &sp2)?; - sp2.rollback()?; - assert_current_sum(1, &sp2)?; - insert(4, &sp2)?; - } - assert_current_sum(5, &sp1)?; - sp1.rollback()?; - { - let mut sp2 = sp1.savepoint_with_name("my_sp")?; - sp2.set_drop_behavior(DropBehavior::Ignore); - insert(8, &sp2)?; - } - assert_current_sum(8, &sp1)?; - sp1.commit()?; - } - assert_current_sum(8, &db)?; - Ok(()) - } - - #[test] - fn test_rc() -> Result<()> { - use std::rc::Rc; - let mut conn = Connection::open_in_memory()?; - let rc_txn = Rc::new(conn.transaction()?); - - // This will compile only if Transaction is Debug - Rc::try_unwrap(rc_txn).unwrap(); - Ok(()) - } - - fn insert(x: i32, conn: &Connection) -> Result { - conn.execute("INSERT INTO foo VALUES(?1)", [x]) - } - - fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> { - assert_eq!(x, conn.one_column::("SELECT SUM(x) FROM foo", [])?); - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] - fn txn_state() -> Result<()> { - use super::TransactionState; - use crate::MAIN_DB; - let db = Connection::open_in_memory()?; - assert_eq!(TransactionState::None, db.transaction_state(Some(MAIN_DB))?); - assert_eq!(TransactionState::None, db.transaction_state::<&str>(None)?); - db.execute_batch("BEGIN")?; - assert_eq!(TransactionState::None, db.transaction_state::<&str>(None)?); - let _: i32 = db.pragma_query_value(None, "user_version", |row| row.get(0))?; - assert_eq!(TransactionState::Read, db.transaction_state::<&str>(None)?); - db.pragma_update(None, "user_version", 1)?; - assert_eq!(TransactionState::Write, db.transaction_state::<&str>(None)?); - db.execute_batch("ROLLBACK")?; - Ok(()) - } - - #[test] - #[cfg(feature = "modern_sqlite")] - fn auto_commit() -> Result<()> { - use super::TransactionState; - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE t(i UNIQUE);")?; - assert!(db.is_autocommit()); - let mut stmt = db.prepare("SELECT name FROM sqlite_master")?; - assert_eq!(TransactionState::None, db.transaction_state::<&str>(None)?); - { - let mut rows = stmt.query([])?; - assert!(rows.next()?.is_some()); // start reading - assert_eq!(TransactionState::Read, db.transaction_state::<&str>(None)?); - db.execute("INSERT INTO t VALUES (1)", [])?; // auto-commit - assert_eq!(TransactionState::Read, db.transaction_state::<&str>(None)?); - assert!(rows.next()?.is_some()); // still reading - assert_eq!(TransactionState::Read, db.transaction_state::<&str>(None)?); - assert!(rows.next()?.is_none()); // end - assert_eq!(TransactionState::None, db.transaction_state::<&str>(None)?); - } - Ok(()) - } -} diff --git a/vendor/rusqlite/src/types/chrono.rs b/vendor/rusqlite/src/types/chrono.rs deleted file mode 100644 index 81ad4bf..0000000 --- a/vendor/rusqlite/src/types/chrono.rs +++ /dev/null @@ -1,317 +0,0 @@ -//! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types. - -use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; - -use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Type, ValueRef}; -use crate::Result; - -/// ISO 8601 calendar date without timezone => "YYYY-MM-DD" -impl ToSql for NaiveDate { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.format("%F").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone. -impl FromSql for NaiveDate { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value - .as_str() - .and_then(|s| Self::parse_from_str(s, "%F").map_err(FromSqlError::other)) - } -} - -/// ISO 8601 time without timezone => "HH:MM:SS.SSS" -impl ToSql for NaiveTime { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.format("%T%.f").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone. -impl FromSql for NaiveTime { - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_str().and_then(|s| { - let fmt = match s.len() { - 5 => "%H:%M", - 8 => "%T", - _ => "%T%.f", - }; - Self::parse_from_str(s, fmt).map_err(FromSqlError::other) - }) - } -} - -/// ISO 8601 combined date and time without timezone => -/// "YYYY-MM-DD HH:MM:SS.SSS" -impl ToSql for NaiveDateTime { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.format("%F %T%.f").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date -/// and time 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 { - value.as_str().and_then(|s| { - let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { - "%FT%T%.f" - } else { - "%F %T%.f" - }; - - Self::parse_from_str(s, fmt).map_err(FromSqlError::other) - }) - } -} - -/// UTC time => UTC RFC3339 timestamp -/// ("YYYY-MM-DD HH:MM:SS.SSS+00:00"). -impl ToSql for DateTime { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.format("%F %T%.f%:z").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// Local time => UTC RFC3339 timestamp -/// ("YYYY-MM-DD HH:MM:SS.SSS+00:00"). -impl ToSql for DateTime { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.with_timezone(&Utc).format("%F %T%.f%:z").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// Date and time with time zone => RFC3339 timestamp -/// ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM"). -impl ToSql for DateTime { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.format("%F %T%.f%:z").to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") or unix timestamp (in seconds) into `DateTime`. -impl FromSql for DateTime { - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - if value.data_type() == Type::Integer { - return value.as_i64().and_then(|i| { - DateTime::from_timestamp_secs(i).ok_or_else(|| FromSqlError::OutOfRange(i)) - }); - } - { - // Try to parse value as rfc3339 first. - let s = value.as_str()?; - - let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' { - "%FT%T%.f%#z" - } else { - "%F %T%.f%#z" - }; - - if let Ok(dt) = DateTime::parse_from_str(s, fmt) { - 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)) - } -} - -/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") or unix timestamp (in seconds) into `DateTime`. -impl FromSql for DateTime { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let utc_dt = DateTime::::column_result(value)?; - Ok(utc_dt.with_timezone(&Local)) - } -} - -/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") into `DateTime`. -impl FromSql for DateTime { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let s = String::column_result(value)?; - Self::parse_from_rfc3339(s.as_str()) - .or_else(|_| Self::parse_from_str(s.as_str(), "%F %T%.f%:z")) - .map_err(FromSqlError::other) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{ - types::{FromSql, ValueRef}, - Connection, Result, - }; - use chrono::{ - DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, - Timelike, Utc, - }; - - fn checked_memory_handle() -> Result { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER AS (strftime('%s', t)), b BLOB)")?; - Ok(db) - } - - #[test] - fn test_naive_date() -> Result<()> { - let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); - db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("2016-02-23", s); - let t: NaiveDate = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(date, t); - Ok(()) - } - - #[test] - fn test_naive_time() -> Result<()> { - let db = checked_memory_handle()?; - let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); - db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("23:56:04", s); - let v: NaiveTime = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(time, v); - Ok(()) - } - - #[test] - fn test_naive_date_time() -> Result<()> { - let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); - let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); - let dt = NaiveDateTime::new(date, time); - - db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("2016-02-23 23:56:04", s); - let v: NaiveDateTime = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(dt, v); - - db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS" - let hms: NaiveDateTime = db.one_column("SELECT b FROM foo", [])?; - assert_eq!(dt, hms); - Ok(()) - } - - #[test] - fn test_date_time_utc() -> Result<()> { - let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); - let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap(); - let dt = NaiveDateTime::new(date, time); - let utc = Utc.from_utc_datetime(&dt); - - db.execute("INSERT INTO foo (t) VALUES (?1)", [utc])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("2016-02-23 23:56:04.789+00:00", s); - - let v1: DateTime = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(utc, v1); - let v1: DateTime = db.one_column("SELECT i FROM foo", [])?; - assert_eq!(utc.with_nanosecond(0).unwrap(), v1); - - let v2: DateTime = db.one_column("SELECT '2016-02-23 23:56:04.789'", [])?; - assert_eq!(utc, v2); - - let v3: DateTime = db.one_column("SELECT '2016-02-23 23:56:04'", [])?; - assert_eq!(utc - Duration::try_milliseconds(789).unwrap(), v3); - - let v4: DateTime = db.one_column("SELECT '2016-02-23 23:56:04.789+00:00'", [])?; - assert_eq!(utc, v4); - Ok(()) - } - - #[test] - fn test_date_time_local() -> Result<()> { - let db = checked_memory_handle()?; - let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap(); - let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap(); - let dt = NaiveDateTime::new(date, time); - let local = Local.from_local_datetime(&dt).single().unwrap(); - - db.execute("INSERT INTO foo (t) VALUES (?1)", [local])?; - - // Stored string should be in UTC - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert!(s.ends_with("+00:00")); - - let v: DateTime = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(local, v); - let v: DateTime = db.one_column("SELECT i FROM foo", [])?; - assert_eq!(local.with_nanosecond(0).unwrap(), v); - Ok(()) - } - - #[test] - fn test_date_time_fixed() -> Result<()> { - let db = checked_memory_handle()?; - let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap(); - - db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; - - // Stored string should preserve timezone offset - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert!(s.ends_with("+04:00")); - - let v: DateTime = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(time.offset(), v.offset()); - assert_eq!(time, v); - Ok(()) - } - - #[test] - fn test_sqlite_functions() -> Result<()> { - let db = checked_memory_handle()?; - db.one_column::("SELECT CURRENT_TIME", [])?; - db.one_column::("SELECT CURRENT_DATE", [])?; - db.one_column::("SELECT CURRENT_TIMESTAMP", [])?; - db.one_column::, _>("SELECT CURRENT_TIMESTAMP", [])?; - Ok(()) - } - - #[test] - fn test_naive_date_time_param() -> Result<()> { - let db = checked_memory_handle()?; - db.one_column::("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()])?; - Ok(()) - } - - #[test] - fn test_date_time_param() -> Result<()> { - let db = checked_memory_handle()?; - db.one_column::("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()])?; - Ok(()) - } - - #[test] - fn test_lenient_parse_timezone() { - DateTime::::column_result(ValueRef::Text(b"1970-01-01T00:00:00Z")).unwrap(); - DateTime::::column_result(ValueRef::Text(b"1970-01-01T00:00:00+00")).unwrap(); - } -} diff --git a/vendor/rusqlite/src/types/from_sql.rs b/vendor/rusqlite/src/types/from_sql.rs deleted file mode 100644 index 11e76ec..0000000 --- a/vendor/rusqlite/src/types/from_sql.rs +++ /dev/null @@ -1,507 +0,0 @@ -use super::{Value, ValueRef}; -use std::borrow::Cow; -use std::error::Error; -use std::fmt; -use std::str::Utf8Error; - -/// Enum listing possible errors from [`FromSql`] trait. -#[derive(Debug)] -#[non_exhaustive] -pub enum FromSqlError { - /// Error when an SQLite value is requested, but the type of the result - /// cannot be converted to the requested Rust type. - InvalidType, - - /// Error when the i64 value returned by SQLite cannot be stored into the - /// requested type. - OutOfRange(i64), - - /// Error converting a string to UTF-8. - Utf8Error(Utf8Error), - - /// Error when the blob result returned by SQLite cannot be stored into the - /// requested type due to a size mismatch. - InvalidBlobSize { - /// The expected size of the blob. - expected_size: usize, - /// The actual size of the blob that was returned. - blob_size: usize, - }, - - /// An error case available for implementors of the [`FromSql`] trait. - Other(Box), -} - -impl FromSqlError { - /// Converts an arbitrary error type to [`FromSqlError`]. - /// - /// This is a convenience function that boxes and unsizes the error type. It's main purpose is - /// to be usable in the `map_err` method. So instead of - /// `result.map_err(|error| FromSqlError::Other(Box::new(error))` you can write - /// `result.map_err(FromSqlError::other)`. - pub fn other(error: E) -> Self { - Self::Other(Box::new(error)) - } -} - -impl PartialEq for FromSqlError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::InvalidType, Self::InvalidType) => true, - (Self::OutOfRange(n1), Self::OutOfRange(n2)) => n1 == n2, - (Self::Utf8Error(u1), Self::Utf8Error(u2)) => u1 == u2, - ( - Self::InvalidBlobSize { - expected_size: es1, - blob_size: bs1, - }, - Self::InvalidBlobSize { - expected_size: es2, - blob_size: bs2, - }, - ) => es1 == es2 && bs1 == bs2, - (..) => false, - } - } -} - -impl fmt::Display for FromSqlError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Self::InvalidType => write!(f, "Invalid type"), - Self::OutOfRange(i) => write!(f, "Value {i} out of range"), - Self::Utf8Error(ref err) => err.fmt(f), - Self::InvalidBlobSize { - expected_size, - blob_size, - } => { - write!( - f, - "Cannot read {expected_size} byte value out of {blob_size} byte blob" - ) - } - Self::Other(ref err) => err.fmt(f), - } - } -} - -impl Error for FromSqlError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Utf8Error(ref err) => Some(err), - Self::Other(ref err) => Some(&**err), - _ => None, - } - } -} - -/// Result type for implementors of the [`FromSql`] trait. -pub type FromSqlResult = Result; - -/// A trait for types that can be created from a SQLite value. -pub trait FromSql: Sized { - /// Converts SQLite value into Rust value. - fn column_result(value: ValueRef<'_>) -> FromSqlResult; -} - -macro_rules! from_sql_integral( - ($t:ident) => ( - impl FromSql for $t { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let i = i64::column_result(value)?; - i.try_into().map_err(|_| FromSqlError::OutOfRange(i)) - } - } - ); - (non_zero $nz:ty, $z:ty) => ( - impl FromSql for $nz { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let i = <$z>::column_result(value)?; - <$nz>::new(i).ok_or(FromSqlError::OutOfRange(0)) - } - } - ) -); - -from_sql_integral!(i8); -from_sql_integral!(i16); -from_sql_integral!(i32); -// from_sql_integral!(i64); // Not needed because the native type is i64. -from_sql_integral!(isize); -from_sql_integral!(u8); -from_sql_integral!(u16); -from_sql_integral!(u32); -#[cfg(feature = "fallible_uint")] -from_sql_integral!(u64); -#[cfg(feature = "fallible_uint")] -from_sql_integral!(usize); - -from_sql_integral!(non_zero std::num::NonZeroIsize, isize); -from_sql_integral!(non_zero std::num::NonZeroI8, i8); -from_sql_integral!(non_zero std::num::NonZeroI16, i16); -from_sql_integral!(non_zero std::num::NonZeroI32, i32); -from_sql_integral!(non_zero std::num::NonZeroI64, i64); -#[cfg(feature = "i128_blob")] -from_sql_integral!(non_zero std::num::NonZeroI128, i128); - -#[cfg(feature = "fallible_uint")] -from_sql_integral!(non_zero std::num::NonZeroUsize, usize); -from_sql_integral!(non_zero std::num::NonZeroU8, u8); -from_sql_integral!(non_zero std::num::NonZeroU16, u16); -from_sql_integral!(non_zero std::num::NonZeroU32, u32); -#[cfg(feature = "fallible_uint")] -from_sql_integral!(non_zero std::num::NonZeroU64, u64); -// std::num::NonZeroU128 is not supported since u128 isn't either - -impl FromSql for i64 { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_i64() - } -} - -impl FromSql for f32 { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - match value { - ValueRef::Integer(i) => Ok(i as Self), - ValueRef::Real(f) => Ok(f as Self), - _ => Err(FromSqlError::InvalidType), - } - } -} - -impl FromSql for f64 { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - match value { - ValueRef::Integer(i) => Ok(i as Self), - ValueRef::Real(f) => Ok(f), - _ => Err(FromSqlError::InvalidType), - } - } -} - -impl FromSql for bool { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - i64::column_result(value).map(|i| i != 0) - } -} - -impl FromSql for String { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_str().map(ToString::to_string) - } -} - -impl FromSql for Box { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_str().map(Into::into) - } -} - -impl FromSql for std::rc::Rc { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_str().map(Into::into) - } -} - -impl FromSql for std::sync::Arc { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_str().map(Into::into) - } -} - -impl FromSql for Vec { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_blob().map(<[u8]>::to_vec) - } -} - -impl FromSql for Box<[u8]> { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_blob().map(Box::<[u8]>::from) - } -} - -impl FromSql for std::rc::Rc<[u8]> { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_blob().map(std::rc::Rc::<[u8]>::from) - } -} - -impl FromSql for std::sync::Arc<[u8]> { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.as_blob().map(std::sync::Arc::<[u8]>::from) - } -} - -impl FromSql for [u8; N] { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let slice = value.as_blob()?; - slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize { - expected_size: N, - blob_size: slice.len(), - }) - } -} - -#[cfg(feature = "i128_blob")] -impl FromSql for i128 { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let bytes = <[u8; 16]>::column_result(value)?; - Ok(Self::from_be_bytes(bytes) ^ (1_i128 << 127)) - } -} - -#[cfg(feature = "uuid")] -impl FromSql for uuid::Uuid { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - let bytes = <[u8; 16]>::column_result(value)?; - Ok(Self::from_u128(u128::from_be_bytes(bytes))) - } -} - -impl FromSql for Option { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - match value { - ValueRef::Null => Ok(None), - _ => FromSql::column_result(value).map(Some), - } - } -} - -impl FromSql for Cow<'_, T> -where - T: ToOwned, - T::Owned: FromSql, -{ - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - ::column_result(value).map(Cow::Owned) - } -} - -impl FromSql for Value { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value.try_into() - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use super::{FromSql, FromSqlError}; - use crate::{Connection, Error, Result}; - use std::borrow::Cow; - use std::rc::Rc; - use std::sync::Arc; - - #[test] - fn test_integral_ranges() -> Result<()> { - let db = Connection::open_in_memory()?; - - fn check_ranges(db: &Connection, out_of_range: &[i64], in_range: &[i64]) - where - T: Into + FromSql + std::fmt::Debug, - { - for n in out_of_range { - let err = db - .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0)) - .unwrap_err(); - match err { - Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value), - _ => panic!("unexpected error: {err}"), - } - } - for n in in_range { - assert_eq!( - *n, - db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0)) - .unwrap() - .into() - ); - } - } - - check_ranges::(&db, &[-129, 128], &[-128, 0, 1, 127]); - check_ranges::(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]); - check_ranges::( - &db, - &[-2_147_483_649, 2_147_483_648], - &[-2_147_483_648, -1, 0, 1, 2_147_483_647], - ); - check_ranges::(&db, &[-2, -1, 256], &[0, 1, 255]); - check_ranges::(&db, &[-2, -1, 65536], &[0, 1, 65535]); - check_ranges::(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]); - Ok(()) - } - - #[test] - fn test_nonzero_ranges() -> Result<()> { - let db = Connection::open_in_memory()?; - - macro_rules! check_ranges { - ($nz:ty, $out_of_range:expr, $in_range:expr) => { - for &n in $out_of_range { - assert_eq!( - db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)), - Err(Error::IntegralValueOutOfRange(0, n)), - "{}", - std::any::type_name::<$nz>() - ); - } - for &n in $in_range { - let non_zero = <$nz>::new(n).unwrap(); - assert_eq!( - Ok(non_zero), - db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0)) - ); - } - }; - } - - check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]); - check_ranges!( - std::num::NonZeroI16, - &[0, -32769, 32768], - &[-32768, -1, 1, 32767] - ); - check_ranges!( - std::num::NonZeroI32, - &[0, -2_147_483_649, 2_147_483_648], - &[-2_147_483_648, -1, 1, 2_147_483_647] - ); - check_ranges!( - std::num::NonZeroI64, - &[0], - &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN] - ); - check_ranges!( - std::num::NonZeroIsize, - &[0], - &[-2_147_483_648, -1, 1, 2_147_483_647] - ); - check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]); - check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]); - check_ranges!( - std::num::NonZeroU32, - &[0, -2, -1, 4_294_967_296], - &[1, 4_294_967_295] - ); - #[cfg(feature = "fallible_uint")] - check_ranges!( - std::num::NonZeroU64, - &[0, -2, -1, -4_294_967_296], - &[1, 4_294_967_295, i64::MAX as u64] - ); - #[cfg(feature = "fallible_uint")] - check_ranges!( - std::num::NonZeroUsize, - &[0, -2, -1, -4_294_967_296], - &[1, 4_294_967_295] - ); - - Ok(()) - } - - #[test] - fn test_cow() -> Result<()> { - let db = Connection::open_in_memory()?; - - assert_eq!( - db.query_row("SELECT 'this is a string'", [], |r| r - .get::<_, Cow<'_, str>>(0)), - Ok(Cow::Borrowed("this is a string")), - ); - assert_eq!( - db.query_row("SELECT x'09ab20fdee87'", [], |r| r - .get::<_, Cow<'_, [u8]>>(0)), - Ok(Cow::Owned(vec![0x09, 0xab, 0x20, 0xfd, 0xee, 0x87])), - ); - assert_eq!( - db.query_row("SELECT 24.5", [], |r| r.get::<_, Cow<'_, f32>>(0),), - Ok(Cow::Borrowed(&24.5)), - ); - - Ok(()) - } - - #[test] - fn test_heap_slice() -> Result<()> { - let db = Connection::open_in_memory()?; - - assert_eq!( - db.query_row("SELECT 'text'", [], |r| r.get::<_, Box>(0)), - Ok(Box::from("text")), - ); - assert_eq!( - db.query_row("SELECT 'Some string slice!'", [], |r| r - .get::<_, Rc>(0)), - Ok(Rc::from("Some string slice!")), - ); - assert_eq!( - db.query_row("SELECT x'012366779988fedc'", [], |r| r - .get::<_, Rc<[u8]>>(0)), - Ok(Rc::from(b"\x01\x23\x66\x77\x99\x88\xfe\xdc".as_slice())), - ); - - assert_eq!( - db.query_row( - "SELECT x'6120737472696e672043414e206265206120626c6f62'", - [], - |r| r.get::<_, Box<[u8]>>(0) - ), - Ok(b"a string CAN be a blob".to_vec().into_boxed_slice()), - ); - assert_eq!( - db.query_row("SELECT 'This is inside an Arc.'", [], |r| r - .get::<_, Arc>(0)), - Ok(Arc::from("This is inside an Arc.")), - ); - assert_eq!( - db.query_row("SELECT x'afd374'", [], |r| r.get::<_, Arc<[u8]>>(0),), - Ok(Arc::from(b"\xaf\xd3\x74".as_slice())), - ); - - Ok(()) - } - - #[test] - fn from_sql_error() { - use std::error::Error as _; - assert_ne!(FromSqlError::InvalidType, FromSqlError::OutOfRange(0)); - assert_ne!(FromSqlError::OutOfRange(0), FromSqlError::OutOfRange(1)); - assert_ne!( - FromSqlError::InvalidBlobSize { - expected_size: 0, - blob_size: 0 - }, - FromSqlError::InvalidBlobSize { - expected_size: 0, - blob_size: 1 - } - ); - assert!(FromSqlError::InvalidType.source().is_none()); - let err = std::io::Error::from(std::io::ErrorKind::UnexpectedEof); - assert!(FromSqlError::Other(Box::new(err)).source().is_some()); - } -} diff --git a/vendor/rusqlite/src/types/jiff.rs b/vendor/rusqlite/src/types/jiff.rs deleted file mode 100644 index 68c492f..0000000 --- a/vendor/rusqlite/src/types/jiff.rs +++ /dev/null @@ -1,223 +0,0 @@ -//! Convert some `jiff` types. - -use jiff::{ - civil::{Date, DateTime, Time}, - Timestamp, -}; - -use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, Type, ValueRef}; -use crate::Result; - -/// Gregorian calendar date => "YYYY-MM-DD" -impl ToSql for Date { - #[inline] - fn to_sql(&self) -> Result> { - let s = self.to_string(); - Ok(ToSqlOutput::from(s)) - } -} - -/// "YYYY-MM-DD" => Gregorian calendar date. -impl FromSql for Date { - #[inline] - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value - .as_str() - .and_then(|s| s.parse().map_err(FromSqlError::other)) - } -} -/// time => "HH:MM:SS.SSS" -impl ToSql for Time { - #[inline] - fn to_sql(&self) -> Result> { - let date_str = self.to_string(); - Ok(ToSqlOutput::from(date_str)) - } -} - -/// "HH:MM:SS.SSS" => time. -impl FromSql for Time { - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value - .as_str() - .and_then(|s| s.parse().map_err(FromSqlError::other)) - } -} - -/// Gregorian datetime => "YYYY-MM-DDTHH:MM:SS.SSS" -impl ToSql for DateTime { - #[inline] - fn to_sql(&self) -> Result> { - let s = self.to_string(); - Ok(ToSqlOutput::from(s)) - } -} - -/// "YYYY-MM-DDTHH:MM:SS.SSS" => Gregorian datetime. -impl FromSql for DateTime { - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - value - .as_str() - .and_then(|s| s.parse().map_err(FromSqlError::other)) - } -} - -/// UTC time => UTC RFC3339 timestamp -/// ("YYYY-MM-DDTHH:MM:SS.SSSZ"). -impl ToSql for Timestamp { - #[inline] - fn to_sql(&self) -> Result> { - Ok(ToSqlOutput::from(self.to_string())) - } -} - -/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") or unix timestamp (in seconds) into `Timestamp`. -impl FromSql for Timestamp { - fn column_result(value: ValueRef<'_>) -> FromSqlResult { - if value.data_type() == Type::Integer { - return value - .as_i64() - .and_then(|i| Timestamp::from_second(i).map_err(FromSqlError::other)); - } - value - .as_str()? - .parse::() - .map_err(FromSqlError::other) - } -} - -#[cfg(test)] -mod test { - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - use wasm_bindgen_test::wasm_bindgen_test as test; - - use crate::{Connection, Result}; - use jiff::{ - civil::{Date, DateTime, Time}, - Timestamp, - }; - - fn checked_memory_handle() -> Result { - let db = Connection::open_in_memory()?; - db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER AS (strftime('%s', t)), b BLOB)")?; - Ok(db) - } - - #[test] - fn test_date() -> Result<()> { - let db = checked_memory_handle()?; - let date = Date::constant(2016, 2, 23); - db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("2016-02-23", s); - let t: Date = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(date, t); - - db.execute("UPDATE foo set b = date(t)", [])?; - let t: Date = db.one_column("SELECT b FROM foo", [])?; - assert_eq!(date, t); - - let r: Result = db.one_column("SELECT '2023-02-29'", []); - assert!(r.is_err()); - Ok(()) - } - - #[test] - fn test_time() -> Result<()> { - let db = checked_memory_handle()?; - let time = Time::constant(23, 56, 4, 0); - db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?; - - let s: String = db.one_column("SELECT t FROM foo", [])?; - assert_eq!("23:56:04", s); - let v: Time = db.one_column("SELECT t FROM foo", [])?; - assert_eq!(time, v); - - db.execute("UPDATE foo set b = time(t)", [])?; - let v: Time = db.one_column("SELECT b FROM foo", [])?; - assert_eq!(time, v); - - let r: Result