sqlite_wasm_rs/vfs/
memory.rs1use crate::libsqlite3::*;
28use crate::vfs::utils::{
29 check_import_db, ImportDbError, MemChunksFile, SQLiteIoMethods, SQLiteVfs, SQLiteVfsFile,
30 VfsAppData, VfsError, VfsFile, VfsResult, VfsStore,
31};
32
33use std::cell::RefCell;
34use std::collections::HashMap;
35
36type Result<T> = std::result::Result<T, MemVfsError>;
37
38pub(crate) enum MemFile {
39 Main(MemChunksFile),
40 Temp(MemChunksFile),
41}
42
43impl MemFile {
44 fn new(flags: i32) -> Self {
45 if flags & SQLITE_OPEN_MAIN_DB == 0 {
46 Self::Temp(MemChunksFile::default())
47 } else {
48 Self::Main(MemChunksFile::waiting_for_write())
49 }
50 }
51
52 fn file(&self) -> &MemChunksFile {
53 let (MemFile::Main(file) | MemFile::Temp(file)) = self;
54 file
55 }
56
57 fn file_mut(&mut self) -> &mut MemChunksFile {
58 let (MemFile::Main(file) | MemFile::Temp(file)) = self;
59 file
60 }
61}
62
63impl VfsFile for MemFile {
64 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool> {
65 self.file().read(buf, offset)
66 }
67
68 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
69 self.file_mut().write(buf, offset)
70 }
71
72 fn truncate(&mut self, size: usize) -> VfsResult<()> {
73 self.file_mut().truncate(size)
74 }
75
76 fn flush(&mut self) -> VfsResult<()> {
77 self.file_mut().flush()
78 }
79
80 fn size(&self) -> VfsResult<usize> {
81 self.file().size()
82 }
83}
84
85type MemAppData = RefCell<HashMap<String, MemFile>>;
86
87struct MemStore;
88
89impl VfsStore<MemFile, MemAppData> for MemStore {
90 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()> {
91 let app_data = unsafe { Self::app_data(vfs) };
92 app_data
93 .borrow_mut()
94 .insert(file.into(), MemFile::new(flags));
95 Ok(())
96 }
97
98 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool> {
99 let app_data = unsafe { Self::app_data(vfs) };
100 Ok(app_data.borrow().contains_key(file))
101 }
102
103 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()> {
104 let app_data = unsafe { Self::app_data(vfs) };
105 if app_data.borrow_mut().remove(file).is_none() {
106 return Err(VfsError::new(
107 SQLITE_IOERR_DELETE,
108 format!("{file} not found"),
109 ));
110 }
111 Ok(())
112 }
113
114 fn with_file<F: Fn(&MemFile) -> VfsResult<i32>>(
115 vfs_file: &SQLiteVfsFile,
116 f: F,
117 ) -> VfsResult<i32> {
118 let name = unsafe { vfs_file.name() };
119 let app_data = unsafe { Self::app_data(vfs_file.vfs) };
120 match app_data.borrow().get(name) {
121 Some(file) => f(file),
122 None => Err(VfsError::new(SQLITE_IOERR, format!("{name} not found"))),
123 }
124 }
125
126 fn with_file_mut<F: Fn(&mut MemFile) -> VfsResult<i32>>(
127 vfs_file: &SQLiteVfsFile,
128 f: F,
129 ) -> VfsResult<i32> {
130 let name = unsafe { vfs_file.name() };
131 let app_data = unsafe { Self::app_data(vfs_file.vfs) };
132 match app_data.borrow_mut().get_mut(name) {
133 Some(file) => f(file),
134 None => Err(VfsError::new(SQLITE_IOERR, format!("{name} not found"))),
135 }
136 }
137}
138
139struct MemIoMethods;
140
141impl SQLiteIoMethods for MemIoMethods {
142 type File = MemFile;
143 type AppData = MemAppData;
144 type Store = MemStore;
145
146 const VERSION: ::std::os::raw::c_int = 1;
147}
148
149struct MemVfs;
150
151impl SQLiteVfs<MemIoMethods> for MemVfs {
152 const VERSION: ::std::os::raw::c_int = 1;
153}
154
155#[derive(thiserror::Error, Debug)]
156pub enum MemVfsError {
157 #[error(transparent)]
158 ImportDb(#[from] ImportDbError),
159 #[error("Generic error: {0}")]
160 Generic(String),
161}
162
163pub struct MemVfsUtil(&'static VfsAppData<MemAppData>);
165
166unsafe impl Send for MemVfsUtil {}
167
168unsafe impl Sync for MemVfsUtil {}
169
170impl Default for MemVfsUtil {
171 fn default() -> Self {
172 MemVfsUtil::new()
173 }
174}
175
176impl MemVfsUtil {
177 pub fn new() -> Self {
179 MemVfsUtil(unsafe { install() })
180 }
181}
182
183impl MemVfsUtil {
184 fn import_db_unchecked_impl(
185 &self,
186 filename: &str,
187 bytes: &[u8],
188 page_size: usize,
189 clear_wal: bool,
190 ) -> Result<()> {
191 if self.exists(filename) {
192 return Err(MemVfsError::Generic(format!(
193 "{filename} file already exists"
194 )));
195 }
196
197 self.0.borrow_mut().insert(filename.into(), {
198 let mut file = MemFile::Main(MemChunksFile::new(page_size));
199 file.write(bytes, 0).unwrap();
200 if clear_wal {
201 file.write(&[1, 1], 18).unwrap();
202 }
203 file
204 });
205
206 Ok(())
207 }
208
209 pub fn import_db(&self, filename: &str, bytes: &[u8]) -> Result<()> {
217 let page_size = check_import_db(bytes)?;
218 self.import_db_unchecked_impl(filename, bytes, page_size, true)
219 }
220
221 pub fn import_db_unchecked(
223 &self,
224 filename: &str,
225 bytes: &[u8],
226 page_size: usize,
227 ) -> Result<()> {
228 self.import_db_unchecked_impl(filename, bytes, page_size, false)
229 }
230
231 pub fn export_db(&self, filename: &str) -> Result<Vec<u8>> {
233 let name2file = self.0.borrow();
234
235 if let Some(file) = name2file.get(filename) {
236 let file_size = file.size().unwrap();
237 let mut ret = vec![0; file_size];
238 file.read(&mut ret, 0).unwrap();
239 Ok(ret)
240 } else {
241 Err(MemVfsError::Generic(
242 "The file to be exported does not exist".into(),
243 ))
244 }
245 }
246
247 pub fn delete_db(&self, filename: &str) {
249 self.0.borrow_mut().remove(filename);
250 }
251
252 pub fn clear_all(&self) {
254 std::mem::take(&mut *self.0.borrow_mut());
255 }
256
257 pub fn exists(&self, filename: &str) -> bool {
259 self.0.borrow().contains_key(filename)
260 }
261
262 pub fn list(&self) -> Vec<String> {
264 self.0.borrow().keys().cloned().collect()
265 }
266
267 pub fn count(&self) -> usize {
269 self.0.borrow().len()
270 }
271}
272
273pub(crate) unsafe fn install() -> &'static VfsAppData<MemAppData> {
274 let vfs_name = c"memvfs";
275 let vfs = sqlite3_vfs_find(vfs_name.as_ptr());
276
277 let vfs = if vfs.is_null() {
278 let vfs = Box::leak(Box::new(MemVfs::vfs(
279 vfs_name.as_ptr(),
280 VfsAppData::new(MemAppData::default()).leak(),
281 )));
282 assert_eq!(
283 sqlite3_vfs_register(vfs, 1),
284 SQLITE_OK,
285 "failed to register memvfs"
286 );
287 vfs as *mut sqlite3_vfs
288 } else {
289 vfs
290 };
291
292 MemStore::app_data(vfs)
293}
294
295#[cfg(test)]
296mod tests {
297 use crate::{
298 mem_vfs::{MemAppData, MemFile, MemStore},
299 utils::{test_suite::test_vfs_store, VfsAppData},
300 };
301 use wasm_bindgen_test::wasm_bindgen_test;
302
303 #[wasm_bindgen_test]
304 fn test_memory_vfs_store() {
305 test_vfs_store::<MemAppData, MemFile, MemStore>(VfsAppData::new(MemAppData::default()))
306 .unwrap();
307 }
308}