rust - 如何为具有映射到多个柴油列的自定义字段的类型派生 Queryable?

标签 rust rust-diesel

我正在使用 Diesel crate执行一些数据库工作。在某些表中,应将表的两列一起视为一个键。
这种模式在数据库中的许多地方都重复出现,因此最好避免使用大量重复的复制粘贴代码来处理这种情况。但是,我无法说服 Diesel 自动生成可以在查询或插入中使用的类型。
考虑表格

table! {
    records (iid) {
        iid -> Integer,
        id_0 -> BigInt,
        id_1 -> BigInt,
        data -> Text,
    }
}
和理想类型
#[derive(Debug, Copy, Clone, FromSqlRow)]
pub struct RecordId {
    id_0: i64,
    id_1: i64,
}

#[derive(Queryable, Debug)]
pub struct Record {
    pub iid: i32,
    pub id: RecordId,
    pub data: String,
}
此代码编译正常,但是当我尝试使用它时出现错误,例如:
pub fn find(connection: &SqliteConnection) -> types::Record {
    records
        .find(1)
        .get_result::<types::Record>(connection)
        .unwrap()
}
产生:
error[E0277]: the trait bound `(i32, types::RecordId, std::string::String): diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not satisfied
  --> src/main.rs:76:21
   |
76 |     records.find(1).get_result::<types::Record>(connection).unwrap()
   |                     ^^^^^^^^^^ the trait `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` is not implemented for `(i32, types::RecordId, std::string::String)`
   |
   = help: the following implementations were found:
             <(A, B, C) as diesel::Queryable<(SA, SB, SC), __DB>>
             <(A, B, C) as diesel::Queryable<diesel::sql_types::Record<(SA, SB, SC)>, diesel::pg::Pg>>
   = note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Integer, diesel::sql_types::BigInt, diesel::sql_types::BigInt, diesel::sql_types::Text), _>` for `types::Record`
   = note: required because of the requirements on the impl of `diesel::query_dsl::LoadQuery<_, types::Record>` for `diesel::query_builder::SelectStatement<types::records::table, diesel::query_builder::select_clause::DefaultSelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause<diesel::expression::operators::Eq<types::records::columns::iid, diesel::expression::bound::Bound<diesel::sql_types::Integer, i32>>>>`
如果我创建的版本不包含 RecordId但直接有子片,则没有错误:
pub struct RecordDirect {
    pub iid: i32,
    pub id_0: i64,
    pub id_1: i64,
    pub data: String,
}

// ...

pub fn find_direct(connection: &SqliteConnection) -> types::RecordDirect {
    records
        .find(1)
        .get_result::<types::RecordDirect>(connection)
        .unwrap()
}
同样,我可以手动实现 Queryable特质,这也行得通,
#[derive(Debug)]
pub struct RecordManual {
    pub iid: i32,
    pub id: RecordId,
    pub data: String,
}

impl Queryable<records::SqlType, diesel::sqlite::Sqlite> for RecordManual {
    type Row = (i32, i64, i64, String);
    fn build(row: Self::Row) -> Self {
        RecordManual {
            iid: row.0,
            id: RecordId {
                id_0: row.1,
                id_1: row.2,
            },
            data: row.3,
        }
    }
}

// ...

pub fn find_manual(connection: &SqliteConnection) -> types::RecordManual {
    records
        .find(1)
        .get_result::<types::RecordManual>(connection)
        .unwrap()
}
这种情况很难维护,我无法弄清楚如何让它为插入工作——手动实现 Insertable似乎比 Queryable 要复杂一些.
为了让任何看到它的人都能更轻松地使用它,我创建了一个存储库,其中包含一个几乎可以编译的小型复制器,其中包含这篇文章中的代码块。 (通常我会把它放在使用rust 的操场上,但这不支持柴油)。您可以在 https://github.com/mikeando/diesel_custom_type_demo 找到该代码.
有没有办法制作 #[derive(Queryable)] (和 #[derive(Insertable)] )适用于这些情况?

初始失败案例的最小复制器是:
#[macro_use]
extern crate diesel;

use diesel::prelude::*;

mod types {
    use diesel::deserialize::Queryable;
    use diesel::sqlite::SqliteConnection;

    table! {
        records (iid) {
            iid -> Integer,
            id_0 -> BigInt,
            id_1 -> BigInt,
            data -> Text,
        }
    }

    #[derive(Debug, Copy, Clone, FromSqlRow)]
    pub struct RecordId {
        id_0: i64,
        id_1: i64,
    }

    // Using a RecordId in a Record compiles, but 
    // produces an error when used in an actual query
    #[derive(Queryable, Debug)]
    pub struct Record {
        pub iid: i32,
        pub id: RecordId,
        pub data: String,
    }
}

use types::records::dsl::*;

pub fn find(connection:&SqliteConnection) -> types::Record {
    records.find(1).get_result::<types::Record>(connection).unwrap()
}

最佳答案

Is there a way to make the #[derive(Queryable)] (and #[derive(Insertable)]) work for these kinds of cases?


对于 #[derive(Insertable)]这可以通过添加 #[diesel(embedded)] 来实现给您的id字段和 #[derive(Insertable)]在两个结构上。参见 Insertable 的文档详情。
对于 #[derive(Queryable)]这是不可能的,因为 Queryable应该是从查询结果到结构的普通映射,基本假设输出的“形状”保持不变(至少对于派生而言)。

关于rust - 如何为具有映射到多个柴油列的自定义字段的类型派生 Queryable?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63571163/

相关文章:

rust - 封装可变切片修改

rust - 如何将 std::hash::Hasher 放入 Box 中?

mysql - 无法使用柴油箱从 mysql 数据库加载结果

sql - 如何使用 Diesel 计算数组列中不同元素的数量?

sqlite - 使用rust 柴油 : SQLite INSERT RETURNING multiple ids

不支持 PostgreSQL 身份验证方法 10

rust 柴油有条件地过滤一个查询

rust - 有没有办法在不使它成为静态mut的情况下初始化一个非平凡的静态std::collections::HashMap?

json - 从 Rust 中的 Json 枚举中读取特定字段

binary - 如何将二进制值输入 Rust 中的向量?