Merge branch 'rusqlite:master' into wasm

This commit is contained in:
Simon Choi 2023-12-31 10:51:26 +01:00 committed by GitHub
commit 7bc2214ec6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 25720 additions and 4382 deletions

View File

@ -65,6 +65,11 @@ jobs:
- run: cargo test --features 'bundled-full session buildtime_bindgen' --all-targets --workspace --verbose - run: cargo test --features 'bundled-full session buildtime_bindgen' --all-targets --workspace --verbose
- run: cargo test --features 'bundled-full session buildtime_bindgen' --doc --workspace --verbose - run: cargo test --features 'bundled-full session buildtime_bindgen' --doc --workspace --verbose
- name: loadable extension
run: |
cargo build --example loadable_extension --features "loadable_extension functions trace"
cargo run --example load_extension --features "load_extension bundled functions trace"
# TODO: move into own action for better caching # TODO: move into own action for better caching
- name: Static build - name: Static build
# Do we expect this to work / should we test with gnu toolchain? # Do we expect this to work / should we test with gnu toolchain?

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.29.0" version = "0.30.0"
authors = ["The rusqlite developers"] authors = ["The rusqlite developers"]
edition = "2021" edition = "2021"
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
@ -50,6 +50,7 @@ bundled-sqlcipher = ["libsqlite3-sys/bundled-sqlcipher", "bundled"]
bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"] bundled-sqlcipher-vendored-openssl = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "bundled-sqlcipher"]
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"] buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
limits = [] limits = []
loadable_extension = ["libsqlite3-sys/loadable_extension"]
hooks = [] hooks = []
i128_blob = [] i128_blob = []
sqlcipher = ["libsqlite3-sys/sqlcipher"] sqlcipher = ["libsqlite3-sys/sqlcipher"]
@ -124,6 +125,7 @@ fallible-iterator = "0.3"
fallible-streaming-iterator = "0.1" fallible-streaming-iterator = "0.1"
uuid = { version = "1.0", optional = true } uuid = { version = "1.0", optional = true }
smallvec = "1.6.1" smallvec = "1.6.1"
rusqlite-macros = { path = "rusqlite-macros", version = "0.1.0", optional = true }
[dev-dependencies] [dev-dependencies]
doc-comment = "0.3" doc-comment = "0.3"
@ -138,7 +140,7 @@ bencher = "0.1"
[dependencies.libsqlite3-sys] [dependencies.libsqlite3-sys]
path = "libsqlite3-sys" path = "libsqlite3-sys"
version = "0.26.0" version = "0.27.0"
[[test]] [[test]]
name = "config_log" name = "config_log"
@ -158,8 +160,17 @@ harness = false
name = "exec" name = "exec"
harness = false harness = false
[[example]]
name = "loadable_extension"
crate-type = ["cdylib"]
required-features = ["loadable_extension", "functions", "trace"]
[[example]]
name = "load_extension"
required-features = ["load_extension", "bundled", "functions", "trace"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["modern-full"] features = ["modern-full", "rusqlite-macros"]
all-features = false all-features = false
no-default-features = true no-default-features = true
default-target = "x86_64-unknown-linux-gnu" default-target = "x86_64-unknown-linux-gnu"

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.29.0", features = ["bundled"] } rusqlite = { version = "0.30.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.41.2 (as of `rusqlite` 0.29.0 / `libsqlite3-sys` is currently SQLite 3.44.0 (as of `rusqlite` 0.30.0 / `libsqlite3-sys`
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: 0.27.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.29.0" version = "0.30.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

@ -0,0 +1,22 @@
//! Ensure loadable_extension.rs works.
use rusqlite::{Connection, Result};
fn main() -> Result<()> {
let db = Connection::open_in_memory()?;
unsafe {
db.load_extension_enable()?;
#[cfg(not(windows))]
db.load_extension("target/debug/examples/libloadable_extension", None)?;
#[cfg(windows)]
db.load_extension("target/debug/examples/loadable_extension", None)?;
db.load_extension_disable()?;
}
let str = db.query_row("SELECT rusqlite_test_function()", [], |row| {
row.get::<_, String>(0)
})?;
assert_eq!(&str, "Rusqlite extension loaded correctly!");
Ok(())
}

View File

@ -0,0 +1,50 @@
//! Adaptation of https://sqlite.org/loadext.html#programming_loadable_extensions
use std::os::raw::{c_char, c_int};
use rusqlite::ffi;
use rusqlite::functions::FunctionFlags;
use rusqlite::types::{ToSqlOutput, Value};
use rusqlite::{to_sqlite_error, Connection, Result};
/// # build
/// ```sh
/// cargo build --example loadable_extension --features "loadable_extension functions trace"
/// ```
/// # test
/// ```sh
/// sqlite> .log on
/// sqlite> .load target/debug/examples/libloadable_extension.so
/// (28) Rusqlite extension initialized
/// sqlite> SELECT rusqlite_test_function();
/// Rusqlite extension loaded correctly!
/// ```
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn sqlite3_extension_init(
db: *mut ffi::sqlite3,
pz_err_msg: *mut *mut c_char,
p_api: *mut ffi::sqlite3_api_routines,
) -> c_int {
if p_api.is_null() {
return ffi::SQLITE_ERROR;
} else if let Err(err) = extension_init(db, p_api) {
return unsafe { to_sqlite_error(&err, pz_err_msg) };
}
ffi::SQLITE_OK
}
fn extension_init(db: *mut ffi::sqlite3, p_api: *mut ffi::sqlite3_api_routines) -> Result<()> {
let db = unsafe { Connection::extension_init2(db, p_api)? };
db.create_scalar_function(
"rusqlite_test_function",
0,
FunctionFlags::SQLITE_DETERMINISTIC,
|_ctx| {
Ok(ToSqlOutput::Owned(Value::Text(
"Rusqlite extension loaded correctly!".to_string(),
)))
},
)?;
rusqlite::trace::log(ffi::SQLITE_WARNING, "Rusqlite extension initialized");
Ok(())
}

View File

@ -0,0 +1,28 @@
# Persons example
## Run
```
$ cargo run --example persons
```
## Run (wasm32-wasi)
### Requisites
- [wasi-sdk](https://github.com/WebAssembly/wasi-sdk)
- [wasmtime](https://wasmtime.dev/)
```
# Set to wasi-sdk directory
$ export WASI_SDK_PATH=`<wasi-sdk-path>`
$ export CC_wasm32_wasi="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot"
# Build
$ cargo build --example persons --target wasm32-wasi --release --features bundled
# Run
$ wasmtime target/wasm32-wasi/release/examples/persons.wasm
Found persons:
ID: 1, Name: Steven
ID: 2, Name: John
ID: 3, Name: Alex
```

42
examples/persons/main.rs Normal file
View File

@ -0,0 +1,42 @@
extern crate rusqlite;
use rusqlite::{Connection, Result};
struct Person {
id: i32,
name: String,
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute(
"CREATE TABLE IF NOT EXISTS persons (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)",
(), // empty list of parameters.
)?;
conn.execute(
"INSERT INTO persons (name) VALUES (?1), (?2), (?3)",
["Steven", "John", "Alex"].map(|n| n.to_string()),
)?;
let mut stmt = conn.prepare("SELECT id, name FROM persons")?;
let rows = stmt.query_map([], |row| {
Ok(Person {
id: row.get(0)?,
name: row.get(1)?,
})
})?;
println!("Found persons:");
for person in rows {
match person {
Ok(p) => println!("ID: {}, Name: {}", p.id, p.name),
Err(e) => eprintln!("Error: {e:?}"),
}
}
Ok(())
}

View File

@ -1,8 +1,8 @@
[package] [package]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.26.0" version = "0.27.0"
authors = ["The rusqlite developers"] authors = ["The rusqlite developers"]
edition = "2018" edition = "2021"
repository = "https://github.com/rusqlite/rusqlite" repository = "https://github.com/rusqlite/rusqlite"
description = "Native bindings to the libsqlite3 library" description = "Native bindings to the libsqlite3 library"
license = "MIT" license = "MIT"
@ -23,6 +23,7 @@ min_sqlite_version_3_14_0 = ["pkg-config", "vcpkg"]
# Bundle only the bindings file. Note that this does nothing if # Bundle only the bindings file. Note that this does nothing if
# `buildtime_bindgen` is enabled. # `buildtime_bindgen` is enabled.
bundled_bindings = [] bundled_bindings = []
loadable_extension = ["prettyplease", "quote", "syn"]
# sqlite3_unlock_notify >= 3.6.12 # sqlite3_unlock_notify >= 3.6.12
unlock_notify = [] unlock_notify = []
# 3.13.0 # 3.13.0
@ -44,7 +45,13 @@ winsqlite3 = []
openssl-sys = { version = "0.9", optional = true } openssl-sys = { version = "0.9", optional = true }
[build-dependencies] [build-dependencies]
bindgen = { version = "0.66", optional = true, default-features = false, features = ["runtime"] } bindgen = { version = "0.69", 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 }
# for loadable_extension:
prettyplease = {version = "0.2", optional = true }
# like bindgen
quote = { version = "1", optional = true, default-features = false }
# like bindgen
syn = { version = "2.0", optional = true, features = ["full", "extra-traits", "visit-mut"] }

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,12 @@ fn is_compiler(compiler_name: &str) -> bool {
/// Copy bindgen file from `dir` to `out_path`. /// Copy bindgen file from `dir` to `out_path`.
fn copy_bindings<T: AsRef<Path>>(dir: &str, bindgen_name: &str, out_path: T) { fn copy_bindings<T: AsRef<Path>>(dir: &str, bindgen_name: &str, out_path: T) {
std::fs::copy(format!("{dir}/{bindgen_name}"), out_path) let from = if cfg!(feature = "loadable_extension") {
.expect("Could not copy bindings to output directory"); format!("{dir}/{bindgen_name}_ext.rs")
} else {
format!("{dir}/{bindgen_name}.rs")
};
std::fs::copy(from, out_path).expect("Could not copy bindings to output directory");
} }
fn main() { fn main() {
@ -38,12 +42,14 @@ fn main() {
if cfg!(feature = "in_gecko") { if cfg!(feature = "in_gecko") {
// When inside mozilla-central, we are included into the build with // When inside mozilla-central, we are included into the build with
// sqlite3.o directly, so we don't want to provide any linker arguments. // sqlite3.o directly, so we don't want to provide any linker arguments.
copy_bindings("sqlite3", "bindgen_bundled_version.rs", out_path); copy_bindings("sqlite3", "bindgen_bundled_version", out_path);
return; return;
} }
println!("cargo:rerun-if-env-changed=LIBSQLITE3_SYS_USE_PKG_CONFIG"); println!("cargo:rerun-if-env-changed=LIBSQLITE3_SYS_USE_PKG_CONFIG");
if env::var_os("LIBSQLITE3_SYS_USE_PKG_CONFIG").map_or(false, |s| s != "0") { if env::var_os("LIBSQLITE3_SYS_USE_PKG_CONFIG").map_or(false, |s| s != "0")
|| cfg!(feature = "loadable_extension")
{
build_linked::main(&out_dir, &out_path); build_linked::main(&out_dir, &out_path);
} else if cfg!(all( } else if cfg!(all(
feature = "sqlcipher", feature = "sqlcipher",
@ -106,7 +112,7 @@ mod build_bundled {
} }
#[cfg(not(feature = "buildtime_bindgen"))] #[cfg(not(feature = "buildtime_bindgen"))]
{ {
super::copy_bindings(lib_name, "bindgen_bundled_version.rs", out_path); super::copy_bindings(lib_name, "bindgen_bundled_version", out_path);
} }
println!("cargo:rerun-if-changed={lib_name}/sqlite3.c"); println!("cargo:rerun-if-changed={lib_name}/sqlite3.c");
println!("cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c"); println!("cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c");
@ -233,11 +239,16 @@ mod build_bundled {
if !win_target() { if !win_target() {
cfg.flag("-DHAVE_LOCALTIME_R"); cfg.flag("-DHAVE_LOCALTIME_R");
} }
// Target wasm32-wasi can't compile the default VFS
if env::var("TARGET").map_or(false, |v| v == "wasm32-wasi") { if env::var("TARGET").map_or(false, |v| v == "wasm32-wasi") {
cfg.flag("-DSQLITE_OS_OTHER") cfg.flag("-USQLITE_THREADSAFE")
.flag("-DSQLITE_THREADSAFE=0")
// https://github.com/rust-lang/rust/issues/74393 // https://github.com/rust-lang/rust/issues/74393
.flag("-DLONGDOUBLE_TYPE=double"); .flag("-DLONGDOUBLE_TYPE=double")
.flag("-D_WASI_EMULATED_MMAN")
.flag("-D_WASI_EMULATED_GETPID")
.flag("-D_WASI_EMULATED_SIGNAL")
.flag("-D_WASI_EMULATED_PROCESS_CLOCKS");
if cfg!(feature = "wasm32-wasi-vfs") { if cfg!(feature = "wasm32-wasi-vfs") {
cfg.file("sqlite3/wasm32-wasi-vfs.c"); cfg.file("sqlite3/wasm32-wasi-vfs.c");
} }
@ -363,16 +374,30 @@ impl From<HeaderLocation> for String {
HeaderLocation::FromEnvironment => { HeaderLocation::FromEnvironment => {
let prefix = env_prefix(); let prefix = env_prefix();
let mut header = env::var(format!("{prefix}_INCLUDE_DIR")).unwrap_or_else(|_| { let mut header = env::var(format!("{prefix}_INCLUDE_DIR")).unwrap_or_else(|_| {
panic!( panic!("{prefix}_INCLUDE_DIR must be set if {prefix}_LIB_DIR is set")
"{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", });
prefix, prefix header.push_str(if cfg!(feature = "loadable_extension") {
) "/sqlite3ext.h"
} else {
"/sqlite3.h"
}); });
header.push_str("/sqlite3.h");
header header
} }
HeaderLocation::Wrapper => "wrapper.h".into(), HeaderLocation::Wrapper => if cfg!(feature = "loadable_extension") {
HeaderLocation::FromPath(path) => format!("{}/sqlite3.h", path), "wrapper_ext.h"
} else {
"wrapper.h"
}
.into(),
HeaderLocation::FromPath(path) => format!(
"{}/{}",
path,
if cfg!(feature = "loadable_extension") {
"sqlite3ext.h"
} else {
"sqlite3.h"
}
),
} }
} }
} }
@ -399,12 +424,13 @@ mod build_linked {
// on buildtime_bindgen instead, but this is still supported as we // on buildtime_bindgen instead, but this is still supported as we
// have runtime version checks and there are good reasons to not // have runtime version checks and there are good reasons to not
// want to run bindgen. // want to run bindgen.
super::copy_bindings(lib_name(), "bindgen_bundled_version.rs", out_path); super::copy_bindings(lib_name(), "bindgen_bundled_version", out_path);
} else { } else {
bindings::write_to_out_dir(header, out_path); bindings::write_to_out_dir(header, out_path);
} }
} }
#[cfg(not(feature = "loadable_extension"))]
fn find_link_mode() -> &'static str { fn find_link_mode() -> &'static str {
// If the user specifies SQLITE3_STATIC (or SQLCIPHER_STATIC), do static // If the user specifies SQLITE3_STATIC (or SQLCIPHER_STATIC), do static
// linking, unless it's explicitly set to 0. // linking, unless it's explicitly set to 0.
@ -428,9 +454,11 @@ mod build_linked {
// `links=` value in our Cargo.toml) to get this value. This might be // `links=` value in our Cargo.toml) to get this value. This might be
// useful if you need to ensure whatever crypto library sqlcipher relies // useful if you need to ensure whatever crypto library sqlcipher relies
// on is available, for example. // on is available, for example.
#[cfg(not(feature = "loadable_extension"))]
println!("cargo:link-target={link_lib}"); println!("cargo:link-target={link_lib}");
if win_target() && cfg!(feature = "winsqlite3") { if win_target() && cfg!(feature = "winsqlite3") {
#[cfg(not(feature = "loadable_extension"))]
println!("cargo:rustc-link-lib=dylib={link_lib}"); println!("cargo:rustc-link-lib=dylib={link_lib}");
return HeaderLocation::Wrapper; return HeaderLocation::Wrapper;
} }
@ -440,6 +468,7 @@ mod build_linked {
// Try to use pkg-config to determine link commands // Try to use pkg-config to determine link commands
let pkgconfig_path = Path::new(&dir).join("pkgconfig"); let pkgconfig_path = Path::new(&dir).join("pkgconfig");
env::set_var("PKG_CONFIG_PATH", pkgconfig_path); env::set_var("PKG_CONFIG_PATH", pkgconfig_path);
#[cfg(not(feature = "loadable_extension"))]
if pkg_config::Config::new().probe(link_lib).is_err() { if pkg_config::Config::new().probe(link_lib).is_err() {
// Otherwise just emit the bare minimum link commands. // Otherwise just emit the bare minimum link commands.
println!("cargo:rustc-link-lib={}={link_lib}", find_link_mode()); println!("cargo:rustc-link-lib={}={link_lib}", find_link_mode());
@ -467,6 +496,7 @@ mod build_linked {
// request and hope that the library exists on the system paths. We used to // request and hope that the library exists on the system paths. We used to
// output /usr/lib explicitly, but that can introduce other linking problems; // output /usr/lib explicitly, but that can introduce other linking problems;
// see https://github.com/rusqlite/rusqlite/issues/207. // see https://github.com/rusqlite/rusqlite/issues/207.
#[cfg(not(feature = "loadable_extension"))]
println!("cargo:rustc-link-lib={}={link_lib}", find_link_mode()); println!("cargo:rustc-link-lib={}={link_lib}", find_link_mode());
HeaderLocation::Wrapper HeaderLocation::Wrapper
} }
@ -494,7 +524,7 @@ mod bindings {
use std::path::Path; use std::path::Path;
static PREBUILT_BINDGENS: &[&str] = &["bindgen_3.14.0.rs"]; static PREBUILT_BINDGENS: &[&str] = &["bindgen_3.14.0"];
pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) { pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
let name = PREBUILT_BINDGENS[PREBUILT_BINDGENS.len() - 1]; let name = PREBUILT_BINDGENS[PREBUILT_BINDGENS.len() - 1];
@ -546,7 +576,11 @@ mod bindings {
.disable_nested_struct_naming() .disable_nested_struct_naming()
.trust_clang_mangling(false) .trust_clang_mangling(false)
.header(header.clone()) .header(header.clone())
.parse_callbacks(Box::new(SqliteTypeChooser)) .parse_callbacks(Box::new(SqliteTypeChooser));
if cfg!(feature = "loadable_extension") {
bindings = bindings.ignore_functions(); // see generate_functions
} else {
bindings = bindings
.blocklist_function("sqlite3_auto_extension") .blocklist_function("sqlite3_auto_extension")
.raw_line( .raw_line(
r#"extern "C" { r#"extern "C" {
@ -575,6 +609,7 @@ mod bindings {
) -> ::std::os::raw::c_int; ) -> ::std::os::raw::c_int;
}"#, }"#,
); );
}
if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) { if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) {
bindings = bindings.clang_arg("-DSQLITE_HAS_CODEC"); bindings = bindings.clang_arg("-DSQLITE_HAS_CODEC");
@ -645,11 +680,183 @@ mod bindings {
.blocklist_item("__.*"); .blocklist_item("__.*");
} }
bindings let bindings = bindings
.layout_tests(false) .layout_tests(false)
.generate() .generate()
.unwrap_or_else(|_| panic!("could not run bindgen on header {}", header)) .unwrap_or_else(|_| panic!("could not run bindgen on header {}", header));
#[cfg(feature = "loadable_extension")]
{
let mut output = Vec::new();
bindings
.write(Box::new(&mut output))
.expect("could not write output of bindgen");
let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");
super::loadable_extension::generate_functions(&mut output);
std::fs::write(out_path, output.as_bytes())
.unwrap_or_else(|_| panic!("Could not write to {:?}", out_path));
}
#[cfg(not(feature = "loadable_extension"))]
bindings
.write_to_file(out_path) .write_to_file(out_path)
.unwrap_or_else(|_| panic!("Could not write to {:?}", out_path)); .unwrap_or_else(|_| panic!("Could not write to {:?}", out_path));
} }
} }
#[cfg(all(feature = "buildtime_bindgen", feature = "loadable_extension"))]
mod loadable_extension {
/// try to generate similar rust code for all `#define sqlite3_xyz
/// sqlite3_api->abc` macros` in sqlite3ext.h
pub fn generate_functions(output: &mut String) {
// (1) parse sqlite3_api_routines fields from bindgen output
let ast: syn::File = syn::parse_str(output).expect("could not parse bindgen output");
let sqlite3_api_routines: syn::ItemStruct = ast
.items
.into_iter()
.find_map(|i| {
if let syn::Item::Struct(s) = i {
if s.ident == "sqlite3_api_routines" {
Some(s)
} else {
None
}
} else {
None
}
})
.expect("could not find sqlite3_api_routines");
let sqlite3_api_routines_ident = sqlite3_api_routines.ident;
let p_api = quote::format_ident!("p_api");
let mut stores = Vec::new();
let mut malloc = Vec::new();
// (2) `#define sqlite3_xyz sqlite3_api->abc` => `pub unsafe fn
// sqlite3_xyz(args) -> ty {...}` for each `abc` field:
for field in sqlite3_api_routines.fields {
let ident = field.ident.expect("unamed field");
let span = ident.span();
let name = ident.to_string();
if name == "vmprintf" || name == "xvsnprintf" || name == "str_vappendf" {
continue; // skip va_list
} else if name == "aggregate_count"
|| name == "expired"
|| name == "global_recover"
|| name == "thread_cleanup"
|| name == "transfer_bindings"
{
continue; // omit deprecated
}
let sqlite3_name = match name.as_ref() {
"xthreadsafe" => "sqlite3_threadsafe".to_owned(),
"interruptx" => "sqlite3_interrupt".to_owned(),
_ => {
format!("sqlite3_{name}")
}
};
let ptr_name =
syn::Ident::new(format!("__{}", sqlite3_name.to_uppercase()).as_ref(), span);
let sqlite3_fn_name = syn::Ident::new(&sqlite3_name, span);
let method =
extract_method(&field.ty).unwrap_or_else(|| panic!("unexpected type for {name}"));
let arg_names: syn::punctuated::Punctuated<&syn::Ident, syn::token::Comma> = method
.inputs
.iter()
.map(|i| &i.name.as_ref().unwrap().0)
.collect();
let args = &method.inputs;
// vtab_config/sqlite3_vtab_config: ok
let varargs = &method.variadic;
if varargs.is_some() && "db_config" != name && "log" != name && "vtab_config" != name {
continue; // skip ...
}
let ty = &method.output;
let tokens = if "db_config" == name {
quote::quote! {
static #ptr_name: ::std::sync::atomic::AtomicPtr<()> = ::std::sync::atomic::AtomicPtr::new(::std::ptr::null_mut());
pub unsafe fn #sqlite3_fn_name(#args arg3: ::std::os::raw::c_int, arg4: *mut ::std::os::raw::c_int) #ty {
let ptr = #ptr_name.load(::std::sync::atomic::Ordering::Acquire);
assert!(!ptr.is_null(), "SQLite API not initialized");
let fun: unsafe extern "C" fn(#args #varargs) #ty = ::std::mem::transmute(ptr);
(fun)(#arg_names, arg3, arg4)
}
}
} else if "log" == name {
quote::quote! {
static #ptr_name: ::std::sync::atomic::AtomicPtr<()> = ::std::sync::atomic::AtomicPtr::new(::std::ptr::null_mut());
pub unsafe fn #sqlite3_fn_name(#args arg3: *const ::std::os::raw::c_char) #ty {
let ptr = #ptr_name.load(::std::sync::atomic::Ordering::Acquire);
assert!(!ptr.is_null(), "SQLite API not initialized");
let fun: unsafe extern "C" fn(#args #varargs) #ty = ::std::mem::transmute(ptr);
(fun)(#arg_names, arg3)
}
}
} else {
quote::quote! {
static #ptr_name: ::std::sync::atomic::AtomicPtr<()> = ::std::sync::atomic::AtomicPtr::new(::std::ptr::null_mut());
pub unsafe fn #sqlite3_fn_name(#args) #ty {
let ptr = #ptr_name.load(::std::sync::atomic::Ordering::Acquire);
assert!(!ptr.is_null(), "SQLite API not initialized or SQLite feature omitted");
let fun: unsafe extern "C" fn(#args #varargs) #ty = ::std::mem::transmute(ptr);
(fun)(#arg_names)
}
}
};
output.push_str(&prettyplease::unparse(
&syn::parse2(tokens).expect("could not parse quote output"),
));
output.push('\n');
if name == "malloc" {
&mut malloc
} else {
&mut stores
}
.push(quote::quote! {
if let Some(fun) = (*#p_api).#ident {
#ptr_name.store(
fun as usize as *mut (),
::std::sync::atomic::Ordering::Release,
);
}
});
}
// (3) generate rust code similar to SQLITE_EXTENSION_INIT2 macro
let tokens = quote::quote! {
/// Like SQLITE_EXTENSION_INIT2 macro
pub unsafe fn rusqlite_extension_init2(#p_api: *mut #sqlite3_api_routines_ident) -> ::std::result::Result<(),crate::InitError> {
#(#malloc)* // sqlite3_malloc needed by to_sqlite_error
if let Some(fun) = (*#p_api).libversion_number {
let version = fun();
if SQLITE_VERSION_NUMBER > version {
return Err(crate::InitError::VersionMismatch{compile_time: SQLITE_VERSION_NUMBER, runtime: version});
}
} else {
return Err(crate::InitError::NullFunctionPointer);
}
#(#stores)*
Ok(())
}
};
output.push_str(&prettyplease::unparse(
&syn::parse2(tokens).expect("could not parse quote output"),
));
output.push('\n');
}
fn extract_method(ty: &syn::Type) -> Option<&syn::TypeBareFn> {
match ty {
syn::Type::Path(tp) => tp.path.segments.last(),
_ => None,
}
.map(|seg| match &seg.arguments {
syn::PathArguments::AngleBracketed(args) => args.args.first(),
_ => None,
})?
.map(|arg| match arg {
syn::GenericArgument::Type(t) => Some(t),
_ => None,
})?
.map(|ty| match ty {
syn::Type::BareFn(r) => Some(r),
_ => None,
})?
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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.42.0" #define SQLITE_VERSION "3.44.0"
#define SQLITE_VERSION_NUMBER 3042000 #define SQLITE_VERSION_NUMBER 3044000
#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" #define SQLITE_SOURCE_ID "2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
@ -528,6 +528,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
@ -1190,7 +1191,7 @@ struct sqlite3_io_methods {
** by clients within the current process, only within other processes. ** by clients within the current process, only within other processes.
** **
** <li>[[SQLITE_FCNTL_CKSM_FILE]] ** <li>[[SQLITE_FCNTL_CKSM_FILE]]
** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the ** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the
** [checksum VFS shim] only. ** [checksum VFS shim] only.
** **
** <li>[[SQLITE_FCNTL_RESET_CACHE]] ** <li>[[SQLITE_FCNTL_RESET_CACHE]]
@ -2126,7 +2127,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded ** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default ** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a ** value for this option is to never use this optimization. Specifying a
** negative value for this option restores the default behaviour. ** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the ** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
** **
@ -2301,7 +2302,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no ** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint ** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to ** operation before closing the connection. This option may be used to
** override this behaviour. The first parameter passed to this operation ** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the ** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged. ** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer ** The second parameter is a pointer to an integer
@ -2454,7 +2455,7 @@ struct sqlite3_mem_methods {
** the [VACUUM] command will fail with an obscure error when attempting to ** the [VACUUM] command will fail with an obscure error when attempting to
** process a table with generated columns and a descending index. This is ** process a table with generated columns and a descending index. This is
** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** not considered a bug since SQLite versions 3.3.0 and earlier do not support
** either generated columns or decending indexes. ** either generated columns or descending indexes.
** </dd> ** </dd>
** **
** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
@ -2735,6 +2736,7 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
** **
** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether ** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
** or not an interrupt is currently in effect for [database connection] D. ** or not an interrupt is currently in effect for [database connection] D.
** It returns 1 if an interrupt is currently in effect, or 0 otherwise.
*/ */
SQLITE_API void sqlite3_interrupt(sqlite3*); SQLITE_API void sqlite3_interrupt(sqlite3*);
SQLITE_API int sqlite3_is_interrupted(sqlite3*); SQLITE_API int sqlite3_is_interrupted(sqlite3*);
@ -3388,8 +3390,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** M argument should be the bitwise OR-ed combination of ** M argument should be the bitwise OR-ed combination of
** zero or more [SQLITE_TRACE] constants. ** zero or more [SQLITE_TRACE] constants.
** **
** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides ** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P)
** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). ** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or
** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each
** database connection may have at most one trace callback.
** **
** ^The X callback is invoked whenever any of the events identified by ** ^The X callback is invoked whenever any of the events identified by
** mask M occur. ^The integer return value from the callback is currently ** mask M occur. ^The integer return value from the callback is currently
@ -3758,7 +3762,7 @@ SQLITE_API int sqlite3_open_v2(
** as F) must be one of: ** as F) must be one of:
** <ul> ** <ul>
** <li> A database filename pointer created by the SQLite core and ** <li> A database filename pointer created by the SQLite core and
** passed into the xOpen() method of a VFS implemention, or ** passed into the xOpen() method of a VFS implementation, or
** <li> A filename obtained from [sqlite3_db_filename()], or ** <li> A filename obtained from [sqlite3_db_filename()], or
** <li> A new filename constructed using [sqlite3_create_filename()]. ** <li> A new filename constructed using [sqlite3_create_filename()].
** </ul> ** </ul>
@ -3871,7 +3875,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
/* /*
** CAPI3REF: Create and Destroy VFS Filenames ** CAPI3REF: Create and Destroy VFS Filenames
** **
** These interfces are provided for use by [VFS shim] implementations and ** These interfaces are provided for use by [VFS shim] implementations and
** are not useful outside of that context. ** are not useful outside of that context.
** **
** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
@ -3951,6 +3955,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
** **
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
** text that describes the error, as either UTF-8 or UTF-16 respectively. ** text that describes the error, as either UTF-8 or UTF-16 respectively.
** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally. ** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result. ** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by ** However, the error string might be overwritten or deallocated by
@ -4418,6 +4423,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
*/ */
SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement
** METHOD: sqlite3_stmt
**
** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN
** setting for [prepared statement] S. If E is zero, then S becomes
** a normal prepared statement. If E is 1, then S behaves as if
** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if
** its SQL text began with "[EXPLAIN QUERY PLAN]".
**
** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared.
** SQLite tries to avoid a reprepare, but a reprepare might be necessary
** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode.
**
** Because of the potential need to reprepare, a call to
** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be
** reprepared because it was created using [sqlite3_prepare()] instead of
** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and
** hence has no saved SQL text with which to reprepare.
**
** Changing the explain setting for a prepared statement does not change
** the original SQL text for the statement. Hence, if the SQL text originally
** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0)
** is called to convert the statement into an ordinary statement, the EXPLAIN
** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S)
** output, even though the statement now acts like a normal SQL statement.
**
** This routine returns SQLITE_OK if the explain mode is successfully
** changed, or an error code if the explain mode could not be changed.
** The explain mode cannot be changed while a statement is active.
** Hence, it is good practice to call [sqlite3_reset(S)]
** immediately prior to calling sqlite3_stmt_explain(S,E).
*/
SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode);
/* /*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt ** METHOD: sqlite3_stmt
@ -4581,7 +4621,7 @@ typedef struct sqlite3_context sqlite3_context;
** with it may be passed. ^It is called to dispose of the BLOB or string even ** with it may be passed. ^It is called to dispose of the BLOB or string even
** if the call to the bind API fails, except the destructor is not called if ** if the call to the bind API fails, except the destructor is not called if
** the third parameter is a NULL pointer or the fourth parameter is negative. ** the third parameter is a NULL pointer or the fourth parameter is negative.
** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that ** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that
** the application remains responsible for disposing of the object. ^In this ** the application remains responsible for disposing of the object. ^In this
** case, the object and the provided pointer to it must remain valid until ** case, the object and the provided pointer to it must remain valid until
** either the prepared statement is finalized or the same SQL parameter is ** either the prepared statement is finalized or the same SQL parameter is
@ -5260,20 +5300,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S
** back to the beginning of its program. ** back to the beginning of its program.
** **
** ^If the most recent call to [sqlite3_step(S)] for the ** ^The return code from [sqlite3_reset(S)] indicates whether or not
** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], ** the previous evaluation of prepared statement S completed successfully.
** or if [sqlite3_step(S)] has never before been called on S, ** ^If [sqlite3_step(S)] has never before been called on S or if
** then [sqlite3_reset(S)] returns [SQLITE_OK]. ** [sqlite3_step(S)] has not been called since the previous call
** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return
** [SQLITE_OK].
** **
** ^If the most recent call to [sqlite3_step(S)] for the ** ^If the most recent call to [sqlite3_step(S)] for the
** [prepared statement] S indicated an error, then ** [prepared statement] S indicated an error, then
** [sqlite3_reset(S)] returns an appropriate [error code]. ** [sqlite3_reset(S)] returns an appropriate [error code].
** ^The [sqlite3_reset(S)] interface might also return an [error code]
** if there were no prior errors but the process of resetting
** the prepared statement caused a new error. ^For example, if an
** [INSERT] statement with a [RETURNING] clause is only stepped one time,
** that one call to [sqlite3_step(S)] might return SQLITE_ROW but
** the overall statement might still fail and the [sqlite3_reset(S)] call
** might return SQLITE_BUSY if locking constraints prevent the
** database change from committing. Therefore, it is important that
** applications check the return code from [sqlite3_reset(S)] even if
** no prior call to [sqlite3_step(S)] indicated a problem.
** **
** ^The [sqlite3_reset(S)] interface does not change the values ** ^The [sqlite3_reset(S)] interface does not change the values
** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
*/ */
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
/* /*
** CAPI3REF: Create Or Redefine SQL Functions ** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines} ** KEYWORDS: {function creation routines}
@ -5484,7 +5537,7 @@ SQLITE_API int sqlite3_create_window_function(
** [application-defined SQL function] ** [application-defined SQL function]
** that has side-effects or that could potentially leak sensitive information. ** that has side-effects or that could potentially leak sensitive information.
** This will prevent attacks in which an application is tricked ** This will prevent attacks in which an application is tricked
** into using a database file that has had its schema surreptiously ** into using a database file that has had its schema surreptitiously
** modified to invoke the application-defined function in ways that are ** modified to invoke the application-defined function in ways that are
** harmful. ** harmful.
** <p> ** <p>
@ -5828,32 +5881,32 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context ** METHOD: sqlite3_context
** **
** These functions may be used by (non-aggregate) SQL functions to ** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to ** associate auxiliary data with argument values. If the same argument
** multiple invocations of the same SQL function during query execution, under ** value is passed to multiple invocations of the same SQL function during
** some circumstances the associated metadata may be preserved. An example ** query execution, under some circumstances the associated auxiliary data
** of where this might be useful is in a regular-expression matching ** might be preserved. An example of where this might be useful is in a
** function. The compiled version of the regular expression can be stored as ** regular-expression matching function. The compiled version of the regular
** metadata associated with the pattern string. ** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same, ** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple ** the compiled regular expression can be reused on multiple
** invocations of the same function. ** invocations of the same function.
** **
** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata ** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most ** value to the application-defined function. ^N is zero for the left-most
** function argument. ^If there is no metadata ** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer. ** returns a NULL pointer.
** **
** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th ** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
** argument of the application-defined function. ^Subsequent ** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent ** calls to sqlite3_get_auxdata(C,N) return P from the most recent
** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or ** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
** NULL if the metadata has been discarded. ** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly ** SQLite will invoke the destructor function X with parameter P exactly
** once, when the metadata is discarded. ** once, when the auxiliary data is discarded.
** SQLite is free to discard the metadata at any time, including: <ul> ** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or ** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the ** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or ** SQL statement)^, or
@ -5869,7 +5922,7 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** function implementation should not make any use of P after ** function implementation should not make any use of P after
** sqlite3_set_auxdata() has been called. ** sqlite3_set_auxdata() has been called.
** **
** ^(In practice, metadata is preserved between function calls for ** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal ** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^ ** values and [parameters] and expressions composed from the same.)^
** **
@ -5879,10 +5932,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** **
** These routines must be called from the same thread in which ** These routines must be called from the same thread in which
** the SQL function is running. ** the SQL function is running.
**
** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/ */
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
/*
** CAPI3REF: Database Connection Client Data
** METHOD: sqlite3
**
** These functions are used to associate one or more named pointers
** with a [database connection].
** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
** to be attached to [database connection] D using name N. Subsequent
** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
** or a NULL pointer if there were no prior calls to
** sqlite3_set_clientdata() with the same values of D and N.
** Names are compared using strcmp() and are thus case sensitive.
**
** If P and X are both non-NULL, then the destructor X is invoked with
** argument P on the first of the following occurrences:
** <ul>
** <li> An out-of-memory error occurs during the call to
** sqlite3_set_clientdata() which attempts to register pointer P.
** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
** with the same D and N parameters.
** <li> The database connection closes. SQLite does not make any guarantees
** about the order in which destructors are called, only that all
** destructors will be called exactly once at some point during the
** database connection closing process.
** </ul>
**
** SQLite does not do anything with client data other than invoke
** destructors on the client data at the appropriate time. The intended
** use for client data is to provide a mechanism for wrapper libraries
** to store additional information about an SQLite database connection.
**
** There is no limit (other than available memory) on the number of different
** client data pointers (with different names) that can be attached to a
** single database connection. However, the implementation is optimized
** for the case of having only one or two different client data names.
** Applications and wrapper libraries are discouraged from using more than
** one client data name each.
**
** There is no way to enumerate the client data pointers
** associated with a database connection. The N parameter can be thought
** of as a secret key such that only code that knows the secret key is able
** to access the associated data.
**
** Security Warning: These interfaces should not be exposed in scripting
** languages or in other circumstances where it might be possible for an
** an attacker to invoke them. Any agent that can invoke these interfaces
** can probably also take control of the process.
**
** Database connection client data is only available for SQLite
** version 3.44.0 ([dateof:3.44.0]) and later.
**
** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
*/
SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/* /*
** CAPI3REF: Constants Defining Special Destructor Behavior ** CAPI3REF: Constants Defining Special Destructor Behavior
@ -6515,7 +6625,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
/* /*
** CAPI3REF: Allowed return values from [sqlite3_txn_state()] ** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state} ** KEYWORDS: {transaction state}
** **
** These constants define the current transaction state of a database file. ** These constants define the current transaction state of a database file.
@ -6647,7 +6757,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback ** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, ** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
** then the autovacuum steps callback is cancelled. The return value ** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current ** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other ** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@ -7166,6 +7276,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object. /* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */ ** Those below are for version 3 and greater. */
int (*xShadowName)(const char*); int (*xShadowName)(const char*);
/* The methods above are in versions 1 through 3 of the sqlite_module object.
** Those below are for version 4 and greater. */
int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
const char *zTabName, int mFlags, char **pzErr);
}; };
/* /*
@ -7653,7 +7767,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back. ** code is returned and the transaction rolled back.
** **
** Calling this function with an argument that is not a NULL pointer or an ** Calling this function with an argument that is not a NULL pointer or an
** open blob handle results in undefined behaviour. ^Calling this routine ** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to ** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the ** is passed a valid open blob handle, the values returned by the
@ -8133,6 +8247,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@ -8161,7 +8276,8 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TRACEFLAGS 31
#define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_TUNE 32
#define SQLITE_TESTCTRL_LOGEST 33 #define SQLITE_TESTCTRL_LOGEST 33
#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ #define SQLITE_TESTCTRL_USELONGDOUBLE 34
#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */
/* /*
** CAPI3REF: SQL Keyword Checking ** CAPI3REF: SQL Keyword Checking
@ -9617,7 +9733,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** [[SQLITE_VTAB_DIRECTONLY]]<dt>SQLITE_VTAB_DIRECTONLY</dt> ** [[SQLITE_VTAB_DIRECTONLY]]<dt>SQLITE_VTAB_DIRECTONLY</dt>
** <dd>Calls of the form ** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the
** the [xConnect] or [xCreate] methods of a [virtual table] implmentation ** the [xConnect] or [xCreate] methods of a [virtual table] implementation
** prohibits that virtual table from being used from within triggers and ** prohibits that virtual table from being used from within triggers and
** views. ** views.
** </dd> ** </dd>
@ -9807,7 +9923,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
** communicated to the xBestIndex method as a ** communicated to the xBestIndex method as a
** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
** this constraint, it must set the corresponding ** this constraint, it must set the corresponding
** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under ** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under
** the usual mode of handling IN operators, SQLite generates [bytecode] ** the usual mode of handling IN operators, SQLite generates [bytecode]
** that invokes the [xFilter|xFilter() method] once for each value ** that invokes the [xFilter|xFilter() method] once for each value
** on the right-hand side of the IN operator.)^ Thus the virtual table ** on the right-hand side of the IN operator.)^ Thus the virtual table
@ -10236,7 +10352,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** When the [sqlite3_blob_write()] API is used to update a blob column, ** When the [sqlite3_blob_write()] API is used to update a blob column,
** the pre-update hook is invoked with SQLITE_DELETE. This is because the ** the pre-update hook is invoked with SQLITE_DELETE. This is because the
** in this case the new values are not available. In this case, when a ** in this case the new values are not available. In this case, when a
** callback made with op==SQLITE_DELETE is actuall a write using the ** callback made with op==SQLITE_DELETE is actually a write using the
** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
** the index of the column being written. In other cases, where the ** the index of the column being written. In other cases, where the
** pre-update hook is being invoked for some other reason, including a ** pre-update hook is being invoked for some other reason, including a
@ -10497,6 +10613,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists. ** of the database exists.
** **
** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
** the returned buffer content will remain accessible and unchanged
** until either the next write operation on the connection or when
** the connection is closed, and applications must not modify the
** buffer. If the bit had been clear, the returned buffer will not
** be accessed by SQLite after the call.
**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs. ** allocation error occurs.
@ -10545,6 +10668,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64() ** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes. ** if writes on the database cause it to grow larger than M bytes.
** **
** Applications must not modify the buffer P or invalidate it before
** the database connection D is closed.
**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup ** database is currently in a read transaction or is involved in a backup
** operation. ** operation.
@ -10553,6 +10679,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR. ** function returns SQLITE_ERROR.
** **
** The deserialized database should not be in [WAL mode]. If the database
** is in WAL mode, then any attempt to use the database file will result
** in an [SQLITE_CANTOPEN] error. The application can set the
** [file format version numbers] (bytes 18 and 19) of the input database P
** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
** database file into rollback mode and work around this limitation.
**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning. ** [sqlite3_free()] is invoked on argument P prior to returning.
@ -11625,6 +11758,18 @@ SQLITE_API int sqlite3changeset_concat(
); );
/*
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
*/
SQLITE_API int sqlite3changeset_upgrade(
sqlite3 *db,
const char *zDb,
int nIn, const void *pIn, /* Input changeset */
int *pnOut, void **ppOut /* OUT: Inverse of input */
);
/* /*
** CAPI3REF: Changegroup Handle ** CAPI3REF: Changegroup Handle
** **
@ -11671,6 +11816,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
*/ */
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
** CAPI3REF: Add a Schema to a Changegroup
** METHOD: sqlite3_changegroup_schema
**
** This method may be used to optionally enforce the rule that the changesets
** added to the changegroup handle must match the schema of database zDb
** ("main", "temp", or the name of an attached database). If
** sqlite3changegroup_add() is called to add a changeset that is not compatible
** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
** object is left in an undefined state.
**
** A changeset schema is considered compatible with the database schema in
** the same way as for sqlite3changeset_apply(). Specifically, for each
** table in the changeset, there exists a database table with:
**
** <ul>
** <li> The name identified by the changeset, and
** <li> at least as many columns as recorded in the changeset, and
** <li> the primary key columns in the same position as recorded in
** the changeset.
** </ul>
**
** The output of the changegroup object always has the same schema as the
** database nominated using this function. In cases where changesets passed
** to sqlite3changegroup_add() have fewer columns than the corresponding table
** in the database schema, these are filled in using the default column
** values from the database schema. This makes it possible to combined
** changesets that have different numbers of columns for a single table
** within a changegroup, provided that they are otherwise compatible.
*/
SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
/* /*
** CAPI3REF: Add A Changeset To A Changegroup ** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup ** METHOD: sqlite3_changegroup
@ -11739,13 +11916,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present ** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the ** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the ** primary key columns for the table must be consistent. If this is not the
** case, this function fails with SQLITE_SCHEMA. If the input changeset ** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is ** object has been configured with a database schema using the
** returned. Or, if an out-of-memory condition occurs during processing, this ** sqlite3changegroup_schema() API, then it is possible to combine changesets
** function returns SQLITE_NOMEM. In all cases, if an error occurs the state ** with different numbers of columns for a single table, provided that
** of the final contents of the changegroup is undefined. ** they are otherwise compatible.
** **
** If no error occurs, SQLITE_OK is returned. ** If the input changeset appears to be corrupt and the corruption is
** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
** occurs during processing, this function returns SQLITE_NOMEM.
**
** In all cases, if an error occurs the state of the final contents of the
** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/ */
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
@ -12010,10 +12192,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <li>an insert change if all fields of the conflicting row match ** <li>an insert change if all fields of the conflicting row match
** the row being inserted. ** the row being inserted.
** </ul> ** </ul>
**
** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
** If this flag it set, then all foreign key constraints in the target
** database behave as if they were declared with "ON UPDATE NO ACTION ON
** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
** or SET DEFAULT.
*/ */
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
/* /*
** CAPI3REF: Constants Passed To The Conflict Handler ** CAPI3REF: Constants Passed To The Conflict Handler
@ -12754,7 +12943,7 @@ struct Fts5PhraseIter {
** See xPhraseFirstColumn above. ** See xPhraseFirstColumn above.
*/ */
struct Fts5ExtensionApi { struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */ int iVersion; /* Currently always set to 2 */
void *(*xUserData)(Fts5Context*); void *(*xUserData)(Fts5Context*);
@ -12983,8 +13172,8 @@ struct Fts5ExtensionApi {
** as separate queries of the FTS index are required for each synonym. ** as separate queries of the FTS index are required for each synonym.
** **
** When using methods (2) or (3), it is important that the tokenizer only ** When using methods (2) or (3), it is important that the tokenizer only
** provide synonyms when tokenizing document text (method (2)) or query ** provide synonyms when tokenizing document text (method (3)) or query
** text (method (3)), not both. Doing so will not cause any errors, but is ** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient. ** inefficient.
*/ */
typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct Fts5Tokenizer Fts5Tokenizer;
@ -13032,7 +13221,7 @@ struct fts5_api {
int (*xCreateTokenizer)( int (*xCreateTokenizer)(
fts5_api *pApi, fts5_api *pApi,
const char *zName, const char *zName,
void *pContext, void *pUserData,
fts5_tokenizer *pTokenizer, fts5_tokenizer *pTokenizer,
void (*xDestroy)(void*) void (*xDestroy)(void*)
); );
@ -13041,7 +13230,7 @@ struct fts5_api {
int (*xFindTokenizer)( int (*xFindTokenizer)(
fts5_api *pApi, fts5_api *pApi,
const char *zName, const char *zName,
void **ppContext, void **ppUserData,
fts5_tokenizer *pTokenizer fts5_tokenizer *pTokenizer
); );
@ -13049,7 +13238,7 @@ struct fts5_api {
int (*xCreateFunction)( int (*xCreateFunction)(
fts5_api *pApi, fts5_api *pApi,
const char *zName, const char *zName,
void *pContext, void *pUserData,
fts5_extension_function xFunction, fts5_extension_function xFunction,
void (*xDestroy)(void*) void (*xDestroy)(void*)
); );

View File

@ -361,6 +361,11 @@ struct sqlite3_api_routines {
int (*value_encoding)(sqlite3_value*); int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */ /* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*); int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
/* Version 3.44.0 and later */
void *(*get_clientdata)(sqlite3*,const char*);
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
}; };
/* /*
@ -689,6 +694,11 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_value_encoding sqlite3_api->value_encoding #define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */ /* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted #define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
/* Version 3.44.0 and later */
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#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

@ -132,6 +132,8 @@ const SQLITE_IOERR_BEGIN_ATOMIC: c_int = super::SQLITE_IOERR | (29 << 8);
const SQLITE_IOERR_COMMIT_ATOMIC: c_int = super::SQLITE_IOERR | (30 << 8); const SQLITE_IOERR_COMMIT_ATOMIC: c_int = super::SQLITE_IOERR | (30 << 8);
const SQLITE_IOERR_ROLLBACK_ATOMIC: c_int = super::SQLITE_IOERR | (31 << 8); const SQLITE_IOERR_ROLLBACK_ATOMIC: c_int = super::SQLITE_IOERR | (31 << 8);
const SQLITE_IOERR_DATA: c_int = super::SQLITE_IOERR | (32 << 8); const SQLITE_IOERR_DATA: c_int = super::SQLITE_IOERR | (32 << 8);
const SQLITE_IOERR_CORRUPTFS: c_int = super::SQLITE_IOERR | (33 << 8);
const SQLITE_IOERR_IN_PAGE: c_int = super::SQLITE_IOERR | (34 << 8);
const SQLITE_LOCKED_VTAB: c_int = super::SQLITE_LOCKED | (2 << 8); const SQLITE_LOCKED_VTAB: c_int = super::SQLITE_LOCKED | (2 << 8);
@ -219,6 +221,8 @@ pub fn code_to_str(code: c_int) -> &'static str {
SQLITE_IOERR_COMMIT_ATOMIC => "SQLITE_IOERR_COMMIT_ATOMIC", SQLITE_IOERR_COMMIT_ATOMIC => "SQLITE_IOERR_COMMIT_ATOMIC",
SQLITE_IOERR_ROLLBACK_ATOMIC => "SQLITE_IOERR_ROLLBACK_ATOMIC", SQLITE_IOERR_ROLLBACK_ATOMIC => "SQLITE_IOERR_ROLLBACK_ATOMIC",
SQLITE_IOERR_DATA => "SQLITE_IOERR_DATA", SQLITE_IOERR_DATA => "SQLITE_IOERR_DATA",
SQLITE_IOERR_CORRUPTFS => "SQLITE_IOERR_CORRUPTFS",
SQLITE_IOERR_IN_PAGE => "SQLITE_IOERR_IN_PAGE",
super::SQLITE_LOCKED_SHAREDCACHE => "Locking conflict due to another connection with a shared cache", super::SQLITE_LOCKED_SHAREDCACHE => "Locking conflict due to another connection with a shared cache",
SQLITE_LOCKED_VTAB => "SQLITE_LOCKED_VTAB", SQLITE_LOCKED_VTAB => "SQLITE_LOCKED_VTAB",
@ -269,3 +273,32 @@ pub fn code_to_str(code: c_int) -> &'static str {
_ => "Unknown error code", _ => "Unknown error code",
} }
} }
/// Loadable extension initialization error
#[cfg(feature = "loadable_extension")]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum InitError {
/// Version mismatch between the extension and the SQLite3 library
VersionMismatch { compile_time: i32, runtime: i32 },
/// Invalid function pointer in one of sqlite3_api_routines fields
NullFunctionPointer,
}
#[cfg(feature = "loadable_extension")]
impl ::std::fmt::Display for InitError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
InitError::VersionMismatch {
compile_time,
runtime,
} => {
write!(f, "SQLite version mismatch: {runtime} < {compile_time}")
}
InitError::NullFunctionPointer => {
write!(f, "Some sqlite3_api_routines fields are null")
}
}
}
}
#[cfg(feature = "loadable_extension")]
impl error::Error for InitError {}

View File

@ -3,26 +3,38 @@
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
echo "$SCRIPT_DIR" echo "$SCRIPT_DIR"
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; } cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
cargo clean cargo clean -p libsqlite3-sys
mkdir -p "$SCRIPT_DIR/../target" "$SCRIPT_DIR/sqlite3" TARGET_DIR="$SCRIPT_DIR/../target"
export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3" export SQLITE3_LIB_DIR="$SCRIPT_DIR/sqlite3"
export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR" mkdir -p "$TARGET_DIR" "$SQLITE3_LIB_DIR"
# Download and extract amalgamation # Download and extract amalgamation
SQLITE=sqlite-amalgamation-3420000 SQLITE=sqlite-amalgamation-3440000
curl -O https://sqlite.org/2023/$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"
rm -f "$SQLITE.zip" rm -f "$SQLITE.zip"
# Regenerate bindgen file for sqlite3 export SQLITE3_INCLUDE_DIR="$SQLITE3_LIB_DIR"
# Regenerate bindgen file for sqlite3.h
rm -f "$SQLITE3_LIB_DIR/bindgen_bundled_version.rs" rm -f "$SQLITE3_LIB_DIR/bindgen_bundled_version.rs"
cargo update cargo update
# Just to make sure there is only one bindgen.rs file in target dir # Just to make sure there is only one bindgen.rs file in target dir
find "$SCRIPT_DIR/../target" -type f -name bindgen.rs -exec rm {} \; find "$TARGET_DIR" -type f -name bindgen.rs -exec rm {} \;
env LIBSQLITE3_SYS_BUNDLING=1 cargo build --features "buildtime_bindgen session" --no-default-features env LIBSQLITE3_SYS_BUNDLING=1 cargo build --features "buildtime_bindgen session" --no-default-features
find "$SCRIPT_DIR/../target" -type f -name bindgen.rs -exec mv {} "$SQLITE3_LIB_DIR/bindgen_bundled_version.rs" \; find "$TARGET_DIR" -type f -name bindgen.rs -exec mv {} "$SQLITE3_LIB_DIR/bindgen_bundled_version.rs" \;
# Regenerate bindgen file for sqlite3ext.h
# some sqlite3_api_routines fields are function pointers with va_list arg but currently stable Rust doesn't support this type.
# FIXME how to generate portable bindings without :
sed -i.bk -e 's/va_list/void*/' "$SQLITE3_LIB_DIR/sqlite3ext.h"
rm -f "$SQLITE3_LIB_DIR/bindgen_bundled_version_ext.rs"
find "$TARGET_DIR" -type f -name bindgen.rs -exec rm {} \;
env LIBSQLITE3_SYS_BUNDLING=1 cargo build --features "buildtime_bindgen loadable_extension" --no-default-features
find "$TARGET_DIR" -type f -name bindgen.rs -exec mv {} "$SQLITE3_LIB_DIR/bindgen_bundled_version_ext.rs" \;
git checkout "$SQLITE3_LIB_DIR/sqlite3ext.h"
rm -f "$SQLITE3_LIB_DIR/sqlite3ext.h.bk"
# Sanity checks # Sanity checks
cd "$SCRIPT_DIR/.." || { echo "fatal error" >&2; exit 1; } cd "$SCRIPT_DIR/.." || { echo "fatal error" >&2; exit 1; }

View File

@ -3,7 +3,7 @@
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
echo "$SCRIPT_DIR" echo "$SCRIPT_DIR"
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; } cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
cargo clean cargo clean -p libsqlite3-sys
mkdir -p "$SCRIPT_DIR/../target" "$SCRIPT_DIR/sqlcipher" 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"

View File

@ -0,0 +1,2 @@
#include "sqlite3ext.h"

View File

@ -0,0 +1,17 @@
[package]
name = "rusqlite-macros"
version = "0.1.0"
authors = ["The rusqlite developers"]
edition = "2021"
description = "Private implementation detail of rusqlite crate"
repository = "https://github.com/rusqlite/rusqlite"
license = "MIT"
categories = ["database"]
[lib]
proc-macro = true
[dependencies]
sqlite3-parser = { version = "0.12", default-features = false, features = ["YYNOERRORRECOVERY"] }
fallible-iterator = "0.3"
litrs = { version = "0.4", default-features = false }

View File

@ -0,0 +1,91 @@
//! Private implementation details of `rusqlite`.
use litrs::StringLit;
use proc_macro::{Group, Span, TokenStream, TokenTree};
use fallible_iterator::FallibleIterator;
use sqlite3_parser::ast::{ParameterInfo, ToTokens};
use sqlite3_parser::lexer::sql::Parser;
// https://internals.rust-lang.org/t/custom-error-diagnostics-with-procedural-macros-on-almost-stable-rust/8113
#[doc(hidden)]
#[proc_macro]
pub fn __bind(input: TokenStream) -> TokenStream {
try_bind(input).unwrap_or_else(|msg| parse_ts(&format!("compile_error!({msg:?})")))
}
type Result<T> = std::result::Result<T, String>;
fn try_bind(input: TokenStream) -> Result<TokenStream> {
let (stmt, literal) = {
let mut iter = input.clone().into_iter();
let stmt = iter.next().unwrap();
let literal = iter.next().unwrap();
assert!(iter.next().is_none());
(stmt, literal)
};
let call_site = literal.span();
let string_lit = match StringLit::try_from(literal) {
Ok(string_lit) => string_lit,
Err(e) => return Ok(e.to_compile_error()),
};
let sql = string_lit.value();
let mut parser = Parser::new(sql.as_bytes());
let ast = match parser.next() {
Ok(None) => return Err("Invalid input".to_owned()),
Err(err) => {
return Err(err.to_string());
}
Ok(Some(ast)) => ast,
};
let mut info = ParameterInfo::default();
if let Err(err) = ast.to_tokens(&mut info) {
return Err(err.to_string());
}
if info.count == 0 {
return Ok(TokenStream::new());
}
if info.count as usize != info.names.len() {
return Err("Mixing named and numbered parameters is not supported.".to_string());
}
let mut res = TokenStream::new();
for (i, name) in info.names.iter().enumerate() {
res.extend(Some(stmt.clone()));
res.extend(respan(
parse_ts(&format!(
".raw_bind_parameter({}, &{})?;",
i + 1,
&name[1..]
)),
call_site,
));
}
Ok(res)
}
fn respan(ts: TokenStream, span: Span) -> TokenStream {
let mut res = TokenStream::new();
for tt in ts {
let tt = match tt {
TokenTree::Ident(mut ident) => {
ident.set_span(ident.span().resolved_at(span).located_at(span));
TokenTree::Ident(ident)
}
TokenTree::Group(group) => {
TokenTree::Group(Group::new(group.delimiter(), respan(group.stream(), span)))
}
_ => tt,
};
res.extend(Some(tt))
}
res
}
fn parse_ts(s: &str) -> TokenStream {
s.parse().unwrap()
}

View File

@ -0,0 +1,43 @@
use rusqlite_macros::__bind;
type Result = std::result::Result<(), String>;
struct Stmt;
impl Stmt {
pub fn raw_bind_parameter(&mut self, one_based_col_index: usize, param: &str) -> Result {
let (..) = (one_based_col_index, param);
Ok(())
}
}
#[test]
fn test_literal() -> Result {
let first_name = "El";
let last_name = "Barto";
let mut stmt = Stmt;
__bind!(stmt "SELECT $first_name, $last_name");
Ok(())
}
#[test]
fn test_no_placeholder() {
let _stmt = Stmt;
__bind!(_stmt "SELECT 1");
}
#[test]
fn test_raw_string() {
let _stmt = Stmt;
__bind!(_stmt r"SELECT 1");
__bind!(_stmt r#"SELECT 1"#);
}
/* FIXME
#[test]
fn test_const() {
const SQL: &str = "SELECT 1";
let stmt = ();
__bind!(stmt SQL);
}
*/

View File

@ -3,12 +3,16 @@ use std::str;
use crate::{Error, Result, Statement}; use crate::{Error, Result, Statement};
/// Information about a column of a SQLite query. /// Information about a column of a SQLite query.
#[cfg(feature = "column_decltype")]
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
#[derive(Debug)] #[derive(Debug)]
pub struct Column<'stmt> { pub struct Column<'stmt> {
name: &'stmt str, name: &'stmt str,
decl_type: Option<&'stmt str>, decl_type: Option<&'stmt str>,
} }
#[cfg(feature = "column_decltype")]
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
impl Column<'_> { impl Column<'_> {
/// Returns the name of the column. /// Returns the name of the column.
#[inline] #[inline]
@ -90,6 +94,8 @@ impl Statement<'_> {
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid /// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
/// column range for this row. /// column range for this row.
/// ///
/// # Panics
///
/// Panics when column name is not valid UTF-8. /// Panics when column name is not valid UTF-8.
#[inline] #[inline]
pub fn column_name(&self, col: usize) -> Result<&str> { pub fn column_name(&self, col: usize) -> Result<&str> {

View File

@ -9,6 +9,7 @@ use crate::{Connection, Result};
/// Database Connection Configuration Options /// Database Connection Configuration Options
/// See [Database Connection Configuration Options](https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html) for details. /// See [Database Connection Configuration Options](https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html) for details.
#[repr(i32)] #[repr(i32)]
#[derive(Copy, Clone, Debug)]
#[allow(non_snake_case, non_camel_case_types)] #[allow(non_snake_case, non_camel_case_types)]
#[non_exhaustive] #[non_exhaustive]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]

View File

@ -141,6 +141,10 @@ pub enum Error {
/// byte offset of the start of invalid token /// byte offset of the start of invalid token
offset: c_int, offset: c_int,
}, },
/// Loadable extension initialization error
#[cfg(feature = "loadable_extension")]
#[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
InitError(ffi::InitError),
} }
impl PartialEq for Error { impl PartialEq for Error {
@ -200,6 +204,8 @@ impl PartialEq for Error {
offset: o2, offset: o2,
}, },
) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2, ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
#[cfg(feature = "loadable_extension")]
(Error::InitError(e1), Error::InitError(e2)) => e1 == e2,
(..) => false, (..) => false,
} }
} }
@ -241,6 +247,14 @@ impl From<FromSqlError> for Error {
} }
} }
#[cfg(feature = "loadable_extension")]
impl From<ffi::InitError> for Error {
#[cold]
fn from(err: ffi::InitError) -> Error {
Error::InitError(err)
}
}
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {
@ -311,6 +325,8 @@ impl fmt::Display for Error {
ref sql, ref sql,
.. ..
} => write!(f, "{msg} in {sql} at offset {offset}"), } => write!(f, "{msg} in {sql} at offset {offset}"),
#[cfg(feature = "loadable_extension")]
Error::InitError(ref err) => err.fmt(f),
} }
} }
} }
@ -360,6 +376,8 @@ impl error::Error for Error {
Error::BlobSizeError => None, Error::BlobSizeError => None,
#[cfg(feature = "modern_sqlite")] #[cfg(feature = "modern_sqlite")]
Error::SqlInputError { ref error, .. } => Some(error), Error::SqlInputError { ref error, .. } => Some(error),
#[cfg(feature = "loadable_extension")]
Error::InitError(ref err) => Some(err),
} }
} }
} }
@ -367,6 +385,7 @@ impl error::Error for Error {
impl Error { impl Error {
/// Returns the underlying SQLite error if this is [`Error::SqliteFailure`]. /// Returns the underlying SQLite error if this is [`Error::SqliteFailure`].
#[inline] #[inline]
#[must_use]
pub fn sqlite_error(&self) -> Option<&ffi::Error> { pub fn sqlite_error(&self) -> Option<&ffi::Error> {
match self { match self {
Self::SqliteFailure(error, _) => Some(error), Self::SqliteFailure(error, _) => Some(error),
@ -377,6 +396,7 @@ impl Error {
/// Returns the underlying SQLite error code if this is /// Returns the underlying SQLite error code if this is
/// [`Error::SqliteFailure`]. /// [`Error::SqliteFailure`].
#[inline] #[inline]
#[must_use]
pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> { pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
self.sqlite_error().map(|error| error.code) self.sqlite_error().map(|error| error.code)
} }
@ -439,10 +459,7 @@ pub fn check(code: c_int) -> Result<()> {
/// Transform Rust error to SQLite error (message and code). /// Transform Rust error to SQLite error (message and code).
/// # Safety /// # Safety
/// This function is unsafe because it uses raw pointer /// This function is unsafe because it uses raw pointer
pub unsafe fn to_sqlite_error( pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut std::os::raw::c_char) -> c_int {
e: &Error,
err_msg: *mut *mut std::os::raw::c_char,
) -> std::os::raw::c_int {
use crate::util::alloc; use crate::util::alloc;
match e { match e {
Error::SqliteFailure(err, s) => { Error::SqliteFailure(err, s) => {

View File

@ -272,11 +272,11 @@ where
/// call to [`step()`](Aggregate::step) to set up the context for an /// call to [`step()`](Aggregate::step) to set up the context for an
/// invocation of the function. (Note: `init()` will not be called if /// invocation of the function. (Note: `init()` will not be called if
/// there are no rows.) /// there are no rows.)
fn init(&self, _: &mut Context<'_>) -> Result<A>; fn init(&self, ctx: &mut Context<'_>) -> Result<A>;
/// "step" function called once for each row in an aggregate group. May be /// "step" function called once for each row in an aggregate group. May be
/// called 0 times if there are no rows. /// called 0 times if there are no rows.
fn step(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>; fn step(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>;
/// Computes and returns the final result. Will be called exactly once for /// Computes and returns the final result. Will be called exactly once for
/// each invocation of the function. If [`step()`](Aggregate::step) was /// each invocation of the function. If [`step()`](Aggregate::step) was
@ -287,7 +287,7 @@ where
/// given `None`. /// given `None`.
/// ///
/// The passed context will have no arguments. /// The passed context will have no arguments.
fn finalize(&self, _: &mut Context<'_>, _: Option<A>) -> Result<T>; fn finalize(&self, ctx: &mut Context<'_>, acc: Option<A>) -> Result<T>;
} }
/// `WindowAggregate` is the callback interface for /// `WindowAggregate` is the callback interface for
@ -301,16 +301,17 @@ where
{ {
/// Returns the current value of the aggregate. Unlike xFinal, the /// Returns the current value of the aggregate. Unlike xFinal, the
/// implementation should not delete any context. /// implementation should not delete any context.
fn value(&self, _: Option<&A>) -> Result<T>; fn value(&self, acc: Option<&mut A>) -> Result<T>;
/// Removes a row from the current window. /// Removes a row from the current window.
fn inverse(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>; fn inverse(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>;
} }
bitflags::bitflags! { bitflags::bitflags! {
/// Function Flags. /// Function Flags.
/// See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html) /// See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html)
/// and [Function Flags](https://sqlite.org/c3ref/c_deterministic.html) for details. /// and [Function Flags](https://sqlite.org/c3ref/c_deterministic.html) for details.
#[derive(Clone, Copy, Debug)]
#[repr(C)] #[repr(C)]
pub struct FunctionFlags: ::std::os::raw::c_int { pub struct FunctionFlags: ::std::os::raw::c_int {
/// Specifies UTF-8 as the text encoding this SQL function prefers for its parameters. /// Specifies UTF-8 as the text encoding this SQL function prefers for its parameters.
@ -638,6 +639,7 @@ unsafe extern "C" fn call_boxed_step<A, D, T>(
args: slice::from_raw_parts(argv, argc as usize), args: slice::from_raw_parts(argv, argc as usize),
}; };
#[allow(clippy::unnecessary_cast)]
if (*pac as *mut A).is_null() { if (*pac as *mut A).is_null() {
*pac = Box::into_raw(Box::new((*boxed_aggr).init(&mut ctx)?)); *pac = Box::into_raw(Box::new((*boxed_aggr).init(&mut ctx)?));
} }
@ -708,7 +710,9 @@ where
// Within the xFinal callback, it is customary to set N=0 in calls to // Within the xFinal callback, it is customary to set N=0 in calls to
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
let a: Option<A> = match aggregate_context(ctx, 0) { let a: Option<A> = match aggregate_context(ctx, 0) {
Some(pac) => { Some(pac) =>
{
#[allow(clippy::unnecessary_cast)]
if (*pac as *mut A).is_null() { if (*pac as *mut A).is_null() {
None None
} else { } else {
@ -752,17 +756,10 @@ where
{ {
// Within the xValue callback, it is customary to set N=0 in calls to // Within the xValue callback, it is customary to set N=0 in calls to
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur. // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
let a: Option<&A> = match aggregate_context(ctx, 0) { let pac = aggregate_context(ctx, 0).filter(|&pac| {
Some(pac) => { #[allow(clippy::unnecessary_cast)]
if (*pac as *mut A).is_null() { !(*pac as *mut A).is_null()
None });
} else {
let a = &**pac;
Some(a)
}
}
None => None,
};
let r = catch_unwind(|| { let r = catch_unwind(|| {
let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::<W>(); let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::<W>();
@ -770,7 +767,7 @@ where
!boxed_aggr.is_null(), !boxed_aggr.is_null(),
"Internal error - null aggregate pointer" "Internal error - null aggregate pointer"
); );
(*boxed_aggr).value(a) (*boxed_aggr).value(pac.map(|pac| &mut **pac))
}); });
let t = match r { let t = match r {
Err(_) => { Err(_) => {
@ -1025,7 +1022,7 @@ mod test {
Ok(()) Ok(())
} }
fn value(&self, sum: Option<&i64>) -> Result<Option<i64>> { fn value(&self, sum: Option<&mut i64>) -> Result<Option<i64>> {
Ok(sum.copied()) Ok(sum.copied())
} }
} }

View File

@ -776,9 +776,10 @@ mod test {
.unwrap(); .unwrap();
let authorizer = move |ctx: AuthContext<'_>| match ctx.action { let authorizer = move |ctx: AuthContext<'_>| match ctx.action {
AuthAction::Read { column_name, .. } if column_name == "private" => { AuthAction::Read {
Authorization::Ignore column_name: "private",
} ..
} => Authorization::Ignore,
AuthAction::DropTable { .. } => Authorization::Deny, AuthAction::DropTable { .. } => Authorization::Deny,
AuthAction::Pragma { .. } => panic!("shouldn't be called"), AuthAction::Pragma { .. } => panic!("shouldn't be called"),
_ => Authorization::Allow, _ => Authorization::Allow,

View File

@ -4,7 +4,7 @@ use std::os::raw::{c_char, c_int};
use std::path::Path; use std::path::Path;
use std::ptr; use std::ptr;
use std::str; use std::str;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use super::ffi; use super::ffi;
@ -40,7 +40,7 @@ pub struct InnerConnection {
unsafe impl Send for InnerConnection {} unsafe impl Send for InnerConnection {}
impl InnerConnection { impl InnerConnection {
#[allow(clippy::mutex_atomic)] #[allow(clippy::mutex_atomic, clippy::arc_with_non_send_sync)] // See unsafe impl Send / Sync for InterruptHandle
#[inline] #[inline]
pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection { pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection { InnerConnection {
@ -390,7 +390,7 @@ impl Drop for InnerConnection {
} }
} }
#[cfg(not(any(target_arch = "wasm32")))] #[cfg(not(any(target_arch = "wasm32", feature = "loadable_extension")))]
static SQLITE_INIT: std::sync::Once = std::sync::Once::new(); static SQLITE_INIT: std::sync::Once = std::sync::Once::new();
pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false); pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
@ -440,7 +440,9 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
Ok(()) Ok(())
} }
} else { } else {
#[cfg(not(feature = "loadable_extension"))]
SQLITE_INIT.call_once(|| { SQLITE_INIT.call_once(|| {
use std::sync::atomic::Ordering;
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) { if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
return; return;
} }

View File

@ -74,6 +74,7 @@ use crate::raw_statement::RawStatement;
use crate::types::ValueRef; use crate::types::ValueRef;
pub use crate::cache::CachedStatement; pub use crate::cache::CachedStatement;
#[cfg(feature = "column_decltype")]
pub use crate::column::Column; pub use crate::column::Column;
pub use crate::error::{to_sqlite_error, Error}; pub use crate::error::{to_sqlite_error, Error};
pub use crate::ffi::ErrorCode; pub use crate::ffi::ErrorCode;
@ -82,9 +83,14 @@ pub use crate::load_extension_guard::LoadExtensionGuard;
pub use crate::params::{params_from_iter, Params, ParamsFromIter}; pub use crate::params::{params_from_iter, Params, ParamsFromIter};
pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows}; pub use crate::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
pub use crate::statement::{Statement, StatementStatus}; pub use crate::statement::{Statement, StatementStatus};
#[cfg(feature = "modern_sqlite")]
pub use crate::transaction::TransactionState;
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
pub use crate::types::ToSql; pub use crate::types::ToSql;
pub use crate::version::*; pub use crate::version::*;
#[cfg(feature = "rusqlite-macros")]
#[doc(hidden)]
pub use rusqlite_macros::__bind;
mod error; mod error;
@ -215,6 +221,51 @@ macro_rules! named_params {
}; };
} }
/// Captured identifiers in SQL
///
/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not
/// work).
/// * `$x.y` expression does not work.
///
/// # Example
///
/// ```rust, no_run
/// # use rusqlite::{prepare_and_bind, Connection, Result, Statement};
///
/// fn misc(db: &Connection) -> Result<Statement> {
/// let name = "Lisa";
/// let age = 8;
/// let smart = true;
/// Ok(prepare_and_bind!(db, "SELECT $name, @age, :smart;"))
/// }
/// ```
#[cfg(feature = "rusqlite-macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite-macros")))]
#[macro_export]
macro_rules! prepare_and_bind {
($conn:expr, $sql:literal) => {{
let mut stmt = $conn.prepare($sql)?;
$crate::__bind!(stmt $sql);
stmt
}};
}
/// Captured identifiers in SQL
///
/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not
/// work).
/// * `$x.y` expression does not work.
#[cfg(feature = "rusqlite-macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite-macros")))]
#[macro_export]
macro_rules! prepare_cached_and_bind {
($conn:expr, $sql:literal) => {{
let mut stmt = $conn.prepare_cached($sql)?;
$crate::__bind!(stmt $sql);
stmt
}};
}
/// A typedef of the result returned by many methods. /// A typedef of the result returned by many methods.
pub type Result<T, E = Error> = result::Result<T, E>; pub type Result<T, E = Error> = result::Result<T, E>;
@ -568,7 +619,7 @@ impl Connection {
#[inline] #[inline]
pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> { pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
self.prepare(sql) self.prepare(sql)
.and_then(|mut stmt| stmt.check_no_tail().and_then(|_| stmt.execute(params))) .and_then(|mut stmt| stmt.check_no_tail().and_then(|()| stmt.execute(params)))
} }
/// 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.
@ -651,7 +702,7 @@ impl Connection {
// https://sqlite.org/tclsqlite.html#onecolumn // https://sqlite.org/tclsqlite.html#onecolumn
#[cfg(test)] #[cfg(test)]
pub(crate) fn one_column<T: crate::types::FromSql>(&self, sql: &str) -> Result<T> { pub(crate) fn one_column<T: types::FromSql>(&self, sql: &str) -> Result<T> {
self.query_row(sql, [], |r| r.get(0)) self.query_row(sql, [], |r| r.get(0))
} }
@ -901,6 +952,17 @@ impl Connection {
}) })
} }
/// Like SQLITE_EXTENSION_INIT2 macro
#[cfg(feature = "loadable_extension")]
#[cfg_attr(docsrs, doc(cfg(feature = "loadable_extension")))]
pub unsafe fn extension_init2(
db: *mut ffi::sqlite3,
p_api: *mut ffi::sqlite3_api_routines,
) -> Result<Connection> {
ffi::rusqlite_extension_init2(p_api)?;
Connection::from_handle(db)
}
/// Create a `Connection` from a raw owned handle. /// Create a `Connection` from a raw owned handle.
/// ///
/// The returned connection will attempt to close the inner connection /// The returned connection will attempt to close the inner connection
@ -912,7 +974,7 @@ impl Connection {
/// This function is unsafe because improper use may impact the Connection. /// This function is unsafe because improper use may impact the Connection.
/// In particular, it should only be called on connections created /// In particular, it should only be called on connections created
/// and owned by the caller, e.g. as a result of calling /// and owned by the caller, e.g. as a result of calling
/// ffi::sqlite3_open(). /// `ffi::sqlite3_open`().
#[inline] #[inline]
pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result<Connection> { pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result<Connection> {
let db = InnerConnection::new(db, true); let db = InnerConnection::new(db, true);
@ -1432,7 +1494,7 @@ mod test {
#[test] #[test]
#[cfg(feature = "extra_check")] #[cfg(feature = "extra_check")]
fn test_execute_select() { fn test_execute_select_with_no_row() {
let db = checked_memory_handle(); let db = checked_memory_handle();
let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err(); let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err();
assert_eq!( assert_eq!(
@ -1442,6 +1504,13 @@ mod test {
); );
} }
#[test]
fn test_execute_select_with_row() {
let db = checked_memory_handle();
let err = db.execute("SELECT 1", []).unwrap_err();
assert_eq!(err, Error::ExecuteReturnedResults);
}
#[test] #[test]
#[cfg(feature = "extra_check")] #[cfg(feature = "extra_check")]
fn test_execute_multiple() { fn test_execute_multiple() {
@ -1812,7 +1881,7 @@ mod test {
#[test] #[test]
fn test_from_handle_owned() -> Result<()> { fn test_from_handle_owned() -> Result<()> {
let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut(); 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) }; let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const c_char, &mut handle) };
assert_eq!(r, ffi::SQLITE_OK); assert_eq!(r, ffi::SQLITE_OK);
let db = unsafe { Connection::from_handle_owned(handle) }?; let db = unsafe { Connection::from_handle_owned(handle) }?;
db.execute_batch("PRAGMA VACUUM")?; db.execute_batch("PRAGMA VACUUM")?;
@ -2114,9 +2183,25 @@ mod test {
} }
#[test] #[test]
pub fn db_readonly() -> Result<()> { fn db_readonly() -> Result<()> {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
assert!(!db.is_readonly(MAIN_DB)?); assert!(!db.is_readonly(MAIN_DB)?);
Ok(()) Ok(())
} }
#[test]
#[cfg(feature = "rusqlite-macros")]
fn prepare_and_bind() -> Result<()> {
let db = Connection::open_in_memory()?;
let name = "Lisa";
let age = 8;
let mut stmt = prepare_and_bind!(db, "SELECT $name, $age;");
let (v1, v2) = stmt
.raw_query()
.next()
.and_then(|o| o.ok_or(Error::QueryReturnedNoRows))
.and_then(|r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))?;
assert_eq!((v1.as_str(), v2), (name, age));
Ok(())
}
} }

View File

@ -9,6 +9,7 @@ use std::os::raw::c_int;
/// See the official documentation for more information: /// See the official documentation for more information:
/// - <https://www.sqlite.org/c3ref/c_limit_attached.html> /// - <https://www.sqlite.org/c3ref/c_limit_attached.html>
/// - <https://www.sqlite.org/limits.html> /// - <https://www.sqlite.org/limits.html>
#[derive(Copy, Clone, Debug)]
#[repr(i32)] #[repr(i32)]
#[non_exhaustive] #[non_exhaustive]
#[allow(clippy::upper_case_acronyms, non_camel_case_types)] #[allow(clippy::upper_case_acronyms, non_camel_case_types)]

View File

@ -384,7 +384,6 @@ mod test {
let mut rows = table_info.query(["sqlite_master"])?; let mut rows = table_info.query(["sqlite_master"])?;
while let Some(row) = rows.next()? { while let Some(row) = rows.next()? {
let row = row;
let column: String = row.get(1)?; let column: String = row.get(1)?;
columns.push(column); columns.push(column);
} }

View File

@ -14,9 +14,11 @@ pub struct Rows<'stmt> {
impl<'stmt> Rows<'stmt> { impl<'stmt> Rows<'stmt> {
#[inline] #[inline]
fn reset(&mut self) { fn reset(&mut self) -> Result<()> {
if let Some(stmt) = self.stmt.take() { if let Some(stmt) = self.stmt.take() {
stmt.reset(); stmt.reset()
} else {
Ok(())
} }
} }
@ -29,8 +31,8 @@ impl<'stmt> Rows<'stmt> {
/// This interface is not compatible with Rust's `Iterator` trait, because /// This interface is not compatible with Rust's `Iterator` trait, because
/// the lifetime of the returned row is tied to the lifetime of `self`. /// the lifetime of the returned row is tied to the lifetime of `self`.
/// This is a fallible "streaming iterator". For a more natural interface, /// This is a fallible "streaming iterator". For a more natural interface,
/// consider using [`query_map`](crate::Statement::query_map) or /// consider using [`query_map`](Statement::query_map) or
/// [`query_and_then`](crate::Statement::query_and_then) instead, which /// [`query_and_then`](Statement::query_and_then) instead, which
/// return types that implement `Iterator`. /// return types that implement `Iterator`.
#[allow(clippy::should_implement_trait)] // cannot implement Iterator #[allow(clippy::should_implement_trait)] // cannot implement Iterator
#[inline] #[inline]
@ -105,6 +107,7 @@ impl<'stmt> Rows<'stmt> {
} }
impl Drop for Rows<'_> { impl Drop for Rows<'_> {
#[allow(unused_must_use)]
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
self.reset(); self.reset();
@ -217,12 +220,12 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
Ok(()) Ok(())
} }
Ok(false) => { Ok(false) => {
self.reset(); let r = self.reset();
self.row = None; self.row = None;
Ok(()) r
} }
Err(e) => { Err(e) => {
self.reset(); let _ = self.reset(); // prevents infinite loop on error
self.row = None; self.row = None;
Err(e) Err(e)
} }
@ -247,7 +250,7 @@ pub struct Row<'stmt> {
impl<'stmt> Row<'stmt> { impl<'stmt> Row<'stmt> {
/// Get the value of a particular column of the result row. /// Get the value of a particular column of the result row.
/// ///
/// ## Failure /// # Panics
/// ///
/// Panics if calling [`row.get(idx)`](Row::get) would return an error, /// Panics if calling [`row.get(idx)`](Row::get) would return an error,
/// including: /// including:
@ -330,7 +333,7 @@ impl<'stmt> Row<'stmt> {
/// it can be difficult to use, and most callers will be better served by /// it can be difficult to use, and most callers will be better served by
/// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap). /// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap).
/// ///
/// ## Failure /// # Panics
/// ///
/// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an /// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an
/// error, including: /// error, including:
@ -585,4 +588,30 @@ mod tests {
// We don't test one bigger because it's unimplemented // We don't test one bigger because it's unimplemented
Ok(()) Ok(())
} }
#[test]
#[cfg(feature = "bundled")]
fn pathological_case() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
"CREATE TABLE foo(x);
CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;",
)?;
let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?;
{
let iterator_count = stmt.query_map([], |_| Ok(()))?.count();
assert_eq!(1, iterator_count); // should be 0
use fallible_streaming_iterator::FallibleStreamingIterator;
let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0);
assert_eq!(0, fallible_iterator_count);
}
{
let iterator_last = stmt.query_map([], |_| Ok(()))?.last();
assert!(iterator_last.is_some()); // should be none
use fallible_iterator::FallibleIterator;
let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last();
assert!(fallible_iterator_last.is_err());
}
Ok(())
}
} }

View File

@ -22,8 +22,9 @@ pub struct OwnedData {
} }
impl OwnedData { impl OwnedData {
/// SAFETY: Caller must be certain that `ptr` is allocated by /// # Safety
/// `sqlite3_malloc`. ///
/// Caller must be certain that `ptr` is allocated by `sqlite3_malloc`.
pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self { pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
Self { ptr, sz } Self { ptr, sz }
} }
@ -65,7 +66,7 @@ impl<'conn> Deref for Data<'conn> {
impl Connection { impl Connection {
/// Serialize a database. /// Serialize a database.
pub fn serialize<'conn>(&'conn self, schema: DatabaseName<'_>) -> Result<Data<'conn>> { pub fn serialize(&self, schema: DatabaseName) -> Result<Data> {
let schema = schema.as_cstring()?; let schema = schema.as_cstring()?;
let mut sz = 0; let mut sz = 0;
let mut ptr: *mut u8 = unsafe { let mut ptr: *mut u8 = unsafe {
@ -142,7 +143,9 @@ mod test {
let db = Connection::open_in_memory()?; let db = Connection::open_in_memory()?;
db.execute_batch("CREATE TABLE x AS SELECT 'data'")?; db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
let data = db.serialize(DatabaseName::Main)?; let data = db.serialize(DatabaseName::Main)?;
let Data::Owned(data) = data else { panic!("expected OwnedData")}; let Data::Owned(data) = data else {
panic!("expected OwnedData")
};
assert!(data.sz > 0); assert!(data.sz > 0);
Ok(()) Ok(())
} }
@ -152,7 +155,9 @@ mod test {
let src = Connection::open_in_memory()?; let src = Connection::open_in_memory()?;
src.execute_batch("CREATE TABLE x AS SELECT 'data'")?; src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
let data = src.serialize(DatabaseName::Main)?; let data = src.serialize(DatabaseName::Main)?;
let Data::Owned(data) = data else { panic!("expected OwnedData")}; let Data::Owned(data) = data else {
panic!("expected OwnedData")
};
let mut dst = Connection::open_in_memory()?; let mut dst = Connection::open_in_memory()?;
dst.deserialize(DatabaseName::Main, data, false)?; dst.deserialize(DatabaseName::Main, data, false)?;

View File

@ -405,7 +405,7 @@ impl Drop for ChangesetIter<'_> {
} }
/// An item passed to a conflict-handler by /// An item passed to a conflict-handler by
/// [`Connection::apply`](crate::Connection::apply), or an item generated by /// [`Connection::apply`](Connection::apply), or an item generated by
/// [`ChangesetIter::next`](ChangesetIter::next). /// [`ChangesetIter::next`](ChangesetIter::next).
// TODO enum ? Delete, Insert, Update, ... // TODO enum ? Delete, Insert, Update, ...
pub struct ChangesetItem { pub struct ChangesetItem {

View File

@ -435,6 +435,10 @@ impl Statement<'_> {
/// ///
/// Will return `None` if the column index is out of bounds or if the /// Will return `None` if the column index is out of bounds or if the
/// parameter is positional. /// parameter is positional.
///
/// # Panics
///
/// Panics when parameter name is not valid UTF-8.
#[inline] #[inline]
pub fn parameter_name(&self, index: usize) -> Option<&'_ str> { pub fn parameter_name(&self, index: usize) -> Option<&'_ str> {
self.stmt.bind_parameter_name(index as i32).map(|name| { self.stmt.bind_parameter_name(index as i32).map(|name| {
@ -450,7 +454,7 @@ impl Statement<'_> {
{ {
let expected = self.stmt.bind_parameter_count(); let expected = self.stmt.bind_parameter_count();
let mut index = 0; let mut index = 0;
for p in params.into_iter() { for p in params {
index += 1; // The leftmost SQL parameter has an index of 1. index += 1; // The leftmost SQL parameter has an index of 1.
if index > expected { if index > expected {
break; break;
@ -646,9 +650,12 @@ impl Statement<'_> {
fn execute_with_bound_parameters(&mut self) -> Result<usize> { fn execute_with_bound_parameters(&mut self) -> Result<usize> {
self.check_update()?; self.check_update()?;
let r = self.stmt.step(); let r = self.stmt.step();
self.stmt.reset(); let rr = self.stmt.reset();
match r { match r {
ffi::SQLITE_DONE => Ok(self.conn.changes() as usize), ffi::SQLITE_DONE => match rr {
ffi::SQLITE_OK => Ok(self.conn.changes() as usize),
_ => Err(self.conn.decode_result(rr).unwrap_err()),
},
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults), ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
_ => Err(self.conn.decode_result(r).unwrap_err()), _ => Err(self.conn.decode_result(r).unwrap_err()),
} }
@ -744,7 +751,7 @@ impl Statement<'_> {
/// Reset all bindings /// Reset all bindings
pub fn clear_bindings(&mut self) { pub fn clear_bindings(&mut self) {
self.stmt.clear_bindings() self.stmt.clear_bindings();
} }
} }
@ -843,8 +850,11 @@ impl Statement<'_> {
} }
#[inline] #[inline]
pub(super) fn reset(&self) -> c_int { pub(super) fn reset(&self) -> Result<()> {
self.stmt.reset() match self.stmt.reset() {
ffi::SQLITE_OK => Ok(()),
code => Err(self.conn.decode_result(code).unwrap_err()),
}
} }
} }
@ -1270,7 +1280,7 @@ mod test {
assert_eq!(0, stmt.column_count()); assert_eq!(0, stmt.column_count());
stmt.parameter_index("test").unwrap(); stmt.parameter_index("test").unwrap();
stmt.step().unwrap_err(); stmt.step().unwrap_err();
stmt.reset(); stmt.reset().unwrap(); // SQLITE_OMIT_AUTORESET = false
stmt.execute([]).unwrap_err(); stmt.execute([]).unwrap_err();
Ok(()) Ok(())
} }

View File

@ -8,8 +8,7 @@ use std::ptr;
use std::time::Duration; use std::time::Duration;
use super::ffi; use super::ffi;
use crate::error::error_from_sqlite_code; use crate::Connection;
use crate::{Connection, Result};
/// Set up the process-wide SQLite error logging callback. /// Set up the process-wide SQLite error logging callback.
/// ///
@ -25,7 +24,8 @@ use crate::{Connection, Result};
/// * It must be threadsafe if SQLite is used in a multithreaded way. /// * It must be threadsafe if SQLite is used in a multithreaded way.
/// ///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html). /// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> { #[cfg(not(feature = "loadable_extension"))]
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> crate::Result<()> {
extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) { extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() }; let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };
@ -48,7 +48,7 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
if rc == ffi::SQLITE_OK { if rc == ffi::SQLITE_OK {
Ok(()) Ok(())
} else { } else {
Err(error_from_sqlite_code(rc, None)) Err(crate::error::error_from_sqlite_code(rc, None))
} }
} }

View File

@ -122,7 +122,7 @@ impl Transaction<'_> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE", TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
}; };
conn.execute_batch(query).map(move |_| Transaction { conn.execute_batch(query).map(move |()| Transaction {
conn, conn,
drop_behavior: DropBehavior::Rollback, drop_behavior: DropBehavior::Rollback,
}) })
@ -251,7 +251,7 @@ impl Savepoint<'_> {
fn with_name_<T: Into<String>>(conn: &Connection, name: T) -> Result<Savepoint<'_>> { fn with_name_<T: Into<String>>(conn: &Connection, name: T) -> Result<Savepoint<'_>> {
let name = name.into(); let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {name}")) conn.execute_batch(&format!("SAVEPOINT {name}"))
.map(|_| Savepoint { .map(|()| Savepoint {
conn, conn,
name, name,
drop_behavior: DropBehavior::Rollback, drop_behavior: DropBehavior::Rollback,
@ -346,8 +346,8 @@ impl Savepoint<'_> {
match self.drop_behavior() { match self.drop_behavior() {
DropBehavior::Commit => self DropBehavior::Commit => self
.commit_() .commit_()
.or_else(|_| self.rollback().and_then(|_| self.commit_())), .or_else(|_| self.rollback().and_then(|()| self.commit_())),
DropBehavior::Rollback => self.rollback().and_then(|_| self.commit_()), DropBehavior::Rollback => self.rollback().and_then(|()| self.commit_()),
DropBehavior::Ignore => Ok(()), DropBehavior::Ignore => Ok(()),
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."), DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
} }
@ -471,8 +471,7 @@ impl Connection {
/// ///
/// The savepoint defaults to rolling back when it is dropped. If you want /// The savepoint defaults to rolling back when it is dropped. If you want
/// the savepoint to commit, you must call [`commit`](Savepoint::commit) or /// the savepoint to commit, you must call [`commit`](Savepoint::commit) or
/// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint:: /// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::set_drop_behavior).
/// set_drop_behavior).
/// ///
/// ## Example /// ## Example
/// ///

View File

@ -111,7 +111,7 @@ pub struct Null;
/// SQLite data types. /// SQLite data types.
/// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html). /// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Type { pub enum Type {
/// NULL /// NULL
Null, Null,

View File

@ -36,8 +36,7 @@ impl ValueRef<'_> {
impl<'a> ValueRef<'a> { impl<'a> ValueRef<'a> {
/// If `self` is case `Integer`, returns the integral value. Otherwise, /// If `self` is case `Integer`, returns the integral value. Otherwise,
/// returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_i64(&self) -> FromSqlResult<i64> { pub fn as_i64(&self) -> FromSqlResult<i64> {
match *self { match *self {
@ -48,8 +47,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None. /// If `self` is case `Null` returns None.
/// If `self` is case `Integer`, returns the integral value. /// If `self` is case `Integer`, returns the integral value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_i64_or_null(&self) -> FromSqlResult<Option<i64>> { pub fn as_i64_or_null(&self) -> FromSqlResult<Option<i64>> {
match *self { match *self {
@ -60,8 +58,7 @@ impl<'a> ValueRef<'a> {
} }
/// If `self` is case `Real`, returns the floating point value. Otherwise, /// If `self` is case `Real`, returns the floating point value. Otherwise,
/// returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_f64(&self) -> FromSqlResult<f64> { pub fn as_f64(&self) -> FromSqlResult<f64> {
match *self { match *self {
@ -72,8 +69,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None. /// If `self` is case `Null` returns None.
/// If `self` is case `Real`, returns the floating point value. /// If `self` is case `Real`, returns the floating point value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_f64_or_null(&self) -> FromSqlResult<Option<f64>> { pub fn as_f64_or_null(&self) -> FromSqlResult<Option<f64>> {
match *self { match *self {
@ -97,8 +93,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None. /// If `self` is case `Null` returns None.
/// If `self` is case `Text`, returns the string value. /// If `self` is case `Text`, returns the string value.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_str_or_null(&self) -> FromSqlResult<Option<&'a str>> { pub fn as_str_or_null(&self) -> FromSqlResult<Option<&'a str>> {
match *self { match *self {
@ -122,8 +117,7 @@ impl<'a> ValueRef<'a> {
/// If `self` is case `Null` returns None. /// If `self` is case `Null` returns None.
/// If `self` is case `Blob`, returns the byte slice. /// If `self` is case `Blob`, returns the byte slice.
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error:: /// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
/// InvalidColumnType).
#[inline] #[inline]
pub fn as_blob_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> { pub fn as_blob_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> {
match *self { match *self {
@ -133,7 +127,7 @@ impl<'a> ValueRef<'a> {
} }
} }
/// Returns the byte slice that makes up this ValueRef if it's either /// Returns the byte slice that makes up this `ValueRef` if it's either
/// [`ValueRef::Blob`] or [`ValueRef::Text`]. /// [`ValueRef::Blob`] or [`ValueRef::Text`].
#[inline] #[inline]
pub fn as_bytes(&self) -> FromSqlResult<&'a [u8]> { pub fn as_bytes(&self) -> FromSqlResult<&'a [u8]> {

View File

@ -14,6 +14,10 @@ pub fn version_number() -> i32 {
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2. /// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
/// ///
/// See [`sqlite3_libversion()`](https://www.sqlite.org/c3ref/libversion.html). /// See [`sqlite3_libversion()`](https://www.sqlite.org/c3ref/libversion.html).
///
/// # Panics
///
/// Panics when version is not valid UTF-8.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn version() -> &'static str { pub fn version() -> &'static str {

View File

@ -373,6 +373,7 @@ bitflags::bitflags! {
/// Virtual table scan flags /// Virtual table scan flags
/// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details. /// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details.
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct IndexFlags: ::std::os::raw::c_int { pub struct IndexFlags: ::std::os::raw::c_int {
/// Default /// Default
const NONE = 0; const NONE = 0;

View File

@ -1,11 +1,11 @@
//! Ensure we reject connections when SQLite is in single-threaded mode, as it //! Ensure we reject connections when SQLite is in single-threaded mode, as it
//! would violate safety if multiple Rust threads tried to use connections. //! would violate safety if multiple Rust threads tried to use connections.
use rusqlite::ffi; #[cfg(not(feature = "loadable_extension"))]
use rusqlite::Connection;
#[test] #[test]
fn test_error_when_singlethread_mode() { fn test_error_when_singlethread_mode() {
use rusqlite::ffi;
use rusqlite::Connection;
// put SQLite into single-threaded mode // put SQLite into single-threaded mode
unsafe { unsafe {
// Note: macOS system SQLite seems to return an error if you attempt to // Note: macOS system SQLite seems to return an error if you attempt to