mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-25 02:21:37 +08:00
Merge branch 'rusqlite:master' into wasm
This commit is contained in:
commit
7bc2214ec6
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@ -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' --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
|
||||
- name: Static build
|
||||
# Do we expect this to work / should we test with gnu toolchain?
|
||||
|
17
Cargo.toml
17
Cargo.toml
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "rusqlite"
|
||||
# Note: Update version in README.md when you change this.
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
authors = ["The rusqlite developers"]
|
||||
edition = "2021"
|
||||
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"]
|
||||
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
|
||||
limits = []
|
||||
loadable_extension = ["libsqlite3-sys/loadable_extension"]
|
||||
hooks = []
|
||||
i128_blob = []
|
||||
sqlcipher = ["libsqlite3-sys/sqlcipher"]
|
||||
@ -124,6 +125,7 @@ fallible-iterator = "0.3"
|
||||
fallible-streaming-iterator = "0.1"
|
||||
uuid = { version = "1.0", optional = true }
|
||||
smallvec = "1.6.1"
|
||||
rusqlite-macros = { path = "rusqlite-macros", version = "0.1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
doc-comment = "0.3"
|
||||
@ -138,7 +140,7 @@ bencher = "0.1"
|
||||
|
||||
[dependencies.libsqlite3-sys]
|
||||
path = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
|
||||
[[test]]
|
||||
name = "config_log"
|
||||
@ -158,8 +160,17 @@ harness = false
|
||||
name = "exec"
|
||||
harness = false
|
||||
|
||||
[[example]]
|
||||
name = "loadable_extension"
|
||||
crate-type = ["cdylib"]
|
||||
required-features = ["loadable_extension", "functions", "trace"]
|
||||
|
||||
[[example]]
|
||||
name = "load_extension"
|
||||
required-features = ["load_extension", "bundled", "functions", "trace"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["modern-full"]
|
||||
features = ["modern-full", "rusqlite-macros"]
|
||||
all-features = false
|
||||
no-default-features = true
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
|
@ -27,7 +27,7 @@ In your Cargo.toml:
|
||||
# That said, it's not ideal for all scenarios and in particular, generic
|
||||
# libraries built around `rusqlite` should probably not enable it, which
|
||||
# is why it is not a default feature -- it could become hard to disable.
|
||||
rusqlite = { version = "0.29.0", features = ["bundled"] }
|
||||
rusqlite = { version = "0.30.0", features = ["bundled"] }
|
||||
```
|
||||
|
||||
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
|
||||
[cc](https://crates.io/crates/cc) crate to compile SQLite or SQLCipher from source and
|
||||
link against that. This source is embedded in the `libsqlite3-sys` crate and
|
||||
is currently SQLite 3.41.2 (as of `rusqlite` 0.29.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:
|
||||
is currently SQLite 3.44.0 (as of `rusqlite` 0.30.0 / `libsqlite3-sys`
|
||||
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
|
||||
[dependencies.rusqlite]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = ["bundled"]
|
||||
```
|
||||
* When using any of the `bundled` features, the build script will honor `SQLITE_MAX_VARIABLE_NUMBER` and `SQLITE_MAX_EXPR_DEPTH` variables. It will also honor a `LIBSQLITE3_FLAGS` variable, which can have a format like `"-USQLITE_ALPHA -DSQLITE_BETA SQLITE_GAMMA ..."`. That would disable the `SQLITE_ALPHA` flag, and set the `SQLITE_BETA` and `SQLITE_GAMMA` flags. (The initial `-D` can be omitted, as on the last one.)
|
||||
|
22
examples/load_extension.rs
Normal file
22
examples/load_extension.rs
Normal 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(())
|
||||
}
|
50
examples/loadable_extension.rs
Normal file
50
examples/loadable_extension.rs
Normal 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(())
|
||||
}
|
28
examples/persons/README.md
Normal file
28
examples/persons/README.md
Normal 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
42
examples/persons/main.rs
Normal 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(())
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
authors = ["The rusqlite developers"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/rusqlite/rusqlite"
|
||||
description = "Native bindings to the libsqlite3 library"
|
||||
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
|
||||
# `buildtime_bindgen` is enabled.
|
||||
bundled_bindings = []
|
||||
loadable_extension = ["prettyplease", "quote", "syn"]
|
||||
# sqlite3_unlock_notify >= 3.6.12
|
||||
unlock_notify = []
|
||||
# 3.13.0
|
||||
@ -44,7 +45,13 @@ winsqlite3 = []
|
||||
openssl-sys = { version = "0.9", optional = true }
|
||||
|
||||
[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 }
|
||||
cc = { version = "1.0", 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"] }
|
||||
|
6956
libsqlite3-sys/bindgen-bindings/bindgen_3.14.0_ext.rs
Normal file
6956
libsqlite3-sys/bindgen-bindings/bindgen_3.14.0_ext.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -28,8 +28,12 @@ fn is_compiler(compiler_name: &str) -> bool {
|
||||
|
||||
/// Copy bindgen file from `dir` to `out_path`.
|
||||
fn copy_bindings<T: AsRef<Path>>(dir: &str, bindgen_name: &str, out_path: T) {
|
||||
std::fs::copy(format!("{dir}/{bindgen_name}"), out_path)
|
||||
.expect("Could not copy bindings to output directory");
|
||||
let from = if cfg!(feature = "loadable_extension") {
|
||||
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() {
|
||||
@ -38,12 +42,14 @@ fn main() {
|
||||
if cfg!(feature = "in_gecko") {
|
||||
// When inside mozilla-central, we are included into the build with
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
} else if cfg!(all(
|
||||
feature = "sqlcipher",
|
||||
@ -106,7 +112,7 @@ mod build_bundled {
|
||||
}
|
||||
#[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=sqlite3/wasm32-wasi-vfs.c");
|
||||
@ -233,11 +239,16 @@ mod build_bundled {
|
||||
if !win_target() {
|
||||
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") {
|
||||
cfg.flag("-DSQLITE_OS_OTHER")
|
||||
cfg.flag("-USQLITE_THREADSAFE")
|
||||
.flag("-DSQLITE_THREADSAFE=0")
|
||||
// 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") {
|
||||
cfg.file("sqlite3/wasm32-wasi-vfs.c");
|
||||
}
|
||||
@ -363,16 +374,30 @@ impl From<HeaderLocation> for String {
|
||||
HeaderLocation::FromEnvironment => {
|
||||
let prefix = env_prefix();
|
||||
let mut header = env::var(format!("{prefix}_INCLUDE_DIR")).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
|
||||
prefix, prefix
|
||||
)
|
||||
panic!("{prefix}_INCLUDE_DIR must be set if {prefix}_LIB_DIR is set")
|
||||
});
|
||||
header.push_str(if cfg!(feature = "loadable_extension") {
|
||||
"/sqlite3ext.h"
|
||||
} else {
|
||||
"/sqlite3.h"
|
||||
});
|
||||
header.push_str("/sqlite3.h");
|
||||
header
|
||||
}
|
||||
HeaderLocation::Wrapper => "wrapper.h".into(),
|
||||
HeaderLocation::FromPath(path) => format!("{}/sqlite3.h", path),
|
||||
HeaderLocation::Wrapper => if cfg!(feature = "loadable_extension") {
|
||||
"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
|
||||
// have runtime version checks and there are good reasons to not
|
||||
// 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 {
|
||||
bindings::write_to_out_dir(header, out_path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
fn find_link_mode() -> &'static str {
|
||||
// If the user specifies SQLITE3_STATIC (or SQLCIPHER_STATIC), do static
|
||||
// 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
|
||||
// useful if you need to ensure whatever crypto library sqlcipher relies
|
||||
// on is available, for example.
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
println!("cargo:link-target={link_lib}");
|
||||
|
||||
if win_target() && cfg!(feature = "winsqlite3") {
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
println!("cargo:rustc-link-lib=dylib={link_lib}");
|
||||
return HeaderLocation::Wrapper;
|
||||
}
|
||||
@ -440,6 +468,7 @@ mod build_linked {
|
||||
// Try to use pkg-config to determine link commands
|
||||
let pkgconfig_path = Path::new(&dir).join("pkgconfig");
|
||||
env::set_var("PKG_CONFIG_PATH", pkgconfig_path);
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
if pkg_config::Config::new().probe(link_lib).is_err() {
|
||||
// Otherwise just emit the bare minimum link commands.
|
||||
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
|
||||
// output /usr/lib explicitly, but that can introduce other linking problems;
|
||||
// see https://github.com/rusqlite/rusqlite/issues/207.
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
println!("cargo:rustc-link-lib={}={link_lib}", find_link_mode());
|
||||
HeaderLocation::Wrapper
|
||||
}
|
||||
@ -494,7 +524,7 @@ mod bindings {
|
||||
|
||||
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) {
|
||||
let name = PREBUILT_BINDGENS[PREBUILT_BINDGENS.len() - 1];
|
||||
@ -546,10 +576,14 @@ mod bindings {
|
||||
.disable_nested_struct_naming()
|
||||
.trust_clang_mangling(false)
|
||||
.header(header.clone())
|
||||
.parse_callbacks(Box::new(SqliteTypeChooser))
|
||||
.blocklist_function("sqlite3_auto_extension")
|
||||
.raw_line(
|
||||
r#"extern "C" {
|
||||
.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")
|
||||
.raw_line(
|
||||
r#"extern "C" {
|
||||
pub fn sqlite3_auto_extension(
|
||||
xEntryPoint: ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
@ -560,10 +594,10 @@ mod bindings {
|
||||
>,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}"#,
|
||||
)
|
||||
.blocklist_function("sqlite3_cancel_auto_extension")
|
||||
.raw_line(
|
||||
r#"extern "C" {
|
||||
)
|
||||
.blocklist_function("sqlite3_cancel_auto_extension")
|
||||
.raw_line(
|
||||
r#"extern "C" {
|
||||
pub fn sqlite3_cancel_auto_extension(
|
||||
xEntryPoint: ::std::option::Option<
|
||||
unsafe extern "C" fn(
|
||||
@ -574,7 +608,8 @@ mod bindings {
|
||||
>,
|
||||
) -> ::std::os::raw::c_int;
|
||||
}"#,
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
if cfg!(any(feature = "sqlcipher", feature = "bundled-sqlcipher")) {
|
||||
bindings = bindings.clang_arg("-DSQLITE_HAS_CODEC");
|
||||
@ -645,11 +680,183 @@ mod bindings {
|
||||
.blocklist_item("__.*");
|
||||
}
|
||||
|
||||
bindings
|
||||
let bindings = bindings
|
||||
.layout_tests(false)
|
||||
.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)
|
||||
.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,
|
||||
})?
|
||||
}
|
||||
}
|
||||
|
838
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
838
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
vendored
File diff suppressed because it is too large
Load Diff
8430
libsqlite3-sys/sqlite3/bindgen_bundled_version_ext.rs
vendored
Normal file
8430
libsqlite3-sys/sqlite3/bindgen_bundled_version_ext.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12594
libsqlite3-sys/sqlite3/sqlite3.c
vendored
12594
libsqlite3-sys/sqlite3/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
291
libsqlite3-sys/sqlite3/sqlite3.h
vendored
291
libsqlite3-sys/sqlite3/sqlite3.h
vendored
@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.42.0"
|
||||
#define SQLITE_VERSION_NUMBER 3042000
|
||||
#define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0"
|
||||
#define SQLITE_VERSION "3.44.0"
|
||||
#define SQLITE_VERSION_NUMBER 3044000
|
||||
#define SQLITE_SOURCE_ID "2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad81301"
|
||||
|
||||
/*
|
||||
** 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_DATA (SQLITE_IOERR | (32<<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_VTAB (SQLITE_LOCKED | (2<<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.
|
||||
**
|
||||
** <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.
|
||||
**
|
||||
** <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
|
||||
** from the database as records are returned in sorted order. The default
|
||||
** 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
|
||||
** [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
|
||||
** connections at all to the database. If so, it performs a checkpoint
|
||||
** 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
|
||||
** default) to enable them, and negative to leave the setting unchanged.
|
||||
** 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
|
||||
** 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
|
||||
** either generated columns or decending indexes.
|
||||
** either generated columns or descending indexes.
|
||||
** </dd>
|
||||
**
|
||||
** [[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
|
||||
** 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 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
|
||||
** zero or more [SQLITE_TRACE] constants.
|
||||
**
|
||||
** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
|
||||
** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
|
||||
** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P)
|
||||
** 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
|
||||
** 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:
|
||||
** <ul>
|
||||
** <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 new filename constructed using [sqlite3_create_filename()].
|
||||
** </ul>
|
||||
@ -3871,7 +3875,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
|
||||
/*
|
||||
** 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.
|
||||
**
|
||||
** 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
|
||||
** 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.
|
||||
** The application does not need to worry about freeing the result.
|
||||
** 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);
|
||||
|
||||
/*
|
||||
** 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
|
||||
** 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
|
||||
** 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.
|
||||
** ^ (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
|
||||
** 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
|
||||
@ -5260,20 +5300,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
|
||||
** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S
|
||||
** back to the beginning of its program.
|
||||
**
|
||||
** ^If the most recent call to [sqlite3_step(S)] for the
|
||||
** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],
|
||||
** or if [sqlite3_step(S)] has never before been called on S,
|
||||
** then [sqlite3_reset(S)] returns [SQLITE_OK].
|
||||
** ^The return code from [sqlite3_reset(S)] indicates whether or not
|
||||
** the previous evaluation of prepared statement S completed successfully.
|
||||
** ^If [sqlite3_step(S)] has never before been called on S or if
|
||||
** [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
|
||||
** [prepared statement] S indicated an error, then
|
||||
** [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
|
||||
** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
|
||||
*/
|
||||
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Create Or Redefine SQL Functions
|
||||
** KEYWORDS: {function creation routines}
|
||||
@ -5484,7 +5537,7 @@ SQLITE_API int sqlite3_create_window_function(
|
||||
** [application-defined SQL function]
|
||||
** that has side-effects or that could potentially leak sensitive information.
|
||||
** This will prevent attacks in which an application is tricked
|
||||
** into using a database file that has had its schema surreptiously
|
||||
** into using a database file that has had its schema surreptitiously
|
||||
** modified to invoke the application-defined function in ways that are
|
||||
** harmful.
|
||||
** <p>
|
||||
@ -5828,32 +5881,32 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
|
||||
** METHOD: sqlite3_context
|
||||
**
|
||||
** These functions may be used by (non-aggregate) SQL functions to
|
||||
** associate metadata with argument values. If the same value is passed to
|
||||
** multiple invocations of the same SQL function during query execution, under
|
||||
** some circumstances the associated metadata may be preserved. An example
|
||||
** of where this might be useful is in a regular-expression matching
|
||||
** function. The compiled version of the regular expression can be stored as
|
||||
** metadata associated with the pattern string.
|
||||
** associate auxiliary data with argument values. If the same argument
|
||||
** value is passed to multiple invocations of the same SQL function during
|
||||
** query execution, under some circumstances the associated auxiliary data
|
||||
** might be preserved. An example of where this might be useful is in a
|
||||
** regular-expression matching function. The compiled version of the regular
|
||||
** expression can be stored as auxiliary data associated with the pattern string.
|
||||
** Then as long as the pattern string remains the same,
|
||||
** the compiled regular expression can be reused on multiple
|
||||
** 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
|
||||
** 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
|
||||
** returns a NULL pointer.
|
||||
**
|
||||
** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
|
||||
** argument of the application-defined function. ^Subsequent
|
||||
** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
|
||||
** N-th argument of the application-defined function. ^Subsequent
|
||||
** 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
|
||||
** NULL if the metadata has been discarded.
|
||||
** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
|
||||
** NULL if the auxiliary data has been discarded.
|
||||
** ^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
|
||||
** once, when the metadata is discarded.
|
||||
** SQLite is free to discard the metadata at any time, including: <ul>
|
||||
** once, when the auxiliary data is discarded.
|
||||
** SQLite is free to discard the auxiliary data at any time, including: <ul>
|
||||
** <li> ^(when the corresponding function parameter changes)^, or
|
||||
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
|
||||
** 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
|
||||
** 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
|
||||
** 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
|
||||
** 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_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
|
||||
@ -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);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
|
||||
** CAPI3REF: Allowed return values from sqlite3_txn_state()
|
||||
** KEYWORDS: {transaction state}
|
||||
**
|
||||
** 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
|
||||
** previous invocations for that database connection. ^If the callback
|
||||
** 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
|
||||
** be some other error code if something goes wrong. The current
|
||||
** 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.
|
||||
** Those below are for version 3 and greater. */
|
||||
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.
|
||||
**
|
||||
** 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
|
||||
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
|
||||
** 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_RESTORE 6
|
||||
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_FK_NO_ACTION 7
|
||||
#define SQLITE_TESTCTRL_BITVEC_TEST 8
|
||||
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
|
||||
#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_TUNE 32
|
||||
#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
|
||||
@ -9617,7 +9733,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
|
||||
** [[SQLITE_VTAB_DIRECTONLY]]<dt>SQLITE_VTAB_DIRECTONLY</dt>
|
||||
** <dd>Calls of the form
|
||||
** [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
|
||||
** views.
|
||||
** </dd>
|
||||
@ -9807,7 +9923,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
|
||||
** communicated to the xBestIndex method as a
|
||||
** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
|
||||
** 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]
|
||||
** that invokes the [xFilter|xFilter() method] once for each value
|
||||
** 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,
|
||||
** 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
|
||||
** 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
|
||||
** the index of the column being written. In other cases, where the
|
||||
** 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
|
||||
** 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
|
||||
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
|
||||
** allocation error occurs.
|
||||
@ -10545,6 +10668,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
|
||||
** SQLite will try to increase the buffer size using sqlite3_realloc64()
|
||||
** 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
|
||||
** database is currently in a read transaction or is involved in a backup
|
||||
** 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
|
||||
** 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
|
||||
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
|
||||
** [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
|
||||
**
|
||||
@ -11671,6 +11816,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
|
||||
*/
|
||||
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
|
||||
** 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
|
||||
** 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
|
||||
** case, this function fails with SQLITE_SCHEMA. 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.
|
||||
** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
|
||||
** object has been configured with a database schema using the
|
||||
** sqlite3changegroup_schema() API, then it is possible to combine changesets
|
||||
** with different numbers of columns for a single table, provided that
|
||||
** 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);
|
||||
|
||||
@ -12010,10 +12192,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
|
||||
** <li>an insert change if all fields of the conflicting row match
|
||||
** the row being inserted.
|
||||
** </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_INVERT 0x0002
|
||||
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
|
||||
#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||
@ -12754,7 +12943,7 @@ struct Fts5PhraseIter {
|
||||
** See xPhraseFirstColumn above.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
int iVersion; /* Currently always set to 2 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
@ -12983,8 +13172,8 @@ struct Fts5ExtensionApi {
|
||||
** 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
|
||||
** provide synonyms when tokenizing document text (method (2)) or query
|
||||
** text (method (3)), not both. Doing so will not cause any errors, but is
|
||||
** provide synonyms when tokenizing document text (method (3)) or query
|
||||
** text (method (2)), not both. Doing so will not cause any errors, but is
|
||||
** inefficient.
|
||||
*/
|
||||
typedef struct Fts5Tokenizer Fts5Tokenizer;
|
||||
@ -13032,7 +13221,7 @@ struct fts5_api {
|
||||
int (*xCreateTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pContext,
|
||||
void *pUserData,
|
||||
fts5_tokenizer *pTokenizer,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
@ -13041,7 +13230,7 @@ struct fts5_api {
|
||||
int (*xFindTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void **ppContext,
|
||||
void **ppUserData,
|
||||
fts5_tokenizer *pTokenizer
|
||||
);
|
||||
|
||||
@ -13049,7 +13238,7 @@ struct fts5_api {
|
||||
int (*xCreateFunction)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pContext,
|
||||
void *pUserData,
|
||||
fts5_extension_function xFunction,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
10
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
10
libsqlite3-sys/sqlite3/sqlite3ext.h
vendored
@ -361,6 +361,11 @@ struct sqlite3_api_routines {
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
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
|
||||
/* Version 3.41.0 and later */
|
||||
#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) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
@ -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_ROLLBACK_ATOMIC: c_int = super::SQLITE_IOERR | (31 << 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);
|
||||
|
||||
@ -219,6 +221,8 @@ pub fn code_to_str(code: c_int) -> &'static str {
|
||||
SQLITE_IOERR_COMMIT_ATOMIC => "SQLITE_IOERR_COMMIT_ATOMIC",
|
||||
SQLITE_IOERR_ROLLBACK_ATOMIC => "SQLITE_IOERR_ROLLBACK_ATOMIC",
|
||||
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",
|
||||
SQLITE_LOCKED_VTAB => "SQLITE_LOCKED_VTAB",
|
||||
@ -269,3 +273,32 @@ pub fn code_to_str(code: c_int) -> &'static str {
|
||||
_ => "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 {}
|
||||
|
@ -3,26 +3,38 @@
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
echo "$SCRIPT_DIR"
|
||||
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
|
||||
cargo clean
|
||||
mkdir -p "$SCRIPT_DIR/../target" "$SCRIPT_DIR/sqlite3"
|
||||
cargo clean -p libsqlite3-sys
|
||||
TARGET_DIR="$SCRIPT_DIR/../target"
|
||||
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
|
||||
SQLITE=sqlite-amalgamation-3420000
|
||||
SQLITE=sqlite-amalgamation-3440000
|
||||
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.h" > "$SQLITE3_LIB_DIR/sqlite3.h"
|
||||
unzip -p "$SQLITE.zip" "$SQLITE/sqlite3ext.h" > "$SQLITE3_LIB_DIR/sqlite3ext.h"
|
||||
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"
|
||||
cargo update
|
||||
# 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
|
||||
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
|
||||
cd "$SCRIPT_DIR/.." || { echo "fatal error" >&2; exit 1; }
|
||||
|
@ -3,7 +3,7 @@
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
echo "$SCRIPT_DIR"
|
||||
cd "$SCRIPT_DIR" || { echo "fatal error" >&2; exit 1; }
|
||||
cargo clean
|
||||
cargo clean -p libsqlite3-sys
|
||||
mkdir -p "$SCRIPT_DIR/../target" "$SCRIPT_DIR/sqlcipher"
|
||||
export SQLCIPHER_LIB_DIR="$SCRIPT_DIR/sqlcipher"
|
||||
export SQLCIPHER_INCLUDE_DIR="$SQLCIPHER_LIB_DIR"
|
||||
|
2
libsqlite3-sys/wrapper_ext.h
Normal file
2
libsqlite3-sys/wrapper_ext.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include "sqlite3ext.h"
|
||||
|
17
rusqlite-macros/Cargo.toml
Normal file
17
rusqlite-macros/Cargo.toml
Normal 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 }
|
91
rusqlite-macros/src/lib.rs
Normal file
91
rusqlite-macros/src/lib.rs
Normal 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()
|
||||
}
|
43
rusqlite-macros/tests/test.rs
Normal file
43
rusqlite-macros/tests/test.rs
Normal 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);
|
||||
}
|
||||
*/
|
@ -3,12 +3,16 @@ use std::str;
|
||||
use crate::{Error, Result, Statement};
|
||||
|
||||
/// Information about a column of a SQLite query.
|
||||
#[cfg(feature = "column_decltype")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
|
||||
#[derive(Debug)]
|
||||
pub struct Column<'stmt> {
|
||||
name: &'stmt str,
|
||||
decl_type: Option<&'stmt str>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "column_decltype")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "column_decltype")))]
|
||||
impl Column<'_> {
|
||||
/// Returns the name of the column.
|
||||
#[inline]
|
||||
@ -90,6 +94,8 @@ impl Statement<'_> {
|
||||
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid
|
||||
/// column range for this row.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when column name is not valid UTF-8.
|
||||
#[inline]
|
||||
pub fn column_name(&self, col: usize) -> Result<&str> {
|
||||
|
@ -9,6 +9,7 @@ use crate::{Connection, Result};
|
||||
/// Database Connection Configuration Options
|
||||
/// See [Database Connection Configuration Options](https://sqlite.org/c3ref/c_dbconfig_enable_fkey.html) for details.
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[allow(non_snake_case, non_camel_case_types)]
|
||||
#[non_exhaustive]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
|
25
src/error.rs
25
src/error.rs
@ -141,6 +141,10 @@ pub enum Error {
|
||||
/// byte offset of the start of invalid token
|
||||
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 {
|
||||
@ -200,6 +204,8 @@ impl PartialEq for Error {
|
||||
offset: o2,
|
||||
},
|
||||
) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2,
|
||||
#[cfg(feature = "loadable_extension")]
|
||||
(Error::InitError(e1), Error::InitError(e2)) => e1 == e2,
|
||||
(..) => 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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
@ -311,6 +325,8 @@ impl fmt::Display for Error {
|
||||
ref sql,
|
||||
..
|
||||
} => 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,
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
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 {
|
||||
/// Returns the underlying SQLite error if this is [`Error::SqliteFailure`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sqlite_error(&self) -> Option<&ffi::Error> {
|
||||
match self {
|
||||
Self::SqliteFailure(error, _) => Some(error),
|
||||
@ -377,6 +396,7 @@ impl Error {
|
||||
/// Returns the underlying SQLite error code if this is
|
||||
/// [`Error::SqliteFailure`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn sqlite_error_code(&self) -> Option<ffi::ErrorCode> {
|
||||
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).
|
||||
/// # Safety
|
||||
/// This function is unsafe because it uses raw pointer
|
||||
pub unsafe fn to_sqlite_error(
|
||||
e: &Error,
|
||||
err_msg: *mut *mut std::os::raw::c_char,
|
||||
) -> std::os::raw::c_int {
|
||||
pub unsafe fn to_sqlite_error(e: &Error, err_msg: *mut *mut std::os::raw::c_char) -> c_int {
|
||||
use crate::util::alloc;
|
||||
match e {
|
||||
Error::SqliteFailure(err, s) => {
|
||||
|
@ -272,11 +272,11 @@ where
|
||||
/// call to [`step()`](Aggregate::step) to set up the context for an
|
||||
/// invocation of the function. (Note: `init()` will not be called if
|
||||
/// there are no rows.)
|
||||
fn init(&self, _: &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
|
||||
/// 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
|
||||
/// each invocation of the function. If [`step()`](Aggregate::step) was
|
||||
@ -287,7 +287,7 @@ where
|
||||
/// given `None`.
|
||||
///
|
||||
/// 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
|
||||
@ -301,16 +301,17 @@ where
|
||||
{
|
||||
/// Returns the current value of the aggregate. Unlike xFinal, the
|
||||
/// 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.
|
||||
fn inverse(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>;
|
||||
fn inverse(&self, ctx: &mut Context<'_>, acc: &mut A) -> Result<()>;
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Function Flags.
|
||||
/// See [sqlite3_create_function](https://sqlite.org/c3ref/create_function.html)
|
||||
/// and [Function Flags](https://sqlite.org/c3ref/c_deterministic.html) for details.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct FunctionFlags: ::std::os::raw::c_int {
|
||||
/// 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),
|
||||
};
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
if (*pac as *mut A).is_null() {
|
||||
*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
|
||||
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
|
||||
let a: Option<A> = match aggregate_context(ctx, 0) {
|
||||
Some(pac) => {
|
||||
Some(pac) =>
|
||||
{
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
if (*pac as *mut A).is_null() {
|
||||
None
|
||||
} else {
|
||||
@ -752,17 +756,10 @@ where
|
||||
{
|
||||
// Within the xValue callback, it is customary to set N=0 in calls to
|
||||
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
|
||||
let a: Option<&A> = match aggregate_context(ctx, 0) {
|
||||
Some(pac) => {
|
||||
if (*pac as *mut A).is_null() {
|
||||
None
|
||||
} else {
|
||||
let a = &**pac;
|
||||
Some(a)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let pac = aggregate_context(ctx, 0).filter(|&pac| {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
!(*pac as *mut A).is_null()
|
||||
});
|
||||
|
||||
let r = catch_unwind(|| {
|
||||
let boxed_aggr: *mut W = ffi::sqlite3_user_data(ctx).cast::<W>();
|
||||
@ -770,7 +767,7 @@ where
|
||||
!boxed_aggr.is_null(),
|
||||
"Internal error - null aggregate pointer"
|
||||
);
|
||||
(*boxed_aggr).value(a)
|
||||
(*boxed_aggr).value(pac.map(|pac| &mut **pac))
|
||||
});
|
||||
let t = match r {
|
||||
Err(_) => {
|
||||
@ -1025,7 +1022,7 @@ mod test {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn value(&self, sum: Option<&i64>) -> Result<Option<i64>> {
|
||||
fn value(&self, sum: Option<&mut i64>) -> Result<Option<i64>> {
|
||||
Ok(sum.copied())
|
||||
}
|
||||
}
|
||||
|
@ -776,9 +776,10 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
let authorizer = move |ctx: AuthContext<'_>| match ctx.action {
|
||||
AuthAction::Read { column_name, .. } if column_name == "private" => {
|
||||
Authorization::Ignore
|
||||
}
|
||||
AuthAction::Read {
|
||||
column_name: "private",
|
||||
..
|
||||
} => Authorization::Ignore,
|
||||
AuthAction::DropTable { .. } => Authorization::Deny,
|
||||
AuthAction::Pragma { .. } => panic!("shouldn't be called"),
|
||||
_ => Authorization::Allow,
|
||||
|
@ -4,7 +4,7 @@ use std::os::raw::{c_char, c_int};
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::ffi;
|
||||
@ -40,7 +40,7 @@ pub struct InnerConnection {
|
||||
unsafe impl Send for 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]
|
||||
pub unsafe fn new(db: *mut ffi::sqlite3, owned: bool) -> 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();
|
||||
|
||||
pub static BYPASS_SQLITE_INIT: AtomicBool = AtomicBool::new(false);
|
||||
@ -440,7 +440,9 @@ fn ensure_safe_sqlite_threading_mode() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
SQLITE_INIT.call_once(|| {
|
||||
use std::sync::atomic::Ordering;
|
||||
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
|
97
src/lib.rs
97
src/lib.rs
@ -74,6 +74,7 @@ use crate::raw_statement::RawStatement;
|
||||
use crate::types::ValueRef;
|
||||
|
||||
pub use crate::cache::CachedStatement;
|
||||
#[cfg(feature = "column_decltype")]
|
||||
pub use crate::column::Column;
|
||||
pub use crate::error::{to_sqlite_error, Error};
|
||||
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::row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows};
|
||||
pub use crate::statement::{Statement, StatementStatus};
|
||||
#[cfg(feature = "modern_sqlite")]
|
||||
pub use crate::transaction::TransactionState;
|
||||
pub use crate::transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
|
||||
pub use crate::types::ToSql;
|
||||
pub use crate::version::*;
|
||||
#[cfg(feature = "rusqlite-macros")]
|
||||
#[doc(hidden)]
|
||||
pub use rusqlite_macros::__bind;
|
||||
|
||||
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.
|
||||
pub type Result<T, E = Error> = result::Result<T, E>;
|
||||
|
||||
@ -568,7 +619,7 @@ impl Connection {
|
||||
#[inline]
|
||||
pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
|
||||
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.
|
||||
@ -651,7 +702,7 @@ impl Connection {
|
||||
|
||||
// https://sqlite.org/tclsqlite.html#onecolumn
|
||||
#[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))
|
||||
}
|
||||
|
||||
@ -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.
|
||||
///
|
||||
/// 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.
|
||||
/// In particular, it should only be called on connections created
|
||||
/// and owned by the caller, e.g. as a result of calling
|
||||
/// ffi::sqlite3_open().
|
||||
/// `ffi::sqlite3_open`().
|
||||
#[inline]
|
||||
pub unsafe fn from_handle_owned(db: *mut ffi::sqlite3) -> Result<Connection> {
|
||||
let db = InnerConnection::new(db, true);
|
||||
@ -1432,7 +1494,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "extra_check")]
|
||||
fn test_execute_select() {
|
||||
fn test_execute_select_with_no_row() {
|
||||
let db = checked_memory_handle();
|
||||
let err = db.execute("SELECT 1 WHERE 1 < ?1", [1i32]).unwrap_err();
|
||||
assert_eq!(
|
||||
@ -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]
|
||||
#[cfg(feature = "extra_check")]
|
||||
fn test_execute_multiple() {
|
||||
@ -1812,7 +1881,7 @@ mod test {
|
||||
#[test]
|
||||
fn test_from_handle_owned() -> Result<()> {
|
||||
let mut handle: *mut ffi::sqlite3 = std::ptr::null_mut();
|
||||
let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const i8, &mut handle) };
|
||||
let r = unsafe { ffi::sqlite3_open(":memory:\0".as_ptr() as *const c_char, &mut handle) };
|
||||
assert_eq!(r, ffi::SQLITE_OK);
|
||||
let db = unsafe { Connection::from_handle_owned(handle) }?;
|
||||
db.execute_batch("PRAGMA VACUUM")?;
|
||||
@ -2114,9 +2183,25 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn db_readonly() -> Result<()> {
|
||||
fn db_readonly() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
assert!(!db.is_readonly(MAIN_DB)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rusqlite-macros")]
|
||||
fn prepare_and_bind() -> Result<()> {
|
||||
let db = Connection::open_in_memory()?;
|
||||
let name = "Lisa";
|
||||
let age = 8;
|
||||
let mut stmt = prepare_and_bind!(db, "SELECT $name, $age;");
|
||||
let (v1, v2) = stmt
|
||||
.raw_query()
|
||||
.next()
|
||||
.and_then(|o| o.ok_or(Error::QueryReturnedNoRows))
|
||||
.and_then(|r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))?;
|
||||
assert_eq!((v1.as_str(), v2), (name, age));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use std::os::raw::c_int;
|
||||
/// See the official documentation for more information:
|
||||
/// - <https://www.sqlite.org/c3ref/c_limit_attached.html>
|
||||
/// - <https://www.sqlite.org/limits.html>
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(i32)]
|
||||
#[non_exhaustive]
|
||||
#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
|
||||
|
@ -384,7 +384,6 @@ mod test {
|
||||
let mut rows = table_info.query(["sqlite_master"])?;
|
||||
|
||||
while let Some(row) = rows.next()? {
|
||||
let row = row;
|
||||
let column: String = row.get(1)?;
|
||||
columns.push(column);
|
||||
}
|
||||
|
47
src/row.rs
47
src/row.rs
@ -14,9 +14,11 @@ pub struct Rows<'stmt> {
|
||||
|
||||
impl<'stmt> Rows<'stmt> {
|
||||
#[inline]
|
||||
fn reset(&mut self) {
|
||||
fn reset(&mut self) -> Result<()> {
|
||||
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
|
||||
/// the lifetime of the returned row is tied to the lifetime of `self`.
|
||||
/// This is a fallible "streaming iterator". For a more natural interface,
|
||||
/// consider using [`query_map`](crate::Statement::query_map) or
|
||||
/// [`query_and_then`](crate::Statement::query_and_then) instead, which
|
||||
/// consider using [`query_map`](Statement::query_map) or
|
||||
/// [`query_and_then`](Statement::query_and_then) instead, which
|
||||
/// return types that implement `Iterator`.
|
||||
#[allow(clippy::should_implement_trait)] // cannot implement Iterator
|
||||
#[inline]
|
||||
@ -105,6 +107,7 @@ impl<'stmt> Rows<'stmt> {
|
||||
}
|
||||
|
||||
impl Drop for Rows<'_> {
|
||||
#[allow(unused_must_use)]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.reset();
|
||||
@ -217,12 +220,12 @@ impl<'stmt> FallibleStreamingIterator for Rows<'stmt> {
|
||||
Ok(())
|
||||
}
|
||||
Ok(false) => {
|
||||
self.reset();
|
||||
let r = self.reset();
|
||||
self.row = None;
|
||||
Ok(())
|
||||
r
|
||||
}
|
||||
Err(e) => {
|
||||
self.reset();
|
||||
let _ = self.reset(); // prevents infinite loop on error
|
||||
self.row = None;
|
||||
Err(e)
|
||||
}
|
||||
@ -247,7 +250,7 @@ pub struct Row<'stmt> {
|
||||
impl<'stmt> Row<'stmt> {
|
||||
/// 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,
|
||||
/// including:
|
||||
@ -330,7 +333,7 @@ impl<'stmt> Row<'stmt> {
|
||||
/// it can be difficult to use, and most callers will be better served by
|
||||
/// [`get`](Row::get) or [`get_unwrap`](Row::get_unwrap).
|
||||
///
|
||||
/// ## Failure
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if calling [`row.get_ref(idx)`](Row::get_ref) would return an
|
||||
/// error, including:
|
||||
@ -585,4 +588,30 @@ mod tests {
|
||||
// We don't test one bigger because it's unimplemented
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "bundled")]
|
||||
fn pathological_case() -> Result<()> {
|
||||
let conn = Connection::open_in_memory()?;
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE foo(x);
|
||||
CREATE TRIGGER oops BEFORE INSERT ON foo BEGIN SELECT RAISE(FAIL, 'Boom'); END;",
|
||||
)?;
|
||||
let mut stmt = conn.prepare("INSERT INTO foo VALUES (0) RETURNING rowid;")?;
|
||||
{
|
||||
let iterator_count = stmt.query_map([], |_| Ok(()))?.count();
|
||||
assert_eq!(1, iterator_count); // should be 0
|
||||
use fallible_streaming_iterator::FallibleStreamingIterator;
|
||||
let fallible_iterator_count = stmt.query([])?.count().unwrap_or(0);
|
||||
assert_eq!(0, fallible_iterator_count);
|
||||
}
|
||||
{
|
||||
let iterator_last = stmt.query_map([], |_| Ok(()))?.last();
|
||||
assert!(iterator_last.is_some()); // should be none
|
||||
use fallible_iterator::FallibleIterator;
|
||||
let fallible_iterator_last = stmt.query([])?.map(|_| Ok(())).last();
|
||||
assert!(fallible_iterator_last.is_err());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,9 @@ pub struct OwnedData {
|
||||
}
|
||||
|
||||
impl OwnedData {
|
||||
/// SAFETY: Caller must be certain that `ptr` is allocated by
|
||||
/// `sqlite3_malloc`.
|
||||
/// # Safety
|
||||
///
|
||||
/// Caller must be certain that `ptr` is allocated by `sqlite3_malloc`.
|
||||
pub unsafe fn from_raw_nonnull(ptr: NonNull<u8>, sz: usize) -> Self {
|
||||
Self { ptr, sz }
|
||||
}
|
||||
@ -65,7 +66,7 @@ impl<'conn> Deref for Data<'conn> {
|
||||
|
||||
impl Connection {
|
||||
/// 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 mut sz = 0;
|
||||
let mut ptr: *mut u8 = unsafe {
|
||||
@ -142,7 +143,9 @@ mod test {
|
||||
let db = Connection::open_in_memory()?;
|
||||
db.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||
let data = db.serialize(DatabaseName::Main)?;
|
||||
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||
let Data::Owned(data) = data else {
|
||||
panic!("expected OwnedData")
|
||||
};
|
||||
assert!(data.sz > 0);
|
||||
Ok(())
|
||||
}
|
||||
@ -152,7 +155,9 @@ mod test {
|
||||
let src = Connection::open_in_memory()?;
|
||||
src.execute_batch("CREATE TABLE x AS SELECT 'data'")?;
|
||||
let data = src.serialize(DatabaseName::Main)?;
|
||||
let Data::Owned(data) = data else { panic!("expected OwnedData")};
|
||||
let Data::Owned(data) = data else {
|
||||
panic!("expected OwnedData")
|
||||
};
|
||||
|
||||
let mut dst = Connection::open_in_memory()?;
|
||||
dst.deserialize(DatabaseName::Main, data, false)?;
|
||||
|
@ -405,7 +405,7 @@ impl Drop for ChangesetIter<'_> {
|
||||
}
|
||||
|
||||
/// 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).
|
||||
// TODO enum ? Delete, Insert, Update, ...
|
||||
pub struct ChangesetItem {
|
||||
|
@ -435,6 +435,10 @@ impl Statement<'_> {
|
||||
///
|
||||
/// Will return `None` if the column index is out of bounds or if the
|
||||
/// parameter is positional.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when parameter name is not valid UTF-8.
|
||||
#[inline]
|
||||
pub fn parameter_name(&self, index: usize) -> Option<&'_ str> {
|
||||
self.stmt.bind_parameter_name(index as i32).map(|name| {
|
||||
@ -450,7 +454,7 @@ impl Statement<'_> {
|
||||
{
|
||||
let expected = self.stmt.bind_parameter_count();
|
||||
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.
|
||||
if index > expected {
|
||||
break;
|
||||
@ -646,9 +650,12 @@ impl Statement<'_> {
|
||||
fn execute_with_bound_parameters(&mut self) -> Result<usize> {
|
||||
self.check_update()?;
|
||||
let r = self.stmt.step();
|
||||
self.stmt.reset();
|
||||
let rr = self.stmt.reset();
|
||||
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),
|
||||
_ => Err(self.conn.decode_result(r).unwrap_err()),
|
||||
}
|
||||
@ -744,7 +751,7 @@ impl Statement<'_> {
|
||||
|
||||
/// Reset all bindings
|
||||
pub fn clear_bindings(&mut self) {
|
||||
self.stmt.clear_bindings()
|
||||
self.stmt.clear_bindings();
|
||||
}
|
||||
}
|
||||
|
||||
@ -843,8 +850,11 @@ impl Statement<'_> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn reset(&self) -> c_int {
|
||||
self.stmt.reset()
|
||||
pub(super) fn reset(&self) -> Result<()> {
|
||||
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());
|
||||
stmt.parameter_index("test").unwrap();
|
||||
stmt.step().unwrap_err();
|
||||
stmt.reset();
|
||||
stmt.reset().unwrap(); // SQLITE_OMIT_AUTORESET = false
|
||||
stmt.execute([]).unwrap_err();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,8 +8,7 @@ use std::ptr;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::ffi;
|
||||
use crate::error::error_from_sqlite_code;
|
||||
use crate::{Connection, Result};
|
||||
use crate::Connection;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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) {
|
||||
let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
|
||||
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 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error_from_sqlite_code(rc, None))
|
||||
Err(crate::error::error_from_sqlite_code(rc, None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ impl Transaction<'_> {
|
||||
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
|
||||
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
|
||||
};
|
||||
conn.execute_batch(query).map(move |_| Transaction {
|
||||
conn.execute_batch(query).map(move |()| Transaction {
|
||||
conn,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
})
|
||||
@ -251,7 +251,7 @@ impl Savepoint<'_> {
|
||||
fn with_name_<T: Into<String>>(conn: &Connection, name: T) -> Result<Savepoint<'_>> {
|
||||
let name = name.into();
|
||||
conn.execute_batch(&format!("SAVEPOINT {name}"))
|
||||
.map(|_| Savepoint {
|
||||
.map(|()| Savepoint {
|
||||
conn,
|
||||
name,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
@ -346,8 +346,8 @@ impl Savepoint<'_> {
|
||||
match self.drop_behavior() {
|
||||
DropBehavior::Commit => self
|
||||
.commit_()
|
||||
.or_else(|_| self.rollback().and_then(|_| self.commit_())),
|
||||
DropBehavior::Rollback => self.rollback().and_then(|_| self.commit_()),
|
||||
.or_else(|_| self.rollback().and_then(|()| self.commit_())),
|
||||
DropBehavior::Rollback => self.rollback().and_then(|()| self.commit_()),
|
||||
DropBehavior::Ignore => Ok(()),
|
||||
DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
|
||||
}
|
||||
@ -471,8 +471,7 @@ impl Connection {
|
||||
///
|
||||
/// The savepoint defaults to rolling back when it is dropped. If you want
|
||||
/// the savepoint to commit, you must call [`commit`](Savepoint::commit) or
|
||||
/// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::
|
||||
/// set_drop_behavior).
|
||||
/// [`set_drop_behavior(DropBehavior::Commit)`](Savepoint::set_drop_behavior).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
|
@ -111,7 +111,7 @@ pub struct Null;
|
||||
|
||||
/// SQLite data types.
|
||||
/// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html).
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Type {
|
||||
/// NULL
|
||||
Null,
|
||||
|
@ -36,8 +36,7 @@ impl ValueRef<'_> {
|
||||
|
||||
impl<'a> ValueRef<'a> {
|
||||
/// If `self` is case `Integer`, returns the integral value. Otherwise,
|
||||
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> FromSqlResult<i64> {
|
||||
match *self {
|
||||
@ -48,8 +47,7 @@ impl<'a> ValueRef<'a> {
|
||||
|
||||
/// If `self` is case `Null` returns None.
|
||||
/// If `self` is case `Integer`, returns the integral value.
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_i64_or_null(&self) -> FromSqlResult<Option<i64>> {
|
||||
match *self {
|
||||
@ -60,8 +58,7 @@ impl<'a> ValueRef<'a> {
|
||||
}
|
||||
|
||||
/// If `self` is case `Real`, returns the floating point value. Otherwise,
|
||||
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_f64(&self) -> FromSqlResult<f64> {
|
||||
match *self {
|
||||
@ -72,8 +69,7 @@ impl<'a> ValueRef<'a> {
|
||||
|
||||
/// If `self` is case `Null` returns None.
|
||||
/// If `self` is case `Real`, returns the floating point value.
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_f64_or_null(&self) -> FromSqlResult<Option<f64>> {
|
||||
match *self {
|
||||
@ -97,8 +93,7 @@ impl<'a> ValueRef<'a> {
|
||||
|
||||
/// If `self` is case `Null` returns None.
|
||||
/// If `self` is case `Text`, returns the string value.
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_str_or_null(&self) -> FromSqlResult<Option<&'a str>> {
|
||||
match *self {
|
||||
@ -122,8 +117,7 @@ impl<'a> ValueRef<'a> {
|
||||
|
||||
/// If `self` is case `Null` returns None.
|
||||
/// If `self` is case `Blob`, returns the byte slice.
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::
|
||||
/// InvalidColumnType).
|
||||
/// Otherwise returns [`Err(Error::InvalidColumnType)`](crate::Error::InvalidColumnType).
|
||||
#[inline]
|
||||
pub fn as_blob_or_null(&self) -> FromSqlResult<Option<&'a [u8]>> {
|
||||
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`].
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> FromSqlResult<&'a [u8]> {
|
||||
|
@ -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.
|
||||
///
|
||||
/// See [`sqlite3_libversion()`](https://www.sqlite.org/c3ref/libversion.html).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when version is not valid UTF-8.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn version() -> &'static str {
|
||||
|
@ -373,6 +373,7 @@ bitflags::bitflags! {
|
||||
/// Virtual table scan flags
|
||||
/// See [Function Flags](https://sqlite.org/c3ref/c_index_scan_unique.html) for details.
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct IndexFlags: ::std::os::raw::c_int {
|
||||
/// Default
|
||||
const NONE = 0;
|
||||
|
@ -1,11 +1,11 @@
|
||||
//! Ensure we reject connections when SQLite is in single-threaded mode, as it
|
||||
//! would violate safety if multiple Rust threads tried to use connections.
|
||||
|
||||
use rusqlite::ffi;
|
||||
use rusqlite::Connection;
|
||||
|
||||
#[cfg(not(feature = "loadable_extension"))]
|
||||
#[test]
|
||||
fn test_error_when_singlethread_mode() {
|
||||
use rusqlite::ffi;
|
||||
use rusqlite::Connection;
|
||||
// put SQLite into single-threaded mode
|
||||
unsafe {
|
||||
// Note: macOS system SQLite seems to return an error if you attempt to
|
||||
|
Loading…
Reference in New Issue
Block a user