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