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