From 57a3a8f62e09204a6665687498bfd0aa3e6a1a7f Mon Sep 17 00:00:00 2001 From: gwenn <45554+gwenn@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:01:44 +0100 Subject: [PATCH] Add bindings to automatic extension loading API (#1487) * Add bindings to automatic extension loading API it doesn't seem possible to directly register an `AutoExtension`. --- Cargo.toml | 3 + .../bindgen-bindings/bindgen_3.14.0.rs | 8 +-- libsqlite3-sys/build.rs | 8 +-- .../sqlcipher/bindgen_bundled_version.rs | 8 +-- .../sqlite3/bindgen_bundled_version.rs | 8 +-- src/auto_extension.rs | 57 +++++++++++++++++++ src/lib.rs | 2 + tests/auto_ext.rs | 41 +++++++++++++ 8 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 src/auto_extension.rs create mode 100644 tests/auto_ext.rs diff --git a/Cargo.toml b/Cargo.toml index 562a745..b781bf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -140,6 +140,9 @@ bencher = "0.1" path = "libsqlite3-sys" version = "0.28.0" +[[test]] +name = "auto_ext" + [[test]] name = "config_log" harness = false diff --git a/libsqlite3-sys/bindgen-bindings/bindgen_3.14.0.rs b/libsqlite3-sys/bindgen-bindings/bindgen_3.14.0.rs index a2af6f0..bb6f871 100644 --- a/libsqlite3-sys/bindgen-bindings/bindgen_3.14.0.rs +++ b/libsqlite3-sys/bindgen-bindings/bindgen_3.14.0.rs @@ -5,8 +5,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; @@ -16,8 +16,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; diff --git a/libsqlite3-sys/build.rs b/libsqlite3-sys/build.rs index b60ea37..987b34f 100644 --- a/libsqlite3-sys/build.rs +++ b/libsqlite3-sys/build.rs @@ -554,8 +554,8 @@ mod bindings { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; @@ -568,8 +568,8 @@ mod bindings { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; diff --git a/libsqlite3-sys/sqlcipher/bindgen_bundled_version.rs b/libsqlite3-sys/sqlcipher/bindgen_bundled_version.rs index e27d49e..7bac3ff 100644 --- a/libsqlite3-sys/sqlcipher/bindgen_bundled_version.rs +++ b/libsqlite3-sys/sqlcipher/bindgen_bundled_version.rs @@ -5,8 +5,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; @@ -16,8 +16,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; diff --git a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs index 7c3a762..996fbc5 100644 --- a/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs +++ b/libsqlite3-sys/sqlite3/bindgen_bundled_version.rs @@ -5,8 +5,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; @@ -16,8 +16,8 @@ extern "C" { xEntryPoint: ::std::option::Option< unsafe extern "C" fn( db: *mut sqlite3, - pzErrMsg: *mut *const ::std::os::raw::c_char, - pThunk: *const sqlite3_api_routines, + pzErrMsg: *mut *mut ::std::os::raw::c_char, + _: *const sqlite3_api_routines, ) -> ::std::os::raw::c_int, >, ) -> ::std::os::raw::c_int; diff --git a/src/auto_extension.rs b/src/auto_extension.rs new file mode 100644 index 0000000..acb7523 --- /dev/null +++ b/src/auto_extension.rs @@ -0,0 +1,57 @@ +//! Automatic axtension loading +use super::ffi; +use crate::error::{check, to_sqlite_error}; +use crate::{Connection, Result}; +use std::os::raw::{c_char, c_int}; + +/// Automatic extension initialization routine +pub type AutoExtension = fn(Connection) -> Result<()>; + +/// Raw automatic extension initialization routine +pub type RawAutoExtension = unsafe extern "C" fn( + db: *mut ffi::sqlite3, + pz_err_msg: *mut *mut c_char, + _: *const ffi::sqlite3_api_routines, +) -> c_int; + +/// Bridge bewteen `RawAutoExtension` and `AutoExtension` +/// +/// # Safety +/// * Opening a database from an auto-extension handler will lead to +/// an endless recursion of the auto-handler triggering itself +/// indirectly for each newly-opened database. +/// * Results are undefined if the given db is closed by an auto-extension. +/// * The list of auto-extensions should not be manipulated from an auto-extension. +pub unsafe fn init_auto_extension( + db: *mut ffi::sqlite3, + pz_err_msg: *mut *mut c_char, + ax: AutoExtension, +) -> c_int { + let c = Connection::from_handle(db); + match c.and_then(ax) { + Err(e) => to_sqlite_error(&e, pz_err_msg), + _ => ffi::SQLITE_OK, + } +} + +/// Register au auto-extension +/// +/// # Safety +/// * Opening a database from an auto-extension handler will lead to +/// an endless recursion of the auto-handler triggering itself +/// indirectly for each newly-opened database. +/// * Results are undefined if the given db is closed by an auto-extension. +/// * The list of auto-extensions should not be manipulated from an auto-extension. +pub unsafe fn register_auto_extension(ax: RawAutoExtension) -> Result<()> { + check(ffi::sqlite3_auto_extension(Some(ax))) +} + +/// Unregister the initialization routine +pub fn cancel_auto_extension(ax: RawAutoExtension) -> bool { + unsafe { ffi::sqlite3_cancel_auto_extension(Some(ax)) == 1 } +} + +/// Disable all automatic extensions previously registered +pub fn reset_auto_extension() { + unsafe { ffi::sqlite3_reset_auto_extension() } +} diff --git a/src/lib.rs b/src/lib.rs index 7572908..1df6e2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,8 @@ pub use rusqlite_macros::__bind; mod error; +#[cfg(not(feature = "loadable_extension"))] +pub mod auto_extension; #[cfg(feature = "backup")] #[cfg_attr(docsrs, doc(cfg(feature = "backup")))] pub mod backup; diff --git a/tests/auto_ext.rs b/tests/auto_ext.rs new file mode 100644 index 0000000..17ed682 --- /dev/null +++ b/tests/auto_ext.rs @@ -0,0 +1,41 @@ +#[cfg(all(feature = "bundled", not(feature = "loadable_extension")))] +#[test] +fn auto_ext() -> rusqlite::Result<()> { + use rusqlite::auto_extension::*; + use rusqlite::{ffi, Connection, Error, Result}; + use std::os::raw::{c_char, c_int}; + + fn test_ok(_: Connection) -> Result<()> { + Ok(()) + } + unsafe extern "C" fn sqlite_test_ok( + db: *mut ffi::sqlite3, + pz_err_msg: *mut *mut c_char, + _: *const ffi::sqlite3_api_routines, + ) -> c_int { + init_auto_extension(db, pz_err_msg, test_ok) + } + fn test_err(_: Connection) -> Result<()> { + Err(Error::SqliteFailure( + ffi::Error::new(ffi::SQLITE_CORRUPT), + Some("AutoExtErr".to_owned()), + )) + } + unsafe extern "C" fn sqlite_test_err( + db: *mut ffi::sqlite3, + pz_err_msg: *mut *mut c_char, + _: *const ffi::sqlite3_api_routines, + ) -> c_int { + init_auto_extension(db, pz_err_msg, test_err) + } + + //assert!(!cancel_auto_extension(sqlite_test_ok)); + unsafe { register_auto_extension(sqlite_test_ok)? }; + Connection::open_in_memory()?; + assert!(cancel_auto_extension(sqlite_test_ok)); + assert!(!cancel_auto_extension(sqlite_test_ok)); + unsafe { register_auto_extension(sqlite_test_err)? }; + Connection::open_in_memory().unwrap_err(); + reset_auto_extension(); + Ok(()) +}