From 08c54b1f750116aab07b8289fa12f2c37bebac73 Mon Sep 17 00:00:00 2001 From: le king fu Date: Sun, 1 Mar 2026 16:27:02 -0500 Subject: [PATCH] Fix migration repair: update checksums instead of deleting records 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 --- src-tauri/src/commands/profile_commands.rs | 45 +++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src-tauri/src/commands/profile_commands.rs b/src-tauri/src/commands/profile_commands.rs index dd667f1..91f63dc 100644 --- a/src-tauri/src/commands/profile_commands.rs +++ b/src-tauri/src/commands/profile_commands.rs @@ -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 { let app_dir = app @@ -174,7 +174,6 @@ pub fn repair_migrations(app: tauri::AppHandle, db_filename: String) -> Result 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 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) }