Skip to content

Diesel 2.1.0

Compare
Choose a tag to compare
@weiznich weiznich released this 26 May 08:46
· 1191 commits to master since this release
v2.1.0
c5a9f7b

Diesel 2.1.0 contains the contributions of 42 people. More than 380 commits were submitted
over a span of 9 months.

This release contains several new features and improves existing features. It introduces support for generating migrations based on the diff between your schema.rs file and your database via Diesel CLI. Diesel derives now provides a #[derive(MultiConnection)] derive macro that allows to easily combine different database connections into a single enum, which implements Connection on its own. The MySQL backend gets support for upsert queries via the ON DUPLICATE KEYS syntax. Finally we provide new tooling to improve complex error messages generated for common error cases. Check out our changelog for a complete list of changes.

This release wouldn't be possible without the support of our contributors and sponsors. If you want to support diesels development, consider joining the reviewer team, submitting PR's, help writing documentation or sponsor the maintainers.

Migration generation

Diesel CLI now includes support for generating migrations based on the difference of your schema.rs file and your local database.
This works as following:

  1. Start editing your schema.rs file, for example by adding your first table:
diesel::table! {
    users {
        id -> Integer,
        name -> Text,
    }
}
  1. Run diesel migration generate my_first_migration --diff-schema --database-url DATABASE_URL
  2. Checkout, verify and possible modify the generated migration:
-- Your SQL goes here
CREATE TABLE `users`(
	`id` INTEGER NOT NULL PRIMARY KEY,
	`name` TEXT NOT NULL
);
  1. Run the migration via diesel migration run --database-url DATABASE_URL

It's important to note that the generated migrations can only contain information that are part of the schema.rs file. This explicitly excludes default value, custom check constraints and similar SQL features. We expect the generated migrations to be a good starting point for writing the actual migration you need, we do not expect them to include all necessary information in all cases or to provide the perfect solution in each scenario.

MultiConnection support

Diesel now includes a #[derive(MultiConnection)] proc macro derive, which allows to easily support more than one database backend in a single application. It can be applied to an enum of different connections:

#[derive(diesel::MultiConnection)]
pub enum AnyConnection {
    Postgresql(diesel::PgConnection),
    Mysql(diesel::MysqlConnection),
    Sqlite(diesel::SqliteConnection),
}

Afterwards the AnyConnection type can be used as ordinary connection:

fn use_multi(conn: &mut AnyConnection) -> QueryResult<()> {
   // Use the connection enum as any other connection type
   // for inserting/updating/loading/…
   diesel::insert_into(users::table)
       .values(users::name.eq("Sean"))
       .execute(conn)?;

   let users = users::table.load::<(i32, String)>(conn)?;
   Ok(())
}

By default this connection type will only support a subset of SQL that's supported by all inner connection types. By being an enum it's easy to fallback to backend specific SQL as soon as required. We provide this feature as derive macro so that it is possible to:

  • Select the backends you actually use
  • Allow to use third party connections as well (this requires the third party connection to be based at least on diesel 2.1 and to implement the MultiConnectionHelper trait in addition to the existing Connection trait.

Upsert support for the MySQL backend

Diesel 2.1 adds support for INSERT INTO … ON DUPLICATE KEYS … queries for the MySQL backend using the existing upsert framework. It's now possible to write such queries using the diesel provided DSL:

diesel::insert_into(users)
    .values(&user2)
    .on_conflict(diesel::dsl::DuplicatedKeys)
    .do_update()
    .set(name.eq("I DONT KNOW ANYMORE"))
    .execute(conn)?;

Improved error messages

We spend some effort to improve error messages generated by rustc for common diesel issues further.

Consider the following example:

table! {
    users {
        id -> Integer,
        name -> Text,
    }
}

#[derive(Queryable)]
struct User {
    name: String,
    id: i32,
}


users::table.load::<User>(&mut conn)

which would generate the following error message:

error[E0277]: the trait bound `(diesel::sql_types::Integer, diesel::sql_types::Text): load_dsl::private::CompatibleType<User, Mysql>` is not satisfied
    --> src/main.rs:20:31
     |
20   |     users::table.load::<User>(&mut conn);
     |                  ----         ^^^^^^^^^ the trait `load_dsl::private::CompatibleType<User, Mysql>` is not implemented for `(diesel::sql_types::Integer, diesel::sql_types::Text)`
     |                  |
     |                  required by a bound introduced by this call
     |
     = help: the following other types implement trait `load_dsl::private::CompatibleType<U, DB>`:
               (ST0, ST1)
               (ST0, ST1, ST2)
               (ST0, ST1, ST2, ST3)
               (ST0, ST1, ST2, ST3, ST4)
               (ST0, ST1, ST2, ST3, ST4, ST5)
               (ST0, ST1, ST2, ST3, ST4, ST5, ST6)
               (ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7)
               (ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7, ST8)
             and 24 others
     = note: required for `users::table` to implement `LoadQuery<'_, _, User>`
note: required by a bound in `diesel::RunQueryDsl::load`
    --> /home/weiznich/.cargo/git/checkouts/diesel-6e3331fb3b9331ec/ef6252e/diesel/src/query_dsl/mod.rs:1543:15
     |
1543 |         Self: LoadQuery<'query, Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `diesel::RunQueryDsl::load`

This is caused by an field order mismatch between what your query returns and what your struct expects the query to return.

With diesel 2.0 we introduced the Selectable trait and a corresponding derive. That allows to automatically generate a matching select clause to prevent such issues from happening. While this already solved parts of the problem it does not solve the following case:

#[derive(Queryable, Selectable)]
struct User {
    name: i32,
    id: i32,
}

users::table.select(User::as_select()).load(&mut conn);

which generates the following error message:

error[E0277]: the trait bound `expression::select_by::SelectBy<User, _>: load_dsl::private::CompatibleType<_, _>` is not satisfied
    --> src/main.rs:20:49
     |
20   |     users::table.select(User::as_select()).load(&mut conn);
     |                                            ---- ^^^^^^^^^ the trait `load_dsl::private::CompatibleType<_, _>` is not implemented for `expression::select_by::SelectBy<User, _>`
     |                                            |
     |                                            required by a bound introduced by this call
     |
     = help: the trait `load_dsl::private::CompatibleType<U, DB>` is implemented for `expression::select_by::SelectBy<U, DB>`
     = note: required for `SelectStatement<FromClause<users::table>, query_builder::select_clause::SelectClause<expression::select_by::SelectBy<User, _>>>` to implement `LoadQuery<'_, _, _>`
note: required by a bound in `diesel::RunQueryDsl::load`
    --> /home/weiznich/.cargo/git/checkouts/diesel-6e3331fb3b9331ec/ef6252e/diesel/src/query_dsl/mod.rs:1543:15
     |
1543 |         Self: LoadQuery<'query, Conn, U>,
     |               ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `diesel::RunQueryDsl::load`

This is caused by a type mismatch in the name field. With diesel 2.1 we now introduce an additional #[diesel(check_for_backend(diesel::backend::BackendType))] attribute that greatly improves the error messages generated for these cases. This helps pining down which field exactly causes a type mismatch.

By applying this attribute to our example:

#[derive(Queryable, Selectable)]
#[diesel(check_for_backend(diesel::mysql::Mysql))]
struct User {
    name: i32,
    id: i32,
}

we get the following error message:

error[E0277]: the trait bound `i32: FromSql<diesel::sql_types::Text, Mysql>` is not satisfied
  --> src/main.rs:13:11
   |
13 |     name: i32,
   |           ^^^ the trait `FromSql<diesel::sql_types::Text, Mysql>` is not implemented for `i32`
   |
   = help: the trait `FromSql<diesel::sql_types::Integer, Mysql>` is implemented for `i32`
   = note: required for `i32` to implement `diesel::Queryable<diesel::sql_types::Text, Mysql>`
   = note: required for `i32` to implement `FromSqlRow<diesel::sql_types::Text, Mysql>`
   = help: see issue #48214

This error message now points to the exact cause of the issue: You cannot deserialize an Text value into a i32 field. This attribute accepts one or more diesel backend type to check the struct definition against. It requires that the struct is using either both, #[derive(Queryable)] and #[derive(Selectable)] or #[derive(QueryableByName)].

Internal changes

Changes listed here are relevant for crates using the i-implement-a-third-party-backend-and-opt-into-breaking-changes feature flag.
With Diesel 2.1 we have migrated some traits to use GAT. This affects the following traits:

  • RowGatWorkaround
  • ConnectionGatWorkaround
  • HasRawValue
  • HasBindCollector

These traits are removed in favour of generic associated types on the corresponding child trait.

Additionally we slightly relaxed one trait bound on BindCollector::push_bound_value to allow unsized values there as well.

Thanks

Thank you to everyone who helped make this release happen through sponsoring, bug reports, and discussion on GitHub and Gitter. While we don't have a way to collect stats on that form of contribution, it's greatly appreciated.

In addition to the Diesel core team, 42 people contributed code to this release. A huge thank you to:

  • 6293
  • Aleksei Orazov
  • Alexx Roche
  • AndrolGenhald
  • AndyJado
  • Artemis Livingstone
  • czotomo
  • Diana
  • Elliott Forde
  • Elrendio
  • Gorg Worgington
  • Heliozoa
  • Ian
  • Jake Goulding
  • Jeff Schwab
  • Jeremy Lardenois
  • johnmai
  • Leonardo Yvens
  • mikemcdermottredjack
  • Moritz Hedtke
  • MOZGIII
  • Neil Svedberg
  • Neo
  • Nishant Joshi
  • Oliver Cooper
  • Omid Rad
  • Pablito Labarta
  • Pavan Kumar Sunkara
  • Peter Todorov
  • Rutvik Patel
  • Sanchith Hegde
  • schlamar
  • sebasv
  • Silas McCroskey
  • Šimon Lukašík
  • Steven Chu
  • stormofice
  • Thomas Sieverding
  • Trevor Gross
  • Yuki Okushi
  • 无穷无尽的爱
  • 管唯宇