khive_db/backend/
code_map.rs1use std::path::Path;
4use std::sync::atomic::AtomicUsize;
5use std::sync::Arc;
6
7use super::{StorageBackend, StoreSchemaGate};
8use crate::error::SqliteError;
9use crate::pool::{ConnectionPool, PoolConfig};
10use crate::stores::blob::DatabaseGcOwnerGuard;
11use rusqlite::{Connection, OpenFlags};
12
13impl StorageBackend {
14 #[cfg(any(test, feature = "test-support"))]
15 #[doc(hidden)]
16 pub fn active_code_map_handles_for_test(path: impl AsRef<Path>) -> usize {
17 crate::code_map_vfs::active_main_handle_count_for(path.as_ref())
18 }
19
20 pub fn open_code_map_read_only(
23 path: impl AsRef<Path>,
24 expected_identity: crate::file_identity::DatabaseFileIdentity,
25 protected_databases: &[std::path::PathBuf],
26 ) -> Result<Connection, SqliteError> {
27 crate::extension::ensure_extensions_loaded();
28 let path = path.as_ref().to_path_buf();
29 let protected = protected_databases
30 .iter()
31 .map(|path| crate::code_map_vfs::ProductionBase {
32 path: path.clone(),
33 kind: crate::code_map_vfs::ProductionKind::Main,
34 })
35 .collect();
36 let vfs = crate::code_map_vfs::register_read_only_rollback(
37 path.clone(),
38 expected_identity,
39 protected,
40 )
41 .map_err(|error| SqliteError::InvalidData(error.to_string()))?;
42 let connection = crate::code_map_vfs::naming_refusal(&vfs, || {
43 Connection::open_with_flags_and_vfs(
44 &path,
45 OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NO_MUTEX,
46 vfs.as_str(),
47 )
48 .map_err(Into::into)
49 })?;
50 connection.pragma_update(None, "query_only", true)?;
51 Ok(connection)
52 }
53
54 pub fn sqlite_code_map(
61 path: impl AsRef<Path>,
62 protected_main: &[std::path::PathBuf],
63 protected_events: &[std::path::PathBuf],
64 ) -> Result<Self, SqliteError> {
65 let policy = crate::migrations::MigrationWritePolicy::from_environment()?;
66 Self::sqlite_code_map_with_policies(
67 path,
68 protected_main,
69 protected_events,
70 policy.disk_guard_config(),
71 policy.volume_lock_dir().to_path_buf(),
72 )
73 }
74
75 pub fn sqlite_code_map_with_policies(
78 path: impl AsRef<Path>,
79 protected_main: &[std::path::PathBuf],
80 protected_events: &[std::path::PathBuf],
81 disk_guard_config: crate::EffectiveDiskGuardConfig,
82 volume_lock_dir: std::path::PathBuf,
83 ) -> Result<Self, SqliteError> {
84 let policy =
85 crate::migrations::MigrationWritePolicy::new(disk_guard_config, volume_lock_dir)?;
86 crate::extension::ensure_extensions_loaded();
87 let resolved = path.as_ref().to_path_buf();
88 let protected: Vec<_> = protected_main
89 .iter()
90 .map(|path| crate::code_map_vfs::ProductionBase {
91 path: path.clone(),
92 kind: crate::code_map_vfs::ProductionKind::Main,
93 })
94 .chain(
95 protected_events
96 .iter()
97 .map(|path| crate::code_map_vfs::ProductionBase {
98 path: path.clone(),
99 kind: crate::code_map_vfs::ProductionKind::Events,
100 }),
101 )
102 .collect();
103 crate::code_map_vfs::prepare_rollback_target(resolved.clone(), protected.clone()).map_err(
104 |error| {
105 let detail = if error.is_busy() {
106 format!("{error}; retry after other SQLite clients release the target")
107 } else {
108 error.to_string()
109 };
110 SqliteError::InvalidData(detail)
111 },
112 )?;
113 let vfs_name = crate::code_map_vfs::register_rollback(resolved.clone(), protected)
114 .map_err(|error| SqliteError::InvalidData(error.to_string()))?;
115 let pool = ConnectionPool::new(PoolConfig {
116 path: Some(resolved.clone()),
117 code_map_vfs: Some(vfs_name),
118 wal_mode: false,
119 disk_guard_config: Some(policy.disk_guard_config()),
120 volume_lock_dir: Some(policy.volume_lock_dir().to_path_buf()),
121 ..PoolConfig::default()
122 })?;
123 Ok(Self {
124 pool: Arc::new(pool),
125 is_file_backed: true,
126 path: Some(resolved),
127 vector_tables_ready: Default::default(),
128 text_tables_ready: Default::default(),
129 notes_seq_repair_runs: AtomicUsize::new(0),
130 store_schemas: std::array::from_fn(|_| Arc::new(StoreSchemaGate::default())),
131 })
132 }
133
134 pub(super) fn run_core_migrations(
137 &self,
138 owner: &DatabaseGcOwnerGuard,
139 ) -> Result<u32, SqliteError> {
140 let admission = self.pool.write_admission();
141 let run = || {
142 crate::migrations::run_migrations_with_database_gc_owner(
143 &mut self.pool.migration_transactions(),
144 owner,
145 &admission,
146 )
147 };
148 match self.pool.config().code_map_vfs.as_deref() {
149 Some(vfs) => crate::code_map_vfs::naming_refusal(vfs, run),
152 None => run(),
153 }
154 }
155}