Skip to main content

innate_core/storage/
raw.rs

1use super::*;
2
3impl Storage {
4    pub fn attach_shared(&self, path: &str, alias: &str) -> Result<()> {
5        self.conn.execute_batch(&format!(
6            "ATTACH DATABASE '{}' AS '{alias}'",
7            path.replace('\'', "''")
8        ))?;
9        Ok(())
10    }
11
12    pub fn lib_id(&self) -> Result<String> {
13        Ok(self
14            .get_meta("lib_id")?
15            .unwrap_or_else(|| "unknown".to_string()))
16    }
17
18    // ------------------------------------------------------------------
19    // Generic helpers
20    // ------------------------------------------------------------------
21
22    pub(in crate::storage) fn query_json<P: rusqlite::Params>(
23        &self,
24        sql: &str,
25        p: P,
26    ) -> Result<Vec<Value>> {
27        let mut stmt = self.conn.prepare(sql)?;
28        let names: Vec<String> = stmt.column_names().iter().map(|s| s.to_string()).collect();
29        let rows = stmt.query_map(p, |r| row_to_json_with_names(r, &names))?;
30        // Fail-closed: a row that fails to decode aborts the query rather than
31        // silently vanishing from the result set.
32        Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
33    }
34
35    /// Execute a parameterised statement (not batch); returns rows-affected count.
36    pub(crate) fn conn_execute<P: rusqlite::Params>(&self, sql: &str, p: P) -> Result<()> {
37        self.conn.execute(sql, p)?;
38        Ok(())
39    }
40
41    pub(crate) fn conn_execute_count<P: rusqlite::Params>(&self, sql: &str, p: P) -> Result<usize> {
42        Ok(self.conn.execute(sql, p)?)
43    }
44
45    /// On-disk size of the main database file in bytes (`page_count * page_size`).
46    pub fn db_size_bytes(&self) -> Result<i64> {
47        let page_count: i64 = self.conn.query_row("PRAGMA page_count", [], |r| r.get(0))?;
48        let page_size: i64 = self.conn.query_row("PRAGMA page_size", [], |r| r.get(0))?;
49        Ok(page_count * page_size)
50    }
51
52    /// Reclaim space freed by curate's log compaction and trace purges: checkpoint
53    /// and truncate the WAL, then VACUUM to return free pages to the OS. Returns
54    /// `(before_bytes, after_bytes)`. Must run outside any transaction.
55    pub fn vacuum(&self) -> Result<(i64, i64)> {
56        let before = self.db_size_bytes()?;
57        self.conn.execute_batch("PRAGMA wal_checkpoint(TRUNCATE);")?;
58        self.conn.execute_batch("VACUUM;")?;
59        self.conn.execute_batch("PRAGMA wal_checkpoint(TRUNCATE);")?;
60        // VACUUM rebuilds the file but preserves all rows; in-memory vector caches
61        // stay valid (same data), so they are intentionally left untouched.
62        let after = self.db_size_bytes()?;
63        Ok((before, after))
64    }
65}