Hello!
This is related to this topic, but since things have changed since then, I think it’s best to start a new topic.
I have the following code, that was working fine, but latest changes for Diesel 2.0 have made it fail in compilation, and the error messages are pretty difficult to understand:
// Some type definitions to simplify the code:
type DynTable = diesel_dynamic_schema::Table<String>;
type DynExpr = Box<dyn BoxableExpression<DynTable, Pg, SqlType = diesel::sql_types::Bool>>;
pub struct FilterClause {
field: String,
operator: Operator,
value: Option<String>,
}
impl FilterClause {
fn expression_ty<T, E, V>(&self, table: &DynTable) -> Result<DynExpr>
where
T: FromStr<Err = E> + AsExpression<V>,
E: StdError + Send + Sync + 'static,
V: SingleValue + 'static,
<T as AsExpression<V>>::Expression:
NonAggregate + QueryFragment<Pg> + SelectableExpression<DynTable> + 'static,
{
use diesel::ExpressionMethods;
let field = self.get_field::<V>(table);
let res: DynExpr = match (self.operator, self.value.as_ref()) {
(Operator::IsEmpty, None) => Box::new(field.is_null()),
(Operator::IsNotEmpty, None) => Box::new(field.is_not_null()),
(Operator::SameAs, Some(value)) => Box::new(field.eq(table.column(value.to_owned()))),
(Operator::NotSameAs, Some(value)) => {
Box::new(field.ne(table.column(value.to_owned())))
}
(Operator::In, Some(value)) => {
let list: Vec<T> = value
.parse::<StrList<T>>()
.context("could not parse list")?
.into();
Box::new(field.eq_any(list))
}
(Operator::NotIn, Some(value)) => {
let list: Vec<T> = value
.parse::<StrList<T>>()
.context("could not parse list")?
.into();
Box::new(field.ne_all(list))
}
(Operator::Equals, Some(value)) => Box::new(field.eq(value.parse::<T>()?)),
(Operator::DoesNotEqual, Some(value)) => Box::new(field.ne(value.parse::<T>()?)),
(Operator::GreaterThan, Some(value)) => Box::new(field.gt(value.parse::<T>()?)),
(Operator::GreaterOrEqual, Some(value)) => Box::new(field.ge(value.parse::<T>()?)),
(Operator::LessThan, Some(value)) => Box::new(field.lt(value.parse::<T>()?)),
(Operator::LessOrEqual, Some(value)) => Box::new(field.le(value.parse::<T>()?)),
(Operator::On, Some(_value)) => unimplemented!("ON operator for filtering"),
_ => unreachable!("should not have arrived here"), // TODO: log error and don't panic
};
Ok(res)
}
}
The Operator
type is just a simple enum
that has the different possible operations.
This generates many, many errors, and of different types. For example:
error[E0277]: the trait bound `<V as diesel::sql_types::SqlType>::IsNull: diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>` is not satisfied
--> common/src/database/filter/filter_clause.rs:244:17
|
244 | Box::new(field.ne(table.column(value.to_owned())))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>` is not implemented for `<V as diesel::sql_types::SqlType>::IsNull`
|
= note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::NotEq<diesel_dynamic_schema::Column<diesel_dynamic_schema::Table<std::string::String>, std::string::String, V>, diesel_dynamic_schema::Column<diesel_dynamic_schema::Table<std::string::String>, std::string::String, V>>`
= note: required for the cast to the object type `dyn diesel::BoxableExpression<diesel_dynamic_schema::Table<std::string::String>, diesel::pg::Pg, SqlType = diesel::sql_types::Bool, IsAggregate = diesel::expression::is_aggregate::No>`
help: consider further restricting the associated type
|
233 | NonAggregate + QueryFragment<Pg> + SelectableExpression<DynTable> + 'static, <V as diesel::sql_types::SqlType>::IsNull: diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If I add those boundaries, then I get the following error:
error[E0277]: the trait bound `<<V as diesel::sql_types::SqlType>::IsNull as diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>>::Out: diesel::sql_types::MaybeNullableType<diesel::sql_types::Bool>` is not satisfied
--> common/src/database/filter/filter_clause.rs:245:17
|
245 | Box::new(field.ne(table.column(value.to_owned())))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::sql_types::MaybeNullableType<diesel::sql_types::Bool>` is not implemented for `<<V as diesel::sql_types::SqlType>::IsNull as diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>>::Out`
|
= note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::NotEq<diesel_dynamic_schema::Column<diesel_dynamic_schema::Table<std::string::String>, std::string::String, V>, diesel_dynamic_schema::Column<diesel_dynamic_schema::Table<std::string::String>, std::string::String, V>>`
= note: required for the cast to the object type `dyn diesel::BoxableExpression<diesel_dynamic_schema::Table<std::string::String>, diesel::pg::Pg, SqlType = diesel::sql_types::Bool, IsAggregate = diesel::expression::is_aggregate::No>`
help: consider further restricting the associated type
|
234 | <V as SqlType>::IsNull: OneIsNullable<<V as SqlType>::IsNull>, <<V as diesel::sql_types::SqlType>::IsNull as diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>>::Out: diesel::sql_types::MaybeNullableType<diesel::sql_types::Bool>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
And adding this new boundary, gives the following error:
error[E0271]: type mismatch resolving `<diesel::expression::operators::LtEq<diesel_dynamic_schema::Column<diesel_dynamic_schema::Table<std::string::String>, std::string::String, V>, <T as diesel::expression::AsExpression<V>>::Expression> as diesel::Expression>::SqlType == diesel::sql_types::Bool`
--> common/src/database/filter/filter_clause.rs:242:28
|
242 | let res: DynExpr = match (self.operator, self.value.as_ref()) {
| ____________________________^
243 | | (Operator::IsEmpty, None) => Box::new(field.is_null()),
244 | | (Operator::IsNotEmpty, None) => Box::new(field.is_not_null()),
245 | | (Operator::SameAs, Some(value)) => Box::new(field.eq(table.column(value.to_owned()))),
... |
270 | | _ => unreachable!("should not have arrived here"), // TODO: log error and don't panic
271 | | };
| |_________^ expected struct `diesel::sql_types::Bool`, found associated type
|
= note: expected struct `diesel::sql_types::Bool`
found associated type `<<<V as diesel::sql_types::SqlType>::IsNull as diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>>::Out as diesel::sql_types::MaybeNullableType<diesel::sql_types::Bool>>::Out`
= help: consider constraining the associated type `<<<V as diesel::sql_types::SqlType>::IsNull as diesel::sql_types::OneIsNullable<<V as diesel::sql_types::SqlType>::IsNull>>::Out as diesel::sql_types::MaybeNullableType<diesel::sql_types::Bool>>::Out` to `diesel::sql_types::Bool`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
= note: required for the cast to the object type `dyn diesel::BoxableExpression<diesel_dynamic_schema::Table<std::string::String>, diesel::pg::Pg, SqlType = diesel::sql_types::Bool, IsAggregate = diesel::expression::is_aggregate::No>`
So it seems I cannot make it work. I was trying to go through the documentation, but there is no “guide-like” documentation to understand all these traits and types as a whole, and what should you expect on each case, so I don’t know if there is something directly wrong with my approach, or if there is something I’m missing.