rustrust-diesel

Diesel delete function that deletes arbitrary select query specified in argument


I want to write a function with the signature:

fn delete_direct(pool: &Pool, q: T);

That function can be used for an arbitrary table:

use crate::schema::users::dsl::*;
delete_direct(&pool, users.filter(name.eq("Harry")))

To replace the usage:

use crate::schema::users::dsl::*;
let mut conn = &pool.get().expect("Failed to get database connection");
diesel::delete(users.filter(name.eq("Harry")))
    .execute(&mut conn)
    .expect("Failed to run the delete query you specified");

pool here is of type diesel::r2d2::Pool<ConnectionManager<PgConnection>>, which is aliased to type Pool for brevity.

I ended up with this function:

    fn delete_direct<T>(pool: &Pool, q: T) where
        T: diesel::query_builder::IntoUpdateTarget,
        <T as diesel::associations::HasTable>::Table: diesel::query_builder::QueryId + 'static,
        <T as diesel::query_builder::IntoUpdateTarget>::WhereClause: diesel::query_builder::QueryId + diesel::query_builder::QueryFragment<diesel::pg::Pg>,
        <<T as diesel::associations::HasTable>::Table as QuerySource>::FromClause: diesel::query_builder::QueryFragment<diesel::pg::Pg>,
    {
        let mut conn = pool.get().expect("Failed to get database connection");
        diesel::delete(q)
            .execute(&mut conn)
            .expect("Failed to run the delete query you specified");
    }

With the help of the compiler to satisfy all trait constraints that diesel expects to be present.

This looks complex, and looks like I'm missing something obvious. Am I missing something obvious or is this the simplest way to implement this function?


Solution

  • By mimicking the constraints on delete and execute, you can simplify it to:

    use diesel::pg::Pg;
    use diesel::query_builder::{DeleteStatement, IntoUpdateTarget, QueryFragment, QueryId};
    use diesel::RunQueryDsl;
    
    fn delete_direct<T>(pool: &Pool, q: T)
    where
        T: IntoUpdateTarget,
        DeleteStatement<T::Table, T::WhereClause>: QueryFragment<Pg> + QueryId,
    {
        let mut conn = pool.get().expect("Failed to get database connection");
        diesel::delete(q)
            .execute(&mut conn)
            .expect("Failed to run the delete query you specified");
    }
    

    In general though, abstracting over diesel fragments can very easily require a dizzying number of constraints to handle properly. I personally don't find it worth the trouble.