enhancement: (#2934) Allows flatten attribute to work for Optional values when deriving FromRow
fixes #2934
This change appears innocuous enough, but I'm worried that people won't understand what this impl is for and will assume it does something different, i.e. make .fetch_one() return None instead of erroring on an empty resultset. That's not really possible with our current API design, which makes this impl a potential footgun since it'd compile but behave differently than expected at runtime.
I think I'd prefer if this was something much more specific to #[sqlx(flatten)], and I think we can accomplish that without adding a new public blanket impl, using a trick called autoderef specialization: http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html
I used the same trick in https://github.com/launchbadge/sqlx/pull/3356 although the solution I'm imagining isn't going to look exactly like that.
(As for why to use autoderef specialization instead of just checking if the type is wrapped in Option: that can't see through type aliases.)
I agree it would be inconsistent with the way .fetch_one() works right now, I'll look into the suggestions you made, thanks !
Hey, @abonander , I managed to get some of the way there using the autoderef trick as you suggested, however for the optional type I'm having difficulty figuring out how to do the bounds properly:
This is the what the macro expands to:
#[automatically_derived]
impl<'a, R: ::sqlx::Row> ::sqlx::FromRow<'a, R> for Bar
where
&'a ::std::primitive::str: ::sqlx::ColumnIndex<R>,
u64: ::sqlx::decode::Decode<'a, R::Database>,
u64: ::sqlx::types::Type<R::Database>,
String: ::sqlx::decode::Decode<'a, R::Database>,
String: ::sqlx::types::Type<R::Database>,
Option<Foo>: ::sqlx::FromRow<'a, R>, // Should be Foo: ::sqlx::FromRow<'a, R>
{
fn from_row(__row: &'a R) -> ::sqlx::Result<Self> {
let id: u64 = __row.try_get("id")?;
let value: String = __row.try_get("value")?;
let foo: Option<Foo> = {
use ::sqlx_core::from_row::{FromOptRow, Wrapper};
let value: Result<Option<Foo>, sqlx::Error> = Wrapper.__from_row(__row);
value
}?;
::std::result::Result::Ok(Bar { id, value, foo })
}
}
Is there I way to know what #ty here should be ? Just checking for an Option wouldn't work because of type aliases as you mentioned.
predicates.push(parse_quote!(#ty: ::sqlx::FromRow<#lifetime, R>));
Full code on this branch: https://github.com/jonnyso/sqlx/tree/autoderef_flatten_opt