Merge remote-tracking branch 'upstream/master' into captured_identifiers

This commit is contained in:
gwenn 2023-04-11 19:43:00 +02:00
commit a927ce1bb6
42 changed files with 3970 additions and 2064 deletions

9
.github/dependabot.yml vendored Normal file
View 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"

View File

@ -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
@ -48,7 +51,7 @@ jobs:
# 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

View File

@ -1,7 +1,7 @@
[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 = "2018"
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
@ -112,13 +112,12 @@ 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.2" 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 }
@ -137,7 +136,7 @@ bencher = "0.1"
[dependencies.libsqlite3-sys] [dependencies.libsqlite3-sys]
path = "libsqlite3-sys" path = "libsqlite3-sys"
version = "0.25.0" version = "0.26.0"
# FIXME optional # FIXME optional
[dependencies.rusqlite-macros] [dependencies.rusqlite-macros]

View File

@ -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:
@ -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.)

View File

@ -1 +1,2 @@
doc-valid-idents = ["SQLite", "lang_transaction"] doc-valid-idents = ["SQLite", "lang_transaction"]
msrv = "1.55.0"

View File

@ -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.62", optional = true, default-features = false, features = ["runtime"] } bindgen = { version = "0.64", 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 }

View File

@ -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,
} }

View File

@ -181,16 +181,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");
@ -503,6 +497,11 @@ mod bindings {
None None
} }
} }
fn item_name(&self, original_item_name: &str) -> Option<String> {
original_item_name
.strip_prefix("sqlite3_index_info_")
.map(|s| s.to_owned())
}
} }
// Are we generating the bundled bindings? Used to avoid emitting things // Are we generating the bundled bindings? Used to avoid emitting things
@ -524,6 +523,34 @@ mod bindings {
.trust_clang_mangling(false) .trust_clang_mangling(false)
.header(header.clone()) .header(header.clone())
.parse_callbacks(Box::new(SqliteTypeChooser)) .parse_callbacks(Box::new(SqliteTypeChooser))
.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;
}"#,
)
.rustfmt_bindings(true); .rustfmt_bindings(true);
if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) { if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) {

View File

@ -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,
} }

View File

@ -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,8 +41443,11 @@ 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 ){
rc = SQLITE_IOERR_GETTEMPPATH;
}else{
do{ do{
u64 r; u64 r;
sqlite3_randomness(sizeof(r), &r); sqlite3_randomness(sizeof(r), &r);
@ -41441,9 +41455,14 @@ static int unixGetTempname(int nBuf, char *zBuf){
zBuf[nBuf-2] = 0; zBuf[nBuf-2] = 0;
sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
zDir, r, 0); zDir, r, 0);
if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){
rc = SQLITE_ERROR;
break;
}
}while( osAccess(zBuf,0)==0 ); }while( osAccess(zBuf,0)==0 );
return SQLITE_OK; }
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-1; i++){
int iB = i;
for(j=i+1; j<nNew; j++){
if( apNew[j]->pgno < apNew[iB]->pgno ) iB = j;
} }
}
for(i=0; i<nNew; i++){ /* If apNew[i] has a page number that is bigger than any of the
int iBest = 0; /* aPgno[] index of page number to use */ ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent
for(j=1; j<nNew; j++){ ** entry that has the smallest page number (which we know to be
if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j; ** entry apNew[iB]).
} */
pgno = aPgOrder[iBest]; if( iB!=i ){
aPgOrder[iBest] = 0xffffffff; Pgno pgnoA = apNew[i]->pgno;
if( iBest!=i ){ Pgno pgnoB = apNew[iB]->pgno;
if( iBest>i ){ Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1;
sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0); u16 fgA = apNew[i]->pDbPage->flags;
} u16 fgB = apNew[iB]->pDbPage->flags;
sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]); sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB);
apNew[i]->pgno = pgno; 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);
} }
/* /*

View File

@ -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

View File

@ -1,9 +1,33 @@
/* automatically generated by rust-bindgen 0.62.0 */ /* automatically generated by rust-bindgen 0.64.0 */
pub const SQLITE_VERSION: &[u8; 7usize] = b"3.40.0\0"; extern "C" {
pub const SQLITE_VERSION_NUMBER: i32 = 3040000; 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.41.2\0";
pub const SQLITE_VERSION_NUMBER: i32 = 3041002;
pub const SQLITE_SOURCE_ID: &[u8; 85usize] = pub const SQLITE_SOURCE_ID: &[u8; 85usize] =
b"2022-11-16 12:10:08 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318\0"; b"2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da\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;
@ -106,6 +130,7 @@ pub const SQLITE_CONSTRAINT_PINNED: i32 = 2835;
pub const SQLITE_CONSTRAINT_DATATYPE: i32 = 3091; pub const SQLITE_CONSTRAINT_DATATYPE: i32 = 3091;
pub const SQLITE_NOTICE_RECOVER_WAL: i32 = 283; pub const SQLITE_NOTICE_RECOVER_WAL: i32 = 283;
pub const SQLITE_NOTICE_RECOVER_ROLLBACK: i32 = 539; pub const SQLITE_NOTICE_RECOVER_ROLLBACK: i32 = 539;
pub const SQLITE_NOTICE_RBU: i32 = 795;
pub const SQLITE_WARNING_AUTOINDEX: i32 = 284; pub const SQLITE_WARNING_AUTOINDEX: i32 = 284;
pub const SQLITE_AUTH_USER: i32 = 279; pub const SQLITE_AUTH_USER: i32 = 279;
pub const SQLITE_OK_LOAD_PERMANENTLY: i32 = 256; pub const SQLITE_OK_LOAD_PERMANENTLY: i32 = 256;
@ -196,6 +221,7 @@ pub const SQLITE_FCNTL_RESERVE_BYTES: i32 = 38;
pub const SQLITE_FCNTL_CKPT_START: i32 = 39; pub const SQLITE_FCNTL_CKPT_START: i32 = 39;
pub const SQLITE_FCNTL_EXTERNAL_READER: i32 = 40; pub const SQLITE_FCNTL_EXTERNAL_READER: i32 = 40;
pub const SQLITE_FCNTL_CKSM_FILE: i32 = 41; pub const SQLITE_FCNTL_CKSM_FILE: i32 = 41;
pub const SQLITE_FCNTL_RESET_CACHE: i32 = 42;
pub const SQLITE_GET_LOCKPROXYFILE: i32 = 2; pub const SQLITE_GET_LOCKPROXYFILE: i32 = 2;
pub const SQLITE_SET_LOCKPROXYFILE: i32 = 3; pub const SQLITE_SET_LOCKPROXYFILE: i32 = 3;
pub const SQLITE_LAST_ERRNO: i32 = 4; pub const SQLITE_LAST_ERRNO: i32 = 4;
@ -447,6 +473,9 @@ pub const SQLITE_SCANSTAT_EST: i32 = 2;
pub const SQLITE_SCANSTAT_NAME: i32 = 3; pub const SQLITE_SCANSTAT_NAME: i32 = 3;
pub const SQLITE_SCANSTAT_EXPLAIN: i32 = 4; pub const SQLITE_SCANSTAT_EXPLAIN: i32 = 4;
pub const SQLITE_SCANSTAT_SELECTID: i32 = 5; pub const SQLITE_SCANSTAT_SELECTID: i32 = 5;
pub const SQLITE_SCANSTAT_PARENTID: i32 = 6;
pub const SQLITE_SCANSTAT_NCYCLE: i32 = 7;
pub const SQLITE_SCANSTAT_COMPLEX: i32 = 1;
pub const SQLITE_SERIALIZE_NOCOPY: i32 = 1; pub const SQLITE_SERIALIZE_NOCOPY: i32 = 1;
pub const SQLITE_DESERIALIZE_FREEONCLOSE: i32 = 1; pub const SQLITE_DESERIALIZE_FREEONCLOSE: i32 = 1;
pub const SQLITE_DESERIALIZE_RESIZEABLE: i32 = 2; pub const SQLITE_DESERIALIZE_RESIZEABLE: i32 = 2;
@ -854,6 +883,9 @@ extern "C" {
extern "C" { extern "C" {
pub fn sqlite3_interrupt(arg1: *mut sqlite3); pub fn sqlite3_interrupt(arg1: *mut sqlite3);
} }
extern "C" {
pub fn sqlite3_is_interrupted(arg1: *mut sqlite3) -> ::std::os::raw::c_int;
}
extern "C" { extern "C" {
pub fn sqlite3_complete(sql: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int; pub fn sqlite3_complete(sql: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int;
} }
@ -2001,16 +2033,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();
} }
@ -2154,10 +2176,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,
@ -2169,7 +2191,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,
@ -2177,13 +2199,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,
} }
@ -2710,6 +2732,15 @@ extern "C" {
pOut: *mut ::std::os::raw::c_void, pOut: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int; ) -> ::std::os::raw::c_int;
} }
extern "C" {
pub fn sqlite3_stmt_scanstatus_v2(
pStmt: *mut sqlite3_stmt,
idx: ::std::os::raw::c_int,
iScanStatusOp: ::std::os::raw::c_int,
flags: ::std::os::raw::c_int,
pOut: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int;
}
extern "C" { extern "C" {
pub fn sqlite3_stmt_scanstatus_reset(arg1: *mut sqlite3_stmt); pub fn sqlite3_stmt_scanstatus_reset(arg1: *mut sqlite3_stmt);
} }

File diff suppressed because it is too large Load Diff

View File

@ -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.40.0" #define SQLITE_VERSION "3.41.2"
#define SQLITE_VERSION_NUMBER 3040000 #define SQLITE_VERSION_NUMBER 3041002
#define SQLITE_SOURCE_ID "2022-11-16 12:10:08 89c459e766ea7e9165d0beeb124708b955a4950d0f4792f457465d71b158d318" #define SQLITE_SOURCE_ID "2023-03-22 11:56:21 0d1fc92f94cb6b76bffe3ec34d69cffde2924203304e8ffc4155597af0c191da"
/* /*
** 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))
@ -1175,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
@ -1188,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
@ -1234,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
@ -2177,7 +2184,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>
@ -2327,8 +2334,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
@ -2667,8 +2678,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
@ -3286,8 +3301,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>
@ -3350,7 +3365,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.
** **
@ -3375,6 +3390,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*);
@ -3411,13 +3433,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
@ -5398,10 +5425,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>
@ -5542,16 +5580,6 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** then the conversion is performed. Otherwise no conversion occurs. ** then the conversion is performed. Otherwise no conversion occurs.
** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^
** **
** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current 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).
**
** ^Within the [xUpdate] method of a [virtual table], the ** ^Within the [xUpdate] method of a [virtual table], the
** sqlite3_value_nochange(X) interface returns true if and only if ** sqlite3_value_nochange(X) interface returns true if and only if
** the column corresponding to X is unchanged by the UPDATE operation ** the column corresponding to X is unchanged by the UPDATE operation
@ -5616,6 +5644,27 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); 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*); SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
/* /*
@ -6996,15 +7045,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
*/ */
@ -7123,10 +7163,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
@ -7246,7 +7286,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
@ -7405,16 +7445,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}
@ -9618,7 +9648,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
@ -9775,21 +9805,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>
** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal); ** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
** &nbsp; rc==SQLITE_OK && pVal ** &nbsp; rc==SQLITE_OK && pVal;
** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal) ** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
** &nbsp; ){ ** &nbsp; ){
** &nbsp; // do something with pVal ** &nbsp; // do something with pVal
@ -9887,6 +9916,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
@ -9914,12 +9947,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
@ -9928,12 +9973,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.
@ -9944,19 +9991,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()]
*/ */
@ -9966,6 +10019,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
@ -10056,6 +10122,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
@ -10461,6 +10531,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

View File

@ -359,6 +359,8 @@ struct sqlite3_api_routines {
const char *(*db_name)(sqlite3*,int); const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */ /* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*); int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
}; };
/* /*
@ -685,6 +687,8 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_db_name sqlite3_api->db_name #define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */ /* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding #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)

View File

@ -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() }

View File

@ -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-3400000 SQLITE=sqlite-amalgamation-3410200
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"

View File

@ -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"

View File

@ -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)],
//! )?; //! )?;
//! //!

View File

@ -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(())
} }

View File

@ -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(())

View File

@ -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,
@ -371,15 +371,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);
}
}
} }
} }

View File

@ -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;
@ -142,13 +142,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]`.
@ -356,7 +349,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 {}
@ -453,7 +445,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()),
}) })
} }
@ -478,7 +469,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()),
}) })
} }
@ -560,7 +550,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),
/// } /// }
@ -606,12 +596,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
@ -624,26 +625,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
@ -697,28 +678,6 @@ impl Connection {
self.query_row(sql, [], |r| r.get(0)) self.query_row(sql, [], |r| r.get(0))
} }
/// Convenience method to execute a query with named parameter(s) that is
/// expected to return a single row.
///
/// If the query returns more than one row, all rows except the first are
/// ignored.
///
/// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
/// query truly is optional, you can call `.optional()` 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
/// single row, and execute a mapping via `f` on that returned row with /// single row, and execute a mapping via `f` on that returned row with
/// the possibility of failure. The `Result` type of `f` must implement /// the possibility of failure. The `Result` type of `f` must implement
@ -765,7 +724,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(())
@ -947,12 +906,30 @@ 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),
}) })
} }
@ -1005,7 +982,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()
} }
} }
@ -1078,6 +1055,7 @@ bitflags::bitflags! {
/// 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.
@ -1196,16 +1174,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");
@ -1307,6 +1275,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";
@ -1432,8 +1415,8 @@ 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!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?); assert_eq!(3i32, db.one_column::<i32>("SELECT SUM(x) FROM foo")?);
Ok(()) Ok(())
@ -1443,7 +1426,7 @@ 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,
@ -1488,7 +1471,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);
@ -1497,7 +1480,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);
@ -1509,12 +1492,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();
@ -1779,7 +1762,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);
@ -1819,6 +1802,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::*;
@ -2045,7 +2038,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(())
@ -2057,10 +2050,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,

View File

@ -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...

View File

@ -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'
} }
@ -379,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"])?;

View File

@ -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 }

View File

@ -338,20 +338,6 @@ impl<'stmt> Row<'stmt> {
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> {

View File

@ -779,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()
} }
@ -792,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)?;
@ -852,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);
@ -887,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);
@ -903,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(())

View File

@ -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_named(&[(":name", &"one")])?;
let id: Result<i32> = rows.next()?.unwrap().get(0);
assert_eq!(Ok(1), id);
}
// plain api
{
let mut rows = stmt.query(&[(":name", "one")])?; let mut rows = stmt.query(&[(":name", "one")])?;
let id: Result<i32> = rows.next()?.unwrap().get(0); let id: Result<i32> = rows.next()?.unwrap().get(0);
assert_eq!(Ok(1), id); 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,18 +968,6 @@ 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_named(&[(":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);
}
// plain api
{
let mut rows = stmt.query_map(&[(":name", "one")], |row| { let mut rows = stmt.query_map(&[(":name", "one")], |row| {
let id: Result<i32> = row.get(0); let id: Result<i32> = row.get(0);
id.map(|i| 2 * i) id.map(|i| 2 * i)
@ -1129,42 +975,6 @@ mod test {
let doubled_id: i32 = rows.next().unwrap()?; let doubled_id: i32 = rows.next().unwrap()?;
assert_eq!(2, doubled_id); 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()?;
assert_eq!(1, 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,14 +1013,13 @@ 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> = db.one_column("SELECT y FROM test WHERE x = 'one'")?; let result: Option<String> = db.one_column("SELECT y FROM test WHERE x = 'one'")?;
assert!(result.is_none()); assert!(result.is_none());
@ -1267,7 +1076,7 @@ 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() {
@ -1307,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])?);
@ -1316,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),
)?; )?;
@ -1339,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");
@ -1358,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(())
@ -1395,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(())
@ -1497,7 +1306,7 @@ 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.one_column("SELECT x FROM foo")?; let actual: String = db.one_column("SELECT x FROM foo")?;
assert_eq!(expected, actual); assert_eq!(expected, actual);
Ok(()) Ok(())
@ -1507,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(())
} }
@ -1521,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<()> {

View File

@ -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();

View File

@ -718,7 +718,7 @@ 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<()> {

View File

@ -176,7 +176,7 @@ mod 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_opt(2016, 2, 23).unwrap(); 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.one_column("SELECT t FROM foo")?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("2016-02-23", s); assert_eq!("2016-02-23", s);
@ -189,7 +189,7 @@ mod 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_opt(23, 56, 4).unwrap(); 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.one_column("SELECT t FROM foo")?; let s: String = db.one_column("SELECT t FROM foo")?;
assert_eq!("23:56:04", s); assert_eq!("23:56:04", s);
@ -205,7 +205,7 @@ mod test {
let time = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); 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.one_column("SELECT t FROM foo")?; 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);
@ -226,7 +226,7 @@ mod test {
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.one_column("SELECT t FROM foo")?; 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);
@ -253,7 +253,7 @@ mod test {
let dt = NaiveDateTime::new(date, time); let dt = NaiveDateTime::new(date, time);
let local = Local.from_local_datetime(&dt).single().unwrap(); let local = Local.from_local_datetime(&dt).single().unwrap();
db.execute("INSERT INTO foo (t) VALUES (?)", [local])?; 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.one_column("SELECT t FROM foo")?; let s: String = db.one_column("SELECT t FROM foo")?;
@ -269,7 +269,7 @@ 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.one_column("SELECT t FROM foo")?; let s: String = db.one_column("SELECT t FROM foo")?;
@ -298,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(())
} }
@ -306,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(())
} }

View File

@ -244,7 +244,7 @@ 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),
@ -254,7 +254,7 @@ mod test {
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()
); );

View File

@ -102,7 +102,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,7 +153,7 @@ 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.one_column("SELECT b FROM foo")?; let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
assert_eq!(v, v1234); assert_eq!(v, v1234);
@ -165,7 +165,7 @@ 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.one_column("SELECT b FROM foo")?; let v: Vec<u8> = db.one_column("SELECT b FROM foo")?;
assert_eq!(v, empty); assert_eq!(v, empty);
@ -177,7 +177,7 @@ 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.one_column("SELECT t FROM foo")?; let from: String = db.one_column("SELECT t FROM foo")?;
assert_eq!(from, s); assert_eq!(from, s);
@ -189,7 +189,7 @@ 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.one_column("SELECT t FROM foo")?; let from: String = db.one_column("SELECT t FROM foo")?;
assert_eq!(from, s); assert_eq!(from, s);
@ -200,7 +200,7 @@ 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!(10i64, db.one_column::<i64>("SELECT i FROM foo")?); assert_eq!(10i64, db.one_column::<i64>("SELECT i FROM foo")?);
Ok(()) Ok(())
@ -213,8 +213,8 @@ mod test {
let s = Some("hello, world!"); let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]); let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", [&s])?; db.execute("INSERT INTO foo(t) VALUES (?1)", [&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([])?;

View File

@ -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,7 +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::Value; 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()?;
@ -41,7 +79,7 @@ mod test {
let json = r#"{"foo": 13, "bar": "baz"}"#; let json = r#"{"foo": 13, "bar": "baz"}"#;
let data: 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()],
)?; )?;
@ -51,4 +89,47 @@ mod test {
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(())
}
} }

View File

@ -91,7 +91,7 @@ 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.one_column("SELECT t FROM foo")?; let from: OffsetDateTime = db.one_column("SELECT t FROM foo")?;
@ -143,7 +143,7 @@ mod test {
Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()), Ok(OffsetDateTime::parse("2013-10-07T04:23:19.120-04:00", &Rfc3339).unwrap()),
), ),
] { ] {
let result: Result<OffsetDateTime> = db.query_row("SELECT ?", [s], |r| r.get(0)); let result: Result<OffsetDateTime> = db.query_row("SELECT ?1", [s], |r| r.get(0));
assert_eq!(result, t); assert_eq!(result, t);
} }
Ok(()) Ok(())
@ -160,7 +160,7 @@ mod test {
#[test] #[test]
fn test_param() -> Result<()> { fn test_param() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
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 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(())
} }

View File

@ -368,10 +368,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],
)?; )?;
@ -410,11 +410,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();

View File

@ -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"],

View File

@ -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));

View File

@ -619,7 +619,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 +637,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

View File

@ -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();
} }
} }

View File

@ -286,13 +286,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(())