1#[cfg(not(target_os = "linux"))]
5use std::env;
6use std::path::{Path, PathBuf};
7
8use uuid::Uuid;
9
10#[cfg(not(target_arch = "wasm32"))]
11pub mod connection;
12#[cfg(not(target_arch = "wasm32"))]
13pub mod error;
14#[cfg(not(target_arch = "wasm32"))]
15pub mod pragma;
16
17#[derive(Debug, Clone, Eq, PartialEq)]
19pub enum DbPath {
20 File(PathBuf),
22 Tmpfs(PathBuf),
24 Memory(PathBuf),
26}
27
28fn memory_dir() -> PathBuf {
29 #[cfg(target_os = "linux")]
30 {
31 PathBuf::from("/dev/shm")
32 }
33 #[cfg(not(target_os = "linux"))]
34 {
35 env::temp_dir()
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct SqliteConfig {
42 pub path: DbPath,
43 pub flags: OpenFlags,
44 pub journal_mode: JournalMode,
45 pub synchronous_mode: SynchronousMode,
46 pub temp_store: TempStore,
47 pub cache_size: u32,
48 pub wal_autocheckpoint: u32,
49 pub page_size: u32,
50 pub mmap_size: u64,
51}
52
53impl SqliteConfig {
54 pub fn new<P: AsRef<Path>>(path: P) -> Self {
61 Self {
62 path: DbPath::File(path.as_ref().to_path_buf()),
63 flags: OpenFlags::default(),
64 journal_mode: JournalMode::Wal,
65 synchronous_mode: SynchronousMode::Normal,
66 temp_store: TempStore::Memory,
67 cache_size: 2000,
68 wal_autocheckpoint: 1000,
69 page_size: 4096,
70 mmap_size: 64 * 1024 * 1024,
71 }
72 }
73
74 pub fn safe<P: AsRef<Path>>(path: P) -> Self {
80 Self {
81 path: DbPath::File(path.as_ref().to_path_buf()),
82 flags: OpenFlags::default(),
83 journal_mode: JournalMode::Wal,
84 synchronous_mode: SynchronousMode::Full,
85 temp_store: TempStore::File,
86 cache_size: 2000,
87 wal_autocheckpoint: 1000,
88 page_size: 4096,
89 mmap_size: 0,
90 }
91 }
92
93 pub fn fast<P: AsRef<Path>>(path: P) -> Self {
100 Self {
101 path: DbPath::File(path.as_ref().to_path_buf()),
102 flags: OpenFlags::default(),
103 journal_mode: JournalMode::Wal,
104 synchronous_mode: SynchronousMode::Off,
105 temp_store: TempStore::Memory,
106 cache_size: 10000,
107 wal_autocheckpoint: 10000,
108 page_size: 16384,
109 mmap_size: 256 * 1024 * 1024,
110 }
111 }
112
113 pub fn tmpfs() -> Self {
118 Self {
119 path: DbPath::Tmpfs(PathBuf::from(format!("/tmp/reifydb_{}.db", Uuid::new_v4()))),
120 flags: OpenFlags::default(),
121 journal_mode: JournalMode::Wal,
122 synchronous_mode: SynchronousMode::Off,
123 temp_store: TempStore::Memory,
124 cache_size: 2000,
125 wal_autocheckpoint: 10000,
126 page_size: 16384,
127 mmap_size: 0,
128 }
129 }
130
131 pub fn in_memory() -> Self {
135 Self {
136 path: DbPath::Memory(memory_dir().join(format!("reifydb_{}.db", Uuid::new_v4()))),
137 flags: OpenFlags::default(),
138 journal_mode: JournalMode::Wal,
139 synchronous_mode: SynchronousMode::Off,
140 temp_store: TempStore::Memory,
141 cache_size: 2000,
142 wal_autocheckpoint: 10000,
143 page_size: 16384,
144 mmap_size: 0,
145 }
146 }
147
148 pub fn test() -> Self {
151 Self {
152 path: DbPath::Memory(memory_dir().join(format!("reifydb_{}.db", Uuid::new_v4()))),
153 flags: OpenFlags::default(),
154 journal_mode: JournalMode::Wal,
155 synchronous_mode: SynchronousMode::Off,
156 temp_store: TempStore::Memory,
157 cache_size: 1000,
158 wal_autocheckpoint: 10000,
159 page_size: 4096,
160 mmap_size: 0,
161 }
162 }
163
164 pub fn path<P: AsRef<Path>>(mut self, path: P) -> Self {
165 self.path = DbPath::File(path.as_ref().to_path_buf());
166 self
167 }
168
169 pub fn flags(mut self, flags: OpenFlags) -> Self {
170 self.flags = flags;
171 self
172 }
173
174 pub fn journal_mode(mut self, mode: JournalMode) -> Self {
175 self.journal_mode = mode;
176 self
177 }
178
179 pub fn synchronous_mode(mut self, mode: SynchronousMode) -> Self {
180 self.synchronous_mode = mode;
181 self
182 }
183
184 pub fn temp_store(mut self, store: TempStore) -> Self {
185 self.temp_store = store;
186 self
187 }
188
189 pub fn cache_size(mut self, size_kb: u32) -> Self {
190 self.cache_size = size_kb;
191 self
192 }
193
194 pub fn wal_autocheckpoint(mut self, pages: u32) -> Self {
195 self.wal_autocheckpoint = pages;
196 self
197 }
198
199 pub fn page_size(mut self, size: u32) -> Self {
203 self.page_size = size;
204 self
205 }
206
207 pub fn mmap_size(mut self, size: u64) -> Self {
209 self.mmap_size = size;
210 self
211 }
212}
213
214impl Default for SqliteConfig {
215 fn default() -> Self {
216 Self::new("reifydb.db")
217 }
218}
219
220#[derive(Debug, Clone)]
222pub struct OpenFlags {
223 pub read_write: bool,
224 pub create: bool,
225 pub full_mutex: bool,
226 pub no_mutex: bool,
227 pub shared_cache: bool,
228 pub private_cache: bool,
229 pub uri: bool,
230}
231
232impl OpenFlags {
233 pub fn new() -> Self {
234 Self::default()
235 }
236
237 pub fn read_write(mut self, enabled: bool) -> Self {
238 self.read_write = enabled;
239 self
240 }
241
242 pub fn create(mut self, enabled: bool) -> Self {
243 self.create = enabled;
244 self
245 }
246
247 pub fn full_mutex(mut self, enabled: bool) -> Self {
248 self.full_mutex = enabled;
249 self.no_mutex = !enabled;
250 self
251 }
252
253 pub fn no_mutex(mut self, enabled: bool) -> Self {
254 self.no_mutex = enabled;
255 self.full_mutex = !enabled;
256 self
257 }
258
259 pub fn shared_cache(mut self, enabled: bool) -> Self {
260 self.shared_cache = enabled;
261 self.private_cache = !enabled;
262 self
263 }
264
265 pub fn private_cache(mut self, enabled: bool) -> Self {
266 self.private_cache = enabled;
267 self.shared_cache = !enabled;
268 self
269 }
270
271 pub fn uri(mut self, enabled: bool) -> Self {
272 self.uri = enabled;
273 self
274 }
275}
276
277impl Default for OpenFlags {
278 fn default() -> Self {
279 Self {
280 read_write: true,
281 create: true,
282 full_mutex: true,
283 no_mutex: false,
284 shared_cache: false,
285 private_cache: false,
286 uri: false,
287 }
288 }
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
293pub enum JournalMode {
294 Delete,
295 Truncate,
296 Persist,
297 Memory,
298 Wal,
299 Off,
300}
301
302impl JournalMode {
303 pub fn as_str(&self) -> &'static str {
304 match self {
305 JournalMode::Delete => "DELETE",
306 JournalMode::Truncate => "TRUNCATE",
307 JournalMode::Persist => "PERSIST",
308 JournalMode::Memory => "MEMORY",
309 JournalMode::Wal => "WAL",
310 JournalMode::Off => "OFF",
311 }
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum SynchronousMode {
318 Off,
319 Normal,
320 Full,
321 Extra,
322}
323
324impl SynchronousMode {
325 pub fn as_str(&self) -> &'static str {
326 match self {
327 SynchronousMode::Off => "OFF",
328 SynchronousMode::Normal => "NORMAL",
329 SynchronousMode::Full => "FULL",
330 SynchronousMode::Extra => "EXTRA",
331 }
332 }
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum TempStore {
338 Default,
339 File,
340 Memory,
341}
342
343impl TempStore {
344 pub fn as_str(&self) -> &'static str {
345 match self {
346 TempStore::Default => "DEFAULT",
347 TempStore::File => "FILE",
348 TempStore::Memory => "MEMORY",
349 }
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use reifydb_testing::tempdir::temp_dir;
356
357 use super::*;
358
359 #[test]
360 fn test_config_fluent_api() {
361 let config = SqliteConfig::new("/tmp/test.reifydb")
362 .journal_mode(JournalMode::Wal)
363 .synchronous_mode(SynchronousMode::Normal)
364 .temp_store(TempStore::Memory)
365 .cache_size(30000)
366 .flags(OpenFlags::new().read_write(true).create(true).full_mutex(true));
367
368 assert_eq!(config.path, DbPath::File(PathBuf::from("/tmp/test.reifydb")));
369 assert_eq!(config.journal_mode, JournalMode::Wal);
370 assert_eq!(config.synchronous_mode, SynchronousMode::Normal);
371 assert_eq!(config.temp_store, TempStore::Memory);
372 assert_eq!(config.cache_size, 30000);
373 assert!(config.flags.read_write);
374 assert!(config.flags.create);
375 assert!(config.flags.full_mutex);
376 }
377
378 #[test]
379 fn test_enum_string_conversion() {
380 assert_eq!(JournalMode::Wal.as_str(), "WAL");
381 assert_eq!(SynchronousMode::Normal.as_str(), "NORMAL");
382 assert_eq!(TempStore::Memory.as_str(), "MEMORY");
383 }
384
385 #[test]
386 fn test_all_journal_modes() {
387 assert_eq!(JournalMode::Delete.as_str(), "DELETE");
388 assert_eq!(JournalMode::Truncate.as_str(), "TRUNCATE");
389 assert_eq!(JournalMode::Persist.as_str(), "PERSIST");
390 assert_eq!(JournalMode::Memory.as_str(), "MEMORY");
391 assert_eq!(JournalMode::Wal.as_str(), "WAL");
392 assert_eq!(JournalMode::Off.as_str(), "OFF");
393 }
394
395 #[test]
396 fn test_all_synchronous_modes() {
397 assert_eq!(SynchronousMode::Off.as_str(), "OFF");
398 assert_eq!(SynchronousMode::Normal.as_str(), "NORMAL");
399 assert_eq!(SynchronousMode::Full.as_str(), "FULL");
400 assert_eq!(SynchronousMode::Extra.as_str(), "EXTRA");
401 }
402
403 #[test]
404 fn test_all_temp_store_modes() {
405 assert_eq!(TempStore::Default.as_str(), "DEFAULT");
406 assert_eq!(TempStore::File.as_str(), "FILE");
407 assert_eq!(TempStore::Memory.as_str(), "MEMORY");
408 }
409
410 #[test]
411 fn test_default_config() {
412 let config = SqliteConfig::default();
413 assert_eq!(config.path, DbPath::File(PathBuf::from("reifydb.db")));
414 assert_eq!(config.journal_mode, JournalMode::Wal);
415 assert_eq!(config.synchronous_mode, SynchronousMode::Normal);
416 assert_eq!(config.temp_store, TempStore::Memory);
417 }
418
419 #[test]
420 fn test_safe_config() {
421 temp_dir(|db_path| {
422 let db_file = db_path.join("safe.reifydb");
423 let config = SqliteConfig::safe(&db_file);
424
425 assert_eq!(config.path, DbPath::File(db_file));
426 assert_eq!(config.journal_mode, JournalMode::Wal);
427 assert_eq!(config.synchronous_mode, SynchronousMode::Full);
428 assert_eq!(config.temp_store, TempStore::File);
429 Ok(())
430 })
431 .expect("test failed");
432 }
433
434 #[test]
435 fn test_fast_config() {
436 temp_dir(|db_path| {
437 let db_file = db_path.join("fast.reifydb");
438 let config = SqliteConfig::fast(&db_file);
439
440 assert_eq!(config.path, DbPath::File(db_file));
441 assert_eq!(config.journal_mode, JournalMode::Wal);
442 assert_eq!(config.synchronous_mode, SynchronousMode::Off);
443 assert_eq!(config.temp_store, TempStore::Memory);
444 Ok(())
445 })
446 .expect("test failed");
447 }
448
449 #[test]
450 fn test_tmpfs_config() {
451 let config = SqliteConfig::tmpfs();
452
453 match config.path {
454 DbPath::Tmpfs(path) => {
455 assert!(path.to_string_lossy().starts_with("/tmp/reifydb_"));
456 assert!(path.to_string_lossy().ends_with(".db"));
457 }
458 _ => panic!("Expected DbPath::Tmpfs variant"),
459 }
460
461 assert_eq!(config.journal_mode, JournalMode::Wal);
462 assert_eq!(config.synchronous_mode, SynchronousMode::Off);
463 assert_eq!(config.temp_store, TempStore::Memory);
464 assert_eq!(config.cache_size, 2000);
465 assert_eq!(config.wal_autocheckpoint, 10000);
466 }
467
468 #[test]
469 fn test_config_chaining() {
470 temp_dir(|db_path| {
471 let db_file = db_path.join("chain.reifydb");
472
473 let config = SqliteConfig::new(&db_file)
474 .journal_mode(JournalMode::Delete)
475 .synchronous_mode(SynchronousMode::Extra)
476 .temp_store(TempStore::File)
477 .flags(OpenFlags::new().read_write(false).create(false).shared_cache(true));
478
479 assert_eq!(config.journal_mode, JournalMode::Delete);
480 assert_eq!(config.synchronous_mode, SynchronousMode::Extra);
481 assert_eq!(config.temp_store, TempStore::File);
482 assert!(!config.flags.read_write);
483 assert!(!config.flags.create);
484 assert!(config.flags.shared_cache);
485 Ok(())
486 })
487 .expect("test failed");
488 }
489
490 #[test]
491 fn test_open_flags_mutex_exclusivity() {
492 let flags = OpenFlags::new().full_mutex(true);
493 assert!(flags.full_mutex);
494 assert!(!flags.no_mutex);
495
496 let flags = OpenFlags::new().no_mutex(true);
497 assert!(!flags.full_mutex);
498 assert!(flags.no_mutex);
499 }
500
501 #[test]
502 fn test_open_flags_cache_exclusivity() {
503 let flags = OpenFlags::new().shared_cache(true);
504 assert!(flags.shared_cache);
505 assert!(!flags.private_cache);
506
507 let flags = OpenFlags::new().private_cache(true);
508 assert!(!flags.shared_cache);
509 assert!(flags.private_cache);
510 }
511
512 #[test]
513 fn test_open_flags_all_combinations() {
514 let flags =
515 OpenFlags::new().read_write(true).create(true).full_mutex(true).shared_cache(true).uri(true);
516
517 assert!(flags.read_write);
518 assert!(flags.create);
519 assert!(flags.full_mutex);
520 assert!(!flags.no_mutex);
521 assert!(flags.shared_cache);
522 assert!(!flags.private_cache);
523 assert!(flags.uri);
524 }
525
526 #[test]
527 fn test_path_handling() {
528 temp_dir(|db_path| {
529 let file_path = db_path.join("test.reifydb");
530 let config = SqliteConfig::new(&file_path);
531 assert_eq!(config.path, DbPath::File(file_path));
532
533 let config = SqliteConfig::new(db_path);
534 assert_eq!(config.path, DbPath::File(db_path.to_path_buf()));
535 Ok(())
536 })
537 .expect("test failed");
538 }
539}