Require TransactionTrait::Transaction to be a fixed point (#3147) - #3153
Conversation
|
Is it possible that TransactionTrait::Transition is different from itself? |
|
I worried about this too. claude research says:
meaning, unlikely anyone would have a custom impl that is not backed by a transaction manager |
Sounds reasonable to me. |
6752398 to
ee9edc9
Compare
The methods generated by `#[sea_orm::model]` (`insert`/`update`/`save`/ `delete`/`action`/`cascade_delete`) open a transaction and then save related models inside it, so `action::<C>` recurses into `action::<C::Transaction>`. Proving those futures `Send` without a concrete `C` walked the unbounded `C::Transaction::Transaction::...` chain and overflowed (E0275 / `clippy::future_not_send`), so the documented 2.0 entity format failed to compile under clippy. Fix it on the trait rather than on the generated methods: `TransactionTrait` now requires `Sync` and pins its associated `Transaction` to a fixed point (`Transaction::Transaction == Transaction`) that is `Send`. That collapses the chain to a single type, so the obligation terminates. `Sync` and `Send` exclude no implementor: the `#[async_trait]` desugaring already forces both, since `begin` holds `&self` across an await and `TransactionSession::commit` takes `self` by value. Only a non-fixed-point `Transaction` is newly rejected, and it errors at the offending `impl` rather than at `#[sea_orm::model]`. All in-crate implementors are fixed points already. Putting the bounds on the generated methods instead (as in #3149) fixes the same overflow but leaks into user call sites: a helper generic over the connection, e.g. `fn create<C: ConnectionTrait + TransactionTrait>(db: &C)`, compiles on 2.0.0 and stops compiling, with no exported alias to restate. On the trait the requirement is invisible to callers. The regression test is async-only (the sync crate has no futures to prove `Send`), so it lives in the main crate's `tests/` gated on `feature = "sqlx-dep"` and compiles out in `sea-orm-sync`. Its `assert_send` calls are the actual checks, covering every generated method generically over the connection across has_many / has_one / belongs_to / many-to-many / self-ref / no-relation shapes, the `ConnectionTrait + TransactionTrait` and `impl Trait` caller forms, the concrete connection types, and a downstream `TransactionTrait` impl. Without the fix it fails with 7 E0275, checked by plain `cargo test`.
ee9edc9 to
17c0ed4
Compare
Closes #3147. Alternative to #3149 by @Huliiiiii
Problem
The methods generated by
#[sea_orm::model]open a transaction and save related models inside it, soaction::<C>recurses intoaction::<C::Transaction>. Proving those futuresSendwithout a concreteCwalks the unboundedC::Transaction::Transaction::…chain and overflows (E0275 /clippy::future_not_send) — the documented 2.0 entity format fails to compile under clippy.Fix
On the trait, not the generated methods.
TransactionTraitnow requiresSyncand pins its associatedTransactionto aSendfixed point (Transaction::Transaction == Transaction), collapsing the chain to one type so the obligation terminates.Why on the trait
Sync/Sendexclude no implementor — the#[async_trait]desugaring already forces both (beginholds&selfacross an await;TransactionSession::committakesselfby value). Only a non-fixed-pointTransactionis newly rejected, and it errors at the offendingimpl, not at#[sea_orm::model]. All in-crate implementors are fixed points already.Sendbounds for transactions inActiveModelExmethods #3149) leaks into user call sites:fn create<C: ConnectionTrait + TransactionTrait>(db: &C)compiles on 2.0.0 and stops compiling, with no exported alias to restate. On the trait it is invisible to callers.