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