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
15pub(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, xWriteLock: None,
47 xDb: Some(db::<W>),
48 },
49 pData: wal as *mut _,
50 }
51}
52
53pub 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
67pub 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}