From 2003e2687926a18af7c33740917b23fb8c8d76f2 Mon Sep 17 00:00:00 2001 From: Jonny Silva Date: Sun, 5 Jan 2025 20:32:19 -0300 Subject: [PATCH 1/4] enhancement: (#2934) implement FromRow for Option where T: FromRow --- sqlx-core/src/from_row.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sqlx-core/src/from_row.rs b/sqlx-core/src/from_row.rs index 9c647d370a..a3bf6bd2a1 100644 --- a/sqlx-core/src/from_row.rs +++ b/sqlx-core/src/from_row.rs @@ -1,4 +1,7 @@ -use crate::{error::Error, row::Row}; +use crate::{ + error::{Error, UnexpectedNullError}, + row::Row, +}; /// A record that can be built from a row returned by the database. /// @@ -285,6 +288,22 @@ where } } +impl<'r, R, T> FromRow<'r, R> for Option +where + R: Row, + T: FromRow<'r, R>, +{ + fn from_row(row: &'r R) -> Result { + let value = T::from_row(row).map(Some); + if let Err(Error::ColumnDecode { source, .. }) = value.as_ref() { + if let Some(UnexpectedNullError) = source.downcast_ref() { + return Ok(None); + } + } + value + } +} + // implement FromRow for tuples of types that implement Decode // up to tuples of 9 values From 5ad8a4e10d03d455c0994673ca0da781b886a0e6 Mon Sep 17 00:00:00 2001 From: Jonny Silva Date: Tue, 7 Jan 2025 17:14:39 -0300 Subject: [PATCH 2/4] macro expands mostly correctly but need to figure out how to do trait bounds properly --- sqlx-core/src/from_row.rs | 58 +++++++++++++++++++++-------- sqlx-macros-core/src/derives/row.rs | 3 +- src/lib.rs | 3 +- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/sqlx-core/src/from_row.rs b/sqlx-core/src/from_row.rs index a3bf6bd2a1..6181484d69 100644 --- a/sqlx-core/src/from_row.rs +++ b/sqlx-core/src/from_row.rs @@ -288,22 +288,6 @@ where } } -impl<'r, R, T> FromRow<'r, R> for Option -where - R: Row, - T: FromRow<'r, R>, -{ - fn from_row(row: &'r R) -> Result { - let value = T::from_row(row).map(Some); - if let Err(Error::ColumnDecode { source, .. }) = value.as_ref() { - if let Some(UnexpectedNullError) = source.downcast_ref() { - return Ok(None); - } - } - value - } -} - // implement FromRow for tuples of types that implement Decode // up to tuples of 9 values @@ -506,3 +490,45 @@ impl_from_row_for_tuple!( (14) -> T15; (15) -> T16; ); + +pub struct Wrapper; + +pub trait FromOptRow<'r, R, T> { + fn __from_row(&self, row: &'r R) -> Result; +} + +impl<'r, R, T> FromOptRow<'r, R, Option> for &Wrapper +where + R: Row, + T: FromRow<'r, R>, +{ + fn __from_row(&self, row: &'r R) -> Result, Error> { + let value = T::from_row(row).map(Some); + if let Err(Error::ColumnDecode { source, .. }) = value.as_ref() { + if let Some(UnexpectedNullError) = source.downcast_ref() { + return Ok(None); + } + } + value + } +} + +impl<'r, R, T> FromOptRow<'r, R, T> for Wrapper +where + R: Row, + T: FromRow<'r, R>, +{ + fn __from_row(&self, row: &'r R) -> Result { + T::from_row(row) + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __from_opt_row { + ($t:ty, $row:expr) => {{ + use $crate::from_row::{FromOptRow, Wrapper}; + let value: Result<$t, sqlx::Error> = Wrapper.__from_row($row); + value + }}; +} diff --git a/sqlx-macros-core/src/derives/row.rs b/sqlx-macros-core/src/derives/row.rs index 5f7b6dca70..4ddc1f32af 100644 --- a/sqlx-macros-core/src/derives/row.rs +++ b/sqlx-macros-core/src/derives/row.rs @@ -109,7 +109,8 @@ fn expand_derive_from_row_struct( // Flatten (true, None, false) => { predicates.push(parse_quote!(#ty: ::sqlx::FromRow<#lifetime, R>)); - parse_quote!(<#ty as ::sqlx::FromRow<#lifetime, R>>::from_row(__row)) + //parse_quote!(<#ty as ::sqlx::FromRow<#lifetime, R>>::from_row(__row)) + parse_quote!(::sqlx::__from_opt_row!(#ty, __row)) } // Flatten + Try from (true, Some(try_from), false) => { diff --git a/src/lib.rs b/src/lib.rs index 870fa703c5..0fbf7df945 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,8 @@ pub use sqlx_core::connection::{ConnectOptions, Connection}; pub use sqlx_core::database::{self, Database}; pub use sqlx_core::describe::Describe; pub use sqlx_core::executor::{Execute, Executor}; -pub use sqlx_core::from_row::FromRow; +pub use sqlx_core::from_row::{FromRow, Wrapper, FromOptRow}; +pub use sqlx_core::__from_opt_row; pub use sqlx_core::pool::{self, Pool}; #[doc(hidden)] pub use sqlx_core::query::query_with_result as __query_with_result; From 4d9f2925230f2a7b7faa094c1b0d9c18f213fcbd Mon Sep 17 00:00:00 2001 From: Jonny Silva Date: Wed, 8 Jan 2025 13:29:27 -0300 Subject: [PATCH 3/4] #2934 solution using autoref trick instead of blanket implementation for Option --- sqlx-core/src/from_row.rs | 12 ++++++++---- sqlx-macros-core/src/derives/row.rs | 2 -- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sqlx-core/src/from_row.rs b/sqlx-core/src/from_row.rs index 6181484d69..8dc5b8c7fa 100644 --- a/sqlx-core/src/from_row.rs +++ b/sqlx-core/src/from_row.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use crate::{ error::{Error, UnexpectedNullError}, row::Row, @@ -491,13 +493,13 @@ impl_from_row_for_tuple!( (15) -> T16; ); -pub struct Wrapper; +pub struct Wrapper(pub PhantomData); pub trait FromOptRow<'r, R, T> { fn __from_row(&self, row: &'r R) -> Result; } -impl<'r, R, T> FromOptRow<'r, R, Option> for &Wrapper +impl<'r, R, T> FromOptRow<'r, R, Option> for Wrapper> where R: Row, T: FromRow<'r, R>, @@ -513,7 +515,7 @@ where } } -impl<'r, R, T> FromOptRow<'r, R, T> for Wrapper +impl<'r, R, T> FromOptRow<'r, R, T> for &Wrapper where R: Row, T: FromRow<'r, R>, @@ -527,8 +529,10 @@ where #[macro_export] macro_rules! __from_opt_row { ($t:ty, $row:expr) => {{ + use std::marker::PhantomData; use $crate::from_row::{FromOptRow, Wrapper}; - let value: Result<$t, sqlx::Error> = Wrapper.__from_row($row); + let wrapper = Wrapper(PhantomData::<$t>); + let value = (&wrapper).__from_row($row); value }}; } diff --git a/sqlx-macros-core/src/derives/row.rs b/sqlx-macros-core/src/derives/row.rs index 4ddc1f32af..8e1850d2f3 100644 --- a/sqlx-macros-core/src/derives/row.rs +++ b/sqlx-macros-core/src/derives/row.rs @@ -108,8 +108,6 @@ fn expand_derive_from_row_struct( } // Flatten (true, None, false) => { - predicates.push(parse_quote!(#ty: ::sqlx::FromRow<#lifetime, R>)); - //parse_quote!(<#ty as ::sqlx::FromRow<#lifetime, R>>::from_row(__row)) parse_quote!(::sqlx::__from_opt_row!(#ty, __row)) } // Flatten + Try from From 7b153f91dcbb92299d83b1f5e5c9ecd50405f413 Mon Sep 17 00:00:00 2001 From: Jonny Silva Date: Wed, 8 Jan 2025 15:15:05 -0300 Subject: [PATCH 4/4] add suport for flatten optional values with the try_from attribute --- sqlx-macros-core/src/derives/row.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sqlx-macros-core/src/derives/row.rs b/sqlx-macros-core/src/derives/row.rs index 8e1850d2f3..d46c400778 100644 --- a/sqlx-macros-core/src/derives/row.rs +++ b/sqlx-macros-core/src/derives/row.rs @@ -112,9 +112,8 @@ fn expand_derive_from_row_struct( } // Flatten + Try from (true, Some(try_from), false) => { - predicates.push(parse_quote!(#try_from: ::sqlx::FromRow<#lifetime, R>)); parse_quote!( - <#try_from as ::sqlx::FromRow<#lifetime, R>>::from_row(__row) + ::sqlx::__from_opt_row!(#try_from, __row) .and_then(|v| { <#ty as ::std::convert::TryFrom::<#try_from>>::try_from(v) .map_err(|e| {