Search code examples
rustrust-diesel

Explicit JOIN ON in diesel


I have two tables which I want to join, filter, and select only specific columns in Diesel:

contest_users::dsl::contest_users
    .inner_join(
        contests::table.on(contest_users::contest_id.eq(contests::contest_id)),
    )
    .filter(contest_users::user_id.eq(42))
    .select((contests::columns::contest_id,))

Here is a repo with a repro (see comments in the code).

I have found a few solutions that I can make to compile, but I still wonder if I can name a type of this query without using joinable!.

The following thing is compilable:

pub fn join_and_filter() -> diesel::dsl::Filter<
    diesel::dsl::Select<
        diesel::dsl::InnerJoin<contest_users::table, contests::table>,
        (contests::columns::contest_id,),
    >,
    diesel::expression::operators::Eq<
        contest_users::columns::user_id,
        diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>,
    >,
> {
    joinable!(contest_users -> contests (contest_id));

    contest_users::dsl::contest_users
        .inner_join(
            contests::table.on(contest_users::contest_id.nullable().eq(contests::contest_id.nullable())),
        )
        .filter(contest_users::user_id.eq(42))
        .select((contests::columns::contest_id,))
}

While the following (no joinable even though I have explicit .on()) causes compilation error:

pub fn join_and_filter() -> diesel::dsl::Filter<
    diesel::dsl::Select<
        diesel::dsl::InnerJoin<contest_users::table, contests::table>,
        (contests::columns::contest_id,),
    >,
    diesel::expression::operators::Eq<
        contest_users::columns::user_id,
        diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>,
    >,
> {
    contest_users::dsl::contest_users
        .inner_join(
            contests::table.on(contest_users::contest_id.eq(contests::contest_id))
        )
        .filter(contest_users::user_id.eq(42))
        .select((contests::columns::contest_id,))
}
error[E0277]: the trait bound `contest_users::table: JoinTo<contests::table>` is not satisfied
  --> src/main.rs:69:1
   |
69 | / pub fn join_and_filter() -> diesel::dsl::Filter<
70 | |     diesel::dsl::Select<
71 | |         diesel::dsl::InnerJoin<contest_users::table, contests::table>,
72 | |         (contests::columns::contest_id,),
...  |
84 | |         .select((contests::columns::contest_id,))
85 | | }
   | |_^ the trait `JoinTo<contests::table>` is not implemented for `contest_users::table`
   |
   = help: the following implementations were found:
             <contest_users::table as JoinTo<JoinOn<Join, On>>>
             <contest_users::table as JoinTo<diesel::query_builder::BoxedSelectStatement<'a, QS, ST, DB>>>
             <contest_users::table as JoinTo<diesel::query_builder::SelectStatement<F, S, D, W, O, L, Of, G>>>
             <contest_users::table as JoinTo<diesel::query_source::joins::Join<Left, Right, Kind>>>
   = note: required because of the requirements on the impl of `JoinWithImplicitOnClause<contests::table, Inner>` for `contest_users::table`

Solution

  • I have found a few solutions that I can make to compile, but I still wonder if I can name a type of this query without using joinable!.

    According to the documentation diesel::dsl::InnerJoin is defined as following:

    type InnerJoin<Source, Rhs> = <Source as JoinWithImplicitOnClause<Rhs, Inner>>::Output;
    

    This and the statement

    Represents the return type of .inner_join(rhs)

    indicates that this type is designed to be used with inner_join statements without explicit on clauses.

    Now this opens the question how to correctly specify the return type, as diesel 1.4 does not export a corresponding type via diesel::dsl. The documentation of diesels master branch indicates that a corresponding type exists there, but the relevant types form the type definition are not exposed on the 1.4 release as part of the public API. This indicates that it is currently not possible to name this type using an existing diesel release.