Creating a generic insert method for diesel


#1

Hi,

In my quest to create an easy to use event sourcing framework for Rust (Porting a Go event sourcing framework to Rust (generics) - RIIR!) I’m struggling to implement a generic insert method with diesel and the compiler error does not really help me.

here is the code:

fn execute<T: Table, I>(conn: &PgConnection, table: T, aggregate: I)
where I: diesel::Insertable<T> {
    diesel::insert_into(table)
        .values(aggregate)
        .execute(conn);
}

and the error

error[E0277]: the trait bound `<T as diesel::QuerySource>::FromClause: diesel::query_builder::QueryFragment<_>` is not satisfied
  --> examples/bank.rs:84:10
   |
84 |         .execute(conn);
   |          ^^^^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `<T as diesel::QuerySource>::FromClause`
   |
   = help: the following implementations were found:
             <&'a T as diesel::query_builder::QueryFragment<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`

error[E0277]: the trait bound `<I as diesel::Insertable<T>>::Values: diesel::query_builder::QueryFragment<_>` is not satisfied
  --> examples/bank.rs:84:10
   |
84 |         .execute(conn);
   |          ^^^^^^^ the trait `diesel::query_builder::QueryFragment<_>` is not implemented for `<I as diesel::Insertable<T>>::Values`
   |
   = help: the following implementations were found:
             <&'a T as diesel::query_builder::QueryFragment<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`

error[E0277]: the trait bound `<I as diesel::Insertable<T>>::Values: diesel::insertable::CanInsertInSingleQuery<_>` is not satisfied
  --> examples/bank.rs:84:10
   |
84 |         .execute(conn);
   |          ^^^^^^^ the trait `diesel::insertable::CanInsertInSingleQuery<_>` is not implemented for `<I as diesel::Insertable<T>>::Values`
   |
   = help: the following implementations were found:
             <&'a T as diesel::insertable::CanInsertInSingleQuery<DB>>
   = note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<_>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`
   = note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, _>` for `diesel::query_builder::InsertStatement<T, <I as diesel::Insertable<T>>::Values>`

error: aborting due to 4 previous errors

Some errors occurred: E0277, E0601.
For more information about an error, try `rustc --explain E0277`.

The execute method should be able to persist any aggregate (which are Insertable traits).

Any idea about what I’m missing ?


#2

It’s extremely hard to use diesel generically. I’ve done it for a while and you’re very likely to run into type bound recursion issues. I ended up resorting to macros instead.


#3

Thank you for the response, do you have code examples ?

Because as being new to Rust, I’ll not be able to implement such macro from scratch


#4
macro_rules! execute {
    ($conn:expr, $table:expr, $aggregate:expr) => {
        diesel::insert_into($table)
            .values($aggregate)
            .execute($conn);
    };
}

#5

Thank you very much !