diesel-rs/diesel

Missing impl for tuples in custom QueryFragment

Open

#4 292 ouverte le 30 sept. 2024

Voir sur GitHub
 (5 commentaires) (0 réactions) (0 assignés)Rust (1 003 forks)batch import
help wantedhole in api

Métriques du dépôt

Stars
 (12 054 stars)
Métriques de merge PR
 (Merge moyen 30j 16h) (30 PRs mergées en 30 j)

Description

When building a custom QueryFragment, it seems some method do not behave the same when using the standard SelectBy trait or a custom implementation.

This happens in particular when trying to query two structs inside a tuple (see example below).


use std::error::Error;

use diesel::{
    pg::Pg,
    query_builder::{Query, QueryFragment, QueryId},
    query_dsl::methods::{LoadQuery, SelectDsl},
    sql_types::BigInt,
    Connection, PgConnection, QueryResult, Queryable, RunQueryDsl, Selectable, SelectableHelper,
};

pub trait Paginate: Sized {
    fn paginate(self) -> Pagination<Self>;
}

impl<T> Paginate for T {
    fn paginate(self) -> Pagination<Self> {
        Pagination { query: self }
    }
}

#[derive(Debug, Clone, Copy, QueryId)]
pub struct Pagination<T> {
    query: T,
}

impl<T> QueryFragment<Pg> for Pagination<T>
where
    T: QueryFragment<Pg>,
{
    fn walk_ast<'b>(
        &'b self,
        mut out: diesel::query_builder::AstPass<'_, 'b, Pg>,
    ) -> QueryResult<()> {
        out.push_sql("SELECT *, COUNT(*) OVER () FROM(");
        self.query.walk_ast(out.reborrow())?;
        out.push_sql(")");
        Ok(())
    }
}

impl<T: Query> Query for Pagination<T> {
    type SqlType = (T::SqlType, BigInt);
}

impl<T> diesel::RunQueryDsl<PgConnection> for Pagination<T> {}

// this is the function not working
impl<T> Pagination<T> {
    pub fn load_with_info<'a, U>(self, conn: &mut PgConnection) -> QueryResult<(Vec<U>, i64)>
    where
        Self: LoadQuery<'a, PgConnection, (U, i64)>,
    {
        let results = self.load::<(U, i64)>(conn)?;

        let total = results.get(0).map(|x| x.1).unwrap_or(0);
        let records = results.into_iter().map(|x| x.0).collect();

        Ok((records, total))
    }
}

// define a simple table
diesel::table! {
    my_table (field_a) {
        field_a -> Int4,
        field_b -> Int4,
    }
}

// define 2 query structs on this table
#[derive(Queryable, Selectable)]
#[diesel(table_name = my_table)]
#[diesel(check_for_backend(diesel::pg::Pg))]
struct PartA {
    field_a: i32,
}

#[derive(Queryable, Selectable)]
#[diesel(table_name = my_table)]
#[diesel(check_for_backend(diesel::pg::Pg))]
struct PartB {
    field_b: i32,
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut conn = PgConnection::establish("").expect("Error connecting to database");

    // this works
    let result: Vec<(_, _)> = my_table::table
        .select((PartA::as_select(), PartB::as_select()))
        .load(&mut conn)?;

    // this does not compile
    let (result, size): (Vec<(_, _)>, i64) = my_table::table
        .select((PartA::as_select(), PartB::as_select()))
        .paginate()
        .load_with_info(&mut conn)?;

    Ok(())
}

Here is the error produced by the compiler:


error[E0277]: the trait bound `(diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>): diesel::util::TupleSize` is not satisfied
  --> src/main.rs:96:10
   |
96 |         .load_with_info(&mut conn)?;
   |          ^^^^^^^^^^^^^^ the trait `diesel::util::TupleSize` is not implemented for `(diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>)`, which is required by `Pagination<SelectStatement<FromClause<table>, query_builder::select_clause::SelectClause<(diesel::expression::select_by::SelectBy<PartA, _>, diesel::expression::select_by::SelectBy<PartB, _>)>>>: LoadQuery<'_, PgConnection, (_, i64)>`
   |
   = help: the following other types implement trait `diesel::util::TupleSize`:
             (T0, T1)
             (T0, T1, T2)
             (T0, T1, T2, T3)
             (T0, T1, T2, T3, T4)
             (T0, T1, T2, T3, T4, T5)
             (T0, T1, T2, T3, T4, T5, T6)
             (T0, T1, T2, T3, T4, T5, T6, T7)
             (T0, T1, T2, T3, T4, T5, T6, T7, T8)
           and 24 others
   = note: required for `(PartA, PartB)` to implement `StaticallySizedRow<(diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>), Pg>`
   = note: required for `((PartA, PartB), i64)` to implement `FromStaticSqlRow<((diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>), BigInt), Pg>`
   = note: required for `((PartA, PartB), i64)` to implement `Queryable<((diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>), BigInt), Pg>`
   = note: required for `((PartA, PartB), i64)` to implement `FromSqlRow<((diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>), BigInt), Pg>`
   = note: required for `((diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>), BigInt)` to implement `load_dsl::private::CompatibleType<((PartA, PartB), i64), Pg>`
   = note: required for `Pagination<SelectStatement<FromClause<table>, query_builder::select_clause::SelectClause<(diesel::expression::select_by::SelectBy<PartA, Pg>, diesel::expression::select_by::SelectBy<PartB, Pg>)>>>` to implement `LoadQuery<'_, PgConnection, ((PartA, PartB), i64)>`
note: required by a bound in `Pagination::<T>::load_with_info`
  --> src/main.rs:29:15
   |
27 |     pub fn load_with_info<'a, U>(self, conn: &mut PgConnection) -> QueryResult<(Vec<U>, i64)>
   |            -------------- required by a bound in this associated function
28 |     where
29 |         Self: LoadQuery<'a, PgConnection, (U, i64)>,
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Pagination::<T>::load_with_info`

Originally posted by @Zstorm999 in https://github.com/diesel-rs/diesel/discussions/4234#discussioncomment-10557332

In particular, it seems that this impl https://github.com/diesel-rs/diesel/blob/79399de749dcc24283c2f2ddcd46be90042eb91a/diesel/src/type_impls/tuples.rs#L371-L384

and this one https://github.com/diesel-rs/diesel/blob/79399de749dcc24283c2f2ddcd46be90042eb91a/diesel/src/type_impls/tuples.rs#L342-L349

do not behave correctly together

Guide contributeur