π We are pleased to release SeaORM 0.10.0
!
Rust 1.65β
The long-anticipated Rust 1.65 has been released! Generic associated types (GATs) must be the hottest newly-stabilized feature.
How is GAT useful to SeaORM? Let's take a look at the following:
trait StreamTrait<'a>: Send + Sync {
type Stream: Stream<Item = Result<QueryResult, DbErr>> + Send;
fn stream(
&'a self,
stmt: Statement,
) -> Pin<Box<dyn Future<Output = Result<Self::Stream, DbErr>> + 'a + Send>>;
}
You can see that the Future
has a lifetime 'a
, but as a side effect the lifetime is tied to StreamTrait
.
With GAT, the lifetime can be elided:
trait StreamTrait: Send + Sync {
type Stream<'a>: Stream<Item = Result<QueryResult, DbErr>> + Send
where
Self: 'a;
fn stream<'a>(
&'a self,
stmt: Statement,
) -> Pin<Box<dyn Future<Output = Result<Self::Stream<'a>, DbErr>> + 'a + Send>>;
}
What benefit does it bring in practice? Consider you have a function that accepts a generic ConnectionTrait
and calls stream()
:
async fn processor<'a, C>(conn: &'a C) -> Result<...>
where C: ConnectionTrait + StreamTrait<'a> {...}
The fact that the lifetime of the connection is tied to the stream can create confusion to the compiler, most likely when you are making transactions:
async fn do_transaction<C>(conn: &C) -> Result<...>
where C: ConnectionTrait + TransactionTrait
{
let txn = conn.begin().await?;
processor(&txn).await?;
txn.commit().await?;
}
But now, with the lifetime of the stream elided, it's much easier to work on streams inside transactions because the two lifetimes are now distinct and the stream's lifetime is implicit:
async fn processor<C>(conn: &C) -> Result<...>
where C: ConnectionTrait + StreamTrait {...}
Big thanks to @nappa85 for the contribution.
Below are some feature highlights π:
Support Array Data Types in Postgresβ
[#1132] Support model field of type Vec<T>
. (by @hf29h8sh321, @ikrivosheev, @tyt2y3, @billy1624)
You can define a vector of types that are already supported by SeaORM in the model.
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "collection")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub integers: Vec<i32>,
pub integers_opt: Option<Vec<i32>>,
pub floats: Vec<f32>,
pub doubles: Vec<f64>,
pub strings: Vec<String>,
}
Keep in mind that you need to enable the postgres-array
feature and this is a Postgres only feature.
sea-orm = { version = "0.10", features = ["postgres-array", ...] }
Better Error Typesβ
[#750, #1002] Error types with parsable database specific error. (by @mohs8421, @tyt2y3)
let mud_cake = cake::ActiveModel {
id: Set(1),
name: Set("Moldy Cake".to_owned()),
price: Set(dec!(10.25)),
gluten_free: Set(false),
serial: Set(Uuid::new_v4()),
bakery_id: Set(None),
};
// Insert a new cake with its primary key (`id` column) set to 1.
let cake = mud_cake.save(db).await.expect("could not insert cake");
// Insert the same row again and it failed
// because primary key of each row should be unique.
let error: DbErr = cake
.into_active_model()
.insert(db)
.await
.expect_err("inserting should fail due to duplicate primary key");
match error {
DbErr::Exec(RuntimeErr::SqlxError(error)) => match error {
Error::Database(e) => {
// We check the error code thrown by the database (MySQL in this case),
// `23000` means `ER_DUP_KEY`: we have a duplicate key in the table.
assert_eq!(e.code().unwrap(), "23000");
}
_ => panic!("Unexpected sqlx-error kind"),
},
_ => panic!("Unexpected Error kind"),
}
Run Migration on Any Postgres Schemaβ
[#1056] By default migration will be run on the public
schema, you can now override it when running migration on the CLI or programmatically. (by @MattGson, @nahuakang, @billy1624)
For CLI, you can specify the target schema with -s
/ --database_schema
option:
- via sea-orm-cli:
sea-orm-cli migrate -u postgres://root:root@localhost/database -s my_schema
- via SeaORM migrator:
cargo run -- -u postgres://root:root@localhost/database -s my_schema
You can also run the migration on the target schema programmatically:
let connect_options = ConnectOptions::new("postgres://root:root@localhost/database".into())
.set_schema_search_path("my_schema".into()) // Override the default schema
.to_owned();
let db = Database::connect(connect_options).await?
migration::Migrator::up(&db, None).await?;
Breaking Changesβ
[#789] Replaced
usize
withu64
inPaginatorTrait
(by @liberwang1013)[#1002] Type signature of
DbErr
changed as a result of the PR (by @mohs8421, @tyt2y3)ColumnType::Enum
structure changed:
enum ColumnType {
// then
Enum(String, Vec<String>)
// now
Enum {
/// Name of enum
name: DynIden,
/// Variants of enum
variants: Vec<DynIden>,
}
...
}
- A new method
array_type
was added toValueType
:
impl sea_orm::sea_query::ValueType for MyType {
fn array_type() -> sea_orm::sea_query::ArrayType {
sea_orm::sea_query::ArrayType::TypeName
}
...
}
ActiveEnum::name()
changed return type toDynIden
:
#[derive(Debug, Iden)]
#[iden = "category"]
pub struct CategoryEnum;
impl ActiveEnum for Category {
// then
fn name() -> String {
"category".to_owned()
}
// now
fn name() -> DynIden {
SeaRc::new(CategoryEnum)
}
...
}
SeaORM Enhancementsβ
- [#995] Support
time
crate for SQLite (by @billy1624) - [#902] Support
distinct
&distinct_on
expression (by @edg-l, @kyoto7250) - [#973]
fn column()
also handle enum type (by @tyt2y3, @billy1624) - [#897] Added
acquire_timeout
onConnectOptions
(by @001wwang, @billy1624) - [#1020] Better compile error for entity without primary key (by @Sytten, @billy1624)
- [#833] Added blanket implementations of
IntoActiveValue
forOption
values (by @wdcocq) - [#1112] Added
into_model
&into_json
toCursor
(by @jukanntenn, @billy1624) - [#1056] Added
set_schema_search_path
method toConnectOptions
for setting schema search path of PostgreSQL connection (by @MattGson, @billy1624) - [#1042] Serialize
time
types asserde_json::Value
(by @jimmycuadra, @billy1624) - [#986] Implements
fmt::Display
forActiveEnum
(by @jesseduffield, @billy264) - [#990] Implements
TryFrom<ActiveModel>
forModel
(by @MattGson, @greenhandatsjtu)
CLI Enhancementsβ
- [#1052] Escape module name defined with Rust keywords (by @andy128k)
- [#879, #1155] Check to make sure migration name doesn't contain hyphen
-
in it (by @AngelOnFira) - [#953] Generate entity files as a library or module (by @viktorbahr, @HigherOrderLogic)
- [#947] Generate a new migration template with name prefix of unix timestamp (by @Animeshz)
- [#933] Generate migration in modules (by @remlse)
- [#1019] Generate
DeriveRelation
on emptyRelation
enum (by @alper, @billy1624) - [#988] Generate entity derive
Eq
if possible (by @billy1624, @w93163red) - [#1056] Run migration on any PostgreSQL schema (by @MattGson, @billy1624)
- [#864, #991]
migrate fresh
command will drop all PostgreSQL types (by @MaderNoob, @karpa4o4)
Please check here for the complete changelog.
Integration Examplesβ
SeaORM plays well with the other crates in the async ecosystem. We maintain an array of example projects for building REST, GraphQL and gRPC services. More examples wanted!
- Actix v4 Example
- Axum Example
- GraphQL Example
- jsonrpsee Example
- Poem Example
- Rocket Example
- Salvo Example
- Tonic Example
Sponsorβ
Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.
A big shout out to our sponsors π:
Communityβ
SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.
Here is the roadmap for SeaORM 0.11.x
.