Skip to content

2.0.0-rc.41

Latest

Choose a tag to compare

@tyt2y3 tyt2y3 released this 18 Jun 14:25
· 1 commit to master since this release

Release Notes: SeaORM 2.0.0-rc.41

(since 2.0.0-rc.40)

New Features

SelectFourMany with consolidate() (#3054)

SelectFour::consolidate() returns a SelectFourMany; its all() (star topology)
rolls the three has-many children up into Vecs β€” Vec<(E::Model, Vec<F::Model>, Vec<G::Model>, Vec<H::Model>)> β€” mirroring SelectThreeMany instead of returning
flat Option tuples.

let rows = parent::Entity::find()
    .find_also_related(a::Entity)
    .find_also_related(b::Entity)
    .find_also(parent::Entity, c::Entity)
    .consolidate()
    .all(db)
    .await?;

Update without returning (#2965)

ActiveModelTrait::update_without_returning (and UpdateOne /
ValidatedUpdateOne::exec_without_returning) run an UPDATE without a RETURNING
clause, yielding an UpdateResult β€” useful on backends without RETURNING or when
the model isn't needed. It runs before_save, skips after_save, and returns
DbErr::RecordNotUpdated when no row matches.

let res = active_model.update_without_returning(db).await?;

Explicit prefixes for nested result models (#2989)

FromQueryResult and DerivePartialModel accept #[sea_orm(nested(prefix = "..."))],
so a model can hold several nested fields of the same type without column clashes:

#[sea_orm(nested(prefix = "mgr_"), alias = "manager")]
manager: Worker,
#[sea_orm(nested(prefix = "csh_"), alias = "cashier")]
cashier: Worker,

Migration CLI custom connection entrypoints (#3035)

sea-orm-migration exposes custom connection entrypoints so applications can
customize ConnectOptions, load SQLite extensions, or build the connection from
their own config before running migration commands.

run_cli_with_connection(migrator, |mut opt: ConnectOptions| async move {
    opt.sqlx_logging(false); // ...or load extensions, read app config, etc.
    Database::connect(opt).await
})
.await;

Prebuilt sea-orm-cli binaries via cargo-binstall (#2877)

Releases now publish prebuilt sea-orm-cli binaries for Linux (x64/arm64), macOS
(x64/arm64), and Windows (x64), and the crate carries cargo-binstall metadata, so
cargo binstall sea-orm-cli installs without compiling from source.

Enhancements

Stable serde for first-party value wrappers (#3023)

First-party wrappers now have explicit serde impls instead of relying on derive
shape: TextUuid is a JSON string, and the Unix-timestamp wrappers
(ChronoUnixTimestamp, ChronoUnixTimestampMillis, TimeUnixTimestamp,
TimeUnixTimestampMillis) are JSON i64s matching their stored value.

let json = serde_json::to_string(&TextUuid::from(uuid))?; // "\"<uuid>\""
serde_json::from_str::<TextUuid>("\"not-a-uuid\"").is_err(); // parse-checked

Streaming behind stream cargo feature (#3075)

Streaming APIs (QueryStream, StreamTrait, Select::stream*) are gated behind
the stream feature, on by default. default-features = false without stream
drops the ouroboros dependency.

# opt out of streaming (and ouroboros) by dropping default features
sea-orm = { version = "2.0.0-rc.41", default-features = false, features = ["macros", "sqlx-postgres", "runtime-tokio-native-tls"] }

Generated migration names (#2580)

Generated migrations implement MigrationName explicitly, keeping names stable
when the file/module layout is customized.

Bug Fixes

ActiveModelBehavior runs for junction tables (#3027, #3010)

When establishing a many-to-many relation through the ActiveModel builder, the
junction-table rows now run their ActiveModelBehavior hooks (before_save /
after_save on inserted via-models, before_delete / after_delete on removed
ones) instead of being inserted/deleted without hooks.

Generated entities compile when an enum name collides with a generated identifier (#2880, #2153)

An enum named like a generated item (Model, Entity, ActiveModel, Column,
PrimaryKey, Relation) is now imported aliased instead of emitting a
conflicting use:

use super::sea_orm_active_enums::Model as ModelEnum;

serde(rename_all) no longer breaks ActiveModel::from_json defaulting (#2855, #2854)

Regression from #2842: with #[serde(rename_all = "...")] (or field-level
rename), missing fields errored instead of defaulting to ActiveValue::NotSet.
The default-merge is now keyed off each column's json_key(), which respects
both rename forms.

#[serde(rename_all = "camelCase")]
pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub first_name: String }

// `id` omitted β†’ NotSet again (previously errored "missing field")
let am = ActiveModel::from_json(json!({ "firstName": "Max" }))?;
assert_eq!(am.id, ActiveValue::NotSet);

Entity loader diamond relations (#3030)

The entity loader distinguishes multiple relations to the same target by their
relation definition rather than table reference, fixing diamond graphs where two
fields point to the same entity through different foreign keys:

#[sea_orm(belongs_to, from = "sender_id", to = "id")]
pub sender: HasOne<user::Entity>,
#[sea_orm(belongs_to, from = "recipient_id", to = "id")]
pub recipient: HasOne<user::Entity>, // both β†’ user, now loaded independently

Schema-sync indexes for non-default PostgreSQL schemas (#3085, #3084)

On PostgreSQL, indexes from #[sea_orm(indexed)] / #[sea_orm(unique_key)] on a
schema_name entity now target the qualified table (ON "sys"."app_user"),
fixing relation "..." does not exist and wrong-schema targeting. MySQL/SQLite
keep unqualified targets (their SeaQuery index builders reject a qualified one),
which also removes a latent panic for #[sea_orm(unique)] columns there.

CLI extra attribute parsing (#3032)

Entity generation handles comma-separated extra attributes and derives with
nested commas:

--model-extra-attributes 'serde(rename_all = "camelCase"),ts(export)'

PostgreSQL drop_everything with dependent custom types (#3014)

drop_everything drops PostgreSQL custom types with CASCADE, cleaning up
dependent domains and functions while preserving extension-owned types.

Generated delete_by_* for single-column unique keys (#3059)

model_ex generates delete_by_* helpers for one-column unique keys, matching
the existing find_by_* behavior for #[sea_orm(unique)] and single-column
unique_key fields.

Entity::delete_by_name("alpha").exec(db).await?; // alongside find_by_name

SQLite file URI parameters (#3072)

Rusqlite connections allow SQLite file URI query parameters (e.g. shared
in-memory databases), leaving validation to SQLite.

Database::connect("sqlite:file:mydb?mode=memory&cache=shared").await?;

SQLite timestamp with time zone reads (#2878)

Timestamp-with-time-zone values preserve their stored offset instead of
normalizing through UTC on read.

Tracing instrumentation no longer records SQL arguments by default (#3047)

Automatic tracing::instrument fields drop SQL arguments; SQL recording stays
controlled by the explicit tracing span setting.

Compatibility Notes

  • Serde shape for first-party wrappers is now explicit: TextUuid ↔ JSON string,
    Unix-timestamp wrappers ↔ JSON integers.
  • SQLite timestamp-with-time-zone reads preserve offsets; PostgreSQL
    drop_everything is intentionally more destructive for custom-type dependents.
  • The entity loader change adds methods to the public loader traits β€” generated
    code updates automatically, but manual trait impls must add them.
  • default-features = false users of streaming must enable stream.
  • Tracing spans no longer carry SQL statements/arguments as automatic
    instrument fields; re-enable via the explicit tracing span setting if needed.
  • Nested prefixes are opt-in; existing unprefixed #[sea_orm(nested)] fields
    still compile. Reusing the same non-empty prefix for the same nested type is a
    compile error (the fields would decode from identical aliases).