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