tokio-rs/toasty
 Watch   
 Star   
 Fork   
8 days ago
toasty

Toasty v0.9.0

Changelog:

Highlights

#[document] fields. Store an embedded struct in one document column without giving up typed queries into its fields. PostgreSQL uses JSONB, MySQL uses JSON, SQLite and Turso use JSON text, and DynamoDB uses a map. The same generated path works across backends:

#[derive(Model)]
struct User {
    #[document]
    address: Address,
    // ... other fields
}

let users = User::filter(
    User::fields().address().city().eq("Seattle"),
)
.exec(&mut db)
.await?;

Read about document fields.

Atomic upserts. #[derive(Model)] now generates upsert_by_* builders for primary keys and unique constraints. An upsert can share assignments across both branches, set create- and update-specific values, apply mutations such as increment(), or use or_ignore():

let user = User::upsert_by_email("alice@example.com")
    .on_create(|user| user.name("Alice").login_count(0))
    .on_update(|user| {
        user.login_count(toasty::stmt::increment())
    })
    .exec(&mut db)
    .await?;

PostgreSQL, SQLite, and Turso support the complete API. DynamoDB supports primary-key forms; MySQL rejects upserts because its conflict behavior cannot preserve Toasty's targeted semantics. Read about upserts.

Dynamic and native JSON fields. Models can use serde_json::Value directly for payloads whose structure is not known to Toasty. Both serde_json::Value and toasty::Json<T> can select text, PostgreSQL JSONB, or native JSON storage:

#[column(type = jsonb)]
payload: serde_json::Value,

Use #[document] when Toasty should generate typed paths into a fixed structure; use JSON fields for opaque or dynamic values that are read and replaced as a whole. Read about JSON encoding.

Filtered and ordered includes. Association paths passed to .include() now accept .filter() and .order_by(), so applications can preload exactly the related records they need in the required order:

let users = User::all()
    .include(
        User::fields()
            .todos()
            .filter(Todo::fields().complete().eq(false))
            .order_by(Todo::fields().priority().desc()),
    )
    .exec(&mut db)
    .await?;

Turso Sync. The Turso driver can connect a local database to Turso Cloud or a local sync server. Applications configure authentication and bootstrap behavior on the driver, then explicitly call push() and pull() when they want to exchange changes. Read about Turso Sync.

Embedded enum improvements. Enum variants can declare explicitly shared fields with #[shared(name)] and define enum-level indexes or unique constraints over those columns. Embedded enums also support rename_all, explicit integer discriminants, and scalar storage for unit enums.


Added

  • support order_by in includes (#1109)
  • relation link/unlink return a builder instead of executing eagerly (#1118)
  • support serde_json::Value fields (#1116)
  • support native JSON and JSONB column storage (#1114)
  • [breaking] require explicit column types for JSON fields (#1106)
  • support temporal Vec fields (#1105)
  • filter associations in include (#1089)
  • introduce Expr::Static for inline SQL literals, hook up LIMIT/OFFSET (#1001)
  • support integer storage for enum discriminants (#1101)
  • add upsert support (#1091)
  • add #[shared] variant fields and enum-level #[index]/#[unique] (#1078)
  • implement Scalar for unit enum embeds (#1082)
  • add #[document] storage for embedded types with nested-path filtering (#1028)

Fixed

  • type indexed key discovery as primary keys on DynamoDB (#1113)
  • serialize unconstrained numeric migration snapshots (#1115)
  • roll back transactions when finalization fails (#1102)
  • support any() on many-to-many relations (#1097)
  • store Vec as a native enum array on Postgres (#1092)
  • compare composite-FK include filter against target fields (#1086)
  • return None for optional belongs_to with NULL foreign key (#1090)
  • lower IN-list over an embedded-field projection (#1084)
24 days ago
toasty

Toasty v0.8.0

Highlights

#[version] works on SQL. Optimistic concurrency was DynamoDB-only. It now runs on SQLite, PostgreSQL, MySQL, and Turso: creating a record initializes the field to 1, every update and delete increments it, and writing through a stale snapshot fails the condition check instead of clobbering the newer row.

#[version]
version: u64,

#[belongs_to] infers its keys. The key and references arguments are derived from the field name and the target's primary key, so the common case is a bare attribute:

#[belongs_to]
user: toasty::Deferred<User>,

Optional embeds and shared enum columns. An embedded struct can be Option, and enum variants can store a common field in one column by naming it:

metadata: Option<Metadata>,

enum Creature {
    Human { #[column("name")] name: String, profession: String },
    Animal { #[column("name")] name: String, species: String },
}

Both variants write creature_name; profession and species keep their own columns.

Composite unique indices. A model attribute declares uniqueness over several fields at once. SQL backends lower it to a unique index; DynamoDB rejects it with unsupported_feature.

#[unique(org_id, slug)]
struct Account { /* ... */ }

Query DSL additions. between(lo, hi) (inclusive, and usable as a DynamoDB sort-key condition), like_with_escape(pattern, escape_char) for patterns containing literal % or _, and indexes on unit enum fields.

Tracing. Each statement the driver executes emits one toasty::query event carrying duration_ms, rows, error, and the OpenTelemetry db.* fields, under the caller's span. Events past the slow-statement threshold are logged at WARN.


Changelog

toasty

Added

  • Emit one toasty::query event per statement and propagate caller spans (#1071)
  • Support #[version] optimistic concurrency on SQL drivers (#1065)
  • Infer key and references in #[belongs_to] (#1063)
  • Share columns across enum variants via #[column("name")] (#1064)
  • Escape support for LIKE expressions (#1039)
  • set_* replace-variants on the query builder (#1037)
  • Serde serialization and deserialization for toasty::Json<T> (#1035)
  • Index unit enum fields (#1027)
  • between operator in the query DSL (#1029)
  • Composite unique indices (#1018)
  • Option<EmbeddedType> model fields (#1021)
  • Scalar terminal fields in has_many relations (#1012)

Fixed

  • Avoid a panic when updating a mixed enum to a unit variant (#1069)
  • Fix enum decoding for OR'd variant filters (#1067)
  • Make multi-key delete and update consistent (#1053)
  • Truncate auto-generated index names that exceed the backend limit (#1023)
  • Increment the #[version] field on query-based updates (#1022)

Other

  • [breaking] Derive default table names at compile time (#1070)
  • [breaking] Make UpdateByKey returning columns explicit (#1024)
  • [breaking] Rename the RelationManyField/RelationOneField associated type to Target (#1015)
  • [breaking] Align stmt::Query with the per-model Query (#1011)
  • [breaking] Unify per-model query structs into Query<T> (#995)
  • [breaking] Remove the Register trait (#1006)
  • Remove compile-time field validation from the create! macro (#997)

toasty-core

Fixed

  • Encode bool values to match the DynamoDB key attribute type (#945)

Other

  • [breaking] Model::table_name is now a String rather than an Option<String> (#1070)

toasty-sql

Added

  • Serialize #[version] condition checks for optimistic concurrency (#1065)
  • Serialize LIKE ... ESCAPE (#1039)
  • Serialize BETWEEN (#1029)

toasty-driver-sqlite

Fixed

  • Propagate errors from exec_sql instead of panicking (#1007)

toasty-driver-dynamodb

Fixed

  • Make multi-key delete and update consistent (#1053)
  • Encode bool values as N("1"/"0") to match the key attribute type (#945)

toasty-cli

Fixed

  • Include the path in Toasty config load errors (#1036)

Other crates

toasty-macros, toasty-driver-postgresql, toasty-driver-mysql, toasty-driver-turso, and the integration-suite crates received the shared changes listed above (query events, #[version] on SQL, #[belongs_to] inference, unified Query<T>, compile-time table names) with no crate-specific entries.

2026-05-30 02:57:53
toasty

Toasty v0.7.0

Highlights

Relations are now plain fields, with eager loading by default. The HasMany, HasOne, and BelongsTo wrapper types are gone. A relation field is just its target type, and whether you wrap it in Deferred controls when it loads: a bare field loads eagerly with its parent, a Deferred<...> field loads on demand (via .include() or an accessor).

#[has_many]
posts: Vec<Post>, // eager — loaded with the User

#[has_one]
profile: toasty::Deferred<Option<Profile>>,  // on demand

Multi-step via relations. Declare relations that traverse an intermediate relation, and load them with .include():

#[has_many(via = posts.comments)]
comments: toasty::Deferred<Vec<Comment>>,

update! macro. A new macro for updates, mirroring create!. Set multiple fields in one call, with field shorthand, embedded-struct patches, and has-many inserts:

toasty::update!(user {
    name: "Carlos",
    email: "carlos@example.com",
})
.exec(&mut db)
.await?;

Arithmetic updates. Numeric fields get increment, decrement, add, and subtract, compiled to in-place column updates and usable as method shorthand inside update!:

toasty::update!(comment { views.increment() }).exec(&mut db).await?;

Turso driver. libSQL now joins SQLite, PostgreSQL, MySQL, and DynamoDB. On SQLite and Turso, TransactionMode lets you pick the lock-acquisition strategy for concurrent writers.

Raw SQL. An escape hatch for queries the builder doesn't cover, with typed results.

New field wrappers. Deferred<T> (replaces #[deferred]) and toasty::Json<T> (replaces #[serialize(json)]) are ordinary types — they compose with Option, embeds, and derives.


Changelog

toasty

Added

  • Derive Clone and add Deserialize for Deferred<T> (#994)
  • [breaking] Increment, decrement, add, and subtract update operators (#979)
  • update! macro for concise field updates (#980)
  • Reject create() on multi-step relation scopes at compile time (#978)
  • Raw SQL execution API (#965)
  • Remove the #[deferred] attribute in favor of Deferred<T> (#961)
  • Eager relation fields (#958)
  • .include() of multi-step via relations (#946)
  • Expose migration core from toasty (#944)
  • Turso driver with TransactionMode-aware concurrent writes (#938)
  • Dispatch has-many stmt::apply batches per entry (#932)
  • TransactionMode for SQLite lock-acquisition control (#931)
  • Allow #[version] on tuple-newtype embeds of u64 (#930)
  • Serialize SELECT DISTINCT (#934)
  • [breaking] Replace #[serialize(json)] with the toasty::Json<T> wrapper (#926)
  • Expose the primary-key type via Model::PrimaryKey (#921)
  • Fold simple Batch assignments in update lowering (#917)
  • Multi-step (via) has_many and has_one relations (#890)
  • Non-panicking try_get on relation types (#918)

Fixed

  • Deserialize a present Deferred<T> value as loaded (#999)
  • Lift relation-path LIKE into a foreign-key subquery (#992)
  • Lift relation-path IN-subquery through BelongsTo chains (#990)
  • Make starts_with case-sensitive on SQLite and MySQL (#983)
  • Handle ExprOr in eval verify_expr (#959)
  • [breaking] Scope .ilike() to PostgreSQL and document operator pass-through (#937)

Other

  • [breaking] Merge one-relation field traits (#971)
  • Simplify Field bounds now that Load<Output = Self> is required (#976)
  • Move UpdateTarget::Query rewrite into lower (#975)
  • [breaking] Delete the Relation trait, tighten relation field shapes (#967)
  • Split via relations into a field variant (#966)
  • [breaking] Merge has-relation field variants (#964)
  • [breaking] Require Deferred relation fields (#954)
  • Gate the SQLite connect doctest (#953)
  • Split relation field traits from targets (#950)
  • Unify lazy-slot relation encoding (#949)
  • [breaking] Move schema diff types to schema::diff (#929)
  • Consolidate migration types and reorganize the db::diff API (#928)

toasty-core

Added

  • INNER join variant (#922)

Fixed

  • Cap SQLite auto-increment integer storage at 4 bytes (#969)

toasty-macros

Added

  • [breaking] Remove singular has-many create-builder methods (#977)

Fixed

  • Respect the pair attribute in the #[has_one] macro (#927)

toasty-sql

Other

  • Rename types in toasty_core::schema::diff (#942)

toasty-driver-postgresql

Added

  • Adopt tokio-postgres-rustls 0.14 (#952)

Fixed

  • Accept libpq query-param connection options (#985)

toasty-driver-dynamodb

Fixed

  • Omit empty ExpressionAttributeValues on IS NULL / IS NOT NULL scans (#940)

Other crates

toasty-driver-mysql, toasty-driver-sqlite, toasty-driver-turso, toasty-cli, and the integration-suite crates received the shared changes listed above (raw SQL API, Turso driver, required Deferred relation fields, schema-diff move) with no crate-specific entries.

2026-05-15 03:23:45
toasty

Toasty v0.6.0

Added

  • Vec model fields with push, extend, clear, pop, remove_at, and remove operations on PostgreSQL, MySQL, SQLite, and DynamoDB (#866, #872, #880, #887)
  • Connection pooling improvements: lifetime capping, idle time limits, and automatic broken connection detection and eviction (#879, #882, #874, #867)
  • Query capabilities: .select() projection through BelongsTo relations, per-call column projection, ilike() filter method, filtering by associated model fields, all-condition filters for associations, and latest_by queries (#827, #820, #801, #781, #784, #707)
  • Compile-time validation for column storage types and create! macro field sets (#832, #648)
  • Auto proxying through tuple-newtype Embed types (#836)
  • #[deferred] field attribute and Deferred wrapper with embedded type support (#793, #799)
  • Backward pagination driver capability (#757)
  • Full-table scan support on DynamoDB (#821)
  • IN-list parameter optimization for PostgreSQL (#818)

Fixed

  • Compile-time validation for explicit auto strategies (#851)
  • Record equality comparisons now properly apply type casting rules (#838)