Skip to main content

engram_core/migrations/
mod.rs

1//! Idempotent startup migrations gated by `schema_meta(key, value)`.
2
3pub mod embedding_model_v1;
4pub mod feedback_query_id_v1;
5pub mod fts_tokenizer_v1;
6pub mod tags_format_v1;
7
8use engram_storage::Database;
9
10use crate::error::CoreError;
11
12pub use embedding_model_v1::EMBEDDING_MODEL_KEY;
13pub use feedback_query_id_v1::{FEEDBACK_QUERY_ID_KEY, FEEDBACK_QUERY_ID_TARGET};
14pub use fts_tokenizer_v1::{FTS_TOKENIZER_KEY, FTS_TOKENIZER_TARGET};
15pub use tags_format_v1::{TAGS_FORMAT_KEY, TAGS_FORMAT_TARGET_VALUE, TagsFormatV1Stats};
16
17const ENV_DRY_RUN: &str = "ENGRAM_MIGRATIONS_DRY_RUN";
18const ENV_TAGS_STRICT: &str = "ENGRAM_TAGS_MIGRATION_STRICT";
19
20#[derive(Debug, Default, Clone, PartialEq, Eq)]
21pub struct MigrationReport {
22    pub tags_format_v1: Option<TagsFormatV1Stats>,
23    pub feedback_query_id_v1_applied: bool,
24    pub fts_tokenizer_v1_applied: bool,
25}
26
27pub fn run_pending(database: &Database) -> Result<MigrationReport, CoreError> {
28    let dry_run = std::env::var(ENV_DRY_RUN).is_ok();
29    let strict = std::env::var(ENV_TAGS_STRICT).is_ok();
30    let tags_format_v1 = tags_format_v1::run(database, dry_run, strict)?;
31    let feedback_query_id_v1_applied = feedback_query_id_v1::run(database)?;
32    let fts_tokenizer_v1_applied = fts_tokenizer_v1::run(database)?;
33    Ok(MigrationReport {
34        tags_format_v1,
35        feedback_query_id_v1_applied,
36        fts_tokenizer_v1_applied,
37    })
38}
39
40pub(crate) fn read_meta(database: &Database, key: &str) -> Result<Option<String>, CoreError> {
41    use rusqlite::OptionalExtension;
42    database
43        .connection()
44        .query_row(
45            "SELECT value FROM schema_meta WHERE key = ?1",
46            [key],
47            |row| row.get::<_, String>(0),
48        )
49        .optional()
50        .map_err(|error| CoreError::MigrationFailed(format!("schema_meta read: {error}")))
51}
52
53pub(crate) fn write_meta(database: &Database, key: &str, value: &str) -> Result<(), CoreError> {
54    database
55        .connection()
56        .execute(
57            "INSERT OR REPLACE INTO schema_meta(key, value) VALUES (?1, ?2)",
58            [key, value],
59        )
60        .map_err(|error| CoreError::MigrationFailed(format!("schema_meta write: {error}")))?;
61    Ok(())
62}