mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
feat: allow opt-out of PgHasArrayType
with #[derive(sqlx::Type)]
(#2619)
closes #2611
This commit is contained in:
parent
3268698f2e
commit
1d1095e94f
5 changed files with 101 additions and 2 deletions
|
@ -104,7 +104,7 @@ pub use json::{Json, JsonRawValue, JsonValue};
|
|||
///
|
||||
/// ### Transparent
|
||||
///
|
||||
/// Rust-only domain or wrappers around SQL types. The generated implementations directly delegate
|
||||
/// Rust-only domain wrappers around SQL types. The generated implementations directly delegate
|
||||
/// to the implementation of the inner type.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
|
@ -113,6 +113,35 @@ pub use json::{Json, JsonRawValue, JsonValue};
|
|||
/// struct UserId(i64);
|
||||
/// ```
|
||||
///
|
||||
/// ##### Note: `PgHasArrayType`
|
||||
/// If you have the `postgres` feature enabled, this derive also generates a `PgHasArrayType` impl
|
||||
/// so that you may use it with `Vec` and other types that decode from an array in Postgres:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let user_ids: Vec<UserId> = sqlx::query_scalar("select '{ 123, 456 }'::int8[]")
|
||||
/// .fetch(&mut pg_connection)
|
||||
/// .await?;
|
||||
/// ```
|
||||
///
|
||||
/// However, if you are wrapping a type that does not implement `PgHasArrayType`
|
||||
/// (e.g. `Vec` itself, because we don't currently support multidimensional arrays),
|
||||
/// you may receive an error:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::Type)] // ERROR: `Vec<i64>` does not implement `PgHasArrayType`
|
||||
/// #[sqlx(transparent)]
|
||||
/// struct UserIds(Vec<i64>);
|
||||
/// ```
|
||||
///
|
||||
/// To remedy this, add `#[sqlx(no_pg_array)]`, which disables the generation
|
||||
/// of the `PgHasArrayType` impl:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::Type)]
|
||||
/// #[sqlx(transparent, no_pg_array)]
|
||||
/// struct UserIds(Vec<i64>);
|
||||
/// ```
|
||||
///
|
||||
/// ##### Attributes
|
||||
///
|
||||
/// * `#[sqlx(type_name = "<SQL type name>")]` on struct definition: instead of inferring the SQL
|
||||
|
@ -121,6 +150,7 @@ pub use json::{Json, JsonRawValue, JsonValue};
|
|||
/// given type is different than that of the inferred type (e.g. if you rename the above to
|
||||
/// `VARCHAR`). Affects Postgres only.
|
||||
/// * `#[sqlx(rename_all = "<strategy>")]` on struct definition: See [`derive docs in FromRow`](crate::from_row::FromRow#rename_all)
|
||||
/// * `#[sqlx(no_pg_array)]`: do not emit a `PgHasArrayType` impl (see above).
|
||||
///
|
||||
/// ### Enumeration
|
||||
///
|
||||
|
|
|
@ -56,6 +56,7 @@ pub struct SqlxContainerAttributes {
|
|||
pub type_name: Option<TypeName>,
|
||||
pub rename_all: Option<RenameAll>,
|
||||
pub repr: Option<Ident>,
|
||||
pub no_pg_array: bool,
|
||||
}
|
||||
|
||||
pub struct SqlxChildAttributes {
|
||||
|
@ -71,6 +72,7 @@ pub fn parse_container_attributes(input: &[Attribute]) -> syn::Result<SqlxContai
|
|||
let mut repr = None;
|
||||
let mut type_name = None;
|
||||
let mut rename_all = None;
|
||||
let mut no_pg_array = None;
|
||||
|
||||
for attr in input
|
||||
.iter()
|
||||
|
@ -88,6 +90,10 @@ pub fn parse_container_attributes(input: &[Attribute]) -> syn::Result<SqlxContai
|
|||
try_set!(transparent, true, value)
|
||||
}
|
||||
|
||||
Meta::Path(p) if p.is_ident("no_pg_array") => {
|
||||
try_set!(no_pg_array, true, value);
|
||||
}
|
||||
|
||||
Meta::NameValue(MetaNameValue {
|
||||
path,
|
||||
lit: Lit::Str(val),
|
||||
|
@ -148,6 +154,7 @@ pub fn parse_container_attributes(input: &[Attribute]) -> syn::Result<SqlxContai
|
|||
repr,
|
||||
type_name,
|
||||
rename_all,
|
||||
no_pg_array: no_pg_array.unwrap_or(false),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -229,6 +236,12 @@ pub fn check_enum_attributes(input: &DeriveInput) -> syn::Result<SqlxContainerAt
|
|||
input
|
||||
);
|
||||
|
||||
assert_attribute!(
|
||||
!attributes.no_pg_array,
|
||||
"unused #[sqlx(no_pg_array)]; derive does not emit `PgHasArrayType` impls for enums",
|
||||
input
|
||||
);
|
||||
|
||||
Ok(attributes)
|
||||
}
|
||||
|
||||
|
@ -288,6 +301,12 @@ pub fn check_struct_attributes<'a>(
|
|||
input
|
||||
);
|
||||
|
||||
assert_attribute!(
|
||||
!attributes.no_pg_array,
|
||||
"unused #[sqlx(no_pg_array)]; derive does not emit `PgHasArrayType` impls for custom structs",
|
||||
input
|
||||
);
|
||||
|
||||
assert_attribute!(attributes.repr.is_none(), "unexpected #[repr(..)]", input);
|
||||
|
||||
for field in fields {
|
||||
|
|
|
@ -90,7 +90,7 @@ fn expand_derive_has_sql_type_transparent(
|
|||
}
|
||||
);
|
||||
|
||||
if cfg!(feature = "postgres") {
|
||||
if cfg!(feature = "postgres") && !attr.no_pg_array {
|
||||
tokens.extend(quote!(
|
||||
#[automatically_derived]
|
||||
impl #array_impl_generics ::sqlx::postgres::PgHasArrayType for #ident #ty_generics
|
||||
|
|
|
@ -9,6 +9,44 @@ use crate::types::Oid;
|
|||
use crate::types::Type;
|
||||
use crate::{PgArgumentBuffer, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
|
||||
|
||||
/// Provides information necessary to encode and decode Postgres arrays as compatible Rust types.
|
||||
///
|
||||
/// Implementing this trait for some type `T` enables relevant `Type`,`Encode` and `Decode` impls
|
||||
/// for `Vec<T>`, `&[T]` (slices), `[T; N]` (arrays), etc.
|
||||
///
|
||||
/// ### Note: `#[derive(sqlx::Type)]`
|
||||
/// If you have the `postgres` feature enabled, `#[derive(sqlx::Type)]` will also generate
|
||||
/// an impl of this trait for your type if your wrapper is marked `#[sqlx(transparent)]`:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::Type)]
|
||||
/// #[sqlx(transparent)]
|
||||
/// struct UserId(i64);
|
||||
///
|
||||
/// let user_ids: Vec<UserId> = sqlx::query_scalar("select '{ 123, 456 }'::int8[]")
|
||||
/// .fetch(&mut pg_connection)
|
||||
/// .await?;
|
||||
/// ```
|
||||
///
|
||||
/// However, this may cause an error if the type being wrapped does not implement `PgHasArrayType`,
|
||||
/// e.g. `Vec` itself, because we don't currently support multidimensional arrays:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::Type)] // ERROR: `Vec<i64>` does not implement `PgHasArrayType`
|
||||
/// #[sqlx(transparent)]
|
||||
/// struct UserIds(Vec<i64>);
|
||||
/// ```
|
||||
///
|
||||
/// To remedy this, add `#[sqlx(no_pg_array)]`, which disables the generation
|
||||
/// of the `PgHasArrayType` impl:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[derive(sqlx::Type)]
|
||||
/// #[sqlx(transparent, no_pg_array)]
|
||||
/// struct UserIds(Vec<i64>);
|
||||
/// ```
|
||||
///
|
||||
/// See [the documentation of `Type`][Type] for more details.
|
||||
pub trait PgHasArrayType {
|
||||
fn array_type_info() -> PgTypeInfo;
|
||||
fn array_compatible(ty: &PgTypeInfo) -> bool {
|
||||
|
|
|
@ -10,6 +10,13 @@ use std::ops::Bound;
|
|||
#[sqlx(transparent)]
|
||||
struct Transparent(i32);
|
||||
|
||||
#[derive(PartialEq, Debug, sqlx::Type)]
|
||||
// https://github.com/launchbadge/sqlx/issues/2611
|
||||
// Previously, the derive would generate a `PgHasArrayType` impl that errored on an
|
||||
// impossible-to-satisfy `where` bound. This attribute allows the user to opt-out.
|
||||
#[sqlx(transparent, no_pg_array)]
|
||||
struct TransparentArray(Vec<i64>);
|
||||
|
||||
#[sqlx_macros::test]
|
||||
async fn test_transparent_slice_to_array() -> anyhow::Result<()> {
|
||||
let mut conn = new::<Postgres>().await?;
|
||||
|
@ -139,6 +146,11 @@ test_type!(transparent<Transparent>(Postgres,
|
|||
"23523" == Transparent(23523)
|
||||
));
|
||||
|
||||
test_type!(transparent_array<TransparentArray>(Postgres,
|
||||
"'{}'::int8[]" == TransparentArray(vec![]),
|
||||
"'{ 23523, 123456, 789 }'::int8[]" == TransparentArray(vec![23523, 123456, 789])
|
||||
));
|
||||
|
||||
test_type!(weak_enum<Weak>(Postgres,
|
||||
"0::int4" == Weak::One,
|
||||
"2::int4" == Weak::Two,
|
||||
|
|
Loading…
Reference in a new issue