@@ -11,7 +11,7 @@ use std::ptr::NonNull;
11
11
use futures_intrusive:: sync:: MutexGuard ;
12
12
use libsqlite3_sys:: {
13
13
sqlite3, sqlite3_commit_hook, sqlite3_progress_handler, sqlite3_rollback_hook,
14
- sqlite3_update_hook, SQLITE_DELETE , SQLITE_INSERT , SQLITE_UPDATE ,
14
+ sqlite3_update_hook, sqlite3_wal_hook , SQLITE_DELETE , SQLITE_INSERT , SQLITE_OK , SQLITE_UPDATE ,
15
15
} ;
16
16
#[ cfg( feature = "preupdate-hook" ) ]
17
17
pub use preupdate_hook:: * ;
@@ -96,6 +96,11 @@ pub struct UpdateHookResult<'a> {
96
96
pub rowid : i64 ,
97
97
}
98
98
99
+ pub struct WalHookResult < ' a > {
100
+ pub database : & ' a str ,
101
+ pub page_count : i32 ,
102
+ }
103
+
99
104
pub ( crate ) struct UpdateHookHandler ( NonNull < dyn FnMut ( UpdateHookResult ) + Send + ' static > ) ;
100
105
unsafe impl Send for UpdateHookHandler { }
101
106
@@ -105,6 +110,9 @@ unsafe impl Send for CommitHookHandler {}
105
110
pub ( crate ) struct RollbackHookHandler ( NonNull < dyn FnMut ( ) + Send + ' static > ) ;
106
111
unsafe impl Send for RollbackHookHandler { }
107
112
113
+ pub ( crate ) struct WalHookHandler ( NonNull < dyn FnMut ( WalHookResult ) + Send + ' static > ) ;
114
+ unsafe impl Send for WalHookHandler { }
115
+
108
116
pub ( crate ) struct ConnectionState {
109
117
pub ( crate ) handle : ConnectionHandle ,
110
118
@@ -123,6 +131,8 @@ pub(crate) struct ConnectionState {
123
131
commit_hook_callback : Option < CommitHookHandler > ,
124
132
125
133
rollback_hook_callback : Option < RollbackHookHandler > ,
134
+
135
+ wal_hook_callback : Option < WalHookHandler > ,
126
136
}
127
137
128
138
impl ConnectionState {
@@ -172,6 +182,15 @@ impl ConnectionState {
172
182
}
173
183
}
174
184
}
185
+
186
+ pub ( crate ) fn remove_wal_hook ( & mut self ) {
187
+ if let Some ( mut handler) = self . wal_hook_callback . take ( ) {
188
+ unsafe {
189
+ sqlite3_wal_hook ( self . handle . as_ptr ( ) , None , ptr:: null_mut ( ) ) ;
190
+ let _ = { Box :: from_raw ( handler. 0 . as_mut ( ) ) } ;
191
+ }
192
+ }
193
+ }
175
194
}
176
195
177
196
pub ( crate ) struct Statements {
@@ -353,6 +372,28 @@ where
353
372
}
354
373
}
355
374
375
+ extern "C" fn wal_hook < F > (
376
+ callback : * mut c_void ,
377
+ _db : * mut sqlite3 ,
378
+ database : * const c_char ,
379
+ page_count : c_int ,
380
+ ) -> c_int
381
+ where
382
+ F : FnMut ( WalHookResult ) + Send + ' static ,
383
+ {
384
+ unsafe {
385
+ let _ = catch_unwind ( || {
386
+ let callback: * mut F = callback. cast :: < F > ( ) ;
387
+ let database = CStr :: from_ptr ( database) . to_str ( ) . unwrap_or_default ( ) ;
388
+ ( * callback) ( WalHookResult {
389
+ database,
390
+ page_count,
391
+ } )
392
+ } ) ;
393
+ }
394
+ SQLITE_OK
395
+ }
396
+
356
397
impl LockedSqliteHandle < ' _ > {
357
398
/// Returns the underlying sqlite3* connection handle.
358
399
///
@@ -520,6 +561,26 @@ impl LockedSqliteHandle<'_> {
520
561
}
521
562
}
522
563
564
+ /// Sets a WAL hook that is invoked whenever a commit occurs in WAL mode. Only a single WAL hook may be
565
+ /// defined at one time per database connection; setting a new WAL hook overrides the old one.
566
+ ///
567
+ /// Note that sqlite3_wal_autocheckpoint() and the wal_autocheckpoint pragma overwrite the WAL hook.
568
+ pub fn set_wal_hook < F > ( & mut self , callback : F )
569
+ where
570
+ F : FnMut ( WalHookResult ) + Send + ' static ,
571
+ {
572
+ unsafe {
573
+ let callback_boxed = Box :: new ( callback) ;
574
+ // SAFETY: `Box::into_raw()` always returns a non-null pointer.
575
+ let callback = NonNull :: new_unchecked ( Box :: into_raw ( callback_boxed) ) ;
576
+ let handler = callback. as_ptr ( ) as * mut _ ;
577
+ self . guard . remove_wal_hook ( ) ;
578
+ self . guard . wal_hook_callback = Some ( WalHookHandler ( callback) ) ;
579
+
580
+ sqlite3_wal_hook ( self . as_raw_handle ( ) . as_mut ( ) , Some ( wal_hook :: < F > ) , handler) ;
581
+ }
582
+ }
583
+
523
584
/// Removes the progress handler on a database connection. The method does nothing if no handler was set.
524
585
pub fn remove_progress_handler ( & mut self ) {
525
586
self . guard . remove_progress_handler ( ) ;
@@ -542,6 +603,10 @@ impl LockedSqliteHandle<'_> {
542
603
self . guard . remove_rollback_hook ( ) ;
543
604
}
544
605
606
+ pub fn remove_wal_hook ( & mut self ) {
607
+ self . guard . remove_wal_hook ( ) ;
608
+ }
609
+
545
610
pub fn last_error ( & mut self ) -> Option < SqliteError > {
546
611
self . guard . handle . last_error ( )
547
612
}
0 commit comments