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