1use crate::flags::{AccessFlags, LockLevel, OpenOpts, ShmLockMode};
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::{ManuallyDrop, size_of};
11use core::slice;
12use core::{
13 ffi::{CStr, c_char, c_int, c_void},
14 ptr::{NonNull, 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: 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, handle: &mut Self::Handle) -> VfsResult<i32> {
183 Ok(DEFAULT_SECTOR_SIZE)
184 }
185
186 fn device_characteristics(&self, handle: &mut Self::Handle) -> VfsResult<i32> {
187 Ok(DEFAULT_DEVICE_CHARACTERISTICS)
188 }
189
190 fn shm_map(
191 &self,
192 handle: &mut Self::Handle,
193 region_idx: usize,
194 region_size: usize,
195 extend: bool,
196 ) -> VfsResult<Option<NonNull<u8>>> {
197 Err(vars::SQLITE_READONLY_CANTINIT)
198 }
199
200 fn shm_lock(
201 &self,
202 handle: &mut Self::Handle,
203 offset: u32,
204 count: u32,
205 mode: ShmLockMode,
206 ) -> VfsResult<()> {
207 Err(vars::SQLITE_IOERR)
208 }
209
210 fn shm_barrier(&self, handle: &mut Self::Handle) {}
211
212 fn shm_unmap(&self, handle: &mut Self::Handle, delete: bool) -> VfsResult<()> {
213 Err(vars::SQLITE_IOERR)
214 }
215
216 fn fetch(
229 &self,
230 handle: &mut Self::Handle,
231 offset: i64,
232 amt: usize,
233 ) -> VfsResult<Option<NonNull<u8>>> {
234 Ok(None)
235 }
236
237 fn unfetch(&self, handle: &mut Self::Handle, offset: i64, ptr: *mut u8) -> VfsResult<()> {
243 Ok(())
244 }
245}
246
247#[derive(Clone)]
248pub struct SqliteApi {
249 register: unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: c_int) -> c_int,
250 find: unsafe extern "C" fn(arg1: *const c_char) -> *mut ffi::sqlite3_vfs,
251 mprintf: unsafe extern "C" fn(arg1: *const c_char, ...) -> *mut c_char,
252 log: unsafe extern "C" fn(arg1: c_int, arg2: *const c_char, ...),
253 libversion_number: unsafe extern "C" fn() -> c_int,
254}
255
256impl SqliteApi {
257 #[cfg(feature = "static")]
258 pub fn new_static() -> Self {
259 Self {
260 register: ffi::sqlite3_vfs_register,
261 find: ffi::sqlite3_vfs_find,
262 mprintf: ffi::sqlite3_mprintf,
263 log: ffi::sqlite3_log,
264 libversion_number: ffi::sqlite3_libversion_number,
265 }
266 }
267
268 #[cfg(feature = "dynamic")]
272 pub unsafe fn new_dynamic(api: &ffi::sqlite3_api_routines) -> VfsResult<Self> {
273 Ok(Self {
274 register: api.vfs_register.ok_or(vars::SQLITE_INTERNAL)?,
275 find: api.vfs_find.ok_or(vars::SQLITE_INTERNAL)?,
276 mprintf: api.mprintf.ok_or(vars::SQLITE_INTERNAL)?,
277 log: api.log.ok_or(vars::SQLITE_INTERNAL)?,
278 libversion_number: api.libversion_number.ok_or(vars::SQLITE_INTERNAL)?,
279 })
280 }
281
282 pub unsafe fn mprintf(&self, s: &str, out: *mut *const c_char) -> VfsResult<()> {
288 let s = CString::new(s).map_err(|_| vars::SQLITE_INTERNAL)?;
289 let p = unsafe { (self.mprintf)(s.as_ptr()) };
290 if p.is_null() {
291 Err(vars::SQLITE_NOMEM)
292 } else {
293 unsafe {
294 *out = p;
295 }
296 Ok(())
297 }
298 }
299}
300
301pub struct RegisterOpts {
302 pub make_default: bool,
304}
305
306#[cfg(feature = "static")]
307pub fn register_static<T: Vfs>(
308 name: CString,
309 vfs: T,
310 opts: RegisterOpts,
311) -> VfsResult<SqliteLogger> {
312 register_inner(SqliteApi::new_static(), name, vfs, opts)
313}
314
315#[cfg(feature = "dynamic")]
320pub unsafe fn register_dynamic<T: Vfs>(
321 p_api: *mut ffi::sqlite3_api_routines,
322 name: CString,
323 vfs: T,
324 opts: RegisterOpts,
325) -> VfsResult<SqliteLogger> {
326 let api = unsafe { p_api.as_ref() }.ok_or(vars::SQLITE_INTERNAL)?;
327 let sqlite_api = unsafe { SqliteApi::new_dynamic(api)? };
328 register_inner(sqlite_api, name, vfs, opts)
329}
330
331fn register_inner<T: Vfs>(
332 sqlite_api: SqliteApi,
333 name: CString,
334 vfs: T,
335 opts: RegisterOpts,
336) -> VfsResult<SqliteLogger> {
337 let version = unsafe { (sqlite_api.libversion_number)() };
338 if version < MIN_SQLITE_VERSION_NUMBER {
339 panic!(
340 "sqlite3 must be at least version {}, found version {}",
341 MIN_SQLITE_VERSION_NUMBER, version
342 );
343 }
344
345 let io_methods = ffi::sqlite3_io_methods {
346 iVersion: 3,
347 xClose: Some(x_close::<T>),
348 xRead: Some(x_read::<T>),
349 xWrite: Some(x_write::<T>),
350 xTruncate: Some(x_truncate::<T>),
351 xSync: Some(x_sync::<T>),
352 xFileSize: Some(x_file_size::<T>),
353 xLock: Some(x_lock::<T>),
354 xUnlock: Some(x_unlock::<T>),
355 xCheckReservedLock: Some(x_check_reserved_lock::<T>),
356 xFileControl: Some(x_file_control::<T>),
357 xSectorSize: Some(x_sector_size::<T>),
358 xDeviceCharacteristics: Some(x_device_characteristics::<T>),
359 xShmMap: Some(x_shm_map::<T>),
360 xShmLock: Some(x_shm_lock::<T>),
361 xShmBarrier: Some(x_shm_barrier::<T>),
362 xShmUnmap: Some(x_shm_unmap::<T>),
363 xFetch: Some(x_fetch::<T>),
364 xUnfetch: Some(x_unfetch::<T>),
365 };
366
367 let logger = SqliteLogger::new(sqlite_api.log);
368
369 let p_name = ManuallyDrop::new(name).as_ptr();
370 let base_vfs = unsafe { (sqlite_api.find)(null_mut()) };
371 let vfs_register = sqlite_api.register;
372 let p_appdata = Box::into_raw(Box::new(AppData { base_vfs, vfs, io_methods, sqlite_api }));
373
374 let filewrapper_size: c_int = size_of::<FileWrapper<T::Handle>>()
375 .try_into()
376 .map_err(|_| vars::SQLITE_INTERNAL)?;
377
378 let p_vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs {
379 iVersion: 3,
380 szOsFile: filewrapper_size,
381 mxPathname: DEFAULT_MAX_PATH_LEN,
382 pNext: null_mut(),
383 zName: p_name,
384 pAppData: p_appdata.cast(),
385 xOpen: Some(x_open::<T>),
386 xDelete: Some(x_delete::<T>),
387 xAccess: Some(x_access::<T>),
388 xFullPathname: Some(x_full_pathname::<T>),
389 xDlOpen: Some(x_dlopen::<T>),
390 xDlError: Some(x_dlerror::<T>),
391 xDlSym: Some(x_dlsym::<T>),
392 xDlClose: Some(x_dlclose::<T>),
393 xRandomness: Some(x_randomness::<T>),
394 xSleep: Some(x_sleep::<T>),
395 xCurrentTime: Some(x_current_time::<T>),
396 xGetLastError: None,
397 xCurrentTimeInt64: Some(x_current_time_int64::<T>),
398 xSetSystemCall: None,
399 xGetSystemCall: None,
400 xNextSystemCall: None,
401 }));
402
403 let result = unsafe { vfs_register(p_vfs, opts.make_default.into()) };
404 if result != vars::SQLITE_OK {
405 unsafe {
407 drop(Box::from_raw(p_vfs));
408 drop(Box::from_raw(p_appdata));
409 drop(CString::from_raw(p_name as *mut c_char));
410 };
411 Err(result)
412 } else {
413 Ok(logger)
414 }
415}
416
417unsafe extern "C" fn x_open<T: Vfs>(
418 p_vfs: *mut ffi::sqlite3_vfs,
419 z_name: ffi::sqlite3_filename,
420 p_file: *mut ffi::sqlite3_file,
421 flags: c_int,
422 p_out_flags: *mut c_int,
423) -> c_int {
424 if let Some(f) = unsafe { p_file.as_mut() } {
428 f.pMethods = core::ptr::null();
429 }
430
431 fallible(|| {
432 let opts = flags.into();
433 let name = unsafe { lossy_cstr(z_name) }.ok();
434 let vfs = unwrap_vfs!(p_vfs, T)?;
435 let handle = vfs.open(name.as_ref().map(|s| s.as_ref()), opts)?;
436
437 let appdata = unwrap_appdata!(p_vfs, T)?;
438
439 if let Some(p_out_flags) = unsafe { p_out_flags.as_mut() } {
440 let mut out_flags = flags;
441 if handle.readonly() {
442 out_flags |= vars::SQLITE_OPEN_READONLY;
443 }
444 if handle.in_memory() {
445 out_flags |= vars::SQLITE_OPEN_MEMORY;
446 }
447 *p_out_flags = out_flags;
448 }
449
450 let out_file = p_file.cast::<FileWrapper<T::Handle>>();
451 unsafe {
454 core::ptr::write(
455 out_file,
456 FileWrapper {
457 file: ffi::sqlite3_file { pMethods: &appdata.io_methods },
458 vfs: p_vfs,
459 handle,
460 },
461 );
462 }
463
464 Ok(vars::SQLITE_OK)
465 })
466}
467
468unsafe extern "C" fn x_delete<T: Vfs>(
469 p_vfs: *mut ffi::sqlite3_vfs,
470 z_name: ffi::sqlite3_filename,
471 _sync_dir: c_int,
472) -> c_int {
473 fallible(|| {
474 let name = unsafe { lossy_cstr(z_name)? };
475 let vfs = unwrap_vfs!(p_vfs, T)?;
476 vfs.delete(&name)?;
477 Ok(vars::SQLITE_OK)
478 })
479}
480
481unsafe extern "C" fn x_access<T: Vfs>(
482 p_vfs: *mut ffi::sqlite3_vfs,
483 z_name: ffi::sqlite3_filename,
484 flags: c_int,
485 p_res_out: *mut c_int,
486) -> c_int {
487 fallible(|| {
488 let name = unsafe { lossy_cstr(z_name)? };
489 let vfs = unwrap_vfs!(p_vfs, T)?;
490 let result = vfs.access(&name, flags.into())?;
491 let out = unsafe { p_res_out.as_mut() }.ok_or(vars::SQLITE_IOERR_ACCESS)?;
492 *out = result as i32;
493 Ok(vars::SQLITE_OK)
494 })
495}
496
497unsafe extern "C" fn x_full_pathname<T: Vfs>(
498 p_vfs: *mut ffi::sqlite3_vfs,
499 z_name: ffi::sqlite3_filename,
500 n_out: c_int,
501 z_out: *mut c_char,
502) -> c_int {
503 fallible(|| {
504 let name = unsafe { lossy_cstr(z_name)? };
505 let vfs = unwrap_vfs!(p_vfs, T)?;
506 let full_name = vfs.canonical_path(name)?;
507 let n_out = n_out.try_into().map_err(|_| vars::SQLITE_INTERNAL)?;
508 let out = unsafe { slice::from_raw_parts_mut(z_out as *mut u8, n_out) };
509 let from = &full_name.as_bytes()[..full_name.len().min(n_out - 1)];
510 out[..from.len()].copy_from_slice(from);
512 out[from.len()] = 0;
514 Ok(vars::SQLITE_OK)
515 })
516}
517
518unsafe extern "C" fn x_close<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
521 fallible(|| {
522 let (vfs, handle) = unsafe {
528 let p_file_ref = p_file.as_mut().ok_or(vars::SQLITE_INTERNAL)?;
530 p_file_ref.pMethods = core::ptr::null();
532
533 let file = core::ptr::read(p_file.cast::<FileWrapper<T::Handle>>());
535 (file.vfs, file.handle)
536 };
537
538 let vfs = unwrap_vfs!(vfs, T)?;
539 vfs.close(handle)?;
540 Ok(vars::SQLITE_OK)
541 })
542}
543
544unsafe extern "C" fn x_read<T: Vfs>(
545 p_file: *mut ffi::sqlite3_file,
546 buf: *mut c_void,
547 i_amt: c_int,
548 i_ofst: ffi::sqlite_int64,
549) -> c_int {
550 fallible(|| {
551 let file = unwrap_file!(p_file, T)?;
552 let vfs = unwrap_vfs!(file.vfs, T)?;
553 let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
554 let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
555 let buf = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), buf_len) };
556 let bytes_read = vfs.read(&mut file.handle, offset, buf)?;
557 if bytes_read < buf_len {
558 buf[bytes_read..].fill(0);
562 return Err(vars::SQLITE_IOERR_SHORT_READ);
563 }
564 Ok(vars::SQLITE_OK)
565 })
566}
567
568unsafe extern "C" fn x_write<T: Vfs>(
569 p_file: *mut ffi::sqlite3_file,
570 buf: *const c_void,
571 i_amt: c_int,
572 i_ofst: ffi::sqlite_int64,
573) -> c_int {
574 fallible(|| {
575 let file = unwrap_file!(p_file, T)?;
576 let vfs = unwrap_vfs!(file.vfs, T)?;
577 let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
578 let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
579 let buf = unsafe { slice::from_raw_parts(buf.cast::<u8>(), buf_len) };
580 let n = vfs.write(&mut file.handle, offset, buf)?;
581 if n != buf_len {
582 return Err(vars::SQLITE_IOERR_WRITE);
583 }
584 Ok(vars::SQLITE_OK)
585 })
586}
587
588unsafe extern "C" fn x_truncate<T: Vfs>(
589 p_file: *mut ffi::sqlite3_file,
590 size: ffi::sqlite_int64,
591) -> c_int {
592 fallible(|| {
593 let file = unwrap_file!(p_file, T)?;
594 let vfs = unwrap_vfs!(file.vfs, T)?;
595 let size: usize = size.try_into().map_err(|_| vars::SQLITE_IOERR_TRUNCATE)?;
596 vfs.truncate(&mut file.handle, size)?;
597 Ok(vars::SQLITE_OK)
598 })
599}
600
601unsafe extern "C" fn x_sync<T: Vfs>(p_file: *mut ffi::sqlite3_file, _flags: c_int) -> c_int {
602 fallible(|| {
603 let file = unwrap_file!(p_file, T)?;
604 let vfs = unwrap_vfs!(file.vfs, T)?;
605 vfs.sync(&mut file.handle)?;
606 Ok(vars::SQLITE_OK)
607 })
608}
609
610unsafe extern "C" fn x_file_size<T: Vfs>(
611 p_file: *mut ffi::sqlite3_file,
612 p_size: *mut ffi::sqlite3_int64,
613) -> c_int {
614 fallible(|| {
615 let file = unwrap_file!(p_file, T)?;
616 let vfs = unwrap_vfs!(file.vfs, T)?;
617 let size = vfs.file_size(&mut file.handle)?;
618 let p_size = unsafe { p_size.as_mut() }.ok_or(vars::SQLITE_INTERNAL)?;
619 *p_size = size.try_into().map_err(|_| vars::SQLITE_IOERR_FSTAT)?;
620 Ok(vars::SQLITE_OK)
621 })
622}
623
624unsafe extern "C" fn x_lock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int {
625 fallible(|| {
626 let level: LockLevel = raw_lock.into();
627 let file = unwrap_file!(p_file, T)?;
628 let vfs = unwrap_vfs!(file.vfs, T)?;
629 vfs.lock(&mut file.handle, level)?;
630 Ok(vars::SQLITE_OK)
631 })
632}
633
634unsafe extern "C" fn x_unlock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock: c_int) -> c_int {
635 fallible(|| {
636 let level: LockLevel = raw_lock.into();
637 let file = unwrap_file!(p_file, T)?;
638 let vfs = unwrap_vfs!(file.vfs, T)?;
639 vfs.unlock(&mut file.handle, level)?;
640 Ok(vars::SQLITE_OK)
641 })
642}
643
644unsafe extern "C" fn x_check_reserved_lock<T: Vfs>(
645 p_file: *mut ffi::sqlite3_file,
646 p_out: *mut c_int,
647) -> c_int {
648 fallible(|| {
649 let file = unwrap_file!(p_file, T)?;
650 let vfs = unwrap_vfs!(file.vfs, T)?;
651 unsafe {
652 *p_out = vfs.check_reserved_lock(&mut file.handle)? as c_int;
653 }
654 Ok(vars::SQLITE_OK)
655 })
656}
657
658unsafe extern "C" fn x_file_control<T: Vfs>(
659 p_file: *mut ffi::sqlite3_file,
660 op: c_int,
661 p_arg: *mut c_void,
662) -> c_int {
663 if op == vars::SQLITE_FCNTL_PRAGMA {
677 return fallible(|| {
678 let file = unwrap_file!(p_file, T)?;
679 let vfs = unwrap_vfs!(file.vfs, T)?;
680
681 let args = p_arg.cast::<*const c_char>();
685 let name = unsafe { lossy_cstr(*args.add(1)) }?;
686 let arg = unsafe {
687 (*args.add(2))
688 .as_ref()
689 .map(|p| CStr::from_ptr(p).to_string_lossy())
690 };
691 let pragma = Pragma { name: &name, arg: arg.as_deref() };
692
693 let (result, msg) = match vfs.pragma(&mut file.handle, pragma) {
694 Ok(msg) => (Ok(vars::SQLITE_OK), msg),
695 Err(PragmaErr::NotFound) => (Err(vars::SQLITE_NOTFOUND), None),
696 Err(PragmaErr::Fail(err, msg)) => (Err(err), msg),
697 };
698
699 if let Some(msg) = msg {
700 let appdata = unwrap_appdata!(file.vfs, T)?;
703 unsafe { appdata.sqlite_api.mprintf(&msg, args)? };
704 }
705
706 result
707 });
708 }
709 vars::SQLITE_NOTFOUND
710}
711
712unsafe extern "C" fn x_sector_size<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
715 fallible(|| {
716 let file = unwrap_file!(p_file, T)?;
717 let vfs = unwrap_vfs!(file.vfs, T)?;
718 vfs.sector_size(&mut file.handle)
719 })
720}
721
722unsafe extern "C" fn x_device_characteristics<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
723 fallible(|| {
724 let file = unwrap_file!(p_file, T)?;
725 let vfs = unwrap_vfs!(file.vfs, T)?;
726 vfs.device_characteristics(&mut file.handle)
727 })
728}
729
730unsafe extern "C" fn x_shm_map<T: Vfs>(
731 p_file: *mut ffi::sqlite3_file,
732 pg: c_int,
733 pgsz: c_int,
734 extend: c_int,
735 p_page: *mut *mut c_void,
736) -> c_int {
737 fallible(|| {
738 let file = unwrap_file!(p_file, T)?;
739 let vfs = unwrap_vfs!(file.vfs, T)?;
740 if let Some(region) = vfs.shm_map(
741 &mut file.handle,
742 pg.try_into().map_err(|_| vars::SQLITE_IOERR)?,
743 pgsz.try_into().map_err(|_| vars::SQLITE_IOERR)?,
744 extend != 0,
745 )? {
746 unsafe { *p_page = region.as_ptr() as *mut c_void }
747 } else {
748 unsafe { *p_page = null_mut() }
749 }
750 Ok(vars::SQLITE_OK)
751 })
752}
753
754unsafe extern "C" fn x_shm_lock<T: Vfs>(
755 p_file: *mut ffi::sqlite3_file,
756 offset: c_int,
757 n: c_int,
758 flags: c_int,
759) -> c_int {
760 fallible(|| {
761 let file = unwrap_file!(p_file, T)?;
762 let vfs = unwrap_vfs!(file.vfs, T)?;
763 vfs.shm_lock(
764 &mut file.handle,
765 offset.try_into().map_err(|_| vars::SQLITE_IOERR)?,
766 n.try_into().map_err(|_| vars::SQLITE_IOERR)?,
767 ShmLockMode::try_from(flags)?,
768 )?;
769 Ok(vars::SQLITE_OK)
770 })
771}
772
773unsafe extern "C" fn x_shm_barrier<T: Vfs>(p_file: *mut ffi::sqlite3_file) {
774 if let Ok(file) = unwrap_file!(p_file, T) {
775 if let Ok(vfs) = unwrap_vfs!(file.vfs, T) {
776 vfs.shm_barrier(&mut file.handle)
777 }
778 }
779}
780
781unsafe extern "C" fn x_shm_unmap<T: Vfs>(
782 p_file: *mut ffi::sqlite3_file,
783 delete_flag: c_int,
784) -> c_int {
785 fallible(|| {
786 let file = unwrap_file!(p_file, T)?;
787 let vfs = unwrap_vfs!(file.vfs, T)?;
788 vfs.shm_unmap(&mut file.handle, delete_flag != 0)?;
789 Ok(vars::SQLITE_OK)
790 })
791}
792
793unsafe extern "C" fn x_fetch<T: Vfs>(
794 p_file: *mut ffi::sqlite3_file,
795 i_ofst: ffi::sqlite3_int64,
796 i_amt: c_int,
797 pp: *mut *mut c_void,
798) -> c_int {
799 fallible(|| {
800 let file = unwrap_file!(p_file, T)?;
801 let vfs = unwrap_vfs!(file.vfs, T)?;
802 let amt: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR)?;
803 if let Some(ptr) = vfs.fetch(&mut file.handle, i_ofst, amt)? {
804 unsafe { *pp = ptr.as_ptr() as *mut c_void }
805 } else {
806 unsafe { *pp = null_mut() }
807 }
808 Ok(vars::SQLITE_OK)
809 })
810}
811
812unsafe extern "C" fn x_unfetch<T: Vfs>(
813 p_file: *mut ffi::sqlite3_file,
814 i_ofst: ffi::sqlite3_int64,
815 p: *mut c_void,
816) -> c_int {
817 fallible(|| {
818 let file = unwrap_file!(p_file, T)?;
819 let vfs = unwrap_vfs!(file.vfs, T)?;
820 vfs.unfetch(&mut file.handle, i_ofst, p as *mut u8)?;
821 Ok(vars::SQLITE_OK)
822 })
823}
824
825unsafe extern "C" fn x_dlopen<T: Vfs>(
828 p_vfs: *mut ffi::sqlite3_vfs,
829 z_path: *const c_char,
830) -> *mut c_void {
831 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
832 if let Some(x_dlopen) = vfs.xDlOpen {
833 return unsafe { x_dlopen(vfs, z_path) };
834 }
835 }
836 null_mut()
837}
838
839unsafe extern "C" fn x_dlerror<T: Vfs>(
840 p_vfs: *mut ffi::sqlite3_vfs,
841 n_byte: c_int,
842 z_err_msg: *mut c_char,
843) {
844 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
845 if let Some(x_dlerror) = vfs.xDlError {
846 unsafe { x_dlerror(vfs, n_byte, z_err_msg) };
847 }
848 }
849}
850
851unsafe extern "C" fn x_dlsym<T: Vfs>(
852 p_vfs: *mut ffi::sqlite3_vfs,
853 p_handle: *mut c_void,
854 z_symbol: *const c_char,
855) -> Option<unsafe extern "C" fn(arg1: *mut ffi::sqlite3_vfs, arg2: *mut c_void, arg3: *const c_char)>
856{
857 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
858 if let Some(x_dlsym) = vfs.xDlSym {
859 return unsafe { x_dlsym(vfs, p_handle, z_symbol) };
860 }
861 }
862 None
863}
864
865unsafe extern "C" fn x_dlclose<T: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) {
866 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
867 if let Some(x_dlclose) = vfs.xDlClose {
868 unsafe { x_dlclose(vfs, p_handle) };
869 }
870 }
871}
872
873unsafe extern "C" fn x_randomness<T: Vfs>(
874 p_vfs: *mut ffi::sqlite3_vfs,
875 n_byte: c_int,
876 z_out: *mut c_char,
877) -> c_int {
878 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
879 if let Some(x_randomness) = vfs.xRandomness {
880 return unsafe { x_randomness(vfs, n_byte, z_out) };
881 }
882 }
883 vars::SQLITE_INTERNAL
884}
885
886unsafe extern "C" fn x_sleep<T: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, microseconds: c_int) -> c_int {
887 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
888 if let Some(x_sleep) = vfs.xSleep {
889 return unsafe { x_sleep(vfs, microseconds) };
890 }
891 }
892 vars::SQLITE_INTERNAL
893}
894
895unsafe extern "C" fn x_current_time<T: Vfs>(
896 p_vfs: *mut ffi::sqlite3_vfs,
897 p_time: *mut f64,
898) -> c_int {
899 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
900 if let Some(x_current_time) = vfs.xCurrentTime {
901 return unsafe { x_current_time(vfs, p_time) };
902 }
903 }
904 vars::SQLITE_INTERNAL
905}
906
907unsafe extern "C" fn x_current_time_int64<T: Vfs>(
908 p_vfs: *mut ffi::sqlite3_vfs,
909 p_time: *mut i64,
910) -> c_int {
911 if let Ok(vfs) = unwrap_base_vfs!(p_vfs, T) {
912 if let Some(x_current_time_int64) = vfs.xCurrentTimeInt64 {
913 return unsafe { x_current_time_int64(vfs, p_time) };
914 }
915 }
916 vars::SQLITE_INTERNAL
917}
918
919#[cfg(test)]
920mod tests {
921 extern crate std;
923
924 use super::*;
925 use crate::{
926 flags::{CreateMode, OpenKind, OpenMode},
927 mock::*,
928 };
929 use alloc::{sync::Arc, vec::Vec};
930 use parking_lot::Mutex;
931 use rusqlite::{Connection, OpenFlags};
932 use std::{boxed::Box, io::Write, println};
933
934 fn log_handler(_: i32, arg2: &str) {
935 println!("{arg2}");
936 }
937
938 #[test]
939 fn sanity() -> Result<(), Box<dyn std::error::Error>> {
940 unsafe {
941 rusqlite::trace::config_log(Some(log_handler)).unwrap();
942 }
943
944 struct H {}
945 impl Hooks for H {
946 fn open(&mut self, path: &Option<&str>, opts: &OpenOpts) {
947 let path = path.unwrap();
948 if path == "main.db" {
949 assert!(!opts.delete_on_close());
950 assert_eq!(opts.kind(), OpenKind::MainDb);
951 assert_eq!(
952 opts.mode(),
953 OpenMode::ReadWrite { create: CreateMode::Create }
954 );
955 } else if path == "main.db-journal" {
956 assert!(!opts.delete_on_close());
957 assert_eq!(opts.kind(), OpenKind::MainJournal);
958 assert_eq!(
959 opts.mode(),
960 OpenMode::ReadWrite { create: CreateMode::Create }
961 );
962 } else {
963 panic!("unexpected path: {}", path);
964 }
965 }
966 }
967
968 let shared = Arc::new(Mutex::new(MockState::new(Box::new(H {}))));
969 let vfs = MockVfs::new(shared.clone());
970 let logger = register_static(
971 CString::new("mock").unwrap(),
972 vfs,
973 RegisterOpts { make_default: true },
974 )
975 .map_err(|_| "failed to register vfs")?;
976
977 shared.lock().setup_logger(logger);
979
980 let conn = Connection::open_with_flags_and_vfs(
982 "main.db",
983 OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE,
984 "mock",
985 )?;
986
987 conn.execute("create table t (val int)", [])?;
988 conn.execute("insert into t (val) values (1)", [])?;
989 conn.execute("insert into t (val) values (2)", [])?;
990
991 conn.execute("pragma mock_test", [])?;
992
993 let n: i64 = conn.query_row("select sum(val) from t", [], |row| row.get(0))?;
994 assert_eq!(n, 3);
995
996 conn.execute("create table b (data blob)", [])?;
998 println!("inserting zero blob");
999 conn.execute("insert into b values (zeroblob(8192))", [])?;
1000 let rowid = conn.last_insert_rowid();
1001 let mut blob = conn.blob_open(rusqlite::MAIN_DB, "b", "data", rowid, false)?;
1002
1003 println!("writing to blob");
1005 let n = blob.write(b"hello")?;
1006 assert_eq!(n, 5);
1007
1008 blob.close()?;
1009
1010 let mut stmt = conn.prepare("select data from b")?;
1012 let mut rows = stmt.query([])?;
1013 while let Some(row) = rows.next()? {
1014 let data: Vec<u8> = row.get(0)?;
1015 assert_eq!(&data[0..5], b"hello");
1016 }
1017 drop(rows);
1018 drop(stmt);
1019
1020 conn.close().expect("failed to close connection");
1021
1022 Ok(())
1023 }
1024}