2个版本

0.0.2 2022年2月11日
0.0.1 2022年2月11日

#8#tiberius

每月 下载 25

MIT/Apache

20KB
254

tiberius-derive

Tiberius 提供派生宏。

该crate提供了一个FromRow派生宏,允许更容易地解析和处理行。结构体字段必须实现FromSqlFromSqlOwned,并且FromRow解析行为由结构体容器属性控制。

应该可以自定义宏展开,通过'名称/键'或位置(位置更快,但强制结构体字段与SELECT子句中的值顺序相同)来获取列,无论是通过借用(实现FromSql的结构体字段)还是通过值(实现FromSqlOwned的结构体字段)。字段重命名也像在serde中一样实现(目前只实现了renameAll)。

所有功能仍处于alpha测试阶段,但已经在近一年的生产环境中用于多个关键/高可用服务,没有出现重大问题。错误处理可以大幅改进,目前没有支持通过字段属性来更改行为(例如,仅重命名单个结构体字段而不是所有字段)。

已知问题

目前有一个tiberius的bug(?)会破坏Option字段。Tiberius似乎总是将null Float(53)(double)列解析为Nullable(f32),无论列类型如何。

// DoubleColumn is a Nullable float(53)
let query = "SELECT
        [DoubleColumn]
    FROM 
        [TestRow]
";

let rows = client
        .simple_query(query)
        .await?
        .into_first_result()
        .await?;

let double : Option<f64> = rows[0].get("DoubleColumn") // Error: Conversion("cannot interpret F32(None) as an f64 value")

示例

FromRow: 通过引用和名称获取

字段必须实现FromSql。由于String没有实现FromSql,如果想要使用字符串,则需要使用#[tiberius_derive(auto)]容器属性。

https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/by_ref.rs

#[allow(non_snake_case)]
#[derive(FromRow)]
struct TestRow<'a> {
    pub Id: i32,
    pub VarCharRow: &'a str,
    pub NVarCharRow: &'a str,
    pub UuidRow: Uuid,
    pub LongRow: i64,
    pub DateTimeRow: chrono::NaiveDateTime,
    pub SmallIntRow: i16,
    pub BitRow: bool,
    pub FloatRow: f32,
    pub DoubleRow: f64,
    pub RealRow: f32,
}

async fn by_ref_not_null() -> Result<(), tiberius::error::Error> {
  let query = r"
  SELECT
      [Id],[VarCharRow],[NVarCharRow],[UuidRow],[LongRow],[DateTimeRow],[SmallIntRow],[BitRow],[FloatRow],[DoubleRow],[RealRow]
  FROM 
      [TiberiusDeriveTest].[dbo].[TestRow]
  ";

  let rows = client
      .simple_query(query)
      .await?
      .into_first_result()
      .await?;

  let rows = rows
      .iter()
      .map(TestRow::from_row)
      .collect::<Result<Vec<_>, _>>()?;
        
}

FromRow: 通过引用和位置获取

#[tiberius_derive(by_position)]

更快,但结构体字段必须与SELECT子句中的值顺序相同。

https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/by_ref_by_position.rs

#[derive(FromRow)]
#[tiberius_derive(by_position)]
struct TestRow<'a> {
    pub id: i32,
    pub var_char_row: &'a str,
    pub n_var_char_row: &'a str,
    pub uuid_row: Uuid,
    pub long_row: i64,
    pub date_time_row: chrono::NaiveDateTime,
    pub small_int_row: i16,
    pub bit_row: bool,
    pub float_row: f32,
    pub double_row: f64,
    pub real_row: f32,
}


async fn by_ref_indexed_not_null() -> Result<(), tiberius::error::Error> {
    let mut client = connect_localhost().await.unwrap();
    let query = r"
    SELECT
        [Id],[VarCharRow],[NVarCharRow],[UuidRow],[LongRow],[DateTimeRow],[SmallIntRow],[BitRow],[FloatRow],[DoubleRow],[RealRow]
    FROM 
        [TiberiusDeriveTest].[dbo].[TestRow]
        ";

    let rows = client
        .simple_query(query)
        .await?
        .into_first_result()
        .await?;

    let rows = rows
        .iter()
        .map(TestRow::from_row)
        .collect::<Result<Vec<_>, _>>()?;
}


FromRow: 通过值(和位置)获取

#[tiberius_derive(owned)]

字段必须实现FromSqlOwned,并且结构体字段必须与SELECT子句中的值顺序相同。

https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/by_value.rs


#[allow(non_snake_case)]
#[derive(FromRow)]
#[tiberius_derive(owned)]
struct TestRow {
    pub id: i32,
    pub var_char_row: String,
    pub n_var_char_row: String,
    pub uuid_row: Uuid,
    pub long_row: i64,
    pub date_time_row: chrono::NaiveDateTime,
    pub small_int_row: i16,
    pub bit_row: bool,
    pub float_row: f32,
    pub double_row: f64,
    pub real_row: f32,
}


#[tokio::test]
async fn by_value() -> Result<(), tiberius::error::Error> {
    let mut client = connect_localhost().await.unwrap();
    let query = r"
    SELECT
        [Id],[VarCharRow],[NVarCharRow],[UuidRow],[LongRow],[DateTimeRow],[SmallIntRow],[BitRow],[FloatRow],[DoubleRow],[RealRow]
    FROM 
        [TiberiusDeriveTest].[dbo].[TestRow]
        ";

    let rows = client
        .simple_query(query)
        .await?
        .into_first_result()
        .await?;

    let rows = rows
        .into_iter()
        .map(TestRow::from_row)
        .collect::<Result<Vec<_>, _>>()?;
 }

FromRow: RenameAll

#[tiberius_derive(rename_all= "PascalCase")]

https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/rename_all.rs

重命名字段(如果通过名称获取列数据)

#[derive(FromRowa)]
#[tiberius_derive(rename_all = "PascalCase")]
struct TestRow<'a> {
    pub id: i32,
    pub var_char_row: &'a str,
    pub n_var_char_row: &'a str,
    pub uuid_row: Uuid,
    pub long_row: i64,
    pub date_time_row: chrono::NaiveDateTime,
    pub small_int_row: i16,
    pub bit_row: bool,
    pub float_row: f32,
    pub double_row: f64,
    pub real_row: f32,
}

#[tokio::test]
async fn rename_all() -> Result<(), tiberius::error::Error> {
    let mut client = connect_localhost().await.unwrap();
    let query = r"
    SELECT
        [Id],[VarCharRow],[NVarCharRow],[UuidRow],[LongRow],[DateTimeRow],[SmallIntRow],[BitRow],[FloatRow],[DoubleRow],[RealRow]
    FROM 
        [TiberiusDeriveTest].[dbo].[TestRow]
        ";

    let rows = client
        .simple_query(query)
        .await?
        .into_first_result()
        .await?;

    let rows = rows
        .iter()
        .map(TestRow::from_row)
        .collect::<Result<Vec<_>, _>>()?;
 }

FromRow: 通过克隆将 &str 转换为 String

#[tiberius_derive(自动)]

https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/str_auto_owned.rs

由于字符串没有实现 FromSql,因此拥有一个执行此操作的特性是相当便利的。


#[allow(non_snake_case)]
#[derive(FromRow)]
#[tiberius_derive(auto)]
struct TestRow {
    pub Id: i32,
    pub VarCharRow: String,
    pub NVarCharRow: String,
    pub UuidRow: Uuid,
    pub LongRow: i64,
    pub DateTimeRow: chrono::NaiveDateTime,
    pub SmallIntRow: i16,
    pub BitRow: bool,
    pub FloatRow: f32,
    pub DoubleRow: f64,
    pub RealRow: f32,
}

#[tokio::test]
async fn by_ref_auto_not_null() -> Result<(), tiberius::error::Error> {
    let mut client = connect_localhost().await.unwrap();
    let query = r"
    SELECT
        [Id],[VarCharRow],[NVarCharRow],[UuidRow],[LongRow],[DateTimeRow],[SmallIntRow],[BitRow],[FloatRow],[DoubleRow],[RealRow]
    FROM 
        [TiberiusDeriveTest].[dbo].[TestRow]
        ";

    let rows = client
        .simple_query(query)
        .await?
        .into_first_result()
        .await?;

    let rows = rows
        .iter()
        .map(TestRow::from_row)
        .collect::<Result<Vec<_>, _>>()?;
}


依赖项

~5–16MB
~201K SLoC