Rustc does already emit as part of your error message what's the underlying problem:
required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
This can be read as LoadQuery<C, bool> needs to be implemented for your query (that long type represents your query at compile time).
Now rustc and especially the error reporting part is really clever. It is aware of possible existing impl and tries to be helpful by suggesting a "simple" solution here. That's why it is suggesting that Backend should be Pg because for that the only impl of that trait is found. It's the only existing impl because you only enabled one backend (the postgres backend), I expect that the error message changes if you change add more backends in your Cargo.toml via diesels feature flags.
So how to write that trait bound in such a way that it supports all backends instead. The compiler already hints a direction with that required because … line. You need to state that LoadQuery is implemented for your query. Now unfortunately you cannot just copy the corresponding type from the error message, as this is not a type available in the public API. So this seems like a dead end. The documentation lists two potential impls of the LoadQuery trait. We are interested in the second one, as our type is not SqlQuery. Now looking over the required bounds we can observe the following things:
Conn: Connection => already fulfilled,
Conn::Backend: HasSqlType<T::SqlType> => already fulfilled,
T: AsQuery + RunQueryDsl<Conn> => This requires that we could name T, which is our query type. That's not possible, so let's ignore that for now.
T::Query: QueryFragment<Conn::Backend> + QueryId => Same as the last bound, requires to name T.
U: Queryable<T::SqlType, Conn::Backend> => That one is interesting.
So let's have a detailed look at the `U: Queryable<T::SqlType, Conn::Backend> bound:
U is the type returned by your query. So bool in this case.
T::SqlType is the sql type returned by the query, so a type from diesel::sql_types. exists returns a boolean, so diesel::sql_type::Bool
Conn::Backend is your generic B type.
That means we need the verify that bool: Queryable<diesels::sql_type::Bool, B>. There is an existing impl in diesel which requires that bool: FromSql<diesel::sql_types::Bool, B>. Again there are existing impls for that as well, but note that those are only implemented for a specific backend. So what happens here is that rustc sees that impl and concludes that this is the only available impl. This then means that B must be Pg otherwise this function wouldn't be valid. Now there is another way to convince rustc that this trait impl is there, and that is by adding the it as trait bound. This gives you the following working impl:
impl<C, B> AuthDB for DB<C, B>
where
C: diesel::Connection<Backend = B>,
B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>,
bool: diesel::deserialize::FromSql<diesel::sql_types::Bool, B>
{
fn exists_by_id_and_password(
&self,
id: String,
password: String,
) -> QueryResult<bool> {
Ok(diesel::select(diesel::dsl::exists(
users::dsl::users.filter(
users::dsl::username
.eq(id)
.and(users::dsl::password.eq(password)),
),
))
.get_result::<bool>(&self.conn)?)
}
}
Side note:
If I enable an additional backend I get the following error message
error[E0277]: the trait bound `bool: FromSql<Bool, B>` is not satisfied
--> src/main.rs:39:10
|
39 | .get_result::<bool>(&self.conn)?)
| ^^^^^^^^^^ the trait `FromSql<Bool, B>` is not implemented for `bool`
|
= note: required because of the requirements on the impl of `Queryable<Bool, B>` for `bool`
= note: required because of the requirements on the impl of `LoadQuery<C, bool>` for `diesel::query_builder::SelectStatement<(), query_builder::select_clause::SelectClause<Exists<diesel::query_builder::SelectStatement<table, query_builder::select_clause::DefaultSelectClause, query_builder::distinct_clause::NoDistinctClause, query_builder::where_clause::WhereClause<And<diesel::expression::operators::Eq<columns::username, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>, diesel::expression::operators::Eq<columns::password, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>>>>>>`
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
|
25 | B: diesel::backend::Backend + diesel::sql_types::HasSqlType<diesel::sql_types::Bool>, bool: FromSql<Bool, B>
| ~~~~~~~~~~~~~~~~~~~~~~~~
This exactly suggests the trait bound described above.
Two other semi-releated side notes:
- There is no reason to explicitly have the backend as second parameter to your
DB struct. You can simply access it at any point via C::Backend.
- Obviously you should not store a password as plaintext in your database