New Features
Per-Migration Transaction Control (#2980)
Previously, all Postgres migrations ran inside a single batch transaction, while MySQL and SQLite ran without one. This was an all-or-nothing approach with no way to opt out for individual migrations (e.g. CREATE INDEX CONCURRENTLY on Postgres requires running outside a transaction).
MigrationTrait now has a use_transaction() method to control this per migration:
impl MigrationTrait for Migration {
fn use_transaction(&self) -> Option<bool> {
Some(false) // opt out of automatic transaction
}
}None(default): follow backend convention — Postgres uses a transaction, MySQL/SQLite do notSome(true): force a transaction on any backendSome(false): disable automatic transaction wrapping
For migrations that opt out, SchemaManager::begin() and SchemaManager::commit() allow manual transaction control within the migration body:
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// DDL in a transaction
let m = manager.begin().await?;
m.create_table(
Table::create()
.table("my_table")
.col(pk_auto("id"))
.col(string("name"))
.to_owned(),
).await?;
m.commit().await?;
// Non-transactional DDL
manager.get_connection()
.execute_unprepared("CREATE INDEX CONCURRENTLY idx_name ON my_table (name)")
.await?;
Ok(())
}Core changes:
- Added
OwnedTransactionvariant toDatabaseExecutor, enablingSchemaManagerto own a transaction - Added
DatabaseExecutor::is_transaction()for runtime introspection - Each migration is now wrapped individually rather than in a batch