mirror of
https://github.com/isar/rusqlite.git
synced 2025-04-02 04:12:59 +08:00
Merge remote-tracking branch 'upstream/master' into preupdate_hook
This commit is contained in:
commit
0d56555875
@ -141,6 +141,9 @@ bencher = "0.1"
|
|||||||
path = "libsqlite3-sys"
|
path = "libsqlite3-sys"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "auto_ext"
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "config_log"
|
name = "config_log"
|
||||||
harness = false
|
harness = false
|
||||||
|
@ -5,8 +5,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
@ -16,8 +16,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
|
@ -554,8 +554,8 @@ mod bindings {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
@ -568,8 +568,8 @@ mod bindings {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
|
@ -5,8 +5,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
@ -16,8 +16,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
|
@ -5,8 +5,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
@ -16,8 +16,8 @@ extern "C" {
|
|||||||
xEntryPoint: ::std::option::Option<
|
xEntryPoint: ::std::option::Option<
|
||||||
unsafe extern "C" fn(
|
unsafe extern "C" fn(
|
||||||
db: *mut sqlite3,
|
db: *mut sqlite3,
|
||||||
pzErrMsg: *mut *const ::std::os::raw::c_char,
|
pzErrMsg: *mut *mut ::std::os::raw::c_char,
|
||||||
pThunk: *const sqlite3_api_routines,
|
_: *const sqlite3_api_routines,
|
||||||
) -> ::std::os::raw::c_int,
|
) -> ::std::os::raw::c_int,
|
||||||
>,
|
>,
|
||||||
) -> ::std::os::raw::c_int;
|
) -> ::std::os::raw::c_int;
|
||||||
|
62
src/auto_extension.rs
Normal file
62
src/auto_extension.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//! Automatic axtension loading
|
||||||
|
use super::ffi;
|
||||||
|
use crate::error::{check, to_sqlite_error};
|
||||||
|
use crate::{Connection, Error, Result};
|
||||||
|
use std::os::raw::{c_char, c_int};
|
||||||
|
use std::panic::catch_unwind;
|
||||||
|
|
||||||
|
/// 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 r = catch_unwind(|| {
|
||||||
|
let c = Connection::from_handle(db);
|
||||||
|
c.and_then(ax)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| Err(Error::UnwindingPanic));
|
||||||
|
match r {
|
||||||
|
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() }
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
//! Add, remove, or modify a collation
|
//! Add, remove, or modify a collation
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, UnwindSafe};
|
use std::panic::catch_unwind;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ impl Connection {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_collation<C>(&self, collation_name: &str, x_compare: C) -> Result<()>
|
pub fn create_collation<C>(&self, collation_name: &str, x_compare: C) -> Result<()>
|
||||||
where
|
where
|
||||||
C: Fn(&str, &str) -> Ordering + Send + UnwindSafe + 'static,
|
C: Fn(&str, &str) -> Ordering + Send + 'static,
|
||||||
{
|
{
|
||||||
self.db
|
self.db
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
@ -42,9 +42,31 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InnerConnection {
|
impl InnerConnection {
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.create_collation("foo", |_, _| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// std::cmp::Ordering::Equal
|
||||||
|
/// })?;
|
||||||
|
/// }
|
||||||
|
/// let value: String = db.query_row(
|
||||||
|
/// "WITH cte(bar) AS
|
||||||
|
/// (VALUES ('v1'),('v2'),('v3'),('v4'),('v5'))
|
||||||
|
/// SELECT DISTINCT bar COLLATE foo FROM cte;",
|
||||||
|
/// [],
|
||||||
|
/// |row| row.get(0),
|
||||||
|
/// )?;
|
||||||
|
/// assert_eq!(value, "v1");
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn create_collation<C>(&mut self, collation_name: &str, x_compare: C) -> Result<()>
|
fn create_collation<C>(&mut self, collation_name: &str, x_compare: C) -> Result<()>
|
||||||
where
|
where
|
||||||
C: Fn(&str, &str) -> Ordering + Send + UnwindSafe + 'static,
|
C: Fn(&str, &str) -> Ordering + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<C>(
|
unsafe extern "C" fn call_boxed_closure<C>(
|
||||||
arg1: *mut c_void,
|
arg1: *mut c_void,
|
||||||
|
@ -102,8 +102,6 @@ pub enum Error {
|
|||||||
ModuleError(String),
|
ModuleError(String),
|
||||||
|
|
||||||
/// An unwinding panic occurs in an UDF (user-defined function).
|
/// An unwinding panic occurs in an UDF (user-defined function).
|
||||||
#[cfg(feature = "functions")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
|
|
||||||
UnwindingPanic,
|
UnwindingPanic,
|
||||||
|
|
||||||
/// An error returned when
|
/// An error returned when
|
||||||
@ -185,7 +183,6 @@ impl PartialEq for Error {
|
|||||||
(Error::InvalidQuery, Error::InvalidQuery) => true,
|
(Error::InvalidQuery, Error::InvalidQuery) => true,
|
||||||
#[cfg(feature = "vtab")]
|
#[cfg(feature = "vtab")]
|
||||||
(Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
|
(Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
|
||||||
#[cfg(feature = "functions")]
|
|
||||||
(Error::UnwindingPanic, Error::UnwindingPanic) => true,
|
(Error::UnwindingPanic, Error::UnwindingPanic) => true,
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
(Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
|
(Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
|
||||||
@ -318,7 +315,6 @@ impl fmt::Display for Error {
|
|||||||
Error::InvalidQuery => write!(f, "Query is not read-only"),
|
Error::InvalidQuery => write!(f, "Query is not read-only"),
|
||||||
#[cfg(feature = "vtab")]
|
#[cfg(feature = "vtab")]
|
||||||
Error::ModuleError(ref desc) => write!(f, "{desc}"),
|
Error::ModuleError(ref desc) => write!(f, "{desc}"),
|
||||||
#[cfg(feature = "functions")]
|
|
||||||
Error::UnwindingPanic => write!(f, "unwinding panic"),
|
Error::UnwindingPanic => write!(f, "unwinding panic"),
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
|
Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
|
||||||
@ -375,7 +371,6 @@ impl error::Error for Error {
|
|||||||
#[cfg(feature = "vtab")]
|
#[cfg(feature = "vtab")]
|
||||||
Error::ModuleError(_) => None,
|
Error::ModuleError(_) => None,
|
||||||
|
|
||||||
#[cfg(feature = "functions")]
|
|
||||||
Error::UnwindingPanic => None,
|
Error::UnwindingPanic => None,
|
||||||
|
|
||||||
#[cfg(feature = "functions")]
|
#[cfg(feature = "functions")]
|
||||||
|
@ -444,7 +444,7 @@ impl Connection {
|
|||||||
x_func: F,
|
x_func: F,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: FnMut(&Context<'_>) -> Result<T> + Send + UnwindSafe + 'static,
|
F: FnMut(&Context<'_>) -> Result<T> + Send + 'static,
|
||||||
T: SqlFnOutput,
|
T: SqlFnOutput,
|
||||||
{
|
{
|
||||||
self.db
|
self.db
|
||||||
@ -518,6 +518,27 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InnerConnection {
|
impl InnerConnection {
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{functions::FunctionFlags, Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.create_scalar_function(
|
||||||
|
/// "test",
|
||||||
|
/// 0,
|
||||||
|
/// FunctionFlags::SQLITE_UTF8 | FunctionFlags::SQLITE_DETERMINISTIC,
|
||||||
|
/// |_| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// Ok(true)
|
||||||
|
/// },
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// let result: Result<bool> = db.query_row("SELECT test()", [], |r| r.get(0));
|
||||||
|
/// assert!(result?);
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn create_scalar_function<F, T>(
|
fn create_scalar_function<F, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
@ -526,7 +547,7 @@ impl InnerConnection {
|
|||||||
x_func: F,
|
x_func: F,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: FnMut(&Context<'_>) -> Result<T> + Send + UnwindSafe + 'static,
|
F: FnMut(&Context<'_>) -> Result<T> + Send + 'static,
|
||||||
T: SqlFnOutput,
|
T: SqlFnOutput,
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<F, T>(
|
unsafe extern "C" fn call_boxed_closure<F, T>(
|
||||||
|
101
src/hooks/mod.rs
101
src/hooks/mod.rs
@ -2,7 +2,7 @@
|
|||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, RefUnwindSafe};
|
use std::panic::catch_unwind;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
@ -394,7 +394,7 @@ impl Connection {
|
|||||||
/// If the progress callback returns `true`, the operation is interrupted.
|
/// If the progress callback returns `true`, the operation is interrupted.
|
||||||
pub fn progress_handler<F>(&self, num_ops: c_int, handler: Option<F>)
|
pub fn progress_handler<F>(&self, num_ops: c_int, handler: Option<F>)
|
||||||
where
|
where
|
||||||
F: FnMut() -> bool + Send + RefUnwindSafe + 'static,
|
F: FnMut() -> bool + Send + 'static,
|
||||||
{
|
{
|
||||||
self.db.borrow_mut().progress_handler(num_ops, handler);
|
self.db.borrow_mut().progress_handler(num_ops, handler);
|
||||||
}
|
}
|
||||||
@ -404,7 +404,7 @@ impl Connection {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn authorizer<'c, F>(&self, hook: Option<F>)
|
pub fn authorizer<'c, F>(&self, hook: Option<F>)
|
||||||
where
|
where
|
||||||
F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + RefUnwindSafe + 'static,
|
F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + 'static,
|
||||||
{
|
{
|
||||||
self.db.borrow_mut().authorizer(hook);
|
self.db.borrow_mut().authorizer(hook);
|
||||||
}
|
}
|
||||||
@ -420,6 +420,27 @@ impl InnerConnection {
|
|||||||
self.authorizer(None::<fn(AuthContext<'_>) -> Authorization>);
|
self.authorizer(None::<fn(AuthContext<'_>) -> Authorization>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.commit_hook(Some(|| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// true
|
||||||
|
/// }));
|
||||||
|
/// }
|
||||||
|
/// assert!(db
|
||||||
|
/// .execute_batch(
|
||||||
|
/// "BEGIN;
|
||||||
|
/// CREATE TABLE foo (t TEXT);
|
||||||
|
/// COMMIT;",
|
||||||
|
/// )
|
||||||
|
/// .is_err());
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn commit_hook<F>(&mut self, hook: Option<F>)
|
fn commit_hook<F>(&mut self, hook: Option<F>)
|
||||||
where
|
where
|
||||||
F: FnMut() -> bool + Send + 'static,
|
F: FnMut() -> bool + Send + 'static,
|
||||||
@ -465,6 +486,26 @@ impl InnerConnection {
|
|||||||
self.free_commit_hook = free_commit_hook;
|
self.free_commit_hook = free_commit_hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.rollback_hook(Some(|| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// }));
|
||||||
|
/// }
|
||||||
|
/// assert!(db
|
||||||
|
/// .execute_batch(
|
||||||
|
/// "BEGIN;
|
||||||
|
/// CREATE TABLE foo (t TEXT);
|
||||||
|
/// ROLLBACK;",
|
||||||
|
/// )
|
||||||
|
/// .is_err());
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn rollback_hook<F>(&mut self, hook: Option<F>)
|
fn rollback_hook<F>(&mut self, hook: Option<F>)
|
||||||
where
|
where
|
||||||
F: FnMut() + Send + 'static,
|
F: FnMut() + Send + 'static,
|
||||||
@ -506,6 +547,19 @@ impl InnerConnection {
|
|||||||
self.free_rollback_hook = free_rollback_hook;
|
self.free_rollback_hook = free_rollback_hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.update_hook(Some(|_, _: &str, _: &str, _| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// }));
|
||||||
|
/// }
|
||||||
|
/// db.execute_batch("CREATE TABLE foo AS SELECT 1 AS bar;")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn update_hook<F>(&mut self, hook: Option<F>)
|
fn update_hook<F>(&mut self, hook: Option<F>)
|
||||||
where
|
where
|
||||||
F: FnMut(Action, &str, &str, i64) + Send + 'static,
|
F: FnMut(Action, &str, &str, i64) + Send + 'static,
|
||||||
@ -558,9 +612,29 @@ impl InnerConnection {
|
|||||||
self.free_update_hook = free_update_hook;
|
self.free_update_hook = free_update_hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.progress_handler(
|
||||||
|
/// 1,
|
||||||
|
/// Some(|| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// true
|
||||||
|
/// }),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// assert!(db
|
||||||
|
/// .execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
|
||||||
|
/// .is_err());
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn progress_handler<F>(&mut self, num_ops: c_int, handler: Option<F>)
|
fn progress_handler<F>(&mut self, num_ops: c_int, handler: Option<F>)
|
||||||
where
|
where
|
||||||
F: FnMut() -> bool + Send + RefUnwindSafe + 'static,
|
F: FnMut() -> bool + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void) -> c_int
|
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void) -> c_int
|
||||||
where
|
where
|
||||||
@ -590,9 +664,26 @@ impl InnerConnection {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use rusqlite::{Connection, Result};
|
||||||
|
/// fn main() -> Result<()> {
|
||||||
|
/// let db = Connection::open_in_memory()?;
|
||||||
|
/// {
|
||||||
|
/// let mut called = std::sync::atomic::AtomicBool::new(false);
|
||||||
|
/// db.authorizer(Some(|_: rusqlite::hooks::AuthContext<'_>| {
|
||||||
|
/// called.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
/// rusqlite::hooks::Authorization::Deny
|
||||||
|
/// }));
|
||||||
|
/// }
|
||||||
|
/// assert!(db
|
||||||
|
/// .execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
|
||||||
|
/// .is_err());
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
fn authorizer<'c, F>(&'c mut self, authorizer: Option<F>)
|
fn authorizer<'c, F>(&'c mut self, authorizer: Option<F>)
|
||||||
where
|
where
|
||||||
F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + RefUnwindSafe + 'static,
|
F: for<'r> FnMut(AuthContext<'r>) -> Authorization + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<'c, F>(
|
unsafe extern "C" fn call_boxed_closure<'c, F>(
|
||||||
p_arg: *mut c_void,
|
p_arg: *mut c_void,
|
||||||
|
@ -93,6 +93,8 @@ pub use rusqlite_macros::__bind;
|
|||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "loadable_extension"))]
|
||||||
|
pub mod auto_extension;
|
||||||
#[cfg(feature = "backup")]
|
#[cfg(feature = "backup")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "backup")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "backup")))]
|
||||||
pub mod backup;
|
pub mod backup;
|
||||||
|
@ -5,7 +5,7 @@ use std::ffi::CStr;
|
|||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
||||||
use std::panic::{catch_unwind, RefUnwindSafe};
|
use std::panic::catch_unwind;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
||||||
|
|
||||||
@ -59,20 +59,22 @@ impl Session<'_> {
|
|||||||
/// Set a table filter
|
/// Set a table filter
|
||||||
pub fn table_filter<F>(&mut self, filter: Option<F>)
|
pub fn table_filter<F>(&mut self, filter: Option<F>)
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
|
F: Fn(&str) -> bool + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe extern "C" fn call_boxed_closure<F>(
|
unsafe extern "C" fn call_boxed_closure<F>(
|
||||||
p_arg: *mut c_void,
|
p_arg: *mut c_void,
|
||||||
tbl_str: *const c_char,
|
tbl_str: *const c_char,
|
||||||
) -> c_int
|
) -> c_int
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + RefUnwindSafe,
|
F: Fn(&str) -> bool,
|
||||||
{
|
{
|
||||||
let boxed_filter: *mut F = p_arg as *mut F;
|
|
||||||
let tbl_name = CStr::from_ptr(tbl_str).to_str();
|
let tbl_name = CStr::from_ptr(tbl_str).to_str();
|
||||||
c_int::from(
|
c_int::from(
|
||||||
catch_unwind(|| (*boxed_filter)(tbl_name.expect("non-utf8 table name")))
|
catch_unwind(|| {
|
||||||
.unwrap_or_default(),
|
let boxed_filter: *mut F = p_arg.cast::<F>();
|
||||||
|
(*boxed_filter)(tbl_name.expect("non-utf8 table name"))
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,8 +590,8 @@ impl Connection {
|
|||||||
/// Apply a changeset to a database
|
/// Apply a changeset to a database
|
||||||
pub fn apply<F, C>(&self, cs: &Changeset, filter: Option<F>, conflict: C) -> Result<()>
|
pub fn apply<F, C>(&self, cs: &Changeset, filter: Option<F>, conflict: C) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
|
F: Fn(&str) -> bool + Send + 'static,
|
||||||
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
|
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static,
|
||||||
{
|
{
|
||||||
let db = self.db.borrow_mut().db;
|
let db = self.db.borrow_mut().db;
|
||||||
|
|
||||||
@ -626,8 +628,8 @@ impl Connection {
|
|||||||
conflict: C,
|
conflict: C,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
|
F: Fn(&str) -> bool + Send + 'static,
|
||||||
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
|
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static,
|
||||||
{
|
{
|
||||||
let input_ref = &input;
|
let input_ref = &input;
|
||||||
let db = self.db.borrow_mut().db;
|
let db = self.db.borrow_mut().db;
|
||||||
@ -701,17 +703,21 @@ pub enum ConflictAction {
|
|||||||
|
|
||||||
unsafe extern "C" fn call_filter<F, C>(p_ctx: *mut c_void, tbl_str: *const c_char) -> c_int
|
unsafe extern "C" fn call_filter<F, C>(p_ctx: *mut c_void, tbl_str: *const c_char) -> c_int
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
|
F: Fn(&str) -> bool + Send + 'static,
|
||||||
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
|
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static,
|
||||||
{
|
{
|
||||||
let tuple: *mut (Option<F>, C) = p_ctx as *mut (Option<F>, C);
|
|
||||||
let tbl_name = CStr::from_ptr(tbl_str).to_str();
|
let tbl_name = CStr::from_ptr(tbl_str).to_str();
|
||||||
match *tuple {
|
c_int::from(
|
||||||
(Some(ref filter), _) => c_int::from(
|
catch_unwind(|| {
|
||||||
catch_unwind(|| filter(tbl_name.expect("illegal table name"))).unwrap_or_default(),
|
let tuple: *mut (Option<F>, C) = p_ctx.cast::<(Option<F>, C)>();
|
||||||
),
|
if let Some(ref filter) = (*tuple).0 {
|
||||||
_ => unimplemented!(),
|
filter(tbl_name.expect("illegal table name"))
|
||||||
}
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn call_conflict<F, C>(
|
unsafe extern "C" fn call_conflict<F, C>(
|
||||||
@ -720,13 +726,15 @@ unsafe extern "C" fn call_conflict<F, C>(
|
|||||||
p: *mut ffi::sqlite3_changeset_iter,
|
p: *mut ffi::sqlite3_changeset_iter,
|
||||||
) -> c_int
|
) -> c_int
|
||||||
where
|
where
|
||||||
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
|
F: Fn(&str) -> bool + Send + 'static,
|
||||||
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
|
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + 'static,
|
||||||
{
|
{
|
||||||
let tuple: *mut (Option<F>, C) = p_ctx as *mut (Option<F>, C);
|
|
||||||
let conflict_type = ConflictType::from(e_conflict);
|
let conflict_type = ConflictType::from(e_conflict);
|
||||||
let item = ChangesetItem { it: p };
|
let item = ChangesetItem { it: p };
|
||||||
if let Ok(action) = catch_unwind(|| (*tuple).1(conflict_type, item)) {
|
if let Ok(action) = catch_unwind(|| {
|
||||||
|
let tuple: *mut (Option<F>, C) = p_ctx.cast::<(Option<F>, C)>();
|
||||||
|
(*tuple).1(conflict_type, item)
|
||||||
|
}) {
|
||||||
action as c_int
|
action as c_int
|
||||||
} else {
|
} else {
|
||||||
ffi::SQLITE_CHANGESET_ABORT
|
ffi::SQLITE_CHANGESET_ABORT
|
||||||
|
41
tests/auto_ext.rs
Normal file
41
tests/auto_ext.rs
Normal file
@ -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(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user