sqlite_plugin/
vfs.rs

1use crate::flags::{AccessFlags, LockLevel, OpenOpts};
2use crate::logger::SqliteLogger;
3use crate::vars::SQLITE_ERROR;
4use crate::{ffi, vars};
5use alloc::borrow::Cow;
6use alloc::boxed::Box;
7use alloc::ffi::CString;
8use alloc::format;
9use alloc::string::String;
10use core::mem::{self, ManuallyDrop, MaybeUninit, size_of};
11use core::slice;
12use core::{
13    ffi::{CStr, c_char, c_int, c_void},
14    ptr::null_mut,
15};
16
17/// The minimim supported `SQLite` version.
18// If you need to make this earlier, make sure the tests are testing the earlier version
19pub const MIN_SQLITE_VERSION_NUMBER: i32 = 3044000;
20
21const DEFAULT_MAX_PATH_LEN: i32 = 512;
22pub const DEFAULT_SECTOR_SIZE: i32 = 4096;
23
24pub const DEFAULT_DEVICE_CHARACTERISTICS: i32 =
25    // writes of any size are atomic
26    vars::SQLITE_IOCAP_ATOMIC |
27    // after reboot following a crash or power loss, the only bytes in a file that were written
28    // at the application level might have changed and that adjacent bytes, even bytes within
29    // the same sector are guaranteed to be unchanged
30    vars::SQLITE_IOCAP_POWERSAFE_OVERWRITE |
31    // when data is appended to a file, the data is appended first then the size of the file is
32    // extended, never the other way around
33    vars::SQLITE_IOCAP_SAFE_APPEND |
34    // information is written to disk in the same order as calls to xWrite()
35    vars::SQLITE_IOCAP_SEQUENTIAL;
36
37/// A `SQLite3` extended error code
38pub type SqliteErr = i32;
39
40pub type VfsResult<T> = Result<T, SqliteErr>;
41
42// FileWrapper needs to be repr(C) and have sqlite3_file as it's first member
43// because it's a "subclass" of sqlite3_file
44#[repr(C)]
45struct FileWrapper<Handle> {
46    file: ffi::sqlite3_file,
47    vfs: *mut ffi::sqlite3_vfs,
48    handle: MaybeUninit<Handle>,
49}
50
51struct AppData<Vfs> {
52    base_vfs: *mut ffi::sqlite3_vfs,
53    vfs: Vfs,
54    io_methods: ffi::sqlite3_io_methods,
55    sqlite_api: SqliteApi,
56}
57
58#[derive(Debug)]
59pub struct Pragma<'a> {
60    pub name: &'a str,
61    pub arg: Option<&'a str>,
62}
63
64#[derive(Debug)]
65pub enum PragmaErr {
66    NotFound,
67    Fail(SqliteErr, Option<String>),
68}
69
70impl PragmaErr {
71    pub fn required_arg(p: &Pragma<'_>) -> Self {
72        PragmaErr::Fail(
73            SQLITE_ERROR,
74            Some(format!(
75                "argument required (e.g. `pragma {} = ...`)",
76                p.name
77            )),
78        )
79    }
80}
81
82fn fallible(mut cb: impl FnMut() -> Result<i32, SqliteErr>) -> i32 {
83    cb().unwrap_or_else(|err| err)
84}
85
86unsafe fn lossy_cstr<'a>(p: *const c_char) -> VfsResult<Cow<'a, str>> {
87    unsafe {
88        p.as_ref()
89            .map(|p| CStr::from_ptr(p).to_string_lossy())
90            .ok_or(vars::SQLITE_INTERNAL)
91    }
92}
93
94// uses sqlite3_mprintf to allocate memory for the string using sqlite's memory allocator
95// returns a pointer to the sqlite3 allocated string
96// # Safety
97// the returned pointer must be freed using sqlite3_free
98fn sqlite3_mprintf(api: &SqliteApi, s: &str) -> VfsResult<*mut c_char> {
99    let s = CString::new(s).map_err(|_| vars::SQLITE_INTERNAL)?;
100    let p = unsafe { (api.mprintf)(s.as_ptr()) };
101    if p.is_null() {
102        Err(vars::SQLITE_NOMEM)
103    } else {
104        Ok(p)
105    }
106}
107
108macro_rules! unwrap_appdata {
109    ($p_vfs:expr, $t_vfs:ty) => {
110        unsafe {
111            let out: VfsResult<&AppData<$t_vfs>> = (*$p_vfs)
112                .pAppData
113                .cast::<AppData<$t_vfs>>()
114                .as_ref()
115                .ok_or(vars::SQLITE_INTERNAL);
116            out
117        }
118    };
119}
120
121macro_rules! unwrap_vfs {
122    ($p_vfs:expr, $t_vfs:ty) => {{
123        let out: VfsResult<&$t_vfs> = unwrap_appdata!($p_vfs, $t_vfs).map(|app_data| &app_data.vfs);
124        out
125    }};
126}
127
128macro_rules! unwrap_base_vfs {
129    ($p_vfs:expr, $t_vfs:ty) => {{
130        let out: VfsResult<&mut ffi::sqlite3_vfs> =
131            unwrap_appdata!($p_vfs, $t_vfs).and_then(|app_data| {
132                unsafe { app_data.base_vfs.as_mut() }.ok_or(vars::SQLITE_INTERNAL)
133            });
134        out
135    }};
136}
137
138macro_rules! unwrap_file {
139    ($p_file:expr, $t_vfs:ty) => {
140        unsafe {
141            let out: VfsResult<&mut FileWrapper<<$t_vfs>::Handle>> = $p_file
142                .cast::<FileWrapper<<$t_vfs>::Handle>>()
143                .as_mut()
144                .ok_or(vars::SQLITE_INTERNAL);
145            out
146        }
147    };
148}
149
150pub trait VfsHandle: Send {
151    fn readonly(&self) -> bool;
152    fn in_memory(&self) -> bool;
153}
154
155#[allow(unused_variables)]
156pub trait Vfs: Send + Sync {
157    type Handle: VfsHandle;
158
159    /// Register the provided logger with this Vfs.
160    /// This function is guaranteed to only be called once per
161    /// register_{static,dynamic} call.
162    fn register_logger(&self, logger: SqliteLogger);
163
164    /// construct a canonical version of the given path
165    fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult<Cow<'a, str>> {
166        Ok(path)
167    }
168
169    // file system operations
170    fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult<Self::Handle>;
171    fn delete(&self, path: &str) -> VfsResult<()>;
172    fn access(&self, path: &str, flags: AccessFlags) -> VfsResult<bool>;
173
174    // file operations
175    fn file_size(&self, handle: &mut Self::Handle) -> VfsResult<usize>;
176    fn truncate(&self, handle: &mut Self::Handle, size: usize) -> VfsResult<()>;
177    fn write(&self, handle: &mut Self::Handle, offset: usize, data: &[u8]) -> VfsResult<usize>;
178    fn read(&self, handle: &mut Self::Handle, offset: usize, data: &mut [u8]) -> VfsResult<usize>;
179
180    fn lock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> {
181        Ok(())
182    }
183
184    fn unlock(&self, handle: &mut Self::Handle, level: LockLevel) -> VfsResult<()> {
185        Ok(())
186    }
187
188    fn sync(&self, handle: &mut Self::Handle) -> VfsResult<()> {
189        Ok(())
190    }
191
192    fn close(&self, handle: Self::Handle) -> VfsResult<()>;
193
194    fn pragma(
195        &self,
196        handle: &mut Self::Handle,
197        pragma: Pragma<'_>,
198    ) -> Result<Option<String>, PragmaErr> {
199        Err(PragmaErr::NotFound)
200    }
201
202    // system queries
203    fn sector_size(&self) -> i32 {
204        DEFAULT_SECTOR_SIZE
205    }
206
207    fn device_characteristics(&self) -> i32 {
208        DEFAULT_DEVICE_CHARACTERISTICS
209    }
210}
211
212#[derive(Clone)]
213pub(crate) struct SqliteApi {
214    register: unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: c_int) -> c_int,
215    find: unsafe extern "C" fn(arg1: *const c_char) -> *mut ffi::sqlite3_vfs,
216    mprintf: unsafe extern "C" fn(arg1: *const c_char, ...) -> *mut c_char,
217    log: unsafe extern "C" fn(arg1: c_int, arg2: *const c_char, ...),
218    libversion_number: unsafe extern "C" fn() -> c_int,
219}
220
221impl SqliteApi {
222    #[cfg(feature = "static")]
223    fn new_static() -> Self {
224        Self {
225            register: ffi::sqlite3_vfs_register,
226            find: ffi::sqlite3_vfs_find,
227            mprintf: ffi::sqlite3_mprintf,
228            log: ffi::sqlite3_log,
229            libversion_number: ffi::sqlite3_libversion_number,
230        }
231    }
232
233    #[cfg(feature = "dynamic")]
234    fn new_dynamic(api: &ffi::sqlite3_api_routines) -> VfsResult<Self> {
235        Ok(Self {
236            register: api.vfs_register.ok_or(vars::SQLITE_INTERNAL)?,
237            find: api.vfs_find.ok_or(vars::SQLITE_INTERNAL)?,
238            mprintf: api.mprintf.ok_or(vars::SQLITE_INTERNAL)?,
239            log: api.log.ok_or(vars::SQLITE_INTERNAL)?,
240            libversion_number: api.libversion_number.ok_or(vars::SQLITE_INTERNAL)?,
241        })
242    }
243}
244
245pub struct RegisterOpts {
246    pub make_default: bool,
247}
248
249#[cfg(feature = "static")]
250pub fn register_static<T: Vfs>(name: CString, vfs: T, opts: RegisterOpts) -> VfsResult<()> {
251    register_inner(SqliteApi::new_static(), name, vfs, opts)
252}
253
254/// Register a vfs with `SQLite` using the dynamic API. This API is available when
255/// `SQLite` is initializing extensions.
256/// # Safety
257/// `p_api` must be a valid, aligned pointer to a `sqlite3_api_routines` struct
258#[cfg(feature = "dynamic")]
259pub unsafe fn register_dynamic<T: Vfs>(
260    p_api: *mut ffi::sqlite3_api_routines,
261    name: CString,
262    vfs: T,
263    opts: RegisterOpts,
264) -> VfsResult<()> {
265    let api = unsafe { p_api.as_ref() }.ok_or(vars::SQLITE_INTERNAL)?;
266    let sqlite_api = SqliteApi::new_dynamic(api)?;
267    register_inner(sqlite_api, name, vfs, opts)
268}
269
270fn register_inner<T: Vfs>(
271    sqlite_api: SqliteApi,
272    name: CString,
273    vfs: T,
274    opts: RegisterOpts,
275) -> VfsResult<()> {
276    let version = unsafe { (sqlite_api.libversion_number)() };
277    if version < MIN_SQLITE_VERSION_NUMBER {
278        panic!(
279            "sqlite3 must be at least version {}, found version {}",
280            MIN_SQLITE_VERSION_NUMBER, version
281        );
282    }
283
284    let io_methods = ffi::sqlite3_io_methods {
285        iVersion: 3,
286        xClose: Some(x_close::<T>),
287        xRead: Some(x_read::<T>),
288        xWrite: Some(x_write::<T>),
289        xTruncate: Some(x_truncate::<T>),
290        xSync: Some(x_sync::<T>),
291        xFileSize: Some(x_file_size::<T>),
292        xLock: Some(x_lock::<T>),
293        xUnlock: Some(x_unlock::<T>),
294        xCheckReservedLock: None,
295        xFileControl: Some(x_file_control::<T>),
296        xSectorSize: Some(x_sector_size::<T>),
297        xDeviceCharacteristics: Some(x_device_characteristics::<T>),
298        xShmMap: None,
299        xShmLock: None,
300        xShmBarrier: None,
301        xShmUnmap: None,
302        xFetch: None,
303        xUnfetch: None,
304    };
305
306    vfs.register_logger(SqliteLogger::new(sqlite_api.log));
307
308    let p_name = ManuallyDrop::new(name).as_ptr();
309    let base_vfs = unsafe { (sqlite_api.find)(null_mut()) };
310    let vfs_register = sqlite_api.register;
311    let p_appdata = Box::into_raw(Box::new(AppData { base_vfs, vfs, io_methods, sqlite_api }));
312
313    let filewrapper_size: c_int = size_of::<FileWrapper<T::Handle>>()
314        .try_into()
315        .map_err(|_| vars::SQLITE_INTERNAL)?;
316
317    let p_vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs {
318        iVersion: 3,
319        szOsFile: filewrapper_size,
320        mxPathname: DEFAULT_MAX_PATH_LEN,
321        pNext: null_mut(),
322        zName: p_name,
323        pAppData: p_appdata.cast(),
324        xOpen: Some(x_open::<T>),
325        xDelete: Some(x_delete::<T>),
326        xAccess: Some(x_access::<T>),
327        xFullPathname: Some(x_full_pathname::<T>),
328        xDlOpen: Some(x_dlopen::<T>),
329        xDlError: Some(x_dlerror::<T>),
330        xDlSym: Some(x_dlsym::<T>),
331        xDlClose: Some(x_dlclose::<T>),
332        xRandomness: Some(x_randomness::<T>),
333        xSleep: Some(x_sleep::<T>),
334        xCurrentTime: Some(x_current_time::<T>),
335        xGetLastError: None,
336        xCurrentTimeInt64: Some(x_current_time_int64::<T>),
337        xSetSystemCall: None,
338        xGetSystemCall: None,
339        xNextSystemCall: None,
340    }));
341
342    let result = unsafe { vfs_register(p_vfs, opts.make_default.into()) };
343    if result != vars::SQLITE_OK {
344        // cleanup memory
345        unsafe {
346            drop(Box::from_raw(p_vfs));
347            drop(Box::from_raw(p_appdata));
348            drop(CString::from_raw(p_name as *mut c_char));
349        };
350        Err(result)
351    } else {
352        Ok(())
353    }
354}
355
356unsafe extern "C" fn x_open<T: Vfs>(
357    p_vfs: *mut ffi::sqlite3_vfs,
358    z_name: ffi::sqlite3_filename,
359    p_file: *mut ffi::sqlite3_file,
360    flags: c_int,
361    p_out_flags: *mut c_int,
362) -> c_int {
363    fallible(|| {
364        let opts = flags.into();
365        let name = unsafe { lossy_cstr(z_name) }.ok();
366        let vfs = unwrap_vfs!(p_vfs, T)?;
367        let handle = vfs.open(name.as_ref().map(|s| s.as_ref()), opts)?;
368
369        let out_file = unwrap_file!(p_file, T)?;
370        let appdata = unwrap_appdata!(p_vfs, T)?;
371
372        if let Some(p_out_flags) = unsafe { p_out_flags.as_mut() } {
373            let mut out_flags = flags;
374            if handle.readonly() {
375                out_flags |= vars::SQLITE_OPEN_READONLY;
376            }
377            if handle.in_memory() {
378                out_flags |= vars::SQLITE_OPEN_MEMORY;
379            }
380            *p_out_flags = out_flags;
381        }
382
383        out_file.file.pMethods = &appdata.io_methods;
384        out_file.vfs = p_vfs;
385        out_file.handle.write(handle);
386
387        Ok(vars::SQLITE_OK)
388    })
389}
390
391unsafe extern "C" fn x_delete<T: Vfs>(
392    p_vfs: *mut ffi::sqlite3_vfs,
393    z_name: ffi::sqlite3_filename,
394    _sync_dir: c_int,
395) -> c_int {
396    fallible(|| {
397        let name = unsafe { lossy_cstr(z_name)? };
398        let vfs = unwrap_vfs!(p_vfs, T)?;
399        vfs.delete(&name)?;
400        Ok(vars::SQLITE_OK)
401    })
402}
403
404unsafe extern "C" fn x_access<T: Vfs>(
405    p_vfs: *mut ffi::sqlite3_vfs,
406    z_name: ffi::sqlite3_filename,
407    flags: c_int,
408    p_res_out: *mut c_int,
409) -> c_int {
410    fallible(|| {
411        let name = unsafe { lossy_cstr(z_name)? };
412        let vfs = unwrap_vfs!(p_vfs, T)?;
413        let result = vfs.access(&name, flags.into())?;
414        let out = unsafe { p_res_out.as_mut() }.ok_or(vars::SQLITE_IOERR_ACCESS)?;
415        *out = result as i32;
416        Ok(vars::SQLITE_OK)
417    })
418}
419
420unsafe extern "C" fn x_full_pathname<T: Vfs>(
421    p_vfs: *mut ffi::sqlite3_vfs,
422    z_name: ffi::sqlite3_filename,
423    n_out: c_int,
424    z_out: *mut c_char,
425) -> c_int {
426    fallible(|| {
427        let name = unsafe { lossy_cstr(z_name)? };
428        let vfs = unwrap_vfs!(p_vfs, T)?;
429        let full_name = vfs.canonical_path(name)?;
430        let n_out = n_out.try_into().map_err(|_| vars::SQLITE_INTERNAL)?;
431        let out = unsafe { slice::from_raw_parts_mut(z_out as *mut u8, n_out) };
432        let from = &full_name.as_bytes()[..full_name.len().min(n_out - 1)];
433        // copy the name into the output buffer
434        out[..from.len()].copy_from_slice(from);
435        // add the trailing null byte
436        out[from.len()] = 0;
437        Ok(vars::SQLITE_OK)
438    })
439}
440
441// file operations
442
443unsafe extern "C" fn x_close<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
444    fallible(|| {
445        let file = unwrap_file!(p_file, T)?;
446        let vfs = unwrap_vfs!(file.vfs, T)?;
447        let handle = mem::replace(&mut file.handle, MaybeUninit::uninit());
448        let handle = unsafe { handle.assume_init() };
449        vfs.close(handle)?;
450        Ok(vars::SQLITE_OK)
451    })
452}
453
454unsafe extern "C" fn x_read<T: Vfs>(
455    p_file: *mut ffi::sqlite3_file,
456    buf: *mut c_void,
457    i_amt: c_int,
458    i_ofst: ffi::sqlite_int64,
459) -> c_int {
460    fallible(|| {
461        let file = unwrap_file!(p_file, T)?;
462        let vfs = unwrap_vfs!(file.vfs, T)?;
463        let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
464        let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
465        let buf = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), buf_len) };
466        vfs.read(unsafe { file.handle.assume_init_mut() }, offset, buf)?;
467        Ok(vars::SQLITE_OK)
468    })
469}
470
471unsafe extern "C" fn x_write<T: Vfs>(
472    p_file: *mut ffi::sqlite3_file,
473    buf: *const c_void,
474    i_amt: c_int,
475    i_ofst: ffi::sqlite_int64,
476) -> c_int {
477    fallible(|| {
478        let file = unwrap_file!(p_file, T)?;
479        let vfs = unwrap_vfs!(file.vfs, T)?;
480        let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
481        let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
482        let buf = unsafe { slice::from_raw_parts(buf.cast::<u8>(), buf_len) };
483        let n = vfs.write(unsafe { file.handle.assume_init_mut() }, offset, buf)?;
484        if n != buf_len {
485            return Err(vars::SQLITE_IOERR_WRITE);
486        }
487        Ok(vars::SQLITE_OK)
488    })
489}
490
491unsafe extern "C" fn x_truncate<T: Vfs>(
492    p_file: *mut ffi::sqlite3_file,
493    size: ffi::sqlite_int64,
494) -> c_int {
495    fallible(|| {
496        let file = unwrap_file!(p_file, T)?;
497        let vfs = unwrap_vfs!(file.vfs, T)?;
498        let size: usize = size.try_into().map_err(|_| vars::SQLITE_IOERR_TRUNCATE)?;
499        vfs.truncate(unsafe { file.handle.assume_init_mut() }, size)?;
500        Ok(vars::SQLITE_OK)
501    })
502}
503
504unsafe extern "C" fn x_sync<T: Vfs>(p_file: *mut ffi::sqlite3_file, _flags: c_int) -> c_int {
505    fallible(|| {
506        let file = unwrap_file!(p_file, T)?;
507        let vfs = unwrap_vfs!(file.vfs, T)?;
508        vfs.sync(unsafe { file.handle.assume_init_mut() })?;
509        Ok(vars::SQLITE_OK)
510    })
511}
512
513unsafe extern "C" fn x_file_size<T: Vfs>(
514    p_file: *mut ffi::sqlite3_file,
515    p_size: *mut ffi::sqlite3_int64,
516) -> c_int {
517    fallible(|| {
518        let file = unwrap_file!(p_file, T)?;
519        let vfs = unwrap_vfs!(file.vfs, T)?;
520        let size = vfs.file_size(unsafe { file.handle.assume_init_mut() })?;
521        let p_size = unsafe { p_size.as_mut() }.ok_or(vars::SQLITE_INTERNAL)?;
522        *p_size = size.try_into().map_err(|_| vars::SQLITE_IOERR_FSTAT)?;
523        Ok(vars::SQLITE_OK)
524    })
525}
526
527unsafe extern "C" fn x_lock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int {
528    fallible(|| {
529        let level: LockLevel = raw_lock.into();
530        let file = unwrap_file!(p_file, T)?;
531        let vfs = unwrap_vfs!(file.vfs, T)?;
532        vfs.lock(unsafe { file.handle.assume_init_mut() }, level)?;
533        Ok(vars::SQLITE_OK)
534    })
535}
536
537unsafe extern "C" fn x_unlock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int {
538    fallible(|| {
539        let level: LockLevel = raw_lock.into();
540        let file = unwrap_file!(p_file, T)?;
541        let vfs = unwrap_vfs!(file.vfs, T)?;
542        vfs.unlock(unsafe { file.handle.assume_init_mut() }, level)?;
543        Ok(vars::SQLITE_OK)
544    })
545}
546
547unsafe extern "C" fn x_file_control<T: Vfs>(
548    p_file: *mut ffi::sqlite3_file,
549    op: c_int,
550    p_arg: *mut c_void,
551) -> c_int {
552    /*
553    Other interesting ops:
554    SIZE_HINT: hint of how large the database will grow during the current transaction
555    COMMIT_PHASETWO: after transaction commits before file unlocks (only used in WAL mode)
556    VFS_NAME: should return this vfs's name + / + base vfs's name
557
558    Atomic write support: (requires SQLITE_IOCAP_BATCH_ATOMIC device characteristic)
559    Docs: https://www3.sqlite.org/cgi/src/technote/714f6cbbf78c8a1351cbd48af2b438f7f824b336
560    BEGIN_ATOMIC_WRITE: start an atomic write operation
561    COMMIT_ATOMIC_WRITE: commit an atomic write operation
562    ROLLBACK_ATOMIC_WRITE: rollback an atomic write operation
563    */
564
565    if op == vars::SQLITE_FCNTL_PRAGMA {
566        return fallible(|| {
567            let file = unwrap_file!(p_file, T)?;
568            let vfs = unwrap_vfs!(file.vfs, T)?;
569
570            // p_arg is a pointer to an array of strings
571            // the second value is the pragma name
572            // the third value is either null or the pragma arg
573            let args = p_arg.cast::<*const c_char>();
574            let name = unsafe { lossy_cstr(*args.add(1)) }?;
575            let arg = unsafe {
576                (*args.add(2))
577                    .as_ref()
578                    .map(|p| CStr::from_ptr(p).to_string_lossy())
579            };
580            let pragma = Pragma { name: &name, arg: arg.as_deref() };
581
582            let (result, msg) = match vfs.pragma(unsafe { file.handle.assume_init_mut() }, pragma) {
583                Ok(msg) => (Ok(vars::SQLITE_OK), msg),
584                Err(PragmaErr::NotFound) => (Err(vars::SQLITE_NOTFOUND), None),
585                Err(PragmaErr::Fail(err, msg)) => (Err(err), msg),
586            };
587
588            if let Some(msg) = msg {
589                // write the msg back to the first element of the args array.
590                // SQLite is responsible for eventually freeing the result
591                let appdata = unwrap_appdata!(file.vfs, T)?;
592                unsafe { *args = sqlite3_mprintf(&appdata.sqlite_api, &msg)? };
593            }
594
595            result
596        });
597    }
598    vars::SQLITE_NOTFOUND
599}
600
601// system queries
602
603unsafe extern "C" fn x_sector_size<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
604    fallible(|| {
605        let file = unwrap_file!(p_file, T)?;
606        let vfs = unwrap_vfs!(file.vfs, T)?;
607        Ok(vfs.sector_size())
608    })
609}
610
611unsafe extern "C" fn x_device_characteristics<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
612    fallible(|| {
613        let file = unwrap_file!(p_file, T)?;
614        let vfs = unwrap_vfs!(file.vfs, T)?;
615        Ok(vfs.device_characteristics())
616    })
617}
618
619// the following functions are wrappers around the base vfs functions
620
621unsafe extern "C" fn x_dlopen<T: Vfs>(
622    p_vfs: *mut ffi::sqlite3_vfs,
623    z_path: *const c_char,
624) -> *mut c_void {
625    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
626        if let Some(x_dlopen) = vfs.xDlOpen {
627            return unsafe { x_dlopen(vfs, z_path) };
628        }
629    }
630    null_mut()
631}
632
633unsafe extern "C" fn x_dlerror<T: Vfs>(
634    p_vfs: *mut ffi::sqlite3_vfs,
635    n_byte: c_int,
636    z_err_msg: *mut c_char,
637) {
638    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
639        if let Some(x_dlerror) = vfs.xDlError {
640            unsafe { x_dlerror(vfs, n_byte, z_err_msg) };
641        }
642    }
643}
644
645unsafe extern "C" fn x_dlsym<T: Vfs>(
646    p_vfs: *mut ffi::sqlite3_vfs,
647    p_handle: *mut c_void,
648    z_symbol: *const c_char,
649) -> Option<
650    unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: *mut c_void, zSymbol: *const c_char),
651> {
652    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
653        if let Some(x_dlsym) = vfs.xDlSym {
654            return unsafe { x_dlsym(vfs, p_handle, z_symbol) };
655        }
656    }
657    None
658}
659
660unsafe extern "C" fn x_dlclose<T: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) {
661    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
662        if let Some(x_dlclose) = vfs.xDlClose {
663            unsafe { x_dlclose(vfs, p_handle) };
664        }
665    }
666}
667
668unsafe extern "C" fn x_randomness<T: Vfs>(
669    p_vfs: *mut ffi::sqlite3_vfs,
670    n_byte: c_int,
671    z_out: *mut c_char,
672) -> c_int {
673    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
674        if let Some(x_randomness) = vfs.xRandomness {
675            return unsafe { x_randomness(vfs, n_byte, z_out) };
676        }
677    }
678    vars::SQLITE_INTERNAL
679}
680
681unsafe extern "C" fn x_sleep<T: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, microseconds: c_int) -> c_int {
682    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
683        if let Some(x_sleep) = vfs.xSleep {
684            return unsafe { x_sleep(vfs, microseconds) };
685        }
686    }
687    vars::SQLITE_INTERNAL
688}
689
690unsafe extern "C" fn x_current_time<T: Vfs>(
691    p_vfs: *mut ffi::sqlite3_vfs,
692    p_time: *mut f64,
693) -> c_int {
694    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
695        if let Some(x_current_time) = vfs.xCurrentTime {
696            return unsafe { x_current_time(vfs, p_time) };
697        }
698    }
699    vars::SQLITE_INTERNAL
700}
701
702unsafe extern "C" fn x_current_time_int64<T: Vfs>(
703    p_vfs: *mut ffi::sqlite3_vfs,
704    p_time: *mut i64,
705) -> c_int {
706    if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
707        if let Some(x_current_time_int64) = vfs.xCurrentTimeInt64 {
708            return unsafe { x_current_time_int64(vfs, p_time) };
709        }
710    }
711    vars::SQLITE_INTERNAL
712}
713
714#[cfg(test)]
715mod tests {
716    // tests use std
717    extern crate std;
718
719    use super::*;
720    use crate::{
721        flags::{CreateMode, OpenKind, OpenMode},
722        mock::*,
723    };
724    use alloc::vec::Vec;
725    use rusqlite::{Connection, OpenFlags};
726    use std::{boxed::Box, io::Write, println};
727
728    fn log_handler(_: i32, arg2: &str) {
729        println!("{arg2}");
730    }
731
732    #[test]
733    fn sanity() -> Result<(), Box<dyn std::error::Error>> {
734        unsafe {
735            rusqlite::trace::config_log(Some(log_handler)).unwrap();
736        }
737
738        struct H {}
739        impl Hooks for H {
740            fn open(&mut self, path: &Option<&str>, opts: &OpenOpts) {
741                let path = path.unwrap();
742                if path == "main.db" {
743                    assert!(!opts.delete_on_close());
744                    assert_eq!(opts.kind(), OpenKind::MainDb);
745                    assert_eq!(
746                        opts.mode(),
747                        OpenMode::ReadWrite { create: CreateMode::Create }
748                    );
749                } else if path == "main.db-journal" {
750                    assert!(!opts.delete_on_close());
751                    assert_eq!(opts.kind(), OpenKind::MainJournal);
752                    assert_eq!(
753                        opts.mode(),
754                        OpenMode::ReadWrite { create: CreateMode::Create }
755                    );
756                } else {
757                    panic!("unexpected path: {}", path);
758                }
759            }
760        }
761
762        let vfs = MockVfs::new(Box::new(H {}));
763        register_static(
764            CString::new("mock").unwrap(),
765            vfs,
766            RegisterOpts { make_default: true },
767        )
768        .map_err(|_| "failed to register vfs")?;
769
770        // create a sqlite connection using the mock vfs
771        let conn = Connection::open_with_flags_and_vfs(
772            "main.db",
773            OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE,
774            "mock",
775        )?;
776
777        conn.execute("create table t (val int)", [])?;
778        conn.execute("insert into t (val) values (1)", [])?;
779        conn.execute("insert into t (val) values (2)", [])?;
780
781        conn.execute("pragma mock_test", [])?;
782
783        let n: i64 = conn.query_row("select sum(val) from t", [], |row| row.get(0))?;
784        assert_eq!(n, 3);
785
786        // the blob api is interesting and stress tests reading/writing pages and journaling
787        conn.execute("create table b (data blob)", [])?;
788        println!("inserting zero blob");
789        conn.execute("insert into b values (zeroblob(8192))", [])?;
790        let rowid = conn.last_insert_rowid();
791        let mut blob = conn.blob_open(rusqlite::MAIN_DB, "b", "data", rowid, false)?;
792
793        // write some data to the blob
794        println!("writing to blob");
795        let n = blob.write(b"hello")?;
796        assert_eq!(n, 5);
797
798        // query the table for the blob and print it
799        let mut stmt = conn.prepare("select data from b")?;
800        let mut rows = stmt.query([])?;
801        while let Some(row) = rows.next()? {
802            let data: Vec<u8> = row.get(0)?;
803            assert_eq!(&data[0..5], b"hello");
804        }
805
806        Ok(())
807    }
808}