mirror of
https://github.com/isar/rusqlite.git
synced 2025-06-17 00:42:36 +08:00
Merge branch 'master' into sub_type
This commit is contained in:
commit
5c1fefbef8
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
78
.github/workflows/main.yml
vendored
78
.github/workflows/main.yml
vendored
@ -17,6 +17,9 @@ env:
|
|||||||
# smaller. (TODO: use -Cdebuginfo=0 if it doesn't make backtraces useless)
|
# smaller. (TODO: use -Cdebuginfo=0 if it doesn't make backtraces useless)
|
||||||
RUSTFLAGS: -Cdebuginfo=1
|
RUSTFLAGS: -Cdebuginfo=1
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Test ${{ matrix.target }}
|
name: Test ${{ matrix.target }}
|
||||||
@ -36,7 +39,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# This has a matcher for test panics, so we use it even though elsewhere
|
# This has a matcher for test panics, so we use it even though elsewhere
|
||||||
# we use actions-rs/toolchain.
|
# we use actions-rs/toolchain.
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
@ -46,9 +49,9 @@ jobs:
|
|||||||
# The `{ sharedKey: ... }` allows different actions to share the cache.
|
# The `{ sharedKey: ... }` allows different actions to share the cache.
|
||||||
# We're using a `fullBuild` key mostly as a "this needs to do the
|
# We're using a `fullBuild` key mostly as a "this needs to do the
|
||||||
# complete" that needs to do the complete build (that is, including
|
# complete" that needs to do the complete build (that is, including
|
||||||
# `--features 'bundled-full session buildtime_bindgen`), which is very
|
# `--features 'bundled-full session buildtime_bindgen'`), which is very
|
||||||
# slow, and has several deps.
|
# slow, and has several deps.
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { sharedKey: fullBuild }
|
with: { sharedKey: fullBuild }
|
||||||
|
|
||||||
- run: cargo build --features bundled --workspace --all-targets --verbose
|
- run: cargo build --features bundled --workspace --all-targets --verbose
|
||||||
@ -80,14 +83,14 @@ jobs:
|
|||||||
# TODO: find a way to test this on windows :(
|
# TODO: find a way to test this on windows :(
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# This has a matcher for test panics, so we use it even though elsewhere
|
# This has a matcher for test panics, so we use it even though elsewhere
|
||||||
# we use actions-rs/toolchain.
|
# we use actions-rs/toolchain.
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
with:
|
with:
|
||||||
rust-version: stable${{ matrix.host }}
|
rust-version: stable${{ matrix.host }}
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { sharedKey: fullBuild }
|
with: { sharedKey: fullBuild }
|
||||||
|
|
||||||
- run: cargo test --features 'bundled-sqlcipher' --workspace --all-targets --verbose
|
- run: cargo test --features 'bundled-sqlcipher' --workspace --all-targets --verbose
|
||||||
@ -113,9 +116,9 @@ jobs:
|
|||||||
name: Test with winsqlite3
|
name: Test with winsqlite3
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
# TODO: Should this test GNU toolchain? What about +crt-static?
|
# TODO: Should this test GNU toolchain? What about +crt-static?
|
||||||
# TODO: Is it worth testing other features?
|
# TODO: Is it worth testing other features?
|
||||||
- run: cargo build --features winsqlite3 --workspace --all-targets --verbose
|
- run: cargo build --features winsqlite3 --workspace --all-targets --verbose
|
||||||
@ -125,9 +128,9 @@ jobs:
|
|||||||
name: Test with sqlcipher
|
name: Test with sqlcipher
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: sudo apt-get install sqlcipher libsqlcipher-dev
|
- run: sudo apt-get install sqlcipher libsqlcipher-dev
|
||||||
- run: sqlcipher --version
|
- run: sqlcipher --version
|
||||||
# TODO: Is it worth testing other features?
|
# TODO: Is it worth testing other features?
|
||||||
@ -138,13 +141,13 @@ jobs:
|
|||||||
name: Address Sanitizer
|
name: Address Sanitizer
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# Need nightly rust.
|
# Need nightly rust.
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
with:
|
with:
|
||||||
rust-version: nightly
|
rust-version: nightly
|
||||||
components: rust-src
|
components: rust-src
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Tests with asan
|
- name: Tests with asan
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: -Zsanitizer=address -Cdebuginfo=0
|
RUSTFLAGS: -Zsanitizer=address -Cdebuginfo=0
|
||||||
@ -162,11 +165,11 @@ jobs:
|
|||||||
name: Clippy
|
name: Clippy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo clippy --all-targets --workspace --features bundled -- -D warnings
|
- run: cargo clippy --all-targets --workspace --features bundled -- -D warnings
|
||||||
# Clippy with all non-conflicting features
|
# Clippy with all non-conflicting features
|
||||||
- run: cargo clippy --all-targets --workspace --features 'bundled-full session buildtime_bindgen' -- -D warnings
|
- run: cargo clippy --all-targets --workspace --features 'bundled-full session buildtime_bindgen' -- -D warnings
|
||||||
@ -176,7 +179,7 @@ jobs:
|
|||||||
name: Format
|
name: Format
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
with:
|
with:
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
@ -187,9 +190,9 @@ jobs:
|
|||||||
name: Docs
|
name: Docs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v1
|
||||||
- uses: Swatinem/rust-cache@v1
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { sharedKey: fullBuild }
|
with: { sharedKey: fullBuild }
|
||||||
- run: cargo doc --features 'bundled-full session buildtime_bindgen' --no-deps
|
- run: cargo doc --features 'bundled-full session buildtime_bindgen' --no-deps
|
||||||
env: { RUSTDOCFLAGS: -Dwarnings }
|
env: { RUSTDOCFLAGS: -Dwarnings }
|
||||||
@ -198,15 +201,38 @@ jobs:
|
|||||||
name: Generate code coverage
|
name: Generate code coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
# TODO: we don't use caching here because it's unclear if it will cause
|
|
||||||
# the coverage to get less accurate (this is the case for some coverage
|
|
||||||
# tools, although possibly not tarpaulin?)
|
|
||||||
- name: Run cargo-tarpaulin
|
|
||||||
uses: actions-rs/tarpaulin@v0.1
|
|
||||||
with:
|
with:
|
||||||
args: '--features "bundled-full session buildtime_bindgen"'
|
components: 'llvm-tools-preview'
|
||||||
|
- uses: taiki-e/install-action@main
|
||||||
|
with:
|
||||||
|
tool: grcov
|
||||||
|
- name: Run tests for coverage
|
||||||
|
run: |
|
||||||
|
cargo test --verbose
|
||||||
|
cargo test --features="bundled-full" --verbose
|
||||||
|
cargo test --features="bundled-full session buildtime_bindgen" --verbose
|
||||||
|
cargo test --features="bundled-sqlcipher-vendored-openssl" --verbose
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -Cinstrument-coverage
|
||||||
|
RUSTDOCFLAGS: -Cinstrument-coverage
|
||||||
|
LLVM_PROFILE_FILE: rusqlite-%p-%m.profraw
|
||||||
|
- name: Produce coverage info
|
||||||
|
run: |
|
||||||
|
grcov $(find . -name "rusqlite-*.profraw" -print) \
|
||||||
|
-s . \
|
||||||
|
--branch \
|
||||||
|
--ignore-not-existing \
|
||||||
|
--ignore='target/*' \
|
||||||
|
--ignore='benches/*' \
|
||||||
|
--ignore='/*' \
|
||||||
|
--binary-path ./target/debug/ \
|
||||||
|
--excl-line='#\[derive' \
|
||||||
|
-t lcov \
|
||||||
|
-o lcov.info
|
||||||
- name: Upload to codecov.io
|
- name: Upload to codecov.io
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
files: lcov.info
|
||||||
|
fail_ci_if_error: true
|
||||||
|
15
Cargo.toml
15
Cargo.toml
@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
# Note: Update version in README.md when you change this.
|
# Note: Update version in README.md when you change this.
|
||||||
version = "0.28.0"
|
version = "0.29.0"
|
||||||
authors = ["The rusqlite developers"]
|
authors = ["The rusqlite developers"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
description = "Ergonomic wrapper for SQLite"
|
description = "Ergonomic wrapper for SQLite"
|
||||||
repository = "https://github.com/rusqlite/rusqlite"
|
repository = "https://github.com/rusqlite/rusqlite"
|
||||||
documentation = "http://docs.rs/rusqlite/"
|
documentation = "https://docs.rs/rusqlite/"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["sqlite", "database", "ffi"]
|
keywords = ["sqlite", "database", "ffi"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@ -77,6 +77,8 @@ column_decltype = []
|
|||||||
wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"]
|
wasm32-wasi-vfs = ["libsqlite3-sys/wasm32-wasi-vfs"]
|
||||||
# Note: doesn't support 32-bit.
|
# Note: doesn't support 32-bit.
|
||||||
winsqlite3 = ["libsqlite3-sys/winsqlite3"]
|
winsqlite3 = ["libsqlite3-sys/winsqlite3"]
|
||||||
|
# 3.23.0
|
||||||
|
serialize = ["modern_sqlite"]
|
||||||
|
|
||||||
# Helper feature for enabling most non-build-related optional features
|
# Helper feature for enabling most non-build-related optional features
|
||||||
# or dependencies (except `session`). This is useful for running tests / clippy
|
# or dependencies (except `session`). This is useful for running tests / clippy
|
||||||
@ -112,14 +114,13 @@ bundled-full = ["modern-full", "bundled"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = { version = "0.3.0", features = ["formatting", "macros", "parsing"], optional = true }
|
time = { version = "0.3.0", features = ["formatting", "macros", "parsing"], optional = true }
|
||||||
bitflags = "1.2"
|
bitflags = "2.0"
|
||||||
hashlink = "0.8"
|
hashlink = "0.8"
|
||||||
chrono = { version = "0.4", optional = true, default-features = false, features = ["clock"] }
|
chrono = { version = "0.4", optional = true, default-features = false, features = ["clock"] }
|
||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
csv = { version = "1.1", optional = true }
|
csv = { version = "1.1", optional = true }
|
||||||
url = { version = "2.1", optional = true }
|
url = { version = "2.1", optional = true }
|
||||||
lazy_static = { version = "1.4", optional = true }
|
fallible-iterator = "0.3"
|
||||||
fallible-iterator = "0.2"
|
|
||||||
fallible-streaming-iterator = "0.1"
|
fallible-streaming-iterator = "0.1"
|
||||||
uuid = { version = "1.0", optional = true }
|
uuid = { version = "1.0", optional = true }
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
@ -137,7 +138,7 @@ bencher = "0.1"
|
|||||||
|
|
||||||
[dependencies.libsqlite3-sys]
|
[dependencies.libsqlite3-sys]
|
||||||
path = "libsqlite3-sys"
|
path = "libsqlite3-sys"
|
||||||
version = "0.25.0"
|
version = "0.26.0"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "config_log"
|
name = "config_log"
|
||||||
|
14
Changelog.md
14
Changelog.md
@ -15,7 +15,7 @@ For version 0.15.0 and above, see [Releases](https://github.com/rusqlite/rusqlit
|
|||||||
* Add DropBehavior::Panic to enforce intentional commit or rollback.
|
* Add DropBehavior::Panic to enforce intentional commit or rollback.
|
||||||
* Implement `sqlite3_update_hook` (#260, #328), `sqlite3_commit_hook` and `sqlite3_rollback_hook`.
|
* Implement `sqlite3_update_hook` (#260, #328), `sqlite3_commit_hook` and `sqlite3_rollback_hook`.
|
||||||
* Add support to unlock notification behind `unlock_notify` feature (#294, #331).
|
* Add support to unlock notification behind `unlock_notify` feature (#294, #331).
|
||||||
* Make `Statement::column_index` case insensitive (#330).
|
* Make `Statement::column_index` case-insensitive (#330).
|
||||||
* Add comment to justify `&mut Connection` in `Transaction`.
|
* Add comment to justify `&mut Connection` in `Transaction`.
|
||||||
* Fix `tyvar_behind_raw_pointer` warnings.
|
* Fix `tyvar_behind_raw_pointer` warnings.
|
||||||
* Fix handful of clippy warnings.
|
* Fix handful of clippy warnings.
|
||||||
@ -29,7 +29,7 @@ For version 0.15.0 and above, see [Releases](https://github.com/rusqlite/rusqlit
|
|||||||
# Version 0.13.0 (2017-11-13)
|
# Version 0.13.0 (2017-11-13)
|
||||||
|
|
||||||
* Added ToSqlConversionFailure case to Error enum.
|
* Added ToSqlConversionFailure case to Error enum.
|
||||||
* Now depends on chrono 0.4, bitflats 1.0, and (optionally) cc 1.0 / bindgen 0.31.
|
* Now depends on chrono 0.4, bitflags 1.0, and (optionally) cc 1.0 / bindgen 0.31.
|
||||||
* The ToSql/FromSql implementations for time::Timespec now include
|
* The ToSql/FromSql implementations for time::Timespec now include
|
||||||
and expect fractional seconds and timezone in the serialized string.
|
and expect fractional seconds and timezone in the serialized string.
|
||||||
* The RowIndex type used in Row::get is now publicly exported.
|
* The RowIndex type used in Row::get is now publicly exported.
|
||||||
@ -61,18 +61,18 @@ For version 0.15.0 and above, see [Releases](https://github.com/rusqlite/rusqlit
|
|||||||
* Adds `version()` and `version_number()` functions for querying the version of SQLite in use.
|
* Adds `version()` and `version_number()` functions for querying the version of SQLite in use.
|
||||||
* Adds the `limits` feature, exposing `limit()` and `set_limit()` methods on `Connection`.
|
* Adds the `limits` feature, exposing `limit()` and `set_limit()` methods on `Connection`.
|
||||||
* Updates to `libsqlite3-sys` 0.7.0, which runs rust-bindgen at build-time instead of assuming the
|
* Updates to `libsqlite3-sys` 0.7.0, which runs rust-bindgen at build-time instead of assuming the
|
||||||
precense of all expected SQLite constants and functions.
|
presence of all expected SQLite constants and functions.
|
||||||
* Clarifies supported SQLite versions. Running with SQLite older than 3.6.8 now panics, and
|
* Clarifies supported SQLite versions. Running with SQLite older than 3.6.8 now panics, and
|
||||||
some features will not compile unless a sufficiently-recent SQLite version is used. See
|
some features will not compile unless a sufficiently-recent SQLite version is used. See
|
||||||
the README for requirements of particular features.
|
the README for requirements of particular features.
|
||||||
* When running with SQLite 3.6.x, rusqlite attempts to perform SQLite initialization. If it fails,
|
* When running with SQLite 3.6.x, rusqlite attempts to perform SQLite initialization. If it fails,
|
||||||
rusqlite will panic since it cannot ensure the threading mode for SQLite. This check can by
|
rusqlite will panic since it cannot ensure the threading mode for SQLite. This check can be
|
||||||
skipped by calling the unsafe function `rusqlite::bypass_sqlite_initialization()`. This is
|
skipped by calling the unsafe function `rusqlite::bypass_sqlite_initialization()`. This is
|
||||||
technically a breaking change but is unlikely to affect anyone in practice, since prior to this
|
technically a breaking change but is unlikely to affect anyone in practice, since prior to this
|
||||||
version the check that rusqlite was using would cause a segfault if linked against a SQLite
|
version the check that rusqlite was using would cause a segfault if linked against a SQLite
|
||||||
older than 3.7.0.
|
older than 3.7.0.
|
||||||
* rusqlite now performs a one-time check (prior to the first connection attempt) that the runtime
|
* rusqlite now performs a one-time check (prior to the first connection attempt) that the runtime
|
||||||
SQLite version is at least as new as the SQLite version found at buildtime. This check can by
|
SQLite version is at least as new as the SQLite version found at buildtime. This check can be
|
||||||
skipped by calling the unsafe function `rusqlite::bypass_sqlite_version_check()`.
|
skipped by calling the unsafe function `rusqlite::bypass_sqlite_version_check()`.
|
||||||
* Removes the `libc` dependency in favor of using `std::os::raw`
|
* Removes the `libc` dependency in favor of using `std::os::raw`
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ For version 0.15.0 and above, see [Releases](https://github.com/rusqlite/rusqlit
|
|||||||
This behavior is more correct. Previously there were runtime checks to prevent misuse, but
|
This behavior is more correct. Previously there were runtime checks to prevent misuse, but
|
||||||
other changes in this release to reset statements as soon as possible introduced yet another
|
other changes in this release to reset statements as soon as possible introduced yet another
|
||||||
hazard related to the lack of these lifetime connections. We were already recommending the
|
hazard related to the lack of these lifetime connections. We were already recommending the
|
||||||
use of `query_map` and `query_and_then` over raw `query`; both of theose still return handles
|
use of `query_map` and `query_and_then` over raw `query`; both of those still return handles
|
||||||
that implement `Iterator`.
|
that implement `Iterator`.
|
||||||
* BREAKING CHANGE: `Transaction::savepoint()` now returns a `Savepoint` instead of another
|
* BREAKING CHANGE: `Transaction::savepoint()` now returns a `Savepoint` instead of another
|
||||||
`Transaction`. Unlike `Transaction`, `Savepoint`s can be rolled back while keeping the current
|
`Transaction`. Unlike `Transaction`, `Savepoint`s can be rolled back while keeping the current
|
||||||
@ -239,7 +239,7 @@ For version 0.15.0 and above, see [Releases](https://github.com/rusqlite/rusqlit
|
|||||||
|
|
||||||
* Add `column_names()` to `SqliteStatement`.
|
* Add `column_names()` to `SqliteStatement`.
|
||||||
* By default, include `SQLITE_OPEN_NO_MUTEX` and `SQLITE_OPEN_URI` flags when opening a
|
* By default, include `SQLITE_OPEN_NO_MUTEX` and `SQLITE_OPEN_URI` flags when opening a
|
||||||
new conneciton.
|
new connection.
|
||||||
* Fix generated bindings (e.g., `sqlite3_exec` was wrong).
|
* Fix generated bindings (e.g., `sqlite3_exec` was wrong).
|
||||||
* Use now-generated `sqlite3_destructor_type` to define `SQLITE_STATIC` and `SQLITE_TRANSIENT`.
|
* Use now-generated `sqlite3_destructor_type` to define `SQLITE_STATIC` and `SQLITE_TRANSIENT`.
|
||||||
|
|
||||||
|
12
README.md
12
README.md
@ -27,7 +27,7 @@ In your Cargo.toml:
|
|||||||
# That said, it's not ideal for all scenarios and in particular, generic
|
# That said, it's not ideal for all scenarios and in particular, generic
|
||||||
# libraries built around `rusqlite` should probably not enable it, which
|
# libraries built around `rusqlite` should probably not enable it, which
|
||||||
# is why it is not a default feature -- it could become hard to disable.
|
# is why it is not a default feature -- it could become hard to disable.
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
rusqlite = { version = "0.29.0", features = ["bundled"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple example usage:
|
Simple example usage:
|
||||||
@ -113,8 +113,8 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
|
|||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
|
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).
|
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
||||||
* `time` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
* `time` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
||||||
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for the
|
and [`ToSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.ToSql.html) for various
|
||||||
`time::OffsetDateTime` type from the [`time` crate](https://crates.io/crates/time).
|
types from the [`time` crate](https://crates.io/crates/time).
|
||||||
* `url` implements [`FromSql`](https://docs.rs/rusqlite/~0/rusqlite/types/trait.FromSql.html)
|
* `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
|
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).
|
`Url` type from the [`url` crate](https://crates.io/crates/url).
|
||||||
@ -149,11 +149,11 @@ 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
|
* 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
|
[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
|
link against that. This source is embedded in the `libsqlite3-sys` crate and
|
||||||
is currently SQLite 3.39.0 (as of `rusqlite` 0.28.0 / `libsqlite3-sys`
|
is currently SQLite 3.41.2 (as of `rusqlite` 0.29.0 / `libsqlite3-sys`
|
||||||
0.25.0). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
|
0.26.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
|
```toml
|
||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
version = "0.28.0"
|
version = "0.29.0"
|
||||||
features = ["bundled"]
|
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 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.)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.25.2"
|
version = "0.26.0"
|
||||||
authors = ["The rusqlite developers"]
|
authors = ["The rusqlite developers"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/rusqlite/rusqlite"
|
repository = "https://github.com/rusqlite/rusqlite"
|
||||||
@ -44,7 +44,7 @@ winsqlite3 = []
|
|||||||
openssl-sys = { version = "0.9", optional = true }
|
openssl-sys = { version = "0.9", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = { version = "0.61", optional = true, default-features = false, features = ["runtime"] }
|
bindgen = { version = "0.66", optional = true, default-features = false, features = ["runtime"] }
|
||||||
pkg-config = { version = "0.3.19", optional = true }
|
pkg-config = { version = "0.3.19", optional = true }
|
||||||
cc = { version = "1.0", optional = true }
|
cc = { version = "1.0", optional = true }
|
||||||
vcpkg = { version = "0.2", optional = true }
|
vcpkg = { version = "0.2", optional = true }
|
||||||
|
@ -1,5 +1,29 @@
|
|||||||
/* automatically generated by rust-bindgen 0.60.1 */
|
/* automatically generated by rust-bindgen 0.60.1 */
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn sqlite3_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn sqlite3_cancel_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.14.0\0";
|
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.14.0\0";
|
||||||
pub const SQLITE_VERSION_NUMBER: i32 = 3014000;
|
pub const SQLITE_VERSION_NUMBER: i32 = 3014000;
|
||||||
pub const SQLITE_SOURCE_ID: &[u8; 61usize] =
|
pub const SQLITE_SOURCE_ID: &[u8; 61usize] =
|
||||||
@ -1729,16 +1753,6 @@ extern "C" {
|
|||||||
onoff: ::std::os::raw::c_int,
|
onoff: ::std::os::raw::c_int,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
pub fn sqlite3_auto_extension(
|
|
||||||
xEntryPoint: ::std::option::Option<unsafe extern "C" fn()>,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn sqlite3_cancel_auto_extension(
|
|
||||||
xEntryPoint: ::std::option::Option<unsafe extern "C" fn()>,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn sqlite3_reset_auto_extension();
|
pub fn sqlite3_reset_auto_extension();
|
||||||
}
|
}
|
||||||
@ -1879,10 +1893,10 @@ pub struct sqlite3_module {
|
|||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info {
|
pub struct sqlite3_index_info {
|
||||||
pub nConstraint: ::std::os::raw::c_int,
|
pub nConstraint: ::std::os::raw::c_int,
|
||||||
pub aConstraint: *mut sqlite3_index_info_sqlite3_index_constraint,
|
pub aConstraint: *mut sqlite3_index_constraint,
|
||||||
pub nOrderBy: ::std::os::raw::c_int,
|
pub nOrderBy: ::std::os::raw::c_int,
|
||||||
pub aOrderBy: *mut sqlite3_index_info_sqlite3_index_orderby,
|
pub aOrderBy: *mut sqlite3_index_orderby,
|
||||||
pub aConstraintUsage: *mut sqlite3_index_info_sqlite3_index_constraint_usage,
|
pub aConstraintUsage: *mut sqlite3_index_constraint_usage,
|
||||||
pub idxNum: ::std::os::raw::c_int,
|
pub idxNum: ::std::os::raw::c_int,
|
||||||
pub idxStr: *mut ::std::os::raw::c_char,
|
pub idxStr: *mut ::std::os::raw::c_char,
|
||||||
pub needToFreeIdxStr: ::std::os::raw::c_int,
|
pub needToFreeIdxStr: ::std::os::raw::c_int,
|
||||||
@ -1894,7 +1908,7 @@ pub struct sqlite3_index_info {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_constraint {
|
pub struct sqlite3_index_constraint {
|
||||||
pub iColumn: ::std::os::raw::c_int,
|
pub iColumn: ::std::os::raw::c_int,
|
||||||
pub op: ::std::os::raw::c_uchar,
|
pub op: ::std::os::raw::c_uchar,
|
||||||
pub usable: ::std::os::raw::c_uchar,
|
pub usable: ::std::os::raw::c_uchar,
|
||||||
@ -1902,13 +1916,13 @@ pub struct sqlite3_index_info_sqlite3_index_constraint {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_orderby {
|
pub struct sqlite3_index_orderby {
|
||||||
pub iColumn: ::std::os::raw::c_int,
|
pub iColumn: ::std::os::raw::c_int,
|
||||||
pub desc: ::std::os::raw::c_uchar,
|
pub desc: ::std::os::raw::c_uchar,
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_constraint_usage {
|
pub struct sqlite3_index_constraint_usage {
|
||||||
pub argvIndex: ::std::os::raw::c_int,
|
pub argvIndex: ::std::os::raw::c_int,
|
||||||
pub omit: ::std::os::raw::c_uchar,
|
pub omit: ::std::os::raw::c_uchar,
|
||||||
}
|
}
|
||||||
|
@ -26,17 +26,26 @@ fn is_compiler(compiler_name: &str) -> bool {
|
|||||||
env::var("CARGO_CFG_TARGET_ENV").map_or(false, |v| v == compiler_name)
|
env::var("CARGO_CFG_TARGET_ENV").map_or(false, |v| v == compiler_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy bindgen file from `dir` to `out_path`.
|
||||||
|
fn copy_bindings<T: AsRef<Path>>(dir: &str, bindgen_name: &str, out_path: T) {
|
||||||
|
std::fs::copy(format!("{dir}/{bindgen_name}"), out_path)
|
||||||
|
.expect("Could not copy bindings to output directory");
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
||||||
if cfg!(feature = "in_gecko") {
|
if cfg!(feature = "in_gecko") {
|
||||||
// When inside mozilla-central, we are included into the build with
|
// When inside mozilla-central, we are included into the build with
|
||||||
// sqlite3.o directly, so we don't want to provide any linker arguments.
|
// sqlite3.o directly, so we don't want to provide any linker arguments.
|
||||||
std::fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
|
copy_bindings("sqlite3", "bindgen_bundled_version.rs", out_path);
|
||||||
.expect("Could not copy bindings to output directory");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if cfg!(all(
|
|
||||||
|
println!("cargo:rerun-if-env-changed=LIBSQLITE3_SYS_USE_PKG_CONFIG");
|
||||||
|
if env::var_os("LIBSQLITE3_SYS_USE_PKG_CONFIG").map_or(false, |s| s != "0") {
|
||||||
|
build_linked::main(&out_dir, &out_path);
|
||||||
|
} else if cfg!(all(
|
||||||
feature = "sqlcipher",
|
feature = "sqlcipher",
|
||||||
not(feature = "bundled-sqlcipher")
|
not(feature = "bundled-sqlcipher")
|
||||||
)) {
|
)) {
|
||||||
@ -92,17 +101,13 @@ mod build_bundled {
|
|||||||
#[cfg(feature = "buildtime_bindgen")]
|
#[cfg(feature = "buildtime_bindgen")]
|
||||||
{
|
{
|
||||||
use super::{bindings, HeaderLocation};
|
use super::{bindings, HeaderLocation};
|
||||||
let header = HeaderLocation::FromPath(format!("{}/sqlite3.h", lib_name));
|
let header = HeaderLocation::FromPath(lib_name.to_owned());
|
||||||
bindings::write_to_out_dir(header, out_path);
|
bindings::write_to_out_dir(header, out_path);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "buildtime_bindgen"))]
|
#[cfg(not(feature = "buildtime_bindgen"))]
|
||||||
{
|
{
|
||||||
use std::fs;
|
super::copy_bindings(lib_name, "bindgen_bundled_version.rs", out_path);
|
||||||
fs::copy(format!("{lib_name}/bindgen_bundled_version.rs"), out_path)
|
|
||||||
.expect("Could not copy bindings to output directory");
|
|
||||||
}
|
}
|
||||||
// println!("cargo:rerun-if-changed=sqlite3/sqlite3.c");
|
|
||||||
// println!("cargo:rerun-if-changed=sqlcipher/sqlite3.c");
|
|
||||||
println!("cargo:rerun-if-changed={lib_name}/sqlite3.c");
|
println!("cargo:rerun-if-changed={lib_name}/sqlite3.c");
|
||||||
println!("cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c");
|
println!("cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c");
|
||||||
let mut cfg = cc::Build::new();
|
let mut cfg = cc::Build::new();
|
||||||
@ -181,16 +186,10 @@ mod build_bundled {
|
|||||||
cfg.include(env::var("DEP_OPENSSL_INCLUDE").unwrap());
|
cfg.include(env::var("DEP_OPENSSL_INCLUDE").unwrap());
|
||||||
// cargo will resolve downstream to the static lib in
|
// cargo will resolve downstream to the static lib in
|
||||||
// openssl-sys
|
// openssl-sys
|
||||||
} else if is_windows {
|
|
||||||
// Windows without `-vendored-openssl` takes this to link against a prebuilt
|
|
||||||
// OpenSSL lib
|
|
||||||
cfg.include(inc_dir.to_string_lossy().as_ref());
|
|
||||||
let lib = lib_dir.join("libcrypto.lib");
|
|
||||||
cfg.flag(lib.to_string_lossy().as_ref());
|
|
||||||
} else if use_openssl {
|
} else if use_openssl {
|
||||||
cfg.include(inc_dir.to_string_lossy().as_ref());
|
cfg.include(inc_dir.to_string_lossy().as_ref());
|
||||||
// branch not taken on Windows, just `crypto` is fine.
|
let lib_name = if is_windows { "libcrypto" } else { "crypto" };
|
||||||
println!("cargo:rustc-link-lib=dylib=crypto");
|
println!("cargo:rustc-link-lib=dylib={}", lib_name);
|
||||||
println!("cargo:rustc-link-search={}", lib_dir.to_string_lossy());
|
println!("cargo:rustc-link-search={}", lib_dir.to_string_lossy());
|
||||||
} else if is_apple {
|
} else if is_apple {
|
||||||
cfg.flag("-DSQLCIPHER_CRYPTO_CC");
|
cfg.flag("-DSQLCIPHER_CRYPTO_CC");
|
||||||
@ -242,7 +241,7 @@ mod build_bundled {
|
|||||||
cfg.flag("-DHAVE_LOCALTIME_R");
|
cfg.flag("-DHAVE_LOCALTIME_R");
|
||||||
}
|
}
|
||||||
// Target wasm32-wasi can't compile the default VFS
|
// Target wasm32-wasi can't compile the default VFS
|
||||||
if is_compiler("wasm32-wasi") {
|
if env::var("TARGET").map_or(false, |v| v == "wasm32-wasi") {
|
||||||
cfg.flag("-DSQLITE_OS_OTHER")
|
cfg.flag("-DSQLITE_OS_OTHER")
|
||||||
// https://github.com/rust-lang/rust/issues/74393
|
// https://github.com/rust-lang/rust/issues/74393
|
||||||
.flag("-DLONGDOUBLE_TYPE=double");
|
.flag("-DLONGDOUBLE_TYPE=double");
|
||||||
@ -270,6 +269,11 @@ mod build_bundled {
|
|||||||
}
|
}
|
||||||
println!("cargo:rerun-if-env-changed=SQLITE_MAX_EXPR_DEPTH");
|
println!("cargo:rerun-if-env-changed=SQLITE_MAX_EXPR_DEPTH");
|
||||||
|
|
||||||
|
if let Ok(limit) = env::var("SQLITE_MAX_COLUMN") {
|
||||||
|
cfg.flag(&format!("-DSQLITE_MAX_COLUMN={limit}"));
|
||||||
|
}
|
||||||
|
println!("cargo:rerun-if-env-changed=SQLITE_MAX_COLUMN");
|
||||||
|
|
||||||
if let Ok(extras) = env::var("LIBSQLITE3_FLAGS") {
|
if let Ok(extras) = env::var("LIBSQLITE3_FLAGS") {
|
||||||
for extra in extras.split_whitespace() {
|
for extra in extras.split_whitespace() {
|
||||||
if extra.starts_with("-D") || extra.starts_with("-U") {
|
if extra.starts_with("-D") || extra.starts_with("-U") {
|
||||||
@ -344,7 +348,7 @@ impl From<HeaderLocation> for String {
|
|||||||
header
|
header
|
||||||
}
|
}
|
||||||
HeaderLocation::Wrapper => "wrapper.h".into(),
|
HeaderLocation::Wrapper => "wrapper.h".into(),
|
||||||
HeaderLocation::FromPath(path) => path,
|
HeaderLocation::FromPath(path) => format!("{}/sqlite3.h", path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,11 +375,7 @@ mod build_linked {
|
|||||||
// on buildtime_bindgen instead, but this is still supported as we
|
// on buildtime_bindgen instead, but this is still supported as we
|
||||||
// have runtime version checks and there are good reasons to not
|
// have runtime version checks and there are good reasons to not
|
||||||
// want to run bindgen.
|
// want to run bindgen.
|
||||||
std::fs::copy(
|
super::copy_bindings(lib_name(), "bindgen_bundled_version.rs", out_path);
|
||||||
format!("{}/bindgen_bundled_version.rs", lib_name()),
|
|
||||||
out_path,
|
|
||||||
)
|
|
||||||
.expect("Could not copy bindings to output directory");
|
|
||||||
} else {
|
} else {
|
||||||
bindings::write_to_out_dir(header, out_path);
|
bindings::write_to_out_dir(header, out_path);
|
||||||
}
|
}
|
||||||
@ -433,8 +433,7 @@ mod build_linked {
|
|||||||
.print_system_libs(false)
|
.print_system_libs(false)
|
||||||
.probe(link_lib)
|
.probe(link_lib)
|
||||||
{
|
{
|
||||||
if let Some(mut header) = lib.include_paths.pop() {
|
if let Some(header) = lib.include_paths.pop() {
|
||||||
header.push("sqlite3.h");
|
|
||||||
HeaderLocation::FromPath(header.to_string_lossy().into())
|
HeaderLocation::FromPath(header.to_string_lossy().into())
|
||||||
} else {
|
} else {
|
||||||
HeaderLocation::Wrapper
|
HeaderLocation::Wrapper
|
||||||
@ -453,8 +452,7 @@ mod build_linked {
|
|||||||
if cfg!(feature = "vcpkg") && is_compiler("msvc") {
|
if cfg!(feature = "vcpkg") && is_compiler("msvc") {
|
||||||
// See if vcpkg can find it.
|
// See if vcpkg can find it.
|
||||||
if let Ok(mut lib) = vcpkg::Config::new().probe(lib_name()) {
|
if let Ok(mut lib) = vcpkg::Config::new().probe(lib_name()) {
|
||||||
if let Some(mut header) = lib.include_paths.pop() {
|
if let Some(header) = lib.include_paths.pop() {
|
||||||
header.push("sqlite3.h");
|
|
||||||
return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
|
return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,14 +468,13 @@ mod bindings {
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use super::HeaderLocation;
|
use super::HeaderLocation;
|
||||||
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
static PREBUILT_BINDGEN_PATHS: &[&str] = &["bindgen-bindings/bindgen_3.14.0.rs"];
|
static PREBUILT_BINDGENS: &[&str] = &["bindgen_3.14.0.rs"];
|
||||||
|
|
||||||
pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
|
pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
|
||||||
let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
|
let name = PREBUILT_BINDGENS[PREBUILT_BINDGENS.len() - 1];
|
||||||
fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
|
super::copy_bindings("bindgen-bindings", name, out_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,8 +483,6 @@ mod bindings {
|
|||||||
use super::HeaderLocation;
|
use super::HeaderLocation;
|
||||||
use bindgen::callbacks::{IntKind, ParseCallbacks};
|
use bindgen::callbacks::{IntKind, ParseCallbacks};
|
||||||
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::win_target;
|
use super::win_target;
|
||||||
@ -496,9 +491,12 @@ mod bindings {
|
|||||||
struct SqliteTypeChooser;
|
struct SqliteTypeChooser;
|
||||||
|
|
||||||
impl ParseCallbacks for SqliteTypeChooser {
|
impl ParseCallbacks for SqliteTypeChooser {
|
||||||
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
|
fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> {
|
||||||
if value >= i32::MIN as i64 && value <= i32::MAX as i64 {
|
if name == "SQLITE_SERIALIZE_NOCOPY"
|
||||||
Some(IntKind::I32)
|
|| name.starts_with("SQLITE_DESERIALIZE_")
|
||||||
|
|| name.starts_with("SQLITE_PREPARE_")
|
||||||
|
{
|
||||||
|
Some(IntKind::UInt)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -519,12 +517,40 @@ mod bindings {
|
|||||||
|
|
||||||
pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) {
|
pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) {
|
||||||
let header: String = header.into();
|
let header: String = header.into();
|
||||||
let mut output = Vec::new();
|
|
||||||
let mut bindings = bindgen::builder()
|
let mut bindings = bindgen::builder()
|
||||||
|
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
||||||
|
.disable_nested_struct_naming()
|
||||||
.trust_clang_mangling(false)
|
.trust_clang_mangling(false)
|
||||||
.header(header.clone())
|
.header(header.clone())
|
||||||
.parse_callbacks(Box::new(SqliteTypeChooser))
|
.parse_callbacks(Box::new(SqliteTypeChooser))
|
||||||
.rustfmt_bindings(true);
|
.blocklist_function("sqlite3_auto_extension")
|
||||||
|
.raw_line(
|
||||||
|
r#"extern "C" {
|
||||||
|
pub fn sqlite3_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.blocklist_function("sqlite3_cancel_auto_extension")
|
||||||
|
.raw_line(
|
||||||
|
r#"extern "C" {
|
||||||
|
pub fn sqlite3_cancel_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
|
||||||
if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) {
|
if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) {
|
||||||
bindings = bindings.clang_arg("-DSQLITE_HAS_CODEC");
|
bindings = bindings.clang_arg("-DSQLITE_HAS_CODEC");
|
||||||
@ -592,38 +618,14 @@ mod bindings {
|
|||||||
.blocklist_function("sqlite3_vsnprintf")
|
.blocklist_function("sqlite3_vsnprintf")
|
||||||
.blocklist_function("sqlite3_str_vappendf")
|
.blocklist_function("sqlite3_str_vappendf")
|
||||||
.blocklist_type("va_list")
|
.blocklist_type("va_list")
|
||||||
.blocklist_type("__builtin_va_list")
|
.blocklist_item("__.*");
|
||||||
.blocklist_type("__gnuc_va_list")
|
|
||||||
.blocklist_type("__va_list_tag")
|
|
||||||
.blocklist_item("__GNUC_VA_LIST");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings
|
bindings
|
||||||
.layout_tests(false)
|
.layout_tests(false)
|
||||||
.generate()
|
.generate()
|
||||||
.unwrap_or_else(|_| panic!("could not run bindgen on header {}", header))
|
.unwrap_or_else(|_| panic!("could not run bindgen on header {}", header))
|
||||||
.write(Box::new(&mut output))
|
.write_to_file(out_path)
|
||||||
.expect("could not write output of bindgen");
|
|
||||||
let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");
|
|
||||||
|
|
||||||
// rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it
|
|
||||||
// can. This flag was added in SQLite 3.8.3, but oring it in in prior
|
|
||||||
// versions of SQLite is harmless. We don't want to not build just
|
|
||||||
// because this flag is missing (e.g., if we're linking against
|
|
||||||
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's
|
|
||||||
// output.
|
|
||||||
if !output.contains("pub const SQLITE_DETERMINISTIC") {
|
|
||||||
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.create(true)
|
|
||||||
.open(out_path)
|
|
||||||
.unwrap_or_else(|_| panic!("Could not write to {:?}", out_path));
|
|
||||||
|
|
||||||
file.write_all(output.as_bytes())
|
|
||||||
.unwrap_or_else(|_| panic!("Could not write to {:?}", out_path));
|
.unwrap_or_else(|_| panic!("Could not write to {:?}", out_path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,33 @@
|
|||||||
/* automatically generated by rust-bindgen 0.61.0 */
|
/* automatically generated by rust-bindgen 0.63.0 */
|
||||||
|
|
||||||
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.39.2\0";
|
extern "C" {
|
||||||
pub const SQLITE_VERSION_NUMBER: i32 = 3039002;
|
pub fn sqlite3_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn sqlite3_cancel_auto_extension(
|
||||||
|
xEntryPoint: ::std::option::Option<
|
||||||
|
unsafe extern "C" fn(
|
||||||
|
db: *mut sqlite3,
|
||||||
|
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
||||||
|
pThunk: *const sqlite3_api_routines,
|
||||||
|
) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
) -> ::std::os::raw::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.39.4\0";
|
||||||
|
pub const SQLITE_VERSION_NUMBER: i32 = 3039004;
|
||||||
pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
|
pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
|
||||||
b"2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668ealt1\0";
|
b"2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26alt1\0";
|
||||||
pub const SQLITE_OK: i32 = 0;
|
pub const SQLITE_OK: i32 = 0;
|
||||||
pub const SQLITE_ERROR: i32 = 1;
|
pub const SQLITE_ERROR: i32 = 1;
|
||||||
pub const SQLITE_INTERNAL: i32 = 2;
|
pub const SQLITE_INTERNAL: i32 = 2;
|
||||||
@ -2036,16 +2060,6 @@ extern "C" {
|
|||||||
onoff: ::std::os::raw::c_int,
|
onoff: ::std::os::raw::c_int,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
pub fn sqlite3_auto_extension(
|
|
||||||
xEntryPoint: ::std::option::Option<unsafe extern "C" fn()>,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
|
||||||
pub fn sqlite3_cancel_auto_extension(
|
|
||||||
xEntryPoint: ::std::option::Option<unsafe extern "C" fn()>,
|
|
||||||
) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn sqlite3_reset_auto_extension();
|
pub fn sqlite3_reset_auto_extension();
|
||||||
}
|
}
|
||||||
@ -2189,10 +2203,10 @@ pub struct sqlite3_module {
|
|||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info {
|
pub struct sqlite3_index_info {
|
||||||
pub nConstraint: ::std::os::raw::c_int,
|
pub nConstraint: ::std::os::raw::c_int,
|
||||||
pub aConstraint: *mut sqlite3_index_info_sqlite3_index_constraint,
|
pub aConstraint: *mut sqlite3_index_constraint,
|
||||||
pub nOrderBy: ::std::os::raw::c_int,
|
pub nOrderBy: ::std::os::raw::c_int,
|
||||||
pub aOrderBy: *mut sqlite3_index_info_sqlite3_index_orderby,
|
pub aOrderBy: *mut sqlite3_index_orderby,
|
||||||
pub aConstraintUsage: *mut sqlite3_index_info_sqlite3_index_constraint_usage,
|
pub aConstraintUsage: *mut sqlite3_index_constraint_usage,
|
||||||
pub idxNum: ::std::os::raw::c_int,
|
pub idxNum: ::std::os::raw::c_int,
|
||||||
pub idxStr: *mut ::std::os::raw::c_char,
|
pub idxStr: *mut ::std::os::raw::c_char,
|
||||||
pub needToFreeIdxStr: ::std::os::raw::c_int,
|
pub needToFreeIdxStr: ::std::os::raw::c_int,
|
||||||
@ -2204,7 +2218,7 @@ pub struct sqlite3_index_info {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_constraint {
|
pub struct sqlite3_index_constraint {
|
||||||
pub iColumn: ::std::os::raw::c_int,
|
pub iColumn: ::std::os::raw::c_int,
|
||||||
pub op: ::std::os::raw::c_uchar,
|
pub op: ::std::os::raw::c_uchar,
|
||||||
pub usable: ::std::os::raw::c_uchar,
|
pub usable: ::std::os::raw::c_uchar,
|
||||||
@ -2212,13 +2226,13 @@ pub struct sqlite3_index_info_sqlite3_index_constraint {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_orderby {
|
pub struct sqlite3_index_orderby {
|
||||||
pub iColumn: ::std::os::raw::c_int,
|
pub iColumn: ::std::os::raw::c_int,
|
||||||
pub desc: ::std::os::raw::c_uchar,
|
pub desc: ::std::os::raw::c_uchar,
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct sqlite3_index_info_sqlite3_index_constraint_usage {
|
pub struct sqlite3_index_constraint_usage {
|
||||||
pub argvIndex: ::std::os::raw::c_int,
|
pub argvIndex: ::std::os::raw::c_int,
|
||||||
pub omit: ::std::os::raw::c_uchar,
|
pub omit: ::std::os::raw::c_uchar,
|
||||||
}
|
}
|
||||||
|
309
libsqlite3-sys/sqlcipher/sqlite3.c
vendored
309
libsqlite3-sys/sqlcipher/sqlite3.c
vendored
@ -1,6 +1,6 @@
|
|||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
** This file is an amalgamation of many separate C source files from SQLite
|
** This file is an amalgamation of many separate C source files from SQLite
|
||||||
** version 3.39.2. By combining all the individual C code files into this
|
** version 3.39.4. By combining all the individual C code files into this
|
||||||
** single large file, the entire code can be compiled as a single translation
|
** single large file, the entire code can be compiled as a single translation
|
||||||
** unit. This allows many compilers to do optimizations that would not be
|
** unit. This allows many compilers to do optimizations that would not be
|
||||||
** possible if the files were compiled separately. Performance improvements
|
** possible if the files were compiled separately. Performance improvements
|
||||||
@ -452,9 +452,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.39.2"
|
#define SQLITE_VERSION "3.39.4"
|
||||||
#define SQLITE_VERSION_NUMBER 3039002
|
#define SQLITE_VERSION_NUMBER 3039004
|
||||||
#define SQLITE_SOURCE_ID "2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668ealt1"
|
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26alt1"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@ -13204,6 +13204,11 @@ struct fts5_api {
|
|||||||
/************** End of sqlite3.h *********************************************/
|
/************** End of sqlite3.h *********************************************/
|
||||||
/************** Continuing where we left off in sqliteInt.h ******************/
|
/************** Continuing where we left off in sqliteInt.h ******************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory.
|
||||||
|
*/
|
||||||
|
#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Include the configuration header output by 'configure' if we're using the
|
** Include the configuration header output by 'configure' if we're using the
|
||||||
** autoconf-based build
|
** autoconf-based build
|
||||||
@ -29665,8 +29670,13 @@ SQLITE_PRIVATE void *sqlite3OomFault(sqlite3 *db){
|
|||||||
}
|
}
|
||||||
DisableLookaside;
|
DisableLookaside;
|
||||||
if( db->pParse ){
|
if( db->pParse ){
|
||||||
|
Parse *pParse;
|
||||||
sqlite3ErrorMsg(db->pParse, "out of memory");
|
sqlite3ErrorMsg(db->pParse, "out of memory");
|
||||||
db->pParse->rc = SQLITE_NOMEM_BKPT;
|
db->pParse->rc = SQLITE_NOMEM_BKPT;
|
||||||
|
for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){
|
||||||
|
pParse->nErr++;
|
||||||
|
pParse->rc = SQLITE_NOMEM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -33561,7 +33571,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
sqlite3 *db = pParse->db;
|
sqlite3 *db = pParse->db;
|
||||||
assert( db!=0 );
|
assert( db!=0 );
|
||||||
assert( db->pParse==pParse );
|
assert( db->pParse==pParse || db->pParse->pToplevel==pParse );
|
||||||
db->errByteOffset = -2;
|
db->errByteOffset = -2;
|
||||||
va_start(ap, zFormat);
|
va_start(ap, zFormat);
|
||||||
zMsg = sqlite3VMPrintf(db, zFormat, ap);
|
zMsg = sqlite3VMPrintf(db, zFormat, ap);
|
||||||
@ -41424,6 +41434,7 @@ static const char *unixTempFileDir(void){
|
|||||||
static int unixGetTempname(int nBuf, char *zBuf){
|
static int unixGetTempname(int nBuf, char *zBuf){
|
||||||
const char *zDir;
|
const char *zDir;
|
||||||
int iLimit = 0;
|
int iLimit = 0;
|
||||||
|
int rc = SQLITE_OK;
|
||||||
|
|
||||||
/* It's odd to simulate an io-error here, but really this is just
|
/* It's odd to simulate an io-error here, but really this is just
|
||||||
** using the io-error infrastructure to test that SQLite handles this
|
** using the io-error infrastructure to test that SQLite handles this
|
||||||
@ -41432,18 +41443,26 @@ static int unixGetTempname(int nBuf, char *zBuf){
|
|||||||
zBuf[0] = 0;
|
zBuf[0] = 0;
|
||||||
SimulateIOError( return SQLITE_IOERR );
|
SimulateIOError( return SQLITE_IOERR );
|
||||||
|
|
||||||
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
zDir = unixTempFileDir();
|
zDir = unixTempFileDir();
|
||||||
if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
|
if( zDir==0 ){
|
||||||
do{
|
rc = SQLITE_IOERR_GETTEMPPATH;
|
||||||
u64 r;
|
}else{
|
||||||
sqlite3_randomness(sizeof(r), &r);
|
do{
|
||||||
assert( nBuf>2 );
|
u64 r;
|
||||||
zBuf[nBuf-2] = 0;
|
sqlite3_randomness(sizeof(r), &r);
|
||||||
sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
|
assert( nBuf>2 );
|
||||||
zDir, r, 0);
|
zBuf[nBuf-2] = 0;
|
||||||
if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
|
sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
|
||||||
}while( osAccess(zBuf,0)==0 );
|
zDir, r, 0);
|
||||||
return SQLITE_OK;
|
if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){
|
||||||
|
rc = SQLITE_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}while( osAccess(zBuf,0)==0 );
|
||||||
|
}
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
|
||||||
@ -45582,10 +45601,12 @@ SQLITE_API int sqlite3_win32_set_directory8(
|
|||||||
const char *zValue /* New value for directory being set or reset */
|
const char *zValue /* New value for directory being set or reset */
|
||||||
){
|
){
|
||||||
char **ppDirectory = 0;
|
char **ppDirectory = 0;
|
||||||
|
int rc;
|
||||||
#ifndef SQLITE_OMIT_AUTOINIT
|
#ifndef SQLITE_OMIT_AUTOINIT
|
||||||
int rc = sqlite3_initialize();
|
rc = sqlite3_initialize();
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
#endif
|
#endif
|
||||||
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
|
if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
|
||||||
ppDirectory = &sqlite3_data_directory;
|
ppDirectory = &sqlite3_data_directory;
|
||||||
}else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
|
}else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
|
||||||
@ -45600,14 +45621,19 @@ SQLITE_API int sqlite3_win32_set_directory8(
|
|||||||
if( zValue && zValue[0] ){
|
if( zValue && zValue[0] ){
|
||||||
zCopy = sqlite3_mprintf("%s", zValue);
|
zCopy = sqlite3_mprintf("%s", zValue);
|
||||||
if ( zCopy==0 ){
|
if ( zCopy==0 ){
|
||||||
return SQLITE_NOMEM_BKPT;
|
rc = SQLITE_NOMEM_BKPT;
|
||||||
|
goto set_directory8_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_free(*ppDirectory);
|
sqlite3_free(*ppDirectory);
|
||||||
*ppDirectory = zCopy;
|
*ppDirectory = zCopy;
|
||||||
return SQLITE_OK;
|
rc = SQLITE_OK;
|
||||||
|
}else{
|
||||||
|
rc = SQLITE_ERROR;
|
||||||
}
|
}
|
||||||
return SQLITE_ERROR;
|
set_directory8_done:
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -48381,6 +48407,18 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If sqlite3_temp_directory is not, take the mutex and return true.
|
||||||
|
**
|
||||||
|
** If sqlite3_temp_directory is NULL, omit the mutex and return false.
|
||||||
|
*/
|
||||||
|
static int winTempDirDefined(void){
|
||||||
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
|
if( sqlite3_temp_directory!=0 ) return 1;
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Create a temporary file name and store the resulting pointer into pzBuf.
|
** Create a temporary file name and store the resulting pointer into pzBuf.
|
||||||
** The pointer returned in pzBuf must be freed via sqlite3_free().
|
** The pointer returned in pzBuf must be freed via sqlite3_free().
|
||||||
@ -48417,20 +48455,23 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
|
|||||||
*/
|
*/
|
||||||
nDir = nMax - (nPre + 15);
|
nDir = nMax - (nPre + 15);
|
||||||
assert( nDir>0 );
|
assert( nDir>0 );
|
||||||
if( sqlite3_temp_directory ){
|
if( winTempDirDefined() ){
|
||||||
int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
|
int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
|
||||||
if( nDirLen>0 ){
|
if( nDirLen>0 ){
|
||||||
if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
|
if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
|
||||||
nDirLen++;
|
nDirLen++;
|
||||||
}
|
}
|
||||||
if( nDirLen>nDir ){
|
if( nDirLen>nDir ){
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
sqlite3_free(zBuf);
|
sqlite3_free(zBuf);
|
||||||
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
||||||
return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
|
return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
|
||||||
}
|
}
|
||||||
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
|
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
|
||||||
}
|
}
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__CYGWIN__)
|
#if defined(__CYGWIN__)
|
||||||
else{
|
else{
|
||||||
static const char *azDirs[] = {
|
static const char *azDirs[] = {
|
||||||
@ -49219,7 +49260,7 @@ static BOOL winIsVerbatimPathname(
|
|||||||
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
|
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
|
||||||
** bytes in size.
|
** bytes in size.
|
||||||
*/
|
*/
|
||||||
static int winFullPathname(
|
static int winFullPathnameNoMutex(
|
||||||
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
||||||
const char *zRelative, /* Possibly relative input path */
|
const char *zRelative, /* Possibly relative input path */
|
||||||
int nFull, /* Size of output buffer in bytes */
|
int nFull, /* Size of output buffer in bytes */
|
||||||
@ -49398,6 +49439,19 @@ static int winFullPathname(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
static int winFullPathname(
|
||||||
|
sqlite3_vfs *pVfs, /* Pointer to vfs object */
|
||||||
|
const char *zRelative, /* Possibly relative input path */
|
||||||
|
int nFull, /* Size of output buffer in bytes */
|
||||||
|
char *zFull /* Output buffer */
|
||||||
|
){
|
||||||
|
int rc;
|
||||||
|
sqlite3_mutex *pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR);
|
||||||
|
sqlite3_mutex_enter(pMutex);
|
||||||
|
rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull);
|
||||||
|
sqlite3_mutex_leave(pMutex);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||||
/*
|
/*
|
||||||
@ -51742,14 +51796,24 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
|
|||||||
*/
|
*/
|
||||||
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
|
||||||
PCache *pCache = p->pCache;
|
PCache *pCache = p->pCache;
|
||||||
|
sqlite3_pcache_page *pOther;
|
||||||
assert( p->nRef>0 );
|
assert( p->nRef>0 );
|
||||||
assert( newPgno>0 );
|
assert( newPgno>0 );
|
||||||
assert( sqlite3PcachePageSanity(p) );
|
assert( sqlite3PcachePageSanity(p) );
|
||||||
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
|
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
|
||||||
|
pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0);
|
||||||
|
if( pOther ){
|
||||||
|
PgHdr *pXPage = (PgHdr*)pOther->pExtra;
|
||||||
|
assert( pXPage->nRef==0 );
|
||||||
|
pXPage->nRef++;
|
||||||
|
pCache->nRefSum++;
|
||||||
|
sqlite3PcacheDrop(pXPage);
|
||||||
|
}
|
||||||
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
|
||||||
p->pgno = newPgno;
|
p->pgno = newPgno;
|
||||||
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
|
||||||
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
|
||||||
|
assert( sqlite3PcachePageSanity(p) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53131,23 +53195,26 @@ static void pcache1Rekey(
|
|||||||
PCache1 *pCache = (PCache1 *)p;
|
PCache1 *pCache = (PCache1 *)p;
|
||||||
PgHdr1 *pPage = (PgHdr1 *)pPg;
|
PgHdr1 *pPage = (PgHdr1 *)pPg;
|
||||||
PgHdr1 **pp;
|
PgHdr1 **pp;
|
||||||
unsigned int h;
|
unsigned int hOld, hNew;
|
||||||
assert( pPage->iKey==iOld );
|
assert( pPage->iKey==iOld );
|
||||||
assert( pPage->pCache==pCache );
|
assert( pPage->pCache==pCache );
|
||||||
|
assert( iOld!=iNew ); /* The page number really is changing */
|
||||||
|
|
||||||
pcache1EnterMutex(pCache->pGroup);
|
pcache1EnterMutex(pCache->pGroup);
|
||||||
|
|
||||||
h = iOld%pCache->nHash;
|
assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */
|
||||||
pp = &pCache->apHash[h];
|
hOld = iOld%pCache->nHash;
|
||||||
|
pp = &pCache->apHash[hOld];
|
||||||
while( (*pp)!=pPage ){
|
while( (*pp)!=pPage ){
|
||||||
pp = &(*pp)->pNext;
|
pp = &(*pp)->pNext;
|
||||||
}
|
}
|
||||||
*pp = pPage->pNext;
|
*pp = pPage->pNext;
|
||||||
|
|
||||||
h = iNew%pCache->nHash;
|
assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */
|
||||||
|
hNew = iNew%pCache->nHash;
|
||||||
pPage->iKey = iNew;
|
pPage->iKey = iNew;
|
||||||
pPage->pNext = pCache->apHash[h];
|
pPage->pNext = pCache->apHash[hNew];
|
||||||
pCache->apHash[h] = pPage;
|
pCache->apHash[hNew] = pPage;
|
||||||
if( iNew>pCache->iMaxKey ){
|
if( iNew>pCache->iMaxKey ){
|
||||||
pCache->iMaxKey = iNew;
|
pCache->iMaxKey = iNew;
|
||||||
}
|
}
|
||||||
@ -59905,6 +59972,7 @@ static int pager_open_journal(Pager *pPager){
|
|||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
sqlite3BitvecDestroy(pPager->pInJournal);
|
sqlite3BitvecDestroy(pPager->pInJournal);
|
||||||
pPager->pInJournal = 0;
|
pPager->pInJournal = 0;
|
||||||
|
pPager->journalOff = 0;
|
||||||
}else{
|
}else{
|
||||||
assert( pPager->eState==PAGER_WRITER_LOCKED );
|
assert( pPager->eState==PAGER_WRITER_LOCKED );
|
||||||
pPager->eState = PAGER_WRITER_CACHEMOD;
|
pPager->eState = PAGER_WRITER_CACHEMOD;
|
||||||
@ -61503,7 +61571,7 @@ SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager *pPager){
|
|||||||
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
|
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){
|
||||||
assert( assert_pager_state(pPager) );
|
assert( assert_pager_state(pPager) );
|
||||||
if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
|
if( pPager->eState>=PAGER_WRITER_CACHEMOD ) return 0;
|
||||||
if( NEVER(isOpen(pPager->jfd) && pPager->journalOff>0) ) return 0;
|
if( isOpen(pPager->jfd) && pPager->journalOff>0 ) return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68647,7 +68715,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
|
|||||||
if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
|
if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
|
||||||
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
|
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
|
||||||
sz += sz2;
|
sz += sz2;
|
||||||
}else if( NEVER(iFree+sz>usableSize) ){
|
}else if( iFree+sz>usableSize ){
|
||||||
return SQLITE_CORRUPT_PAGE(pPage);
|
return SQLITE_CORRUPT_PAGE(pPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75003,8 +75071,6 @@ static int balance_nonroot(
|
|||||||
Pgno pgno; /* Temp var to store a page number in */
|
Pgno pgno; /* Temp var to store a page number in */
|
||||||
u8 abDone[NB+2]; /* True after i'th new page is populated */
|
u8 abDone[NB+2]; /* True after i'th new page is populated */
|
||||||
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
|
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
|
||||||
Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
|
|
||||||
u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
|
|
||||||
CellArray b; /* Parsed information on cells being balanced */
|
CellArray b; /* Parsed information on cells being balanced */
|
||||||
|
|
||||||
memset(abDone, 0, sizeof(abDone));
|
memset(abDone, 0, sizeof(abDone));
|
||||||
@ -75428,42 +75494,39 @@ static int balance_nonroot(
|
|||||||
** of the table is closer to a linear scan through the file. That in turn
|
** of the table is closer to a linear scan through the file. That in turn
|
||||||
** helps the operating system to deliver pages from the disk more rapidly.
|
** helps the operating system to deliver pages from the disk more rapidly.
|
||||||
**
|
**
|
||||||
** An O(n^2) insertion sort algorithm is used, but since n is never more
|
** An O(N*N) sort algorithm is used, but since N is never more than NB+2
|
||||||
** than (NB+2) (a small constant), that should not be a problem.
|
** (5), that is not a performance concern.
|
||||||
**
|
**
|
||||||
** When NB==3, this one optimization makes the database about 25% faster
|
** When NB==3, this one optimization makes the database about 25% faster
|
||||||
** for large insertions and deletions.
|
** for large insertions and deletions.
|
||||||
*/
|
*/
|
||||||
for(i=0; i<nNew; i++){
|
for(i=0; i<nNew; i++){
|
||||||
aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
|
aPgno[i] = apNew[i]->pgno;
|
||||||
aPgFlags[i] = apNew[i]->pDbPage->flags;
|
assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE );
|
||||||
for(j=0; j<i; j++){
|
assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY );
|
||||||
if( NEVER(aPgno[j]==aPgno[i]) ){
|
|
||||||
/* This branch is taken if the set of sibling pages somehow contains
|
|
||||||
** duplicate entries. This can happen if the database is corrupt.
|
|
||||||
** It would be simpler to detect this as part of the loop below, but
|
|
||||||
** we do the detection here in order to avoid populating the pager
|
|
||||||
** cache with two separate objects associated with the same
|
|
||||||
** page number. */
|
|
||||||
assert( CORRUPT_DB );
|
|
||||||
rc = SQLITE_CORRUPT_BKPT;
|
|
||||||
goto balance_cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for(i=0; i<nNew; i++){
|
for(i=0; i<nNew-1; i++){
|
||||||
int iBest = 0; /* aPgno[] index of page number to use */
|
int iB = i;
|
||||||
for(j=1; j<nNew; j++){
|
for(j=i+1; j<nNew; j++){
|
||||||
if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j;
|
if( apNew[j]->pgno < apNew[iB]->pgno ) iB = j;
|
||||||
}
|
}
|
||||||
pgno = aPgOrder[iBest];
|
|
||||||
aPgOrder[iBest] = 0xffffffff;
|
/* If apNew[i] has a page number that is bigger than any of the
|
||||||
if( iBest!=i ){
|
** subsequence apNew[i] entries, then swap apNew[i] with the subsequent
|
||||||
if( iBest>i ){
|
** entry that has the smallest page number (which we know to be
|
||||||
sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0);
|
** entry apNew[iB]).
|
||||||
}
|
*/
|
||||||
sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]);
|
if( iB!=i ){
|
||||||
apNew[i]->pgno = pgno;
|
Pgno pgnoA = apNew[i]->pgno;
|
||||||
|
Pgno pgnoB = apNew[iB]->pgno;
|
||||||
|
Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1;
|
||||||
|
u16 fgA = apNew[i]->pDbPage->flags;
|
||||||
|
u16 fgB = apNew[iB]->pDbPage->flags;
|
||||||
|
sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB);
|
||||||
|
sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA);
|
||||||
|
sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB);
|
||||||
|
apNew[i]->pgno = pgnoB;
|
||||||
|
apNew[iB]->pgno = pgnoA;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81395,6 +81458,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall(
|
|||||||
addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function,
|
addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function,
|
||||||
p1, p2, p3, (char*)pCtx, P4_FUNCCTX);
|
p1, p2, p3, (char*)pCtx, P4_FUNCCTX);
|
||||||
sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef);
|
sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef);
|
||||||
|
sqlite3MayAbort(pParse);
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81730,6 +81794,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|
|||||||
|| opcode==OP_VDestroy
|
|| opcode==OP_VDestroy
|
||||||
|| opcode==OP_VCreate
|
|| opcode==OP_VCreate
|
||||||
|| opcode==OP_ParseSchema
|
|| opcode==OP_ParseSchema
|
||||||
|
|| opcode==OP_Function || opcode==OP_PureFunc
|
||||||
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
|
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
|
||||||
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
|
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
|
||||||
){
|
){
|
||||||
@ -101783,7 +101848,7 @@ SQLITE_API void sqlite3pager_reset(Pager *pPager);
|
|||||||
#define CIPHER_STR(s) #s
|
#define CIPHER_STR(s) #s
|
||||||
|
|
||||||
#ifndef CIPHER_VERSION_NUMBER
|
#ifndef CIPHER_VERSION_NUMBER
|
||||||
#define CIPHER_VERSION_NUMBER 4.5.2
|
#define CIPHER_VERSION_NUMBER 4.5.3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CIPHER_VERSION_BUILD
|
#ifndef CIPHER_VERSION_BUILD
|
||||||
@ -138008,6 +138073,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
case PragTyp_TEMP_STORE_DIRECTORY: {
|
case PragTyp_TEMP_STORE_DIRECTORY: {
|
||||||
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
if( !zRight ){
|
if( !zRight ){
|
||||||
returnSingleText(v, sqlite3_temp_directory);
|
returnSingleText(v, sqlite3_temp_directory);
|
||||||
}else{
|
}else{
|
||||||
@ -138017,6 +138083,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||||
if( rc!=SQLITE_OK || res==0 ){
|
if( rc!=SQLITE_OK || res==0 ){
|
||||||
sqlite3ErrorMsg(pParse, "not a writable directory");
|
sqlite3ErrorMsg(pParse, "not a writable directory");
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
goto pragma_out;
|
goto pragma_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138034,6 +138101,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_WSD */
|
#endif /* SQLITE_OMIT_WSD */
|
||||||
}
|
}
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138052,6 +138120,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
**
|
**
|
||||||
*/
|
*/
|
||||||
case PragTyp_DATA_STORE_DIRECTORY: {
|
case PragTyp_DATA_STORE_DIRECTORY: {
|
||||||
|
sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
if( !zRight ){
|
if( !zRight ){
|
||||||
returnSingleText(v, sqlite3_data_directory);
|
returnSingleText(v, sqlite3_data_directory);
|
||||||
}else{
|
}else{
|
||||||
@ -138061,6 +138130,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||||
if( rc!=SQLITE_OK || res==0 ){
|
if( rc!=SQLITE_OK || res==0 ){
|
||||||
sqlite3ErrorMsg(pParse, "not a writable directory");
|
sqlite3ErrorMsg(pParse, "not a writable directory");
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
goto pragma_out;
|
goto pragma_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138072,6 +138142,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
|
|||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_WSD */
|
#endif /* SQLITE_OMIT_WSD */
|
||||||
}
|
}
|
||||||
|
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -142560,7 +142631,7 @@ static void generateSortTail(
|
|||||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
||||||
VdbeCoverage(v);
|
VdbeCoverage(v);
|
||||||
codeOffset(v, p->iOffset, addrContinue);
|
assert( p->iLimit==0 && p->iOffset==0 );
|
||||||
sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
|
sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
|
||||||
bSeq = 0;
|
bSeq = 0;
|
||||||
}else{
|
}else{
|
||||||
@ -142568,6 +142639,9 @@ static void generateSortTail(
|
|||||||
codeOffset(v, p->iOffset, addrContinue);
|
codeOffset(v, p->iOffset, addrContinue);
|
||||||
iSortTab = iTab;
|
iSortTab = iTab;
|
||||||
bSeq = 1;
|
bSeq = 1;
|
||||||
|
if( p->iOffset>0 ){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
|
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
|
||||||
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
|
||||||
@ -144560,10 +144634,11 @@ static int multiSelectOrderBy(
|
|||||||
*/
|
*/
|
||||||
sqlite3VdbeResolveLabel(v, labelEnd);
|
sqlite3VdbeResolveLabel(v, labelEnd);
|
||||||
|
|
||||||
/* Reassembly the compound query so that it will be freed correctly
|
/* Reassemble the compound query so that it will be freed correctly
|
||||||
** by the calling function */
|
** by the calling function */
|
||||||
if( pSplit->pPrior ){
|
if( pSplit->pPrior ){
|
||||||
sqlite3SelectDelete(db, pSplit->pPrior);
|
sqlite3ParserAddCleanup(pParse,
|
||||||
|
(void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
|
||||||
}
|
}
|
||||||
pSplit->pPrior = pPrior;
|
pSplit->pPrior = pPrior;
|
||||||
pPrior->pNext = pSplit;
|
pPrior->pNext = pSplit;
|
||||||
@ -146082,6 +146157,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
|
|||||||
|| p->pSrc->nSrc!=1
|
|| p->pSrc->nSrc!=1
|
||||||
|| p->pSrc->a[0].pSelect
|
|| p->pSrc->a[0].pSelect
|
||||||
|| pAggInfo->nFunc!=1
|
|| pAggInfo->nFunc!=1
|
||||||
|
|| p->pHaving
|
||||||
){
|
){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -149319,6 +149395,23 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
|
|||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
char *z;
|
char *z;
|
||||||
|
|
||||||
|
/* If this is a new CREATE TABLE statement, and if shadow tables
|
||||||
|
** are read-only, and the trigger makes a change to a shadow table,
|
||||||
|
** then raise an error - do not allow the trigger to be created. */
|
||||||
|
if( sqlite3ReadOnlyShadowTables(db) ){
|
||||||
|
TriggerStep *pStep;
|
||||||
|
for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){
|
||||||
|
if( pStep->zTarget!=0
|
||||||
|
&& sqlite3ShadowTableName(db, pStep->zTarget)
|
||||||
|
){
|
||||||
|
sqlite3ErrorMsg(pParse,
|
||||||
|
"trigger \"%s\" may not write to shadow table \"%s\"",
|
||||||
|
pTrig->zName, pStep->zTarget);
|
||||||
|
goto triggerfinish_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Make an entry in the sqlite_schema table */
|
/* Make an entry in the sqlite_schema table */
|
||||||
v = sqlite3GetVdbe(pParse);
|
v = sqlite3GetVdbe(pParse);
|
||||||
if( v==0 ) goto triggerfinish_cleanup;
|
if( v==0 ) goto triggerfinish_cleanup;
|
||||||
@ -155143,7 +155236,8 @@ static int codeEqualityTerm(
|
|||||||
}
|
}
|
||||||
sqlite3ExprDelete(db, pX);
|
sqlite3ExprDelete(db, pX);
|
||||||
}else{
|
}else{
|
||||||
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
|
int n = sqlite3ExprVectorSize(pX->pLeft);
|
||||||
|
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n));
|
||||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
|
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
|
||||||
}
|
}
|
||||||
pX = pExpr;
|
pX = pExpr;
|
||||||
@ -182345,7 +182439,7 @@ struct Fts3MultiSegReader {
|
|||||||
int nAdvance; /* How many seg-readers to advance */
|
int nAdvance; /* How many seg-readers to advance */
|
||||||
Fts3SegFilter *pFilter; /* Pointer to filter object */
|
Fts3SegFilter *pFilter; /* Pointer to filter object */
|
||||||
char *aBuffer; /* Buffer to merge doclists in */
|
char *aBuffer; /* Buffer to merge doclists in */
|
||||||
int nBuffer; /* Allocated size of aBuffer[] in bytes */
|
i64 nBuffer; /* Allocated size of aBuffer[] in bytes */
|
||||||
|
|
||||||
int iColFilter; /* If >=0, filter for this column */
|
int iColFilter; /* If >=0, filter for this column */
|
||||||
int bRestart;
|
int bRestart;
|
||||||
@ -185041,7 +185135,7 @@ static int fts3TermSelectMerge(
|
|||||||
**
|
**
|
||||||
** Similar padding is added in the fts3DoclistOrMerge() function.
|
** Similar padding is added in the fts3DoclistOrMerge() function.
|
||||||
*/
|
*/
|
||||||
pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1);
|
pTS->aaOutput[0] = sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1);
|
||||||
pTS->anOutput[0] = nDoclist;
|
pTS->anOutput[0] = nDoclist;
|
||||||
if( pTS->aaOutput[0] ){
|
if( pTS->aaOutput[0] ){
|
||||||
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
|
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
|
||||||
@ -186529,7 +186623,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
|
|||||||
nDistance = iPrev - nMaxUndeferred;
|
nDistance = iPrev - nMaxUndeferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
aOut = (char *)sqlite3_malloc(nPoslist+8);
|
aOut = (char *)sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING);
|
||||||
if( !aOut ){
|
if( !aOut ){
|
||||||
sqlite3_free(aPoslist);
|
sqlite3_free(aPoslist);
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
@ -186898,7 +186992,7 @@ static int fts3EvalIncrPhraseNext(
|
|||||||
if( bEof==0 ){
|
if( bEof==0 ){
|
||||||
int nList = 0;
|
int nList = 0;
|
||||||
int nByte = a[p->nToken-1].nList;
|
int nByte = a[p->nToken-1].nList;
|
||||||
char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING);
|
char *aDoclist = sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING);
|
||||||
if( !aDoclist ) return SQLITE_NOMEM;
|
if( !aDoclist ) return SQLITE_NOMEM;
|
||||||
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
|
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
|
||||||
memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING);
|
memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING);
|
||||||
@ -191134,7 +191228,7 @@ static int porterNext(
|
|||||||
if( n>c->nAllocated ){
|
if( n>c->nAllocated ){
|
||||||
char *pNew;
|
char *pNew;
|
||||||
c->nAllocated = n+20;
|
c->nAllocated = n+20;
|
||||||
pNew = sqlite3_realloc(c->zToken, c->nAllocated);
|
pNew = sqlite3_realloc64(c->zToken, c->nAllocated);
|
||||||
if( !pNew ) return SQLITE_NOMEM;
|
if( !pNew ) return SQLITE_NOMEM;
|
||||||
c->zToken = pNew;
|
c->zToken = pNew;
|
||||||
}
|
}
|
||||||
@ -191886,7 +191980,7 @@ static int simpleNext(
|
|||||||
if( n>c->nTokenAllocated ){
|
if( n>c->nTokenAllocated ){
|
||||||
char *pNew;
|
char *pNew;
|
||||||
c->nTokenAllocated = n+20;
|
c->nTokenAllocated = n+20;
|
||||||
pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated);
|
pNew = sqlite3_realloc64(c->pToken, c->nTokenAllocated);
|
||||||
if( !pNew ) return SQLITE_NOMEM;
|
if( !pNew ) return SQLITE_NOMEM;
|
||||||
c->pToken = pNew;
|
c->pToken = pNew;
|
||||||
}
|
}
|
||||||
@ -193048,7 +193142,7 @@ static int fts3PendingListAppendVarint(
|
|||||||
|
|
||||||
/* Allocate or grow the PendingList as required. */
|
/* Allocate or grow the PendingList as required. */
|
||||||
if( !p ){
|
if( !p ){
|
||||||
p = sqlite3_malloc(sizeof(*p) + 100);
|
p = sqlite3_malloc64(sizeof(*p) + 100);
|
||||||
if( !p ){
|
if( !p ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -193057,14 +193151,14 @@ static int fts3PendingListAppendVarint(
|
|||||||
p->nData = 0;
|
p->nData = 0;
|
||||||
}
|
}
|
||||||
else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){
|
else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){
|
||||||
int nNew = p->nSpace * 2;
|
i64 nNew = p->nSpace * 2;
|
||||||
p = sqlite3_realloc(p, sizeof(*p) + nNew);
|
p = sqlite3_realloc64(p, sizeof(*p) + nNew);
|
||||||
if( !p ){
|
if( !p ){
|
||||||
sqlite3_free(*pp);
|
sqlite3_free(*pp);
|
||||||
*pp = 0;
|
*pp = 0;
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
p->nSpace = nNew;
|
p->nSpace = (int)nNew;
|
||||||
p->aData = (char *)&p[1];
|
p->aData = (char *)&p[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193621,7 +193715,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
|
|||||||
int nByte = sqlite3_blob_bytes(p->pSegments);
|
int nByte = sqlite3_blob_bytes(p->pSegments);
|
||||||
*pnBlob = nByte;
|
*pnBlob = nByte;
|
||||||
if( paBlob ){
|
if( paBlob ){
|
||||||
char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING);
|
char *aByte = sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING);
|
||||||
if( !aByte ){
|
if( !aByte ){
|
||||||
rc = SQLITE_NOMEM;
|
rc = SQLITE_NOMEM;
|
||||||
}else{
|
}else{
|
||||||
@ -193738,7 +193832,7 @@ static int fts3SegReaderNext(
|
|||||||
int nTerm = fts3HashKeysize(pElem);
|
int nTerm = fts3HashKeysize(pElem);
|
||||||
if( (nTerm+1)>pReader->nTermAlloc ){
|
if( (nTerm+1)>pReader->nTermAlloc ){
|
||||||
sqlite3_free(pReader->zTerm);
|
sqlite3_free(pReader->zTerm);
|
||||||
pReader->zTerm = (char*)sqlite3_malloc((nTerm+1)*2);
|
pReader->zTerm = (char*)sqlite3_malloc64(((i64)nTerm+1)*2);
|
||||||
if( !pReader->zTerm ) return SQLITE_NOMEM;
|
if( !pReader->zTerm ) return SQLITE_NOMEM;
|
||||||
pReader->nTermAlloc = (nTerm+1)*2;
|
pReader->nTermAlloc = (nTerm+1)*2;
|
||||||
}
|
}
|
||||||
@ -193746,7 +193840,7 @@ static int fts3SegReaderNext(
|
|||||||
pReader->zTerm[nTerm] = '\0';
|
pReader->zTerm[nTerm] = '\0';
|
||||||
pReader->nTerm = nTerm;
|
pReader->nTerm = nTerm;
|
||||||
|
|
||||||
aCopy = (char*)sqlite3_malloc(nCopy);
|
aCopy = (char*)sqlite3_malloc64(nCopy);
|
||||||
if( !aCopy ) return SQLITE_NOMEM;
|
if( !aCopy ) return SQLITE_NOMEM;
|
||||||
memcpy(aCopy, pList->aData, nCopy);
|
memcpy(aCopy, pList->aData, nCopy);
|
||||||
pReader->nNode = pReader->nDoclist = nCopy;
|
pReader->nNode = pReader->nDoclist = nCopy;
|
||||||
@ -194033,7 +194127,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
|
|||||||
nExtra = nRoot + FTS3_NODE_PADDING;
|
nExtra = nRoot + FTS3_NODE_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra);
|
pReader = (Fts3SegReader *)sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra);
|
||||||
if( !pReader ){
|
if( !pReader ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -194125,7 +194219,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
|
|||||||
if( nElem==nAlloc ){
|
if( nElem==nAlloc ){
|
||||||
Fts3HashElem **aElem2;
|
Fts3HashElem **aElem2;
|
||||||
nAlloc += 16;
|
nAlloc += 16;
|
||||||
aElem2 = (Fts3HashElem **)sqlite3_realloc(
|
aElem2 = (Fts3HashElem **)sqlite3_realloc64(
|
||||||
aElem, nAlloc*sizeof(Fts3HashElem *)
|
aElem, nAlloc*sizeof(Fts3HashElem *)
|
||||||
);
|
);
|
||||||
if( !aElem2 ){
|
if( !aElem2 ){
|
||||||
@ -194459,7 +194553,7 @@ static int fts3NodeAddTerm(
|
|||||||
** this is not expected to be a serious problem.
|
** this is not expected to be a serious problem.
|
||||||
*/
|
*/
|
||||||
assert( pTree->aData==(char *)&pTree[1] );
|
assert( pTree->aData==(char *)&pTree[1] );
|
||||||
pTree->aData = (char *)sqlite3_malloc(nReq);
|
pTree->aData = (char *)sqlite3_malloc64(nReq);
|
||||||
if( !pTree->aData ){
|
if( !pTree->aData ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -194477,7 +194571,7 @@ static int fts3NodeAddTerm(
|
|||||||
|
|
||||||
if( isCopyTerm ){
|
if( isCopyTerm ){
|
||||||
if( pTree->nMalloc<nTerm ){
|
if( pTree->nMalloc<nTerm ){
|
||||||
char *zNew = sqlite3_realloc(pTree->zMalloc, nTerm*2);
|
char *zNew = sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2);
|
||||||
if( !zNew ){
|
if( !zNew ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -194503,7 +194597,7 @@ static int fts3NodeAddTerm(
|
|||||||
** now. Instead, the term is inserted into the parent of pTree. If pTree
|
** now. Instead, the term is inserted into the parent of pTree. If pTree
|
||||||
** has no parent, one is created here.
|
** has no parent, one is created here.
|
||||||
*/
|
*/
|
||||||
pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize);
|
pNew = (SegmentNode *)sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize);
|
||||||
if( !pNew ){
|
if( !pNew ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -194641,7 +194735,7 @@ static int fts3SegWriterAdd(
|
|||||||
){
|
){
|
||||||
int nPrefix; /* Size of term prefix in bytes */
|
int nPrefix; /* Size of term prefix in bytes */
|
||||||
int nSuffix; /* Size of term suffix in bytes */
|
int nSuffix; /* Size of term suffix in bytes */
|
||||||
int nReq; /* Number of bytes required on leaf page */
|
i64 nReq; /* Number of bytes required on leaf page */
|
||||||
int nData;
|
int nData;
|
||||||
SegmentWriter *pWriter = *ppWriter;
|
SegmentWriter *pWriter = *ppWriter;
|
||||||
|
|
||||||
@ -194650,13 +194744,13 @@ static int fts3SegWriterAdd(
|
|||||||
sqlite3_stmt *pStmt;
|
sqlite3_stmt *pStmt;
|
||||||
|
|
||||||
/* Allocate the SegmentWriter structure */
|
/* Allocate the SegmentWriter structure */
|
||||||
pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter));
|
pWriter = (SegmentWriter *)sqlite3_malloc64(sizeof(SegmentWriter));
|
||||||
if( !pWriter ) return SQLITE_NOMEM;
|
if( !pWriter ) return SQLITE_NOMEM;
|
||||||
memset(pWriter, 0, sizeof(SegmentWriter));
|
memset(pWriter, 0, sizeof(SegmentWriter));
|
||||||
*ppWriter = pWriter;
|
*ppWriter = pWriter;
|
||||||
|
|
||||||
/* Allocate a buffer in which to accumulate data */
|
/* Allocate a buffer in which to accumulate data */
|
||||||
pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize);
|
pWriter->aData = (char *)sqlite3_malloc64(p->nNodeSize);
|
||||||
if( !pWriter->aData ) return SQLITE_NOMEM;
|
if( !pWriter->aData ) return SQLITE_NOMEM;
|
||||||
pWriter->nSize = p->nNodeSize;
|
pWriter->nSize = p->nNodeSize;
|
||||||
|
|
||||||
@ -194731,7 +194825,7 @@ static int fts3SegWriterAdd(
|
|||||||
** the buffer to make it large enough.
|
** the buffer to make it large enough.
|
||||||
*/
|
*/
|
||||||
if( nReq>pWriter->nSize ){
|
if( nReq>pWriter->nSize ){
|
||||||
char *aNew = sqlite3_realloc(pWriter->aData, nReq);
|
char *aNew = sqlite3_realloc64(pWriter->aData, nReq);
|
||||||
if( !aNew ) return SQLITE_NOMEM;
|
if( !aNew ) return SQLITE_NOMEM;
|
||||||
pWriter->aData = aNew;
|
pWriter->aData = aNew;
|
||||||
pWriter->nSize = nReq;
|
pWriter->nSize = nReq;
|
||||||
@ -194756,7 +194850,7 @@ static int fts3SegWriterAdd(
|
|||||||
*/
|
*/
|
||||||
if( isCopyTerm ){
|
if( isCopyTerm ){
|
||||||
if( nTerm>pWriter->nMalloc ){
|
if( nTerm>pWriter->nMalloc ){
|
||||||
char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2);
|
char *zNew = sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2);
|
||||||
if( !zNew ){
|
if( !zNew ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -195064,12 +195158,12 @@ static void fts3ColumnFilter(
|
|||||||
static int fts3MsrBufferData(
|
static int fts3MsrBufferData(
|
||||||
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
|
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
|
||||||
char *pList,
|
char *pList,
|
||||||
int nList
|
i64 nList
|
||||||
){
|
){
|
||||||
if( nList>pMsr->nBuffer ){
|
if( nList>pMsr->nBuffer ){
|
||||||
char *pNew;
|
char *pNew;
|
||||||
pMsr->nBuffer = nList*2;
|
pMsr->nBuffer = nList*2;
|
||||||
pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer);
|
pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, pMsr->nBuffer);
|
||||||
if( !pNew ) return SQLITE_NOMEM;
|
if( !pNew ) return SQLITE_NOMEM;
|
||||||
pMsr->aBuffer = pNew;
|
pMsr->aBuffer = pNew;
|
||||||
}
|
}
|
||||||
@ -195125,7 +195219,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
|
|||||||
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
|
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
|
||||||
|
|
||||||
if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){
|
if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){
|
||||||
rc = fts3MsrBufferData(pMsr, pList, nList+1);
|
rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1);
|
||||||
if( rc!=SQLITE_OK ) return rc;
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
|
assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
|
||||||
pList = pMsr->aBuffer;
|
pList = pMsr->aBuffer;
|
||||||
@ -195262,11 +195356,11 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, int nReq){
|
static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){
|
||||||
if( nReq>pCsr->nBuffer ){
|
if( nReq>pCsr->nBuffer ){
|
||||||
char *aNew;
|
char *aNew;
|
||||||
pCsr->nBuffer = nReq*2;
|
pCsr->nBuffer = nReq*2;
|
||||||
aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
|
aNew = sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer);
|
||||||
if( !aNew ){
|
if( !aNew ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -195357,7 +195451,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
|
|||||||
){
|
){
|
||||||
pCsr->nDoclist = apSegment[0]->nDoclist;
|
pCsr->nDoclist = apSegment[0]->nDoclist;
|
||||||
if( fts3SegReaderIsPending(apSegment[0]) ){
|
if( fts3SegReaderIsPending(apSegment[0]) ){
|
||||||
rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
|
rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist,
|
||||||
|
(i64)pCsr->nDoclist);
|
||||||
pCsr->aDoclist = pCsr->aBuffer;
|
pCsr->aDoclist = pCsr->aBuffer;
|
||||||
}else{
|
}else{
|
||||||
pCsr->aDoclist = apSegment[0]->aDoclist;
|
pCsr->aDoclist = apSegment[0]->aDoclist;
|
||||||
@ -195410,7 +195505,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
|
|||||||
|
|
||||||
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
|
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
|
||||||
|
|
||||||
rc = fts3GrowSegReaderBuffer(pCsr, nByte+nDoclist+FTS3_NODE_PADDING);
|
rc = fts3GrowSegReaderBuffer(pCsr,
|
||||||
|
(i64)nByte+nDoclist+FTS3_NODE_PADDING);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
|
|
||||||
if( isFirst ){
|
if( isFirst ){
|
||||||
@ -195436,7 +195532,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
|
|||||||
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
|
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
|
||||||
}
|
}
|
||||||
if( nDoclist>0 ){
|
if( nDoclist>0 ){
|
||||||
rc = fts3GrowSegReaderBuffer(pCsr, nDoclist+FTS3_NODE_PADDING);
|
rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
|
memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
|
||||||
pCsr->aDoclist = pCsr->aBuffer;
|
pCsr->aDoclist = pCsr->aBuffer;
|
||||||
@ -196149,7 +196245,7 @@ struct NodeReader {
|
|||||||
static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
|
static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
|
||||||
if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){
|
if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){
|
||||||
int nAlloc = nMin;
|
int nAlloc = nMin;
|
||||||
char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc);
|
char *a = (char *)sqlite3_realloc64(pBlob->a, nAlloc);
|
||||||
if( a ){
|
if( a ){
|
||||||
pBlob->nAlloc = nAlloc;
|
pBlob->nAlloc = nAlloc;
|
||||||
pBlob->a = a;
|
pBlob->a = a;
|
||||||
@ -196946,7 +197042,7 @@ static int fts3RepackSegdirLevel(
|
|||||||
if( nIdx>=nAlloc ){
|
if( nIdx>=nAlloc ){
|
||||||
int *aNew;
|
int *aNew;
|
||||||
nAlloc += 16;
|
nAlloc += 16;
|
||||||
aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int));
|
aNew = sqlite3_realloc64(aIdx, nAlloc*sizeof(int));
|
||||||
if( !aNew ){
|
if( !aNew ){
|
||||||
rc = SQLITE_NOMEM;
|
rc = SQLITE_NOMEM;
|
||||||
break;
|
break;
|
||||||
@ -197320,7 +197416,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
|
|||||||
|
|
||||||
/* Allocate space for the cursor, filter and writer objects */
|
/* Allocate space for the cursor, filter and writer objects */
|
||||||
const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
|
const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
|
||||||
pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
|
pWriter = (IncrmergeWriter *)sqlite3_malloc64(nAlloc);
|
||||||
if( !pWriter ) return SQLITE_NOMEM;
|
if( !pWriter ) return SQLITE_NOMEM;
|
||||||
pFilter = (Fts3SegFilter *)&pWriter[1];
|
pFilter = (Fts3SegFilter *)&pWriter[1];
|
||||||
pCsr = (Fts3MultiSegReader *)&pFilter[1];
|
pCsr = (Fts3MultiSegReader *)&pFilter[1];
|
||||||
@ -197956,7 +198052,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
|
|||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
pRet = (char *)sqlite3_malloc(p->pList->nData);
|
pRet = (char *)sqlite3_malloc64(p->pList->nData);
|
||||||
if( !pRet ) return SQLITE_NOMEM;
|
if( !pRet ) return SQLITE_NOMEM;
|
||||||
|
|
||||||
nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
|
nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
|
||||||
@ -197976,7 +198072,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
|
|||||||
int iCol /* Column that token must appear in (or -1) */
|
int iCol /* Column that token must appear in (or -1) */
|
||||||
){
|
){
|
||||||
Fts3DeferredToken *pDeferred;
|
Fts3DeferredToken *pDeferred;
|
||||||
pDeferred = sqlite3_malloc(sizeof(*pDeferred));
|
pDeferred = sqlite3_malloc64(sizeof(*pDeferred));
|
||||||
if( !pDeferred ){
|
if( !pDeferred ){
|
||||||
return SQLITE_NOMEM;
|
return SQLITE_NOMEM;
|
||||||
}
|
}
|
||||||
@ -209555,7 +209651,7 @@ static int geopolyUpdate(
|
|||||||
sqlite3_free(p);
|
sqlite3_free(p);
|
||||||
nChange = 1;
|
nChange = 1;
|
||||||
}
|
}
|
||||||
for(jj=1; jj<pRtree->nAux; jj++){
|
for(jj=1; jj<nData-2; jj++){
|
||||||
nChange++;
|
nChange++;
|
||||||
sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
|
sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
|
||||||
}
|
}
|
||||||
@ -210158,8 +210254,9 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
|
|||||||
|
|
||||||
if( U_SUCCESS(status) ){
|
if( U_SUCCESS(status) ){
|
||||||
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
|
||||||
}else{
|
pExpr = sqlite3_get_auxdata(p, 0);
|
||||||
assert(!pExpr);
|
}
|
||||||
|
if( !pExpr ){
|
||||||
icuFunctionError(p, "uregex_open", status);
|
icuFunctionError(p, "uregex_open", status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -242045,7 +242142,7 @@ static void fts5SourceIdFunc(
|
|||||||
){
|
){
|
||||||
assert( nArg==0 );
|
assert( nArg==0 );
|
||||||
UNUSED_PARAM2(nArg, apUnused);
|
UNUSED_PARAM2(nArg, apUnused);
|
||||||
sqlite3_result_text(pCtx, "fts5: 2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668e6603", -1, SQLITE_TRANSIENT);
|
sqlite3_result_text(pCtx, "fts5: 2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309", -1, SQLITE_TRANSIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
6
libsqlite3-sys/sqlcipher/sqlite3.h
vendored
6
libsqlite3-sys/sqlcipher/sqlite3.h
vendored
@ -146,9 +146,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.39.2"
|
#define SQLITE_VERSION "3.39.4"
|
||||||
#define SQLITE_VERSION_NUMBER 3039002
|
#define SQLITE_VERSION_NUMBER 3039004
|
||||||
#define SQLITE_SOURCE_ID "2022-07-21 15:24:47 698edb77537b67c41adc68f9b892db56bcf9a55e00371a61420f3ddd668ealt1"
|
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26alt1"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
|
866
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
866
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
File diff suppressed because it is too large
Load Diff
15563
libsqlite3-sys/sqlite3/sqlite3.c
vendored
15563
libsqlite3-sys/sqlite3/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
524
libsqlite3-sys/sqlite3/sqlite3.h
vendored
524
libsqlite3-sys/sqlite3/sqlite3.h
vendored
@ -146,9 +146,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.39.4"
|
#define SQLITE_VERSION "3.42.0"
|
||||||
#define SQLITE_VERSION_NUMBER 3039004
|
#define SQLITE_VERSION_NUMBER 3042000
|
||||||
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309"
|
#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@ -563,6 +563,7 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
|
#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
|
||||||
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
|
||||||
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
|
||||||
|
#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
|
||||||
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
|
||||||
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
|
||||||
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
|
||||||
@ -670,13 +671,17 @@ SQLITE_API int sqlite3_exec(
|
|||||||
**
|
**
|
||||||
** SQLite uses one of these integer values as the second
|
** SQLite uses one of these integer values as the second
|
||||||
** argument to calls it makes to the xLock() and xUnlock() methods
|
** argument to calls it makes to the xLock() and xUnlock() methods
|
||||||
** of an [sqlite3_io_methods] object.
|
** of an [sqlite3_io_methods] object. These values are ordered from
|
||||||
|
** lest restrictive to most restrictive.
|
||||||
|
**
|
||||||
|
** The argument to xLock() is always SHARED or higher. The argument to
|
||||||
|
** xUnlock is either SHARED or NONE.
|
||||||
*/
|
*/
|
||||||
#define SQLITE_LOCK_NONE 0
|
#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
|
||||||
#define SQLITE_LOCK_SHARED 1
|
#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
|
||||||
#define SQLITE_LOCK_RESERVED 2
|
#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
|
||||||
#define SQLITE_LOCK_PENDING 3
|
#define SQLITE_LOCK_PENDING 3 /* xLock() only */
|
||||||
#define SQLITE_LOCK_EXCLUSIVE 4
|
#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Synchronization Type Flags
|
** CAPI3REF: Synchronization Type Flags
|
||||||
@ -754,7 +759,14 @@ struct sqlite3_file {
|
|||||||
** <li> [SQLITE_LOCK_PENDING], or
|
** <li> [SQLITE_LOCK_PENDING], or
|
||||||
** <li> [SQLITE_LOCK_EXCLUSIVE].
|
** <li> [SQLITE_LOCK_EXCLUSIVE].
|
||||||
** </ul>
|
** </ul>
|
||||||
** xLock() increases the lock. xUnlock() decreases the lock.
|
** xLock() upgrades the database file lock. In other words, xLock() moves the
|
||||||
|
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
|
||||||
|
** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
|
||||||
|
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
|
||||||
|
** requested lock, then the call to xLock() is a no-op.
|
||||||
|
** xUnlock() downgrades the database file lock to either SHARED or NONE.
|
||||||
|
* If the lock is already at or below the requested lock state, then the call
|
||||||
|
** to xUnlock() is a no-op.
|
||||||
** The xCheckReservedLock() method checks whether any database connection,
|
** The xCheckReservedLock() method checks whether any database connection,
|
||||||
** either in this process or in some other process, is holding a RESERVED,
|
** either in this process or in some other process, is holding a RESERVED,
|
||||||
** PENDING, or EXCLUSIVE lock on the file. It returns true
|
** PENDING, or EXCLUSIVE lock on the file. It returns true
|
||||||
@ -859,9 +871,8 @@ struct sqlite3_io_methods {
|
|||||||
** opcode causes the xFileControl method to write the current state of
|
** opcode causes the xFileControl method to write the current state of
|
||||||
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
|
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
|
||||||
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
|
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
|
||||||
** into an integer that the pArg argument points to. This capability
|
** into an integer that the pArg argument points to.
|
||||||
** is used during testing and is only available when the SQLITE_TEST
|
** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
|
||||||
** compile-time option is used.
|
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
|
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
|
||||||
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
|
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
|
||||||
@ -1165,7 +1176,6 @@ struct sqlite3_io_methods {
|
|||||||
** in wal mode after the client has finished copying pages from the wal
|
** in wal mode after the client has finished copying pages from the wal
|
||||||
** file to the database file, but before the *-shm file is updated to
|
** file to the database file, but before the *-shm file is updated to
|
||||||
** record the fact that the pages have been checkpointed.
|
** record the fact that the pages have been checkpointed.
|
||||||
** </ul>
|
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
||||||
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
||||||
@ -1178,10 +1188,16 @@ struct sqlite3_io_methods {
|
|||||||
** the database is not a wal-mode db, or if there is no such connection in any
|
** the database is not a wal-mode db, or if there is no such connection in any
|
||||||
** other process. This opcode cannot be used to detect transactions opened
|
** other process. This opcode cannot be used to detect transactions opened
|
||||||
** by clients within the current process, only within other processes.
|
** by clients within the current process, only within other processes.
|
||||||
** </ul>
|
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
|
** <li>[[SQLITE_FCNTL_CKSM_FILE]]
|
||||||
** Used by the cksmvfs VFS module only.
|
** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the
|
||||||
|
** [checksum VFS shim] only.
|
||||||
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_RESET_CACHE]]
|
||||||
|
** If there is currently no transaction open on the database, and the
|
||||||
|
** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
|
||||||
|
** purges the contents of the in-memory page cache. If there is an open
|
||||||
|
** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
|
||||||
** </ul>
|
** </ul>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||||
@ -1224,6 +1240,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_CKPT_START 39
|
#define SQLITE_FCNTL_CKPT_START 39
|
||||||
#define SQLITE_FCNTL_EXTERNAL_READER 40
|
#define SQLITE_FCNTL_EXTERNAL_READER 40
|
||||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||||
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
@ -1253,6 +1270,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
|
|||||||
*/
|
*/
|
||||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: File Name
|
||||||
|
**
|
||||||
|
** Type [sqlite3_filename] is used by SQLite to pass filenames to the
|
||||||
|
** xOpen method of a [VFS]. It may be cast to (const char*) and treated
|
||||||
|
** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
|
||||||
|
** may also be passed to special APIs such as:
|
||||||
|
**
|
||||||
|
** <ul>
|
||||||
|
** <li> sqlite3_filename_database()
|
||||||
|
** <li> sqlite3_filename_journal()
|
||||||
|
** <li> sqlite3_filename_wal()
|
||||||
|
** <li> sqlite3_uri_parameter()
|
||||||
|
** <li> sqlite3_uri_boolean()
|
||||||
|
** <li> sqlite3_uri_int64()
|
||||||
|
** <li> sqlite3_uri_key()
|
||||||
|
** </ul>
|
||||||
|
*/
|
||||||
|
typedef const char *sqlite3_filename;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: OS Interface Object
|
** CAPI3REF: OS Interface Object
|
||||||
**
|
**
|
||||||
@ -1431,7 +1468,7 @@ struct sqlite3_vfs {
|
|||||||
sqlite3_vfs *pNext; /* Next registered VFS */
|
sqlite3_vfs *pNext; /* Next registered VFS */
|
||||||
const char *zName; /* Name of this virtual file system */
|
const char *zName; /* Name of this virtual file system */
|
||||||
void *pAppData; /* Pointer to application-specific data */
|
void *pAppData; /* Pointer to application-specific data */
|
||||||
int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
|
int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
|
||||||
int flags, int *pOutFlags);
|
int flags, int *pOutFlags);
|
||||||
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
|
||||||
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
|
||||||
@ -1618,20 +1655,23 @@ SQLITE_API int sqlite3_os_end(void);
|
|||||||
** must ensure that no other SQLite interfaces are invoked by other
|
** must ensure that no other SQLite interfaces are invoked by other
|
||||||
** threads while sqlite3_config() is running.</b>
|
** threads while sqlite3_config() is running.</b>
|
||||||
**
|
**
|
||||||
** The sqlite3_config() interface
|
|
||||||
** may only be invoked prior to library initialization using
|
|
||||||
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
|
|
||||||
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
|
|
||||||
** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.
|
|
||||||
** Note, however, that ^sqlite3_config() can be called as part of the
|
|
||||||
** implementation of an application-defined [sqlite3_os_init()].
|
|
||||||
**
|
|
||||||
** The first argument to sqlite3_config() is an integer
|
** The first argument to sqlite3_config() is an integer
|
||||||
** [configuration option] that determines
|
** [configuration option] that determines
|
||||||
** what property of SQLite is to be configured. Subsequent arguments
|
** what property of SQLite is to be configured. Subsequent arguments
|
||||||
** vary depending on the [configuration option]
|
** vary depending on the [configuration option]
|
||||||
** in the first argument.
|
** in the first argument.
|
||||||
**
|
**
|
||||||
|
** For most configuration options, the sqlite3_config() interface
|
||||||
|
** may only be invoked prior to library initialization using
|
||||||
|
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
|
||||||
|
** The exceptional configuration options that may be invoked at any time
|
||||||
|
** are called "anytime configuration options".
|
||||||
|
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
|
||||||
|
** [sqlite3_shutdown()] with a first argument that is not an anytime
|
||||||
|
** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE.
|
||||||
|
** Note, however, that ^sqlite3_config() can be called as part of the
|
||||||
|
** implementation of an application-defined [sqlite3_os_init()].
|
||||||
|
**
|
||||||
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
|
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
|
||||||
** ^If the option is unknown or SQLite is unable to set the option
|
** ^If the option is unknown or SQLite is unable to set the option
|
||||||
** then this routine returns a non-zero [error code].
|
** then this routine returns a non-zero [error code].
|
||||||
@ -1739,6 +1779,23 @@ struct sqlite3_mem_methods {
|
|||||||
** These constants are the available integer configuration options that
|
** These constants are the available integer configuration options that
|
||||||
** can be passed as the first argument to the [sqlite3_config()] interface.
|
** can be passed as the first argument to the [sqlite3_config()] interface.
|
||||||
**
|
**
|
||||||
|
** Most of the configuration options for sqlite3_config()
|
||||||
|
** will only work if invoked prior to [sqlite3_initialize()] or after
|
||||||
|
** [sqlite3_shutdown()]. The few exceptions to this rule are called
|
||||||
|
** "anytime configuration options".
|
||||||
|
** ^Calling [sqlite3_config()] with a first argument that is not an
|
||||||
|
** anytime configuration option in between calls to [sqlite3_initialize()] and
|
||||||
|
** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
|
||||||
|
**
|
||||||
|
** The set of anytime configuration options can change (by insertions
|
||||||
|
** and/or deletions) from one release of SQLite to the next.
|
||||||
|
** As of SQLite version 3.42.0, the complete set of anytime configuration
|
||||||
|
** options is:
|
||||||
|
** <ul>
|
||||||
|
** <li> SQLITE_CONFIG_LOG
|
||||||
|
** <li> SQLITE_CONFIG_PCACHE_HDRSZ
|
||||||
|
** </ul>
|
||||||
|
**
|
||||||
** New configuration options may be added in future releases of SQLite.
|
** New configuration options may be added in future releases of SQLite.
|
||||||
** Existing configuration options might be discontinued. Applications
|
** Existing configuration options might be discontinued. Applications
|
||||||
** should check the return code from [sqlite3_config()] to make sure that
|
** should check the return code from [sqlite3_config()] to make sure that
|
||||||
@ -2085,28 +2142,28 @@ struct sqlite3_mem_methods {
|
|||||||
** compile-time option is not set, then the default maximum is 1073741824.
|
** compile-time option is not set, then the default maximum is 1073741824.
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||||
#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
|
#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
|
||||||
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
|
||||||
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
|
||||||
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
|
||||||
#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
|
||||||
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
|
||||||
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
|
||||||
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
|
||||||
#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
|
#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
|
||||||
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
|
||||||
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
|
||||||
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
|
||||||
#define SQLITE_CONFIG_PCACHE 14 /* no-op */
|
#define SQLITE_CONFIG_PCACHE 14 /* no-op */
|
||||||
#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
|
#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
|
||||||
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
|
||||||
#define SQLITE_CONFIG_URI 17 /* int */
|
#define SQLITE_CONFIG_URI 17 /* int */
|
||||||
#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
|
#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
|
||||||
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
|
||||||
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
|
||||||
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
|
||||||
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
|
||||||
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
|
||||||
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
|
||||||
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
||||||
@ -2147,7 +2204,7 @@ struct sqlite3_mem_methods {
|
|||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the "current value" returned by
|
||||||
** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
|
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].)^</dd>
|
||||||
@ -2297,8 +2354,12 @@ struct sqlite3_mem_methods {
|
|||||||
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
||||||
** </ol>
|
** </ol>
|
||||||
** Because resetting a database is destructive and irreversible, the
|
** Because resetting a database is destructive and irreversible, the
|
||||||
** process requires the use of this obscure API and multiple steps to help
|
** process requires the use of this obscure API and multiple steps to
|
||||||
** ensure that it does not happen by accident.
|
** help ensure that it does not happen by accident. Because this
|
||||||
|
** feature must be capable of resetting corrupt databases, and
|
||||||
|
** shutting down virtual tables may require access to that corrupt
|
||||||
|
** storage, the library must abandon any installed virtual tables
|
||||||
|
** without calling their xDestroy() methods.
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
|
||||||
@ -2309,6 +2370,7 @@ struct sqlite3_mem_methods {
|
|||||||
** <ul>
|
** <ul>
|
||||||
** <li> The [PRAGMA writable_schema=ON] statement.
|
** <li> The [PRAGMA writable_schema=ON] statement.
|
||||||
** <li> The [PRAGMA journal_mode=OFF] statement.
|
** <li> The [PRAGMA journal_mode=OFF] statement.
|
||||||
|
** <li> The [PRAGMA schema_version=N] statement.
|
||||||
** <li> Writes to the [sqlite_dbpage] virtual table.
|
** <li> Writes to the [sqlite_dbpage] virtual table.
|
||||||
** <li> Direct writes to [shadow tables].
|
** <li> Direct writes to [shadow tables].
|
||||||
** </ul>
|
** </ul>
|
||||||
@ -2336,7 +2398,7 @@ struct sqlite3_mem_methods {
|
|||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_DQS_DML]]
|
** [[SQLITE_DBCONFIG_DQS_DML]]
|
||||||
** <dt>SQLITE_DBCONFIG_DQS_DML</td>
|
** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
|
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
|
||||||
** the legacy [double-quoted string literal] misfeature for DML statements
|
** the legacy [double-quoted string literal] misfeature for DML statements
|
||||||
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
|
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
|
||||||
@ -2345,7 +2407,7 @@ struct sqlite3_mem_methods {
|
|||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_DQS_DDL]]
|
** [[SQLITE_DBCONFIG_DQS_DDL]]
|
||||||
** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
|
** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
|
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
|
||||||
** the legacy [double-quoted string literal] misfeature for DDL statements,
|
** the legacy [double-quoted string literal] misfeature for DDL statements,
|
||||||
** such as CREATE TABLE and CREATE INDEX. The
|
** such as CREATE TABLE and CREATE INDEX. The
|
||||||
@ -2354,7 +2416,7 @@ struct sqlite3_mem_methods {
|
|||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
|
** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
|
||||||
** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</td>
|
** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
|
** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
|
||||||
** assume that database schemas are untainted by malicious content.
|
** assume that database schemas are untainted by malicious content.
|
||||||
** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
|
** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
|
||||||
@ -2374,7 +2436,7 @@ struct sqlite3_mem_methods {
|
|||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
|
** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
|
||||||
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</td>
|
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
||||||
** the legacy file format flag. When activated, this flag causes all newly
|
** the legacy file format flag. When activated, this flag causes all newly
|
||||||
** created database file to have a schema format version number (the 4-byte
|
** created database file to have a schema format version number (the 4-byte
|
||||||
@ -2383,7 +2445,7 @@ struct sqlite3_mem_methods {
|
|||||||
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
||||||
** newly created databases are generally not understandable by SQLite versions
|
** newly created databases are generally not understandable by SQLite versions
|
||||||
** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there
|
** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there
|
||||||
** is now scarcely any need to generated database files that are compatible
|
** is now scarcely any need to generate database files that are compatible
|
||||||
** all the way back to version 3.0.0, and so this setting is of little
|
** all the way back to version 3.0.0, and so this setting is of little
|
||||||
** practical use, but is provided so that SQLite can continue to claim the
|
** practical use, but is provided so that SQLite can continue to claim the
|
||||||
** ability to generate new database files that are compatible with version
|
** ability to generate new database files that are compatible with version
|
||||||
@ -2394,6 +2456,38 @@ struct sqlite3_mem_methods {
|
|||||||
** not considered a bug since SQLite versions 3.3.0 and earlier do not support
|
** not considered a bug since SQLite versions 3.3.0 and earlier do not support
|
||||||
** either generated columns or decending indexes.
|
** either generated columns or decending indexes.
|
||||||
** </dd>
|
** </dd>
|
||||||
|
**
|
||||||
|
** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
|
||||||
|
** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
|
||||||
|
** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
|
||||||
|
** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
|
||||||
|
** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
|
||||||
|
** statistics. For statistics to be collected, the flag must be set on
|
||||||
|
** the database handle both when the SQL statement is prepared and when it
|
||||||
|
** is stepped. The flag is set (collection of statistics is enabled)
|
||||||
|
** by default. This option takes two arguments: an integer and a pointer to
|
||||||
|
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
|
||||||
|
** leave unchanged the statement scanstatus option. If the second argument
|
||||||
|
** is not NULL, then the value of the statement scanstatus setting after
|
||||||
|
** processing the first argument is written into the integer that the second
|
||||||
|
** argument points to.
|
||||||
|
** </dd>
|
||||||
|
**
|
||||||
|
** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]]
|
||||||
|
** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt>
|
||||||
|
** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order
|
||||||
|
** in which tables and indexes are scanned so that the scans start at the end
|
||||||
|
** and work toward the beginning rather than starting at the beginning and
|
||||||
|
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
|
||||||
|
** same as setting [PRAGMA reverse_unordered_selects]. This option takes
|
||||||
|
** two arguments which are an integer and a pointer to an integer. The first
|
||||||
|
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
|
||||||
|
** reverse scan order flag, respectively. If the second argument is not NULL,
|
||||||
|
** then 0 or 1 is written into the integer that the second argument points to
|
||||||
|
** depending on if the reverse scan order flag is set after processing the
|
||||||
|
** first argument.
|
||||||
|
** </dd>
|
||||||
|
**
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||||
@ -2414,7 +2508,9 @@ struct sqlite3_mem_methods {
|
|||||||
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
|
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
|
#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
|
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
|
||||||
#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */
|
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
|
||||||
|
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
|
||||||
|
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||||
@ -2636,8 +2732,12 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
|
|||||||
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
|
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
|
||||||
** SQL statements is a no-op and has no effect on SQL statements
|
** SQL statements is a no-op and has no effect on SQL statements
|
||||||
** that are started after the sqlite3_interrupt() call returns.
|
** that are started after the sqlite3_interrupt() call returns.
|
||||||
|
**
|
||||||
|
** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
|
||||||
|
** or not an interrupt is currently in effect for [database connection] D.
|
||||||
*/
|
*/
|
||||||
SQLITE_API void sqlite3_interrupt(sqlite3*);
|
SQLITE_API void sqlite3_interrupt(sqlite3*);
|
||||||
|
SQLITE_API int sqlite3_is_interrupted(sqlite3*);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Determine If An SQL Statement Is Complete
|
** CAPI3REF: Determine If An SQL Statement Is Complete
|
||||||
@ -3255,8 +3355,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
|
|||||||
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
|
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
|
||||||
** information as is provided by the [sqlite3_profile()] callback.
|
** information as is provided by the [sqlite3_profile()] callback.
|
||||||
** ^The P argument is a pointer to the [prepared statement] and the
|
** ^The P argument is a pointer to the [prepared statement] and the
|
||||||
** X argument points to a 64-bit integer which is the estimated of
|
** X argument points to a 64-bit integer which is approximately
|
||||||
** the number of nanosecond that the prepared statement took to run.
|
** the number of nanoseconds that the prepared statement took to run.
|
||||||
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
|
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
|
||||||
**
|
**
|
||||||
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
|
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
|
||||||
@ -3319,7 +3419,7 @@ SQLITE_API int sqlite3_trace_v2(
|
|||||||
**
|
**
|
||||||
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
|
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
|
||||||
** function X to be invoked periodically during long running calls to
|
** function X to be invoked periodically during long running calls to
|
||||||
** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
|
** [sqlite3_step()] and [sqlite3_prepare()] and similar for
|
||||||
** database connection D. An example use for this
|
** database connection D. An example use for this
|
||||||
** interface is to keep a GUI updated during a large query.
|
** interface is to keep a GUI updated during a large query.
|
||||||
**
|
**
|
||||||
@ -3344,6 +3444,13 @@ SQLITE_API int sqlite3_trace_v2(
|
|||||||
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
|
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
|
||||||
** database connections for the meaning of "modify" in this paragraph.
|
** database connections for the meaning of "modify" in this paragraph.
|
||||||
**
|
**
|
||||||
|
** The progress handler callback would originally only be invoked from the
|
||||||
|
** bytecode engine. It still might be invoked during [sqlite3_prepare()]
|
||||||
|
** and similar because those routines might force a reparse of the schema
|
||||||
|
** which involves running the bytecode engine. However, beginning with
|
||||||
|
** SQLite version 3.41.0, the progress handler callback might also be
|
||||||
|
** invoked directly from [sqlite3_prepare()] while analyzing and generating
|
||||||
|
** code for complex queries.
|
||||||
*/
|
*/
|
||||||
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
||||||
|
|
||||||
@ -3380,13 +3487,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
**
|
**
|
||||||
** <dl>
|
** <dl>
|
||||||
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
|
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
|
||||||
** <dd>The database is opened in read-only mode. If the database does not
|
** <dd>The database is opened in read-only mode. If the database does
|
||||||
** already exist, an error is returned.</dd>)^
|
** not already exist, an error is returned.</dd>)^
|
||||||
**
|
**
|
||||||
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
|
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
|
||||||
** <dd>The database is opened for reading and writing if possible, or reading
|
** <dd>The database is opened for reading and writing if possible, or
|
||||||
** only if the file is write protected by the operating system. In either
|
** reading only if the file is write protected by the operating
|
||||||
** case the database must already exist, otherwise an error is returned.</dd>)^
|
** system. In either case the database must already exist, otherwise
|
||||||
|
** an error is returned. For historical reasons, if opening in
|
||||||
|
** read-write mode fails due to OS-level permissions, an attempt is
|
||||||
|
** made to open it in read-only mode. [sqlite3_db_readonly()] can be
|
||||||
|
** used to determine whether the database is actually
|
||||||
|
** read-write.</dd>)^
|
||||||
**
|
**
|
||||||
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
|
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
|
||||||
** <dd>The database is opened for reading and writing, and is created if
|
** <dd>The database is opened for reading and writing, and is created if
|
||||||
@ -3424,6 +3536,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
** <dd>The database is opened [shared cache] enabled, overriding
|
** <dd>The database is opened [shared cache] enabled, overriding
|
||||||
** the default shared cache setting provided by
|
** the default shared cache setting provided by
|
||||||
** [sqlite3_enable_shared_cache()].)^
|
** [sqlite3_enable_shared_cache()].)^
|
||||||
|
** The [use of shared cache mode is discouraged] and hence shared cache
|
||||||
|
** capabilities may be omitted from many builds of SQLite. In such cases,
|
||||||
|
** this option is a no-op.
|
||||||
**
|
**
|
||||||
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
||||||
** <dd>The database is opened [shared cache] disabled, overriding
|
** <dd>The database is opened [shared cache] disabled, overriding
|
||||||
@ -3439,7 +3554,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
** to return an extended result code.</dd>
|
** to return an extended result code.</dd>
|
||||||
**
|
**
|
||||||
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
|
** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
|
||||||
** <dd>The database filename is not allowed to be a symbolic link</dd>
|
** <dd>The database filename is not allowed to contain a symbolic link</dd>
|
||||||
** </dl>)^
|
** </dl>)^
|
||||||
**
|
**
|
||||||
** If the 3rd parameter to sqlite3_open_v2() is not one of the
|
** If the 3rd parameter to sqlite3_open_v2() is not one of the
|
||||||
@ -3698,10 +3813,10 @@ SQLITE_API int sqlite3_open_v2(
|
|||||||
**
|
**
|
||||||
** See the [URI filename] documentation for additional information.
|
** See the [URI filename] documentation for additional information.
|
||||||
*/
|
*/
|
||||||
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
|
SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
|
||||||
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
|
SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
|
||||||
SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
|
SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
|
||||||
SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
|
SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Translate filenames
|
** CAPI3REF: Translate filenames
|
||||||
@ -3730,9 +3845,9 @@ SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N);
|
|||||||
** return value from [sqlite3_db_filename()], then the result is
|
** return value from [sqlite3_db_filename()], then the result is
|
||||||
** undefined and is likely a memory access violation.
|
** undefined and is likely a memory access violation.
|
||||||
*/
|
*/
|
||||||
SQLITE_API const char *sqlite3_filename_database(const char*);
|
SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
|
||||||
SQLITE_API const char *sqlite3_filename_journal(const char*);
|
SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
|
||||||
SQLITE_API const char *sqlite3_filename_wal(const char*);
|
SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Database File Corresponding To A Journal
|
** CAPI3REF: Database File Corresponding To A Journal
|
||||||
@ -3798,14 +3913,14 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
|
|||||||
** then the corresponding [sqlite3_module.xClose() method should also be
|
** then the corresponding [sqlite3_module.xClose() method should also be
|
||||||
** invoked prior to calling sqlite3_free_filename(Y).
|
** invoked prior to calling sqlite3_free_filename(Y).
|
||||||
*/
|
*/
|
||||||
SQLITE_API char *sqlite3_create_filename(
|
SQLITE_API sqlite3_filename sqlite3_create_filename(
|
||||||
const char *zDatabase,
|
const char *zDatabase,
|
||||||
const char *zJournal,
|
const char *zJournal,
|
||||||
const char *zWal,
|
const char *zWal,
|
||||||
int nParam,
|
int nParam,
|
||||||
const char **azParam
|
const char **azParam
|
||||||
);
|
);
|
||||||
SQLITE_API void sqlite3_free_filename(char*);
|
SQLITE_API void sqlite3_free_filename(sqlite3_filename);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Error Codes And Messages
|
** CAPI3REF: Error Codes And Messages
|
||||||
@ -5364,10 +5479,21 @@ SQLITE_API int sqlite3_create_window_function(
|
|||||||
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
|
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
|
||||||
** schema structures such as [CHECK constraints], [DEFAULT clauses],
|
** schema structures such as [CHECK constraints], [DEFAULT clauses],
|
||||||
** [expression indexes], [partial indexes], or [generated columns].
|
** [expression indexes], [partial indexes], or [generated columns].
|
||||||
** The SQLITE_DIRECTONLY flags is a security feature which is recommended
|
** <p>
|
||||||
** for all [application-defined SQL functions], and especially for functions
|
** The SQLITE_DIRECTONLY flag is recommended for any
|
||||||
** that have side-effects or that could potentially leak sensitive
|
** [application-defined SQL function]
|
||||||
** information.
|
** that has side-effects or that could potentially leak sensitive information.
|
||||||
|
** This will prevent attacks in which an application is tricked
|
||||||
|
** into using a database file that has had its schema surreptiously
|
||||||
|
** modified to invoke the application-defined function in ways that are
|
||||||
|
** harmful.
|
||||||
|
** <p>
|
||||||
|
** Some people say it is good practice to set SQLITE_DIRECTONLY on all
|
||||||
|
** [application-defined SQL functions], regardless of whether or not they
|
||||||
|
** are security sensitive, as doing so prevents those functions from being used
|
||||||
|
** inside of the database schema, and thus ensures that the database
|
||||||
|
** can be inspected and modified using generic tools (such as the [CLI])
|
||||||
|
** that do not have access to the application-defined functions.
|
||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
|
** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
|
||||||
@ -5573,6 +5699,28 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
|
|||||||
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
|
||||||
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
|
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
|
||||||
|
** METHOD: sqlite3_value
|
||||||
|
**
|
||||||
|
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
|
||||||
|
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
|
||||||
|
** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
|
||||||
|
** returns something other than SQLITE_TEXT, then the return value from
|
||||||
|
** sqlite3_value_encoding(X) is meaningless. ^Calls to
|
||||||
|
** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
|
||||||
|
** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
|
||||||
|
** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
|
||||||
|
** thus change the return from subsequent calls to sqlite3_value_encoding(X).
|
||||||
|
**
|
||||||
|
** This routine is intended for used by applications that test and validate
|
||||||
|
** the SQLite implementation. This routine is inquiring about the opaque
|
||||||
|
** internal state of an [sqlite3_value] object. Ordinary applications should
|
||||||
|
** not need to know what the internal state of an sqlite3_value object is and
|
||||||
|
** hence should not need to use this interface.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Finding The Subtype Of SQL Values
|
** CAPI3REF: Finding The Subtype Of SQL Values
|
||||||
** METHOD: sqlite3_value
|
** METHOD: sqlite3_value
|
||||||
@ -5625,7 +5773,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
|
|||||||
**
|
**
|
||||||
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
|
** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
|
||||||
** when first called if N is less than or equal to zero or if a memory
|
** when first called if N is less than or equal to zero or if a memory
|
||||||
** allocate error occurs.
|
** allocation error occurs.
|
||||||
**
|
**
|
||||||
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
||||||
** determined by the N parameter on first successful call. Changing the
|
** determined by the N parameter on first successful call. Changing the
|
||||||
@ -5830,9 +5978,10 @@ typedef void (*sqlite3_destructor_type)(void*);
|
|||||||
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
|
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
|
||||||
** ^SQLite takes the text result from the application from
|
** ^SQLite takes the text result from the application from
|
||||||
** the 2nd parameter of the sqlite3_result_text* interfaces.
|
** the 2nd parameter of the sqlite3_result_text* interfaces.
|
||||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
|
||||||
** is negative, then SQLite takes result text from the 2nd parameter
|
** other than sqlite3_result_text64() is negative, then SQLite computes
|
||||||
** through the first zero character.
|
** the string length itself by searching the 2nd parameter for the first
|
||||||
|
** zero character.
|
||||||
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
|
||||||
** is non-negative, then as many bytes (not characters) of the text
|
** is non-negative, then as many bytes (not characters) of the text
|
||||||
** pointed to by the 2nd parameter are taken as the application-defined
|
** pointed to by the 2nd parameter are taken as the application-defined
|
||||||
@ -6106,6 +6255,13 @@ SQLITE_API void sqlite3_activate_cerod(
|
|||||||
** of the default VFS is not implemented correctly, or not implemented at
|
** of the default VFS is not implemented correctly, or not implemented at
|
||||||
** all, then the behavior of sqlite3_sleep() may deviate from the description
|
** all, then the behavior of sqlite3_sleep() may deviate from the description
|
||||||
** in the previous paragraphs.
|
** in the previous paragraphs.
|
||||||
|
**
|
||||||
|
** If a negative argument is passed to sqlite3_sleep() the results vary by
|
||||||
|
** VFS and operating system. Some system treat a negative argument as an
|
||||||
|
** instruction to sleep forever. Others understand it to mean do not sleep
|
||||||
|
** at all. ^In SQLite version 3.42.0 and later, a negative
|
||||||
|
** argument passed into sqlite3_sleep() is changed to zero before it is relayed
|
||||||
|
** down into the xSleep method of the VFS.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_sleep(int);
|
SQLITE_API int sqlite3_sleep(int);
|
||||||
|
|
||||||
@ -6328,7 +6484,7 @@ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
|
|||||||
** <li> [sqlite3_filename_wal()]
|
** <li> [sqlite3_filename_wal()]
|
||||||
** </ul>
|
** </ul>
|
||||||
*/
|
*/
|
||||||
SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Determine if a database is read-only
|
** CAPI3REF: Determine if a database is read-only
|
||||||
@ -6465,7 +6621,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
|||||||
** function C that is invoked prior to each autovacuum of the database
|
** function C that is invoked prior to each autovacuum of the database
|
||||||
** file. ^The callback is passed a copy of the generic data pointer (P),
|
** file. ^The callback is passed a copy of the generic data pointer (P),
|
||||||
** the schema-name of the attached database that is being autovacuumed,
|
** the schema-name of the attached database that is being autovacuumed,
|
||||||
** the the size of the database file in pages, the number of free pages,
|
** the size of the database file in pages, the number of free pages,
|
||||||
** and the number of bytes per page, respectively. The callback should
|
** and the number of bytes per page, respectively. The callback should
|
||||||
** return the number of free pages that should be removed by the
|
** return the number of free pages that should be removed by the
|
||||||
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
|
** autovacuum. ^If the callback returns zero, then no autovacuum happens.
|
||||||
@ -6586,6 +6742,11 @@ SQLITE_API void *sqlite3_update_hook(
|
|||||||
** to the same database. Sharing is enabled if the argument is true
|
** to the same database. Sharing is enabled if the argument is true
|
||||||
** and disabled if the argument is false.)^
|
** and disabled if the argument is false.)^
|
||||||
**
|
**
|
||||||
|
** This interface is omitted if SQLite is compiled with
|
||||||
|
** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
|
||||||
|
** compile-time option is recommended because the
|
||||||
|
** [use of shared cache mode is discouraged].
|
||||||
|
**
|
||||||
** ^Cache sharing is enabled and disabled for an entire process.
|
** ^Cache sharing is enabled and disabled for an entire process.
|
||||||
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
|
** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
|
||||||
** In prior versions of SQLite,
|
** In prior versions of SQLite,
|
||||||
@ -6684,7 +6845,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
|
|||||||
** ^The soft heap limit may not be greater than the hard heap limit.
|
** ^The soft heap limit may not be greater than the hard heap limit.
|
||||||
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
|
** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
|
||||||
** is invoked with a value of N that is greater than the hard heap limit,
|
** is invoked with a value of N that is greater than the hard heap limit,
|
||||||
** the the soft heap limit is set to the value of the hard heap limit.
|
** the soft heap limit is set to the value of the hard heap limit.
|
||||||
** ^The soft heap limit is automatically enabled whenever the hard heap
|
** ^The soft heap limit is automatically enabled whenever the hard heap
|
||||||
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
|
** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
|
||||||
** the soft heap limit is outside the range of 1..N, then the soft heap
|
** the soft heap limit is outside the range of 1..N, then the soft heap
|
||||||
@ -6945,15 +7106,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
|
|||||||
*/
|
*/
|
||||||
SQLITE_API void sqlite3_reset_auto_extension(void);
|
SQLITE_API void sqlite3_reset_auto_extension(void);
|
||||||
|
|
||||||
/*
|
|
||||||
** The interface to the virtual-table mechanism is currently considered
|
|
||||||
** to be experimental. The interface might change in incompatible ways.
|
|
||||||
** If this is a problem for you, do not use the interface at this time.
|
|
||||||
**
|
|
||||||
** When the virtual-table mechanism stabilizes, we will declare the
|
|
||||||
** interface fixed, support it indefinitely, and remove this comment.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Structures used by the virtual table interface
|
** Structures used by the virtual table interface
|
||||||
*/
|
*/
|
||||||
@ -7072,10 +7224,10 @@ struct sqlite3_module {
|
|||||||
** when the omit flag is true there is no guarantee that the constraint will
|
** when the omit flag is true there is no guarantee that the constraint will
|
||||||
** not be checked again using byte code.)^
|
** not be checked again using byte code.)^
|
||||||
**
|
**
|
||||||
** ^The idxNum and idxPtr values are recorded and passed into the
|
** ^The idxNum and idxStr values are recorded and passed into the
|
||||||
** [xFilter] method.
|
** [xFilter] method.
|
||||||
** ^[sqlite3_free()] is used to free idxPtr if and only if
|
** ^[sqlite3_free()] is used to free idxStr if and only if
|
||||||
** needToFreeIdxPtr is true.
|
** needToFreeIdxStr is true.
|
||||||
**
|
**
|
||||||
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
|
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
|
||||||
** the correct order to satisfy the ORDER BY clause so that no separate
|
** the correct order to satisfy the ORDER BY clause so that no separate
|
||||||
@ -7195,7 +7347,7 @@ struct sqlite3_index_info {
|
|||||||
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
|
** the [sqlite3_vtab_collation()] interface. For most real-world virtual
|
||||||
** tables, the collating sequence of constraints does not matter (for example
|
** tables, the collating sequence of constraints does not matter (for example
|
||||||
** because the constraints are numeric) and so the sqlite3_vtab_collation()
|
** because the constraints are numeric) and so the sqlite3_vtab_collation()
|
||||||
** interface is no commonly needed.
|
** interface is not commonly needed.
|
||||||
*/
|
*/
|
||||||
#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
#define SQLITE_INDEX_CONSTRAINT_EQ 2
|
||||||
#define SQLITE_INDEX_CONSTRAINT_GT 4
|
#define SQLITE_INDEX_CONSTRAINT_GT 4
|
||||||
@ -7354,16 +7506,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
|
|||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
|
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
|
||||||
|
|
||||||
/*
|
|
||||||
** The interface to the virtual-table mechanism defined above (back up
|
|
||||||
** to a comment remarkably similar to this one) is currently considered
|
|
||||||
** to be experimental. The interface might change in incompatible ways.
|
|
||||||
** If this is a problem for you, do not use the interface at this time.
|
|
||||||
**
|
|
||||||
** When the virtual-table mechanism stabilizes, we will declare the
|
|
||||||
** interface fixed, support it indefinitely, and remove this comment.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: A Handle To An Open BLOB
|
** CAPI3REF: A Handle To An Open BLOB
|
||||||
** KEYWORDS: {BLOB handle} {BLOB handles}
|
** KEYWORDS: {BLOB handle} {BLOB handles}
|
||||||
@ -7747,9 +7889,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|||||||
** is undefined if the mutex is not currently entered by the
|
** is undefined if the mutex is not currently entered by the
|
||||||
** calling thread or is not currently allocated.
|
** calling thread or is not currently allocated.
|
||||||
**
|
**
|
||||||
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
|
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
|
||||||
** sqlite3_mutex_leave() is a NULL pointer, then all three routines
|
** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer,
|
||||||
** behave as no-ops.
|
** then any of the four routines behaves as a no-op.
|
||||||
**
|
**
|
||||||
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
|
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
|
||||||
*/
|
*/
|
||||||
@ -8979,7 +9121,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
|||||||
** if the application incorrectly accesses the destination [database connection]
|
** if the application incorrectly accesses the destination [database connection]
|
||||||
** and so no error code is reported, but the operations may malfunction
|
** and so no error code is reported, but the operations may malfunction
|
||||||
** nevertheless. Use of the destination database connection while a
|
** nevertheless. Use of the destination database connection while a
|
||||||
** backup is in progress might also also cause a mutex deadlock.
|
** backup is in progress might also cause a mutex deadlock.
|
||||||
**
|
**
|
||||||
** If running in [shared cache mode], the application must
|
** If running in [shared cache mode], the application must
|
||||||
** guarantee that the shared cache used by the destination database
|
** guarantee that the shared cache used by the destination database
|
||||||
@ -9407,7 +9549,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
|
|||||||
*/
|
*/
|
||||||
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
|
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
|
||||||
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
|
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
|
||||||
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
|
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
|
||||||
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
|
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -9483,18 +9625,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
|
|||||||
** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
|
** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
|
||||||
** <dd>Calls of the form
|
** <dd>Calls of the form
|
||||||
** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
|
** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
|
||||||
** the [xConnect] or [xCreate] methods of a [virtual table] implmentation
|
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
|
||||||
** identify that virtual table as being safe to use from within triggers
|
** identify that virtual table as being safe to use from within triggers
|
||||||
** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
|
** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
|
||||||
** virtual table can do no serious harm even if it is controlled by a
|
** virtual table can do no serious harm even if it is controlled by a
|
||||||
** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
|
** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
|
||||||
** flag unless absolutely necessary.
|
** flag unless absolutely necessary.
|
||||||
** </dd>
|
** </dd>
|
||||||
|
**
|
||||||
|
** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
|
||||||
|
** <dd>Calls of the form
|
||||||
|
** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
|
||||||
|
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
|
||||||
|
** instruct the query planner to begin at least a read transaction on
|
||||||
|
** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
|
||||||
|
** virtual table is used.
|
||||||
|
** </dd>
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
|
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
|
||||||
#define SQLITE_VTAB_INNOCUOUS 2
|
#define SQLITE_VTAB_INNOCUOUS 2
|
||||||
#define SQLITE_VTAB_DIRECTONLY 3
|
#define SQLITE_VTAB_DIRECTONLY 3
|
||||||
|
#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Determine The Virtual Table Conflict Policy
|
** CAPI3REF: Determine The Virtual Table Conflict Policy
|
||||||
@ -9567,7 +9719,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
|
|||||||
** <li><p> Otherwise, "BINARY" is returned.
|
** <li><p> Otherwise, "BINARY" is returned.
|
||||||
** </ol>
|
** </ol>
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Determine if a virtual table query is DISTINCT
|
** CAPI3REF: Determine if a virtual table query is DISTINCT
|
||||||
@ -9724,21 +9876,20 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
|
|||||||
** is undefined and probably harmful.
|
** is undefined and probably harmful.
|
||||||
**
|
**
|
||||||
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
|
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
|
||||||
** sqlite3_vtab_in_next(X,P) must be one of the parameters to the
|
** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
|
||||||
** xFilter method which invokes these routines, and specifically
|
** xFilter method which invokes these routines, and specifically
|
||||||
** a parameter that was previously selected for all-at-once IN constraint
|
** a parameter that was previously selected for all-at-once IN constraint
|
||||||
** processing use the [sqlite3_vtab_in()] interface in the
|
** processing use the [sqlite3_vtab_in()] interface in the
|
||||||
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
|
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
|
||||||
** an xFilter argument that was selected for all-at-once IN constraint
|
** an xFilter argument that was selected for all-at-once IN constraint
|
||||||
** processing, then these routines return [SQLITE_MISUSE])^ or perhaps
|
** processing, then these routines return [SQLITE_ERROR].)^
|
||||||
** exhibit some other undefined or harmful behavior.
|
|
||||||
**
|
**
|
||||||
** ^(Use these routines to access all values on the right-hand side
|
** ^(Use these routines to access all values on the right-hand side
|
||||||
** of the IN constraint using code like the following:
|
** of the IN constraint using code like the following:
|
||||||
**
|
**
|
||||||
** <blockquote><pre>
|
** <blockquote><pre>
|
||||||
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
|
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
|
||||||
** rc==SQLITE_OK && pVal
|
** rc==SQLITE_OK && pVal;
|
||||||
** rc=sqlite3_vtab_in_next(pList, &pVal)
|
** rc=sqlite3_vtab_in_next(pList, &pVal)
|
||||||
** ){
|
** ){
|
||||||
** // do something with pVal
|
** // do something with pVal
|
||||||
@ -9836,6 +9987,10 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
|
|||||||
** managed by the prepared statement S and will be automatically freed when
|
** managed by the prepared statement S and will be automatically freed when
|
||||||
** S is finalized.
|
** S is finalized.
|
||||||
**
|
**
|
||||||
|
** Not all values are available for all query elements. When a value is
|
||||||
|
** not available, the output variable is set to -1 if the value is numeric,
|
||||||
|
** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
|
||||||
|
**
|
||||||
** <dl>
|
** <dl>
|
||||||
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
|
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
|
||||||
** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
|
** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
|
||||||
@ -9863,12 +10018,24 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
|
|||||||
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
|
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
|
||||||
** description for the X-th loop.
|
** description for the X-th loop.
|
||||||
**
|
**
|
||||||
** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
|
** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
|
||||||
** <dd>^The "int" variable pointed to by the V parameter will be set to the
|
** <dd>^The "int" variable pointed to by the V parameter will be set to the
|
||||||
** "select-id" for the X-th loop. The select-id identifies which query or
|
** id for the X-th query plan element. The id value is unique within the
|
||||||
** subquery the loop is part of. The main query has a select-id of zero.
|
** statement. The select-id is the same value as is output in the first
|
||||||
** The select-id is the same value as is output in the first column
|
** column of an [EXPLAIN QUERY PLAN] query.
|
||||||
** of an [EXPLAIN QUERY PLAN] query.
|
**
|
||||||
|
** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
|
||||||
|
** <dd>The "int" variable pointed to by the V parameter will be set to the
|
||||||
|
** the id of the parent of the current query element, if applicable, or
|
||||||
|
** to zero if the query element has no parent. This is the same value as
|
||||||
|
** returned in the second column of an [EXPLAIN QUERY PLAN] query.
|
||||||
|
**
|
||||||
|
** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
|
||||||
|
** <dd>The sqlite3_int64 output value is set to the number of cycles,
|
||||||
|
** according to the processor time-stamp counter, that elapsed while the
|
||||||
|
** query element was being processed. This value is not available for
|
||||||
|
** all query elements - if it is unavailable the output variable is
|
||||||
|
** set to -1.
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_SCANSTAT_NLOOP 0
|
#define SQLITE_SCANSTAT_NLOOP 0
|
||||||
@ -9877,12 +10044,14 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
|
|||||||
#define SQLITE_SCANSTAT_NAME 3
|
#define SQLITE_SCANSTAT_NAME 3
|
||||||
#define SQLITE_SCANSTAT_EXPLAIN 4
|
#define SQLITE_SCANSTAT_EXPLAIN 4
|
||||||
#define SQLITE_SCANSTAT_SELECTID 5
|
#define SQLITE_SCANSTAT_SELECTID 5
|
||||||
|
#define SQLITE_SCANSTAT_PARENTID 6
|
||||||
|
#define SQLITE_SCANSTAT_NCYCLE 7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Prepared Statement Scan Status
|
** CAPI3REF: Prepared Statement Scan Status
|
||||||
** METHOD: sqlite3_stmt
|
** METHOD: sqlite3_stmt
|
||||||
**
|
**
|
||||||
** This interface returns information about the predicted and measured
|
** These interfaces return information about the predicted and measured
|
||||||
** performance for pStmt. Advanced applications can use this
|
** performance for pStmt. Advanced applications can use this
|
||||||
** interface to compare the predicted and the measured performance and
|
** interface to compare the predicted and the measured performance and
|
||||||
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
|
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
|
||||||
@ -9893,19 +10062,25 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **
|
|||||||
**
|
**
|
||||||
** The "iScanStatusOp" parameter determines which status information to return.
|
** The "iScanStatusOp" parameter determines which status information to return.
|
||||||
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
|
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
|
||||||
** of this interface is undefined.
|
** of this interface is undefined. ^The requested measurement is written into
|
||||||
** ^The requested measurement is written into a variable pointed to by
|
** a variable pointed to by the "pOut" parameter.
|
||||||
** the "pOut" parameter.
|
|
||||||
** Parameter "idx" identifies the specific loop to retrieve statistics for.
|
|
||||||
** Loops are numbered starting from zero. ^If idx is out of range - less than
|
|
||||||
** zero or greater than or equal to the total number of loops used to implement
|
|
||||||
** the statement - a non-zero value is returned and the variable that pOut
|
|
||||||
** points to is unchanged.
|
|
||||||
**
|
**
|
||||||
** ^Statistics might not be available for all loops in all statements. ^In cases
|
** The "flags" parameter must be passed a mask of flags. At present only
|
||||||
** where there exist loops with no available statistics, this function behaves
|
** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
|
||||||
** as if the loop did not exist - it returns non-zero and leave the variable
|
** is specified, then status information is available for all elements
|
||||||
** that pOut points to unchanged.
|
** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
|
||||||
|
** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
|
||||||
|
** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
|
||||||
|
** the EXPLAIN QUERY PLAN output) are available. Invoking API
|
||||||
|
** sqlite3_stmt_scanstatus() is equivalent to calling
|
||||||
|
** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
|
||||||
|
**
|
||||||
|
** Parameter "idx" identifies the specific query element to retrieve statistics
|
||||||
|
** for. Query elements are numbered starting from zero. A value of -1 may be
|
||||||
|
** to query for statistics regarding the entire query. ^If idx is out of range
|
||||||
|
** - less than -1 or greater than or equal to the total number of query
|
||||||
|
** elements used to implement the statement - a non-zero value is returned and
|
||||||
|
** the variable that pOut points to is unchanged.
|
||||||
**
|
**
|
||||||
** See also: [sqlite3_stmt_scanstatus_reset()]
|
** See also: [sqlite3_stmt_scanstatus_reset()]
|
||||||
*/
|
*/
|
||||||
@ -9915,6 +10090,19 @@ SQLITE_API int sqlite3_stmt_scanstatus(
|
|||||||
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
|
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
|
||||||
void *pOut /* Result written here */
|
void *pOut /* Result written here */
|
||||||
);
|
);
|
||||||
|
SQLITE_API int sqlite3_stmt_scanstatus_v2(
|
||||||
|
sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
|
||||||
|
int idx, /* Index of loop to report on */
|
||||||
|
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
|
||||||
|
int flags, /* Mask of flags defined below */
|
||||||
|
void *pOut /* Result written here */
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Prepared Statement Scan Status
|
||||||
|
** KEYWORDS: {scan status flags}
|
||||||
|
*/
|
||||||
|
#define SQLITE_SCANSTAT_COMPLEX 0x0001
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Zero Scan-Status Counters
|
** CAPI3REF: Zero Scan-Status Counters
|
||||||
@ -10005,6 +10193,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
|
|||||||
** function is not defined for operations on WITHOUT ROWID tables, or for
|
** function is not defined for operations on WITHOUT ROWID tables, or for
|
||||||
** DELETE operations on rowid tables.
|
** DELETE operations on rowid tables.
|
||||||
**
|
**
|
||||||
|
** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from
|
||||||
|
** the previous call on the same [database connection] D, or NULL for
|
||||||
|
** the first call on D.
|
||||||
|
**
|
||||||
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
|
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
|
||||||
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
|
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
|
||||||
** provide additional information about a preupdate event. These routines
|
** provide additional information about a preupdate event. These routines
|
||||||
@ -10410,6 +10602,19 @@ SQLITE_API int sqlite3_deserialize(
|
|||||||
# undef double
|
# undef double
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__wasi__)
|
||||||
|
# undef SQLITE_WASI
|
||||||
|
# define SQLITE_WASI 1
|
||||||
|
# undef SQLITE_OMIT_WAL
|
||||||
|
# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */
|
||||||
|
# ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||||
|
# define SQLITE_OMIT_LOAD_EXTENSION
|
||||||
|
# endif
|
||||||
|
# ifndef SQLITE_THREADSAFE
|
||||||
|
# define SQLITE_THREADSAFE 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* End of the 'extern "C"' block */
|
} /* End of the 'extern "C"' block */
|
||||||
#endif
|
#endif
|
||||||
@ -10616,16 +10821,20 @@ SQLITE_API int sqlite3session_create(
|
|||||||
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPIREF: Conigure a Session Object
|
** CAPI3REF: Configure a Session Object
|
||||||
** METHOD: sqlite3_session
|
** METHOD: sqlite3_session
|
||||||
**
|
**
|
||||||
** This method is used to configure a session object after it has been
|
** This method is used to configure a session object after it has been
|
||||||
** created. At present the only valid value for the second parameter is
|
** created. At present the only valid values for the second parameter are
|
||||||
** [SQLITE_SESSION_OBJCONFIG_SIZE].
|
** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID].
|
||||||
**
|
**
|
||||||
** Arguments for sqlite3session_object_config()
|
*/
|
||||||
|
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Options for sqlite3session_object_config
|
||||||
**
|
**
|
||||||
** The following values may passed as the the 4th parameter to
|
** The following values may passed as the the 2nd parameter to
|
||||||
** sqlite3session_object_config().
|
** sqlite3session_object_config().
|
||||||
**
|
**
|
||||||
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
|
** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
|
||||||
@ -10641,12 +10850,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
|
|||||||
**
|
**
|
||||||
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
||||||
** the first table has been attached to the session object.
|
** the first table has been attached to the session object.
|
||||||
|
**
|
||||||
|
** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd>
|
||||||
|
** This option is used to set, clear or query the flag that enables
|
||||||
|
** collection of data for tables with no explicit PRIMARY KEY.
|
||||||
|
**
|
||||||
|
** Normally, tables with no explicit PRIMARY KEY are simply ignored
|
||||||
|
** by the sessions module. However, if this flag is set, it behaves
|
||||||
|
** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted
|
||||||
|
** as their leftmost columns.
|
||||||
|
**
|
||||||
|
** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
|
||||||
|
** the first table has been attached to the session object.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
|
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
||||||
|
#define SQLITE_SESSION_OBJCONFIG_ROWID 2
|
||||||
/*
|
|
||||||
*/
|
|
||||||
#define SQLITE_SESSION_OBJCONFIG_SIZE 1
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Enable Or Disable A Session Object
|
** CAPI3REF: Enable Or Disable A Session Object
|
||||||
@ -11779,9 +11997,23 @@ SQLITE_API int sqlite3changeset_apply_v2(
|
|||||||
** Invert the changeset before applying it. This is equivalent to inverting
|
** Invert the changeset before applying it. This is equivalent to inverting
|
||||||
** a changeset using sqlite3changeset_invert() before applying it. It is
|
** a changeset using sqlite3changeset_invert() before applying it. It is
|
||||||
** an error to specify this flag with a patchset.
|
** an error to specify this flag with a patchset.
|
||||||
|
**
|
||||||
|
** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd>
|
||||||
|
** Do not invoke the conflict handler callback for any changes that
|
||||||
|
** would not actually modify the database even if they were applied.
|
||||||
|
** Specifically, this means that the conflict handler is not invoked
|
||||||
|
** for:
|
||||||
|
** <ul>
|
||||||
|
** <li>a delete change if the row being deleted cannot be found,
|
||||||
|
** <li>an update change if the modified fields are already set to
|
||||||
|
** their new values in the conflicting row, or
|
||||||
|
** <li>an insert change if all fields of the conflicting row match
|
||||||
|
** the row being inserted.
|
||||||
|
** </ul>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
||||||
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
||||||
|
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||||
|
12
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
12
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
@ -331,9 +331,9 @@ struct sqlite3_api_routines {
|
|||||||
const char *(*filename_journal)(const char*);
|
const char *(*filename_journal)(const char*);
|
||||||
const char *(*filename_wal)(const char*);
|
const char *(*filename_wal)(const char*);
|
||||||
/* Version 3.32.0 and later */
|
/* Version 3.32.0 and later */
|
||||||
char *(*create_filename)(const char*,const char*,const char*,
|
const char *(*create_filename)(const char*,const char*,const char*,
|
||||||
int,const char**);
|
int,const char**);
|
||||||
void (*free_filename)(char*);
|
void (*free_filename)(const char*);
|
||||||
sqlite3_file *(*database_file_object)(const char*);
|
sqlite3_file *(*database_file_object)(const char*);
|
||||||
/* Version 3.34.0 and later */
|
/* Version 3.34.0 and later */
|
||||||
int (*txn_state)(sqlite3*,const char*);
|
int (*txn_state)(sqlite3*,const char*);
|
||||||
@ -357,6 +357,10 @@ struct sqlite3_api_routines {
|
|||||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||||
unsigned int);
|
unsigned int);
|
||||||
const char *(*db_name)(sqlite3*,int);
|
const char *(*db_name)(sqlite3*,int);
|
||||||
|
/* Version 3.40.0 and later */
|
||||||
|
int (*value_encoding)(sqlite3_value*);
|
||||||
|
/* Version 3.41.0 and later */
|
||||||
|
int (*is_interrupted)(sqlite3*);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -681,6 +685,10 @@ typedef int (*sqlite3_loadext_entry)(
|
|||||||
#define sqlite3_serialize sqlite3_api->serialize
|
#define sqlite3_serialize sqlite3_api->serialize
|
||||||
#endif
|
#endif
|
||||||
#define sqlite3_db_name sqlite3_api->db_name
|
#define sqlite3_db_name sqlite3_api->db_name
|
||||||
|
/* Version 3.40.0 and later */
|
||||||
|
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||||
|
/* Version 3.41.0 and later */
|
||||||
|
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
@ -31,9 +31,6 @@ mod bindings {
|
|||||||
}
|
}
|
||||||
pub use bindings::*;
|
pub use bindings::*;
|
||||||
|
|
||||||
pub type sqlite3_index_constraint = sqlite3_index_info_sqlite3_index_constraint;
|
|
||||||
pub type sqlite3_index_constraint_usage = sqlite3_index_info_sqlite3_index_constraint_usage;
|
|
||||||
|
|
||||||
impl Default for sqlite3_vtab {
|
impl Default for sqlite3_vtab {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
unsafe { mem::zeroed() }
|
unsafe { mem::zeroed() }
|
||||||
|
@ -9,8 +9,8 @@ export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3"
|
|||||||
export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
|
export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
|
||||||
|
|
||||||
# Download and extract amalgamation
|
# Download and extract amalgamation
|
||||||
SQLITE=sqlite-amalgamation-3390400
|
SQLITE=sqlite-amalgamation-3420000
|
||||||
curl -O https://sqlite.org/2022/$SQLITE.zip
|
curl -O https://sqlite.org/2023/$SQLITE.zip
|
||||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c"
|
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.c" > "$SQLITE3_LIB_DIR/sqlite3.c"
|
||||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h"
|
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3.h" > "$SQLITE3_LIB_DIR/sqlite3.h"
|
||||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3ext.h" > "$SQLITE3_LIB_DIR/sqlite3ext.h"
|
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3ext.h" > "$SQLITE3_LIB_DIR/sqlite3ext.h"
|
||||||
|
@ -8,7 +8,7 @@ mkdir -p "$SCRIPT_DIR/../target" "$SCRIPT_DIR/sqlcipher"
|
|||||||
export SQLCIPHER_LIB_DIR="$SCRIPT_DIR/sqlcipher"
|
export SQLCIPHER_LIB_DIR="$SCRIPT_DIR/sqlcipher"
|
||||||
export SQLCIPHER_INCLUDE_DIR="$SQLCIPHER_LIB_DIR"
|
export SQLCIPHER_INCLUDE_DIR="$SQLCIPHER_LIB_DIR"
|
||||||
|
|
||||||
SQLCIPHER_VERSION="4.5.2"
|
SQLCIPHER_VERSION="4.5.3"
|
||||||
# Download and generate sqlcipher amalgamation
|
# Download and generate sqlcipher amalgamation
|
||||||
mkdir -p $SCRIPT_DIR/sqlcipher.src
|
mkdir -p $SCRIPT_DIR/sqlcipher.src
|
||||||
[ -e "v${SQLCIPHER_VERSION}.tar.gz" ] || curl -sfL -O "https://github.com/sqlcipher/sqlcipher/archive/v${SQLCIPHER_VERSION}.tar.gz"
|
[ -e "v${SQLCIPHER_VERSION}.tar.gz" ] || curl -sfL -O "https://github.com/sqlcipher/sqlcipher/archive/v${SQLCIPHER_VERSION}.tar.gz"
|
||||||
|
@ -336,7 +336,7 @@ mod test {
|
|||||||
backup.step(-1)?;
|
backup.step(-1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
|
||||||
assert_eq!(42, the_answer);
|
assert_eq!(42, the_answer);
|
||||||
|
|
||||||
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
||||||
@ -346,7 +346,7 @@ mod test {
|
|||||||
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
|
||||||
assert_eq!(42 + 43, the_answer);
|
assert_eq!(42 + 43, the_answer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -368,7 +368,7 @@ mod test {
|
|||||||
backup.step(-1)?;
|
backup.step(-1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
|
||||||
assert_eq!(42, the_answer);
|
assert_eq!(42, the_answer);
|
||||||
|
|
||||||
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
||||||
@ -379,7 +379,7 @@ mod test {
|
|||||||
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
|
||||||
assert_eq!(42 + 43, the_answer);
|
assert_eq!(42 + 43, the_answer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -406,7 +406,7 @@ mod test {
|
|||||||
backup.step(-1)?;
|
backup.step(-1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT x FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT x FROM foo")?;
|
||||||
assert_eq!(42, the_answer);
|
assert_eq!(42, the_answer);
|
||||||
|
|
||||||
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
src.execute_batch("INSERT INTO foo VALUES(43)")?;
|
||||||
@ -421,7 +421,7 @@ mod test {
|
|||||||
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
backup.run_to_completion(5, Duration::from_millis(250), None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", [], |r| r.get(0))?;
|
let the_answer: i64 = dst.one_column("SELECT SUM(x) FROM foo")?;
|
||||||
assert_eq!(42 + 43, the_answer);
|
assert_eq!(42 + 43, the_answer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@
|
|||||||
//! // Insert another BLOB, this time using a parameter passed in from
|
//! // Insert another BLOB, this time using a parameter passed in from
|
||||||
//! // rust (potentially with a dynamic size).
|
//! // rust (potentially with a dynamic size).
|
||||||
//! db.execute(
|
//! db.execute(
|
||||||
//! "INSERT INTO test_table (content) VALUES (?)",
|
//! "INSERT INTO test_table (content) VALUES (?1)",
|
||||||
//! [ZeroBlob(64)],
|
//! [ZeroBlob(64)],
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
@ -175,7 +175,7 @@
|
|||||||
//! // Insert another blob, this time using a parameter passed in from
|
//! // Insert another blob, this time using a parameter passed in from
|
||||||
//! // rust (potentially with a dynamic size).
|
//! // rust (potentially with a dynamic size).
|
||||||
//! db.execute(
|
//! db.execute(
|
||||||
//! "INSERT INTO test_table (content) VALUES (?)",
|
//! "INSERT INTO test_table (content) VALUES (?1)",
|
||||||
//! [ZeroBlob(64)],
|
//! [ZeroBlob(64)],
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
@ -274,7 +274,6 @@ impl Blob<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
use std::convert::TryInto;
|
|
||||||
self.size().try_into().unwrap()
|
self.size().try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ mod test {
|
|||||||
));
|
));
|
||||||
blob.raw_read_at_exact(&mut s2, 5).unwrap_err();
|
blob.raw_read_at_exact(&mut s2, 5).unwrap_err();
|
||||||
|
|
||||||
let end_pos = blob.seek(std::io::SeekFrom::Current(0)).unwrap();
|
let end_pos = blob.stream_position().unwrap();
|
||||||
assert_eq!(end_pos, 1);
|
assert_eq!(end_pos, 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
///! Busy handler (when the database is locked)
|
//! Busy handler (when the database is locked)
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Prepared statements cache for faster execution.
|
//! Prepared statements cache for faster execution.
|
||||||
|
|
||||||
use crate::raw_statement::RawStatement;
|
use crate::raw_statement::RawStatement;
|
||||||
use crate::{Connection, Result, Statement};
|
use crate::{Connection, PrepFlags, Result, Statement};
|
||||||
use hashlink::LruCache;
|
use hashlink::LruCache;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
@ -17,13 +17,13 @@ impl Connection {
|
|||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// fn insert_new_people(conn: &Connection) -> Result<()> {
|
/// fn insert_new_people(conn: &Connection) -> Result<()> {
|
||||||
/// {
|
/// {
|
||||||
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
|
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
|
||||||
/// stmt.execute(["Joe Smith"])?;
|
/// stmt.execute(["Joe Smith"])?;
|
||||||
/// }
|
/// }
|
||||||
/// {
|
/// {
|
||||||
/// // This will return the same underlying SQLite statement handle without
|
/// // This will return the same underlying SQLite statement handle without
|
||||||
/// // having to prepare it again.
|
/// // having to prepare it again.
|
||||||
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
|
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?1)")?;
|
||||||
/// stmt.execute(["Bob Jones"])?;
|
/// stmt.execute(["Bob Jones"])?;
|
||||||
/// }
|
/// }
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -144,7 +144,7 @@ impl StatementCache {
|
|||||||
let mut cache = self.0.borrow_mut();
|
let mut cache = self.0.borrow_mut();
|
||||||
let stmt = match cache.remove(trimmed) {
|
let stmt = match cache.remove(trimmed) {
|
||||||
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
|
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
|
||||||
None => conn.prepare(trimmed),
|
None => conn.prepare_with_flags(trimmed, PrepFlags::SQLITE_PREPARE_PERSISTENT),
|
||||||
};
|
};
|
||||||
stmt.map(|mut stmt| {
|
stmt.map(|mut stmt| {
|
||||||
stmt.stmt.set_statement_cache_key(trimmed);
|
stmt.stmt.set_statement_cache_key(trimmed);
|
||||||
|
@ -203,7 +203,7 @@ mod test {
|
|||||||
assert_eq!(ty, Type::Integer);
|
assert_eq!(ty, Type::Integer);
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
panic!("Unexpected error type: {:?}", e);
|
panic!("Unexpected error type: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match row.get::<_, String>("y").unwrap_err() {
|
match row.get::<_, String>("y").unwrap_err() {
|
||||||
@ -213,7 +213,7 @@ mod test {
|
|||||||
assert_eq!(ty, Type::Null);
|
assert_eq!(ty, Type::Null);
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
panic!("Unexpected error type: {:?}", e);
|
panic!("Unexpected error type: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -61,6 +61,13 @@ pub enum DbConfig {
|
|||||||
/// sqlite_master tables) are untainted by malicious content.
|
/// sqlite_master tables) are untainted by malicious content.
|
||||||
#[cfg(feature = "modern_sqlite")]
|
#[cfg(feature = "modern_sqlite")]
|
||||||
SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017, // 3.31.0
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
|
40
src/error.rs
40
src/error.rs
@ -252,11 +252,7 @@ impl fmt::Display for Error {
|
|||||||
),
|
),
|
||||||
Error::FromSqlConversionFailure(i, ref t, ref err) => {
|
Error::FromSqlConversionFailure(i, ref t, ref err) => {
|
||||||
if i != UNKNOWN_COLUMN {
|
if i != UNKNOWN_COLUMN {
|
||||||
write!(
|
write!(f, "Conversion error from type {t} at index: {i}, {err}")
|
||||||
f,
|
|
||||||
"Conversion error from type {} at index: {}, {}",
|
|
||||||
t, i, err
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
err.fmt(f)
|
err.fmt(f)
|
||||||
}
|
}
|
||||||
@ -278,15 +274,12 @@ impl fmt::Display for Error {
|
|||||||
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
|
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {i}"),
|
||||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
|
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {name}"),
|
||||||
Error::InvalidColumnType(i, ref name, ref t) => write!(
|
Error::InvalidColumnType(i, ref name, ref t) => {
|
||||||
f,
|
write!(f, "Invalid column type {t} at index: {i}, name: {name}")
|
||||||
"Invalid column type {} at index: {}, name: {}",
|
}
|
||||||
t, i, name
|
|
||||||
),
|
|
||||||
Error::InvalidParameterCount(i1, n1) => write!(
|
Error::InvalidParameterCount(i1, n1) => write!(
|
||||||
f,
|
f,
|
||||||
"Wrong number of parameters passed to query. Got {}, needed {}",
|
"Wrong number of parameters passed to query. Got {i1}, needed {n1}"
|
||||||
i1, n1
|
|
||||||
),
|
),
|
||||||
Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
|
Error::StatementChangedRows(i) => write!(f, "Query changed {i} rows"),
|
||||||
|
|
||||||
@ -393,7 +386,6 @@ impl Error {
|
|||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
|
pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
|
||||||
// TODO sqlite3_error_offset // 3.38.0, #1130
|
|
||||||
Error::SqliteFailure(ffi::Error::new(code), message)
|
Error::SqliteFailure(ffi::Error::new(code), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,3 +435,25 @@ pub fn check(code: c_int) -> Result<()> {
|
|||||||
Ok(())
|
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 std::os::raw::c_char,
|
||||||
|
) -> std::os::raw::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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -71,21 +71,13 @@ use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
|
|||||||
use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
|
use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
|
||||||
|
|
||||||
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
||||||
// Extended constraint error codes were added in SQLite 3.7.16. We don't have
|
|
||||||
// an explicit feature check for that, and this doesn't really warrant one.
|
|
||||||
// We'll use the extended code if we're on the bundled version (since it's
|
|
||||||
// at least 3.17.0) and the normal constraint error code if not.
|
|
||||||
fn constraint_error_code() -> i32 {
|
|
||||||
ffi::SQLITE_CONSTRAINT_FUNCTION
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Error::SqliteFailure(ref err, ref s) = *err {
|
if let Error::SqliteFailure(ref err, ref s) = *err {
|
||||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||||
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
|
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ffi::sqlite3_result_error_code(ctx, constraint_error_code());
|
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
||||||
if let Ok(cstr) = str_to_cstring(&err.to_string()) {
|
if let Ok(cstr) = str_to_cstring(&err.to_string()) {
|
||||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||||
}
|
}
|
||||||
@ -833,9 +825,9 @@ mod test {
|
|||||||
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
||||||
half,
|
half,
|
||||||
)?;
|
)?;
|
||||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
let result: f64 = db.one_column("SELECT half(6)")?;
|
||||||
|
|
||||||
assert!((3f64 - result?).abs() < f64::EPSILON);
|
assert!((3f64 - result).abs() < f64::EPSILON);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,11 +840,11 @@ mod test {
|
|||||||
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
||||||
half,
|
half,
|
||||||
)?;
|
)?;
|
||||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
let result: f64 = db.one_column("SELECT half(6)")?;
|
||||||
assert!((3f64 - result?).abs() < f64::EPSILON);
|
assert!((3f64 - result).abs() < f64::EPSILON);
|
||||||
|
|
||||||
db.remove_function("half", 1)?;
|
db.remove_function("half", 1)?;
|
||||||
let result: Result<f64> = db.query_row("SELECT half(6)", [], |r| r.get(0));
|
let result: Result<f64> = db.one_column("SELECT half(6)");
|
||||||
result.unwrap_err();
|
result.unwrap_err();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -860,7 +852,7 @@ mod test {
|
|||||||
// This implementation of a regexp scalar function uses SQLite's auxiliary data
|
// 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
|
// (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular
|
||||||
// expression multiple times within one query.
|
// expression multiple times within one query.
|
||||||
fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result<(bool, SubType)> {
|
fn regexp_with_auxiliary(ctx: &Context<'_>) -> Result<(bool, SubType)> {
|
||||||
assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
|
assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
|
||||||
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
let regexp: std::sync::Arc<Regex> = ctx
|
let regexp: std::sync::Arc<Regex> = ctx
|
||||||
@ -881,7 +873,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_function_regexp_with_auxilliary() -> Result<()> {
|
fn test_function_regexp_with_auxiliary() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch(
|
db.execute_batch(
|
||||||
"BEGIN;
|
"BEGIN;
|
||||||
@ -895,21 +887,17 @@ mod test {
|
|||||||
"regexp",
|
"regexp",
|
||||||
2,
|
2,
|
||||||
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
||||||
regexp_with_auxilliary,
|
regexp_with_auxiliary,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let result: Result<bool> =
|
let result: bool = db.one_column("SELECT regexp('l.s[aeiouy]', 'lisa')")?;
|
||||||
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", [], |r| r.get(0));
|
|
||||||
|
|
||||||
assert!(result?);
|
assert!(result);
|
||||||
|
|
||||||
let result: Result<i64> = db.query_row(
|
let result: i64 =
|
||||||
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
|
db.one_column("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1")?;
|
||||||
[],
|
|
||||||
|r| r.get(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(2, result?);
|
assert_eq!(2, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,7 +925,7 @@ mod test {
|
|||||||
("onetwo", "SELECT my_concat('one', 'two')"),
|
("onetwo", "SELECT my_concat('one', 'two')"),
|
||||||
("abc", "SELECT my_concat('a', 'b', 'c')"),
|
("abc", "SELECT my_concat('a', 'b', 'c')"),
|
||||||
] {
|
] {
|
||||||
let result: String = db.query_row(query, [], |r| r.get(0))?;
|
let result: String = db.one_column(query)?;
|
||||||
assert_eq!(expected, result);
|
assert_eq!(expected, result);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -956,11 +944,8 @@ mod test {
|
|||||||
Ok((true, None))
|
Ok((true, None))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let res: bool = db.query_row(
|
let res: bool =
|
||||||
"SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)",
|
db.one_column("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.
|
// Doesn't actually matter, we'll assert in the function if there's a problem.
|
||||||
assert!(res);
|
assert!(res);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1015,11 +1000,11 @@ mod test {
|
|||||||
|
|
||||||
// sum should return NULL when given no columns (contrast with count below)
|
// sum should return NULL when given no columns (contrast with count below)
|
||||||
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
||||||
let result: Option<i64> = db.query_row(no_result, [], |r| r.get(0))?;
|
let result: Option<i64> = db.one_column(no_result)?;
|
||||||
assert!(result.is_none());
|
assert!(result.is_none());
|
||||||
|
|
||||||
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
||||||
let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?;
|
let result: i64 = db.one_column(single_sum)?;
|
||||||
assert_eq!(4, result);
|
assert_eq!(4, result);
|
||||||
|
|
||||||
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
|
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
|
||||||
@ -1041,11 +1026,11 @@ mod test {
|
|||||||
|
|
||||||
// count should return 0 when given no columns (contrast with sum above)
|
// count should return 0 when given no columns (contrast with sum above)
|
||||||
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
||||||
let result: i64 = db.query_row(no_result, [], |r| r.get(0))?;
|
let result: i64 = db.one_column(no_result)?;
|
||||||
assert_eq!(result, 0);
|
assert_eq!(result, 0);
|
||||||
|
|
||||||
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
||||||
let result: i64 = db.query_row(single_sum, [], |r| r.get(0))?;
|
let result: i64 = db.one_column(single_sum)?;
|
||||||
assert_eq!(2, result);
|
assert_eq!(2, result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -656,7 +656,7 @@ unsafe fn free_boxed_hook<F>(p: *mut c_void) {
|
|||||||
|
|
||||||
unsafe fn expect_utf8<'a>(p_str: *const c_char, description: &'static str) -> &'a str {
|
unsafe fn expect_utf8<'a>(p_str: *const c_char, description: &'static str) -> &'a str {
|
||||||
expect_optional_utf8(p_str, description)
|
expect_optional_utf8(p_str, description)
|
||||||
.unwrap_or_else(|| panic!("received empty {}", description))
|
.unwrap_or_else(|| panic!("received empty {description}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn expect_optional_utf8<'a>(
|
unsafe fn expect_optional_utf8<'a>(
|
||||||
@ -667,7 +667,7 @@ unsafe fn expect_optional_utf8<'a>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
std::str::from_utf8(std::ffi::CStr::from_ptr(p_str).to_bytes())
|
std::str::from_utf8(std::ffi::CStr::from_ptr(p_str).to_bytes())
|
||||||
.unwrap_or_else(|_| panic!("received non-utf8 string as {}", description))
|
.unwrap_or_else(|_| panic!("received non-utf8 string as {description}"))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use super::ffi;
|
use super::ffi;
|
||||||
use super::str_for_sqlite;
|
use super::str_for_sqlite;
|
||||||
use super::{Connection, InterruptHandle, OpenFlags, Result};
|
use super::{Connection, InterruptHandle, OpenFlags, PrepFlags, Result};
|
||||||
use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
|
use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error};
|
||||||
use crate::raw_statement::RawStatement;
|
use crate::raw_statement::RawStatement;
|
||||||
use crate::statement::Statement;
|
use crate::statement::Statement;
|
||||||
@ -69,13 +69,13 @@ impl InnerConnection {
|
|||||||
|
|
||||||
// Replicate the check for sane open flags from SQLite, because the check in
|
// Replicate the check for sane open flags from SQLite, because the check in
|
||||||
// SQLite itself wasn't added until version 3.7.3.
|
// SQLite itself wasn't added until version 3.7.3.
|
||||||
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
|
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits(), 0x02);
|
||||||
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
|
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits(), 0x04);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits,
|
1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits(),
|
||||||
0x40
|
0x40
|
||||||
);
|
);
|
||||||
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
|
if (1 << (flags.bits() & 0x7)) & 0x46 == 0 {
|
||||||
return Err(Error::SqliteFailure(
|
return Err(Error::SqliteFailure(
|
||||||
ffi::Error::new(ffi::SQLITE_MISUSE),
|
ffi::Error::new(ffi::SQLITE_MISUSE),
|
||||||
None,
|
None,
|
||||||
@ -218,33 +218,24 @@ impl InnerConnection {
|
|||||||
unsafe { ffi::sqlite3_last_insert_rowid(self.db()) }
|
unsafe { ffi::sqlite3_last_insert_rowid(self.db()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
|
pub fn prepare<'a>(
|
||||||
let mut c_stmt = ptr::null_mut();
|
&mut self,
|
||||||
|
conn: &'a Connection,
|
||||||
|
sql: &str,
|
||||||
|
flags: PrepFlags,
|
||||||
|
) -> Result<Statement<'a>> {
|
||||||
|
let mut c_stmt: *mut ffi::sqlite3_stmt = ptr::null_mut();
|
||||||
let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
|
let (c_sql, len, _) = str_for_sqlite(sql.as_bytes())?;
|
||||||
let mut c_tail = ptr::null();
|
let mut c_tail: *const c_char = ptr::null();
|
||||||
// TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728
|
// TODO sqlite3_prepare_v3 (https://sqlite.org/c3ref/c_prepare_normalize.html) // 3.20.0, #728
|
||||||
#[cfg(not(feature = "unlock_notify"))]
|
#[cfg(not(feature = "unlock_notify"))]
|
||||||
let r = unsafe {
|
let r = unsafe { self.prepare_(c_sql, len, flags, &mut c_stmt, &mut c_tail) };
|
||||||
ffi::sqlite3_prepare_v2(
|
|
||||||
self.db(),
|
|
||||||
c_sql,
|
|
||||||
len,
|
|
||||||
&mut c_stmt as *mut *mut ffi::sqlite3_stmt,
|
|
||||||
&mut c_tail as *mut *const c_char,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unlock_notify")]
|
#[cfg(feature = "unlock_notify")]
|
||||||
let r = unsafe {
|
let r = unsafe {
|
||||||
use crate::unlock_notify;
|
use crate::unlock_notify;
|
||||||
let mut rc;
|
let mut rc;
|
||||||
loop {
|
loop {
|
||||||
rc = ffi::sqlite3_prepare_v2(
|
rc = self.prepare_(c_sql, len, flags, &mut c_stmt, &mut c_tail);
|
||||||
self.db(),
|
|
||||||
c_sql,
|
|
||||||
len,
|
|
||||||
&mut c_stmt as *mut *mut ffi::sqlite3_stmt,
|
|
||||||
&mut c_tail as *mut *const c_char,
|
|
||||||
);
|
|
||||||
if !unlock_notify::is_locked(self.db, rc) {
|
if !unlock_notify::is_locked(self.db, rc) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -261,8 +252,6 @@ impl InnerConnection {
|
|||||||
}
|
}
|
||||||
// If the input text contains no SQL (if the input is an empty string or a
|
// If the input text contains no SQL (if the input is an empty string or a
|
||||||
// comment) then *ppStmt is set to NULL.
|
// comment) then *ppStmt is set to NULL.
|
||||||
let c_stmt: *mut ffi::sqlite3_stmt = c_stmt;
|
|
||||||
let c_tail: *const c_char = c_tail;
|
|
||||||
let tail = if c_tail.is_null() {
|
let tail = if c_tail.is_null() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
@ -278,6 +267,32 @@ impl InnerConnection {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(not(feature = "modern_sqlite"))]
|
||||||
|
unsafe fn prepare_(
|
||||||
|
&self,
|
||||||
|
z_sql: *const c_char,
|
||||||
|
n_byte: c_int,
|
||||||
|
_: PrepFlags,
|
||||||
|
pp_stmt: *mut *mut ffi::sqlite3_stmt,
|
||||||
|
pz_tail: *mut *const c_char,
|
||||||
|
) -> c_int {
|
||||||
|
ffi::sqlite3_prepare_v2(self.db(), z_sql, n_byte, pp_stmt, pz_tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "modern_sqlite")]
|
||||||
|
unsafe fn prepare_(
|
||||||
|
&self,
|
||||||
|
z_sql: *const c_char,
|
||||||
|
n_byte: c_int,
|
||||||
|
flags: PrepFlags,
|
||||||
|
pp_stmt: *mut *mut ffi::sqlite3_stmt,
|
||||||
|
pz_tail: *mut *const c_char,
|
||||||
|
) -> c_int {
|
||||||
|
ffi::sqlite3_prepare_v3(self.db(), z_sql, n_byte, flags.bits(), pp_stmt, pz_tail)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn changes(&self) -> u64 {
|
pub fn changes(&self) -> u64 {
|
||||||
#[cfg(not(feature = "modern_sqlite"))]
|
#[cfg(not(feature = "modern_sqlite"))]
|
||||||
@ -371,15 +386,7 @@ impl Drop for InnerConnection {
|
|||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
use std::thread::panicking;
|
self.close();
|
||||||
|
|
||||||
if let Err(e) = self.close() {
|
|
||||||
if panicking() {
|
|
||||||
eprintln!("Error while closing SQLite connection: {e:?}");
|
|
||||||
} else {
|
|
||||||
panic!("Error while closing SQLite connection: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +397,7 @@ pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
|
|||||||
|
|
||||||
// threading mode checks are not necessary (and do not work) on target
|
// threading mode checks are not necessary (and do not work) on target
|
||||||
// platforms that do not have threading (such as webassembly)
|
// platforms that do not have threading (such as webassembly)
|
||||||
#[cfg(any(target_arch = "wasm32"))]
|
#[cfg(target_arch = "wasm32")]
|
||||||
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
276
src/lib.rs
276
src/lib.rs
@ -62,7 +62,7 @@ use std::ffi::{CStr, CString};
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
@ -75,7 +75,7 @@ use crate::types::ValueRef;
|
|||||||
|
|
||||||
pub use crate::cache::CachedStatement;
|
pub use crate::cache::CachedStatement;
|
||||||
pub use crate::column::Column;
|
pub use crate::column::Column;
|
||||||
pub use crate::error::Error;
|
pub use crate::error::{to_sqlite_error, Error};
|
||||||
pub use crate::ffi::ErrorCode;
|
pub use crate::ffi::ErrorCode;
|
||||||
#[cfg(feature = "load_extension")]
|
#[cfg(feature = "load_extension")]
|
||||||
pub use crate::load_extension_guard::LoadExtensionGuard;
|
pub use crate::load_extension_guard::LoadExtensionGuard;
|
||||||
@ -119,6 +119,9 @@ mod params;
|
|||||||
mod pragma;
|
mod pragma;
|
||||||
mod raw_statement;
|
mod raw_statement;
|
||||||
mod row;
|
mod row;
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||||
|
pub mod serialize;
|
||||||
#[cfg(feature = "session")]
|
#[cfg(feature = "session")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "session")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "session")))]
|
||||||
pub mod session;
|
pub mod session;
|
||||||
@ -140,13 +143,6 @@ pub(crate) use util::SmallCString;
|
|||||||
|
|
||||||
// Number of cached prepared statements we'll hold on to.
|
// Number of cached prepared statements we'll hold on to.
|
||||||
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
|
||||||
/// To be used when your statement has no [parameter][sqlite-varparam].
|
|
||||||
///
|
|
||||||
/// [sqlite-varparam]: https://sqlite.org/lang_expr.html#varparam
|
|
||||||
///
|
|
||||||
/// This is deprecated in favor of using an empty array literal.
|
|
||||||
#[deprecated = "Use an empty array instead; `stmt.execute(NO_PARAMS)` => `stmt.execute([])`"]
|
|
||||||
pub const NO_PARAMS: &[&dyn ToSql] = &[];
|
|
||||||
|
|
||||||
/// A macro making it more convenient to longer lists of
|
/// A macro making it more convenient to longer lists of
|
||||||
/// parameters as a `&[&dyn ToSql]`.
|
/// parameters as a `&[&dyn ToSql]`.
|
||||||
@ -330,7 +326,6 @@ impl DatabaseName<'_> {
|
|||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
db: RefCell<InnerConnection>,
|
db: RefCell<InnerConnection>,
|
||||||
cache: StatementCache,
|
cache: StatementCache,
|
||||||
path: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Connection {}
|
unsafe impl Send for Connection {}
|
||||||
@ -427,7 +422,6 @@ impl Connection {
|
|||||||
InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection {
|
InnerConnection::open_with_flags(&c_path, flags, None).map(|db| Connection {
|
||||||
db: RefCell::new(db),
|
db: RefCell::new(db),
|
||||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||||
path: Some(path.as_ref().to_path_buf()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +446,6 @@ impl Connection {
|
|||||||
InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection {
|
InnerConnection::open_with_flags(&c_path, flags, Some(&c_vfs)).map(|db| Connection {
|
||||||
db: RefCell::new(db),
|
db: RefCell::new(db),
|
||||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||||
path: Some(path.as_ref().to_path_buf()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,7 +527,7 @@ impl Connection {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection};
|
/// # use rusqlite::{Connection};
|
||||||
/// fn update_rows(conn: &Connection) {
|
/// fn update_rows(conn: &Connection) {
|
||||||
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) {
|
/// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?1", [1i32]) {
|
||||||
/// Ok(updated) => println!("{} rows were updated", updated),
|
/// Ok(updated) => println!("{} rows were updated", updated),
|
||||||
/// Err(err) => println!("update failed: {}", err),
|
/// Err(err) => println!("update failed: {}", err),
|
||||||
/// }
|
/// }
|
||||||
@ -580,12 +573,23 @@ impl Connection {
|
|||||||
|
|
||||||
/// Returns the path to the database file, if one exists and is known.
|
/// 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
|
/// Note that in some cases [PRAGMA
|
||||||
/// database_list](https://sqlite.org/pragma.html#pragma_database_list) is
|
/// database_list](https://sqlite.org/pragma.html#pragma_database_list) is
|
||||||
/// likely to be more robust.
|
/// likely to be more robust.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn path(&self) -> Option<&Path> {
|
pub fn path(&self) -> Option<&str> {
|
||||||
self.path.as_deref()
|
unsafe {
|
||||||
|
let db = self.handle();
|
||||||
|
let db_name = DatabaseName::Main.as_cstring().unwrap();
|
||||||
|
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
|
||||||
|
if db_filename.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
CStr::from_ptr(db_filename).to_str().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to free as much heap memory as possible from the database
|
/// Attempts to free as much heap memory as possible from the database
|
||||||
@ -598,26 +602,6 @@ impl Connection {
|
|||||||
self.db.borrow_mut().release_memory()
|
self.db.borrow_mut().release_memory()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to prepare and execute a single SQL statement with
|
|
||||||
/// named parameter(s).
|
|
||||||
///
|
|
||||||
/// On success, returns the number of rows that were changed or inserted or
|
|
||||||
/// deleted (via `sqlite3_changes`).
|
|
||||||
///
|
|
||||||
/// # Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
||||||
/// or if the underlying SQLite call fails.
|
|
||||||
#[deprecated = "You can use `execute` with named params now."]
|
|
||||||
pub fn execute_named(&self, sql: &str, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
|
|
||||||
// This function itself is deprecated, so it's fine
|
|
||||||
#![allow(deprecated)]
|
|
||||||
self.prepare(sql).and_then(|mut stmt| {
|
|
||||||
stmt.check_no_tail()
|
|
||||||
.and_then(|_| stmt.execute_named(params))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the SQLite rowid of the most recent successful INSERT.
|
/// 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
|
/// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under
|
||||||
@ -665,26 +649,10 @@ impl Connection {
|
|||||||
stmt.query_row(params, f)
|
stmt.query_row(params, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to execute a query with named parameter(s) that is
|
// https://sqlite.org/tclsqlite.html#onecolumn
|
||||||
/// expected to return a single row.
|
#[cfg(test)]
|
||||||
///
|
pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> {
|
||||||
/// If the query returns more than one row, all rows except the first are
|
self.query_row(sql, [], |r| r.get(0))
|
||||||
/// 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<Option<T>>`.
|
|
||||||
///
|
|
||||||
/// # Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
||||||
/// or if the underlying SQLite call fails.
|
|
||||||
#[deprecated = "You can use `query_row` with named params now."]
|
|
||||||
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
|
|
||||||
where
|
|
||||||
F: FnOnce(&Row<'_>) -> Result<T>,
|
|
||||||
{
|
|
||||||
self.query_row(sql, params, f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to execute a query that is expected to return a
|
/// Convenience method to execute a query that is expected to return a
|
||||||
@ -733,7 +701,7 @@ impl Connection {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// fn insert_new_people(conn: &Connection) -> Result<()> {
|
/// fn insert_new_people(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?;
|
/// let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?1)")?;
|
||||||
/// stmt.execute(["Joe Smith"])?;
|
/// stmt.execute(["Joe Smith"])?;
|
||||||
/// stmt.execute(["Bob Jones"])?;
|
/// stmt.execute(["Bob Jones"])?;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -746,7 +714,18 @@ impl Connection {
|
|||||||
/// or if the underlying SQLite call fails.
|
/// or if the underlying SQLite call fails.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
|
pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
|
||||||
self.db.borrow_mut().prepare(self, sql)
|
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<Statement<'_>> {
|
||||||
|
self.db.borrow_mut().prepare(self, sql, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the SQLite connection.
|
/// Close the SQLite connection.
|
||||||
@ -915,12 +894,31 @@ impl Connection {
|
|||||||
/// This function is unsafe because improper use may impact the Connection.
|
/// This function is unsafe because improper use may impact the Connection.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> {
|
pub unsafe fn from_handle(db: *mut ffi::sqlite3) -> Result<Connection> {
|
||||||
let db_path = db_filename(db);
|
|
||||||
let db = InnerConnection::new(db, false);
|
let db = InnerConnection::new(db, false);
|
||||||
Ok(Connection {
|
Ok(Connection {
|
||||||
db: RefCell::new(db),
|
db: RefCell::new(db),
|
||||||
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||||
path: db_path,
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Connection> {
|
||||||
|
let db = InnerConnection::new(db, true);
|
||||||
|
Ok(Connection {
|
||||||
|
db: RefCell::new(db),
|
||||||
|
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,7 +971,7 @@ impl Connection {
|
|||||||
impl fmt::Debug for Connection {
|
impl fmt::Debug for Connection {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Connection")
|
f.debug_struct("Connection")
|
||||||
.field("path", &self.path)
|
.field("path", &self.path())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1041,11 +1039,12 @@ impl<'conn> Iterator for Batch<'conn, '_> {
|
|||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
/// Flags for opening SQLite database connections. See
|
/// Flags for opening SQLite database connections. See
|
||||||
/// [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details.
|
/// [sqlite3_open_v2](https://www.sqlite.org/c3ref/open.html) for details.
|
||||||
///
|
///
|
||||||
/// The default open flags are `SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE
|
/// The default open flags are `SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE
|
||||||
/// | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX`. See [`Connection::open`] for
|
/// | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX`. See [`Connection::open`] for
|
||||||
/// some discussion about these flags.
|
/// some discussion about these flags.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct OpenFlags: ::std::os::raw::c_int {
|
pub struct OpenFlags: ::std::os::raw::c_int {
|
||||||
/// The database is opened in read-only mode.
|
/// The database is opened in read-only mode.
|
||||||
@ -1122,6 +1121,19 @@ impl Default for OpenFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
/// Prepare flags. See
|
||||||
|
/// [sqlite3_prepare_v3](https://sqlite.org/c3ref/c_prepare_normalize.html) for details.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PrepFlags: ::std::os::raw::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or
|
/// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or
|
||||||
/// later. If you are running against a SQLite older than that, rusqlite
|
/// later. If you are running against a SQLite older than that, rusqlite
|
||||||
/// attempts to ensure safety by performing configuration and initialization of
|
/// attempts to ensure safety by performing configuration and initialization of
|
||||||
@ -1164,16 +1176,6 @@ impl InterruptHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn db_filename(db: *mut ffi::sqlite3) -> Option<PathBuf> {
|
|
||||||
let db_name = DatabaseName::Main.as_cstring().unwrap();
|
|
||||||
let db_filename = ffi::sqlite3_db_filename(db, db_name.as_ptr());
|
|
||||||
if db_filename.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
CStr::from_ptr(db_filename).to_str().ok().map(PathBuf::from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(doctest)]
|
#[cfg(doctest)]
|
||||||
doc_comment::doctest!("../README.md");
|
doc_comment::doctest!("../README.md");
|
||||||
|
|
||||||
@ -1188,13 +1190,21 @@ mod test {
|
|||||||
// this function is never called, but is still type checked; in
|
// this function is never called, but is still type checked; in
|
||||||
// particular, calls with specific instantiations will require
|
// particular, calls with specific instantiations will require
|
||||||
// that those types are `Send`.
|
// that those types are `Send`.
|
||||||
#[allow(dead_code, unconditional_recursion)]
|
#[allow(
|
||||||
|
dead_code,
|
||||||
|
unconditional_recursion,
|
||||||
|
clippy::extra_unused_type_parameters
|
||||||
|
)]
|
||||||
fn ensure_send<T: Send>() {
|
fn ensure_send<T: Send>() {
|
||||||
ensure_send::<Connection>();
|
ensure_send::<Connection>();
|
||||||
ensure_send::<InterruptHandle>();
|
ensure_send::<InterruptHandle>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code, unconditional_recursion)]
|
#[allow(
|
||||||
|
dead_code,
|
||||||
|
unconditional_recursion,
|
||||||
|
clippy::extra_unused_type_parameters
|
||||||
|
)]
|
||||||
fn ensure_sync<T: Sync>() {
|
fn ensure_sync<T: Sync>() {
|
||||||
ensure_sync::<InterruptHandle>();
|
ensure_sync::<InterruptHandle>();
|
||||||
}
|
}
|
||||||
@ -1261,9 +1271,9 @@ mod test {
|
|||||||
|
|
||||||
let path_string = path.to_str().unwrap();
|
let path_string = path.to_str().unwrap();
|
||||||
let db = Connection::open(path_string)?;
|
let db = Connection::open(path_string)?;
|
||||||
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
|
let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
|
||||||
|
|
||||||
assert_eq!(42i64, the_answer?);
|
assert_eq!(42i64, the_answer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1275,6 +1285,21 @@ mod test {
|
|||||||
db.close().unwrap();
|
db.close().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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().map(|p| p.ends_with("file.db")).unwrap_or(false));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_open_failure() {
|
fn test_open_failure() {
|
||||||
let filename = "no_such_file.db";
|
let filename = "no_such_file.db";
|
||||||
@ -1285,9 +1310,7 @@ mod test {
|
|||||||
assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
|
assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code);
|
||||||
assert!(
|
assert!(
|
||||||
msg.contains(filename),
|
msg.contains(filename),
|
||||||
"error message '{}' does not contain '{}'",
|
"error message '{msg}' does not contain '{filename}'"
|
||||||
msg,
|
|
||||||
filename
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("SqliteFailure expected");
|
panic!("SqliteFailure expected");
|
||||||
@ -1318,9 +1341,9 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let db = Connection::open(&db_path)?;
|
let db = Connection::open(&db_path)?;
|
||||||
let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
|
let the_answer: i64 = db.one_column("SELECT x FROM foo")?;
|
||||||
|
|
||||||
assert_eq!(42i64, the_answer?);
|
assert_eq!(42i64, the_answer);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1400,13 +1423,10 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
|
||||||
|
|
||||||
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [1i32])?);
|
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [1i32])?);
|
||||||
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [2i32])?);
|
assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?1)", [2i32])?);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
|
||||||
3i32,
|
|
||||||
db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1414,12 +1434,11 @@ mod test {
|
|||||||
#[cfg(feature = "extra_check")]
|
#[cfg(feature = "extra_check")]
|
||||||
fn test_execute_select() {
|
fn test_execute_select() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
let err = db.execute("SELECT 1 WHERE 1 < ?", [1i32]).unwrap_err();
|
let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err,
|
err,
|
||||||
Error::ExecuteReturnedResults,
|
Error::ExecuteReturnedResults,
|
||||||
"Unexpected error: {}",
|
"Unexpected error: {err}"
|
||||||
err
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1435,7 +1454,7 @@ mod test {
|
|||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
match err {
|
match err {
|
||||||
Error::MultipleStatement => (),
|
Error::MultipleStatement => (),
|
||||||
_ => panic!("Unexpected error: {}", err),
|
_ => panic!("Unexpected error: {err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1459,7 +1478,7 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
||||||
|
|
||||||
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?;
|
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?;
|
||||||
assert_eq!(insert_stmt.execute([1i32])?, 1);
|
assert_eq!(insert_stmt.execute([1i32])?, 1);
|
||||||
assert_eq!(insert_stmt.execute([2i32])?, 1);
|
assert_eq!(insert_stmt.execute([2i32])?, 1);
|
||||||
assert_eq!(insert_stmt.execute([3i32])?, 1);
|
assert_eq!(insert_stmt.execute([3i32])?, 1);
|
||||||
@ -1468,7 +1487,7 @@ mod test {
|
|||||||
assert_eq!(insert_stmt.execute(["goodbye"])?, 1);
|
assert_eq!(insert_stmt.execute(["goodbye"])?, 1);
|
||||||
assert_eq!(insert_stmt.execute([types::Null])?, 1);
|
assert_eq!(insert_stmt.execute([types::Null])?, 1);
|
||||||
|
|
||||||
let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?")?;
|
let mut update_stmt = db.prepare("UPDATE foo SET x=?1 WHERE x<?2")?;
|
||||||
assert_eq!(update_stmt.execute([3i32, 3i32])?, 2);
|
assert_eq!(update_stmt.execute([3i32, 3i32])?, 2);
|
||||||
assert_eq!(update_stmt.execute([3i32, 3i32])?, 0);
|
assert_eq!(update_stmt.execute([3i32, 3i32])?, 0);
|
||||||
assert_eq!(update_stmt.execute([8i32, 8i32])?, 3);
|
assert_eq!(update_stmt.execute([8i32, 8i32])?, 3);
|
||||||
@ -1480,12 +1499,12 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
||||||
|
|
||||||
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?;
|
let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?1)")?;
|
||||||
assert_eq!(insert_stmt.execute([1i32])?, 1);
|
assert_eq!(insert_stmt.execute([1i32])?, 1);
|
||||||
assert_eq!(insert_stmt.execute([2i32])?, 1);
|
assert_eq!(insert_stmt.execute([2i32])?, 1);
|
||||||
assert_eq!(insert_stmt.execute([3i32])?, 1);
|
assert_eq!(insert_stmt.execute([3i32])?, 1);
|
||||||
|
|
||||||
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?;
|
let mut query = db.prepare("SELECT x FROM foo WHERE x < ?1 ORDER BY x DESC")?;
|
||||||
{
|
{
|
||||||
let mut rows = query.query([4i32])?;
|
let mut rows = query.query([4i32])?;
|
||||||
let mut v = Vec::<i32>::new();
|
let mut v = Vec::<i32>::new();
|
||||||
@ -1541,15 +1560,12 @@ mod test {
|
|||||||
END;";
|
END;";
|
||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(10i64, db.one_column::<i64>("SELECT SUM(x) FROM foo")?);
|
||||||
10i64,
|
|
||||||
db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
|
|
||||||
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0));
|
let result: Result<i64> = db.one_column("SELECT x FROM foo WHERE x > 5");
|
||||||
match result.unwrap_err() {
|
match result.unwrap_err() {
|
||||||
Error::QueryReturnedNoRows => (),
|
Error::QueryReturnedNoRows => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
|
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
|
||||||
@ -1562,21 +1578,21 @@ mod test {
|
|||||||
fn test_optional() -> Result<()> {
|
fn test_optional() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
|
|
||||||
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0));
|
let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 <> 0");
|
||||||
let result = result.optional();
|
let result = result.optional();
|
||||||
match result? {
|
match result? {
|
||||||
None => (),
|
None => (),
|
||||||
_ => panic!("Unexpected result"),
|
_ => panic!("Unexpected result"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0));
|
let result: Result<i64> = db.one_column("SELECT 1 WHERE 0 == 0");
|
||||||
let result = result.optional();
|
let result = result.optional();
|
||||||
match result? {
|
match result? {
|
||||||
Some(1) => (),
|
Some(1) => (),
|
||||||
_ => panic!("Unexpected result"),
|
_ => panic!("Unexpected result"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0));
|
let bad_query_result: Result<i64> = db.one_column("NOT A PROPER QUERY");
|
||||||
let bad_query_result = bad_query_result.optional();
|
let bad_query_result = bad_query_result.optional();
|
||||||
bad_query_result.unwrap_err();
|
bad_query_result.unwrap_err();
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1585,11 +1601,8 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pragma_query_row() -> Result<()> {
|
fn test_pragma_query_row() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
assert_eq!(
|
assert_eq!("memory", db.one_column::<String>("PRAGMA journal_mode")?);
|
||||||
"memory",
|
let mode = db.one_column::<String>("PRAGMA journal_mode=off")?;
|
||||||
db.query_row::<String, _, _>("PRAGMA journal_mode", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
let mode = db.query_row::<String, _, _>("PRAGMA journal_mode=off", [], |r| r.get(0))?;
|
|
||||||
if cfg!(features = "bundled") {
|
if cfg!(features = "bundled") {
|
||||||
assert_eq!(mode, "off");
|
assert_eq!(mode, "off");
|
||||||
} else {
|
} else {
|
||||||
@ -1604,7 +1617,7 @@ mod test {
|
|||||||
// > MEMORY or OFF and can not be changed to a different value. An
|
// > 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
|
// > attempt to change the journal_mode of an in-memory database to
|
||||||
// > any setting other than MEMORY or OFF is ignored.
|
// > any setting other than MEMORY or OFF is ignored.
|
||||||
assert!(mode == "memory" || mode == "off", "Got mode {:?}", mode);
|
assert!(mode == "memory" || mode == "off", "Got mode {mode:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1691,7 +1704,7 @@ mod test {
|
|||||||
assert_eq!(err.code, ErrorCode::ConstraintViolation);
|
assert_eq!(err.code, ErrorCode::ConstraintViolation);
|
||||||
check_extended_code(err.extended_code);
|
check_extended_code(err.extended_code);
|
||||||
}
|
}
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1756,7 +1769,7 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(i, x);")?;
|
db.execute_batch("CREATE TABLE foo(i, x);")?;
|
||||||
let vals = ["foobar", "1234", "qwerty"];
|
let vals = ["foobar", "1234", "qwerty"];
|
||||||
let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?, ?)")?;
|
let mut insert_stmt = db.prepare("INSERT INTO foo(i, x) VALUES(?1, ?2)")?;
|
||||||
for (i, v) in vals.iter().enumerate() {
|
for (i, v) in vals.iter().enumerate() {
|
||||||
let i_to_insert = i as i64;
|
let i_to_insert = i as i64;
|
||||||
assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1);
|
assert_eq!(insert_stmt.execute(params![i_to_insert, v])?, 1);
|
||||||
@ -1796,6 +1809,16 @@ mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_handle_owned() -> Result<()> {
|
||||||
|
let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut();
|
||||||
|
let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const i8, &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 {
|
mod query_and_then_tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1873,7 +1896,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_type.unwrap_err() {
|
match bad_type.unwrap_err() {
|
||||||
Error::InvalidColumnType(..) => (),
|
Error::InvalidColumnType(..) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let bad_idx: Result<Vec<String>> =
|
let bad_idx: Result<Vec<String>> =
|
||||||
@ -1881,7 +1904,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_idx.unwrap_err() {
|
match bad_idx.unwrap_err() {
|
||||||
Error::InvalidColumnIndex(_) => (),
|
Error::InvalidColumnIndex(_) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1926,7 +1949,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_type.unwrap_err() {
|
match bad_type.unwrap_err() {
|
||||||
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let bad_idx: CustomResult<Vec<String>> = query
|
let bad_idx: CustomResult<Vec<String>> = query
|
||||||
@ -1935,7 +1958,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_idx.unwrap_err() {
|
match bad_idx.unwrap_err() {
|
||||||
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let non_sqlite_err: CustomResult<Vec<String>> = query
|
let non_sqlite_err: CustomResult<Vec<String>> = query
|
||||||
@ -1944,7 +1967,7 @@ mod test {
|
|||||||
|
|
||||||
match non_sqlite_err.unwrap_err() {
|
match non_sqlite_err.unwrap_err() {
|
||||||
CustomError::SomeError => (),
|
CustomError::SomeError => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1981,7 +2004,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_type.unwrap_err() {
|
match bad_type.unwrap_err() {
|
||||||
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let bad_idx: CustomResult<String> =
|
let bad_idx: CustomResult<String> =
|
||||||
@ -1989,7 +2012,7 @@ mod test {
|
|||||||
|
|
||||||
match bad_idx.unwrap_err() {
|
match bad_idx.unwrap_err() {
|
||||||
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let non_sqlite_err: CustomResult<String> =
|
let non_sqlite_err: CustomResult<String> =
|
||||||
@ -1997,7 +2020,7 @@ mod test {
|
|||||||
|
|
||||||
match non_sqlite_err.unwrap_err() {
|
match non_sqlite_err.unwrap_err() {
|
||||||
CustomError::SomeError => (),
|
CustomError::SomeError => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -2022,7 +2045,7 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
|
||||||
let b: Box<dyn ToSql> = Box::new(5);
|
let b: Box<dyn ToSql> = Box::new(5);
|
||||||
db.execute("INSERT INTO foo VALUES(?)", [b])?;
|
db.execute("INSERT INTO foo VALUES(?1)", [b])?;
|
||||||
db.query_row("SELECT x FROM foo", [], |r| {
|
db.query_row("SELECT x FROM foo", [], |r| {
|
||||||
assert_eq!(5, r.get_unwrap::<_, i32>(0));
|
assert_eq!(5, r.get_unwrap::<_, i32>(0));
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -2034,10 +2057,10 @@ mod test {
|
|||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.query_row(
|
db.query_row(
|
||||||
"SELECT
|
"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![
|
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, 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,
|
||||||
@ -2079,10 +2102,7 @@ mod test {
|
|||||||
fn test_returning() -> Result<()> {
|
fn test_returning() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?;
|
||||||
let row_id =
|
let row_id = db.one_column::<i64>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID")?;
|
||||||
db.query_row::<i64, _, _>("INSERT INTO foo DEFAULT VALUES RETURNING ROWID", [], |r| {
|
|
||||||
r.get(0)
|
|
||||||
})?;
|
|
||||||
assert_eq!(row_id, 1);
|
assert_eq!(row_id, 1);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ use sealed::Sealed;
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result, params};
|
/// # use rusqlite::{Connection, Result, params};
|
||||||
/// fn update_rows(conn: &Connection) -> Result<()> {
|
/// fn update_rows(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?, ?)")?;
|
/// let mut stmt = conn.prepare("INSERT INTO test (a, b) VALUES (?1, ?2)")?;
|
||||||
///
|
///
|
||||||
/// // Using a tuple:
|
/// // Using a tuple:
|
||||||
/// stmt.execute((0, "foobar"))?;
|
/// stmt.execute((0, "foobar"))?;
|
||||||
@ -138,9 +138,7 @@ use sealed::Sealed;
|
|||||||
/// ## No parameters
|
/// ## No parameters
|
||||||
///
|
///
|
||||||
/// You can just use an empty tuple or the empty array literal to run a query
|
/// You can just use an empty tuple or the empty array literal to run a query
|
||||||
/// that accepts no parameters. (The `rusqlite::NO_PARAMS` constant which was
|
/// that accepts no parameters.
|
||||||
/// common in previous versions of this library is no longer needed, and is now
|
|
||||||
/// deprecated).
|
|
||||||
///
|
///
|
||||||
/// ### Example (no parameters)
|
/// ### Example (no parameters)
|
||||||
///
|
///
|
||||||
@ -192,8 +190,7 @@ pub trait Params: Sealed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
|
// Explicitly impl for empty array. Critically, for `conn.execute([])` to be
|
||||||
// unambiguous, this must be the *only* implementation for an empty array. This
|
// unambiguous, this must be the *only* implementation for an empty array.
|
||||||
// avoids `NO_PARAMS` being a necessary part of the API.
|
|
||||||
//
|
//
|
||||||
// This sadly prevents `impl<T: ToSql, const N: usize> Params for [T; N]`, which
|
// This sadly prevents `impl<T: ToSql, const N: usize> Params for [T; N]`, which
|
||||||
// forces people to use `params![...]` or `rusqlite::params_from_iter` for long
|
// forces people to use `params![...]` or `rusqlite::params_from_iter` for long
|
||||||
@ -357,7 +354,7 @@ impl_for_array_ref!(
|
|||||||
/// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
|
/// fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
|
||||||
/// assert_eq!(ids.len(), 3, "Unrealistic sample code");
|
/// assert_eq!(ids.len(), 3, "Unrealistic sample code");
|
||||||
///
|
///
|
||||||
/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?;
|
/// let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?1, ?2, ?3)")?;
|
||||||
/// let _rows = stmt.query(params_from_iter(ids.iter()))?;
|
/// let _rows = stmt.query(params_from_iter(ids.iter()))?;
|
||||||
///
|
///
|
||||||
/// // use _rows...
|
/// // use _rows...
|
||||||
|
@ -211,7 +211,7 @@ impl Connection {
|
|||||||
/// (e.g. `integrity_check`).
|
/// (e.g. `integrity_check`).
|
||||||
///
|
///
|
||||||
/// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
|
/// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
|
||||||
/// `SELECT * FROM pragma_table_info(?);`
|
/// `SELECT * FROM pragma_table_info(?1);`
|
||||||
pub fn pragma<F, V>(
|
pub fn pragma<F, V>(
|
||||||
&self,
|
&self,
|
||||||
schema_name: Option<DatabaseName<'_>>,
|
schema_name: Option<DatabaseName<'_>>,
|
||||||
@ -303,15 +303,15 @@ fn is_identifier(s: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_identifier_start(c: char) -> bool {
|
fn is_identifier_start(c: char) -> bool {
|
||||||
('A'..='Z').contains(&c) || c == '_' || ('a'..='z').contains(&c) || c > '\x7F'
|
c.is_ascii_uppercase() || c == '_' || c.is_ascii_lowercase() || c > '\x7F'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_identifier_continue(c: char) -> bool {
|
fn is_identifier_continue(c: char) -> bool {
|
||||||
c == '$'
|
c == '$'
|
||||||
|| ('0'..='9').contains(&c)
|
|| c.is_ascii_digit()
|
||||||
|| ('A'..='Z').contains(&c)
|
|| c.is_ascii_uppercase()
|
||||||
|| c == '_'
|
|| c == '_'
|
||||||
|| ('a'..='z').contains(&c)
|
|| c.is_ascii_lowercase()
|
||||||
|| c > '\x7F'
|
|| c > '\x7F'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,10 +333,7 @@ mod test {
|
|||||||
#[cfg(feature = "modern_sqlite")]
|
#[cfg(feature = "modern_sqlite")]
|
||||||
fn pragma_func_query_value() -> Result<()> {
|
fn pragma_func_query_value() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let user_version: i32 =
|
let user_version: i32 = db.one_column("SELECT user_version FROM pragma_user_version")?;
|
||||||
db.query_row("SELECT user_version FROM pragma_user_version", [], |row| {
|
|
||||||
row.get(0)
|
|
||||||
})?;
|
|
||||||
assert_eq!(0, user_version);
|
assert_eq!(0, user_version);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -382,7 +379,7 @@ mod test {
|
|||||||
#[cfg(feature = "modern_sqlite")]
|
#[cfg(feature = "modern_sqlite")]
|
||||||
fn pragma_func() -> Result<()> {
|
fn pragma_func() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)")?;
|
let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?1)")?;
|
||||||
let mut columns = Vec::new();
|
let mut columns = Vec::new();
|
||||||
let mut rows = table_info.query(["sqlite_master"])?;
|
let mut rows = table_info.query(["sqlite_master"])?;
|
||||||
|
|
||||||
@ -408,18 +405,17 @@ mod test {
|
|||||||
db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get(0))?;
|
db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get(0))?;
|
||||||
assert!(
|
assert!(
|
||||||
journal_mode == "off" || journal_mode == "memory",
|
journal_mode == "off" || journal_mode == "memory",
|
||||||
"mode: {:?}",
|
"mode: {journal_mode:?}"
|
||||||
journal_mode,
|
|
||||||
);
|
);
|
||||||
// Sanity checks to ensure the move to a generic `ToSql` wasn't breaking
|
// Sanity checks to ensure the move to a generic `ToSql` wasn't breaking
|
||||||
let mode =
|
let mode =
|
||||||
db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get::<_, String>(0))?;
|
db.pragma_update_and_check(None, "journal_mode", "OFF", |row| row.get::<_, String>(0))?;
|
||||||
assert!(mode == "off" || mode == "memory", "mode: {:?}", mode);
|
assert!(mode == "off" || mode == "memory", "mode: {mode:?}");
|
||||||
|
|
||||||
let param: &dyn crate::ToSql = &"OFF";
|
let param: &dyn crate::ToSql = &"OFF";
|
||||||
let mode =
|
let mode =
|
||||||
db.pragma_update_and_check(None, "journal_mode", param, |row| row.get::<_, String>(0))?;
|
db.pragma_update_and_check(None, "journal_mode", param, |row| row.get::<_, String>(0))?;
|
||||||
assert!(mode == "off" || mode == "memory", "mode: {:?}", mode);
|
assert!(mode == "off" || mode == "memory", "mode: {mode:?}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +169,10 @@ impl RawStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clear_bindings(&self) -> c_int {
|
pub fn clear_bindings(&self) {
|
||||||
unsafe { ffi::sqlite3_clear_bindings(self.ptr) }
|
unsafe {
|
||||||
|
ffi::sqlite3_clear_bindings(self.ptr);
|
||||||
|
} // rc is always SQLITE_OK
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -195,7 +197,6 @@ impl RawStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// does not work for PRAGMA
|
// does not work for PRAGMA
|
||||||
#[cfg(feature = "extra_check")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn readonly(&self) -> bool {
|
pub fn readonly(&self) -> bool {
|
||||||
unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
|
unsafe { ffi::sqlite3_stmt_readonly(self.ptr) != 0 }
|
||||||
|
57
src/row.rs
57
src/row.rs
@ -257,6 +257,7 @@ impl<'stmt> Row<'stmt> {
|
|||||||
/// * If the underlying SQLite integral value is outside the range
|
/// * If the underlying SQLite integral value is outside the range
|
||||||
/// representable by `T`
|
/// representable by `T`
|
||||||
/// * If `idx` is outside the range of columns in the returned query
|
/// * If `idx` is outside the range of columns in the returned query
|
||||||
|
#[track_caller]
|
||||||
pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
pub fn get_unwrap<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
||||||
self.get(idx).unwrap()
|
self.get(idx).unwrap()
|
||||||
}
|
}
|
||||||
@ -277,6 +278,7 @@ impl<'stmt> Row<'stmt> {
|
|||||||
/// If the result type is i128 (which requires the `i128_blob` feature to be
|
/// 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
|
/// enabled), and the underlying SQLite column is a blob whose size is not
|
||||||
/// 16 bytes, `Error::InvalidColumnType` will also be returned.
|
/// 16 bytes, `Error::InvalidColumnType` will also be returned.
|
||||||
|
#[track_caller]
|
||||||
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
||||||
let idx = idx.idx(self.stmt)?;
|
let idx = idx.idx(self.stmt)?;
|
||||||
let value = self.stmt.value_ref(idx);
|
let value = self.stmt.value_ref(idx);
|
||||||
@ -335,23 +337,10 @@ impl<'stmt> Row<'stmt> {
|
|||||||
///
|
///
|
||||||
/// * If `idx` is outside the range of columns in the returned query.
|
/// * If `idx` is outside the range of columns in the returned query.
|
||||||
/// * If `idx` is not a valid column name for this row.
|
/// * If `idx` is not a valid column name for this row.
|
||||||
|
#[track_caller]
|
||||||
pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
pub fn get_ref_unwrap<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
||||||
self.get_ref(idx).unwrap()
|
self.get_ref(idx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renamed to [`get_ref`](Row::get_ref).
|
|
||||||
#[deprecated = "Use [`get_ref`](Row::get_ref) instead."]
|
|
||||||
#[inline]
|
|
||||||
pub fn get_raw_checked<I: RowIndex>(&self, idx: I) -> Result<ValueRef<'_>> {
|
|
||||||
self.get_ref(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renamed to [`get_ref_unwrap`](Row::get_ref_unwrap).
|
|
||||||
#[deprecated = "Use [`get_ref_unwrap`](Row::get_ref_unwrap) instead."]
|
|
||||||
#[inline]
|
|
||||||
pub fn get_raw<I: RowIndex>(&self, idx: I) -> ValueRef<'_> {
|
|
||||||
self.get_ref_unwrap(idx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
|
impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
|
||||||
@ -360,6 +349,46 @@ impl<'stmt> AsRef<Statement<'stmt>> for Row<'stmt> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Debug `Row` like an ordered `Map<Result<&str>, Result<(Type, ValueRef)>>`
|
||||||
|
/// with column name as key except that for `Type::Blob` only its size is
|
||||||
|
/// printed (not its content).
|
||||||
|
impl<'stmt> std::fmt::Debug for Row<'stmt> {
|
||||||
|
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);
|
||||||
|
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 {
|
mod sealed {
|
||||||
/// This trait exists just to ensure that the only impls of `trait Params`
|
/// This trait exists just to ensure that the only impls of `trait Params`
|
||||||
/// that are allowed are ones in this crate.
|
/// that are allowed are ones in this crate.
|
||||||
|
162
src/serialize.rs
Normal file
162
src/serialize.rs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
//! Serialize a database.
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::error::error_from_handle;
|
||||||
|
use crate::ffi;
|
||||||
|
use crate::{Connection, DatabaseName, Result};
|
||||||
|
|
||||||
|
/// Shared (SQLITE_SERIALIZE_NOCOPY) serialized database
|
||||||
|
pub struct SharedData<'conn> {
|
||||||
|
phantom: PhantomData<&'conn Connection>,
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
sz: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Owned serialized database
|
||||||
|
pub struct OwnedData {
|
||||||
|
ptr: NonNull<u8>,
|
||||||
|
sz: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnedData {
|
||||||
|
/// SAFETY: Caller must be certain that `ptr` is allocated by
|
||||||
|
/// `sqlite3_malloc`.
|
||||||
|
pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, 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<'conn> Deref for Data<'conn> {
|
||||||
|
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<'conn>(&'conn self, schema: DatabaseName<'_>) -> Result<Data<'conn>> {
|
||||||
|
let schema = schema.as_cstring()?;
|
||||||
|
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 a database.
|
||||||
|
pub fn deserialize(
|
||||||
|
&mut self,
|
||||||
|
schema: DatabaseName<'_>,
|
||||||
|
data: OwnedData,
|
||||||
|
read_only: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let schema = schema.as_cstring()?;
|
||||||
|
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
|
||||||
|
};
|
||||||
|
let rc = unsafe {
|
||||||
|
ffi::sqlite3_deserialize(self.handle(), schema.as_ptr(), data, sz, sz, flags)
|
||||||
|
};
|
||||||
|
if rc != ffi::SQLITE_OK {
|
||||||
|
// TODO sqlite3_free(data) ?
|
||||||
|
return Err(unsafe { error_from_handle(self.handle(), rc) });
|
||||||
|
}
|
||||||
|
/* TODO
|
||||||
|
if let Some(mxSize) = mxSize {
|
||||||
|
unsafe {
|
||||||
|
ffi::sqlite3_file_control(
|
||||||
|
self.handle(),
|
||||||
|
schema.as_ptr(),
|
||||||
|
ffi::SQLITE_FCNTL_SIZE_LIMIT,
|
||||||
|
&mut mxSize,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}*/
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::{Connection, DatabaseName, Result};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||||
|
let data = db.serialize(DatabaseName::Main)?;
|
||||||
|
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||||
|
assert!(data.sz > 0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() -> Result<()> {
|
||||||
|
let src = Connection::open_in_memory()?;
|
||||||
|
src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||||
|
let data = src.serialize(DatabaseName::Main)?;
|
||||||
|
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||||
|
|
||||||
|
let mut dst = Connection::open_in_memory()?;
|
||||||
|
dst.deserialize(DatabaseName::Main, data, false)?;
|
||||||
|
dst.execute("DELETE FROM x", [])?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -75,13 +75,10 @@ impl Session<'_> {
|
|||||||
let c_slice = CStr::from_ptr(tbl_str).to_bytes();
|
let c_slice = CStr::from_ptr(tbl_str).to_bytes();
|
||||||
str::from_utf8(c_slice)
|
str::from_utf8(c_slice)
|
||||||
};
|
};
|
||||||
if let Ok(true) =
|
c_int::from(
|
||||||
catch_unwind(|| (*boxed_filter)(tbl_name.expect("non-utf8 table name")))
|
catch_unwind(|| (*boxed_filter)(tbl_name.expect("non-utf8 table name")))
|
||||||
{
|
.unwrap_or_default(),
|
||||||
1
|
)
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match filter {
|
match filter {
|
||||||
@ -193,7 +190,7 @@ impl Session<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_enabled(&mut self, enabled: bool) {
|
pub fn set_enabled(&mut self, enabled: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3session_enable(self.s, if enabled { 1 } else { 0 });
|
ffi::sqlite3session_enable(self.s, c_int::from(enabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +204,7 @@ impl Session<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_indirect(&mut self, indirect: bool) {
|
pub fn set_indirect(&mut self, indirect: bool) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::sqlite3session_indirect(self.s, if indirect { 1 } else { 0 });
|
ffi::sqlite3session_indirect(self.s, c_int::from(indirect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -708,13 +705,9 @@ where
|
|||||||
str::from_utf8(c_slice)
|
str::from_utf8(c_slice)
|
||||||
};
|
};
|
||||||
match *tuple {
|
match *tuple {
|
||||||
(Some(ref filter), _) => {
|
(Some(ref filter), _) => c_int::from(
|
||||||
if let Ok(true) = catch_unwind(|| filter(tbl_name.expect("illegal table name"))) {
|
catch_unwind(|| filter(tbl_name.expect("illegal table name"))).unwrap_or_default(),
|
||||||
1
|
),
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -786,7 +779,7 @@ mod test {
|
|||||||
assert!(session.is_empty());
|
assert!(session.is_empty());
|
||||||
|
|
||||||
session.attach(None)?;
|
session.attach(None)?;
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
|
||||||
|
|
||||||
session.changeset()
|
session.changeset()
|
||||||
}
|
}
|
||||||
@ -799,7 +792,7 @@ mod test {
|
|||||||
assert!(session.is_empty());
|
assert!(session.is_empty());
|
||||||
|
|
||||||
session.attach(None)?;
|
session.attach(None)?;
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
|
||||||
|
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
session.changeset_strm(&mut output)?;
|
session.changeset_strm(&mut output)?;
|
||||||
@ -859,7 +852,7 @@ mod test {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert!(!CALLED.load(Ordering::Relaxed));
|
assert!(!CALLED.load(Ordering::Relaxed));
|
||||||
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| {
|
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| {
|
||||||
row.get::<_, i32>(0)
|
row.get::<_, i32>(0)
|
||||||
})?;
|
})?;
|
||||||
assert_eq!(1, check);
|
assert_eq!(1, check);
|
||||||
@ -894,7 +887,7 @@ mod test {
|
|||||||
|_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT,
|
|_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?", ["bar"], |row| {
|
let check = db.query_row("SELECT 1 FROM foo WHERE t = ?1", ["bar"], |row| {
|
||||||
row.get::<_, i32>(0)
|
row.get::<_, i32>(0)
|
||||||
})?;
|
})?;
|
||||||
assert_eq!(1, check);
|
assert_eq!(1, check);
|
||||||
@ -910,7 +903,7 @@ mod test {
|
|||||||
assert!(session.is_empty());
|
assert!(session.is_empty());
|
||||||
|
|
||||||
session.attach(None)?;
|
session.attach(None)?;
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?);", ["bar"])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1);", ["bar"])?;
|
||||||
|
|
||||||
assert!(!session.is_empty());
|
assert!(!session.is_empty());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
293
src/statement.rs
293
src/statement.rs
@ -33,7 +33,7 @@ impl Statement<'_> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result, params};
|
/// # use rusqlite::{Connection, Result, params};
|
||||||
/// fn update_rows(conn: &Connection) -> Result<()> {
|
/// fn update_rows(conn: &Connection) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("UPDATE foo SET bar = ? WHERE qux = ?")?;
|
/// 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
|
/// // For a single parameter, or a parameter where all the values have
|
||||||
/// // the same type, just passing an array is simplest.
|
/// // the same type, just passing an array is simplest.
|
||||||
/// stmt.execute([2i32])?;
|
/// stmt.execute([2i32])?;
|
||||||
@ -58,7 +58,7 @@ impl Statement<'_> {
|
|||||||
/// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> {
|
/// fn store_file(conn: &Connection, path: &str, data: &[u8]) -> Result<()> {
|
||||||
/// # // no need to do it for real.
|
/// # // no need to do it for real.
|
||||||
/// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] }
|
/// # fn sha256(_: &[u8]) -> [u8; 32] { [0; 32] }
|
||||||
/// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?, ?, ?)";
|
/// let query = "INSERT OR REPLACE INTO files(path, hash, data) VALUES (?1, ?2, ?3)";
|
||||||
/// let mut stmt = conn.prepare_cached(query)?;
|
/// let mut stmt = conn.prepare_cached(query)?;
|
||||||
/// let hash: [u8; 32] = sha256(data);
|
/// let hash: [u8; 32] = sha256(data);
|
||||||
/// // The easiest way to pass positional parameters of have several
|
/// // The easiest way to pass positional parameters of have several
|
||||||
@ -114,31 +114,6 @@ impl Statement<'_> {
|
|||||||
self.execute_with_bound_parameters()
|
self.execute_with_bound_parameters()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement with named parameter(s).
|
|
||||||
///
|
|
||||||
/// Note: This function is deprecated in favor of [`Statement::execute`],
|
|
||||||
/// which can now take named parameters directly.
|
|
||||||
///
|
|
||||||
/// If any parameters that were in the prepared statement are not included
|
|
||||||
/// in `params`, they will continue to use the most-recently bound value
|
|
||||||
/// from a previous call to `execute_named`, or `NULL` if they have never
|
|
||||||
/// been bound.
|
|
||||||
///
|
|
||||||
/// On success, returns the number of rows that were changed or inserted or
|
|
||||||
/// deleted (via `sqlite3_changes`).
|
|
||||||
///
|
|
||||||
/// # 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.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated = "You can use `execute` with named params now."]
|
|
||||||
#[inline]
|
|
||||||
pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
|
|
||||||
self.execute(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute an INSERT and return the ROWID.
|
/// Execute an INSERT and return the ROWID.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
@ -193,7 +168,7 @@ impl Statement<'_> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// fn query(conn: &Connection, name: &str) -> Result<()> {
|
/// fn query(conn: &Connection, name: &str) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
|
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
|
||||||
/// let mut rows = stmt.query(rusqlite::params![name])?;
|
/// let mut rows = stmt.query(rusqlite::params![name])?;
|
||||||
/// while let Some(row) = rows.next()? {
|
/// while let Some(row) = rows.next()? {
|
||||||
/// // ...
|
/// // ...
|
||||||
@ -207,7 +182,7 @@ impl Statement<'_> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// fn query(conn: &Connection, name: &str) -> Result<()> {
|
/// fn query(conn: &Connection, name: &str) -> Result<()> {
|
||||||
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
|
/// let mut stmt = conn.prepare("SELECT * FROM test where name = ?1")?;
|
||||||
/// let mut rows = stmt.query([name])?;
|
/// let mut rows = stmt.query([name])?;
|
||||||
/// while let Some(row) = rows.next()? {
|
/// while let Some(row) = rows.next()? {
|
||||||
/// // ...
|
/// // ...
|
||||||
@ -254,26 +229,6 @@ impl Statement<'_> {
|
|||||||
Ok(Rows::new(self))
|
Ok(Rows::new(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement with named parameter(s), returning a
|
|
||||||
/// handle for the resulting rows.
|
|
||||||
///
|
|
||||||
/// Note: This function is deprecated in favor of [`Statement::query`],
|
|
||||||
/// which can now take named parameters directly.
|
|
||||||
///
|
|
||||||
/// If any parameters that were in the prepared statement are not included
|
|
||||||
/// in `params`, they will continue to use the most-recently bound value
|
|
||||||
/// from a previous call to `query_named`, or `NULL` if they have never been
|
|
||||||
/// bound.
|
|
||||||
///
|
|
||||||
/// # Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if binding parameters fails.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated = "You can use `query` with named params now."]
|
|
||||||
pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'_>> {
|
|
||||||
self.query(params)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes the prepared statement and maps a function over the resulting
|
/// Executes the prepared statement and maps a function over the resulting
|
||||||
/// rows, returning an iterator over the mapped function results.
|
/// rows, returning an iterator over the mapped function results.
|
||||||
///
|
///
|
||||||
@ -328,37 +283,6 @@ impl Statement<'_> {
|
|||||||
self.query(params).map(|rows| rows.mapped(f))
|
self.query(params).map(|rows| rows.mapped(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement with named parameter(s), returning an
|
|
||||||
/// iterator over the result of calling the mapping function over the
|
|
||||||
/// query's rows.
|
|
||||||
///
|
|
||||||
/// Note: This function is deprecated in favor of [`Statement::query_map`],
|
|
||||||
/// which can now take named parameters directly.
|
|
||||||
///
|
|
||||||
/// If any parameters that were in the prepared statement
|
|
||||||
/// are not included in `params`, they will continue to use the
|
|
||||||
/// most-recently bound value from a previous call to `query_named`,
|
|
||||||
/// or `NULL` if they have never been bound.
|
|
||||||
///
|
|
||||||
/// `f` is used to transform the _streaming_ iterator into a _standard_
|
|
||||||
/// iterator.
|
|
||||||
///
|
|
||||||
/// ## Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if binding parameters fails.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated = "You can use `query_map` with named params now."]
|
|
||||||
pub fn query_map_named<T, F>(
|
|
||||||
&mut self,
|
|
||||||
params: &[(&str, &dyn ToSql)],
|
|
||||||
f: F,
|
|
||||||
) -> Result<MappedRows<'_, F>>
|
|
||||||
where
|
|
||||||
F: FnMut(&Row<'_>) -> Result<T>,
|
|
||||||
{
|
|
||||||
self.query_map(params, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executes the prepared statement and maps a function over the resulting
|
/// Executes the prepared statement and maps a function over the resulting
|
||||||
/// rows, where the function returns a `Result` with `Error` type
|
/// rows, where the function returns a `Result` with `Error` type
|
||||||
/// implementing `std::convert::From<Error>` (so errors can be unified).
|
/// implementing `std::convert::From<Error>` (so errors can be unified).
|
||||||
@ -398,7 +322,7 @@ impl Statement<'_> {
|
|||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use rusqlite::{Connection, Result};
|
/// # use rusqlite::{Connection, Result};
|
||||||
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||||
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?")?;
|
/// 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 rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?;
|
||||||
///
|
///
|
||||||
/// let mut persons = Vec::new();
|
/// let mut persons = Vec::new();
|
||||||
@ -423,36 +347,6 @@ impl Statement<'_> {
|
|||||||
self.query(params).map(|rows| rows.and_then(f))
|
self.query(params).map(|rows| rows.and_then(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the prepared statement with named parameter(s), returning an
|
|
||||||
/// iterator over the result of calling the mapping function over the
|
|
||||||
/// query's rows.
|
|
||||||
///
|
|
||||||
/// Note: This function is deprecated in favor of
|
|
||||||
/// [`Statement::query_and_then`], which can now take named parameters
|
|
||||||
/// directly.
|
|
||||||
///
|
|
||||||
/// If any parameters that were in the prepared statement are not included
|
|
||||||
/// in `params`, they will continue to use the most-recently bound value
|
|
||||||
/// from a previous call to `query_named`, or `NULL` if they have never been
|
|
||||||
/// bound.
|
|
||||||
///
|
|
||||||
/// ## Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if binding parameters fails.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated = "You can use `query_and_then` with named params now."]
|
|
||||||
pub fn query_and_then_named<T, E, F>(
|
|
||||||
&mut self,
|
|
||||||
params: &[(&str, &dyn ToSql)],
|
|
||||||
f: F,
|
|
||||||
) -> Result<AndThenRows<'_, F>>
|
|
||||||
where
|
|
||||||
E: From<Error>,
|
|
||||||
F: FnMut(&Row<'_>) -> Result<T, E>,
|
|
||||||
{
|
|
||||||
self.query_and_then(params, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return `true` if a query in the SQL statement it executes returns one
|
/// 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.
|
/// or more rows and `false` if the SQL returns an empty set.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -487,35 +381,6 @@ impl Statement<'_> {
|
|||||||
rows.get_expected_row().and_then(f)
|
rows.get_expected_row().and_then(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method to execute a query with named parameter(s) that is
|
|
||||||
/// expected to return a single row.
|
|
||||||
///
|
|
||||||
/// Note: This function is deprecated in favor of
|
|
||||||
/// [`Statement::query_and_then`], which can now take named parameters
|
|
||||||
/// directly.
|
|
||||||
///
|
|
||||||
/// 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<Option<T>>` (requires that the trait
|
|
||||||
/// `rusqlite::OptionalExtension` is imported).
|
|
||||||
///
|
|
||||||
/// # Failure
|
|
||||||
///
|
|
||||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string
|
|
||||||
/// or if the underlying SQLite call fails.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[deprecated = "You can use `query_row` with named params now."]
|
|
||||||
pub fn query_row_named<T, F>(&mut self, params: &[(&str, &dyn ToSql)], f: F) -> Result<T>
|
|
||||||
where
|
|
||||||
F: FnOnce(&Row<'_>) -> Result<T>,
|
|
||||||
{
|
|
||||||
self.query_row(params, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes the statement.
|
/// Consumes the statement.
|
||||||
///
|
///
|
||||||
/// Functionally equivalent to the `Drop` implementation, but allows
|
/// Functionally equivalent to the `Drop` implementation, but allows
|
||||||
@ -844,6 +709,12 @@ impl Statement<'_> {
|
|||||||
self.stmt.is_explain()
|
self.stmt.is_explain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the statement is read only.
|
||||||
|
#[inline]
|
||||||
|
pub fn readonly(&self) -> bool {
|
||||||
|
self.stmt.readonly()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "extra_check")]
|
#[cfg(feature = "extra_check")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn check_no_tail(&self) -> Result<()> {
|
pub(crate) fn check_no_tail(&self) -> Result<()> {
|
||||||
@ -870,6 +741,11 @@ impl Statement<'_> {
|
|||||||
mem::swap(&mut stmt, &mut self.stmt);
|
mem::swap(&mut stmt, &mut self.stmt);
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset all bindings
|
||||||
|
pub fn clear_bindings(&mut self) {
|
||||||
|
self.stmt.clear_bindings()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Statement<'_> {
|
impl fmt::Debug for Statement<'_> {
|
||||||
@ -1009,13 +885,12 @@ mod test {
|
|||||||
use crate::{params_from_iter, Connection, Error, Result};
|
use crate::{params_from_iter, Connection, Error, Result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_execute_named() -> Result<()> {
|
fn test_execute_named() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?,
|
db.execute("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])?,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1032,7 +907,7 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
6i32,
|
6i32,
|
||||||
db.query_row_named::<i32, _>(
|
db.query_row::<i32, _, _>(
|
||||||
"SELECT SUM(x) FROM foo WHERE x > :x",
|
"SELECT SUM(x) FROM foo WHERE x > :x",
|
||||||
&[(":x", &0i32)],
|
&[(":x", &0i32)],
|
||||||
|r| r.get(0)
|
|r| r.get(0)
|
||||||
@ -1050,7 +925,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_stmt_execute_named() -> Result<()> {
|
fn test_stmt_execute_named() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
|
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
|
||||||
@ -1058,13 +932,9 @@ mod test {
|
|||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?;
|
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")?;
|
||||||
stmt.execute_named(&[(":name", &"one")])?;
|
stmt.execute(&[(":name", &"one")])?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = :name")?;
|
let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = :name")?;
|
||||||
assert_eq!(
|
|
||||||
1i32,
|
|
||||||
stmt.query_row_named::<i32, _>(&[(":name", &"one")], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
1i32,
|
1i32,
|
||||||
stmt.query_row::<i32, _, _>(&[(":name", "one")], |r| r.get(0))?
|
stmt.query_row::<i32, _, _>(&[(":name", "one")], |r| r.get(0))?
|
||||||
@ -1073,7 +943,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_query_named() -> Result<()> {
|
fn test_query_named() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
@ -1083,24 +952,13 @@ mod test {
|
|||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
|
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
|
||||||
// legacy `_named` api
|
let mut rows = stmt.query(&[(":name", "one")])?;
|
||||||
{
|
let id: Result<i32> = rows.next()?.unwrap().get(0);
|
||||||
let mut rows = stmt.query_named(&[(":name", &"one")])?;
|
assert_eq!(Ok(1), id);
|
||||||
let id: Result<i32> = rows.next()?.unwrap().get(0);
|
|
||||||
assert_eq!(Ok(1), id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// plain api
|
|
||||||
{
|
|
||||||
let mut rows = stmt.query(&[(":name", "one")])?;
|
|
||||||
let id: Result<i32> = rows.next()?.unwrap().get(0);
|
|
||||||
assert_eq!(Ok(1), id);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_query_map_named() -> Result<()> {
|
fn test_query_map_named() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let sql = r#"
|
let sql = r#"
|
||||||
@ -1110,61 +968,13 @@ mod test {
|
|||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
|
let mut stmt = db.prepare("SELECT id FROM test where name = :name")?;
|
||||||
// legacy `_named` api
|
let mut rows = stmt.query_map(&[(":name", "one")], |row| {
|
||||||
{
|
let id: Result<i32> = row.get(0);
|
||||||
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
|
id.map(|i| 2 * i)
|
||||||
let id: Result<i32> = row.get(0);
|
|
||||||
id.map(|i| 2 * i)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let doubled_id: i32 = rows.next().unwrap()?;
|
|
||||||
assert_eq!(2, doubled_id);
|
|
||||||
}
|
|
||||||
// plain api
|
|
||||||
{
|
|
||||||
let mut rows = stmt.query_map(&[(":name", "one")], |row| {
|
|
||||||
let id: Result<i32> = row.get(0);
|
|
||||||
id.map(|i| 2 * i)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let doubled_id: i32 = rows.next().unwrap()?;
|
|
||||||
assert_eq!(2, doubled_id);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_query_and_then_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");
|
|
||||||
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_named(&[(":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()?;
|
let doubled_id: i32 = rows.next().unwrap()?;
|
||||||
assert_eq!(1, doubled_id);
|
assert_eq!(2, doubled_id);
|
||||||
|
|
||||||
// second row should be Err
|
|
||||||
#[allow(clippy::match_wild_err_arm)]
|
|
||||||
match rows.next().unwrap() {
|
|
||||||
Ok(_) => panic!("invalid Ok"),
|
|
||||||
Err(Error::SqliteSingleThreadedMode) => (),
|
|
||||||
Err(_) => panic!("invalid Err"),
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1203,17 +1013,15 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(deprecated)]
|
|
||||||
fn test_unbound_parameters_are_null() -> Result<()> {
|
fn test_unbound_parameters_are_null() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?;
|
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")?;
|
||||||
stmt.execute_named(&[(":x", &"one")])?;
|
stmt.execute(&[(":x", &"one")])?;
|
||||||
|
|
||||||
let result: Option<String> =
|
let result: Option<String> = db.one_column("SELECT y FROM test WHERE x = 'one'")?;
|
||||||
db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?;
|
|
||||||
assert!(result.is_none());
|
assert!(result.is_none());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1259,8 +1067,7 @@ mod test {
|
|||||||
stmt.execute(&[(":x", "one")])?;
|
stmt.execute(&[(":x", "one")])?;
|
||||||
stmt.execute(&[(":y", "two")])?;
|
stmt.execute(&[(":y", "two")])?;
|
||||||
|
|
||||||
let result: String =
|
let result: String = db.one_column("SELECT x FROM test WHERE y = 'two'")?;
|
||||||
db.query_row("SELECT x FROM test WHERE y = 'two'", [], |row| row.get(0))?;
|
|
||||||
assert_eq!(result, "one");
|
assert_eq!(result, "one");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1269,17 +1076,17 @@ mod test {
|
|||||||
fn test_insert() -> Result<()> {
|
fn test_insert() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
|
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
|
||||||
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")?;
|
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?1)")?;
|
||||||
assert_eq!(stmt.insert([1i32])?, 1);
|
assert_eq!(stmt.insert([1i32])?, 1);
|
||||||
assert_eq!(stmt.insert([2i32])?, 2);
|
assert_eq!(stmt.insert([2i32])?, 2);
|
||||||
match stmt.insert([1i32]).unwrap_err() {
|
match stmt.insert([1i32]).unwrap_err() {
|
||||||
Error::StatementChangedRows(0) => (),
|
Error::StatementChangedRows(0) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")?;
|
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")?;
|
||||||
match multi.insert([]).unwrap_err() {
|
match multi.insert([]).unwrap_err() {
|
||||||
Error::StatementChangedRows(2) => (),
|
Error::StatementChangedRows(2) => (),
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1309,7 +1116,7 @@ mod test {
|
|||||||
INSERT INTO foo VALUES(2);
|
INSERT INTO foo VALUES(2);
|
||||||
END;";
|
END;";
|
||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?;
|
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?1")?;
|
||||||
assert!(stmt.exists([1i32])?);
|
assert!(stmt.exists([1i32])?);
|
||||||
assert!(stmt.exists([2i32])?);
|
assert!(stmt.exists([2i32])?);
|
||||||
assert!(!stmt.exists([0i32])?);
|
assert!(!stmt.exists([0i32])?);
|
||||||
@ -1318,18 +1125,18 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_tuple_params() -> Result<()> {
|
fn test_tuple_params() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let s = db.query_row("SELECT printf('[%s]', ?)", ("abc",), |r| {
|
let s = db.query_row("SELECT printf('[%s]', ?1)", ("abc",), |r| {
|
||||||
r.get::<_, String>(0)
|
r.get::<_, String>(0)
|
||||||
})?;
|
})?;
|
||||||
assert_eq!(s, "[abc]");
|
assert_eq!(s, "[abc]");
|
||||||
let s = db.query_row(
|
let s = db.query_row(
|
||||||
"SELECT printf('%d %s %d', ?, ?, ?)",
|
"SELECT printf('%d %s %d', ?1, ?2, ?3)",
|
||||||
(1i32, "abc", 2i32),
|
(1i32, "abc", 2i32),
|
||||||
|r| r.get::<_, String>(0),
|
|r| r.get::<_, String>(0),
|
||||||
)?;
|
)?;
|
||||||
assert_eq!(s, "1 abc 2");
|
assert_eq!(s, "1 abc 2");
|
||||||
let s = db.query_row(
|
let s = db.query_row(
|
||||||
"SELECT printf('%d %s %d %d', ?, ?, ?, ?)",
|
"SELECT printf('%d %s %d %d', ?1, ?2, ?3, ?4)",
|
||||||
(1, "abc", 2i32, 4i64),
|
(1, "abc", 2i32, 4i64),
|
||||||
|r| r.get::<_, String>(0),
|
|r| r.get::<_, String>(0),
|
||||||
)?;
|
)?;
|
||||||
@ -1341,10 +1148,10 @@ mod test {
|
|||||||
);
|
);
|
||||||
let query = "SELECT printf(
|
let query = "SELECT printf(
|
||||||
'%d %s | %d %s | %d %s | %d %s || %d %s | %d %s | %d %s | %d %s',
|
'%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))?;
|
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");
|
assert_eq!(s, "0 a | 1 b | 2 c | 3 d || 4 e | 5 f | 6 g | 7 h");
|
||||||
@ -1360,7 +1167,7 @@ mod test {
|
|||||||
INSERT INTO foo VALUES(2, 4);
|
INSERT INTO foo VALUES(2, 4);
|
||||||
END;";
|
END;";
|
||||||
db.execute_batch(sql)?;
|
db.execute_batch(sql)?;
|
||||||
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?;
|
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?1")?;
|
||||||
let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0));
|
let y: Result<i64> = stmt.query_row([1i32], |r| r.get(0));
|
||||||
assert_eq!(3i64, y?);
|
assert_eq!(3i64, y?);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1397,7 +1204,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_expanded_sql() -> Result<()> {
|
fn test_expanded_sql() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let stmt = db.prepare("SELECT ?")?;
|
let stmt = db.prepare("SELECT ?1")?;
|
||||||
stmt.bind_parameter(&1, 1)?;
|
stmt.bind_parameter(&1, 1)?;
|
||||||
assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql());
|
assert_eq!(Some("SELECT 1".to_owned()), stmt.expanded_sql());
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1499,8 +1306,8 @@ mod test {
|
|||||||
assert_eq!("UTF-16le", encoding);
|
assert_eq!("UTF-16le", encoding);
|
||||||
db.execute_batch("CREATE TABLE foo(x TEXT)")?;
|
db.execute_batch("CREATE TABLE foo(x TEXT)")?;
|
||||||
let expected = "テスト";
|
let expected = "テスト";
|
||||||
db.execute("INSERT INTO foo(x) VALUES (?)", [&expected])?;
|
db.execute("INSERT INTO foo(x) VALUES (?1)", [&expected])?;
|
||||||
let actual: String = db.query_row("SELECT x FROM foo", [], |row| row.get(0))?;
|
let actual: String = db.one_column("SELECT x FROM foo")?;
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1509,7 +1316,7 @@ mod test {
|
|||||||
fn test_nul_byte() -> Result<()> {
|
fn test_nul_byte() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
let expected = "a\x00b";
|
let expected = "a\x00b";
|
||||||
let actual: String = db.query_row("SELECT ?", [expected], |row| row.get(0))?;
|
let actual: String = db.query_row("SELECT ?1", [expected], |row| row.get(0))?;
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1523,6 +1330,14 @@ mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn readonly() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
let stmt = db.prepare("SELECT 1;")?;
|
||||||
|
assert!(stmt.readonly());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
#[cfg(feature = "modern_sqlite")] // SQLite >= 3.38.0
|
||||||
fn test_error_offset() -> Result<()> {
|
fn test_error_offset() -> Result<()> {
|
||||||
@ -1534,7 +1349,7 @@ mod test {
|
|||||||
assert_eq!(error.code, ErrorCode::Unknown);
|
assert_eq!(error.code, ErrorCode::Unknown);
|
||||||
assert_eq!(offset, 7);
|
assert_eq!(offset, 7);
|
||||||
}
|
}
|
||||||
err => panic!("Unexpected error {}", err),
|
err => panic!("Unexpected error {err}"),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -144,13 +144,13 @@ mod test {
|
|||||||
let mut db = Connection::open_in_memory()?;
|
let mut db = Connection::open_in_memory()?;
|
||||||
db.trace(Some(tracer));
|
db.trace(Some(tracer));
|
||||||
{
|
{
|
||||||
let _ = db.query_row("SELECT ?", [1i32], |_| Ok(()));
|
let _ = db.query_row("SELECT ?1", [1i32], |_| Ok(()));
|
||||||
let _ = db.query_row("SELECT ?", ["hello"], |_| Ok(()));
|
let _ = db.query_row("SELECT ?1", ["hello"], |_| Ok(()));
|
||||||
}
|
}
|
||||||
db.trace(None);
|
db.trace(None);
|
||||||
{
|
{
|
||||||
let _ = db.query_row("SELECT ?", [2i32], |_| Ok(()));
|
let _ = db.query_row("SELECT ?1", [2i32], |_| Ok(()));
|
||||||
let _ = db.query_row("SELECT ?", ["goodbye"], |_| Ok(()));
|
let _ = db.query_row("SELECT ?1", ["goodbye"], |_| Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let traced_stmts = TRACED_STMTS.lock().unwrap();
|
let traced_stmts = TRACED_STMTS.lock().unwrap();
|
||||||
|
@ -91,7 +91,6 @@ pub struct Transaction<'conn> {
|
|||||||
pub struct Savepoint<'conn> {
|
pub struct Savepoint<'conn> {
|
||||||
conn: &'conn Connection,
|
conn: &'conn Connection,
|
||||||
name: String,
|
name: String,
|
||||||
depth: u32,
|
|
||||||
drop_behavior: DropBehavior,
|
drop_behavior: DropBehavior,
|
||||||
committed: bool,
|
committed: bool,
|
||||||
}
|
}
|
||||||
@ -158,13 +157,13 @@ impl Transaction<'_> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
|
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth(self.conn, 1)
|
Savepoint::new_(self.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new savepoint with a custom savepoint name. See `savepoint()`.
|
/// Create a new savepoint with a custom savepoint name. See `savepoint()`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
|
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth_and_name(self.conn, 1, name)
|
Savepoint::with_name_(self.conn, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current setting for what happens to the transaction when it is
|
/// Get the current setting for what happens to the transaction when it is
|
||||||
@ -249,50 +248,44 @@ impl Drop for Transaction<'_> {
|
|||||||
|
|
||||||
impl Savepoint<'_> {
|
impl Savepoint<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_depth_and_name<T: Into<String>>(
|
fn with_name_<T: Into<String>>(conn: &Connection, name: T) -> Result<Savepoint<'_>> {
|
||||||
conn: &Connection,
|
|
||||||
depth: u32,
|
|
||||||
name: T,
|
|
||||||
) -> Result<Savepoint<'_>> {
|
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
conn.execute_batch(&format!("SAVEPOINT {name}"))
|
conn.execute_batch(&format!("SAVEPOINT {name}"))
|
||||||
.map(|_| Savepoint {
|
.map(|_| Savepoint {
|
||||||
conn,
|
conn,
|
||||||
name,
|
name,
|
||||||
depth,
|
|
||||||
drop_behavior: DropBehavior::Rollback,
|
drop_behavior: DropBehavior::Rollback,
|
||||||
committed: false,
|
committed: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint<'_>> {
|
fn new_(conn: &Connection) -> Result<Savepoint<'_>> {
|
||||||
let name = format!("_rusqlite_sp_{depth}");
|
Savepoint::with_name_(conn, "_rusqlite_sp")
|
||||||
Savepoint::with_depth_and_name(conn, depth, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin a new savepoint. Can be nested.
|
/// Begin a new savepoint. Can be nested.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(conn: &mut Connection) -> Result<Savepoint<'_>> {
|
pub fn new(conn: &mut Connection) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth(conn, 0)
|
Savepoint::new_(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin a new savepoint with a user-provided savepoint name.
|
/// Begin a new savepoint with a user-provided savepoint name.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_name<T: Into<String>>(conn: &mut Connection, name: T) -> Result<Savepoint<'_>> {
|
pub fn with_name<T: Into<String>>(conn: &mut Connection, name: T) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth_and_name(conn, 0, name)
|
Savepoint::with_name_(conn, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin a nested savepoint.
|
/// Begin a nested savepoint.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
|
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth(self.conn, self.depth + 1)
|
Savepoint::new_(self.conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Begin a nested savepoint with a user-provided savepoint name.
|
/// Begin a nested savepoint with a user-provided savepoint name.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
|
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
|
||||||
Savepoint::with_depth_and_name(self.conn, self.depth + 1, name)
|
Savepoint::with_name_(self.conn, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current setting for what happens to the savepoint when it is
|
/// Get the current setting for what happens to the savepoint when it is
|
||||||
@ -351,8 +344,10 @@ impl Savepoint<'_> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match self.drop_behavior() {
|
match self.drop_behavior() {
|
||||||
DropBehavior::Commit => self.commit_().or_else(|_| self.rollback()),
|
DropBehavior::Commit => self
|
||||||
DropBehavior::Rollback => self.rollback(),
|
.commit_()
|
||||||
|
.or_else(|_| self.rollback().and_then(|_| self.commit_())),
|
||||||
|
DropBehavior::Rollback => self.rollback().and_then(|_| self.commit_()),
|
||||||
DropBehavior::Ignore => Ok(()),
|
DropBehavior::Ignore => Ok(()),
|
||||||
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
|
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
|
||||||
}
|
}
|
||||||
@ -552,10 +547,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
let tx = db.transaction()?;
|
let tx = db.transaction()?;
|
||||||
assert_eq!(
|
assert_eq!(2i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?);
|
||||||
2i32,
|
|
||||||
tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -566,7 +558,7 @@ mod test {
|
|||||||
assert_eq!(e.code, crate::ErrorCode::Unknown);
|
assert_eq!(e.code, crate::ErrorCode::Unknown);
|
||||||
assert!(m.contains("transaction"));
|
assert!(m.contains("transaction"));
|
||||||
} else {
|
} else {
|
||||||
panic!("Unexpected error type: {:?}", e);
|
panic!("Unexpected error type: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,10 +583,7 @@ mod test {
|
|||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(2i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
|
||||||
2i32,
|
|
||||||
db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,10 +608,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
let tx = db.transaction()?;
|
let tx = db.transaction()?;
|
||||||
assert_eq!(
|
assert_eq!(6i32, tx.one_column::<i32>("SELECT SUM(x) FROM foo")?);
|
||||||
6i32,
|
|
||||||
tx.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -684,6 +670,40 @@ mod test {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_savepoint_names() -> Result<()> {
|
fn test_savepoint_names() -> Result<()> {
|
||||||
let mut db = checked_memory_handle()?;
|
let mut db = checked_memory_handle()?;
|
||||||
@ -727,11 +747,11 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn insert(x: i32, conn: &Connection) -> Result<usize> {
|
fn insert(x: i32, conn: &Connection) -> Result<usize> {
|
||||||
conn.execute("INSERT INTO foo VALUES(?)", [x])
|
conn.execute("INSERT INTO foo VALUES(?1)", [x])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> {
|
fn assert_current_sum(x: i32, conn: &Connection) -> Result<()> {
|
||||||
let i = conn.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?;
|
let i = conn.one_column::<i32>("SELECT SUM(x) FROM foo")?;
|
||||||
assert_eq!(x, i);
|
assert_eq!(x, i);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -175,12 +175,12 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_naive_date() -> Result<()> {
|
fn test_naive_date() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [date])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?;
|
||||||
|
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!("2016-02-23", s);
|
assert_eq!("2016-02-23", s);
|
||||||
let t: NaiveDate = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let t: NaiveDate = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(date, t);
|
assert_eq!(date, t);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -188,12 +188,12 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_naive_time() -> Result<()> {
|
fn test_naive_time() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let time = NaiveTime::from_hms(23, 56, 4);
|
let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap();
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [time])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
|
||||||
|
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!("23:56:04", s);
|
assert_eq!("23:56:04", s);
|
||||||
let v: NaiveTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let v: NaiveTime = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(time, v);
|
assert_eq!(time, v);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -201,19 +201,19 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_naive_date_time() -> Result<()> {
|
fn test_naive_date_time() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
|
||||||
let time = NaiveTime::from_hms(23, 56, 4);
|
let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap();
|
||||||
let dt = NaiveDateTime::new(date, time);
|
let dt = NaiveDateTime::new(date, time);
|
||||||
|
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [dt])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?;
|
||||||
|
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!("2016-02-23 23:56:04", s);
|
assert_eq!("2016-02-23 23:56:04", s);
|
||||||
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let v: NaiveDateTime = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(dt, v);
|
assert_eq!(dt, v);
|
||||||
|
|
||||||
db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS"
|
db.execute("UPDATE foo set b = datetime(t)", [])?; // "YYYY-MM-DD HH:MM:SS"
|
||||||
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
let hms: NaiveDateTime = db.one_column("SELECT b FROM foo")?;
|
||||||
assert_eq!(dt, hms);
|
assert_eq!(dt, hms);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -221,28 +221,26 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_date_time_utc() -> Result<()> {
|
fn test_date_time_utc() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
|
||||||
let time = NaiveTime::from_hms_milli(23, 56, 4, 789);
|
let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap();
|
||||||
let dt = NaiveDateTime::new(date, time);
|
let dt = NaiveDateTime::new(date, time);
|
||||||
let utc = Utc.from_utc_datetime(&dt);
|
let utc = Utc.from_utc_datetime(&dt);
|
||||||
|
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [utc])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [utc])?;
|
||||||
|
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!("2016-02-23 23:56:04.789+00:00", s);
|
assert_eq!("2016-02-23 23:56:04.789+00:00", s);
|
||||||
|
|
||||||
let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let v1: DateTime<Utc> = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(utc, v1);
|
assert_eq!(utc, v1);
|
||||||
|
|
||||||
let v2: DateTime<Utc> =
|
let v2: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789'")?;
|
||||||
db.query_row("SELECT '2016-02-23 23:56:04.789'", [], |r| r.get(0))?;
|
|
||||||
assert_eq!(utc, v2);
|
assert_eq!(utc, v2);
|
||||||
|
|
||||||
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", [], |r| r.get(0))?;
|
let v3: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04'")?;
|
||||||
assert_eq!(utc - Duration::milliseconds(789), v3);
|
assert_eq!(utc - Duration::milliseconds(789), v3);
|
||||||
|
|
||||||
let v4: DateTime<Utc> =
|
let v4: DateTime<Utc> = db.one_column("SELECT '2016-02-23 23:56:04.789+00:00'")?;
|
||||||
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", [], |r| r.get(0))?;
|
|
||||||
assert_eq!(utc, v4);
|
assert_eq!(utc, v4);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -250,18 +248,18 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_date_time_local() -> Result<()> {
|
fn test_date_time_local() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
let date = NaiveDate::from_ymd_opt(2016, 2, 23).unwrap();
|
||||||
let time = NaiveTime::from_hms_milli(23, 56, 4, 789);
|
let time = NaiveTime::from_hms_milli_opt(23, 56, 4, 789).unwrap();
|
||||||
let dt = NaiveDateTime::new(date, time);
|
let dt = NaiveDateTime::new(date, time);
|
||||||
let local = Local.from_local_datetime(&dt).single().unwrap();
|
let local = Local.from_local_datetime(&dt).single().unwrap();
|
||||||
|
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [local])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [local])?;
|
||||||
|
|
||||||
// Stored string should be in UTC
|
// Stored string should be in UTC
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert!(s.ends_with("+00:00"));
|
assert!(s.ends_with("+00:00"));
|
||||||
|
|
||||||
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let v: DateTime<Local> = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(local, v);
|
assert_eq!(local, v);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -271,13 +269,13 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap();
|
let time = DateTime::parse_from_rfc3339("2020-04-07T11:23:45+04:00").unwrap();
|
||||||
|
|
||||||
db.execute("INSERT INTO foo (t) VALUES (?)", [time])?;
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
|
||||||
|
|
||||||
// Stored string should preserve timezone offset
|
// Stored string should preserve timezone offset
|
||||||
let s: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert!(s.ends_with("+04:00"));
|
assert!(s.ends_with("+04:00"));
|
||||||
|
|
||||||
let v: DateTime<FixedOffset> = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let v: DateTime<FixedOffset> = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(time.offset(), v.offset());
|
assert_eq!(time.offset(), v.offset());
|
||||||
assert_eq!(time, v);
|
assert_eq!(time, v);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -286,15 +284,13 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sqlite_functions() -> Result<()> {
|
fn test_sqlite_functions() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let result: Result<NaiveTime> = db.query_row("SELECT CURRENT_TIME", [], |r| r.get(0));
|
let result: Result<NaiveTime> = db.one_column("SELECT CURRENT_TIME");
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
let result: Result<NaiveDate> = db.query_row("SELECT CURRENT_DATE", [], |r| r.get(0));
|
let result: Result<NaiveDate> = db.one_column("SELECT CURRENT_DATE");
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
let result: Result<NaiveDateTime> =
|
let result: Result<NaiveDateTime> = db.one_column("SELECT CURRENT_TIMESTAMP");
|
||||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
let result: Result<DateTime<Utc>> =
|
let result: Result<DateTime<Utc>> = db.one_column("SELECT CURRENT_TIMESTAMP");
|
||||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -302,7 +298,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_naive_date_time_param() -> Result<()> {
|
fn test_naive_date_time_param() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0));
|
let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now().naive_utc()], |r| r.get(0));
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -310,7 +306,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_date_time_param() -> Result<()> {
|
fn test_date_time_param() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0));
|
let result: Result<bool> = db.query_row("SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [Utc::now()], |r| r.get(0));
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,7 @@ impl fmt::Display for FromSqlError {
|
|||||||
} => {
|
} => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Cannot read {} byte value out of {} byte blob",
|
"Cannot read {expected_size} byte value out of {blob_size} byte blob"
|
||||||
expected_size, blob_size
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
FromSqlError::Other(ref err) => err.fmt(f),
|
FromSqlError::Other(ref err) => err.fmt(f),
|
||||||
@ -96,6 +95,15 @@ macro_rules! from_sql_integral(
|
|||||||
i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
|
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<Self> {
|
||||||
|
let i = <$z>::column_result(value)?;
|
||||||
|
<$nz>::new(i).ok_or(FromSqlError::OutOfRange(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -110,6 +118,22 @@ from_sql_integral!(u32);
|
|||||||
from_sql_integral!(u64);
|
from_sql_integral!(u64);
|
||||||
from_sql_integral!(usize);
|
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")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI128, i128);
|
||||||
|
|
||||||
|
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);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroU64, u64);
|
||||||
|
// std::num::NonZeroU128 is not supported since u128 isn't either
|
||||||
|
|
||||||
impl FromSql for i64 {
|
impl FromSql for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
@ -244,17 +268,17 @@ mod test {
|
|||||||
{
|
{
|
||||||
for n in out_of_range {
|
for n in out_of_range {
|
||||||
let err = db
|
let err = db
|
||||||
.query_row("SELECT ?", [n], |r| r.get::<_, T>(0))
|
.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
match err {
|
match err {
|
||||||
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
|
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
|
||||||
_ => panic!("unexpected error: {}", err),
|
_ => panic!("unexpected error: {err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for n in in_range {
|
for n in in_range {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*n,
|
*n,
|
||||||
db.query_row("SELECT ?", [n], |r| r.get::<_, T>(0))
|
db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into()
|
.into()
|
||||||
);
|
);
|
||||||
@ -273,4 +297,70 @@ mod test {
|
|||||||
check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
|
check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
|
||||||
Ok(())
|
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]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroU64,
|
||||||
|
&[0, -2, -1, -4_294_967_296],
|
||||||
|
&[1, 4_294_967_295, i64::MAX as u64]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroUsize,
|
||||||
|
&[0, -2, -1, -4_294_967_296],
|
||||||
|
&[1, 4_294_967_295]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
//! [`ToSql`] always succeeds except when storing a `u64` or `usize` value that
|
//! [`ToSql`] always succeeds except when storing a `u64` or `usize` value that
|
||||||
//! cannot fit in an `INTEGER` (`i64`). Also note that SQLite ignores column
|
//! cannot fit in an `INTEGER` (`i64`). Also note that SQLite ignores column
|
||||||
//! types, so if you store an `i64` in a column with type `REAL` it will be
|
//! types, so if you store an `i64` in a column with type `REAL` it will be
|
||||||
//! stored as an `INTEGER`, not a `REAL`.
|
//! stored as an `INTEGER`, not a `REAL` (unless the column is part of a
|
||||||
|
//! [STRICT table](https://www.sqlite.org/stricttables.html)).
|
||||||
//!
|
//!
|
||||||
//! If the `time` feature is enabled, implementations are
|
//! If the `time` feature is enabled, implementations are
|
||||||
//! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format,
|
//! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format,
|
||||||
@ -102,7 +103,7 @@ mod value_ref;
|
|||||||
/// # use rusqlite::types::{Null};
|
/// # use rusqlite::types::{Null};
|
||||||
///
|
///
|
||||||
/// fn insert_null(conn: &Connection) -> Result<usize> {
|
/// fn insert_null(conn: &Connection) -> Result<usize> {
|
||||||
/// conn.execute("INSERT INTO people (name) VALUES (?)", [Null])
|
/// conn.execute("INSERT INTO people (name) VALUES (?1)", [Null])
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -153,9 +154,9 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let v1234 = vec![1u8, 2, 3, 4];
|
let v1234 = vec![1u8, 2, 3, 4];
|
||||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&v1234])?;
|
db.execute("INSERT INTO foo(b) VALUES (?1)", [&v1234])?;
|
||||||
|
|
||||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
|
||||||
assert_eq!(v, v1234);
|
assert_eq!(v, v1234);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -165,9 +166,9 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let empty = vec![];
|
let empty = vec![];
|
||||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&empty])?;
|
db.execute("INSERT INTO foo(b) VALUES (?1)", [&empty])?;
|
||||||
|
|
||||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
|
||||||
assert_eq!(v, empty);
|
assert_eq!(v, empty);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -177,9 +178,9 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let s = "hello, world!";
|
let s = "hello, world!";
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?;
|
db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?;
|
||||||
|
|
||||||
let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let from: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(from, s);
|
assert_eq!(from, s);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -189,9 +190,9 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let s = "hello, world!";
|
let s = "hello, world!";
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", [s.to_owned()])?;
|
db.execute("INSERT INTO foo(t) VALUES (?1)", [s.to_owned()])?;
|
||||||
|
|
||||||
let from: String = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let from: String = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(from, s);
|
assert_eq!(from, s);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -200,12 +201,9 @@ mod test {
|
|||||||
fn test_value() -> Result<()> {
|
fn test_value() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
db.execute("INSERT INTO foo(i) VALUES (?)", [Value::Integer(10)])?;
|
db.execute("INSERT INTO foo(i) VALUES (?1)", [Value::Integer(10)])?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(10i64, db.one_column::<i64>("SELECT i FROM foo")?);
|
||||||
10i64,
|
|
||||||
db.query_row::<i64, _, _>("SELECT i FROM foo", [], |r| r.get(0))?
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,11 +211,11 @@ mod test {
|
|||||||
fn test_option() -> Result<()> {
|
fn test_option() -> Result<()> {
|
||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let s = Some("hello, world!");
|
let s = "hello, world!";
|
||||||
let b = Some(vec![1u8, 2, 3, 4]);
|
let b = Some(vec![1u8, 2, 3, 4]);
|
||||||
|
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?;
|
db.execute("INSERT INTO foo(t) VALUES (?1)", [Some(s)])?;
|
||||||
db.execute("INSERT INTO foo(b) VALUES (?)", [&b])?;
|
db.execute("INSERT INTO foo(b) VALUES (?1)", [&b])?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?;
|
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?;
|
||||||
let mut rows = stmt.query([])?;
|
let mut rows = stmt.query([])?;
|
||||||
@ -226,7 +224,7 @@ mod test {
|
|||||||
let row1 = rows.next()?.unwrap();
|
let row1 = rows.next()?.unwrap();
|
||||||
let s1: Option<String> = row1.get_unwrap(0);
|
let s1: Option<String> = row1.get_unwrap(0);
|
||||||
let b1: Option<Vec<u8>> = row1.get_unwrap(1);
|
let b1: Option<Vec<u8>> = row1.get_unwrap(1);
|
||||||
assert_eq!(s.unwrap(), s1.unwrap());
|
assert_eq!(s, s1.unwrap());
|
||||||
assert!(b1.is_none());
|
assert!(b1.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,7 +353,7 @@ mod test {
|
|||||||
assert_eq!(Value::Integer(1), row.get::<_, Value>(2)?);
|
assert_eq!(Value::Integer(1), row.get::<_, Value>(2)?);
|
||||||
match row.get::<_, Value>(3)? {
|
match row.get::<_, Value>(3)? {
|
||||||
Value::Real(val) => assert!((1.5 - val).abs() < f64::EPSILON),
|
Value::Real(val) => assert!((1.5 - val).abs() < f64::EPSILON),
|
||||||
x => panic!("Invalid Value {:?}", x),
|
x => panic!("Invalid Value {x:?}"),
|
||||||
}
|
}
|
||||||
assert_eq!(Value::Null, row.get::<_, Value>(4)?);
|
assert_eq!(Value::Null, row.get::<_, Value>(4)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,24 +1,62 @@
|
|||||||
//! [`ToSql`] and [`FromSql`] implementation for JSON `Value`.
|
//! [`ToSql`] and [`FromSql`] implementation for JSON `Value`.
|
||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::{Number, Value};
|
||||||
|
|
||||||
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||||
use crate::Result;
|
use crate::{Error, Result};
|
||||||
|
|
||||||
/// Serialize JSON `Value` to text.
|
/// Serialize JSON `Value` to text:
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// | JSON | SQLite |
|
||||||
|
/// |----------|---------|
|
||||||
|
/// | Null | NULL |
|
||||||
|
/// | Bool | 'true' / 'false' |
|
||||||
|
/// | Number | INT or REAL except u64 |
|
||||||
|
/// | _ | TEXT |
|
||||||
impl ToSql for Value {
|
impl ToSql for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap()))
|
match self {
|
||||||
|
Value::Null => Ok(ToSqlOutput::Borrowed(ValueRef::Null)),
|
||||||
|
Value::Number(n) if n.is_i64() => Ok(ToSqlOutput::from(n.as_i64().unwrap())),
|
||||||
|
Value::Number(n) if n.is_f64() => Ok(ToSqlOutput::from(n.as_f64().unwrap())),
|
||||||
|
_ => serde_json::to_string(self)
|
||||||
|
.map(ToSqlOutput::from)
|
||||||
|
.map_err(|err| Error::ToSqlConversionFailure(err.into())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize text/blob to JSON `Value`.
|
/// Deserialize SQLite value to JSON `Value`:
|
||||||
|
///
|
||||||
|
/// | SQLite | JSON |
|
||||||
|
/// |----------|---------|
|
||||||
|
/// | NULL | Null |
|
||||||
|
/// | 'null' | Null |
|
||||||
|
/// | 'true' | Bool |
|
||||||
|
/// | 1 | Number |
|
||||||
|
/// | 0.1 | Number |
|
||||||
|
/// | '"text"' | String |
|
||||||
|
/// | 'text' | _Error_ |
|
||||||
|
/// | '[0, 1]' | Array |
|
||||||
|
/// | '{"x": 1}' | Object |
|
||||||
impl FromSql for Value {
|
impl FromSql for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
let bytes = value.as_bytes()?;
|
match value {
|
||||||
serde_json::from_slice(bytes).map_err(|err| FromSqlError::Other(Box::new(err)))
|
ValueRef::Text(s) => serde_json::from_slice(s), // KO for b"text"
|
||||||
|
ValueRef::Blob(b) => serde_json::from_slice(b),
|
||||||
|
ValueRef::Integer(i) => Ok(Value::Number(Number::from(i))),
|
||||||
|
ValueRef::Real(f) => {
|
||||||
|
match Number::from_f64(f) {
|
||||||
|
Some(n) => Ok(Value::Number(n)),
|
||||||
|
_ => return Err(FromSqlError::InvalidType), // FIXME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueRef::Null => Ok(Value::Null),
|
||||||
|
}
|
||||||
|
.map_err(|err| FromSqlError::Other(Box::new(err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +64,7 @@ impl FromSql for Value {
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::types::ToSql;
|
use crate::types::ToSql;
|
||||||
use crate::{Connection, Result};
|
use crate::{Connection, Result};
|
||||||
|
use serde_json::{Number, Value};
|
||||||
|
|
||||||
fn checked_memory_handle() -> Result<Connection> {
|
fn checked_memory_handle() -> Result<Connection> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = Connection::open_in_memory()?;
|
||||||
@ -38,16 +77,59 @@ mod test {
|
|||||||
let db = checked_memory_handle()?;
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
let json = r#"{"foo": 13, "bar": "baz"}"#;
|
let json = r#"{"foo": 13, "bar": "baz"}"#;
|
||||||
let data: serde_json::Value = serde_json::from_str(json).unwrap();
|
let data: Value = serde_json::from_str(json).unwrap();
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO foo (t, b) VALUES (?, ?)",
|
"INSERT INTO foo (t, b) VALUES (?1, ?2)",
|
||||||
[&data as &dyn ToSql, &json.as_bytes()],
|
[&data as &dyn ToSql, &json.as_bytes()],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let t: serde_json::Value = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let t: Value = db.one_column("SELECT t FROM foo")?;
|
||||||
assert_eq!(data, t);
|
assert_eq!(data, t);
|
||||||
let b: serde_json::Value = db.query_row("SELECT b FROM foo", [], |r| r.get(0))?;
|
let b: Value = db.one_column("SELECT b FROM foo")?;
|
||||||
assert_eq!(data, b);
|
assert_eq!(data, b);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_sql() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
|
||||||
|
let v: Option<String> = db.query_row("SELECT ?", [Value::Null], |r| r.get(0))?;
|
||||||
|
assert_eq!(None, v);
|
||||||
|
let v: String = db.query_row("SELECT ?", [Value::Bool(true)], |r| r.get(0))?;
|
||||||
|
assert_eq!("true", v);
|
||||||
|
let v: i64 = db.query_row("SELECT ?", [Value::Number(Number::from(1))], |r| r.get(0))?;
|
||||||
|
assert_eq!(1, v);
|
||||||
|
let v: f64 = db.query_row(
|
||||||
|
"SELECT ?",
|
||||||
|
[Value::Number(Number::from_f64(0.1).unwrap())],
|
||||||
|
|r| r.get(0),
|
||||||
|
)?;
|
||||||
|
assert_eq!(0.1, v);
|
||||||
|
let v: String =
|
||||||
|
db.query_row("SELECT ?", [Value::String("text".to_owned())], |r| r.get(0))?;
|
||||||
|
assert_eq!("\"text\"", v);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_sql() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
|
||||||
|
let v: Value = db.one_column("SELECT NULL")?;
|
||||||
|
assert_eq!(Value::Null, v);
|
||||||
|
let v: Value = db.one_column("SELECT 'null'")?;
|
||||||
|
assert_eq!(Value::Null, v);
|
||||||
|
let v: Value = db.one_column("SELECT 'true'")?;
|
||||||
|
assert_eq!(Value::Bool(true), v);
|
||||||
|
let v: Value = db.one_column("SELECT 1")?;
|
||||||
|
assert_eq!(Value::Number(Number::from(1)), v);
|
||||||
|
let v: Value = db.one_column("SELECT 0.1")?;
|
||||||
|
assert_eq!(Value::Number(Number::from_f64(0.1).unwrap()), v);
|
||||||
|
let v: Value = db.one_column("SELECT '\"text\"'")?;
|
||||||
|
assert_eq!(Value::String("text".to_owned()), v);
|
||||||
|
let v: Result<Value> = db.one_column("SELECT 'text'");
|
||||||
|
assert!(v.is_err());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,155 @@
|
|||||||
|
//! Convert formats 1-10 in [Time Values](https://sqlite.org/lang_datefunc.html#time_values) to time types.
|
||||||
//! [`ToSql`] and [`FromSql`] implementation for [`time::OffsetDateTime`].
|
//! [`ToSql`] and [`FromSql`] implementation for [`time::OffsetDateTime`].
|
||||||
|
//! [`ToSql`] and [`FromSql`] implementation for [`time::PrimitiveDateTime`].
|
||||||
|
//! [`ToSql`] and [`FromSql`] implementation for [`time::Date`].
|
||||||
|
//! [`ToSql`] and [`FromSql`] implementation for [`time::Time`].
|
||||||
|
//! Time Strings in:
|
||||||
|
//! - Format 2: "YYYY-MM-DD HH:MM"
|
||||||
|
//! - Format 5: "YYYY-MM-DDTHH:MM"
|
||||||
|
//! - Format 8: "HH:MM"
|
||||||
|
//! without an explicit second value will assume 0 seconds.
|
||||||
|
//! Time String that contain an optional timezone without an explicit date are unsupported.
|
||||||
|
//! All other assumptions described in [Time Values](https://sqlite.org/lang_datefunc.html#time_values) section are unsupported.
|
||||||
|
|
||||||
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use time::format_description::well_known::Rfc3339;
|
|
||||||
use time::format_description::FormatItem;
|
use time::format_description::FormatItem;
|
||||||
use time::macros::format_description;
|
use time::macros::format_description;
|
||||||
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
|
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
const PRIMITIVE_SHORT_DATE_TIME_FORMAT: &[FormatItem<'_>] =
|
const OFFSET_DATE_TIME_ENCODING: &[FormatItem<'_>] = format_description!(
|
||||||
format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
|
version = 2,
|
||||||
const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] =
|
|
||||||
format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
|
|
||||||
const PRIMITIVE_DATE_TIME_Z_FORMAT: &[FormatItem<'_>] =
|
|
||||||
format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z");
|
|
||||||
const OFFSET_SHORT_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
|
||||||
"[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"
|
|
||||||
);
|
|
||||||
const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
|
||||||
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"
|
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"
|
||||||
);
|
);
|
||||||
|
const PRIMITIVE_DATE_TIME_ENCODING: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
|
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"
|
||||||
|
);
|
||||||
|
const TIME_ENCODING: &[FormatItem<'_>] =
|
||||||
|
format_description!(version = 2, "[hour]:[minute]:[second].[subsecond]");
|
||||||
|
|
||||||
|
const DATE_FORMAT: &[FormatItem<'_>] = format_description!(version = 2, "[year]-[month]-[day]");
|
||||||
|
const TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
|
"[hour]:[minute][optional [:[second][optional [.[subsecond]]]]]"
|
||||||
|
);
|
||||||
|
const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
|
"[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]]"
|
||||||
|
);
|
||||||
|
const UTC_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
|
"[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]][optional [Z]]"
|
||||||
|
);
|
||||||
|
const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
|
"[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]][offset_hour sign:mandatory]:[offset_minute]"
|
||||||
|
);
|
||||||
const LEGACY_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
const LEGACY_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
|
||||||
|
version = 2,
|
||||||
"[year]-[month]-[day] [hour]:[minute]:[second]:[subsecond] [offset_hour sign:mandatory]:[offset_minute]"
|
"[year]-[month]-[day] [hour]:[minute]:[second]:[subsecond] [offset_hour sign:mandatory]:[offset_minute]"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// OffsetDatetime => RFC3339 format ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM")
|
||||||
impl ToSql for OffsetDateTime {
|
impl ToSql for OffsetDateTime {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
// FIXME keep original offset
|
|
||||||
let time_string = self
|
let time_string = self
|
||||||
.to_offset(UtcOffset::UTC)
|
.format(&OFFSET_DATE_TIME_ENCODING)
|
||||||
.format(&PRIMITIVE_DATE_TIME_Z_FORMAT)
|
|
||||||
.map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
|
.map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
|
||||||
Ok(ToSqlOutput::from(time_string))
|
Ok(ToSqlOutput::from(time_string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supports parsing formats 2-7 from https://www.sqlite.org/lang_datefunc.html
|
||||||
|
// Formats 2-7 without a timezone assumes UTC
|
||||||
impl FromSql for OffsetDateTime {
|
impl FromSql for OffsetDateTime {
|
||||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
value.as_str().and_then(|s| {
|
value.as_str().and_then(|s| {
|
||||||
if s.len() > 10 && s.as_bytes()[10] == b'T' {
|
if let Some(b' ') = s.as_bytes().get(23) {
|
||||||
// YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM
|
// legacy
|
||||||
return OffsetDateTime::parse(s, &Rfc3339)
|
return OffsetDateTime::parse(s, &LEGACY_DATE_TIME_FORMAT)
|
||||||
.map_err(|err| FromSqlError::Other(Box::new(err)));
|
.map_err(|err| FromSqlError::Other(Box::new(err)));
|
||||||
}
|
}
|
||||||
let s = s.strip_suffix('Z').unwrap_or(s);
|
if s[8..].contains('+') || s[8..].contains('-') {
|
||||||
match s.len() {
|
// Formats 2-7 with timezone
|
||||||
len if len <= 19 => {
|
return OffsetDateTime::parse(s, &OFFSET_DATE_TIME_FORMAT)
|
||||||
// TODO YYYY-MM-DDTHH:MM:SS
|
.map_err(|err| FromSqlError::Other(Box::new(err)));
|
||||||
PrimitiveDateTime::parse(s, &PRIMITIVE_SHORT_DATE_TIME_FORMAT)
|
|
||||||
.map(PrimitiveDateTime::assume_utc)
|
|
||||||
}
|
|
||||||
_ if s.as_bytes()[19] == b':' => {
|
|
||||||
// legacy
|
|
||||||
OffsetDateTime::parse(s, &LEGACY_DATE_TIME_FORMAT)
|
|
||||||
}
|
|
||||||
_ if s.as_bytes()[19] == b'.' => OffsetDateTime::parse(s, &OFFSET_DATE_TIME_FORMAT)
|
|
||||||
.or_else(|err| {
|
|
||||||
PrimitiveDateTime::parse(s, &PRIMITIVE_DATE_TIME_FORMAT)
|
|
||||||
.map(PrimitiveDateTime::assume_utc)
|
|
||||||
.map_err(|_| err)
|
|
||||||
}),
|
|
||||||
_ => OffsetDateTime::parse(s, &OFFSET_SHORT_DATE_TIME_FORMAT),
|
|
||||||
}
|
}
|
||||||
.map_err(|err| FromSqlError::Other(Box::new(err)))
|
// Formats 2-7 without timezone
|
||||||
|
PrimitiveDateTime::parse(s, &UTC_DATE_TIME_FORMAT)
|
||||||
|
.map(|p| p.assume_utc())
|
||||||
|
.map_err(|err| FromSqlError::Other(Box::new(err)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
|
||||||
|
impl ToSql for Date {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
let date_str = self
|
||||||
|
.format(&DATE_FORMAT)
|
||||||
|
.map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
|
||||||
|
Ok(ToSqlOutput::from(date_str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
|
||||||
|
impl FromSql for Date {
|
||||||
|
#[inline]
|
||||||
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
value.as_str().and_then(|s| {
|
||||||
|
Date::parse(s, &DATE_FORMAT).map_err(|err| FromSqlError::Other(err.into()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
|
||||||
|
impl ToSql for Time {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
let time_str = self
|
||||||
|
.format(&TIME_ENCODING)
|
||||||
|
.map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
|
||||||
|
Ok(ToSqlOutput::from(time_str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
|
||||||
|
impl FromSql for Time {
|
||||||
|
#[inline]
|
||||||
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
value.as_str().and_then(|s| {
|
||||||
|
Time::parse(s, &TIME_FORMAT).map_err(|err| FromSqlError::Other(err.into()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS"
|
||||||
|
impl ToSql for PrimitiveDateTime {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
let date_time_str = self
|
||||||
|
.format(&PRIMITIVE_DATE_TIME_ENCODING)
|
||||||
|
.map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
|
||||||
|
Ok(ToSqlOutput::from(date_time_str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// YYYY-MM-DD HH:MM
|
||||||
|
/// YYYY-MM-DDTHH:MM
|
||||||
|
/// YYYY-MM-DD HH:MM:SS
|
||||||
|
/// YYYY-MM-DDTHH:MM:SS
|
||||||
|
/// YYYY-MM-DD HH:MM:SS.SSS
|
||||||
|
/// YYYY-MM-DDTHH:MM:SS.SSS
|
||||||
|
/// => ISO 8601 combined date and time with timezone
|
||||||
|
impl FromSql for PrimitiveDateTime {
|
||||||
|
#[inline]
|
||||||
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
value.as_str().and_then(|s| {
|
||||||
|
PrimitiveDateTime::parse(s, &PRIMITIVE_DATE_TIME_FORMAT)
|
||||||
|
.map_err(|err| FromSqlError::Other(err.into()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,13 +157,18 @@ impl FromSql for OffsetDateTime {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{Connection, Result};
|
use crate::{Connection, Result};
|
||||||
use time::format_description::well_known::Rfc3339;
|
use time::macros::{date, datetime, time};
|
||||||
use time::OffsetDateTime;
|
use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
|
fn checked_memory_handle() -> Result<Connection> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)")?;
|
||||||
|
Ok(db)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_offset_date_time() -> Result<()> {
|
fn test_offset_date_time() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = checked_memory_handle()?;
|
||||||
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")?;
|
|
||||||
|
|
||||||
let mut ts_vec = vec![];
|
let mut ts_vec = vec![];
|
||||||
|
|
||||||
@ -91,9 +184,9 @@ mod test {
|
|||||||
ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286
|
ts_vec.push(make_datetime(10_000_000_000, 0)); //November 20, 2286
|
||||||
|
|
||||||
for ts in ts_vec {
|
for ts in ts_vec {
|
||||||
db.execute("INSERT INTO foo(t) VALUES (?)", [ts])?;
|
db.execute("INSERT INTO foo(t) VALUES (?1)", [ts])?;
|
||||||
|
|
||||||
let from: OffsetDateTime = db.query_row("SELECT t FROM foo", [], |r| r.get(0))?;
|
let from: OffsetDateTime = db.one_column("SELECT t FROM foo")?;
|
||||||
|
|
||||||
db.execute("DELETE FROM foo", [])?;
|
db.execute("DELETE FROM foo", [])?;
|
||||||
|
|
||||||
@ -103,47 +196,163 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_values() -> Result<()> {
|
fn test_offset_date_time_parsing() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = checked_memory_handle()?;
|
||||||
for (s, t) in vec![
|
let tests = vec![
|
||||||
|
// Rfc3339
|
||||||
(
|
(
|
||||||
"2013-10-07 08:23:19",
|
"2013-10-07T08:23:19.123456789Z",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123456789 UTC),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"2013-10-07 08:23:19Z",
|
"2013-10-07 08:23:19.123456789Z",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123456789 UTC),
|
||||||
|
),
|
||||||
|
// Format 2
|
||||||
|
("2013-10-07 08:23", datetime!(2013-10-07 8:23 UTC)),
|
||||||
|
("2013-10-07 08:23Z", datetime!(2013-10-07 8:23 UTC)),
|
||||||
|
("2013-10-07 08:23+04:00", datetime!(2013-10-07 8:23 +4)),
|
||||||
|
// Format 3
|
||||||
|
("2013-10-07 08:23:19", datetime!(2013-10-07 8:23:19 UTC)),
|
||||||
|
("2013-10-07 08:23:19Z", datetime!(2013-10-07 8:23:19 UTC)),
|
||||||
|
(
|
||||||
|
"2013-10-07 08:23:19+04:00",
|
||||||
|
datetime!(2013-10-07 8:23:19 +4),
|
||||||
|
),
|
||||||
|
// Format 4
|
||||||
|
(
|
||||||
|
"2013-10-07 08:23:19.123",
|
||||||
|
datetime!(2013-10-07 8:23:19.123 UTC),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"2013-10-07T08:23:19Z",
|
"2013-10-07 08:23:19.123Z",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123 UTC),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"2013-10-07 08:23:19.120",
|
"2013-10-07 08:23:19.123+04:00",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19.120Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123 +4),
|
||||||
|
),
|
||||||
|
// Format 5
|
||||||
|
("2013-10-07T08:23", datetime!(2013-10-07 8:23 UTC)),
|
||||||
|
("2013-10-07T08:23Z", datetime!(2013-10-07 8:23 UTC)),
|
||||||
|
("2013-10-07T08:23+04:00", datetime!(2013-10-07 8:23 +4)),
|
||||||
|
// Format 6
|
||||||
|
("2013-10-07T08:23:19", datetime!(2013-10-07 8:23:19 UTC)),
|
||||||
|
("2013-10-07T08:23:19Z", datetime!(2013-10-07 8:23:19 UTC)),
|
||||||
|
(
|
||||||
|
"2013-10-07T08:23:19+04:00",
|
||||||
|
datetime!(2013-10-07 8:23:19 +4),
|
||||||
|
),
|
||||||
|
// Format 7
|
||||||
|
(
|
||||||
|
"2013-10-07T08:23:19.123",
|
||||||
|
datetime!(2013-10-07 8:23:19.123 UTC),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"2013-10-07 08:23:19.120Z",
|
"2013-10-07T08:23:19.123Z",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19.120Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123 UTC),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"2013-10-07T08:23:19.120Z",
|
"2013-10-07T08:23:19.123+04:00",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T08:23:19.120Z", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:19.123 +4),
|
||||||
),
|
),
|
||||||
|
// Legacy
|
||||||
(
|
(
|
||||||
"2013-10-07 04:23:19-04:00",
|
"2013-10-07 08:23:12:987 -07:00",
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T04:23:19-04:00", &Rfc3339).unwrap()),
|
datetime!(2013-10-07 8:23:12.987 -7),
|
||||||
),
|
),
|
||||||
(
|
];
|
||||||
"2013-10-07 04:23:19.120-04:00",
|
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()),
|
for (s, t) in tests {
|
||||||
),
|
let result: OffsetDateTime = db.query_row("SELECT ?1", [s], |r| r.get(0))?;
|
||||||
(
|
assert_eq!(result, t);
|
||||||
"2013-10-07T04:23:19.120-04:00",
|
}
|
||||||
Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()),
|
Ok(())
|
||||||
),
|
}
|
||||||
] {
|
|
||||||
let result: Result<OffsetDateTime> = db.query_row("SELECT ?", [s], |r| r.get(0));
|
#[test]
|
||||||
|
fn test_date() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let date = date!(2016 - 02 - 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);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let time = time!(23:56:04.00001);
|
||||||
|
db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
|
||||||
|
|
||||||
|
let s: String = db.one_column("SELECT t FROM foo")?;
|
||||||
|
assert_eq!("23:56:04.00001", s);
|
||||||
|
let v: Time = db.one_column("SELECT t FROM foo")?;
|
||||||
|
assert_eq!(time, v);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_primitive_date_time() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let dt = date!(2016 - 02 - 23).with_time(time!(23:56:04));
|
||||||
|
|
||||||
|
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.0", s);
|
||||||
|
let v: PrimitiveDateTime = 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: PrimitiveDateTime = db.one_column("SELECT b FROM foo")?;
|
||||||
|
assert_eq!(dt, hms);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date_parsing() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let result: Date = db.query_row("SELECT ?1", ["2013-10-07"], |r| r.get(0))?;
|
||||||
|
assert_eq!(result, date!(2013 - 10 - 07));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_parsing() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let tests = vec![
|
||||||
|
("08:23", time!(08:23)),
|
||||||
|
("08:23:19", time!(08:23:19)),
|
||||||
|
("08:23:19.111", time!(08:23:19.111)),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (s, t) in tests {
|
||||||
|
let result: Time = db.query_row("SELECT ?1", [s], |r| r.get(0))?;
|
||||||
|
assert_eq!(result, t);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_primitive_date_time_parsing() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
|
||||||
|
let tests = vec![
|
||||||
|
("2013-10-07T08:23", datetime!(2013-10-07 8:23)),
|
||||||
|
("2013-10-07T08:23:19", datetime!(2013-10-07 8:23:19)),
|
||||||
|
("2013-10-07T08:23:19.111", datetime!(2013-10-07 8:23:19.111)),
|
||||||
|
("2013-10-07 08:23", datetime!(2013-10-07 8:23)),
|
||||||
|
("2013-10-07 08:23:19", datetime!(2013-10-07 8:23:19)),
|
||||||
|
("2013-10-07 08:23:19.111", datetime!(2013-10-07 8:23:19.111)),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (s, t) in tests {
|
||||||
|
let result: PrimitiveDateTime = db.query_row("SELECT ?1", [s], |r| r.get(0))?;
|
||||||
assert_eq!(result, t);
|
assert_eq!(result, t);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -151,17 +360,66 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sqlite_functions() -> Result<()> {
|
fn test_sqlite_functions() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = checked_memory_handle()?;
|
||||||
let result: Result<OffsetDateTime> =
|
db.one_column::<Time>("SELECT CURRENT_TIME").unwrap();
|
||||||
db.query_row("SELECT CURRENT_TIMESTAMP", [], |r| r.get(0));
|
db.one_column::<Date>("SELECT CURRENT_DATE").unwrap();
|
||||||
|
db.one_column::<PrimitiveDateTime>("SELECT CURRENT_TIMESTAMP")
|
||||||
|
.unwrap();
|
||||||
|
db.one_column::<OffsetDateTime>("SELECT CURRENT_TIMESTAMP")
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_param() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let now = OffsetDateTime::now_utc().time();
|
||||||
|
let result: Result<bool> = db.query_row(
|
||||||
|
"SELECT 1 WHERE ?1 BETWEEN time('now', '-1 minute') AND time('now', '+1 minute')",
|
||||||
|
[now],
|
||||||
|
|r| r.get(0),
|
||||||
|
);
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_param() -> Result<()> {
|
fn test_date_param() -> Result<()> {
|
||||||
let db = Connection::open_in_memory()?;
|
let db = checked_memory_handle()?;
|
||||||
let result: Result<bool> = db.query_row("SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')", [OffsetDateTime::now_utc()], |r| r.get(0));
|
let now = OffsetDateTime::now_utc().date();
|
||||||
|
let result: Result<bool> = db.query_row(
|
||||||
|
"SELECT 1 WHERE ?1 BETWEEN date('now', '-1 day') AND date('now', '+1 day')",
|
||||||
|
[now],
|
||||||
|
|r| r.get(0),
|
||||||
|
);
|
||||||
|
result.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_primitive_date_time_param() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let now = PrimitiveDateTime::new(
|
||||||
|
OffsetDateTime::now_utc().date(),
|
||||||
|
OffsetDateTime::now_utc().time(),
|
||||||
|
);
|
||||||
|
let result: Result<bool> = db.query_row(
|
||||||
|
"SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')",
|
||||||
|
[now],
|
||||||
|
|r| r.get(0),
|
||||||
|
);
|
||||||
|
result.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_offset_date_time_param() -> Result<()> {
|
||||||
|
let db = checked_memory_handle()?;
|
||||||
|
let result: Result<bool> = db.query_row(
|
||||||
|
"SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')",
|
||||||
|
[OffsetDateTime::now_utc()],
|
||||||
|
|r| r.get(0),
|
||||||
|
);
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,12 @@ macro_rules! from_value(
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
|
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
(non_zero $t:ty) => (
|
||||||
|
impl From<$t> for ToSqlOutput<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
from_value!(String);
|
from_value!(String);
|
||||||
@ -68,6 +74,15 @@ from_value!(f32);
|
|||||||
from_value!(f64);
|
from_value!(f64);
|
||||||
from_value!(Vec<u8>);
|
from_value!(Vec<u8>);
|
||||||
|
|
||||||
|
from_value!(non_zero std::num::NonZeroI8);
|
||||||
|
from_value!(non_zero std::num::NonZeroI16);
|
||||||
|
from_value!(non_zero std::num::NonZeroI32);
|
||||||
|
from_value!(non_zero std::num::NonZeroI64);
|
||||||
|
from_value!(non_zero std::num::NonZeroIsize);
|
||||||
|
from_value!(non_zero std::num::NonZeroU8);
|
||||||
|
from_value!(non_zero std::num::NonZeroU16);
|
||||||
|
from_value!(non_zero std::num::NonZeroU32);
|
||||||
|
|
||||||
// It would be nice if we could avoid the heap allocation (of the `Vec`) that
|
// It would be nice if we could avoid the heap allocation (of the `Vec`) that
|
||||||
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
|
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
|
||||||
// worth adding another case to Value.
|
// worth adding another case to Value.
|
||||||
@ -75,6 +90,10 @@ from_value!(Vec<u8>);
|
|||||||
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
from_value!(i128);
|
from_value!(i128);
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
from_value!(non_zero std::num::NonZeroI128);
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||||
from_value!(uuid::Uuid);
|
from_value!(uuid::Uuid);
|
||||||
@ -165,10 +184,23 @@ to_sql_self!(u32);
|
|||||||
to_sql_self!(f32);
|
to_sql_self!(f32);
|
||||||
to_sql_self!(f64);
|
to_sql_self!(f64);
|
||||||
|
|
||||||
|
to_sql_self!(std::num::NonZeroI8);
|
||||||
|
to_sql_self!(std::num::NonZeroI16);
|
||||||
|
to_sql_self!(std::num::NonZeroI32);
|
||||||
|
to_sql_self!(std::num::NonZeroI64);
|
||||||
|
to_sql_self!(std::num::NonZeroIsize);
|
||||||
|
to_sql_self!(std::num::NonZeroU8);
|
||||||
|
to_sql_self!(std::num::NonZeroU16);
|
||||||
|
to_sql_self!(std::num::NonZeroU32);
|
||||||
|
|
||||||
#[cfg(feature = "i128_blob")]
|
#[cfg(feature = "i128_blob")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
to_sql_self!(i128);
|
to_sql_self!(i128);
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
to_sql_self!(std::num::NonZeroI128);
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||||
to_sql_self!(uuid::Uuid);
|
to_sql_self!(uuid::Uuid);
|
||||||
@ -186,12 +218,27 @@ macro_rules! to_sql_self_fallible(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
(non_zero $t:ty) => (
|
||||||
|
impl ToSql for $t {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
Ok(ToSqlOutput::Owned(Value::Integer(
|
||||||
|
i64::try_from(self.get()).map_err(
|
||||||
|
// TODO: Include the values in the error message.
|
||||||
|
|err| Error::ToSqlConversionFailure(err.into())
|
||||||
|
)?
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Special implementations for usize and u64 because these conversions can fail.
|
// Special implementations for usize and u64 because these conversions can fail.
|
||||||
to_sql_self_fallible!(u64);
|
to_sql_self_fallible!(u64);
|
||||||
to_sql_self_fallible!(usize);
|
to_sql_self_fallible!(usize);
|
||||||
|
to_sql_self_fallible!(non_zero std::num::NonZeroU64);
|
||||||
|
to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
|
||||||
|
|
||||||
impl<T: ?Sized> ToSql for &'_ T
|
impl<T: ?Sized> ToSql for &'_ T
|
||||||
where
|
where
|
||||||
@ -267,9 +314,26 @@ mod test {
|
|||||||
is_to_sql::<i16>();
|
is_to_sql::<i16>();
|
||||||
is_to_sql::<i32>();
|
is_to_sql::<i32>();
|
||||||
is_to_sql::<i64>();
|
is_to_sql::<i64>();
|
||||||
|
is_to_sql::<isize>();
|
||||||
is_to_sql::<u8>();
|
is_to_sql::<u8>();
|
||||||
is_to_sql::<u16>();
|
is_to_sql::<u16>();
|
||||||
is_to_sql::<u32>();
|
is_to_sql::<u32>();
|
||||||
|
is_to_sql::<u64>();
|
||||||
|
is_to_sql::<usize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nonzero_types() {
|
||||||
|
is_to_sql::<std::num::NonZeroI8>();
|
||||||
|
is_to_sql::<std::num::NonZeroI16>();
|
||||||
|
is_to_sql::<std::num::NonZeroI32>();
|
||||||
|
is_to_sql::<std::num::NonZeroI64>();
|
||||||
|
is_to_sql::<std::num::NonZeroIsize>();
|
||||||
|
is_to_sql::<std::num::NonZeroU8>();
|
||||||
|
is_to_sql::<std::num::NonZeroU16>();
|
||||||
|
is_to_sql::<std::num::NonZeroU32>();
|
||||||
|
is_to_sql::<std::num::NonZeroU64>();
|
||||||
|
is_to_sql::<std::num::NonZeroUsize>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -368,10 +432,10 @@ mod test {
|
|||||||
db.execute(
|
db.execute(
|
||||||
"
|
"
|
||||||
INSERT INTO foo(i128, desc) VALUES
|
INSERT INTO foo(i128, desc) VALUES
|
||||||
(?, 'zero'),
|
(?1, 'zero'),
|
||||||
(?, 'neg one'), (?, 'neg two'),
|
(?2, 'neg one'), (?3, 'neg two'),
|
||||||
(?, 'pos one'), (?, 'pos two'),
|
(?4, 'pos one'), (?5, 'pos two'),
|
||||||
(?, 'min'), (?, 'max')",
|
(?6, 'min'), (?7, 'max')",
|
||||||
[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
|
[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -398,6 +462,54 @@ mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[test]
|
||||||
|
fn test_non_zero_i128() -> crate::Result<()> {
|
||||||
|
use std::num::NonZeroI128;
|
||||||
|
macro_rules! nz {
|
||||||
|
($x:expr) => {
|
||||||
|
NonZeroI128::new($x).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = crate::Connection::open_in_memory()?;
|
||||||
|
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
|
||||||
|
db.execute(
|
||||||
|
"INSERT INTO foo(i128, desc) VALUES
|
||||||
|
(?1, 'neg one'), (?2, 'neg two'),
|
||||||
|
(?3, 'pos one'), (?4, 'pos two'),
|
||||||
|
(?5, 'min'), (?6, 'max')",
|
||||||
|
[
|
||||||
|
nz!(-1),
|
||||||
|
nz!(-2),
|
||||||
|
nz!(1),
|
||||||
|
nz!(2),
|
||||||
|
nz!(i128::MIN),
|
||||||
|
nz!(i128::MAX),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
|
||||||
|
|
||||||
|
let res = stmt
|
||||||
|
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
|
||||||
|
.collect::<Result<Vec<(NonZeroI128, String)>, _>>()?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
&[
|
||||||
|
(nz!(i128::MIN), "min".to_owned()),
|
||||||
|
(nz!(-2), "neg two".to_owned()),
|
||||||
|
(nz!(-1), "neg one".to_owned()),
|
||||||
|
(nz!(1), "pos one".to_owned()),
|
||||||
|
(nz!(2), "pos two".to_owned()),
|
||||||
|
(nz!(i128::MAX), "max".to_owned()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0));
|
||||||
|
assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uuid() -> crate::Result<()> {
|
fn test_uuid() -> crate::Result<()> {
|
||||||
@ -410,11 +522,11 @@ mod test {
|
|||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO foo (id, label) VALUES (?, ?)",
|
"INSERT INTO foo (id, label) VALUES (?1, ?2)",
|
||||||
params![id, "target"],
|
params![id, "target"],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?;
|
let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?;
|
||||||
|
|
||||||
let mut rows = stmt.query(params![id])?;
|
let mut rows = stmt.query(params![id])?;
|
||||||
let row = rows.next()?.unwrap();
|
let row = rows.next()?.unwrap();
|
||||||
|
@ -49,7 +49,7 @@ mod test {
|
|||||||
let url2 = "http://www.example2.com/👌";
|
let url2 = "http://www.example2.com/👌";
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO urls (i, v) VALUES (0, ?), (1, ?), (2, ?), (3, ?)",
|
"INSERT INTO urls (i, v) VALUES (0, ?1), (1, ?2), (2, ?3), (3, ?4)",
|
||||||
// also insert a non-hex encoded url (which might be present if it was
|
// also insert a non-hex encoded url (which might be present if it was
|
||||||
// inserted separately)
|
// inserted separately)
|
||||||
params![url0, url1, url2, "illegal"],
|
params![url0, url1, url2, "illegal"],
|
||||||
@ -74,7 +74,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
e => {
|
e => {
|
||||||
panic!("Expected conversion failure, got {}", e);
|
panic!("Expected conversion failure, got {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -158,6 +158,7 @@ impl<'a> ValueRef<'a> {
|
|||||||
|
|
||||||
impl From<ValueRef<'_>> for Value {
|
impl From<ValueRef<'_>> for Value {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
fn from(borrowed: ValueRef<'_>) -> Value {
|
fn from(borrowed: ValueRef<'_>) -> Value {
|
||||||
match borrowed {
|
match borrowed {
|
||||||
ValueRef::Null => Value::Null,
|
ValueRef::Null => Value::Null,
|
||||||
|
@ -109,8 +109,8 @@ mod test {
|
|||||||
tx2.commit().unwrap();
|
tx2.commit().unwrap();
|
||||||
});
|
});
|
||||||
assert_eq!(tx.recv().unwrap(), 1);
|
assert_eq!(tx.recv().unwrap(), 1);
|
||||||
let the_answer: Result<i64> = db1.query_row("SELECT x FROM foo", [], |r| r.get(0));
|
let the_answer: i64 = db1.one_column("SELECT x FROM foo")?;
|
||||||
assert_eq!(42i64, the_answer?);
|
assert_eq!(42i64, the_answer);
|
||||||
child.join().unwrap();
|
child.join().unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,4 @@ pub(crate) use small_cstr::SmallCString;
|
|||||||
|
|
||||||
// Doesn't use any modern features or vtab stuff, but is only used by them.
|
// Doesn't use any modern features or vtab stuff, but is only used by them.
|
||||||
mod sqlite_string;
|
mod sqlite_string;
|
||||||
pub(crate) use sqlite_string::SqliteMallocString;
|
pub(crate) use sqlite_string::{alloc, SqliteMallocString};
|
||||||
|
@ -7,6 +7,12 @@ use std::marker::PhantomData;
|
|||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
// Space to hold this string must be obtained
|
||||||
|
// from an SQLite memory allocation function
|
||||||
|
pub(crate) fn alloc(s: &str) -> *mut c_char {
|
||||||
|
SqliteMallocString::from_str(s).into_raw()
|
||||||
|
}
|
||||||
|
|
||||||
/// A string we own that's allocated on the SQLite heap. Automatically calls
|
/// A string we own that's allocated on the SQLite heap. Automatically calls
|
||||||
/// `sqlite3_free` when dropped, unless `into_raw` (or `into_inner`) is called
|
/// `sqlite3_free` when dropped, unless `into_raw` (or `into_inner`) is called
|
||||||
/// on it. If constructed from a rust string, `sqlite3_malloc` is used.
|
/// on it. If constructed from a rust string, `sqlite3_malloc` is used.
|
||||||
@ -100,7 +106,6 @@ impl SqliteMallocString {
|
|||||||
/// This means it's safe to use in extern "C" functions even outside of
|
/// This means it's safe to use in extern "C" functions even outside of
|
||||||
/// `catch_unwind`.
|
/// `catch_unwind`.
|
||||||
pub(crate) fn from_str(s: &str) -> Self {
|
pub(crate) fn from_str(s: &str) -> Self {
|
||||||
use std::convert::TryFrom;
|
|
||||||
let s = if s.as_bytes().contains(&0) {
|
let s = if s.as_bytes().contains(&0) {
|
||||||
std::borrow::Cow::Owned(make_nonnull(s))
|
std::borrow::Cow::Owned(make_nonnull(s))
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! let v = [1i64, 2, 3, 4];
|
//! let v = [1i64, 2, 3, 4];
|
||||||
//! // Note: A `Rc<Vec<Value>>` must be used as the parameter.
|
//! // Note: A `Rc<Vec<Value>>` must be used as the parameter.
|
||||||
//! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>());
|
//! let values = Rc::new(v.iter().copied().map(Value::from).collect::<Vec<Value>>());
|
||||||
//! let mut stmt = db.prepare("SELECT value from rarray(?);")?;
|
//! let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
|
||||||
//! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?;
|
//! let rows = stmt.query_map([values], |row| row.get::<_, i64>(0))?;
|
||||||
//! for value in rows {
|
//! for value in rows {
|
||||||
//! println!("{}", value?);
|
//! println!("{}", value?);
|
||||||
@ -206,7 +206,7 @@ mod test {
|
|||||||
let values: Vec<Value> = v.into_iter().map(Value::from).collect();
|
let values: Vec<Value> = v.into_iter().map(Value::from).collect();
|
||||||
let ptr = Rc::new(values);
|
let ptr = Rc::new(values);
|
||||||
{
|
{
|
||||||
let mut stmt = db.prepare("SELECT value from rarray(?);")?;
|
let mut stmt = db.prepare("SELECT value from rarray(?1);")?;
|
||||||
|
|
||||||
let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?;
|
let rows = stmt.query_map([&ptr], |row| row.get::<_, i64>(0))?;
|
||||||
assert_eq!(2, Rc::strong_count(&ptr));
|
assert_eq!(2, Rc::strong_count(&ptr));
|
||||||
|
@ -113,10 +113,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
|||||||
match param {
|
match param {
|
||||||
"filename" => {
|
"filename" => {
|
||||||
if !Path::new(value).exists() {
|
if !Path::new(value).exists() {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!("file '{value}' does not exist")));
|
||||||
"file '{}' does not exist",
|
|
||||||
value
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
vtab.filename = value.to_owned();
|
vtab.filename = value.to_owned();
|
||||||
}
|
}
|
||||||
@ -137,8 +134,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
|||||||
n_col = Some(n);
|
n_col = Some(n);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized argument to 'columns': {}",
|
"unrecognized argument to 'columns': {value}"
|
||||||
value
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,8 +143,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
|||||||
vtab.has_headers = b;
|
vtab.has_headers = b;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized argument to 'header': {}",
|
"unrecognized argument to 'header': {value}"
|
||||||
value
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,8 +152,7 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
|||||||
vtab.delimiter = b;
|
vtab.delimiter = b;
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized argument to 'delimiter': {}",
|
"unrecognized argument to 'delimiter': {value}"
|
||||||
value
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,15 +165,13 @@ unsafe impl<'vtab> VTab<'vtab> for CsvTab {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized argument to 'quote': {}",
|
"unrecognized argument to 'quote': {value}"
|
||||||
value
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized parameter '{}'",
|
"unrecognized parameter '{param}'"
|
||||||
param
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,8 +318,7 @@ unsafe impl VTabCursor for CsvTabCursor<'_> {
|
|||||||
fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
|
fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
|
||||||
if col < 0 || col as usize >= self.cols.len() {
|
if col < 0 || col as usize >= self.cols.len() {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"column index out of bounds: {}",
|
"column index out of bounds: {col}"
|
||||||
col
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if self.cols.is_empty() {
|
if self.cols.is_empty() {
|
||||||
|
@ -17,10 +17,11 @@ use std::ptr;
|
|||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use crate::context::set_result;
|
use crate::context::set_result;
|
||||||
use crate::error::error_from_sqlite_code;
|
use crate::error::{error_from_sqlite_code, to_sqlite_error};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
|
pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
|
||||||
use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
|
use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
|
||||||
|
use crate::util::alloc;
|
||||||
use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
|
use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
|
||||||
|
|
||||||
// let conn: Connection = ...;
|
// let conn: Connection = ...;
|
||||||
@ -195,6 +196,8 @@ pub enum VTabConfig {
|
|||||||
Innocuous = 2,
|
Innocuous = 2,
|
||||||
/// Equivalent to SQLITE_VTAB_DIRECTONLY
|
/// Equivalent to SQLITE_VTAB_DIRECTONLY
|
||||||
DirectOnly = 3,
|
DirectOnly = 3,
|
||||||
|
/// Equivalent to SQLITE_VTAB_USES_ALL_SCHEMAS
|
||||||
|
UsesAllSchemas = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `feature = "vtab"`
|
/// `feature = "vtab"`
|
||||||
@ -619,7 +622,7 @@ impl IndexConstraintUsage<'_> {
|
|||||||
|
|
||||||
/// `feature = "vtab"`
|
/// `feature = "vtab"`
|
||||||
pub struct OrderByIter<'a> {
|
pub struct OrderByIter<'a> {
|
||||||
iter: slice::Iter<'a, ffi::sqlite3_index_info_sqlite3_index_orderby>,
|
iter: slice::Iter<'a, ffi::sqlite3_index_orderby>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for OrderByIter<'a> {
|
impl<'a> Iterator for OrderByIter<'a> {
|
||||||
@ -637,7 +640,7 @@ impl<'a> Iterator for OrderByIter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A column of the ORDER BY clause.
|
/// A column of the ORDER BY clause.
|
||||||
pub struct OrderBy<'a>(&'a ffi::sqlite3_index_info_sqlite3_index_orderby);
|
pub struct OrderBy<'a>(&'a ffi::sqlite3_index_orderby);
|
||||||
|
|
||||||
impl OrderBy<'_> {
|
impl OrderBy<'_> {
|
||||||
/// Column number
|
/// Column number
|
||||||
@ -882,7 +885,7 @@ pub fn dequote(s: &str) -> &str {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
match s.bytes().next() {
|
match s.bytes().next() {
|
||||||
Some(b) if b == b'"' || b == b'\'' => match s.bytes().rev().next() {
|
Some(b) if b == b'"' || b == b'\'' => match s.bytes().next_back() {
|
||||||
Some(e) if e == b => &s[1..s.len() - 1], // FIXME handle inner escaped quote(s)
|
Some(e) if e == b => &s[1..s.len() - 1], // FIXME handle inner escaped quote(s)
|
||||||
_ => s,
|
_ => s,
|
||||||
},
|
},
|
||||||
@ -962,8 +965,7 @@ where
|
|||||||
ffi::SQLITE_OK
|
ffi::SQLITE_OK
|
||||||
} else {
|
} else {
|
||||||
let err = error_from_sqlite_code(rc, None);
|
let err = error_from_sqlite_code(rc, None);
|
||||||
*err_msg = alloc(&err.to_string());
|
to_sqlite_error(&err, err_msg)
|
||||||
rc
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -971,16 +973,7 @@ where
|
|||||||
ffi::SQLITE_ERROR
|
ffi::SQLITE_ERROR
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(Error::SqliteFailure(err, s)) => {
|
Err(err) => to_sqlite_error(&err, err_msg),
|
||||||
if let Some(s) = s {
|
|
||||||
*err_msg = alloc(&s);
|
|
||||||
}
|
|
||||||
err.extended_code
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
*err_msg = alloc(&err.to_string());
|
|
||||||
ffi::SQLITE_ERROR
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1014,8 +1007,7 @@ where
|
|||||||
ffi::SQLITE_OK
|
ffi::SQLITE_OK
|
||||||
} else {
|
} else {
|
||||||
let err = error_from_sqlite_code(rc, None);
|
let err = error_from_sqlite_code(rc, None);
|
||||||
*err_msg = alloc(&err.to_string());
|
to_sqlite_error(&err, err_msg)
|
||||||
rc
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -1023,16 +1015,7 @@ where
|
|||||||
ffi::SQLITE_ERROR
|
ffi::SQLITE_ERROR
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(Error::SqliteFailure(err, s)) => {
|
Err(err) => to_sqlite_error(&err, err_msg),
|
||||||
if let Some(s) = s {
|
|
||||||
*err_msg = alloc(&s);
|
|
||||||
}
|
|
||||||
err.extended_code
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
*err_msg = alloc(&err.to_string());
|
|
||||||
ffi::SQLITE_ERROR
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,12 +1292,6 @@ unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Space to hold this string must be obtained
|
|
||||||
// from an SQLite memory allocation function
|
|
||||||
fn alloc(s: &str) -> *mut c_char {
|
|
||||||
crate::util::SqliteMallocString::from_str(s).into_raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "array")]
|
#[cfg(feature = "array")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
|
||||||
pub mod array;
|
pub mod array;
|
||||||
|
@ -28,6 +28,7 @@ const SERIES_COLUMN_STOP: c_int = 2;
|
|||||||
const SERIES_COLUMN_STEP: c_int = 3;
|
const SERIES_COLUMN_STEP: c_int = 3;
|
||||||
|
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct QueryPlanFlags: ::std::os::raw::c_int {
|
struct QueryPlanFlags: ::std::os::raw::c_int {
|
||||||
// start = $value -- constraint exists
|
// start = $value -- constraint exists
|
||||||
@ -41,7 +42,7 @@ bitflags::bitflags! {
|
|||||||
// output in ascending order
|
// output in ascending order
|
||||||
const ASC = 16;
|
const ASC = 16;
|
||||||
// Both start and stop
|
// Both start and stop
|
||||||
const BOTH = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits;
|
const BOTH = QueryPlanFlags::START.bits() | QueryPlanFlags::STOP.bits();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ unsafe impl<'vtab> VTab<'vtab> for SeriesTab {
|
|||||||
}
|
}
|
||||||
if idx_num.contains(QueryPlanFlags::BOTH) {
|
if idx_num.contains(QueryPlanFlags::BOTH) {
|
||||||
// Both start= and stop= boundaries are available.
|
// Both start= and stop= boundaries are available.
|
||||||
//#[allow(clippy::bool_to_int_with_if)]
|
#[allow(clippy::bool_to_int_with_if)]
|
||||||
info.set_estimated_cost(f64::from(
|
info.set_estimated_cost(f64::from(
|
||||||
2 - if idx_num.contains(QueryPlanFlags::STEP) {
|
2 - if idx_num.contains(QueryPlanFlags::STEP) {
|
||||||
1
|
1
|
||||||
@ -199,19 +200,19 @@ unsafe impl VTabCursor for SeriesTabCursor<'_> {
|
|||||||
let mut idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
|
let mut idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
if idx_num.contains(QueryPlanFlags::START) {
|
if idx_num.contains(QueryPlanFlags::START) {
|
||||||
self.min_value = args.get(i)?;
|
self.min_value = args.get::<Option<_>>(i)?.unwrap_or_default();
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
self.min_value = 0;
|
self.min_value = 0;
|
||||||
}
|
}
|
||||||
if idx_num.contains(QueryPlanFlags::STOP) {
|
if idx_num.contains(QueryPlanFlags::STOP) {
|
||||||
self.max_value = args.get(i)?;
|
self.max_value = args.get::<Option<_>>(i)?.unwrap_or_default();
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
self.max_value = 0xffff_ffff;
|
self.max_value = 0xffff_ffff;
|
||||||
}
|
}
|
||||||
if idx_num.contains(QueryPlanFlags::STEP) {
|
if idx_num.contains(QueryPlanFlags::STEP) {
|
||||||
self.step = args.get(i)?;
|
self.step = args.get::<Option<_>>(i)?.unwrap_or_default();
|
||||||
if self.step == 0 {
|
if self.step == 0 {
|
||||||
self.step = 1;
|
self.step = 1;
|
||||||
} else if self.step < 0 {
|
} else if self.step < 0 {
|
||||||
@ -315,6 +316,26 @@ mod test {
|
|||||||
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
assert_eq!(vec![30, 25, 20, 15, 10, 5, 0], series);
|
assert_eq!(vec![30, 25, 20, 15, 10, 5, 0], series);
|
||||||
|
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(NULL)")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
let empty = Vec::<i32>::new();
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(5,NULL)")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(5,10,NULL)")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(NULL,10,2)")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(5,NULL,2)")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
let mut s = db.prepare("SELECT * FROM generate_series(NULL) ORDER BY value DESC")?;
|
||||||
|
let series: Vec<i32> = s.query([])?.map(|r| r.get(0)).collect()?;
|
||||||
|
assert_eq!(empty, series);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
///! Port of C [vtablog](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/vtablog.c)
|
//! Port of C [vtablog](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/vtablog.c)
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
@ -56,8 +56,7 @@ impl VTabLog {
|
|||||||
"schema" => {
|
"schema" => {
|
||||||
if schema.is_some() {
|
if schema.is_some() {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"more than one '{}' parameter",
|
"more than one '{param}' parameter"
|
||||||
param
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
schema = Some(value.to_owned())
|
schema = Some(value.to_owned())
|
||||||
@ -65,8 +64,7 @@ impl VTabLog {
|
|||||||
"rows" => {
|
"rows" => {
|
||||||
if n_row.is_some() {
|
if n_row.is_some() {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"more than one '{}' parameter",
|
"more than one '{param}' parameter"
|
||||||
param
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if let Ok(n) = i64::from_str(value) {
|
if let Ok(n) = i64::from_str(value) {
|
||||||
@ -75,8 +73,7 @@ impl VTabLog {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(Error::ModuleError(format!(
|
return Err(Error::ModuleError(format!(
|
||||||
"unrecognized parameter '{}'",
|
"unrecognized parameter '{param}'"
|
||||||
param
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,13 +283,13 @@ mod test {
|
|||||||
let mut stmt = db.prepare("SELECT * FROM log;")?;
|
let mut stmt = db.prepare("SELECT * FROM log;")?;
|
||||||
let mut rows = stmt.query([])?;
|
let mut rows = stmt.query([])?;
|
||||||
while rows.next()?.is_some() {}
|
while rows.next()?.is_some() {}
|
||||||
db.execute("DELETE FROM log WHERE a = ?", ["a1"])?;
|
db.execute("DELETE FROM log WHERE a = ?1", ["a1"])?;
|
||||||
db.execute(
|
db.execute(
|
||||||
"INSERT INTO log (a, b, c) VALUES (?, ?, ?)",
|
"INSERT INTO log (a, b, c) VALUES (?1, ?2, ?3)",
|
||||||
["a", "b", "c"],
|
["a", "b", "c"],
|
||||||
)?;
|
)?;
|
||||||
db.execute(
|
db.execute(
|
||||||
"UPDATE log SET b = ?, c = ? WHERE a = ?",
|
"UPDATE log SET b = ?1, c = ?2 WHERE a = ?3",
|
||||||
["bn", "cn", "a1"],
|
["bn", "cn", "a1"],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user