diff --git a/conversations/src/storage/db.rs b/conversations/src/storage/db.rs index 65750e9..c855416 100644 --- a/conversations/src/storage/db.rs +++ b/conversations/src/storage/db.rs @@ -25,8 +25,8 @@ impl ChatStorage { } /// Applies all migrations and returns the storage instance. - fn run_migrations(db: SqliteDb) -> Result { - migrations::apply_migrations(db.connection())?; + fn run_migrations(mut db: SqliteDb) -> Result { + migrations::apply_migrations(db.connection_mut())?; Ok(Self { db }) } diff --git a/conversations/src/storage/migrations.rs b/conversations/src/storage/migrations.rs index 41b3cb4..014bb96 100644 --- a/conversations/src/storage/migrations.rs +++ b/conversations/src/storage/migrations.rs @@ -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()?; } } diff --git a/storage/src/sqlite.rs b/storage/src/sqlite.rs index 4d42e9d..c6b00ab 100644 --- a/storage/src/sqlite.rs +++ b/storage/src/sqlite.rs @@ -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, StorageError> { Ok(self.conn.transaction()?)