Search code examples
rustrust-diesel

What's the type for returning a joined, filtered, and then into_boxed() diesel statement?


In diesel 2.0.0, using rust 1.64, I'm trying to return a boxed query from a function to be used elsewhere. This is very easy if you just want to return the table as the base query:

pub fn get_queryset<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table.into_boxed()
}

However, if I have a join and a filter, I can't seem to figure out the type.


I have diesel tables defined as:

diesel::table! {
    Foo (id) {
        id -> Int4,
    }
}

diesel::table! {
    Bar (id) {
        id -> Int4,
        foo_id -> Int4,
    }
}

diesel::allow_tables_to_appear_in_same_query!(
    Foo,
    Bar,
);

From this, the aforementioned query compiles fine:

pub fn get_queryset<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table.into_boxed()
}

Next, I want to define this query:

pub fn get_queryset_with_join<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table
        .inner_join(Bar::table.on(Bar::foo_id.eq(Foo::id)))
        .filter(Bar::id.eq(42))
        .into_boxed()
}

Rust complains

error[E0308]: mismatched types
  --> src/main.rs:54:5
   |
53 |   pub fn get_queryset_with_join<'a>() -> Foo::BoxedQuery<'a, DB> {
   |                                          ----------------------- expected `BoxedSelectStatement<'a, (Integer,), FromClause<Foo::table>, Pg>` because of return type
54 | /     Foo::table
55 | |         .inner_join(Bar::table.on(Bar::foo_id.eq(Foo::id)))
56 | |         .filter(Bar::id.eq(42))
57 | |         .into_boxed()
   | |_____________________^ expected a tuple with 1 element, found one with 2 elements
   |
   = note: expected struct `BoxedSelectStatement<'a, (Integer,), FromClause<Foo::table>, _, _>`
              found struct `BoxedSelectStatement<'_, ((Integer,), (Integer, Integer)), FromClause<JoinOn<query_source::joins::Join<Foo::table, Bar::table, Inner>, diesel::expression::grouped::Grouped<diesel::expression::operators::Eq<Bar::columns::foo_id, Foo::columns::id>>>>, _, _>`

Ok, so I need to define the BoxedSelectStatement type explicitly:

type QuerySetJoinType<'a> = BoxedSelectStatement<
    'a,          
    (
        <Foo::table as diesel::Table>::AllColumns,
        <Bar::table as diesel::Table>::AllColumns
    ),
    FromClause<
        JoinOn<
            Join<
                Foo::table,
                Bar::table,
                Inner
            >,      
            diesel::expression::grouped::Grouped<
                diesel::expression::operators::Eq<
                    Foo::columns::id, 
                    Bar::columns::foo_id
                >
            >
        >
    >,
    DB
>;

However, rust complains:

error[E0603]: module `grouped` is private
  --> src/main.rs:43:33
   |
43 |             diesel::expression::grouped::Grouped<
   |                                 ^^^^^^^ private module
   |
note: the module `grouped` is defined here

How can I correct the type definition? Is there a helper function that implicitly gives me access?


Complete Example:

// main.rs
use diesel::*;

type DB = diesel::pg::Pg;

diesel::table! {
    Foo (id) {
        id -> Int4,
    }
}

diesel::table! {
    Bar (id) {
        id -> Int4,
        foo_id -> Int4,
    }
}

diesel::allow_tables_to_appear_in_same_query!(
    Foo,
    Bar,
);

pub fn get_queryset<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table.into_boxed()
}

use diesel::internal::table_macro::{BoxedSelectStatement, FromClause, Join, JoinOn, Inner};

type QuerySetJoinType<'a> = BoxedSelectStatement<
    'a,          
    (
        <Foo::table as diesel::Table>::AllColumns,
        <Bar::table as diesel::Table>::AllColumns
    ),
    FromClause<
        JoinOn<
            Join<
                Foo::table,
                Bar::table,
                Inner
            >,      
            diesel::expression::grouped::Grouped<
                diesel::expression::operators::Eq<
                    Foo::columns::id, 
                    Bar::columns::foo_id
                >
            >
        >
    >,
    DB
>;

pub fn get_queryset_with_join<'a>() -> Foo::BoxedQuery<'a, DB> {
    Foo::table
        .inner_join(Bar::table.on(Bar::foo_id.eq(Foo::id)))
        .filter(Bar::id.eq(42))
        .into_boxed()
}


fn main() {
    println!("Hello, world!");
}

# Cargo.toml
[package]
name = "rust_boxed_test"
version = "0.1.0"
edition = "2021"

[dependencies]
diesel = { version = "2.0.0", features = ["postgres"] }

FYI, I do know that use diesel::internal::* is an internal interface... but I want to return a non-trivial boxed query from a function.


Solution

  • So, apparently, you're not supposed to use the types emitted by the compiler as they are rather complicated. What you're instead supposed to use is the helper_types module like the following:

    type QuerySetJoinType<'a> = IntoBoxed<
        'a,
        Filter<                                     
            InnerJoinOn<Foo::table, Bar::table, Eq<Bar::foo_id, Foo::id>>,
            Eq<Bar::id, diesel::expression::SqlLiteral<diesel::sql_types::Integer>>
        >,
        DB
    >;
    

    These types are indeed much easier to use.

    This is used like:

    pub fn get_queryset_with_join<'a>() -> QuerySetJoinType<'a> {
        Foo::table
            .inner_join(Bar::table.on(Bar::foo_id.eq(Foo::id)))
            .filter(Bar::id.eq(42))
            .into_boxed()
    }