I'm trying to use generic Diesel functions to shrink repetitive tasks like deleting a row based on the primary key.
I got generic insertion of rows working relatively quick, but deletion queries seem to be quite hard. I tried solving it both by using find()
and filter()
. I also consulted similar topics 1 and 2 without success.
find
use diesel::prelude::*;
use diesel::query_dsl::methods::FindDsl;
use std::error::Error;
pub struct DB {
conn: SqliteConnection,
}
impl DB {
pub fn remove_row<'a, T>(&self, table: T, pk: &'a str) -> Result<(), Box<Error>>
where
T: FindDsl<&'a str>,
<T as FindDsl<&'a str>>::Output: diesel::Identifiable,
<T as FindDsl<&'a str>>::Output: diesel::associations::HasTable,
{
diesel::delete(table.find(pk)).execute(&self.conn)?;
Ok(())
}
}
This leads to the following error which I cannot interpret at all:
error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
--> src/db/mod.rs:103:3
|
103 | diesel::delete (table.find (pk)) .execute (&self.conn) ?;
| ^^^^^^^^^^^^^^
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
= note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<_>` for `<<<T as diesel::query_dsl::filter_dsl::FindDsl<&'a str>>::Output as diesel::associations::HasTable>::Table as diesel::query_builder::AsQuery>::Query`
= note: required because of the requirements on the impl of `diesel::query_builder::IntoUpdateTarget` for `<T as diesel::query_dsl::filter_dsl::FindDsl<&'a str>>::Output`
= note: required by `diesel::delete`
filter()
use diesel::prelude::*;
use diesel::query_dsl::methods::FilterDsl;
use std::error::Error;
pub struct DB {
conn: SqliteConnection,
}
impl DB {
pub fn remove_row<T>(&self, table: T, pk: &str) -> Result<(), Box<Error>>
where
T: FilterDsl<bool>,
<T as FilterDsl<bool>>::Output: diesel::Identifiable,
<T as FilterDsl<bool>>::Output: diesel::associations::HasTable,
{
diesel::delete(table.filter(id.eq(pk))).execute(&self.conn)?;
Ok(())
}
}
In addition to the previous error, this has an error message about id
not being known in the data structure. I can think about one missing trait, which guarantees the existence of that row, but I haven’t found anything about such behavior.
error[E0425]: cannot find value `id` in this scope
--> src/db/mod.rs:117:33
|
117 | diesel::delete (table.filter (id.eq (pk))) .execute (&self.conn) ?;
| ^^ not found in this scope
help: possible candidates are found in other modules, you can import them into scope
|
4 | use crate::db::schema::events::columns::id;
|
4 | use crate::db::schema::ignored_events::columns::id;
|
4 | use crate::db::schema::locations::columns::id;
|
4 | use std::process::id;
error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
--> src/db/mod.rs:117:3
|
117 | diesel::delete (table.filter (id.eq (pk))) .execute (&self.conn) ?;
| ^^^^^^^^^^^^^^
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
= note: required because of the requirements on the impl of `diesel::query_dsl::filter_dsl::FilterDsl<_>` for `<<<T as diesel::query_dsl::filter_dsl::FilterDsl<bool>>::Output as diesel::associations::HasTable>::Table as diesel::query_builder::AsQuery>::Query`
= note: required because of the requirements on the impl of `diesel::query_builder::IntoUpdateTarget` for `<T as diesel::query_dsl::filter_dsl::FilterDsl<bool>>::Output`
= note: required by `diesel::delete`
Generics aren't easy. Generics in a highly generic system like Diesel are even harder.
I prefer to break the steps down into very small parts and avoid chaining as much as possible. With that in play, you will basically need to add trait bounds for each step. One nice thing to do is to use / create type aliases for very complicated trait bounds. Diesel provides a number of these and you can make your own for your special uses.
While I looked at the error messages generated, I mostly looked at the type bounds described by the functions / methods being called.
Point-by-point:
.find
comes from FindDsl
.delete
requires IntoUpdateTarget
.delete
is a DeleteStatement
, parameterized with T::Table
and T::WhereClause
. This is the custom type alias DeleteFindStatement
..execute
comes from ExecuteDsl
.use diesel::{
associations::HasTable,
helper_types::Find,
query_builder::{DeleteStatement, IntoUpdateTarget},
query_dsl::methods::ExecuteDsl,
};
type DeleteFindStatement<F> =
DeleteStatement<<F as HasTable>::Table, <F as IntoUpdateTarget>::WhereClause>;
impl DB {
pub fn remove_row<Tbl, Pk>(&self, table: Tbl, pk: Pk) -> Result<(), Box<Error>>
where
Tbl: FindDsl<Pk>,
Find<Tbl, Pk>: IntoUpdateTarget,
DeleteFindStatement<Find<Tbl, Pk>>: ExecuteDsl<SqliteConnection>,
{
let find = table.find(pk);
let delete = diesel::delete(find);
delete.execute(&self.conn)?;
Ok(())
}
}
You will need to try it on your own for the filter
-based version, as you didn't provide enough code to tell what id
should even be; as shown in your error message.
See also:
is there a benefit in making the primary key generic, even if it is
&str
in all cases?
For me, it's easier to use a generic type than it is to insert a bunch of generic lifetime parameters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With