Expose query progress information

This commit is contained in:
gwenn 2020-10-25 11:58:47 +01:00
parent 8841187717
commit f5c83af863
2 changed files with 58 additions and 1 deletions

View File

@ -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; use std::panic::{catch_unwind, RefUnwindSafe};
use std::ptr; use std::ptr;
use crate::ffi; use crate::ffi;
@ -74,6 +74,19 @@ impl Connection {
{ {
self.db.borrow_mut().update_hook(hook); self.db.borrow_mut().update_hook(hook);
} }
/// `feature = "hooks"` Register a query progress callback.
///
/// The parameter `num_ops` is the approximate number of virtual machine instructions that are evaluated between successive invocations of the `handler`.
/// If `num_ops` is less than one then the progress handler is disabled.
///
/// If the progress callback returns `true`, the operation is interrupted.
pub fn progress_handler<F>(&self, num_ops: c_int, handler: Option<F>)
where
F: FnMut() -> bool + Send + RefUnwindSafe + 'static,
{
self.db.borrow_mut().progress_handler(num_ops, handler);
}
} }
impl InnerConnection { impl InnerConnection {
@ -81,6 +94,7 @@ impl InnerConnection {
self.update_hook(None::<fn(Action, &str, &str, i64)>); self.update_hook(None::<fn(Action, &str, &str, i64)>);
self.commit_hook(None::<fn() -> bool>); self.commit_hook(None::<fn() -> bool>);
self.rollback_hook(None::<fn()>); self.rollback_hook(None::<fn()>);
self.progress_handler(0, None::<fn() -> bool>);
} }
fn commit_hook<F>(&mut self, hook: Option<F>) fn commit_hook<F>(&mut self, hook: Option<F>)
@ -236,6 +250,45 @@ impl InnerConnection {
} }
self.free_update_hook = free_update_hook; self.free_update_hook = free_update_hook;
} }
fn progress_handler<F>(&mut self, num_ops: c_int, handler: Option<F>)
where
F: FnMut() -> bool + Send + RefUnwindSafe + 'static,
{
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void) -> c_int
where
F: FnMut() -> bool,
{
let r = catch_unwind(|| {
let boxed_handler: *mut F = p_arg as *mut F;
(*boxed_handler)()
});
if let Ok(true) = r {
1
} else {
0
}
}
match handler {
Some(handler) => {
let boxed_handler = Box::new(handler);
unsafe {
ffi::sqlite3_progress_handler(
self.db(),
num_ops,
Some(call_boxed_closure::<F>),
&*boxed_handler as *const F as *mut _,
)
}
self.progress_handler = Some(boxed_handler);
}
_ => {
unsafe { ffi::sqlite3_progress_handler(self.db(), num_ops, None, ptr::null_mut()) }
self.progress_handler = None;
}
};
}
} }
unsafe fn free_boxed_hook<F>(p: *mut c_void) { unsafe fn free_boxed_hook<F>(p: *mut c_void) {

View File

@ -31,6 +31,8 @@ pub struct InnerConnection {
pub free_rollback_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>, pub free_rollback_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
pub free_update_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>, pub free_update_hook: Option<unsafe fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")]
pub progress_handler: Option<Box<dyn FnMut() -> bool + Send>>,
owned: bool, owned: bool,
} }
@ -46,6 +48,8 @@ impl InnerConnection {
free_rollback_hook: None, free_rollback_hook: None,
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
free_update_hook: None, free_update_hook: None,
#[cfg(feature = "hooks")]
progress_handler: None,
owned, owned,
} }
} }