diesel-rs/diesel

Missing impl for tuples in custom QueryFragment

Open

#4,292 建立於 2024年9月30日

在 GitHub 查看
 (5 留言) (0 反應) (0 負責人)Rust (12,054 star) (1,003 fork)batch import
help wantedhole in api

描述

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

貢獻者指南