New Features
SQLite Transaction Modes (#2932)
Added begin_with_options to TransactionTrait, allowing you to specify SQLite transaction modes (DEFERRED, IMMEDIATE, EXCLUSIVE), along with isolation level and access mode for other backends. Works for both sqlx-sqlite and rusqlite.
use sea_orm::{TransactionTrait, TransactionOptions, SqliteTransactionMode};
let txn = db.begin_with_options(TransactionOptions {
sqlite_transaction_mode: Some(SqliteTransactionMode::Immediate),
..Default::default()
}).await?;Nested transactions correctly fall back to SAVEPOINT regardless of the mode.
Extend DeriveIntoActiveModel (#2961)
DeriveIntoActiveModel now supports set/fill, default, ignore, skip, exhaustive, and custom active_model path attributes for fine-grained control when converting "form" or "input" structs into ActiveModels.
set / fill — always set fields not present on the struct:
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", fill(cake_id = "None"))]
struct NewFruit {
name: String,
// cake_id is not on the struct, but will always be Set(None)
}
NewFruit { name: "Apple".into() }.into_active_model()
// => ActiveModel { id: NotSet, name: Set("Apple"), cake_id: Set(None) }Multiple set entries can be combined or split across attributes:
#[derive(DeriveIntoActiveModel)]
#[sea_orm(
active_model = "fruit::ActiveModel",
set(name = "String::from(\"cherry\")", cake_id = "None")
)]
struct IdOnlyFruit {
id: i32,
}
IdOnlyFruit { id: 1 }.into_active_model()
// => ActiveModel { id: Set(1), name: Set("cherry"), cake_id: Set(None) }default — fallback value when an Option<T> field is None:
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
#[sea_orm(default = "String::from(\"Unnamed\")")]
name: Option<String>,
}
NewFruit { name: Some("Apple".into()) }.into_active_model()
// => ActiveModel { id: NotSet, name: Set("Apple"), cake_id: NotSet }
NewFruit { name: None }.into_active_model()
// => ActiveModel { id: NotSet, name: Set("Unnamed"), cake_id: NotSet }Bare #[sea_orm(default)] (without a value) uses Default::default() as the fallback. This also works with custom enum types that implement Into<Option<T>>.
ignore / skip — exclude struct fields from the ActiveModel:
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel")]
struct NewFruit {
name: String,
cake_id: i32,
#[sea_orm(ignore)]
_extra: String, // not mapped to ActiveModel
}exhaustive — require all ActiveModel fields to be either on the struct or in set(...):
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", exhaustive, set(cake_id = "None"))]
struct FullFruit {
id: i32,
name: String,
// cake_id is covered by set(...), so all fields are accounted for
}Combining everything — set + default + ignore + exhaustive:
#[derive(DeriveIntoActiveModel)]
#[sea_orm(active_model = "fruit::ActiveModel", exhaustive, set(cake_id = "None"))]
struct NewFruit {
id: i32,
#[sea_orm(default = "String::from(\"Unnamed\")")]
name: Option<String>,
}
NewFruit { id: 1, name: Some("Apple".into()) }.into_active_model()
// => ActiveModel { id: Set(1), name: Set("Apple"), cake_id: Set(None) }
NewFruit { id: 2, name: None }.into_active_model()
// => ActiveModel { id: Set(2), name: Set("Unnamed"), cake_id: Set(None) }IntoSimpleExpr for FunctionCall (#2822)
FunctionCall now implements IntoSimpleExpr, so function calls can be used directly in select expressions and filters without wrapping in SimpleExpr.
Arrow: Support Decimal64 and Fixed-Size Binary (#2957)
- Decimal columns with precision <= 18 now map to Arrow
Decimal64(was alwaysDecimal128) - Precision 19-38 maps to
Decimal128, above 38 toDecimal256 - Added
FixedSizeBinary(N)support via#[sea_orm(arrow_byte_width = N)] - Added
BinaryArray/LargeBinaryArray/FixedSizeBinaryArraytoValue::Bytesconversion
Optional time crate for Migrations (#2865)
Migrations can now use the time crate instead of std::time::SystemTime for timestamps, enabling compilation to WASM targets. Activate with the with-time feature on sea-orm-migration.
OpenTelemetry SpanKind::Client (#2937)
The db_span! macro now emits otel.kind = "client", ensuring database spans are properly recognized as client spans by APM tools (Datadog, Jaeger, etc.).
Bug Fixes
Fix unique column in schema sync (#2971)
Columns marked with #[sea_orm(unique)] are now correctly handled by the schema sync/diff builder, generating proper unique constraints instead of being silently ignored.
Fix DeriveArrowSchema with split attributes (#2973)
Fixed a compilation error when #[sea_orm(...)] attributes were split across multiple lines on the same field (e.g. #[sea_orm(primary_key)] and #[sea_orm(auto_increment = false)] separately). The macro now properly consumes attributes it doesn't recognize.
Map internal error types properly
Internal errors from the schema builder are now mapped to the correct DbErr variants instead of being lost or mistyped.
Improvements
Pi Spigot Example
The sea-orm-sync pi spigot example has been refactored into a tutorial-style example with:
- OOP
PiSpigotstruct with state machine pattern (new/step/finalize/to_state/from_state) clapCLI with--digits,--checkpoint, and--dbflags- Comprehensive tests against 1000 known digits of pi, including three-phase checkpoint/resume
- Tutorial README demonstrating how to add SQLite persistence to any program