Fix migration repair: update checksums instead of deleting records
Some checks are pending
Release / build-and-release (push) Waiting to run
Some checks are pending
Release / build-and-release (push) Waiting to run
The previous approach deleted migration records to force re-application, but this is dangerous for migration 2 which DELETEs all categories and keywords before re-inserting them, wiping user customizations. Now computes the expected SHA-384 checksum (matching sqlx) and updates the stored checksum in _sqlx_migrations, so the migration is recognized as already applied without being re-run. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
efb922eb0e
commit
08c54b1f75
1 changed files with 32 additions and 13 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use rand::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sha2::{Digest, Sha256, Sha384};
|
||||
use std::fs;
|
||||
use tauri::Manager;
|
||||
|
||||
|
|
@ -157,8 +157,8 @@ fn hex_encode(bytes: &[u8]) -> String {
|
|||
}
|
||||
|
||||
/// Repair migration checksums for a profile database.
|
||||
/// Deletes migration records that have mismatched checksums so they can be re-applied.
|
||||
/// This fixes the "migration X was previously applied but has been modified" error.
|
||||
/// Updates stored checksums to match current migration SQL, avoiding re-application
|
||||
/// of destructive migrations (e.g., migration 2 which DELETEs categories/keywords).
|
||||
#[tauri::command]
|
||||
pub fn repair_migrations(app: tauri::AppHandle, db_filename: String) -> Result<bool, String> {
|
||||
let app_dir = app
|
||||
|
|
@ -174,7 +174,6 @@ pub fn repair_migrations(app: tauri::AppHandle, db_filename: String) -> Result<b
|
|||
let conn = rusqlite::Connection::open(&db_path)
|
||||
.map_err(|e| format!("Cannot open database: {}", e))?;
|
||||
|
||||
// Check if _sqlx_migrations table exists
|
||||
let table_exists: bool = conn
|
||||
.query_row(
|
||||
"SELECT COUNT(*) > 0 FROM sqlite_master WHERE type='table' AND name='_sqlx_migrations'",
|
||||
|
|
@ -187,14 +186,34 @@ pub fn repair_migrations(app: tauri::AppHandle, db_filename: String) -> Result<b
|
|||
return Ok(false);
|
||||
}
|
||||
|
||||
// Delete migration 1 record so it can be cleanly re-applied
|
||||
// Migration 1 is idempotent (CREATE IF NOT EXISTS + INSERT OR IGNORE)
|
||||
let deleted = conn
|
||||
.execute(
|
||||
"DELETE FROM _sqlx_migrations WHERE version = 1",
|
||||
[],
|
||||
)
|
||||
.map_err(|e| format!("Cannot repair migrations: {}", e))?;
|
||||
// Current migration SQL — must match the vec in lib.rs
|
||||
let migrations: &[(i64, &str)] = &[
|
||||
(1, database::SCHEMA),
|
||||
(2, database::SEED_CATEGORIES),
|
||||
];
|
||||
|
||||
Ok(deleted > 0)
|
||||
let mut repaired = false;
|
||||
for (version, sql) in migrations {
|
||||
let expected_checksum = Sha384::digest(sql.as_bytes()).to_vec();
|
||||
|
||||
// Check if this migration exists with a different checksum
|
||||
let needs_repair: bool = conn
|
||||
.query_row(
|
||||
"SELECT COUNT(*) > 0 FROM _sqlx_migrations WHERE version = ?1 AND checksum != ?2",
|
||||
rusqlite::params![version, expected_checksum],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.unwrap_or(false);
|
||||
|
||||
if needs_repair {
|
||||
conn.execute(
|
||||
"UPDATE _sqlx_migrations SET checksum = ?1 WHERE version = ?2",
|
||||
rusqlite::params![expected_checksum, version],
|
||||
)
|
||||
.map_err(|e| format!("Cannot repair migration {}: {}", version, e))?;
|
||||
repaired = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(repaired)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue