diff --git a/sqlx-core/src/from_row.rs b/sqlx-core/src/from_row.rs index 08246bbc8a..82ebd9c98e 100644 --- a/sqlx-core/src/from_row.rs +++ b/sqlx-core/src/from_row.rs @@ -123,6 +123,41 @@ use crate::row::Row; /// /// This field is compatible with the `default` attribute. /// +/// #### `skip` +/// +/// This is a variant of the `default` attribute which instead always takes the value from +/// the `Default` implementation for this field type ignoring any results in your query. +/// This can be useful, if some field does not satifisfy the trait bounds (i.e. +/// `sqlx::decode::Decode`, `sqlx::type::Type`), in particular in case of nested structures. +/// For example: +/// +/// ```rust,ignore +/// #[derive(sqlx::FromRow)] +/// struct Address { +/// user_name: String, +/// street: String, +/// city: String, +/// } +/// +/// #[derive(sqlx::FromRow)] +/// struct User { +/// name: String, +/// #[sqlx(skip)] +/// addresses: Vec
, +/// } +/// ``` +/// +/// Then when querying into `User`, only `name` needs to be set: +/// +/// ```rust,ignore +/// let user: User = sqlx::query_as("SELECT name FROM users") +/// .fetch_one(&mut some_connection) +/// .await?; +/// +/// `Default` for `Vec
` is an empty vector. +/// assert!(user.addresses.is_empty()); +/// ``` +/// /// ## Manual implementation /// /// You can also implement the [`FromRow`] trait by hand. This can be useful if you diff --git a/sqlx-macros-core/src/derives/attributes.rs b/sqlx-macros-core/src/derives/attributes.rs index bf3c126c45..bfbc32f60d 100644 --- a/sqlx-macros-core/src/derives/attributes.rs +++ b/sqlx-macros-core/src/derives/attributes.rs @@ -63,6 +63,7 @@ pub struct SqlxChildAttributes { pub default: bool, pub flatten: bool, pub try_from: Option, + pub skip: bool, } pub fn parse_container_attributes(input: &[Attribute]) -> syn::Result { @@ -155,6 +156,7 @@ pub fn parse_child_attributes(input: &[Attribute]) -> syn::Result syn::Result try_set!(try_from, val.parse()?, value), Meta::Path(path) if path.is_ident("default") => default = true, Meta::Path(path) if path.is_ident("flatten") => flatten = true, + Meta::Path(path) if path.is_ident("skip") => skip = true, u => fail!(u, "unexpected attribute"), }, u => fail!(u, "unexpected attribute"), @@ -190,6 +193,7 @@ pub fn parse_child_attributes(input: &[Attribute]) -> syn::Result { predicates.push(parse_quote!(#ty: ::sqlx::FromRow<#lifetime, R>)); diff --git a/tests/postgres/derives.rs b/tests/postgres/derives.rs index 5d53f3f351..16a0f38119 100644 --- a/tests/postgres/derives.rs +++ b/tests/postgres/derives.rs @@ -614,3 +614,31 @@ async fn test_flatten() -> anyhow::Result<()> { Ok(()) } + +#[cfg(feature = "macros")] +#[sqlx_macros::test] +async fn test_skip() -> anyhow::Result<()> { + #[derive(Debug, Default, sqlx::FromRow)] + struct AccountDefault { + default: Option, + } + + #[derive(Debug, sqlx::FromRow)] + struct AccountKeyword { + id: i32, + #[sqlx(skip)] + default: AccountDefault, + } + + let mut conn = new::().await?; + + let account: AccountKeyword = sqlx::query_as(r#"SELECT * from (VALUES (1)) accounts("id")"#) + .fetch_one(&mut conn) + .await?; + println!("{:?}", account); + + assert_eq!(1, account.id); + assert_eq!(None, account.default.default); + + Ok(()) +}