sqlite_vfs/
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 = time::OffsetDateTime::now_utc().unix_timestamp() + UNIX_EPOCH;
802        #[cfg(feature = "sqlite_test")]
803        let now = if ffi::sqlite3_get_current_time() > 0 {
804            ffi::sqlite3_get_current_time() as i64 * 1000 + UNIX_EPOCH
805        } else {
806            now
807        };
808
809        *p = now;
810        ffi::SQLITE_OK
811    }
812
813    #[cfg(feature = "syscall")]
814    pub unsafe extern "C" fn set_system_call<V>(
815        p_vfs: *mut ffi::sqlite3_vfs,
816        z_name: *const ::std::os::raw::c_char,
817        p_new_func: ffi::sqlite3_syscall_ptr,
818    ) -> ::std::os::raw::c_int {
819        let state = match vfs_state::<V>(p_vfs) {
820            Ok(state) => state,
821            Err(_) => return ffi::SQLITE_ERROR,
822        };
823
824        if let Some(set_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xSetSystemCall) {
825            return set_system_call(state.parent_vfs, z_name, p_new_func);
826        }
827
828        ffi::SQLITE_ERROR
829    }
830
831    #[cfg(feature = "syscall")]
832    pub unsafe extern "C" fn get_system_call<V>(
833        p_vfs: *mut ffi::sqlite3_vfs,
834        z_name: *const ::std::os::raw::c_char,
835    ) -> ffi::sqlite3_syscall_ptr {
836        let state = match vfs_state::<V>(p_vfs) {
837            Ok(state) => state,
838            Err(_) => return None,
839        };
840
841        if let Some(get_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xGetSystemCall) {
842            return get_system_call(state.parent_vfs, z_name);
843        }
844
845        None
846    }
847
848    #[cfg(feature = "syscall")]
849    pub unsafe extern "C" fn next_system_call<V>(
850        p_vfs: *mut ffi::sqlite3_vfs,
851        z_name: *const ::std::os::raw::c_char,
852    ) -> *const ::std::os::raw::c_char {
853        let state = match vfs_state::<V>(p_vfs) {
854            Ok(state) => state,
855            Err(_) => return std::ptr::null(),
856        };
857
858        if let Some(next_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xNextSystemCall) {
859            return next_system_call(state.parent_vfs, z_name);
860        }
861
862        std::ptr::null()
863    }
864
865    pub unsafe extern "C" fn get_last_error<V>(
866        p_vfs: *mut ffi::sqlite3_vfs,
867        n_byte: c_int,
868        z_err_msg: *mut c_char,
869    ) -> c_int {
870        let state = match vfs_state::<V>(p_vfs) {
871            Ok(state) => state,
872            Err(_) => return ffi::SQLITE_ERROR,
873        };
874        if let Some((eno, err)) = state.last_error.lock().unwrap().as_ref() {
875            let msg = match CString::new(err.to_string()) {
876                Ok(msg) => msg,
877                Err(_) => return ffi::SQLITE_ERROR,
878            };
879
880            let msg = msg.to_bytes_with_nul();
881            if msg.len() > n_byte as usize {
882                return ffi::SQLITE_ERROR;
883            }
884            let out = slice::from_raw_parts_mut(z_err_msg as *mut u8, msg.len());
885            out.copy_from_slice(msg);
886
887            return *eno;
888        }
889        ffi::SQLITE_OK
890    }
891}
892
893mod io {
894    use std::collections::hash_map::Entry;
895    use std::mem;
896
897    use super::*;
898    use wip::WalIndex;
899
900    /// Close a file.
901    pub unsafe extern "C" fn close<V: Vfs, F: DatabaseHandle>(
902        p_file: *mut ffi::sqlite3_file,
903    ) -> c_int {
904        if let Some(f) = (p_file as *mut FileState<V, F>).as_mut() {
905            let ext = f.ext.assume_init_mut();
906            if ext.delete_on_close {
907                if let Err(err) = ext.vfs.delete(&ext.db_name) {
908                    return ext.set_last_error(ffi::SQLITE_DELETE, err);
909                }
910            }
911
912            let ext = mem::replace(&mut f.ext, MaybeUninit::uninit());
913            let ext = ext.assume_init(); // extract the value to drop it
914            log::trace!("[{}] close ({})", ext.id, ext.db_name);
915        }
916
917        #[cfg(feature = "sqlite_test")]
918        ffi::sqlite3_dec_open_file_count();
919
920        ffi::SQLITE_OK
921    }
922
923    /// Read data from a file.
924    pub unsafe extern "C" fn read<V, F: DatabaseHandle>(
925        p_file: *mut ffi::sqlite3_file,
926        z_buf: *mut c_void,
927        i_amt: c_int,
928        i_ofst: ffi::sqlite3_int64,
929    ) -> c_int {
930        let state = match file_state::<V, F>(p_file) {
931            Ok(f) => f,
932            Err(_) => return ffi::SQLITE_IOERR_CLOSE,
933        };
934        log::trace!(
935            "[{}] read offset={} len={} ({})",
936            state.id,
937            i_ofst,
938            i_amt,
939            state.db_name
940        );
941
942        let out = slice::from_raw_parts_mut(z_buf as *mut u8, i_amt as usize);
943        if let Err(err) = state.file.read_exact_at(out, i_ofst as u64) {
944            let kind = err.kind();
945            if kind == ErrorKind::UnexpectedEof {
946                return ffi::SQLITE_IOERR_SHORT_READ;
947            } else {
948                return state.set_last_error(ffi::SQLITE_IOERR_READ, err);
949            }
950        }
951
952        ffi::SQLITE_OK
953    }
954
955    /// Write data to a file.
956    pub unsafe extern "C" fn write<V, F: DatabaseHandle>(
957        p_file: *mut ffi::sqlite3_file,
958        z: *const c_void,
959        i_amt: c_int,
960        i_ofst: ffi::sqlite3_int64,
961    ) -> c_int {
962        let state = match file_state::<V, F>(p_file) {
963            Ok(f) => f,
964            Err(_) => return ffi::SQLITE_IOERR_WRITE,
965        };
966        log::trace!(
967            "[{}] write offset={} len={} ({})",
968            state.id,
969            i_ofst,
970            i_amt,
971            state.db_name
972        );
973
974        let data = slice::from_raw_parts(z as *mut u8, i_amt as usize);
975        let result = state.file.write_all_at(data, i_ofst as u64);
976
977        #[cfg(feature = "sqlite_test")]
978        let result = if simulate_io_error() {
979            Err(ErrorKind::Other.into())
980        } else {
981            result
982        };
983
984        #[cfg(feature = "sqlite_test")]
985        let result = if simulate_diskfull_error() {
986            Err(ErrorKind::WriteZero.into())
987        } else {
988            result
989        };
990
991        match result {
992            Ok(_) => {}
993            Err(err) if err.kind() == ErrorKind::WriteZero => {
994                return ffi::SQLITE_FULL;
995            }
996            Err(err) => return state.set_last_error(ffi::SQLITE_IOERR_WRITE, err),
997        }
998
999        ffi::SQLITE_OK
1000    }
1001
1002    /// Truncate a file.
1003    pub unsafe extern "C" fn truncate<V, F: DatabaseHandle>(
1004        p_file: *mut ffi::sqlite3_file,
1005        size: ffi::sqlite3_int64,
1006    ) -> c_int {
1007        let state = match file_state::<V, F>(p_file) {
1008            Ok(f) => f,
1009            Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1010        };
1011
1012        let size: u64 = if let Some(chunk_size) = state.chunk_size {
1013            (((size as usize + chunk_size - 1) / chunk_size) * chunk_size) as u64
1014        } else {
1015            size as u64
1016        };
1017
1018        log::trace!("[{}] truncate size={} ({})", state.id, size, state.db_name);
1019
1020        // #[cfg(feature = "sqlite_test")]
1021        // if simulate_io_error() {
1022        //     return ffi::SQLITE_IOERR_TRUNCATE;
1023        // }
1024
1025        if let Err(err) = state.file.set_len(size) {
1026            return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1027        }
1028
1029        ffi::SQLITE_OK
1030    }
1031
1032    /// Persist changes to a file.
1033    pub unsafe extern "C" fn sync<V, F: DatabaseHandle>(
1034        p_file: *mut ffi::sqlite3_file,
1035        flags: c_int,
1036    ) -> c_int {
1037        let state = match file_state::<V, F>(p_file) {
1038            Ok(f) => f,
1039            Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1040        };
1041        log::trace!("[{}] sync ({})", state.id, state.db_name);
1042
1043        #[cfg(feature = "sqlite_test")]
1044        {
1045            let is_full_sync = flags & 0x0F == ffi::SQLITE_SYNC_FULL;
1046            if is_full_sync {
1047                ffi::sqlite3_inc_fullsync_count();
1048            }
1049            ffi::sqlite3_inc_sync_count();
1050        }
1051
1052        if let Err(err) = state.file.sync(flags & ffi::SQLITE_SYNC_DATAONLY > 0) {
1053            return state.set_last_error(ffi::SQLITE_IOERR_FSYNC, err);
1054        }
1055
1056        // #[cfg(feature = "sqlite_test")]
1057        // if simulate_io_error() {
1058        //     return ffi::SQLITE_ERROR;
1059        // }
1060
1061        ffi::SQLITE_OK
1062    }
1063
1064    /// Return the current file-size of a file.
1065    pub unsafe extern "C" fn file_size<V, F: DatabaseHandle>(
1066        p_file: *mut ffi::sqlite3_file,
1067        p_size: *mut ffi::sqlite3_int64,
1068    ) -> c_int {
1069        let state = match file_state::<V, F>(p_file) {
1070            Ok(f) => f,
1071            Err(_) => return ffi::SQLITE_IOERR_FSTAT,
1072        };
1073        log::trace!("[{}] file_size ({})", state.id, state.db_name);
1074
1075        if let Err(err) = state.file.size().and_then(|n| {
1076            let p_size: &mut ffi::sqlite3_int64 = p_size.as_mut().ok_or_else(null_ptr_error)?;
1077            *p_size = n as ffi::sqlite3_int64;
1078            Ok(())
1079        }) {
1080            return state.set_last_error(ffi::SQLITE_IOERR_FSTAT, err);
1081        }
1082
1083        // #[cfg(feature = "sqlite_test")]
1084        // if simulate_io_error() {
1085        //     return ffi::SQLITE_ERROR;
1086        // }
1087
1088        ffi::SQLITE_OK
1089    }
1090
1091    /// Lock a file.
1092    pub unsafe extern "C" fn lock<V, F: DatabaseHandle>(
1093        p_file: *mut ffi::sqlite3_file,
1094        e_lock: c_int,
1095    ) -> c_int {
1096        let state = match file_state::<V, F>(p_file) {
1097            Ok(f) => f,
1098            Err(_) => return ffi::SQLITE_IOERR_LOCK,
1099        };
1100        log::trace!("[{}] lock ({})", state.id, state.db_name);
1101
1102        let lock = match LockKind::from_i32(e_lock) {
1103            Some(lock) => lock,
1104            None => return ffi::SQLITE_IOERR_LOCK,
1105        };
1106        match state.file.lock(lock) {
1107            Ok(true) => {
1108                state.has_exclusive_lock = lock == LockKind::Exclusive;
1109                log::trace!("[{}] lock={:?} ({})", state.id, lock, state.db_name);
1110
1111                // If just acquired a exclusive database lock while not having any exclusive lock
1112                // on the wal index, make sure the wal index is up to date.
1113                if state.has_exclusive_lock {
1114                    let has_exclusive_wal_index = state
1115                        .wal_index_locks
1116                        .iter()
1117                        .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1118
1119                    if !has_exclusive_wal_index {
1120                        log::trace!(
1121                            "[{}] acquired exclusive db lock, pulling wal index changes",
1122                            state.id,
1123                        );
1124
1125                        if let Some((wal_index, _)) = state.wal_index.as_mut() {
1126                            for (region, data) in &mut state.wal_index_regions {
1127                                if let Err(err) = wal_index.pull(*region as u32, data) {
1128                                    log::error!(
1129                                        "[{}] pulling wal index changes failed: {}",
1130                                        state.id,
1131                                        err
1132                                    )
1133                                }
1134                            }
1135                        }
1136                    }
1137                }
1138
1139                ffi::SQLITE_OK
1140            }
1141            Ok(false) => {
1142                log::trace!(
1143                    "[{}] busy (denied {:?}) ({})",
1144                    state.id,
1145                    lock,
1146                    state.db_name
1147                );
1148                ffi::SQLITE_BUSY
1149            }
1150            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_LOCK, err),
1151        }
1152    }
1153
1154    /// Unlock a file.
1155    pub unsafe extern "C" fn unlock<V, F: DatabaseHandle>(
1156        p_file: *mut ffi::sqlite3_file,
1157        e_lock: c_int,
1158    ) -> c_int {
1159        let state = match file_state::<V, F>(p_file) {
1160            Ok(f) => f,
1161            Err(_) => return ffi::SQLITE_IOERR_UNLOCK,
1162        };
1163        log::trace!("[{}] unlock ({})", state.id, state.db_name);
1164
1165        let lock = match LockKind::from_i32(e_lock) {
1166            Some(lock) => lock,
1167            None => return ffi::SQLITE_IOERR_UNLOCK,
1168        };
1169        match state.file.unlock(lock) {
1170            Ok(true) => {
1171                state.has_exclusive_lock = lock == LockKind::Exclusive;
1172                log::trace!("[{}] unlock={:?} ({})", state.id, lock, state.db_name);
1173                ffi::SQLITE_OK
1174            }
1175            Ok(false) => ffi::SQLITE_BUSY,
1176            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err),
1177        }
1178    }
1179
1180    /// Check if another file-handle holds a [LockKind::Reserved] lock on a file.
1181    pub unsafe extern "C" fn check_reserved_lock<V, F: DatabaseHandle>(
1182        p_file: *mut ffi::sqlite3_file,
1183        p_res_out: *mut c_int,
1184    ) -> c_int {
1185        let state = match file_state::<V, F>(p_file) {
1186            Ok(f) => f,
1187            Err(_) => return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK,
1188        };
1189        log::trace!("[{}] check_reserved_lock ({})", state.id, state.db_name);
1190
1191        // #[cfg(feature = "sqlite_test")]
1192        // if simulate_io_error() {
1193        //     return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK;
1194        // }
1195
1196        if let Err(err) = state.file.reserved().and_then(|is_reserved| {
1197            let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
1198            *p_res_out = is_reserved as c_int;
1199            Ok(())
1200        }) {
1201            return state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err);
1202        }
1203
1204        ffi::SQLITE_OK
1205    }
1206
1207    /// File control method. For custom operations on a mem-file.
1208    pub unsafe extern "C" fn file_control<V: Vfs, F: DatabaseHandle>(
1209        p_file: *mut ffi::sqlite3_file,
1210        op: c_int,
1211        p_arg: *mut c_void,
1212    ) -> c_int {
1213        let state = match file_state::<V, F>(p_file) {
1214            Ok(f) => f,
1215            Err(_) => return ffi::SQLITE_NOTFOUND,
1216        };
1217        log::trace!("[{}] file_control op={} ({})", state.id, op, state.db_name);
1218
1219        // Docs: https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
1220        match op {
1221            // The following op codes are alreay handled by sqlite before, so no need to handle them
1222            // in a custom VFS.
1223            ffi::SQLITE_FCNTL_FILE_POINTER
1224            | ffi::SQLITE_FCNTL_VFS_POINTER
1225            | ffi::SQLITE_FCNTL_JOURNAL_POINTER
1226            | ffi::SQLITE_FCNTL_DATA_VERSION
1227            | ffi::SQLITE_FCNTL_RESERVE_BYTES => ffi::SQLITE_NOTFOUND,
1228
1229            // The following op codes are no longer used and thus ignored.
1230            ffi::SQLITE_FCNTL_SYNC_OMITTED => ffi::SQLITE_NOTFOUND,
1231
1232            // Used for debugging. Write current state of the lock into (int)pArg.
1233            ffi::SQLITE_FCNTL_LOCKSTATE => match state.file.current_lock() {
1234                Ok(lock) => {
1235                    if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1236                        *p_arg = lock as i32;
1237                    }
1238                    ffi::SQLITE_OK
1239                }
1240                Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1241            },
1242
1243            // Relevant for proxy-type locking. Not implemented.
1244            ffi::SQLITE_FCNTL_GET_LOCKPROXYFILE | ffi::SQLITE_FCNTL_SET_LOCKPROXYFILE => {
1245                ffi::SQLITE_NOTFOUND
1246            }
1247
1248            // Write last error number into (int)pArg.
1249            ffi::SQLITE_FCNTL_LAST_ERRNO => {
1250                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1251                    *p_arg = state.last_errno;
1252                }
1253                ffi::SQLITE_OK
1254            }
1255
1256            // Give the VFS layer a hint of how large the database file will grow to be during the
1257            // current transaction.
1258            ffi::SQLITE_FCNTL_SIZE_HINT => {
1259                let size_hint = match (p_arg as *mut i64)
1260                    .as_ref()
1261                    .cloned()
1262                    .and_then(|s| u64::try_from(s).ok())
1263                {
1264                    Some(chunk_size) => chunk_size,
1265                    None => {
1266                        return state.set_last_error(
1267                            ffi::SQLITE_NOTFOUND,
1268                            std::io::Error::new(ErrorKind::Other, "expect size hint arg"),
1269                        );
1270                    }
1271                };
1272
1273                // #[cfg(feature = "sqlite_test")]
1274                // let _benign = simulate_io_error_benign();
1275
1276                let current = match state.file.size() {
1277                    Ok(size) => size,
1278                    Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
1279                };
1280
1281                if current > size_hint {
1282                    return ffi::SQLITE_OK;
1283                }
1284
1285                if let Some(chunk_size) = state.chunk_size {
1286                    let chunk_size = chunk_size as u64;
1287                    let size = ((size_hint + chunk_size - 1) / chunk_size) * chunk_size;
1288                    if let Err(err) = state.file.set_len(size) {
1289                        return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1290                    }
1291                } else if let Err(err) = state.file.set_len(size_hint) {
1292                    return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1293                }
1294
1295                // #[cfg(feature = "sqlite_test")]
1296                // if simulate_io_error() {
1297                //     return ffi::SQLITE_IOERR_TRUNCATE;
1298                // }
1299
1300                ffi::SQLITE_OK
1301            }
1302
1303            // Request that the VFS extends and truncates the database file in chunks of a size
1304            // specified by the user. Return an error as this is not forwarded to the [Vfs] trait
1305            // right now.
1306            ffi::SQLITE_FCNTL_CHUNK_SIZE => {
1307                let chunk_size = match (p_arg as *mut i32)
1308                    .as_ref()
1309                    .cloned()
1310                    .and_then(|s| usize::try_from(s).ok())
1311                {
1312                    Some(chunk_size) => chunk_size,
1313                    None => {
1314                        return state.set_last_error(
1315                            ffi::SQLITE_NOTFOUND,
1316                            std::io::Error::new(ErrorKind::Other, "expect chunk_size arg"),
1317                        );
1318                    }
1319                };
1320
1321                if let Err(err) = state.file.set_chunk_size(chunk_size) {
1322                    return state.set_last_error(ffi::SQLITE_ERROR, err);
1323                }
1324
1325                state.chunk_size = Some(chunk_size);
1326
1327                ffi::SQLITE_OK
1328            }
1329
1330            // Configure automatic retry counts and intervals for certain disk I/O operations for
1331            // the windows VFS in order to provide robustness in the presence of anti-virus
1332            // programs. Not implemented.
1333            ffi::SQLITE_FCNTL_WIN32_AV_RETRY => ffi::SQLITE_NOTFOUND,
1334
1335            // Enable or disable the persistent WAL setting.
1336            ffi::SQLITE_FCNTL_PERSIST_WAL => {
1337                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1338                    if *p_arg < 0 {
1339                        // query current setting
1340                        *p_arg = state.persist_wal as i32;
1341                    } else {
1342                        state.persist_wal = *p_arg == 1;
1343                    }
1344                };
1345
1346                ffi::SQLITE_OK
1347            }
1348
1349            // Indicate that, unless it is rolled back for some reason, the entire database file
1350            // will be overwritten by the current transaction. Not implemented.
1351            ffi::SQLITE_FCNTL_OVERWRITE => ffi::SQLITE_NOTFOUND,
1352
1353            // Used to obtain the names of all VFSes in the VFS stack.
1354            ffi::SQLITE_FCNTL_VFSNAME => {
1355                if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1356                    let name = ManuallyDrop::new(state.vfs_name.clone());
1357                    *p_arg = name.as_ptr();
1358                };
1359
1360                ffi::SQLITE_OK
1361            }
1362
1363            // Set or query the persistent "powersafe-overwrite" or "PSOW" setting.
1364            ffi::SQLITE_FCNTL_POWERSAFE_OVERWRITE => {
1365                if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1366                    if *p_arg < 0 {
1367                        // query current setting
1368                        *p_arg = state.powersafe_overwrite as i32;
1369                    } else {
1370                        state.powersafe_overwrite = *p_arg == 1;
1371                    }
1372                };
1373
1374                ffi::SQLITE_OK
1375            }
1376
1377            // Optionally intercept PRAGMA statements. Always fall back to normal pragma processing.
1378            ffi::SQLITE_FCNTL_PRAGMA => ffi::SQLITE_NOTFOUND,
1379
1380            // May be invoked by SQLite on the database file handle shortly after it is opened in
1381            // order to provide a custom VFS with access to the connection's busy-handler callback.
1382            // Not implemented.
1383            ffi::SQLITE_FCNTL_BUSYHANDLER => ffi::SQLITE_NOTFOUND,
1384
1385            // Generate a temporary filename. Not implemented.
1386            ffi::SQLITE_FCNTL_TEMPFILENAME => {
1387                if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1388                    let name = state.vfs.temporary_name();
1389                    // unwrap() is fine as os strings are an arbitrary sequences of non-zero bytes
1390                    let name = CString::new(name.as_bytes()).unwrap();
1391                    let name = ManuallyDrop::new(name);
1392                    *p_arg = name.as_ptr();
1393                };
1394
1395                ffi::SQLITE_OK
1396            }
1397
1398            // Query or set the maximum number of bytes that will be used for memory-mapped I/O.
1399            // Not implemented.
1400            ffi::SQLITE_FCNTL_MMAP_SIZE => ffi::SQLITE_NOTFOUND,
1401
1402            // Advisory information to the VFS about what the higher layers of the SQLite stack are
1403            // doing.
1404            ffi::SQLITE_FCNTL_TRACE => {
1405                let trace = CStr::from_ptr(p_arg as *const c_char);
1406                log::trace!("{}", trace.to_string_lossy());
1407                ffi::SQLITE_OK
1408            }
1409
1410            // Check whether or not the file has been renamed, moved, or deleted since it was first
1411            // opened.
1412            ffi::SQLITE_FCNTL_HAS_MOVED => match state.file.moved() {
1413                Ok(moved) => {
1414                    if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1415                        *p_arg = moved as i32;
1416                    }
1417                    ffi::SQLITE_OK
1418                }
1419                Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1420            },
1421
1422            // Sent to the VFS immediately before the xSync method is invoked on a database file
1423            // descriptor. Silently ignored.
1424            ffi::SQLITE_FCNTL_SYNC => ffi::SQLITE_OK,
1425
1426            // Sent to the VFS after a transaction has been committed immediately but before the
1427            // database is unlocked. Silently ignored.
1428            ffi::SQLITE_FCNTL_COMMIT_PHASETWO => ffi::SQLITE_OK,
1429
1430            // Used for debugging. Swap the file handle with the one pointed to by the pArg
1431            // argument. This capability is used during testing and only needs to be supported when
1432            // SQLITE_TEST is defined. Not implemented.
1433            ffi::SQLITE_FCNTL_WIN32_SET_HANDLE => ffi::SQLITE_NOTFOUND,
1434
1435            // Signal to the VFS layer that it might be advantageous to block on the next WAL lock
1436            // if the lock is not immediately available. The WAL subsystem issues this signal during
1437            // rare circumstances in order to fix a problem with priority inversion.
1438            // Not implemented.
1439            ffi::SQLITE_FCNTL_WAL_BLOCK => ffi::SQLITE_NOTFOUND,
1440
1441            // Implemented by zipvfs only.
1442            ffi::SQLITE_FCNTL_ZIPVFS => ffi::SQLITE_NOTFOUND,
1443
1444            // Implemented by the special VFS used by the RBU extension only.
1445            ffi::SQLITE_FCNTL_RBU => ffi::SQLITE_NOTFOUND,
1446
1447            // Obtain the underlying native file handle associated with a file handle.
1448            // Not implemented.
1449            ffi::SQLITE_FCNTL_WIN32_GET_HANDLE => ffi::SQLITE_NOTFOUND,
1450
1451            // Usage is not documented. Not implemented.
1452            ffi::SQLITE_FCNTL_PDB => ffi::SQLITE_NOTFOUND,
1453
1454            // Used for "batch write mode". Not supported.
1455            ffi::SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
1456            | ffi::SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
1457            | ffi::SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE => ffi::SQLITE_NOTFOUND,
1458
1459            // Configure a VFS to block for up to M milliseconds before failing when attempting to
1460            // obtain a file lock using the xLock or xShmLock methods of the VFS. Not implemented.
1461            ffi::SQLITE_FCNTL_LOCK_TIMEOUT => ffi::SQLITE_NOTFOUND,
1462
1463            // Used by in-memory VFS.
1464            ffi::SQLITE_FCNTL_SIZE_LIMIT => ffi::SQLITE_NOTFOUND,
1465
1466            // Invoked from within a checkpoint in wal mode after the client has finished copying
1467            // pages from the wal file to the database file, but before the *-shm file is updated to
1468            // record the fact that the pages have been checkpointed. Silently ignored.
1469            ffi::SQLITE_FCNTL_CKPT_DONE => ffi::SQLITE_OK,
1470
1471            // Invoked from within a checkpoint in wal mode before the client starts to copy pages
1472            // from the wal file to the database file. Silently ignored.
1473            ffi::SQLITE_FCNTL_CKPT_START => ffi::SQLITE_OK,
1474
1475            // Detect whether or not there is a database client in another process with a wal-mode
1476            // transaction open on the database or not. Not implemented because it is a
1477            // unix-specific feature.
1478            ffi::SQLITE_FCNTL_EXTERNAL_READER => ffi::SQLITE_NOTFOUND,
1479
1480            // Unknown use-case. Ignored.
1481            ffi::SQLITE_FCNTL_CKSM_FILE => ffi::SQLITE_NOTFOUND,
1482
1483            _ => ffi::SQLITE_NOTFOUND,
1484        }
1485    }
1486
1487    /// Return the sector-size in bytes for a file.
1488    pub unsafe extern "C" fn sector_size<F>(_p_file: *mut ffi::sqlite3_file) -> c_int {
1489        log::trace!("sector_size");
1490
1491        1024
1492    }
1493
1494    /// Return the device characteristic flags supported by a file.
1495    pub unsafe extern "C" fn device_characteristics<V, F: DatabaseHandle>(
1496        p_file: *mut ffi::sqlite3_file,
1497    ) -> c_int {
1498        let state = match file_state::<V, F>(p_file) {
1499            Ok(f) => f,
1500            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1501        };
1502
1503        log::trace!("[{}] device_characteristics", state.id,);
1504
1505        // The following characteristics are needed to match the expected behavior of the tests.
1506
1507        // after reboot following a crash or power loss, the only bytes in a file that were written
1508        // at the application level might have changed and that adjacent bytes, even bytes within
1509        // the same sector are guaranteed to be unchanged
1510        if state.powersafe_overwrite {
1511            ffi::SQLITE_IOCAP_POWERSAFE_OVERWRITE
1512        } else {
1513            0
1514        }
1515    }
1516
1517    /// Create a shared memory file mapping.
1518    pub unsafe extern "C" fn shm_map<V, F: DatabaseHandle>(
1519        p_file: *mut ffi::sqlite3_file,
1520        region_ix: i32,
1521        region_size: i32,
1522        b_extend: i32,
1523        pp: *mut *mut c_void,
1524    ) -> i32 {
1525        let state = match file_state::<V, F>(p_file) {
1526            Ok(f) => f,
1527            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1528        };
1529        log::trace!(
1530            "[{}] shm_map pg={} sz={} extend={} ({})",
1531            state.id,
1532            region_ix,
1533            region_size,
1534            b_extend,
1535            state.db_name
1536        );
1537
1538        if !F::WalIndex::enabled() {
1539            return ffi::SQLITE_IOERR_SHMLOCK;
1540        }
1541
1542        if region_size != 32768 {
1543            return state.set_last_error(
1544                ffi::SQLITE_IOERR_SHMMAP,
1545                std::io::Error::new(
1546                    ErrorKind::Other,
1547                    format!(
1548                        "encountered region size other than 32kB; got {}",
1549                        region_size
1550                    ),
1551                ),
1552            );
1553        }
1554
1555        let (wal_index, readonly) = match state.wal_index.as_mut() {
1556            Some((wal_index, readonly)) => (wal_index, *readonly),
1557            None => {
1558                let (wal_index, readonly) = state.wal_index.get_or_insert(
1559                    match state
1560                        .file
1561                        .wal_index(false)
1562                        .map(|wal_index| (wal_index, false))
1563                        .or_else(|err| {
1564                            if err.kind() == ErrorKind::PermissionDenied {
1565                                // Try again as readonly
1566                                state
1567                                    .file
1568                                    .wal_index(true)
1569                                    .map(|wal_index| (wal_index, true))
1570                                    .map_err(|_| err)
1571                            } else {
1572                                Err(err)
1573                            }
1574                        }) {
1575                        Ok((wal_index, readonly)) => (wal_index, readonly),
1576                        Err(err) => {
1577                            return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1578                        }
1579                    },
1580                );
1581                (wal_index, *readonly)
1582            }
1583        };
1584
1585        let entry = state.wal_index_regions.entry(region_ix as u32);
1586        match entry {
1587            Entry::Occupied(mut entry) => {
1588                *pp = entry.get_mut().as_mut_ptr() as *mut c_void;
1589            }
1590            Entry::Vacant(entry) => {
1591                let mut m = match wal_index.map(region_ix as u32) {
1592                    Ok(m) => Box::pin(m),
1593                    Err(err) => {
1594                        return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1595                    }
1596                };
1597                *pp = m.as_mut_ptr() as *mut c_void;
1598                entry.insert(m);
1599            }
1600        }
1601
1602        if readonly {
1603            ffi::SQLITE_READONLY
1604        } else {
1605            ffi::SQLITE_OK
1606        }
1607    }
1608
1609    /// Perform locking on a shared-memory segment.
1610    pub unsafe extern "C" fn shm_lock<V, F: DatabaseHandle>(
1611        p_file: *mut ffi::sqlite3_file,
1612        offset: i32,
1613        n: i32,
1614        flags: i32,
1615    ) -> i32 {
1616        let state = match file_state::<V, F>(p_file) {
1617            Ok(f) => f,
1618            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1619        };
1620        let locking = flags & ffi::SQLITE_SHM_LOCK > 0;
1621        let exclusive = flags & ffi::SQLITE_SHM_EXCLUSIVE > 0;
1622        log::trace!(
1623            "[{}] shm_lock offset={} n={} lock={} exclusive={} (flags={}) ({})",
1624            state.id,
1625            offset,
1626            n,
1627            locking,
1628            exclusive,
1629            flags,
1630            state.db_name
1631        );
1632
1633        let range = offset as u8..(offset + n) as u8;
1634        let lock = match (locking, exclusive) {
1635            (true, true) => wip::WalIndexLock::Exclusive,
1636            (true, false) => wip::WalIndexLock::Shared,
1637            (false, _) => wip::WalIndexLock::None,
1638        };
1639
1640        let (wal_index, readonly) = match state.wal_index.as_mut() {
1641            Some((wal_index, readonly)) => (wal_index, *readonly),
1642            None => {
1643                return state.set_last_error(
1644                    ffi::SQLITE_IOERR_SHMLOCK,
1645                    std::io::Error::new(
1646                        ErrorKind::Other,
1647                        "trying to lock wal index, which isn't created yet",
1648                    ),
1649                )
1650            }
1651        };
1652
1653        if locking {
1654            let has_exclusive = state
1655                .wal_index_locks
1656                .iter()
1657                .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1658
1659            if !has_exclusive {
1660                log::trace!(
1661                    "[{}] does not have wal index write lock, pulling changes",
1662                    state.id
1663                );
1664                for (region, data) in &mut state.wal_index_regions {
1665                    if let Err(err) = wal_index.pull(*region as u32, data) {
1666                        return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1667                    }
1668                }
1669            }
1670        } else {
1671            let releases_any_exclusive = state.wal_index_locks.iter().any(|(region, lock)| {
1672                *lock == wip::WalIndexLock::Exclusive && range.contains(region)
1673            });
1674
1675            // push index changes when moving from any exclusive lock to no exclusive locks
1676            if releases_any_exclusive && !readonly {
1677                log::trace!(
1678                    "[{}] releasing an exclusive lock, pushing wal index changes",
1679                    state.id,
1680                );
1681                for (region, data) in &mut state.wal_index_regions {
1682                    if let Err(err) = wal_index.push(*region as u32, data) {
1683                        return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1684                    }
1685                }
1686            }
1687        }
1688
1689        match wal_index.lock(range.clone(), lock) {
1690            Ok(true) => {
1691                for region in range {
1692                    state.wal_index_locks.insert(region, lock);
1693                }
1694                ffi::SQLITE_OK
1695            }
1696            Ok(false) => ffi::SQLITE_BUSY,
1697            Err(err) => state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err),
1698        }
1699    }
1700
1701    /// Memory barrier operation on shared memory.
1702    pub unsafe extern "C" fn shm_barrier<V, F: DatabaseHandle>(p_file: *mut ffi::sqlite3_file) {
1703        let state = match file_state::<V, F>(p_file) {
1704            Ok(f) => f,
1705            Err(_) => return,
1706        };
1707        log::trace!("[{}] shm_barrier ({})", state.id, state.db_name);
1708
1709        let (wal_index, readonly) = if let Some((wal_index, readonly)) = state.wal_index.as_mut() {
1710            (wal_index, *readonly)
1711        } else {
1712            return;
1713        };
1714
1715        if state.has_exclusive_lock && !readonly {
1716            log::trace!(
1717                "[{}] has exclusive db lock, pushing wal index changes",
1718                state.id,
1719            );
1720            for (region, data) in &mut state.wal_index_regions {
1721                if let Err(err) = wal_index.push(*region as u32, data) {
1722                    log::error!("[{}] pushing wal index changes failed: {}", state.id, err)
1723                }
1724            }
1725
1726            return;
1727        }
1728
1729        let has_exclusive = state
1730            .wal_index_locks
1731            .iter()
1732            .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1733
1734        if !has_exclusive {
1735            log::trace!(
1736                "[{}] does not have wal index write lock, pulling changes",
1737                state.id
1738            );
1739            for (region, data) in &mut state.wal_index_regions {
1740                if let Err(err) = wal_index.pull(*region as u32, data) {
1741                    log::error!("[{}] pulling wal index changes failed: {}", state.id, err)
1742                }
1743            }
1744        }
1745    }
1746
1747    /// Unmap a shared memory segment.
1748    pub unsafe extern "C" fn shm_unmap<V, F: DatabaseHandle>(
1749        p_file: *mut ffi::sqlite3_file,
1750        delete_flags: i32,
1751    ) -> i32 {
1752        let state = match file_state::<V, F>(p_file) {
1753            Ok(f) => f,
1754            Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1755        };
1756        log::trace!(
1757            "[{}] shm_unmap delete={} ({})",
1758            state.id,
1759            delete_flags == 1,
1760            state.db_name
1761        );
1762
1763        state.wal_index_regions.clear();
1764        state.wal_index_locks.clear();
1765
1766        if delete_flags == 1 {
1767            if let Some((wal_index, readonly)) = state.wal_index.take() {
1768                if !readonly {
1769                    if let Err(err) = wal_index.delete() {
1770                        return state.set_last_error(ffi::SQLITE_ERROR, err);
1771                    }
1772                }
1773            }
1774        }
1775
1776        ffi::SQLITE_OK
1777    }
1778}
1779
1780// #[cfg(feature = "sqlite_test")]
1781// struct Benign;
1782
1783// #[cfg(feature = "sqlite_test")]
1784// #[inline]
1785// unsafe fn simulate_io_error_benign() -> Benign {
1786//     ffi::sqlite3_set_io_error_benign(1);
1787//     Benign
1788// }
1789
1790// #[cfg(feature = "sqlite_test")]
1791// impl Drop for Benign {
1792//     fn drop(&mut self) {
1793//         unsafe { ffi::sqlite3_set_io_error_benign(0) }
1794//     }
1795// }
1796
1797// Note: When adding additional simulate_io_error() calls, retest:
1798// - malloc.test
1799// - ioerr2.test
1800// - backup_ioerr.test
1801#[cfg(feature = "sqlite_test")]
1802#[inline]
1803unsafe fn simulate_io_error() -> bool {
1804    if (ffi::sqlite3_get_io_error_persist() != 0 && ffi::sqlite3_get_io_error_hit() != 0)
1805        || ffi::sqlite3_dec_io_error_pending() == 1
1806    {
1807        ffi::sqlite3_inc_io_error_hit();
1808        if ffi::sqlite3_get_io_error_benign() == 0 {
1809            ffi::sqlite3_inc_io_error_hardhit();
1810        }
1811
1812        return true;
1813    }
1814
1815    false
1816}
1817
1818#[cfg(feature = "sqlite_test")]
1819#[inline]
1820unsafe fn simulate_diskfull_error() -> bool {
1821    if ffi::sqlite3_get_diskfull_pending() != 0 {
1822        if ffi::sqlite3_get_diskfull_pending() == 1 {
1823            if ffi::sqlite3_get_io_error_benign() == 0 {
1824                ffi::sqlite3_inc_io_error_hardhit();
1825            }
1826            ffi::sqlite3_set_diskfull();
1827            ffi::sqlite3_set_io_error_hit(1);
1828            return true;
1829        } else {
1830            ffi::sqlite3_dec_diskfull_pending();
1831        }
1832    }
1833
1834    false
1835}
1836
1837impl<V> State<V> {
1838    fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1839        // log::error!("{} ({})", err, no);
1840        *(self.last_error.lock().unwrap()) = Some((no, err));
1841        no
1842    }
1843}
1844
1845impl<V, F: DatabaseHandle> FileExt<V, F> {
1846    fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1847        // log::error!("{} ({})", err, no);
1848        *(self.last_error.lock().unwrap()) = Some((no, err));
1849        self.last_errno = no;
1850        no
1851    }
1852}
1853
1854fn null_ptr_error() -> std::io::Error {
1855    std::io::Error::new(ErrorKind::Other, "received null pointer")
1856}
1857
1858unsafe fn vfs_state<'a, V>(ptr: *mut ffi::sqlite3_vfs) -> Result<&'a mut State<V>, std::io::Error> {
1859    let vfs: &mut ffi::sqlite3_vfs = ptr.as_mut().ok_or_else(null_ptr_error)?;
1860    let state = (vfs.pAppData as *mut State<V>)
1861        .as_mut()
1862        .ok_or_else(null_ptr_error)?;
1863    Ok(state)
1864}
1865
1866unsafe fn file_state<'a, V, F: DatabaseHandle>(
1867    ptr: *mut ffi::sqlite3_file,
1868) -> Result<&'a mut FileExt<V, F>, std::io::Error> {
1869    let f = (ptr as *mut FileState<V, F>)
1870        .as_mut()
1871        .ok_or_else(null_ptr_error)?;
1872    let ext = f.ext.assume_init_mut();
1873    Ok(ext)
1874}
1875
1876impl OpenOptions {
1877    fn from_flags(flags: i32) -> Option<Self> {
1878        Some(OpenOptions {
1879            kind: OpenKind::from_flags(flags)?,
1880            access: OpenAccess::from_flags(flags)?,
1881            delete_on_close: flags & ffi::SQLITE_OPEN_DELETEONCLOSE > 0,
1882        })
1883    }
1884
1885    fn to_flags(&self) -> i32 {
1886        self.kind.to_flags()
1887            | self.access.to_flags()
1888            | if self.delete_on_close {
1889                ffi::SQLITE_OPEN_DELETEONCLOSE
1890            } else {
1891                0
1892            }
1893    }
1894}
1895
1896impl OpenKind {
1897    fn from_flags(flags: i32) -> Option<Self> {
1898        match flags {
1899            flags if flags & ffi::SQLITE_OPEN_MAIN_DB > 0 => Some(Self::MainDb),
1900            flags if flags & ffi::SQLITE_OPEN_MAIN_JOURNAL > 0 => Some(Self::MainJournal),
1901            flags if flags & ffi::SQLITE_OPEN_TEMP_DB > 0 => Some(Self::TempDb),
1902            flags if flags & ffi::SQLITE_OPEN_TEMP_JOURNAL > 0 => Some(Self::TempJournal),
1903            flags if flags & ffi::SQLITE_OPEN_TRANSIENT_DB > 0 => Some(Self::TransientDb),
1904            flags if flags & ffi::SQLITE_OPEN_SUBJOURNAL > 0 => Some(Self::SubJournal),
1905            flags if flags & ffi::SQLITE_OPEN_SUPER_JOURNAL > 0 => Some(Self::SuperJournal),
1906            flags if flags & ffi::SQLITE_OPEN_WAL > 0 => Some(Self::Wal),
1907            _ => None,
1908        }
1909    }
1910
1911    fn to_flags(self) -> i32 {
1912        match self {
1913            OpenKind::MainDb => ffi::SQLITE_OPEN_MAIN_DB,
1914            OpenKind::MainJournal => ffi::SQLITE_OPEN_MAIN_JOURNAL,
1915            OpenKind::TempDb => ffi::SQLITE_OPEN_TEMP_DB,
1916            OpenKind::TempJournal => ffi::SQLITE_OPEN_TEMP_JOURNAL,
1917            OpenKind::TransientDb => ffi::SQLITE_OPEN_TRANSIENT_DB,
1918            OpenKind::SubJournal => ffi::SQLITE_OPEN_SUBJOURNAL,
1919            OpenKind::SuperJournal => ffi::SQLITE_OPEN_SUPER_JOURNAL,
1920            OpenKind::Wal => ffi::SQLITE_OPEN_WAL,
1921        }
1922    }
1923}
1924
1925impl OpenAccess {
1926    fn from_flags(flags: i32) -> Option<Self> {
1927        match flags {
1928            flags
1929                if (flags & ffi::SQLITE_OPEN_CREATE > 0)
1930                    && (flags & ffi::SQLITE_OPEN_EXCLUSIVE > 0) =>
1931            {
1932                Some(Self::CreateNew)
1933            }
1934            flags if flags & ffi::SQLITE_OPEN_CREATE > 0 => Some(Self::Create),
1935            flags if flags & ffi::SQLITE_OPEN_READWRITE > 0 => Some(Self::Write),
1936            flags if flags & ffi::SQLITE_OPEN_READONLY > 0 => Some(Self::Read),
1937            _ => None,
1938        }
1939    }
1940
1941    fn to_flags(self) -> i32 {
1942        match self {
1943            OpenAccess::Read => ffi::SQLITE_OPEN_READONLY,
1944            OpenAccess::Write => ffi::SQLITE_OPEN_READWRITE,
1945            OpenAccess::Create => ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE,
1946            OpenAccess::CreateNew => {
1947                ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_EXCLUSIVE
1948            }
1949        }
1950    }
1951}
1952
1953impl LockKind {
1954    fn from_i32(lock: i32) -> Option<Self> {
1955        Some(match lock {
1956            ffi::SQLITE_LOCK_NONE => Self::None,
1957            ffi::SQLITE_LOCK_SHARED => Self::Shared,
1958            ffi::SQLITE_LOCK_RESERVED => Self::Reserved,
1959            ffi::SQLITE_LOCK_PENDING => Self::Pending,
1960            ffi::SQLITE_LOCK_EXCLUSIVE => Self::Exclusive,
1961            _ => return None,
1962        })
1963    }
1964
1965    fn to_i32(self) -> i32 {
1966        match self {
1967            Self::None => ffi::SQLITE_LOCK_NONE,
1968            Self::Shared => ffi::SQLITE_LOCK_SHARED,
1969            Self::Reserved => ffi::SQLITE_LOCK_RESERVED,
1970            Self::Pending => ffi::SQLITE_LOCK_PENDING,
1971            Self::Exclusive => ffi::SQLITE_LOCK_EXCLUSIVE,
1972        }
1973    }
1974}
1975
1976impl PartialOrd for LockKind {
1977    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1978        self.to_i32().partial_cmp(&other.to_i32())
1979    }
1980}
1981
1982impl Default for LockKind {
1983    fn default() -> Self {
1984        Self::None
1985    }
1986}
1987
1988#[derive(Default)]
1989pub struct WalDisabled;
1990
1991impl wip::WalIndex for WalDisabled {
1992    fn enabled() -> bool {
1993        false
1994    }
1995
1996    fn map(&mut self, _region: u32) -> Result<[u8; 32768], std::io::Error> {
1997        Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
1998    }
1999
2000    fn lock(
2001        &mut self,
2002        _locks: Range<u8>,
2003        _lock: wip::WalIndexLock,
2004    ) -> Result<bool, std::io::Error> {
2005        Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2006    }
2007
2008    fn delete(self) -> Result<(), std::io::Error> {
2009        Ok(())
2010    }
2011}
2012
2013#[derive(Debug)]
2014pub enum RegisterError {
2015    Nul(std::ffi::NulError),
2016    Register(i32),
2017}
2018
2019impl std::error::Error for RegisterError {
2020    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2021        match self {
2022            Self::Nul(err) => Some(err),
2023            Self::Register(_) => None,
2024        }
2025    }
2026}
2027
2028impl std::fmt::Display for RegisterError {
2029    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2030        match self {
2031            Self::Nul(_) => f.write_str("interior nul byte in name found"),
2032            Self::Register(code) => {
2033                write!(f, "registering sqlite vfs failed with error code: {}", code)
2034            }
2035        }
2036    }
2037}
2038
2039impl From<std::ffi::NulError> for RegisterError {
2040    fn from(err: std::ffi::NulError) -> Self {
2041        Self::Nul(err)
2042    }
2043}
2044
2045#[cfg(test)]
2046mod tests {
2047    use super::*;
2048
2049    #[test]
2050    fn test_lock_order() {
2051        assert!(LockKind::None < LockKind::Shared);
2052        assert!(LockKind::Shared < LockKind::Reserved);
2053        assert!(LockKind::Reserved < LockKind::Pending);
2054        assert!(LockKind::Pending < LockKind::Exclusive);
2055    }
2056}