2个版本
0.0.2 | 2022年2月11日 |
---|---|
0.0.1 | 2022年2月11日 |
#8 在 #tiberius
每月 下载 25 次
20KB
254 行
tiberius-derive
为 Tiberius 提供派生宏。
该crate提供了一个FromRow派生宏,允许更容易地解析和处理行。结构体字段必须实现FromSql或FromSqlOwned,并且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子句中的值顺序相同。
#[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")]
重命名字段(如果通过名称获取列数据)
#[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(自动)]
由于字符串没有实现 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