Derive macros for Tiberius.
This crate provides a FromRow derive macro that allows for easier parsing and error handling of rows. Struct fields must implement either FromSql or FromSqlOwned and FromRow parsing behaviour is controlled by struct container attributes.
It should be possible to customize macro expansion to get columns by 'name/key' or by position (by position being faster, but forcing the struct fields to be in the same order as the values in the SELECT clause), either by borrowing (struct fields implementing FromSql) or by value (struct fields implementing FromSqlOwned). Field renaming is also implemented similarly as in serde (right now only renameAll is implemented).
Everything is still in alpha, and there is a lot of functionality to be implemented but have been using this in production in a bunch of critical/HA services for close to an year now without major issues. Error handling could be vastly improved, and there is no support for changing the behaviour with field attributes (ex: renaming a single struct field instead of all of them) at the moment.
There is a tiberius bug(?) that breaks Option
```rust // DoubleColumn is a Nullable float(53) let query = "SELECT [DoubleColumn] FROM [TestRow] ";
let rows = client .simplequery(query) .await? .intofirst_result() .await?;
let double : Option
```
Fields must implement FromSql. Since String
does not implement FromSql, if you want to use strings you need the #[tiberius_derive(auto)] container
attrbute.
https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/by_ref.rs
```rust
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 byrefnot_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 .simplequery(query) .await? .intofirst_result() .await?;
let rows = rows
.iter()
.map(TestRow::from_row)
.collect::
} ```
#[tiberius_derive(by_position)]
Faster, but struct fields must be in the same order as the values in the SELECT clause.
https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/byrefby_position.rs
``` rust
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 real_row: f32, }
async fn byrefindexednotnull() -> 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<_>, _>>()?;
}
```
#[tiberius_derive(owned)]
Fields must implement FromSqlOwned, and struct fields must be in the same order as the values in the SELECT clause.
https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/by_value.rs
``` rust
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 real_row: f32, }
async fn byvalue() -> Result<(), tiberius::error::Error> { let mut client = connectlocalhost().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<_>, _>>()?;
}
```
#[tiberius_derive(rename_all = "PascalCase")]
https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/rename_all.rs
Renames fields (if getting column data by name)
``` rust
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 real_row: f32, }
async fn renameall() -> Result<(), tiberius::error::Error> { let mut client = connectlocalhost().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<_>, _>>()?;
} ```
#[tiberius_derive(auto)]
https://github.com/mobiltracker/tiberius-derive/blob/master/tiberius-derive-tests/tests/strautoowned.rs
Since strings do not implement FromSql, having an attribute to do this is reasonably ergonomic.
``` rust
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, }
async fn byrefautonotnull() -> 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<_>, _>>()?;
}
```