libsql_sys/wal/
ffi.rs

1use std::ffi::{c_char, c_int, c_longlong, c_uint, c_void, CStr};
2use std::num::NonZeroU32;
3use std::ptr::null;
4
5use libsql_ffi::{
6    libsql_wal, libsql_wal_manager, libsql_wal_methods, sqlite3, sqlite3_file, sqlite3_vfs,
7    wal_impl, wal_manager_impl, Error, PgHdr, SQLITE_CHECKPOINT_FULL, SQLITE_CHECKPOINT_PASSIVE,
8    SQLITE_CHECKPOINT_RESTART, SQLITE_CHECKPOINT_TRUNCATE, SQLITE_OK, WAL_SAVEPOINT_NDATA,
9};
10
11use crate::wal::{BusyHandler, CheckpointCallback, CheckpointMode, UndoHandler};
12
13use super::{PageHeaders, Sqlite3Db, Sqlite3File, Vfs, Wal, WalManager};
14
15// Construct a libsql_wal instance from a pointer to a Wal. This pointer must be valid until a call
16// to WalManager::close
17pub(crate) fn construct_libsql_wal<W: Wal>(wal: *mut W) -> libsql_wal {
18    libsql_wal {
19        methods: libsql_wal_methods {
20            iVersion: 1,
21            xLimit: Some(limit::<W>),
22            xBeginReadTransaction: Some(begin_read_transaction::<W>),
23            xEndReadTransaction: Some(end_read_transaction::<W>),
24            xFindFrame: Some(find_frame::<W>),
25            xReadFrame: Some(read_frame::<W>),
26            xReadFrameRaw: Some(read_frame_raw::<W>),
27            xDbsize: Some(db_size::<W>),
28            xBeginWriteTransaction: Some(begin_write_transaction::<W>),
29            xEndWriteTransaction: Some(end_write_transaction::<W>),
30            xUndo: Some(undo::<W>),
31            xSavepoint: Some(savepoint::<W>),
32            xSavepointUndo: Some(savepoint_undo::<W>),
33            xFrameCount: Some(frame_count::<W>),
34            xFrames: Some(frames::<W>),
35            xCheckpoint: Some(checkpoint::<W>),
36            xCallback: Some(callback::<W>),
37            xExclusiveMode: Some(exclusive_mode::<W>),
38            xHeapMemory: Some(heap_memory::<W>),
39            xSnapshotGet: None,
40            xSnapshotOpen: None,
41            xSnapshotRecover: None,
42            xSnapshotCheck: None,
43            xSnapshotUnlock: None,
44            xFramesize: None,
45            xFile: None, // TODO: not all wal are single file based
46            xWriteLock: None,
47            xDb: Some(db::<W>),
48        },
49        pData: wal as *mut _,
50    }
51}
52
53/// Turn a `WalManager` into a `libsql_wal_manager`.
54/// The caller is responsible for deallocating `libsql_wal_manager.pData`
55pub fn make_wal_manager<T: WalManager>(wal_manager: T) -> libsql_wal_manager {
56    libsql_wal_manager {
57        bUsesShm: wal_manager.use_shared_memory() as _,
58        xOpen: Some(open::<T>),
59        xClose: Some(close::<T>),
60        xLogDestroy: Some(log_destroy::<T>),
61        xLogExists: Some(log_exists::<T>),
62        xDestroy: Some(destroy_wal_manager::<T>),
63        pData: Box::into_raw(Box::new(wal_manager)) as *mut _,
64    }
65}
66
67// FFI functions mapping C traits to function pointers.
68
69pub unsafe extern "C" fn open<T: WalManager>(
70    wal_manager: *mut wal_manager_impl,
71    vfs: *mut sqlite3_vfs,
72    db_file: *mut sqlite3_file,
73    no_shm_mode: c_int,
74    max_size: c_longlong,
75    db_path: *const c_char,
76    out_wal: *mut libsql_wal,
77) -> c_int {
78    let this = &*(wal_manager as *mut T);
79    let mut vfs = Vfs { vfs };
80    let db_path = CStr::from_ptr(db_path);
81    let mut file = Sqlite3File { inner: db_file };
82
83    match this.open(
84        &mut vfs,
85        &mut file,
86        no_shm_mode as _,
87        max_size as _,
88        db_path,
89    ) {
90        Ok(wal) => {
91            let wal = Box::into_raw(Box::new(wal));
92            *out_wal = construct_libsql_wal(wal);
93            SQLITE_OK
94        }
95        Err(code) => code.extended_code,
96    }
97}
98
99pub unsafe extern "C" fn close<T: WalManager>(
100    wal_manager: *mut wal_manager_impl,
101    wal: *mut wal_impl,
102    db: *mut sqlite3,
103    sync_flags: c_int,
104    n_buf: c_int,
105    z_buf: *mut u8,
106) -> c_int {
107    let this = &*(wal_manager as *mut T);
108    let mut wal = Box::from_raw(wal as *mut T::Wal);
109    let scratch = if z_buf.is_null() {
110        None
111    } else {
112        Some(std::slice::from_raw_parts_mut(z_buf, n_buf as usize))
113    };
114    let mut db = Sqlite3Db { inner: db };
115
116    match this.close(&mut wal, &mut db, sync_flags, scratch) {
117        Ok(_) => SQLITE_OK,
118        Err(code) => code.extended_code,
119    }
120}
121
122pub unsafe extern "C" fn log_destroy<T: WalManager>(
123    wal_manager: *mut wal_manager_impl,
124    vfs: *mut sqlite3_vfs,
125    db_path: *const c_char,
126) -> c_int {
127    let this = &*(wal_manager as *mut T);
128    let db_path = CStr::from_ptr(db_path);
129    let mut vfs = Vfs { vfs };
130    match this.destroy_log(&mut vfs, db_path) {
131        Ok(_) => SQLITE_OK,
132        Err(code) => code.extended_code,
133    }
134}
135
136pub unsafe extern "C" fn log_exists<T: WalManager>(
137    wal_manager: *mut wal_manager_impl,
138    vfs: *mut sqlite3_vfs,
139    db_path: *const c_char,
140    exists: *mut c_int,
141) -> c_int {
142    let this = &*(wal_manager as *mut T);
143    let db_path = CStr::from_ptr(db_path);
144    let mut vfs = Vfs { vfs };
145    match this.log_exists(&mut vfs, db_path) {
146        Ok(res) => {
147            *exists = res as _;
148            SQLITE_OK
149        }
150        Err(code) => code.extended_code,
151    }
152}
153
154pub unsafe extern "C" fn destroy_wal_manager<T: WalManager>(wal_manager: *mut wal_manager_impl) {
155    let this = Box::from_raw(wal_manager as *mut T);
156    this.destroy();
157}
158
159pub unsafe extern "C" fn limit<T: Wal>(wal: *mut wal_impl, limit: i64) {
160    let this = &mut (*(wal as *mut T));
161    this.limit(limit);
162}
163
164pub unsafe extern "C" fn begin_read_transaction<T: Wal>(
165    wal: *mut wal_impl,
166    changed: *mut i32,
167) -> i32 {
168    let this = &mut (*(wal as *mut T));
169    match this.begin_read_txn() {
170        Ok(res) => {
171            *changed = res as i32;
172            SQLITE_OK
173        }
174        Err(code) => code.extended_code,
175    }
176}
177
178pub unsafe extern "C" fn end_read_transaction<T: Wal>(wal: *mut wal_impl) {
179    let this = &mut (*(wal as *mut T));
180    this.end_read_txn();
181}
182
183pub unsafe extern "C" fn find_frame<T: Wal>(
184    wal: *mut wal_impl,
185    pgno: u32,
186    frame: *mut u32,
187) -> c_int {
188    let this = &mut (*(wal as *mut T));
189    match this.find_frame(NonZeroU32::new(pgno).expect("invalid page number")) {
190        Ok(fno) => {
191            *frame = fno.map(|x| x.get()).unwrap_or(0);
192            SQLITE_OK
193        }
194        Err(code) => code.extended_code,
195    }
196}
197
198pub unsafe extern "C" fn read_frame<T: Wal>(
199    wal: *mut wal_impl,
200    frame: u32,
201    n_out: c_int,
202    p_out: *mut u8,
203) -> i32 {
204    let this = &mut (*(wal as *mut T));
205    let buffer = std::slice::from_raw_parts_mut(p_out, n_out as usize);
206    match this.read_frame(
207        NonZeroU32::new(frame).expect("invalid frame number"),
208        buffer,
209    ) {
210        Ok(_) => SQLITE_OK,
211        Err(code) => code.extended_code,
212    }
213}
214
215pub unsafe extern "C" fn read_frame_raw<T: Wal>(
216    wal: *mut wal_impl,
217    frame: u32,
218    n_out: c_int,
219    p_out: *mut u8,
220) -> i32 {
221    let this = &mut (*(wal as *mut T));
222    let buffer = std::slice::from_raw_parts_mut(p_out, n_out as usize);
223    match this.read_frame_raw(
224        NonZeroU32::new(frame).expect("invalid frame number"),
225        buffer,
226    ) {
227        Ok(_) => SQLITE_OK,
228        Err(code) => code.extended_code,
229    }
230}
231
232pub unsafe extern "C" fn db_size<T: Wal>(wal: *mut wal_impl) -> u32 {
233    let this = &mut (*(wal as *mut T));
234    this.db_size()
235}
236
237pub unsafe extern "C" fn begin_write_transaction<T: Wal>(wal: *mut wal_impl) -> i32 {
238    let this = &mut (*(wal as *mut T));
239    match this.begin_write_txn() {
240        Ok(_) => SQLITE_OK,
241        Err(code) => code.extended_code,
242    }
243}
244
245pub unsafe extern "C" fn end_write_transaction<T: Wal>(wal: *mut wal_impl) -> i32 {
246    let this = &mut (*(wal as *mut T));
247    match this.end_write_txn() {
248        Ok(_) => SQLITE_OK,
249        Err(code) => code.extended_code,
250    }
251}
252
253pub unsafe extern "C" fn undo<T: Wal>(
254    wal: *mut wal_impl,
255    func: Option<unsafe extern "C" fn(*mut c_void, u32) -> i32>,
256    undo_ctx: *mut c_void,
257) -> i32 {
258    let this = &mut (*(wal as *mut T));
259    struct SqliteUndoHandler {
260        data: *mut c_void,
261        f: unsafe extern "C" fn(busy_param: *mut c_void, page_no: u32) -> c_int,
262    }
263
264    impl UndoHandler for SqliteUndoHandler {
265        fn handle_undo(&mut self, page_no: u32) -> Result<(), libsql_ffi::Error> {
266            let rc = unsafe { (self.f)(self.data, page_no) };
267            if rc != 0 {
268                Err(libsql_ffi::Error::new(rc))
269            } else {
270                Ok(())
271            }
272        }
273    }
274
275    let mut undo_handler = func.map(|f| SqliteUndoHandler { data: undo_ctx, f });
276
277    match this.undo(undo_handler.as_mut()) {
278        Ok(_) => SQLITE_OK,
279        Err(code) => code.extended_code,
280    }
281}
282
283pub unsafe extern "C" fn savepoint<T: Wal>(wal: *mut wal_impl, wal_data: *mut u32) {
284    let this = &mut (*(wal as *mut T));
285    let data = std::slice::from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA as usize);
286    this.savepoint(data);
287}
288
289pub unsafe extern "C" fn savepoint_undo<T: Wal>(wal: *mut wal_impl, wal_data: *mut u32) -> i32 {
290    let this = &mut (*(wal as *mut T));
291    let data = std::slice::from_raw_parts_mut(wal_data, WAL_SAVEPOINT_NDATA as usize);
292    match this.savepoint_undo(data) {
293        Ok(_) => SQLITE_OK,
294        Err(code) => code.extended_code,
295    }
296}
297
298pub unsafe extern "C" fn frame_count<T: Wal>(
299    wal: *mut wal_impl,
300    locked: i32,
301    out: *mut c_uint,
302) -> c_int {
303    let this = &mut (*(wal as *mut T));
304    match this.frame_count(locked) {
305        Ok(n) => {
306            if !out.is_null() {
307                unsafe {
308                    *out = n as _;
309                }
310            }
311            SQLITE_OK
312        }
313        Err(code) => code.extended_code,
314    }
315}
316
317pub unsafe extern "C" fn frames<T: Wal>(
318    wal: *mut wal_impl,
319    page_size: c_int,
320    page_headers: *mut PgHdr,
321    size_after: u32,
322    is_commit: c_int,
323    sync_flags: c_int,
324    out_commited_frames: *mut c_int,
325) -> c_int {
326    let this = &mut (*(wal as *mut T));
327    let mut headers = PageHeaders {
328        inner: page_headers,
329    };
330    match this.insert_frames(
331        page_size,
332        &mut headers,
333        size_after,
334        is_commit != 0,
335        sync_flags,
336    ) {
337        Ok(n) => {
338            if !out_commited_frames.is_null() {
339                unsafe {
340                    *out_commited_frames = n as _;
341                }
342            }
343            SQLITE_OK
344        }
345        Err(code) => code.extended_code,
346    }
347}
348
349#[tracing::instrument(skip(wal, db), level = "trace")]
350pub unsafe extern "C" fn checkpoint<T: Wal>(
351    wal: *mut wal_impl,
352    db: *mut libsql_ffi::sqlite3,
353    emode: c_int,
354    busy_handler: Option<unsafe extern "C" fn(busy_param: *mut c_void) -> c_int>,
355    busy_arg: *mut c_void,
356    sync_flags: c_int,
357    n_buf: c_int,
358    z_buf: *mut u8,
359    frames_in_wal_out: *mut c_int,
360    checkpointed_frames_out: *mut c_int,
361    checkpoint_cb: Option<
362        unsafe extern "C" fn(
363            data: *mut c_void,
364            max_safe_frame_no: c_int,
365            page: *const u8,
366            page_size: c_int,
367            page_no: c_int,
368            frame_no: c_int,
369        ) -> c_int,
370    >,
371    checkpoint_cb_data: *mut c_void,
372) -> i32 {
373    let this = &mut (*(wal as *mut T));
374    struct SqliteBusyHandler {
375        data: *mut c_void,
376        f: unsafe extern "C" fn(busy_param: *mut c_void) -> c_int,
377    }
378
379    impl BusyHandler for SqliteBusyHandler {
380        fn handle_busy(&mut self) -> bool {
381            unsafe { (self.f)(self.data) != 0 }
382        }
383    }
384
385    struct SqliteCheckpointCallback {
386        data: *mut c_void,
387        f: unsafe extern "C" fn(
388            data: *mut c_void,
389            max_safe_frame_no: c_int,
390            page: *const u8,
391            page_size: c_int,
392            page_no: c_int,
393            frame_no: c_int,
394        ) -> c_int,
395    }
396
397    impl CheckpointCallback for SqliteCheckpointCallback {
398        fn frame(
399            &mut self,
400            max_safe_frame_no: u32,
401            page: &[u8],
402            page_no: NonZeroU32,
403            frame_no: NonZeroU32,
404        ) -> crate::wal::Result<()> {
405            unsafe {
406                let rc = (self.f)(
407                    self.data,
408                    max_safe_frame_no as _,
409                    page.as_ptr(),
410                    page.len() as _,
411                    page_no.get() as _,
412                    frame_no.get() as _,
413                );
414                if rc == 0 {
415                    Ok(())
416                } else {
417                    Err(Error::new(rc))
418                }
419            }
420        }
421
422        fn finish(&mut self) -> crate::wal::Result<()> {
423            unsafe {
424                let rc = (self.f)(self.data, 0, null(), 0, 0, 0);
425                if rc == 0 {
426                    Ok(())
427                } else {
428                    Err(Error::new(rc))
429                }
430            }
431        }
432    }
433
434    let mut busy_handler = busy_handler.map(|f| SqliteBusyHandler { data: busy_arg, f });
435    let mut checkpoint_cb = checkpoint_cb.map(|f| SqliteCheckpointCallback {
436        f,
437        data: checkpoint_cb_data,
438    });
439    let buf = std::slice::from_raw_parts_mut(z_buf, n_buf as usize);
440
441    let mode = match emode {
442        e if e == SQLITE_CHECKPOINT_TRUNCATE => CheckpointMode::Truncate,
443        e if e == SQLITE_CHECKPOINT_FULL => CheckpointMode::Full,
444        e if e == SQLITE_CHECKPOINT_PASSIVE => CheckpointMode::Passive,
445        e if e == SQLITE_CHECKPOINT_RESTART => CheckpointMode::Restart,
446        _ => panic!("invalid checkpoint mode"),
447    };
448
449    let in_wal = (!frames_in_wal_out.is_null()).then_some(&mut *frames_in_wal_out);
450    let backfilled = (!checkpointed_frames_out.is_null()).then_some(&mut *checkpointed_frames_out);
451    let mut db = Sqlite3Db { inner: db };
452    match this.checkpoint(
453        &mut db,
454        mode,
455        busy_handler.as_mut().map(|x| x as _),
456        sync_flags as _,
457        buf,
458        checkpoint_cb.as_mut().map(|x| x as _),
459        in_wal,
460        backfilled,
461    ) {
462        Ok(()) => SQLITE_OK,
463        Err(code) => code.extended_code,
464    }
465}
466
467pub unsafe extern "C" fn callback<T: Wal>(wal: *mut wal_impl) -> c_int {
468    let this = &mut (*(wal as *mut T));
469    this.callback()
470}
471
472pub unsafe extern "C" fn exclusive_mode<T: Wal>(wal: *mut wal_impl, op: c_int) -> c_int {
473    let this = &mut (*(wal as *mut T));
474    match this.exclusive_mode(op) {
475        Ok(_) => SQLITE_OK,
476        Err(code) => code.extended_code,
477    }
478}
479
480pub unsafe extern "C" fn heap_memory<T: Wal>(wal: *mut wal_impl) -> c_int {
481    let this = &mut (*(wal as *mut T));
482    this.uses_heap_memory() as _
483}
484
485pub unsafe extern "C" fn db<T: Wal>(wal: *mut wal_impl, db: *mut libsql_ffi::sqlite3) {
486    let this = &mut (*(wal as *mut T));
487    let mut db = Sqlite3Db { inner: db };
488    this.set_db(&mut db);
489}