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