Diesel:BoxableExpressions 在表及其连接上通用吗?

Ada*_*dam 2 rust rust-diesel

我正在尝试在运行时构建一些过滤器,这些过滤器可以应用于表tunneltunnel LEFT OUTER JOIN connection ON (tunnel.id = connection.tunnel_id).

这些表的定义如下:


// Define the tunnel table and struct
table! {
    #[allow(unused_imports)]
    use diesel::sql_types::*;
    tunnel (id) {
        id -> BigInt,
        name -> Text,
    }
}
#[derive(Queryable, Identifiable, Clone, Debug, PartialEq, Eq)]
#[table_name = "tunnel"]
pub struct Tunnel {
    pub id: i64,
    pub name: String,
}

// Define the connection table and struct
table! {
    #[allow(unused_imports)]
    use diesel::sql_types::*;
    connection(id) {
        id -> BigInt,
        tunnel_id -> BigInt,
    }
}

#[derive(Debug, Associations, Identifiable, Queryable)]
#[table_name = "connection"]
#[primary_key(id)]
#[belongs_to(Tunnel)]
pub struct Connection {
    pub id: i64,
    pub tunnel_id: i64,
}

joinable!(connection -> tunnel(tunnel_id));
allow_tables_to_appear_in_same_query!(connection, tunnel);
Run Code Online (Sandbox Code Playgroud)

我可以编写一个为单个表构造动态的函数:

fn filters_t(
    name: &'static str,
) -> Vec<Box<dyn BoxableExpression<tunnel::table, Pg, SqlType = Bool>>> {
    let mut wheres: Vec<Box<dyn BoxableExpression<tunnel::table, Pg, SqlType = Bool>>> = Vec::new();
    wheres.push(Box::new(tunnel::name.eq(name)));
    wheres
}
Run Code Online (Sandbox Code Playgroud)

或者对于加入:

pub type TunnelJoinConnection = JoinOn<
    Join<tunnel::table, connection::table, LeftOuter>,
    Eq<Nullable<connection::columns::tunnel_id>, Nullable<tunnel::columns::id>>,
>;

fn filters_j(
    name: &'static str,
) -> Vec<Box<dyn BoxableExpression<TunnelJoinConnection, Pg, SqlType = Bool>>> {
    let mut wheres: Vec<Box<dyn BoxableExpression<TunnelJoinConnection, Pg, SqlType = Bool>>> =
        Vec::new();
    wheres.push(Box::new(tunnel::name.eq(name)));
    wheres
}
Run Code Online (Sandbox Code Playgroud)

请注意,这两个过滤器函数具有完全相同的函数体,因此我应该能够创建一个实现这两个过滤器函数的通用函数。但是当我尝试使其通用时出现错误。

fn filters<T>(name: &'static str) -> Vec<Box<dyn BoxableExpression<T, Pg, SqlType = Bool>>>
where
    T: AppearsInFromClause<tunnel::table, Count = Once>,
{
    vec![Box::new(tunnel::name.eq(name))]
}
Run Code Online (Sandbox Code Playgroud)

错误是

   |
85 |     vec![Box::new(tunnel::name.eq(name))]
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `diesel::SelectableExpression<T>` is not implemented for `tunnel::columns::name`
   |
   = note: required because of the requirements on the impl of `diesel::SelectableExpression<T>` for `diesel::expression::operators::Eq<tunnel::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
   = note: required because of the requirements on the impl of `diesel::BoxableExpression<T, diesel::pg::Pg>` for `diesel::expression::operators::Eq<tunnel::columns::name, diesel::expression::bound::Bound<diesel::sql_types::Text, &str>>`
   = note: required for the cast to the object type `dyn diesel::BoxableExpression<T, diesel::pg::Pg, SqlType = diesel::sql_types::Bool>`
Run Code Online (Sandbox Code Playgroud)

这里是最小示例,您可以克隆该最小示例并cargo check自行运行以查看错误。

wei*_*ich 5

这个问题可以通过一个小改动来解决:

fn filters<T>(name: &'static str) -> Vec<Box<dyn BoxableExpression<T, Pg, SqlType = Bool>>>
where
    diesel::dsl::Eq<tunnel::name, &'static str>: BoxableExpression<T, Pg, SqlType = Bool>,
{
    vec![Box::new(tunnel::name.eq(name))]
}
Run Code Online (Sandbox Code Playgroud)

基本上,您需要在编译时断言您的盒装表达式确实实现了BoxableExpression<T, Pg, SqlType = Bool>所有可能的T。如果 rustc 仅T检查特定的情况,则对于一般情况,需要显式写出。辅助diesel::dsl::Eq类型是 所返回类型的类型级构造函数tunnel::name.eq(name)。这意味着您添加到列表中的每个表达式都需要一个类似的子句。

另一个不相关的注释:

pub type TunnelJoinConnection = JoinOn<
    Join<tunnel::table, connection::table, LeftOuter>,
    Eq<Nullable<connection::columns::tunnel_id>, Nullable<tunnel::columns::id>>,
>;
Run Code Online (Sandbox Code Playgroud)

使用不被视为柴油公共 API 一部分的类型。这意味着这样的表达式可能会随着任何更新而中断。使用公共 API 编写此类型的正确方法是

pub type TunnelJoinConnection = diesel::dsl::LeftJoin<tunnel::table, connection::table>;`
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您的全面回答。 (2认同)