Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Rust 如何在Diesel中为自定义字段类型实现可查询和可插入?_Rust_Rust Diesel - Fatal编程技术网

Rust 如何在Diesel中为自定义字段类型实现可查询和可插入?

Rust 如何在Diesel中为自定义字段类型实现可查询和可插入?,rust,rust-diesel,Rust,Rust Diesel,我有一个SQL表,我想通过Diesel使用它: CREATE TABLE records ( id BIGSERIAL PRIMARY KEY, record_type SMALLINT NOT NULL, value DECIMAL(10, 10) NOT NULL ) 此表生成以下架构: table! { records (id) { id -> Int8, record_type -> Int2,

我有一个SQL表,我想通过Diesel使用它:

CREATE TABLE records (
    id BIGSERIAL PRIMARY KEY,
    record_type SMALLINT NOT NULL,
    value DECIMAL(10, 10) NOT NULL
)
此表生成以下架构:

table! {
    records (id) {
        id -> Int8,
        record_type -> Int2,
        value -> Numeric,
    }
}
Diesel将小数导出为
bigdecimal::bigdecimal
,但我希望使用
decimal::d128
。我还想将
record\u type
映射到一个枚举,因此我声明我的模型如下:

use decimal::d128;

pub enum RecordType {
    A,
    B,
}

pub struct Record {
    pub id: i64,
    pub record_type: RecordType,
    pub value: d128,
}
由于非标准类型映射,我无法使用
#派生(Queryable,Insertable)
,因此我尝试自己实现这些特性:

impl Queryable<records::SqlType, Pg> for Record {
    type Row = (i64, i16, BigDecimal);

    fn build(row: Self::Row) -> Self {
        Record {
            id: row.0,
            record_type: match row.1 {
                1 => RecordType::A,
                2 => RecordType::B,
                _ => panic!("Wrong record type"),
            },
            value: d128!(format!("{}", row.2)),
        }
    }
}

有时,理解宏的作用(派生只是宏的一种不同形式)的最简单方法是向编译器索取扩展代码。对于夜间编译器,可以使用以下命令执行此操作:

cargo rustc -- -Z unstable-options --pretty expanded > expanded.rs
这将在
expanded.rs
中输出扩展代码

现在,我们可以查看此文件以了解
#[派生(可插入)]
的扩展内容。当然,我首先更改了
Record
的定义,以匹配Diesel使用的类型。经过一些清理后,这是生成的代码:

impl<'insert> diesel::insertable::Insertable<records::table> for &'insert Record {
    type Values = <(
        Option<diesel::dsl::Eq<records::id, &'insert i64>>,
        Option<diesel::dsl::Eq<records::record_type, &'insert i16>>,
        Option<diesel::dsl::Eq<records::value, &'insert BigDecimal>>
    ) as diesel::insertable::Insertable<records::table>>::Values;

    #[allow(non_shorthand_field_patterns)]
    fn values(self) -> Self::Values {
        let Record {
            id: ref id,
            record_type: ref record_type,
            value: ref value,
        } = *self;
        diesel::insertable::Insertable::values((
            Some(::ExpressionMethods::eq(records::id, id)),
            Some(::ExpressionMethods::eq(records::record_type, record_type)),
            Some(::ExpressionMethods::eq(records::value, value))))
    }
}

impl diesel::query_builder::UndecoratedInsertRecord<records::table> for Record {
}

我发现创建实现
ToSql
FromSql
的新类型包装器更方便。然后,您可以使用这些基本块进行构建,以创建可以派生
Queryable
/
Insertable
的更大类型

此示例仅显示如何执行枚举与
SmallInt
之间的映射,但十进制的情况相同。唯一的区别在于如何执行转换:

#[macro_use]
extern crate diesel;

mod types {
    use diesel::sql_types::*;
    use diesel::backend::Backend;
    use diesel::deserialize::{self, FromSql};
    use diesel::serialize::{self, ToSql, Output};
    use std::io;

    table! {
        records (id) {
            id -> BigInt,
            record_type -> SmallInt,
        }
    }

    #[derive(Debug, Copy, Clone, AsExpression, FromSqlRow)]
    #[sql_type = "SmallInt"]
    pub enum RecordType {
        A,
        B,
    }

    impl<DB: Backend> ToSql<SmallInt, DB> for RecordType
    where
        i16: ToSql<SmallInt, DB>,
    {
        fn to_sql<W>(&self, out: &mut Output<W, DB>) -> serialize::Result
        where
            W: io::Write,
        {
            let v = match *self {
                RecordType::A => 1,
                RecordType::B => 2,
            };
            v.to_sql(out)
        }
    }

    impl<DB: Backend> FromSql<SmallInt, DB> for RecordType
    where
        i16: FromSql<SmallInt, DB>,
    {
        fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
            let v = i16::from_sql(bytes)?;
            Ok(match v {
                1 => RecordType::A,
                2 => RecordType::B,
                _ => return Err("replace me with a real error".into()),
            })
        }
    }

    #[derive(Insertable, Queryable, Debug)]
    #[table_name = "records"]
    pub struct Record {
        pub id: i64,
        pub record_type: RecordType,
    }
}
#[宏使用]
外部板条箱柴油机;
mod类型{
使用diesel::sql_类型::*;
使用柴油机::后端::后端;
使用diesel::反序列化:{self,FromSql};
使用diesel::serialize::{self,ToSql,Output};
使用std::io;
桌子{
记录(id){
id->BigInt,
记录类型->SmallInt,
}
}
#[派生(调试、复制、克隆、AsExpression、FromSqlRow)]
#[sql_type=“SmallInt”]
发布枚举记录类型{
A.
B
}
记录类型的impl-ToSql
哪里
i16:ToSql,
{
fn to_sql(&self,out:&mut Output)->序列化::结果
哪里
W:io::写,
{
设v=match*self{
RecordType::A=>1,
记录类型::B=>2,
};
v、 to_sql(输出)
}
}
记录类型的impl-FromSql
哪里
i16:FromSql,
{
fn from_sql(bytes:Option)->反序列化::结果{
设v=i16::from_sql(字节)?;
好(第五场){
1=>RecordType::A,
2=>RecordType::B,
_=>返回Err(“用真正的错误替换我“.into()),
})
}
}
#[派生(可插入、可查询、调试)]
#[表_name=“记录”]
发布结构记录{
酒吧id:i64,
发布记录类型:记录类型,
}
}
有一个,但它还没有提到整个类型的
#[sql\u type]
。这让Diesel知道数据库内部需要什么类型的底层存储


另请参见。

是的,这将是理想的,但不幸的是,decimal::d128有~30个方法和实现>50个特性,因此重新实现包装器结构的特性将是一件非常麻烦的事情。@hweom您可以添加一个
Deref
实现,因此,您不必实现这些。感谢您的详细解释,以及关于如何查看扩展代码的提示。我仍然会接受谢普马斯特的回答,因为我不想明确使用柴油机内部构件,这看起来已经够吓人了。
impl<'insert> diesel::insertable::Insertable<records::table> for &'insert Record {
    type Values = <(
        Option<diesel::dsl::Eq<records::id, i64>>,
        Option<diesel::dsl::Eq<records::record_type, i16>>,
        Option<diesel::dsl::Eq<records::value, BigDecimal>>
    ) as diesel::insertable::Insertable<records::table>>::Values;

    #[allow(non_shorthand_field_patterns)]
    fn values(self) -> Self::Values {
        let Record {
            id: ref id,
            record_type: ref record_type,
            value: ref value,
        } = *self;
        let record_type = match *record_type {
            RecordType::A => 1,
            RecordType::B => 2,
        };
        let value: BigDecimal = value.to_string().parse().unwrap();
        diesel::insertable::Insertable::values((
            Some(::ExpressionMethods::eq(records::id, *id)),
            Some(::ExpressionMethods::eq(records::record_type, record_type)),
            Some(::ExpressionMethods::eq(records::value, value))))
    }
}
#[macro_use]
extern crate diesel;

mod types {
    use diesel::sql_types::*;
    use diesel::backend::Backend;
    use diesel::deserialize::{self, FromSql};
    use diesel::serialize::{self, ToSql, Output};
    use std::io;

    table! {
        records (id) {
            id -> BigInt,
            record_type -> SmallInt,
        }
    }

    #[derive(Debug, Copy, Clone, AsExpression, FromSqlRow)]
    #[sql_type = "SmallInt"]
    pub enum RecordType {
        A,
        B,
    }

    impl<DB: Backend> ToSql<SmallInt, DB> for RecordType
    where
        i16: ToSql<SmallInt, DB>,
    {
        fn to_sql<W>(&self, out: &mut Output<W, DB>) -> serialize::Result
        where
            W: io::Write,
        {
            let v = match *self {
                RecordType::A => 1,
                RecordType::B => 2,
            };
            v.to_sql(out)
        }
    }

    impl<DB: Backend> FromSql<SmallInt, DB> for RecordType
    where
        i16: FromSql<SmallInt, DB>,
    {
        fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
            let v = i16::from_sql(bytes)?;
            Ok(match v {
                1 => RecordType::A,
                2 => RecordType::B,
                _ => return Err("replace me with a real error".into()),
            })
        }
    }

    #[derive(Insertable, Queryable, Debug)]
    #[table_name = "records"]
    pub struct Record {
        pub id: i64,
        pub record_type: RecordType,
    }
}