sqlite_vfs_ic/
lib.rs

1#![allow(clippy::question_mark)]
2//! Create a custom SQLite virtual file system by implementing the [Vfs] trait and registering it
3//! using [register].
4
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::ffi::{c_void, CStr, CString};
8use std::io::ErrorKind;
9use std::mem::{size_of, ManuallyDrop, MaybeUninit};
10use std::ops::Range;
11use std::os::raw::{c_char, c_int};
12use std::pin::Pin;
13use std::ptr::null_mut;
14use std::slice;
15use std::sync::{Arc, Mutex};
16use std::time::Duration;
17
18mod ffi;
19
20/// A file opened by [Vfs].
21pub trait DatabaseHandle: Sync {
22    /// An optional trait used to store a WAL (write-ahead log).
23    type WalIndex: wip::WalIndex;
24
25    /// Return the current size in bytes of the database.
26    fn size(&self) -> Result<u64, std::io::Error>;
27
28    /// Reads the exact number of byte required to fill `buf` from the given `offset`.
29    fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), std::io::Error>;
30
31    /// Attempts to write an entire `buf` starting from the given `offset`.
32    fn write_all_at(&mut self, buf: &[u8], offset: u64) -> Result<(), std::io::Error>;
33
34    /// Make sure all writes are committed to the underlying storage. If `data_only` is set to
35    /// `true`, only the data and not the metadata (like size, access time, etc) should be synced.
36    fn sync(&mut self, data_only: bool) -> Result<(), std::io::Error>;
37
38    /// Set the database file to the specified `size`. Truncates or extends the underlying storage.
39    fn set_len(&mut self, size: u64) -> Result<(), std::io::Error>;
40
41    /// Lock the database. Returns whether the requested lock could be acquired.
42    /// Locking sequence:
43    /// - The lock is never moved from [LockKind::None] to anything higher than [LockKind::Shared].
44    /// - A [LockKind::Pending] is never requested explicitly.
45    /// - A [LockKind::Shared] is always held when a [LockKind::Reserved] lock is requested
46    fn lock(&mut self, lock: LockKind) -> Result<bool, std::io::Error>;
47
48    /// Unlock the database.
49    fn unlock(&mut self, lock: LockKind) -> Result<bool, std::io::Error> {
50        self.lock(lock)
51    }
52
53    /// Check if the database this handle points to holds a [LockKind::Reserved],
54    /// [LockKind::Pending] or [LockKind::Exclusive] lock.
55    fn reserved(&mut self) -> Result<bool, std::io::Error>;
56
57    /// Return the current [LockKind] of the this handle.
58    fn current_lock(&self) -> Result<LockKind, std::io::Error>;
59
60    /// Change the chunk size of the database to `chunk_size`.
61    fn set_chunk_size(&self, _chunk_size: usize) -> Result<(), std::io::Error> {
62        Ok(())
63    }
64
65    /// Check if the underlying data of the handle got moved or deleted. When moved, the handle can
66    /// still be read from, but not written to anymore.
67    fn moved(&self) -> Result<bool, std::io::Error> {
68        Ok(false)
69    }
70
71    fn wal_index(&self, readonly: bool) -> Result<Self::WalIndex, std::io::Error>;
72}
73
74/// A virtual file system for SQLite.
75pub trait Vfs: Sync {
76    /// The file returned by [Vfs::open].
77    type Handle: DatabaseHandle;
78
79    /// Open the database `db` (of type `opts.kind`).
80    fn open(&self, db: &str, opts: OpenOptions) -> Result<Self::Handle, std::io::Error>;
81
82    /// Delete the database `db`.
83    fn delete(&self, db: &str) -> Result<(), std::io::Error>;
84
85    /// Check if a database `db` already exists.
86    fn exists(&self, db: &str) -> Result<bool, std::io::Error>;
87
88    /// Generate and return a path for a temporary database.
89    fn temporary_name(&self) -> String;
90
91    /// Populate the `buffer` with random data.
92    fn random(&self, buffer: &mut [i8]);
93
94    /// Sleep for `duration`. Return the duration actually slept.
95    fn sleep(&self, duration: Duration) -> Duration;
96
97    /// Check access to `db`. The default implementation always returns `true`.
98    fn access(&self, _db: &str, _write: bool) -> Result<bool, std::io::Error> {
99        Ok(true)
100    }
101
102    /// Retrieve the full pathname of a database `db`.
103    fn full_pathname<'a>(&self, db: &'a str) -> Result<Cow<'a, str>, std::io::Error> {
104        Ok(db.into())
105    }
106}
107
108#[doc(hidden)]
109pub mod wip {
110    use super::*;
111
112    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
113    #[repr(u16)]
114    pub enum WalIndexLock {
115        None = 1,
116        Shared,
117        Exclusive,
118    }
119
120    pub trait WalIndex: Sync {
121        fn enabled() -> bool {
122            true
123        }
124
125        fn map(&mut self, region: u32) -> Result<[u8; 32768], std::io::Error>;
126        fn lock(&mut self, locks: Range<u8>, lock: WalIndexLock) -> Result<bool, std::io::Error>;
127        fn delete(self) -> Result<(), std::io::Error>;
128
129        fn pull(&mut self, _region: u32, _data: &mut [u8; 32768]) -> Result<(), std::io::Error> {
130            Ok(())
131        }
132
133        fn push(&mut self, _region: u32, _data: &[u8; 32768]) -> Result<(), std::io::Error> {
134            Ok(())
135        }
136    }
137}
138
139#[derive(Debug, Clone, PartialEq)]
140pub struct OpenOptions {
141    /// The object type that is being opened.
142    pub kind: OpenKind,
143
144    /// The access an object is opened with.
145    pub access: OpenAccess,
146
147    /// The file should be deleted when it is closed.
148    delete_on_close: bool,
149}
150
151/// The object type that is being opened.
152#[derive(Debug, Clone, Copy, PartialEq)]
153pub enum OpenKind {
154    MainDb,
155    MainJournal,
156    TempDb,
157    TempJournal,
158    TransientDb,
159    SubJournal,
160    SuperJournal,
161    Wal,
162}
163
164/// The access an object is opened with.
165#[derive(Debug, Clone, Copy, PartialEq)]
166pub enum OpenAccess {
167    /// Read access.
168    Read,
169
170    /// Write access (includes read access).
171    Write,
172
173    /// Create the file if it does not exist (includes write and read access).
174    Create,
175
176    /// Create the file, but throw if it it already exist (includes write and read access).
177    CreateNew,
178}
179
180/// The access an object is opened with.
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum LockKind {
183    /// No locks are held. The database may be neither read nor written. Any internally cached data
184    /// is considered suspect and subject to verification against the database file before being
185    /// used. Other processes can read or write the database as their own locking states permit.
186    /// This is the default state.
187    None,
188
189    /// The database may be read but not written. Any number of processes can hold
190    /// [LockKind::Shared] locks at the same time, hence there can be many simultaneous readers. But
191    /// no other thread or process is allowed to write to the database file while one or more
192    /// [LockKind::Shared] locks are active.
193    Shared,
194
195    /// A [LockKind::Reserved] lock means that the process is planning on writing to the database
196    /// file at some point in the future but that it is currently just reading from the file. Only a
197    /// single [LockKind::Reserved] lock may be active at one time, though multiple
198    /// [LockKind::Shared] locks can coexist with a single [LockKind::Reserved] lock.
199    /// [LockKind::Reserved] differs from [LockKind::Pending] in that new [LockKind::Shared] locks
200    /// can be acquired while there is a [LockKind::Reserved] lock.
201    Reserved,
202
203    /// A [LockKind::Pending] lock means that the process holding the lock wants to write to the
204    /// database as soon as possible and is just waiting on all current [LockKind::Shared] locks to
205    /// clear so that it can get an [LockKind::Exclusive] lock. No new [LockKind::Shared] locks are
206    /// permitted against the database if a [LockKind::Pending] lock is active, though existing
207    /// [LockKind::Shared] locks are allowed to continue.
208    Pending,
209
210    /// An [LockKind::Exclusive] lock is needed in order to write to the database file. Only one
211    /// [LockKind::Exclusive] lock is allowed on the file and no other locks of any kind are allowed
212    /// to coexist with an [LockKind::Exclusive] lock. In order to maximize concurrency, SQLite
213    /// works to minimize the amount of time that [LockKind::Exclusive] locks are held.
214    Exclusive,
215}
216
217struct State<V> {
218    name: CString,
219    vfs: Arc<V>,
220    #[cfg(any(feature = "syscall", feature = "loadext"))]
221    parent_vfs: *mut ffi::sqlite3_vfs,
222    io_methods: ffi::sqlite3_io_methods,
223    last_error: Arc<Mutex<Option<(i32, std::io::Error)>>>,
224    next_id: usize,
225}
226
227/// Register a virtual file system ([Vfs]) to SQLite.
228pub fn register<F: DatabaseHandle, V: Vfs<Handle = F>>(
229    name: &str,
230    vfs: V,
231    as_default: bool,
232) -> Result<(), RegisterError> {
233    let io_methods = ffi::sqlite3_io_methods {
234        iVersion: 2,
235        xClose: Some(io::close::<V, F>),
236        xRead: Some(io::read::<V, F>),
237        xWrite: Some(io::write::<V, F>),
238        xTruncate: Some(io::truncate::<V, F>),
239        xSync: Some(io::sync::<V, F>),
240        xFileSize: Some(io::file_size::<V, F>),
241        xLock: Some(io::lock::<V, F>),
242        xUnlock: Some(io::unlock::<V, F>),
243        xCheckReservedLock: Some(io::check_reserved_lock::<V, F>),
244        xFileControl: Some(io::file_control::<V, F>),
245        xSectorSize: Some(io::sector_size::<F>),
246        xDeviceCharacteristics: Some(io::device_characteristics::<V, F>),
247        xShmMap: Some(io::shm_map::<V, F>),
248        xShmLock: Some(io::shm_lock::<V, F>),
249        xShmBarrier: Some(io::shm_barrier::<V, F>),
250        xShmUnmap: Some(io::shm_unmap::<V, F>),
251        xFetch: None,
252        xUnfetch: None,
253    };
254    let name = CString::new(name)?;
255    let name_ptr = name.as_ptr();
256    let ptr = Box::into_raw(Box::new(State {
257        name,
258        vfs: Arc::new(vfs),
259        #[cfg(any(feature = "syscall", feature = "loadext"))]
260        parent_vfs: unsafe { ffi::sqlite3_vfs_find(std::ptr::null_mut()) },
261        io_methods,
262        last_error: Default::default(),
263        next_id: 0,
264    }));
265    let vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs {
266        #[cfg(not(feature = "syscall"))]
267        iVersion: 2,
268        #[cfg(feature = "syscall")]
269        iVersion: 3,
270        szOsFile: size_of::<FileState<V, F>>() as i32,
271        mxPathname: MAX_PATH_LENGTH as i32, // max path length supported by VFS
272        pNext: null_mut(),
273        zName: name_ptr,
274        pAppData: ptr as _,
275        xOpen: Some(vfs::open::<F, V>),
276        xDelete: Some(vfs::delete::<V>),
277        xAccess: Some(vfs::access::<V>),
278        xFullPathname: Some(vfs::full_pathname::<V>),
279        xDlOpen: Some(vfs::dlopen::<V>),
280        xDlError: Some(vfs::dlerror::<V>),
281        xDlSym: Some(vfs::dlsym::<V>),
282        xDlClose: Some(vfs::dlclose::<V>),
283        xRandomness: Some(vfs::randomness::<V>),
284        xSleep: Some(vfs::sleep::<V>),
285        xCurrentTime: Some(vfs::current_time::<V>),
286        xGetLastError: Some(vfs::get_last_error::<V>),
287        xCurrentTimeInt64: Some(vfs::current_time_int64::<V>),
288
289        #[cfg(not(feature = "syscall"))]
290        xSetSystemCall: None,
291        #[cfg(not(feature = "syscall"))]
292        xGetSystemCall: None,
293        #[cfg(not(feature = "syscall"))]
294        xNextSystemCall: None,
295
296        #[cfg(feature = "syscall")]
297        xSetSystemCall: Some(vfs::set_system_call::<V>),
298        #[cfg(feature = "syscall")]
299        xGetSystemCall: Some(vfs::get_system_call::<V>),
300        #[cfg(feature = "syscall")]
301        xNextSystemCall: Some(vfs::next_system_call::<V>),
302    }));
303
304    let result = unsafe { ffi::sqlite3_vfs_register(vfs, as_default as i32) };
305    if result != ffi::SQLITE_OK {
306        return Err(RegisterError::Register(result));
307    }
308
309    // TODO: return object that allows to unregister (and cleanup the memory)?
310
311    Ok(())
312}
313
314// TODO: add to [Vfs]?
315const MAX_PATH_LENGTH: usize = 512;
316
317#[repr(C)]
318struct FileState<V, F: DatabaseHandle> {
319    base: ffi::sqlite3_file,
320    ext: MaybeUninit<FileExt<V, F>>,
321}
322
323#[repr(C)]
324struct FileExt<V, F: DatabaseHandle> {
325    vfs: Arc<V>,
326    vfs_name: CString,
327    db_name: String,
328    file: F,
329    delete_on_close: bool,
330    /// The last error; shared with the VFS.
331    last_error: Arc<Mutex<Option<(i32, std::io::Error)>>>,
332    /// The last error number of this file/connection (not shared with the VFS).
333    last_errno: i32,
334    wal_index: Option<(F::WalIndex, bool)>,
335    wal_index_regions: HashMap<u32, Pin<Box<[u8; 32768]>>>,
336    wal_index_locks: HashMap<u8, wip::WalIndexLock>,
337    has_exclusive_lock: bool,
338    id: usize,
339    chunk_size: Option<usize>,
340    persist_wal: bool,
341    powersafe_overwrite: bool,
342}
343
344// Example mem-fs implementation:
345// https://github.com/sqlite/sqlite/blob/a959bf53110bfada67a3a52187acd57aa2f34e19/ext/misc/memvfs.c
346mod vfs {
347    use super::*;
348
349    /// Open a new file handler.
350    pub unsafe extern "C" fn open<F: DatabaseHandle, V: Vfs<Handle = F>>(
351        p_vfs: *mut ffi::sqlite3_vfs,
352        z_name: *const c_char,
353        p_file: *mut ffi::sqlite3_file,
354        flags: c_int,
355        p_out_flags: *mut c_int,
356    ) -> c_int {
357        let state = match vfs_state::<V>(p_vfs) {
358            Ok(state) => state,
359            Err(_) => return ffi::SQLITE_ERROR,
360        };
361
362        let name = if z_name.is_null() {
363            None
364        } else {
365            match CStr::from_ptr(z_name).to_str() {
366                Ok(name) => Some(name),
367                Err(_) => {
368                    return state.set_last_error(
369                        ffi::SQLITE_CANTOPEN,
370                        std::io::Error::new(
371                            ErrorKind::Other,
372                            format!(
373                                "open failed: database must be valid utf8 (received: {:?})",
374                                CStr::from_ptr(z_name)
375                            ),
376                        ),
377                    )
378                }
379            }
380        };
381        log::trace!("open z_name={:?} flags={}", name, flags);
382
383        let mut opts = match OpenOptions::from_flags(flags) {
384            Some(opts) => opts,
385            None => {
386                return state.set_last_error(
387                    ffi::SQLITE_CANTOPEN,
388                    std::io::Error::new(ErrorKind::Other, "invalid open flags"),
389                );
390            }
391        };
392
393        if z_name.is_null() && !opts.delete_on_close {
394            return state.set_last_error(
395                ffi::SQLITE_CANTOPEN,
396                std::io::Error::new(
397                    ErrorKind::Other,
398                    "delete on close expected for temporary database",
399                ),
400            );
401        }
402
403        let out_file = match (p_file as *mut FileState<V, F>).as_mut() {
404            Some(f) => f,
405            None => {
406                return state.set_last_error(
407                    ffi::SQLITE_CANTOPEN,
408                    std::io::Error::new(ErrorKind::Other, "invalid file pointer"),
409                );
410            }
411        };
412
413        let mut powersafe_overwrite = true;
414        if flags & ffi::SQLITE_OPEN_URI > 0 && name.is_some() {
415            let param = b"psow\0";
416            if ffi::sqlite3_uri_boolean(z_name, param.as_ptr() as *const c_char, 1) == 0 {
417                powersafe_overwrite = false;
418            }
419        }
420
421        let name = name.map_or_else(|| state.vfs.temporary_name(), String::from);
422        let result = state.vfs.open(&name, opts.clone());
423        let result = match result {
424            Ok(f) => Ok(f),
425            // handle creation failure due to readonly directory
426            Err(err)
427                if err.kind() == ErrorKind::PermissionDenied
428                    && matches!(
429                        opts.kind,
430                        OpenKind::SuperJournal | OpenKind::MainJournal | OpenKind::Wal
431                    )
432                    && matches!(opts.access, OpenAccess::Create | OpenAccess::CreateNew)
433                    && !state.vfs.exists(&name).unwrap_or(false) =>
434            {
435                return state.set_last_error(ffi::SQLITE_READONLY_DIRECTORY, err);
436            }
437
438            // Try again as readonly
439            Err(err)
440                if err.kind() == ErrorKind::PermissionDenied && opts.access != OpenAccess::Read =>
441            {
442                opts.access = OpenAccess::Read;
443                state.vfs.open(&name, opts.clone()).map_err(|_| err)
444            }
445
446            // e.g. tried to open a directory
447            Err(err) if err.kind() == ErrorKind::Other && opts.access == OpenAccess::Read => {
448                return state.set_last_error(ffi::SQLITE_IOERR, err);
449            }
450
451            Err(err) => Err(err),
452        };
453        let file = match result {
454            Ok(f) => f,
455            Err(err) => {
456                return state.set_last_error(ffi::SQLITE_CANTOPEN, err);
457            }
458        };
459
460        if let Some(p_out_flags) = p_out_flags.as_mut() {
461            *p_out_flags = opts.to_flags();
462        }
463
464        out_file.base.pMethods = &state.io_methods;
465        out_file.ext.write(FileExt {
466            vfs: state.vfs.clone(),
467            vfs_name: state.name.clone(),
468            db_name: name,
469            file,
470            delete_on_close: opts.delete_on_close,
471            last_error: Arc::clone(&state.last_error),
472            last_errno: 0,
473            wal_index: None,
474            wal_index_regions: Default::default(),
475            wal_index_locks: Default::default(),
476            has_exclusive_lock: false,
477            id: state.next_id,
478            chunk_size: None,
479            persist_wal: false,
480            powersafe_overwrite,
481        });
482        state.next_id = state.next_id.overflowing_add(1).0;
483
484        #[cfg(feature = "sqlite_test")]
485        ffi::sqlite3_inc_open_file_count();
486
487        ffi::SQLITE_OK
488    }
489
490    /// Delete the file located at `z_path`. If the `sync_dir` argument is true, ensure the
491    /// file-system modifications are synced to disk before returning.
492    pub unsafe extern "C" fn delete<V: Vfs>(
493        p_vfs: *mut ffi::sqlite3_vfs,
494        z_path: *const c_char,
495        _sync_dir: c_int,
496    ) -> c_int {
497        // #[cfg(feature = "sqlite_test")]
498        // if simulate_io_error() {
499        //     return ffi::SQLITE_ERROR;
500        // }
501
502        let state = match vfs_state::<V>(p_vfs) {
503            Ok(state) => state,
504            Err(_) => return ffi::SQLITE_DELETE,
505        };
506
507        let path = match CStr::from_ptr(z_path).to_str() {
508            Ok(name) => name,
509            Err(_) => {
510                return state.set_last_error(
511                    ffi::SQLITE_ERROR,
512                    std::io::Error::new(
513                        ErrorKind::Other,
514                        format!(
515                            "delete failed: database must be valid utf8 (received: {:?})",
516                            CStr::from_ptr(z_path)
517                        ),
518                    ),
519                )
520            }
521        };
522        log::trace!("delete name={}", path);
523
524        match state.vfs.delete(path) {
525            Ok(_) => ffi::SQLITE_OK,
526            Err(err) => {
527                if err.kind() == ErrorKind::NotFound {
528                    ffi::SQLITE_IOERR_DELETE_NOENT
529                } else {
530                    state.set_last_error(ffi::SQLITE_DELETE, err)
531                }
532            }
533        }
534    }
535
536    /// Test for access permissions. Return true if the requested permission is available, or false
537    /// otherwise.
538    pub unsafe extern "C" fn access<V: Vfs>(
539        p_vfs: *mut ffi::sqlite3_vfs,
540        z_path: *const c_char,
541        flags: c_int,
542        p_res_out: *mut c_int,
543    ) -> c_int {
544        #[cfg(feature = "sqlite_test")]
545        if simulate_io_error() {
546            return ffi::SQLITE_IOERR_ACCESS;
547        }
548
549        let state = match vfs_state::<V>(p_vfs) {
550            Ok(state) => state,
551            Err(_) => return ffi::SQLITE_ERROR,
552        };
553
554        let path = match CStr::from_ptr(z_path).to_str() {
555            Ok(name) => name,
556            Err(_) => {
557                log::warn!(
558                    "access failed: database must be valid utf8 (received: {:?})",
559                    CStr::from_ptr(z_path)
560                );
561
562                if let Ok(p_res_out) = p_res_out.as_mut().ok_or_else(null_ptr_error) {
563                    *p_res_out = false as i32;
564                }
565
566                return ffi::SQLITE_OK;
567            }
568        };
569        log::trace!("access z_name={} flags={}", path, flags);
570
571        let result = match flags {
572            ffi::SQLITE_ACCESS_EXISTS => state.vfs.exists(path),
573            ffi::SQLITE_ACCESS_READ => state.vfs.access(path, false),
574            ffi::SQLITE_ACCESS_READWRITE => state.vfs.access(path, true),
575            _ => return ffi::SQLITE_IOERR_ACCESS,
576        };
577
578        if let Err(err) = result.and_then(|ok| {
579            let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
580            *p_res_out = ok as i32;
581            Ok(())
582        }) {
583            return state.set_last_error(ffi::SQLITE_IOERR_ACCESS, err);
584        }
585
586        ffi::SQLITE_OK
587    }
588
589    /// Populate buffer `z_out` with the full canonical pathname corresponding to the pathname in
590    /// `z_path`. `z_out` is guaranteed to point to a buffer of at least (INST_MAX_PATHNAME+1)
591    /// bytes.
592    pub unsafe extern "C" fn full_pathname<V: Vfs>(
593        p_vfs: *mut ffi::sqlite3_vfs,
594        z_path: *const c_char,
595        n_out: c_int,
596        z_out: *mut c_char,
597    ) -> c_int {
598        // #[cfg(feature = "sqlite_test")]
599        // if simulate_io_error() {
600        //     return ffi::SQLITE_ERROR;
601        // }
602
603        let state = match vfs_state::<V>(p_vfs) {
604            Ok(state) => state,
605            Err(_) => return ffi::SQLITE_ERROR,
606        };
607
608        let path = match CStr::from_ptr(z_path).to_str() {
609            Ok(name) => name,
610            Err(_) => {
611                return state.set_last_error(
612                    ffi::SQLITE_ERROR,
613                    std::io::Error::new(
614                        ErrorKind::Other,
615                        format!(
616                            "full_pathname failed: database must be valid utf8 (received: {:?})",
617                            CStr::from_ptr(z_path)
618                        ),
619                    ),
620                )
621            }
622        };
623        log::trace!("full_pathname name={}", path);
624
625        let name = match state.vfs.full_pathname(path).and_then(|name| {
626            CString::new(name.to_string()).map_err(|_| {
627                std::io::Error::new(ErrorKind::Other, "name must not contain a nul byte")
628            })
629        }) {
630            Ok(name) => name,
631            Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
632        };
633
634        let name = name.to_bytes_with_nul();
635        if name.len() > n_out as usize || name.len() > MAX_PATH_LENGTH {
636            return state.set_last_error(
637                ffi::SQLITE_CANTOPEN,
638                std::io::Error::new(ErrorKind::Other, "full pathname is too long"),
639            );
640        }
641        let out = slice::from_raw_parts_mut(z_out as *mut u8, name.len());
642        out.copy_from_slice(name);
643
644        ffi::SQLITE_OK
645    }
646
647    /// Open the dynamic library located at `z_path` and return a handle.
648    #[allow(unused_variables)]
649    pub unsafe extern "C" fn dlopen<V>(
650        p_vfs: *mut ffi::sqlite3_vfs,
651        z_path: *const c_char,
652    ) -> *mut c_void {
653        log::trace!("dlopen");
654
655        #[cfg(feature = "loadext")]
656        {
657            let state = match vfs_state::<V>(p_vfs) {
658                Ok(state) => state,
659                Err(_) => return null_mut(),
660            };
661
662            if let Some(dlopen) = state.parent_vfs.as_ref().and_then(|v| v.xDlOpen) {
663                return dlopen(state.parent_vfs, z_path);
664            }
665        }
666
667        null_mut()
668    }
669
670    /// Populate the buffer `z_err_msg` (size `n_byte` bytes) with a human readable utf-8 string
671    /// describing the most recent error encountered associated with dynamic libraries.
672    #[allow(unused_variables)]
673    pub unsafe extern "C" fn dlerror<V>(
674        p_vfs: *mut ffi::sqlite3_vfs,
675        n_byte: c_int,
676        z_err_msg: *mut c_char,
677    ) {
678        log::trace!("dlerror");
679
680        #[cfg(feature = "loadext")]
681        {
682            let state = match vfs_state::<V>(p_vfs) {
683                Ok(state) => state,
684                Err(_) => return,
685            };
686
687            if let Some(dlerror) = state.parent_vfs.as_ref().and_then(|v| v.xDlError) {
688                return dlerror(state.parent_vfs, n_byte, z_err_msg);
689            }
690
691            return;
692        }
693
694        #[cfg(not(feature = "loadext"))]
695        {
696            let msg = concat!("Loadable extensions are not supported", "\0");
697            ffi::sqlite3_snprintf(n_byte, z_err_msg, msg.as_ptr() as _);
698        }
699    }
700
701    /// Return a pointer to the symbol `z_sym` in the dynamic library pHandle.
702    #[allow(unused_variables)]
703    pub unsafe extern "C" fn dlsym<V>(
704        p_vfs: *mut ffi::sqlite3_vfs,
705        p: *mut c_void,
706        z_sym: *const c_char,
707    ) -> Option<unsafe extern "C" fn(*mut ffi::sqlite3_vfs, *mut c_void, *const c_char)> {
708        log::trace!("dlsym");
709
710        #[cfg(feature = "loadext")]
711        {
712            let state = match vfs_state::<V>(p_vfs) {
713                Ok(state) => state,
714                Err(_) => return None,
715            };
716
717            if let Some(dlsym) = state.parent_vfs.as_ref().and_then(|v| v.xDlSym) {
718                return dlsym(state.parent_vfs, p, z_sym);
719            }
720        }
721
722        None
723    }
724
725    /// Close the dynamic library handle `p_handle`.
726    #[allow(unused_variables)]
727    pub unsafe extern "C" fn dlclose<V>(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) {
728        log::trace!("dlclose");
729
730        #[cfg(feature = "loadext")]
731        {
732            let state = match vfs_state::<V>(p_vfs) {
733                Ok(state) => state,
734                Err(_) => return,
735            };
736
737            if let Some(dlclose) = state.parent_vfs.as_ref().and_then(|v| v.xDlClose) {
738                return dlclose(state.parent_vfs, p_handle);
739            }
740        }
741    }
742
743    /// Populate the buffer pointed to by `z_buf_out` with `n_byte` bytes of random data.
744    pub unsafe extern "C" fn randomness<V: Vfs>(
745        p_vfs: *mut ffi::sqlite3_vfs,
746        n_byte: c_int,
747        z_buf_out: *mut c_char,
748    ) -> c_int {
749        log::trace!("randomness");
750
751        let bytes = slice::from_raw_parts_mut(z_buf_out as *mut i8, n_byte as usize);
752        if cfg!(feature = "sqlite_test") {
753            // During testing, the buffer is simply initialized to all zeroes for repeatability
754            bytes.fill(0);
755        } else {
756            let state = match vfs_state::<V>(p_vfs) {
757                Ok(state) => state,
758                Err(_) => return 0,
759            };
760
761            state.vfs.random(bytes);
762        }
763        bytes.len() as c_int
764    }
765
766    /// Sleep for `n_micro` microseconds. Return the number of microseconds actually slept.
767    pub unsafe extern "C" fn sleep<V: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, n_micro: c_int) -> c_int {
768        log::trace!("sleep");
769
770        let state = match vfs_state::<V>(p_vfs) {
771            Ok(state) => state,
772            Err(_) => return ffi::SQLITE_ERROR,
773        };
774        state
775            .vfs
776            .sleep(Duration::from_micros(n_micro as u64))
777            .as_micros() as c_int
778    }
779
780    /// Return the current time as a Julian Day number in `p_time_out`.
781    pub unsafe extern "C" fn current_time<V>(
782        p_vfs: *mut ffi::sqlite3_vfs,
783        p_time_out: *mut f64,
784    ) -> c_int {
785        log::trace!("current_time");
786
787        let mut i = 0i64;
788        current_time_int64::<V>(p_vfs, &mut i);
789
790        *p_time_out = i as f64 / 86400000.0;
791        ffi::SQLITE_OK
792    }
793
794    pub unsafe extern "C" fn current_time_int64<V>(
795        _p_vfs: *mut ffi::sqlite3_vfs,
796        p: *mut i64,
797    ) -> i32 {
798        log::trace!("current_time_int64");
799
800        const UNIX_EPOCH: i64 = 24405875 * 8640000;
801        let now = if cfg!(all(target_arch = "wasm32", target_os = "unknown")) {
802            (ic_cdk::api::time() as f64 / 1000000000.0) as i64 + UNIX_EPOCH
803        } else {
804            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as i64 + UNIX_EPOCH
805        };
806        #[cfg(feature = "sqlite_test")]
807        let now = if ffi::sqlite3_get_current_time() > 0 {
808            ffi::sqlite3_get_current_time() as i64 * 1000 + UNIX_EPOCH
809        } else {
810            now
811        };
812
813        *p = now;
814        ffi::SQLITE_OK
815    }
816
817    #[cfg(feature = "syscall")]
818    pub unsafe extern "C" fn set_system_call<V>(
819        p_vfs: *mut ffi::sqlite3_vfs,
820        z_name: *const ::std::os::raw::c_char,
821        p_new_func: ffi::sqlite3_syscall_ptr,
822    ) -> ::std::os::raw::c_int {
823        let state = match vfs_state::<V>(p_vfs) {
824            Ok(state) => state,
825            Err(_) => return ffi::SQLITE_ERROR,
826        };
827
828        if let Some(set_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xSetSystemCall) {
829            return set_system_call(state.parent_vfs, z_name, p_new_func);
830        }
831
832        ffi::SQLITE_ERROR
833    }
834
835    #[cfg(feature = "syscall")]
836    pub unsafe extern "C" fn get_system_call<V>(
837        p_vfs: *mut ffi::sqlite3_vfs,
838        z_name: *const ::std::os::raw::c_char,
839    ) -> ffi::sqlite3_syscall_ptr {
840        let state = match vfs_state::<V>(p_vfs) {
841            Ok(state) => state,
842            Err(_) => return None,
843        };
844
845        if let Some(get_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xGetSystemCall) {
846            return get_system_call(state.parent_vfs, z_name);
847        }
848
849        None
850    }
851
852    #[cfg(feature = "syscall")]
853    pub unsafe extern "C" fn next_system_call<V>(
854        p_vfs: *mut ffi::sqlite3_vfs,
855        z_name: *const ::std::os::raw::c_char,
856    ) -> *const ::std::os::raw::c_char {
857        let state = match vfs_state::<V>(p_vfs) {
858            Ok(state) => state,
859            Err(_) => return std::ptr::null(),
860        };
861
862        if let Some(next_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xNextSystemCall) {
863            return next_system_call(state.parent_vfs, z_name);
864        }
865
866        std::ptr::null()
867    }
868
869    pub unsafe extern "C" fn get_last_error<V>(
870        p_vfs: *mut ffi::sqlite3_vfs,
871        n_byte: c_int,
872        z_err_msg: *mut c_char,
873    ) -> c_int {
874        let state = match vfs_state::<V>(p_vfs) {
875            Ok(state) => state,
876            Err(_) => return ffi::SQLITE_ERROR,
877        };
878        if let Some((eno, err)) = state.last_error.lock().unwrap().as_ref() {
879            let msg = match CString::new(err.to_string()) {
880                Ok(msg) => msg,
881                Err(_) => return ffi::SQLITE_ERROR,
882            };
883
884            let msg = msg.to_bytes_with_nul();
885            if msg.len() > n_byte as usize {
886                return ffi::SQLITE_ERROR;
887            }
888            let out = slice::from_raw_parts_mut(z_err_msg as *mut u8, msg.len());
889            out.copy_from_slice(msg);
890
891            return *eno;
892        }
893        ffi::SQLITE_OK
894    }
895}
896
897mod io {
898    use std::collections::hash_map::Entry;
899    use std::mem;
900
901    use super::*;
902    use wip::WalIndex;
903
904    /// Close a file.
905    pub unsafe extern "C" fn close<V: Vfs, F: DatabaseHandle>(
906        p_file: *mut ffi::sqlite3_file,
907    ) -> c_int {
908        if let Some(f) = (p_file as *mut FileState<V, F>).as_mut() {
909            let ext = f.ext.assume_init_mut();
910            if ext.delete_on_close {
911                if let Err(err) = ext.vfs.delete(&ext.db_name) {
912                    return ext.set_last_error(ffi::SQLITE_DELETE, err);
913                }
914            }
915
916            let ext = mem::replace(&mut f.ext, MaybeUninit::uninit());
917            let ext = ext.assume_init(); // extract the value to drop it
918            log::trace!("[{}] close ({})", ext.id, ext.db_name);
919        }
920
921        #[cfg(feature = "sqlite_test")]
922        ffi::sqlite3_dec_open_file_count();
923
924        ffi::SQLITE_OK
925    }
926
927    /// Read data from a file.
928    pub unsafe extern "C" fn read<V, F: DatabaseHandle>(
929        p_file: *mut ffi::sqlite3_file,
930        z_buf: *mut c_void,
931        i_amt: c_int,
932        i_ofst: ffi::sqlite3_int64,
933    ) -> c_int {
934        let state = match file_state::<V, F>(p_file) {
935            Ok(f) => f,
936            Err(_) => return ffi::SQLITE_IOERR_CLOSE,
937        };
938        log::trace!(
939            "[{}] read offset={} len={} ({})",
940            state.id,
941            i_ofst,
942            i_amt,
943            state.db_name
944        );
945
946        let out = slice::from_raw_parts_mut(z_buf as *mut u8, i_amt as usize);
947        if let Err(err) = state.file.read_exact_at(out, i_ofst as u64) {
948            let kind = err.kind();
949            if kind == ErrorKind::UnexpectedEof {
950                return ffi::SQLITE_IOERR_SHORT_READ;
951            } else {
952                return state.set_last_error(ffi::SQLITE_IOERR_READ, err);
953            }
954        }
955
956        ffi::SQLITE_OK
957    }
958
959    /// Write data to a file.
960    pub unsafe extern "C" fn write<V, F: DatabaseHandle>(
961        p_file: *mut ffi::sqlite3_file,
962        z: *const c_void,
963        i_amt: c_int,
964        i_ofst: ffi::sqlite3_int64,
965    ) -> c_int {
966        let state = match file_state::<V, F>(p_file) {
967            Ok(f) => f,
968            Err(_) => return ffi::SQLITE_IOERR_WRITE,
969        };
970        log::trace!(
971            "[{}] write offset={} len={} ({})",
972            state.id,
973            i_ofst,
974            i_amt,
975            state.db_name
976        );
977
978        let data = slice::from_raw_parts(z as *mut u8, i_amt as usize);
979        let result = state.file.write_all_at(data, i_ofst as u64);
980
981        #[cfg(feature = "sqlite_test")]
982        let result = if simulate_io_error() {
983            Err(ErrorKind::Other.into())
984        } else {
985            result
986        };
987
988        #[cfg(feature = "sqlite_test")]
989        let result = if simulate_diskfull_error() {
990            Err(ErrorKind::WriteZero.into())
991        } else {
992            result
993        };
994
995        match result {
996            Ok(_) => {}
997            Err(err) if err.kind() == ErrorKind::WriteZero => {
998                return ffi::SQLITE_FULL;
999            }
1000            Err(err) => return state.set_last_error(ffi::SQLITE_IOERR_WRITE, err),
1001        }
1002
1003        ffi::SQLITE_OK
1004    }
1005
1006    /// Truncate a file.
1007    pub unsafe extern "C" fn truncate<V, F: DatabaseHandle>(
1008        p_file: *mut ffi::sqlite3_file,
1009        size: ffi::sqlite3_int64,
1010    ) -> c_int {
1011        let state = match file_state::<V, F>(p_file) {
1012            Ok(f) => f,
1013            Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1014        };
1015
1016        let size: u64 = if let Some(chunk_size) = state.chunk_size {
1017            (((size as usize + chunk_size - 1) / chunk_size) * chunk_size) as u64
1018        } else {
1019            size as u64
1020        };
1021
1022        log::trace!("[{}] truncate size={} ({})", state.id, size, state.db_name);
1023
1024        // #[cfg(feature = "sqlite_test")]
1025        // if simulate_io_error() {
1026        //     return ffi::SQLITE_IOERR_TRUNCATE;
1027        // }
1028
1029        if let Err(err) = state.file.set_len(size) {
1030            return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1031        }
1032
1033        ffi::SQLITE_OK
1034    }
1035
1036    /// Persist changes to a file.
1037    pub unsafe extern "C" fn sync<V, F: DatabaseHandle>(
1038        p_file: *mut ffi::sqlite3_file,
1039        flags: c_int,
1040    ) -> c_int {
1041        let state = match file_state::<V, F>(p_file) {
1042            Ok(f) => f,
1043            Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1044        };
1045        log::trace!("[{}] sync ({})", state.id, state.db_name);
1046
1047        #[cfg(feature = "sqlite_test")]
1048        {
1049            let is_full_sync = flags & 0x0F == ffi::SQLITE_SYNC_FULL;
1050            if is_full_sync {
1051                ffi::sqlite3_inc_fullsync_count();
1052            }
1053            ffi::sqlite3_inc_sync_count();
1054        }
1055
1056        if let Err(err) = state.file.sync(flags & ffi::SQLITE_SYNC_DATAONLY > 0) {
1057            return state.set_last_error(ffi::SQLITE_IOERR_FSYNC, err);
1058        }
1059
1060        // #[cfg(feature = "sqlite_test")]
1061        // if simulate_io_error() {
1062        //     return ffi::SQLITE_ERROR;
1063        // }
1064
1065        ffi::SQLITE_OK
1066    }
1067
1068    /// Return the current file-size of a file.
1069    pub unsafe extern "C" fn file_size<V, F: DatabaseHandle>(
1070        p_file: *mut ffi::sqlite3_file,
1071        p_size: *mut ffi::sqlite3_int64,
1072    ) -> c_int {
1073        let state = match file_state::<V, F>(p_file) {
1074            Ok(f) => f,
1075            Err(_) => return ffi::SQLITE_IOERR_FSTAT,
1076        };
1077        log::trace!("[{}] file_size ({})", state.id, state.db_name);
1078
1079        if let Err(err) = state.file.size().and_then(|n| {
1080            let p_size: &mut ffi::sqlite3_int64 = p_size.as_mut().ok_or_else(null_ptr_error)?;
1081            *p_size = n as ffi::sqlite3_int64;
1082            Ok(())
1083        }) {
1084            return state.set_last_error(ffi::SQLITE_IOERR_FSTAT, err);
1085        }
1086
1087        // #[cfg(feature = "sqlite_test")]
1088        // if simulate_io_error() {
1089        //     return ffi::SQLITE_ERROR;
1090        // }
1091
1092        ffi::SQLITE_OK
1093    }
1094
1095    /// Lock a file.
1096    pub unsafe extern "C" fn lock<V, F: DatabaseHandle>(
1097        p_file: *mut ffi::sqlite3_file,
1098        e_lock: c_int,
1099    ) -> c_int {
1100        let state = match file_state::<V, F>(p_file) {
1101            Ok(f) => f,
1102            Err(_) => return ffi::SQLITE_IOERR_LOCK,
1103        };
1104        log::trace!("[{}] lock ({})", state.id, state.db_name);
1105
1106        let lock = match LockKind::from_i32(e_lock) {
1107            Some(lock) => lock,
1108            None => return ffi::SQLITE_IOERR_LOCK,
1109        };
1110        match state.file.lock(lock) {
1111            Ok(true) => {
1112                state.has_exclusive_lock = lock == LockKind::Exclusive;
1113                log::trace!("[{}] lock={:?} ({})", state.id, lock, state.db_name);
1114
1115                // If just acquired a exclusive database lock while not having any exclusive lock
1116                // on the wal index, make sure the wal index is up to date.
1117                if state.has_exclusive_lock {
1118                    let has_exclusive_wal_index = state
1119                        .wal_index_locks
1120                        .iter()
1121                        .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1122
1123                    if !has_exclusive_wal_index {
1124                        log::trace!(
1125                            "[{}] acquired exclusive db lock, pulling wal index changes",
1126                            state.id,
1127                        );
1128
1129                        if let Some((wal_index, _)) = state.wal_index.as_mut() {
1130                            for (region, data) in &mut state.wal_index_regions {
1131                                if let Err(err) = wal_index.pull(*region as u32, data) {
1132                                    log::error!(
1133                                        "[{}] pulling wal index changes failed: {}",
1134                                        state.id,
1135                                        err
1136                                    )
1137                                }
1138                            }
1139                        }
1140                    }
1141                }
1142
1143                ffi::SQLITE_OK
1144            }
1145            Ok(false) => {
1146                log::trace!(
1147                    "[{}] busy (denied {:?}) ({})",
1148                    state.id,
1149                    lock,
1150                    state.db_name
1151                );
1152                ffi::SQLITE_BUSY
1153            }
1154            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_LOCK, err),
1155        }
1156    }
1157
1158    /// Unlock a file.
1159    pub unsafe extern "C" fn unlock<V, F: DatabaseHandle>(
1160        p_file: *mut ffi::sqlite3_file,
1161        e_lock: c_int,
1162    ) -> c_int {
1163        let state = match file_state::<V, F>(p_file) {
1164            Ok(f) => f,
1165            Err(_) => return ffi::SQLITE_IOERR_UNLOCK,
1166        };
1167        log::trace!("[{}] unlock ({})", state.id, state.db_name);
1168
1169        let lock = match LockKind::from_i32(e_lock) {
1170            Some(lock) => lock,
1171            None => return ffi::SQLITE_IOERR_UNLOCK,
1172        };
1173        match state.file.unlock(lock) {
1174            Ok(true) => {
1175                state.has_exclusive_lock = lock == LockKind::Exclusive;
1176                log::trace!("[{}] unlock={:?} ({})", state.id, lock, state.db_name);
1177                ffi::SQLITE_OK
1178            }
1179            Ok(false) => ffi::SQLITE_BUSY,
1180            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err),
1181        }
1182    }
1183
1184    /// Check if another file-handle holds a [LockKind::Reserved] lock on a file.
1185    pub unsafe extern "C" fn check_reserved_lock<V, F: DatabaseHandle>(
1186        p_file: *mut ffi::sqlite3_file,
1187        p_res_out: *mut c_int,
1188    ) -> c_int {
1189        let state = match file_state::<V, F>(p_file) {
1190            Ok(f) => f,
1191            Err(_) => return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK,
1192        };
1193        log::trace!("[{}] check_reserved_lock ({})", state.id, state.db_name);
1194
1195        // #[cfg(feature = "sqlite_test")]
1196        // if simulate_io_error() {
1197        //     return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK;
1198        // }
1199
1200        if let Err(err) = state.file.reserved().and_then(|is_reserved| {
1201            let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
1202            *p_res_out = is_reserved as c_int;
1203            Ok(())
1204        }) {
1205            return state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err);
1206        }
1207
1208        ffi::SQLITE_OK
1209    }
1210
1211    /// File control method. For custom operations on a mem-file.
1212    pub unsafe extern "C" fn file_control<V: Vfs, F: DatabaseHandle>(
1213        p_file: *mut ffi::sqlite3_file,
1214        op: c_int,
1215        p_arg: *mut c_void,
1216    ) -> c_int {
1217        let state = match file_state::<V, F>(p_file) {
1218            Ok(f) => f,
1219            Err(_) => return ffi::SQLITE_NOTFOUND,
1220        };
1221        log::trace!("[{}] file_control op={} ({})", state.id, op, state.db_name);
1222
1223        // Docs: https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
1224        match op {
1225            // The following op codes are alreay handled by sqlite before, so no need to handle them
1226            // in a custom VFS.
1227            ffi::SQLITE_FCNTL_FILE_POINTER
1228            | ffi::SQLITE_FCNTL_VFS_POINTER
1229            | ffi::SQLITE_FCNTL_JOURNAL_POINTER
1230            | ffi::SQLITE_FCNTL_DATA_VERSION
1231            | ffi::SQLITE_FCNTL_RESERVE_BYTES => ffi::SQLITE_NOTFOUND,
1232
1233            // The following op codes are no longer used and thus ignored.
1234            ffi::SQLITE_FCNTL_SYNC_OMITTED => ffi::SQLITE_NOTFOUND,
1235
1236            // Used for debugging. Write current state of the lock into (int)pArg.
1237            ffi::SQLITE_FCNTL_LOCKSTATE => match state.file.current_lock() {
1238                Ok(lock) => {
1239                    if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1240                        *p_arg = lock as i32;
1241                    }
1242                    ffi::SQLITE_OK
1243                }
1244                Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1245            },
1246
1247            // Relevant for proxy-type locking. Not implemented.
1248            ffi::SQLITE_FCNTL_GET_LOCKPROXYFILE | ffi::SQLITE_FCNTL_SET_LOCKPROXYFILE => {
1249                ffi::SQLITE_NOTFOUND
1250            }
1251
1252            // Write last error number into (int)pArg.
1253            ffi::SQLITE_FCNTL_LAST_ERRNO => {
1254                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1255                    *p_arg = state.last_errno;
1256                }
1257                ffi::SQLITE_OK
1258            }
1259
1260            // Give the VFS layer a hint of how large the database file will grow to be during the
1261            // current transaction.
1262            ffi::SQLITE_FCNTL_SIZE_HINT => {
1263                let size_hint = match (p_arg as *mut i64)
1264                    .as_ref()
1265                    .cloned()
1266                    .and_then(|s| u64::try_from(s).ok())
1267                {
1268                    Some(chunk_size) => chunk_size,
1269                    None => {
1270                        return state.set_last_error(
1271                            ffi::SQLITE_NOTFOUND,
1272                            std::io::Error::new(ErrorKind::Other, "expect size hint arg"),
1273                        );
1274                    }
1275                };
1276
1277                // #[cfg(feature = "sqlite_test")]
1278                // let _benign = simulate_io_error_benign();
1279
1280                let current = match state.file.size() {
1281                    Ok(size) => size,
1282                    Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
1283                };
1284
1285                if current > size_hint {
1286                    return ffi::SQLITE_OK;
1287                }
1288
1289                if let Some(chunk_size) = state.chunk_size {
1290                    let chunk_size = chunk_size as u64;
1291                    let size = ((size_hint + chunk_size - 1) / chunk_size) * chunk_size;
1292                    if let Err(err) = state.file.set_len(size) {
1293                        return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1294                    }
1295                } else if let Err(err) = state.file.set_len(size_hint) {
1296                    return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1297                }
1298
1299                // #[cfg(feature = "sqlite_test")]
1300                // if simulate_io_error() {
1301                //     return ffi::SQLITE_IOERR_TRUNCATE;
1302                // }
1303
1304                ffi::SQLITE_OK
1305            }
1306
1307            // Request that the VFS extends and truncates the database file in chunks of a size
1308            // specified by the user. Return an error as this is not forwarded to the [Vfs] trait
1309            // right now.
1310            ffi::SQLITE_FCNTL_CHUNK_SIZE => {
1311                let chunk_size = match (p_arg as *mut i32)
1312                    .as_ref()
1313                    .cloned()
1314                    .and_then(|s| usize::try_from(s).ok())
1315                {
1316                    Some(chunk_size) => chunk_size,
1317                    None => {
1318                        return state.set_last_error(
1319                            ffi::SQLITE_NOTFOUND,
1320                            std::io::Error::new(ErrorKind::Other, "expect chunk_size arg"),
1321                        );
1322                    }
1323                };
1324
1325                if let Err(err) = state.file.set_chunk_size(chunk_size) {
1326                    return state.set_last_error(ffi::SQLITE_ERROR, err);
1327                }
1328
1329                state.chunk_size = Some(chunk_size);
1330
1331                ffi::SQLITE_OK
1332            }
1333
1334            // Configure automatic retry counts and intervals for certain disk I/O operations for
1335            // the windows VFS in order to provide robustness in the presence of anti-virus
1336            // programs. Not implemented.
1337            ffi::SQLITE_FCNTL_WIN32_AV_RETRY => ffi::SQLITE_NOTFOUND,
1338
1339            // Enable or disable the persistent WAL setting.
1340            ffi::SQLITE_FCNTL_PERSIST_WAL => {
1341                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1342                    if *p_arg < 0 {
1343                        // query current setting
1344                        *p_arg = state.persist_wal as i32;
1345                    } else {
1346                        state.persist_wal = *p_arg == 1;
1347                    }
1348                };
1349
1350                ffi::SQLITE_OK
1351            }
1352
1353            // Indicate that, unless it is rolled back for some reason, the entire database file
1354            // will be overwritten by the current transaction. Not implemented.
1355            ffi::SQLITE_FCNTL_OVERWRITE => ffi::SQLITE_NOTFOUND,
1356
1357            // Used to obtain the names of all VFSes in the VFS stack.
1358            ffi::SQLITE_FCNTL_VFSNAME => {
1359                if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1360                    let name = ManuallyDrop::new(state.vfs_name.clone());
1361                    *p_arg = name.as_ptr();
1362                };
1363
1364                ffi::SQLITE_OK
1365            }
1366
1367            // Set or query the persistent "powersafe-overwrite" or "PSOW" setting.
1368            ffi::SQLITE_FCNTL_POWERSAFE_OVERWRITE => {
1369                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1370                    if *p_arg < 0 {
1371                        // query current setting
1372                        *p_arg = state.powersafe_overwrite as i32;
1373                    } else {
1374                        state.powersafe_overwrite = *p_arg == 1;
1375                    }
1376                };
1377
1378                ffi::SQLITE_OK
1379            }
1380
1381            // Optionally intercept PRAGMA statements. Always fall back to normal pragma processing.
1382            ffi::SQLITE_FCNTL_PRAGMA => ffi::SQLITE_NOTFOUND,
1383
1384            // May be invoked by SQLite on the database file handle shortly after it is opened in
1385            // order to provide a custom VFS with access to the connection's busy-handler callback.
1386            // Not implemented.
1387            ffi::SQLITE_FCNTL_BUSYHANDLER => ffi::SQLITE_NOTFOUND,
1388
1389            // Generate a temporary filename. Not implemented.
1390            ffi::SQLITE_FCNTL_TEMPFILENAME => {
1391                if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1392                    let name = state.vfs.temporary_name();
1393                    // unwrap() is fine as os strings are an arbitrary sequences of non-zero bytes
1394                    let name = CString::new(name.as_bytes()).unwrap();
1395                    let name = ManuallyDrop::new(name);
1396                    *p_arg = name.as_ptr();
1397                };
1398
1399                ffi::SQLITE_OK
1400            }
1401
1402            // Query or set the maximum number of bytes that will be used for memory-mapped I/O.
1403            // Not implemented.
1404            ffi::SQLITE_FCNTL_MMAP_SIZE => ffi::SQLITE_NOTFOUND,
1405
1406            // Advisory information to the VFS about what the higher layers of the SQLite stack are
1407            // doing.
1408            ffi::SQLITE_FCNTL_TRACE => {
1409                let trace = CStr::from_ptr(p_arg as *const c_char);
1410                log::trace!("{}", trace.to_string_lossy());
1411                ffi::SQLITE_OK
1412            }
1413
1414            // Check whether or not the file has been renamed, moved, or deleted since it was first
1415            // opened.
1416            ffi::SQLITE_FCNTL_HAS_MOVED => match state.file.moved() {
1417                Ok(moved) => {
1418                    if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1419                        *p_arg = moved as i32;
1420                    }
1421                    ffi::SQLITE_OK
1422                }
1423                Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1424            },
1425
1426            // Sent to the VFS immediately before the xSync method is invoked on a database file
1427            // descriptor. Silently ignored.
1428            ffi::SQLITE_FCNTL_SYNC => ffi::SQLITE_OK,
1429
1430            // Sent to the VFS after a transaction has been committed immediately but before the
1431            // database is unlocked. Silently ignored.
1432            ffi::SQLITE_FCNTL_COMMIT_PHASETWO => ffi::SQLITE_OK,
1433
1434            // Used for debugging. Swap the file handle with the one pointed to by the pArg
1435            // argument. This capability is used during testing and only needs to be supported when
1436            // SQLITE_TEST is defined. Not implemented.
1437            ffi::SQLITE_FCNTL_WIN32_SET_HANDLE => ffi::SQLITE_NOTFOUND,
1438
1439            // Signal to the VFS layer that it might be advantageous to block on the next WAL lock
1440            // if the lock is not immediately available. The WAL subsystem issues this signal during
1441            // rare circumstances in order to fix a problem with priority inversion.
1442            // Not implemented.
1443            ffi::SQLITE_FCNTL_WAL_BLOCK => ffi::SQLITE_NOTFOUND,
1444
1445            // Implemented by zipvfs only.
1446            ffi::SQLITE_FCNTL_ZIPVFS => ffi::SQLITE_NOTFOUND,
1447
1448            // Implemented by the special VFS used by the RBU extension only.
1449            ffi::SQLITE_FCNTL_RBU => ffi::SQLITE_NOTFOUND,
1450
1451            // Obtain the underlying native file handle associated with a file handle.
1452            // Not implemented.
1453            ffi::SQLITE_FCNTL_WIN32_GET_HANDLE => ffi::SQLITE_NOTFOUND,
1454
1455            // Usage is not documented. Not implemented.
1456            ffi::SQLITE_FCNTL_PDB => ffi::SQLITE_NOTFOUND,
1457
1458            // Used for "batch write mode". Not supported.
1459            ffi::SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
1460            | ffi::SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
1461            | ffi::SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE => ffi::SQLITE_NOTFOUND,
1462
1463            // Configure a VFS to block for up to M milliseconds before failing when attempting to
1464            // obtain a file lock using the xLock or xShmLock methods of the VFS. Not implemented.
1465            ffi::SQLITE_FCNTL_LOCK_TIMEOUT => ffi::SQLITE_NOTFOUND,
1466
1467            // Used by in-memory VFS.
1468            ffi::SQLITE_FCNTL_SIZE_LIMIT => ffi::SQLITE_NOTFOUND,
1469
1470            // Invoked from within a checkpoint in wal mode after the client has finished copying
1471            // pages from the wal file to the database file, but before the *-shm file is updated to
1472            // record the fact that the pages have been checkpointed. Silently ignored.
1473            ffi::SQLITE_FCNTL_CKPT_DONE => ffi::SQLITE_OK,
1474
1475            // Invoked from within a checkpoint in wal mode before the client starts to copy pages
1476            // from the wal file to the database file. Silently ignored.
1477            ffi::SQLITE_FCNTL_CKPT_START => ffi::SQLITE_OK,
1478
1479            // Detect whether or not there is a database client in another process with a wal-mode
1480            // transaction open on the database or not. Not implemented because it is a
1481            // unix-specific feature.
1482            ffi::SQLITE_FCNTL_EXTERNAL_READER => ffi::SQLITE_NOTFOUND,
1483
1484            // Unknown use-case. Ignored.
1485            ffi::SQLITE_FCNTL_CKSM_FILE => ffi::SQLITE_NOTFOUND,
1486
1487            _ => ffi::SQLITE_NOTFOUND,
1488        }
1489    }
1490
1491    /// Return the sector-size in bytes for a file.
1492    pub unsafe extern "C" fn sector_size<F>(_p_file: *mut ffi::sqlite3_file) -> c_int {
1493        log::trace!("sector_size");
1494
1495        1024
1496    }
1497
1498    /// Return the device characteristic flags supported by a file.
1499    pub unsafe extern "C" fn device_characteristics<V, F: DatabaseHandle>(
1500        p_file: *mut ffi::sqlite3_file,
1501    ) -> c_int {
1502        let state = match file_state::<V, F>(p_file) {
1503            Ok(f) => f,
1504            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1505        };
1506
1507        log::trace!("[{}] device_characteristics", state.id,);
1508
1509        // The following characteristics are needed to match the expected behavior of the tests.
1510
1511        // after reboot following a crash or power loss, the only bytes in a file that were written
1512        // at the application level might have changed and that adjacent bytes, even bytes within
1513        // the same sector are guaranteed to be unchanged
1514        if state.powersafe_overwrite {
1515            ffi::SQLITE_IOCAP_POWERSAFE_OVERWRITE
1516        } else {
1517            0
1518        }
1519    }
1520
1521    /// Create a shared memory file mapping.
1522    pub unsafe extern "C" fn shm_map<V, F: DatabaseHandle>(
1523        p_file: *mut ffi::sqlite3_file,
1524        region_ix: i32,
1525        region_size: i32,
1526        b_extend: i32,
1527        pp: *mut *mut c_void,
1528    ) -> i32 {
1529        let state = match file_state::<V, F>(p_file) {
1530            Ok(f) => f,
1531            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1532        };
1533        log::trace!(
1534            "[{}] shm_map pg={} sz={} extend={} ({})",
1535            state.id,
1536            region_ix,
1537            region_size,
1538            b_extend,
1539            state.db_name
1540        );
1541
1542        if !F::WalIndex::enabled() {
1543            return ffi::SQLITE_IOERR_SHMLOCK;
1544        }
1545
1546        if region_size != 32768 {
1547            return state.set_last_error(
1548                ffi::SQLITE_IOERR_SHMMAP,
1549                std::io::Error::new(
1550                    ErrorKind::Other,
1551                    format!(
1552                        "encountered region size other than 32kB; got {}",
1553                        region_size
1554                    ),
1555                ),
1556            );
1557        }
1558
1559        let (wal_index, readonly) = match state.wal_index.as_mut() {
1560            Some((wal_index, readonly)) => (wal_index, *readonly),
1561            None => {
1562                let (wal_index, readonly) = state.wal_index.get_or_insert(
1563                    match state
1564                        .file
1565                        .wal_index(false)
1566                        .map(|wal_index| (wal_index, false))
1567                        .or_else(|err| {
1568                            if err.kind() == ErrorKind::PermissionDenied {
1569                                // Try again as readonly
1570                                state
1571                                    .file
1572                                    .wal_index(true)
1573                                    .map(|wal_index| (wal_index, true))
1574                                    .map_err(|_| err)
1575                            } else {
1576                                Err(err)
1577                            }
1578                        }) {
1579                        Ok((wal_index, readonly)) => (wal_index, readonly),
1580                        Err(err) => {
1581                            return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1582                        }
1583                    },
1584                );
1585                (wal_index, *readonly)
1586            }
1587        };
1588
1589        let entry = state.wal_index_regions.entry(region_ix as u32);
1590        match entry {
1591            Entry::Occupied(mut entry) => {
1592                *pp = entry.get_mut().as_mut_ptr() as *mut c_void;
1593            }
1594            Entry::Vacant(entry) => {
1595                let mut m = match wal_index.map(region_ix as u32) {
1596                    Ok(m) => Box::pin(m),
1597                    Err(err) => {
1598                        return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1599                    }
1600                };
1601                *pp = m.as_mut_ptr() as *mut c_void;
1602                entry.insert(m);
1603            }
1604        }
1605
1606        if readonly {
1607            ffi::SQLITE_READONLY
1608        } else {
1609            ffi::SQLITE_OK
1610        }
1611    }
1612
1613    /// Perform locking on a shared-memory segment.
1614    pub unsafe extern "C" fn shm_lock<V, F: DatabaseHandle>(
1615        p_file: *mut ffi::sqlite3_file,
1616        offset: i32,
1617        n: i32,
1618        flags: i32,
1619    ) -> i32 {
1620        let state = match file_state::<V, F>(p_file) {
1621            Ok(f) => f,
1622            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1623        };
1624        let locking = flags & ffi::SQLITE_SHM_LOCK > 0;
1625        let exclusive = flags & ffi::SQLITE_SHM_EXCLUSIVE > 0;
1626        log::trace!(
1627            "[{}] shm_lock offset={} n={} lock={} exclusive={} (flags={}) ({})",
1628            state.id,
1629            offset,
1630            n,
1631            locking,
1632            exclusive,
1633            flags,
1634            state.db_name
1635        );
1636
1637        let range = offset as u8..(offset + n) as u8;
1638        let lock = match (locking, exclusive) {
1639            (true, true) => wip::WalIndexLock::Exclusive,
1640            (true, false) => wip::WalIndexLock::Shared,
1641            (false, _) => wip::WalIndexLock::None,
1642        };
1643
1644        let (wal_index, readonly) = match state.wal_index.as_mut() {
1645            Some((wal_index, readonly)) => (wal_index, *readonly),
1646            None => {
1647                return state.set_last_error(
1648                    ffi::SQLITE_IOERR_SHMLOCK,
1649                    std::io::Error::new(
1650                        ErrorKind::Other,
1651                        "trying to lock wal index, which isn't created yet",
1652                    ),
1653                )
1654            }
1655        };
1656
1657        if locking {
1658            let has_exclusive = state
1659                .wal_index_locks
1660                .iter()
1661                .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1662
1663            if !has_exclusive {
1664                log::trace!(
1665                    "[{}] does not have wal index write lock, pulling changes",
1666                    state.id
1667                );
1668                for (region, data) in &mut state.wal_index_regions {
1669                    if let Err(err) = wal_index.pull(*region as u32, data) {
1670                        return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1671                    }
1672                }
1673            }
1674        } else {
1675            let releases_any_exclusive = state.wal_index_locks.iter().any(|(region, lock)| {
1676                *lock == wip::WalIndexLock::Exclusive && range.contains(region)
1677            });
1678
1679            // push index changes when moving from any exclusive lock to no exclusive locks
1680            if releases_any_exclusive && !readonly {
1681                log::trace!(
1682                    "[{}] releasing an exclusive lock, pushing wal index changes",
1683                    state.id,
1684                );
1685                for (region, data) in &mut state.wal_index_regions {
1686                    if let Err(err) = wal_index.push(*region as u32, data) {
1687                        return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1688                    }
1689                }
1690            }
1691        }
1692
1693        match wal_index.lock(range.clone(), lock) {
1694            Ok(true) => {
1695                for region in range {
1696                    state.wal_index_locks.insert(region, lock);
1697                }
1698                ffi::SQLITE_OK
1699            }
1700            Ok(false) => ffi::SQLITE_BUSY,
1701            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err),
1702        }
1703    }
1704
1705    /// Memory barrier operation on shared memory.
1706    pub unsafe extern "C" fn shm_barrier<V, F: DatabaseHandle>(p_file: *mut ffi::sqlite3_file) {
1707        let state = match file_state::<V, F>(p_file) {
1708            Ok(f) => f,
1709            Err(_) => return,
1710        };
1711        log::trace!("[{}] shm_barrier ({})", state.id, state.db_name);
1712
1713        let (wal_index, readonly) = if let Some((wal_index, readonly)) = state.wal_index.as_mut() {
1714            (wal_index, *readonly)
1715        } else {
1716            return;
1717        };
1718
1719        if state.has_exclusive_lock && !readonly {
1720            log::trace!(
1721                "[{}] has exclusive db lock, pushing wal index changes",
1722                state.id,
1723            );
1724            for (region, data) in &mut state.wal_index_regions {
1725                if let Err(err) = wal_index.push(*region as u32, data) {
1726                    log::error!("[{}] pushing wal index changes failed: {}", state.id, err)
1727                }
1728            }
1729
1730            return;
1731        }
1732
1733        let has_exclusive = state
1734            .wal_index_locks
1735            .iter()
1736            .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1737
1738        if !has_exclusive {
1739            log::trace!(
1740                "[{}] does not have wal index write lock, pulling changes",
1741                state.id
1742            );
1743            for (region, data) in &mut state.wal_index_regions {
1744                if let Err(err) = wal_index.pull(*region as u32, data) {
1745                    log::error!("[{}] pulling wal index changes failed: {}", state.id, err)
1746                }
1747            }
1748        }
1749    }
1750
1751    /// Unmap a shared memory segment.
1752    pub unsafe extern "C" fn shm_unmap<V, F: DatabaseHandle>(
1753        p_file: *mut ffi::sqlite3_file,
1754        delete_flags: i32,
1755    ) -> i32 {
1756        let state = match file_state::<V, F>(p_file) {
1757            Ok(f) => f,
1758            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1759        };
1760        log::trace!(
1761            "[{}] shm_unmap delete={} ({})",
1762            state.id,
1763            delete_flags == 1,
1764            state.db_name
1765        );
1766
1767        state.wal_index_regions.clear();
1768        state.wal_index_locks.clear();
1769
1770        if delete_flags == 1 {
1771            if let Some((wal_index, readonly)) = state.wal_index.take() {
1772                if !readonly {
1773                    if let Err(err) = wal_index.delete() {
1774                        return state.set_last_error(ffi::SQLITE_ERROR, err);
1775                    }
1776                }
1777            }
1778        }
1779
1780        ffi::SQLITE_OK
1781    }
1782}
1783
1784// #[cfg(feature = "sqlite_test")]
1785// struct Benign;
1786
1787// #[cfg(feature = "sqlite_test")]
1788// #[inline]
1789// unsafe fn simulate_io_error_benign() -> Benign {
1790//     ffi::sqlite3_set_io_error_benign(1);
1791//     Benign
1792// }
1793
1794// #[cfg(feature = "sqlite_test")]
1795// impl Drop for Benign {
1796//     fn drop(&mut self) {
1797//         unsafe { ffi::sqlite3_set_io_error_benign(0) }
1798//     }
1799// }
1800
1801// Note: When adding additional simulate_io_error() calls, retest:
1802// - malloc.test
1803// - ioerr2.test
1804// - backup_ioerr.test
1805#[cfg(feature = "sqlite_test")]
1806#[inline]
1807unsafe fn simulate_io_error() -> bool {
1808    if (ffi::sqlite3_get_io_error_persist() != 0 && ffi::sqlite3_get_io_error_hit() != 0)
1809        || ffi::sqlite3_dec_io_error_pending() == 1
1810    {
1811        ffi::sqlite3_inc_io_error_hit();
1812        if ffi::sqlite3_get_io_error_benign() == 0 {
1813            ffi::sqlite3_inc_io_error_hardhit();
1814        }
1815
1816        return true;
1817    }
1818
1819    false
1820}
1821
1822#[cfg(feature = "sqlite_test")]
1823#[inline]
1824unsafe fn simulate_diskfull_error() -> bool {
1825    if ffi::sqlite3_get_diskfull_pending() != 0 {
1826        if ffi::sqlite3_get_diskfull_pending() == 1 {
1827            if ffi::sqlite3_get_io_error_benign() == 0 {
1828                ffi::sqlite3_inc_io_error_hardhit();
1829            }
1830            ffi::sqlite3_set_diskfull();
1831            ffi::sqlite3_set_io_error_hit(1);
1832            return true;
1833        } else {
1834            ffi::sqlite3_dec_diskfull_pending();
1835        }
1836    }
1837
1838    false
1839}
1840
1841impl<V> State<V> {
1842    fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1843        // log::error!("{} ({})", err, no);
1844        *(self.last_error.lock().unwrap()) = Some((no, err));
1845        no
1846    }
1847}
1848
1849impl<V, F: DatabaseHandle> FileExt<V, F> {
1850    fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1851        // log::error!("{} ({})", err, no);
1852        *(self.last_error.lock().unwrap()) = Some((no, err));
1853        self.last_errno = no;
1854        no
1855    }
1856}
1857
1858fn null_ptr_error() -> std::io::Error {
1859    std::io::Error::new(ErrorKind::Other, "received null pointer")
1860}
1861
1862unsafe fn vfs_state<'a, V>(ptr: *mut ffi::sqlite3_vfs) -> Result<&'a mut State<V>, std::io::Error> {
1863    let vfs: &mut ffi::sqlite3_vfs = ptr.as_mut().ok_or_else(null_ptr_error)?;
1864    let state = (vfs.pAppData as *mut State<V>)
1865        .as_mut()
1866        .ok_or_else(null_ptr_error)?;
1867    Ok(state)
1868}
1869
1870unsafe fn file_state<'a, V, F: DatabaseHandle>(
1871    ptr: *mut ffi::sqlite3_file,
1872) -> Result<&'a mut FileExt<V, F>, std::io::Error> {
1873    let f = (ptr as *mut FileState<V, F>)
1874        .as_mut()
1875        .ok_or_else(null_ptr_error)?;
1876    let ext = f.ext.assume_init_mut();
1877    Ok(ext)
1878}
1879
1880impl OpenOptions {
1881    fn from_flags(flags: i32) -> Option<Self> {
1882        Some(OpenOptions {
1883            kind: OpenKind::from_flags(flags)?,
1884            access: OpenAccess::from_flags(flags)?,
1885            delete_on_close: flags & ffi::SQLITE_OPEN_DELETEONCLOSE > 0,
1886        })
1887    }
1888
1889    fn to_flags(&self) -> i32 {
1890        self.kind.to_flags()
1891            | self.access.to_flags()
1892            | if self.delete_on_close {
1893                ffi::SQLITE_OPEN_DELETEONCLOSE
1894            } else {
1895                0
1896            }
1897    }
1898}
1899
1900impl OpenKind {
1901    fn from_flags(flags: i32) -> Option<Self> {
1902        match flags {
1903            flags if flags & ffi::SQLITE_OPEN_MAIN_DB > 0 => Some(Self::MainDb),
1904            flags if flags & ffi::SQLITE_OPEN_MAIN_JOURNAL > 0 => Some(Self::MainJournal),
1905            flags if flags & ffi::SQLITE_OPEN_TEMP_DB > 0 => Some(Self::TempDb),
1906            flags if flags & ffi::SQLITE_OPEN_TEMP_JOURNAL > 0 => Some(Self::TempJournal),
1907            flags if flags & ffi::SQLITE_OPEN_TRANSIENT_DB > 0 => Some(Self::TransientDb),
1908            flags if flags & ffi::SQLITE_OPEN_SUBJOURNAL > 0 => Some(Self::SubJournal),
1909            flags if flags & ffi::SQLITE_OPEN_SUPER_JOURNAL > 0 => Some(Self::SuperJournal),
1910            flags if flags & ffi::SQLITE_OPEN_WAL > 0 => Some(Self::Wal),
1911            _ => None,
1912        }
1913    }
1914
1915    fn to_flags(self) -> i32 {
1916        match self {
1917            OpenKind::MainDb => ffi::SQLITE_OPEN_MAIN_DB,
1918            OpenKind::MainJournal => ffi::SQLITE_OPEN_MAIN_JOURNAL,
1919            OpenKind::TempDb => ffi::SQLITE_OPEN_TEMP_DB,
1920            OpenKind::TempJournal => ffi::SQLITE_OPEN_TEMP_JOURNAL,
1921            OpenKind::TransientDb => ffi::SQLITE_OPEN_TRANSIENT_DB,
1922            OpenKind::SubJournal => ffi::SQLITE_OPEN_SUBJOURNAL,
1923            OpenKind::SuperJournal => ffi::SQLITE_OPEN_SUPER_JOURNAL,
1924            OpenKind::Wal => ffi::SQLITE_OPEN_WAL,
1925        }
1926    }
1927}
1928
1929impl OpenAccess {
1930    fn from_flags(flags: i32) -> Option<Self> {
1931        match flags {
1932            flags
1933                if (flags & ffi::SQLITE_OPEN_CREATE > 0)
1934                    && (flags & ffi::SQLITE_OPEN_EXCLUSIVE > 0) =>
1935            {
1936                Some(Self::CreateNew)
1937            }
1938            flags if flags & ffi::SQLITE_OPEN_CREATE > 0 => Some(Self::Create),
1939            flags if flags & ffi::SQLITE_OPEN_READWRITE > 0 => Some(Self::Write),
1940            flags if flags & ffi::SQLITE_OPEN_READONLY > 0 => Some(Self::Read),
1941            _ => None,
1942        }
1943    }
1944
1945    fn to_flags(self) -> i32 {
1946        match self {
1947            OpenAccess::Read => ffi::SQLITE_OPEN_READONLY,
1948            OpenAccess::Write => ffi::SQLITE_OPEN_READWRITE,
1949            OpenAccess::Create => ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE,
1950            OpenAccess::CreateNew => {
1951                ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_EXCLUSIVE
1952            }
1953        }
1954    }
1955}
1956
1957impl LockKind {
1958    fn from_i32(lock: i32) -> Option<Self> {
1959        Some(match lock {
1960            ffi::SQLITE_LOCK_NONE => Self::None,
1961            ffi::SQLITE_LOCK_SHARED => Self::Shared,
1962            ffi::SQLITE_LOCK_RESERVED => Self::Reserved,
1963            ffi::SQLITE_LOCK_PENDING => Self::Pending,
1964            ffi::SQLITE_LOCK_EXCLUSIVE => Self::Exclusive,
1965            _ => return None,
1966        })
1967    }
1968
1969    fn to_i32(self) -> i32 {
1970        match self {
1971            Self::None => ffi::SQLITE_LOCK_NONE,
1972            Self::Shared => ffi::SQLITE_LOCK_SHARED,
1973            Self::Reserved => ffi::SQLITE_LOCK_RESERVED,
1974            Self::Pending => ffi::SQLITE_LOCK_PENDING,
1975            Self::Exclusive => ffi::SQLITE_LOCK_EXCLUSIVE,
1976        }
1977    }
1978}
1979
1980impl PartialOrd for LockKind {
1981    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1982        self.to_i32().partial_cmp(&other.to_i32())
1983    }
1984}
1985
1986impl Default for LockKind {
1987    fn default() -> Self {
1988        Self::None
1989    }
1990}
1991
1992#[derive(Default)]
1993pub struct WalDisabled;
1994
1995impl wip::WalIndex for WalDisabled {
1996    fn enabled() -> bool {
1997        false
1998    }
1999
2000    fn map(&mut self, _region: u32) -> Result<[u8; 32768], std::io::Error> {
2001        Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2002    }
2003
2004    fn lock(
2005        &mut self,
2006        _locks: Range<u8>,
2007        _lock: wip::WalIndexLock,
2008    ) -> Result<bool, std::io::Error> {
2009        Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2010    }
2011
2012    fn delete(self) -> Result<(), std::io::Error> {
2013        Ok(())
2014    }
2015}
2016
2017#[derive(Debug)]
2018pub enum RegisterError {
2019    Nul(std::ffi::NulError),
2020    Register(i32),
2021}
2022
2023impl std::error::Error for RegisterError {
2024    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2025        match self {
2026            Self::Nul(err) => Some(err),
2027            Self::Register(_) => None,
2028        }
2029    }
2030}
2031
2032impl std::fmt::Display for RegisterError {
2033    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2034        match self {
2035            Self::Nul(_) => f.write_str("interior nul byte in name found"),
2036            Self::Register(code) => {
2037                write!(f, "registering sqlite vfs failed with error code: {}", code)
2038            }
2039        }
2040    }
2041}
2042
2043impl From<std::ffi::NulError> for RegisterError {
2044    fn from(err: std::ffi::NulError) -> Self {
2045        Self::Nul(err)
2046    }
2047}
2048
2049#[cfg(test)]
2050mod tests {
2051    use super::*;
2052
2053    #[test]
2054    fn test_lock_order() {
2055        assert!(LockKind::None < LockKind::Shared);
2056        assert!(LockKind::Shared < LockKind::Reserved);
2057        assert!(LockKind::Reserved < LockKind::Pending);
2058        assert!(LockKind::Pending < LockKind::Exclusive);
2059    }
2060}