Creating a generic insert method for diesel

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 ?

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.

1 Like

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

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

Thank you very much !

EDIT: Found the error :grinning:

macro's look for imports where they are used, not where they are defined.

I needed to add the use crate::diesel::RunQueryDsl; import to my calling file

Apologies if obvious error, but macro noob.

I got the error

no method named `execute` found for struct `diesel::query_builder::InsertStatement<schema::competitions::table, diesel::insertable::OwnedBatchInsert<_, schema::competitions::table>>` in the current scope

the function without a macro works fine
(difference between get_results/execute is just so I could check exact same macro failed for me)

pub fn insert_competitions(
    conn: &PgConnection,
    new: Vec<NewCompetition>,
) -> Result<Vec<Competition>, diesel::result::Error> {
    use crate::schema::competitions::table;
    diesel::insert_into(table).values(new).get_results(conn)
}