1
1
use futures_core:: future:: BoxFuture ;
2
2
use futures_intrusive:: sync:: MutexGuard ;
3
3
use futures_util:: future;
4
- use libsqlite3_sys:: sqlite3;
4
+ use libsqlite3_sys:: { sqlite3, sqlite3_progress_handler } ;
5
5
use sqlx_core:: common:: StatementCache ;
6
6
use sqlx_core:: error:: Error ;
7
7
use sqlx_core:: transaction:: Transaction ;
8
8
use std:: cmp:: Ordering ;
9
9
use std:: fmt:: { self , Debug , Formatter } ;
10
+ use std:: os:: raw:: { c_int, c_void} ;
11
+ use std:: panic:: catch_unwind;
10
12
use std:: ptr:: NonNull ;
11
13
12
14
use crate :: connection:: establish:: EstablishParams ;
@@ -51,6 +53,10 @@ pub struct LockedSqliteHandle<'a> {
51
53
pub ( crate ) guard : MutexGuard < ' a , ConnectionState > ,
52
54
}
53
55
56
+ /// Represents a callback handler that will be shared with the underlying sqlite3 connection.
57
+ pub ( crate ) struct Handler ( NonNull < dyn FnMut ( ) -> bool + Send + ' static > ) ;
58
+ unsafe impl Send for Handler { }
59
+
54
60
pub ( crate ) struct ConnectionState {
55
61
pub ( crate ) handle : ConnectionHandle ,
56
62
@@ -60,6 +66,22 @@ pub(crate) struct ConnectionState {
60
66
pub ( crate ) statements : Statements ,
61
67
62
68
log_settings : LogSettings ,
69
+
70
+ /// Stores the progress handler set on the current connection. If the handler returns `false`,
71
+ /// the query is interrupted.
72
+ progress_handler_callback : Option < Handler > ,
73
+ }
74
+
75
+ impl ConnectionState {
76
+ /// Drops the `progress_handler_callback` if it exists.
77
+ pub ( crate ) fn remove_progress_handler ( & mut self ) {
78
+ if let Some ( mut handler) = self . progress_handler_callback . take ( ) {
79
+ unsafe {
80
+ sqlite3_progress_handler ( self . handle . as_ptr ( ) , 0 , None , 0 as * mut _ ) ;
81
+ let _ = { Box :: from_raw ( handler. 0 . as_mut ( ) ) } ;
82
+ }
83
+ }
84
+ }
63
85
}
64
86
65
87
pub ( crate ) struct Statements {
@@ -177,6 +199,21 @@ impl Connection for SqliteConnection {
177
199
}
178
200
}
179
201
202
+ /// Implements a C binding to a progress callback. The function returns `0` if the
203
+ /// user-provided callback returns `true`, and `1` otherwise to signal an interrupt.
204
+ extern "C" fn progress_callback < F > ( callback : * mut c_void ) -> c_int
205
+ where
206
+ F : FnMut ( ) -> bool ,
207
+ {
208
+ unsafe {
209
+ let r = catch_unwind ( || {
210
+ let callback: * mut F = callback. cast :: < F > ( ) ;
211
+ ( * callback) ( )
212
+ } ) ;
213
+ c_int:: from ( !r. unwrap_or_default ( ) )
214
+ }
215
+ }
216
+
180
217
impl LockedSqliteHandle < ' _ > {
181
218
/// Returns the underlying sqlite3* connection handle.
182
219
///
@@ -206,12 +243,52 @@ impl LockedSqliteHandle<'_> {
206
243
) -> Result < ( ) , Error > {
207
244
collation:: create_collation ( & mut self . guard . handle , name, compare)
208
245
}
246
+
247
+ /// Sets a progress handler that is invoked periodically during long running calls. If the progress callback
248
+ /// returns `false`, then the operation is interrupted.
249
+ ///
250
+ /// `num_ops` is the approximate number of [virtual machine instructions](https://www.sqlite.org/opcode.html)
251
+ /// that are evaluated between successive invocations of the callback. If `num_ops` is less than one then the
252
+ /// progress handler is disabled.
253
+ ///
254
+ /// Only a single progress handler may be defined at one time per database connection; setting a new progress
255
+ /// handler cancels the old one.
256
+ ///
257
+ /// The progress handler callback must not do anything that will modify the database connection that invoked
258
+ /// the progress handler. Note that sqlite3_prepare_v2() and sqlite3_step() both modify their database connections
259
+ /// in this context.
260
+ pub fn set_progress_handler < F > ( & mut self , num_ops : i32 , mut callback : F )
261
+ where
262
+ F : FnMut ( ) -> bool + Send + ' static ,
263
+ {
264
+ unsafe {
265
+ let callback_boxed = Box :: new ( callback) ;
266
+ // SAFETY: `Box::into_raw()` always returns a non-null pointer.
267
+ let callback = NonNull :: new_unchecked ( Box :: into_raw ( callback_boxed) ) ;
268
+ let handler = callback. as_ptr ( ) as * mut _ ;
269
+ self . guard . remove_progress_handler ( ) ;
270
+ self . guard . progress_handler_callback = Some ( Handler ( callback) ) ;
271
+
272
+ sqlite3_progress_handler (
273
+ self . as_raw_handle ( ) . as_mut ( ) ,
274
+ num_ops,
275
+ Some ( progress_callback :: < F > ) ,
276
+ handler,
277
+ ) ;
278
+ }
279
+ }
280
+
281
+ /// Removes the progress handler on a database connection. The method does nothing if no handler was set.
282
+ pub fn remove_progress_handler ( & mut self ) {
283
+ self . guard . remove_progress_handler ( ) ;
284
+ }
209
285
}
210
286
211
287
impl Drop for ConnectionState {
212
288
fn drop ( & mut self ) {
213
289
// explicitly drop statements before the connection handle is dropped
214
290
self . statements . clear ( ) ;
291
+ self . remove_progress_handler ( ) ;
215
292
}
216
293
}
217
294
0 commit comments