fix: transactional sql migration

This commit is contained in:
kaichaosun 2026-03-02 11:42:21 +08:00
parent c3e5f361bb
commit 030ab475be
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
3 changed files with 17 additions and 8 deletions

View File

@ -25,8 +25,8 @@ impl ChatStorage {
}
/// Applies all migrations and returns the storage instance.
fn run_migrations(db: SqliteDb) -> Result<Self, StorageError> {
migrations::apply_migrations(db.connection())?;
fn run_migrations(mut db: SqliteDb) -> Result<Self, StorageError> {
migrations::apply_migrations(db.connection_mut())?;
Ok(Self { db })
}

View File

@ -1,6 +1,7 @@
//! Database migrations module.
//!
//! SQL migrations are embedded at compile time and applied in order.
//! Each migration is applied atomically within a transaction.
use storage::{Connection, StorageError};
@ -13,8 +14,9 @@ pub fn get_migrations() -> Vec<(&'static str, &'static str)> {
}
/// Applies all migrations to the database.
///
/// Uses a simple version tracking table to avoid re-running migrations.
pub fn apply_migrations(conn: &Connection) -> Result<(), StorageError> {
pub fn apply_migrations(conn: &mut Connection) -> Result<(), StorageError> {
// Create migrations tracking table if it doesn't exist
conn.execute_batch(
"CREATE TABLE IF NOT EXISTS _migrations (
@ -32,11 +34,11 @@ pub fn apply_migrations(conn: &Connection) -> Result<(), StorageError> {
)?;
if !already_applied {
// Apply migration
conn.execute_batch(sql)?;
// Record migration
conn.execute("INSERT INTO _migrations (name) VALUES (?1)", [name])?;
// Apply migration and record it atomically in a transaction
let tx = conn.transaction()?;
tx.execute_batch(sql)?;
tx.execute("INSERT INTO _migrations (name) VALUES (?1)", [name])?;
tx.commit()?;
}
}

View File

@ -66,6 +66,13 @@ impl SqliteDb {
&self.conn
}
/// Returns a mutable reference to the underlying connection.
///
/// Use this for operations that require mutable access, such as transactions.
pub fn connection_mut(&mut self) -> &mut Connection {
&mut self.conn
}
/// Begins a transaction.
pub fn transaction(&mut self) -> Result<rusqlite::Transaction<'_>, StorageError> {
Ok(self.conn.transaction()?)