Reputation: 135
I am trying to manually implement sqlx::FrowRow
instead of using derive
, since some custom initialization logic is needed (ex. convert integer i16
to enum).
Default impl is generated like this:
use sqlx::FromRow;
impl FromRow for User {
fn from_row(row: &'r R) -> Result<Self, sqlx::Error> {
todo!()
}
}
But it's unclear, what lifetime and type should I use.
Upvotes: 12
Views: 8138
Reputation: 4466
To add to @kmdreco's answer:
If you want to implement it in a generic way over different types of databases, you could also do
use sqlx::{Row, FromRow};
struct User {
name: String,
status: i16,
}
impl<R: Row> FromRow<'_, R> for User {
fn from_row(row: &R) -> Result<Self, sqlx::Error> {
Ok(User {
name: row.try_get("name")?,
status: row.try_get("status")?,
})
}
}
still works as of sqlx ^0.7
For non-native field types, you'll get an error saying the type needs to implement some bound, add that exact bound to your trait under where
Upvotes: 0
Reputation: 60672
The FromRow
trait is extremely generic since it is used for different backend databases that may support different types. You can see how many generic constraints are needed to implement a simple struct by looking at what #[derive(FromRow)]
generates (via cargo expand
):
use sqlx::FromRow;
#[derive(FromRow)]
struct User {
name: String,
status: i16,
}
// generates
impl<'a, R: ::sqlx::Row> ::sqlx::FromRow<'a, R> for User
where
&'a ::std::primitive::str: ::sqlx::ColumnIndex<R>,
String: ::sqlx::decode::Decode<'a, R::Database>,
String: ::sqlx::types::Type<R::Database>,
i16: ::sqlx::decode::Decode<'a, R::Database>,
i16: ::sqlx::types::Type<R::Database>,
{
fn from_row(row: &'a R) -> ::sqlx::Result<Self> {
let name: String = row.try_get("name")?;
let status: i16 = row.try_get("status")?;
::std::result::Result::Ok(User { name, status })
}
}
It has to constrain that the columns can be indexed by name as well as constrain that String
and i16
are valid and can be decoded from the database.
Doing it yourself, you're probably better off picking which database Row
type you intend to use (AnyRow
, MssqlRow
, MySqlRow
, PgRow
, SqliteRow
) and implement it for that. Here using PgRow
for postgres:
use sqlx::{Row, FromRow, Error};
use sqlx::postgres::PgRow;
struct User {
name: String,
status: i16,
}
impl<'r> FromRow<'r, PgRow> for User {
fn from_row(row: &'r PgRow) -> Result<Self, Error> {
let name = row.try_get("name")?;
let status = row.try_get("status")?;
Ok(User{ name, status })
}
}
I do wonder though if there's a different recommendation for doing custom transformations (via a DTO or some other mechanism).
Upvotes: 14