Skip to main content

sqlite_graphrag/storage/
connection.rs

1use crate::errors::AppError;
2use crate::pragmas::apply_connection_pragmas;
3use rusqlite::Connection;
4use sqlite_vec::sqlite3_vec_init;
5use std::path::Path;
6
7/// Register sqlite-vec GLOBALLY before any connection is opened.
8/// Must be called once at program start.
9pub fn register_vec_extension() {
10    #[allow(clippy::missing_transmute_annotations)]
11    unsafe {
12        rusqlite::ffi::sqlite3_auto_extension(Some(std::mem::transmute(
13            sqlite3_vec_init as *const (),
14        )));
15    }
16}
17
18pub fn open_rw(path: &Path) -> Result<Connection, AppError> {
19    let conn = Connection::open(path)?;
20    apply_connection_pragmas(&conn)?;
21    apply_secure_permissions(path);
22    Ok(conn)
23}
24
25pub fn ensure_schema(conn: &mut Connection) -> Result<(), AppError> {
26    crate::migrations::runner()
27        .run(conn)
28        .map_err(|e| AppError::Internal(anyhow::anyhow!("migration failed: {e}")))?;
29    conn.execute_batch(&format!(
30        "PRAGMA user_version = {};",
31        crate::constants::SCHEMA_USER_VERSION
32    ))?;
33    Ok(())
34}
35
36/// Aplica permissões 600 (owner read/write only) ao arquivo SQLite e aos arquivos WAL/SHM
37/// associados no Unix para evitar vazamento de memórias privadas em diretórios compartilhados
38/// (ex: /tmp multi-user, Dropbox, NFS). No-op em Windows. Falhas silenciosas para não
39/// bloquear operação quando o processo não é o dono do arquivo (ex: montagem read-only).
40#[allow(unused_variables)]
41fn apply_secure_permissions(path: &Path) {
42    #[cfg(unix)]
43    {
44        use std::os::unix::fs::PermissionsExt;
45        let candidates = [
46            path.to_path_buf(),
47            path.with_extension(format!(
48                "{}-wal",
49                path.extension()
50                    .and_then(|e| e.to_str())
51                    .unwrap_or("sqlite")
52            )),
53            path.with_extension(format!(
54                "{}-shm",
55                path.extension()
56                    .and_then(|e| e.to_str())
57                    .unwrap_or("sqlite")
58            )),
59        ];
60        for file in candidates.iter() {
61            if file.exists() {
62                if let Ok(meta) = std::fs::metadata(file) {
63                    let mut perms = meta.permissions();
64                    perms.set_mode(0o600);
65                    let _ = std::fs::set_permissions(file, perms);
66                }
67            }
68        }
69    }
70}
71
72pub fn open_ro(path: &Path) -> Result<Connection, AppError> {
73    let conn = Connection::open_with_flags(
74        path,
75        rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_URI,
76    )?;
77    conn.execute_batch("PRAGMA foreign_keys = ON;")?;
78    Ok(conn)
79}