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