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 = 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(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
94fn 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 fn register_logger(&self, logger: SqliteLogger);
163
164 fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult<Cow<'a, str>> {
166 Ok(path)
167 }
168
169 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 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 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#[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 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 out[..from.len()].copy_from_slice(from);
435 out[from.len()] = 0;
437 Ok(vars::SQLITE_OK)
438 })
439}
440
441unsafe 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 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 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 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
601unsafe 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
619unsafe 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 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 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 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 println!("writing to blob");
795 let n = blob.write(b"hello")?;
796 assert_eq!(n, 5);
797
798 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}