Skip to main content

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