1use std::ffi::{c_int, c_void, CStr};
2use std::mem::MaybeUninit;
3use std::num::NonZeroU32;
4use std::ptr::null_mut;
5
6use libsql_ffi::{
7 libsql_wal, libsql_wal_manager, sqlite3_wal, sqlite3_wal_manager, Error, SQLITE_OK,
8 WAL_SAVEPOINT_NDATA,
9};
10
11use super::{
12 BusyHandler, CheckpointCallback, CheckpointMode, PageHeaders, Result, Sqlite3Db, Sqlite3File,
13 UndoHandler, Vfs, Wal, WalManager,
14};
15
16#[derive(Clone, Copy)]
18pub struct Sqlite3WalManager {
19 inner: libsql_wal_manager,
20}
21
22unsafe impl Send for Sqlite3WalManager {}
24unsafe impl Sync for Sqlite3WalManager {}
25
26impl Sqlite3WalManager {
27 pub fn new() -> Self {
28 Self {
29 inner: unsafe { sqlite3_wal_manager },
30 }
31 }
32}
33
34impl Default for Sqlite3WalManager {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl WalManager for Sqlite3WalManager {
41 type Wal = Sqlite3Wal;
42
43 fn use_shared_memory(&self) -> bool {
44 self.inner.bUsesShm != 0
45 }
46
47 fn open(
48 &self,
49 vfs: &mut Vfs,
50 file: &mut Sqlite3File,
51 no_shm_mode: c_int,
52 max_log_size: i64,
53 db_path: &CStr,
54 ) -> Result<Self::Wal> {
55 let mut wal: MaybeUninit<libsql_wal> = MaybeUninit::uninit();
56 let rc = unsafe {
57 (self.inner.xOpen.unwrap())(
58 self.inner.pData,
59 vfs.as_ptr(),
60 file.as_ptr(),
61 no_shm_mode,
62 max_log_size,
63 db_path.as_ptr(),
64 wal.as_mut_ptr(),
65 )
66 };
67
68 if rc != 0 {
69 Err(Error::new(rc))?
70 }
71
72 let inner = unsafe { wal.assume_init() };
73
74 Ok(Sqlite3Wal { inner })
75 }
76
77 fn close(
78 &self,
79 wal: &mut Self::Wal,
80 db: &mut Sqlite3Db,
81 sync_flags: c_int,
82 scratch: Option<&mut [u8]>,
83 ) -> Result<()> {
84 let scratch_len = scratch.as_ref().map(|s| s.len()).unwrap_or(0);
85 let scratch_ptr = scratch.map(|s| s.as_mut_ptr()).unwrap_or(null_mut());
86 let rc = unsafe {
87 (self.inner.xClose.unwrap())(
88 self.inner.pData,
89 wal.inner.pData,
90 db.as_ptr(),
91 sync_flags,
92 scratch_len as _,
93 scratch_ptr as _,
94 )
95 };
96
97 if rc != 0 {
98 Err(Error::new(rc))?
99 } else {
100 Ok(())
101 }
102 }
103
104 fn destroy_log(&self, vfs: &mut Vfs, db_path: &CStr) -> Result<()> {
105 let rc = unsafe {
106 (self.inner.xLogDestroy.unwrap())(self.inner.pData, vfs.as_ptr(), db_path.as_ptr())
107 };
108
109 if rc != 0 {
110 Err(Error::new(rc))?
111 } else {
112 Ok(())
113 }
114 }
115
116 fn log_exists(&self, vfs: &mut Vfs, db_path: &CStr) -> Result<bool> {
117 let mut out: c_int = 0;
118 let rc = unsafe {
119 (self.inner.xLogExists.unwrap())(
120 self.inner.pData,
121 vfs.as_ptr(),
122 db_path.as_ptr(),
123 &mut out,
124 )
125 };
126
127 if rc != 0 {
128 Err(Error::new(rc))?
129 } else {
130 Ok(out != 0)
131 }
132 }
133
134 fn destroy(self)
135 where
136 Self: Sized,
137 {
138 unsafe { (self.inner.xDestroy.unwrap())(self.inner.pData) }
139 }
140}
141
142unsafe impl Send for Sqlite3Wal {}
143
144pub struct Sqlite3Wal {
146 inner: libsql_wal,
147}
148
149impl Wal for Sqlite3Wal {
150 fn limit(&mut self, size: i64) {
151 unsafe {
152 (self.inner.methods.xLimit.unwrap())(self.inner.pData, size);
153 }
154 }
155
156 fn begin_read_txn(&mut self) -> Result<bool> {
157 let mut out: c_int = 0;
158 let rc = unsafe {
159 (self.inner.methods.xBeginReadTransaction.unwrap())(
160 self.inner.pData,
161 &mut out as *mut _,
162 )
163 };
164 if rc != 0 {
165 Err(Error::new(rc))
166 } else {
167 Ok(out != 0)
168 }
169 }
170
171 fn end_read_txn(&mut self) {
172 unsafe {
173 (self.inner.methods.xEndReadTransaction.unwrap())(self.inner.pData);
174 }
175 }
176
177 fn find_frame(&mut self, page_no: NonZeroU32) -> Result<Option<NonZeroU32>> {
178 let mut out: u32 = 0;
179 let rc = unsafe {
180 (self.inner.methods.xFindFrame.unwrap())(self.inner.pData, page_no.into(), &mut out)
181 };
182
183 if rc != 0 {
184 Err(Error::new(rc))
185 } else {
186 Ok(NonZeroU32::new(out))
187 }
188 }
189
190 fn read_frame(&mut self, frame_no: NonZeroU32, buffer: &mut [u8]) -> Result<()> {
191 let rc = unsafe {
192 (self.inner.methods.xReadFrame.unwrap())(
193 self.inner.pData,
194 frame_no.into(),
195 buffer.len() as _,
196 buffer.as_mut_ptr(),
197 )
198 };
199 if rc != 0 {
200 Err(Error::new(rc))
201 } else {
202 Ok(())
203 }
204 }
205
206 fn read_frame_raw(&mut self, frame_no: NonZeroU32, buffer: &mut [u8]) -> Result<()> {
207 let rc = unsafe {
208 (self.inner.methods.xReadFrameRaw.unwrap())(
209 self.inner.pData,
210 frame_no.into(),
211 buffer.len() as _,
212 buffer.as_mut_ptr(),
213 )
214 };
215 if rc != 0 {
216 Err(Error::new(rc))
217 } else {
218 Ok(())
219 }
220 }
221
222 fn db_size(&self) -> u32 {
223 unsafe { (self.inner.methods.xDbsize.unwrap())(self.inner.pData) }
224 }
225
226 fn begin_write_txn(&mut self) -> Result<()> {
227 let rc = unsafe { (self.inner.methods.xBeginWriteTransaction.unwrap())(self.inner.pData) };
228 if rc != 0 {
229 Err(Error::new(rc))
230 } else {
231 Ok(())
232 }
233 }
234
235 fn end_write_txn(&mut self) -> Result<()> {
236 let rc = unsafe { (self.inner.methods.xEndWriteTransaction.unwrap())(self.inner.pData) };
237 if rc != 0 {
238 Err(Error::new(rc))
239 } else {
240 Ok(())
241 }
242 }
243
244 fn undo<U: UndoHandler>(&mut self, undo_handler: Option<&mut U>) -> Result<()> {
245 unsafe extern "C" fn call_handler<U: UndoHandler>(p: *mut c_void, page_no: u32) -> c_int {
246 let this = &mut *(p as *mut U);
247 match this.handle_undo(page_no) {
248 Ok(_) => SQLITE_OK,
249 Err(e) => e.extended_code,
250 }
251 }
252
253 let handler = undo_handler
254 .is_some()
255 .then_some(call_handler::<U> as unsafe extern "C" fn(*mut c_void, u32) -> i32);
256 let handler_data = undo_handler
257 .map(|d| d as *mut _ as *mut _)
258 .unwrap_or(std::ptr::null_mut());
259
260 let rc =
261 unsafe { (self.inner.methods.xUndo.unwrap())(self.inner.pData, handler, handler_data) };
262 if rc != 0 {
263 Err(Error::new(rc))
264 } else {
265 Ok(())
266 }
267 }
268
269 fn savepoint(&mut self, rollback_data: &mut [u32]) {
270 assert_eq!(rollback_data.len(), WAL_SAVEPOINT_NDATA as usize);
271 unsafe {
272 (self.inner.methods.xSavepoint.unwrap())(self.inner.pData, rollback_data.as_mut_ptr());
273 }
274 }
275
276 fn savepoint_undo(&mut self, rollback_data: &mut [u32]) -> Result<()> {
277 assert_eq!(rollback_data.len(), WAL_SAVEPOINT_NDATA as usize);
278 let rc = unsafe {
279 (self.inner.methods.xSavepointUndo.unwrap())(
280 self.inner.pData,
281 rollback_data.as_mut_ptr(),
282 )
283 };
284 if rc != 0 {
285 Err(Error::new(rc))
286 } else {
287 Ok(())
288 }
289 }
290
291 fn savepoint_forget(&mut self, rollback_data: &mut [u32]) {
292 assert_eq!(rollback_data.len(), WAL_SAVEPOINT_NDATA as usize);
293 if self.inner.methods.iVersion >= 2 {
294 if let Some(savepoint_forget) = self.inner.methods.xSavepointForget {
295 unsafe {
296 savepoint_forget(self.inner.pData, rollback_data.as_mut_ptr());
297 }
298 }
299 }
300 }
301
302 fn frame_count(&self, locked: i32) -> Result<u32> {
303 let mut out: u32 = 0;
304 let rc = unsafe {
305 (self.inner.methods.xFrameCount.unwrap())(self.inner.pData, locked, &mut out)
306 };
307 if rc != 0 {
308 Err(Error::new(rc))
309 } else {
310 Ok(out)
311 }
312 }
313
314 fn insert_frames(
315 &mut self,
316 page_size: c_int,
317 page_headers: &mut PageHeaders,
318 size_after: u32,
319 is_commit: bool,
320 sync_flags: c_int,
321 ) -> Result<usize> {
322 let mut frames = 0;
323 let rc = unsafe {
324 (self.inner.methods.xFrames.unwrap())(
325 self.inner.pData,
326 page_size,
327 page_headers.as_mut_ptr(),
328 size_after,
329 is_commit as _,
330 sync_flags,
331 &mut frames,
332 )
333 };
334 if rc != 0 {
335 Err(Error::new(rc))
336 } else {
337 Ok(frames as _)
338 }
339 }
340
341 fn checkpoint(
342 &mut self,
343 db: &mut Sqlite3Db,
344 mode: CheckpointMode,
345 mut busy_handler: Option<&mut dyn BusyHandler>,
346 sync_flags: u32,
347 buf: &mut [u8],
349 mut checkpoint_cb: Option<&mut dyn CheckpointCallback>,
350 in_wal: Option<&mut i32>,
351 backfilled: Option<&mut i32>,
352 ) -> Result<()> {
353 unsafe extern "C" fn call_handler(p: *mut c_void) -> c_int {
354 let this = &mut *(p as *mut &mut dyn BusyHandler);
355 this.handle_busy() as _
356 }
357
358 unsafe extern "C" fn call_cb(
359 data: *mut c_void,
360 max_safe_frame_no: c_int,
361 page: *const u8,
362 page_len: c_int,
363 page_no: c_int,
364 frame_no: c_int,
365 ) -> c_int {
366 let this = &mut *(data as *mut &mut dyn CheckpointCallback);
367 let ret = if page.is_null() {
368 this.finish()
369 } else {
370 this.frame(
371 max_safe_frame_no as _,
372 std::slice::from_raw_parts(page, page_len as _),
373 NonZeroU32::new(page_no as _).unwrap(),
374 NonZeroU32::new(frame_no as _).unwrap(),
375 )
376 };
377
378 match ret {
379 Ok(()) => 0,
380 Err(e) => e.extended_code,
381 }
382 }
383
384 let handler = busy_handler
385 .is_some()
386 .then_some(call_handler as unsafe extern "C" fn(*mut c_void) -> i32);
387 let handler_data = busy_handler
388 .as_mut()
389 .map(|d| d as *mut _ as *mut _)
390 .unwrap_or(std::ptr::null_mut());
391
392 let checkpoint_cb_fn = checkpoint_cb.is_some().then_some(call_cb as _);
393 let checkpoint_cb_data = checkpoint_cb
394 .as_mut()
395 .map(|d| d as *mut &mut dyn CheckpointCallback as *mut _)
396 .unwrap_or(std::ptr::null_mut());
397
398 let out_log_num_frames = in_wal.map(|ptr| ptr as _).unwrap_or(std::ptr::null_mut());
399 let out_backfilled = backfilled
400 .map(|ptr| ptr as _)
401 .unwrap_or(std::ptr::null_mut());
402
403 let rc = unsafe {
404 (self.inner.methods.xCheckpoint.unwrap())(
405 self.inner.pData,
406 db.as_ptr(),
407 mode as _,
408 handler,
409 handler_data,
410 sync_flags as _,
411 buf.len() as _,
412 buf.as_mut_ptr(),
413 out_log_num_frames,
414 out_backfilled,
415 checkpoint_cb_fn,
416 checkpoint_cb_data,
417 )
418 };
419
420 if rc != 0 {
421 Err(Error::new(rc))
422 } else {
423 Ok(())
424 }
425 }
426
427 fn exclusive_mode(&mut self, op: c_int) -> Result<()> {
428 let rc = unsafe { (self.inner.methods.xExclusiveMode.unwrap())(self.inner.pData, op) };
429
430 if rc != 0 {
431 Err(Error::new(rc))
432 } else {
433 Ok(())
434 }
435 }
436
437 fn uses_heap_memory(&self) -> bool {
438 unsafe { (self.inner.methods.xHeapMemory.unwrap())(self.inner.pData) != 0 }
439 }
440
441 fn set_db(&mut self, db: &mut Sqlite3Db) {
442 unsafe {
443 (self.inner.methods.xDb.unwrap())(self.inner.pData, db.as_ptr());
444 }
445 }
446
447 fn callback(&self) -> i32 {
448 unsafe { (self.inner.methods.xCallback.unwrap())(self.inner.pData) }
449 }
450
451 fn frames_in_wal(&self) -> u32 {
452 unsafe {
453 let wal = &*(self.inner.pData as *const sqlite3_wal);
454 wal.hdr.mxFrame
455 }
456 }
457}