Skip to main content

reifydb_sqlite/
connection.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::fs;
5
6use rusqlite::{Connection, OpenFlags as SqliteOpenFlags};
7
8use crate::{DbPath, OpenFlags, error::SqliteError};
9
10/// Connect to a SQLite database.
11pub fn connect(path: &DbPath, flags: SqliteOpenFlags) -> Result<Connection, SqliteError> {
12	match path {
13		DbPath::File(path) => {
14			let path_str = path.to_string_lossy();
15			let is_uri = path_str.contains(':');
16
17			if is_uri {
18				let uri_flags = flags | SqliteOpenFlags::SQLITE_OPEN_URI;
19				let path_string = path_str.to_string();
20				Connection::open_with_flags(path_string, uri_flags).map_err(|source| {
21					SqliteError::Connect {
22						path: path_str.to_string(),
23						source,
24					}
25				})
26			} else {
27				let path_clone = path.clone();
28				Connection::open_with_flags(path_clone, flags).map_err(|source| SqliteError::Connect {
29					path: path.display().to_string(),
30					source,
31				})
32			}
33		}
34		DbPath::Tmpfs(path) => {
35			let path_clone = path.clone();
36			Connection::open_with_flags(path_clone, flags).map_err(|source| SqliteError::Connect {
37				path: path.display().to_string(),
38				source,
39			})
40		}
41		DbPath::Memory(path) => {
42			let path_clone = path.clone();
43			Connection::open_with_flags(path_clone, flags).map_err(|source| SqliteError::Connect {
44				path: path.display().to_string(),
45				source,
46			})
47		}
48	}
49}
50
51/// Resolve the database path, creating directories as needed.
52///
53/// `default_filename` is appended when the caller passes a `DbPath::File`
54/// that points at a directory (no extension), so each subsystem can keep its
55/// own default (e.g. `"cdc.db"` or `"primitive.db"`).
56pub fn resolve_db_path(db_path: DbPath, default_filename: &str) -> DbPath {
57	match db_path {
58		DbPath::Tmpfs(path) => {
59			if let Some(parent) = path.parent() {
60				fs::create_dir_all(parent).ok();
61			}
62			DbPath::Tmpfs(path)
63		}
64		DbPath::Memory(path) => {
65			if let Some(parent) = path.parent() {
66				fs::create_dir_all(parent).ok();
67			}
68			DbPath::Memory(path)
69		}
70		DbPath::File(config_path) => {
71			let is_uri = config_path.to_string_lossy().contains(':');
72			if is_uri {
73				DbPath::File(config_path)
74			} else if config_path.extension().is_none() {
75				fs::create_dir_all(&config_path).ok();
76				DbPath::File(config_path.join(default_filename))
77			} else {
78				if let Some(parent) = config_path.parent() {
79					fs::create_dir_all(parent).ok();
80				}
81				DbPath::File(config_path)
82			}
83		}
84	}
85}
86
87/// Convert our `OpenFlags` to `rusqlite::OpenFlags`.
88pub fn convert_flags(flags: &OpenFlags) -> SqliteOpenFlags {
89	let mut rusqlite_flags = SqliteOpenFlags::empty();
90
91	if flags.read_write {
92		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_READ_WRITE;
93	}
94	if flags.create {
95		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_CREATE;
96	}
97	if flags.full_mutex {
98		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_FULL_MUTEX;
99	}
100	if flags.no_mutex {
101		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_NO_MUTEX;
102	}
103	if flags.shared_cache {
104		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_SHARED_CACHE;
105	}
106	if flags.private_cache {
107		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_PRIVATE_CACHE;
108	}
109	if flags.uri {
110		rusqlite_flags |= SqliteOpenFlags::SQLITE_OPEN_URI;
111	}
112
113	rusqlite_flags
114}