sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

Nested postgres custom composite types

Open feymartynov opened this issue 5 years ago • 5 comments

Consider following types:

#[derive(sqlx::Type)]
struct Foo {
    one: String,
    two: String,
}

#[derive(sqlx::Type)]
struct Bar {
    foo: Foo,
    three: String,
}

This fails to compile on 1.46 with:

error: implementation of `sqlx::Decode` is not general enough
  --> src/main.rs:7:10
   |
7  |   #[derive(sqlx::Type)]
   |            ^^^^^^^^^^ implementation of `sqlx::Decode` is not general enough
   | 
  ::: /Users/fey/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-core-0.4.0-beta.1/src/decode.rs:60:1
   |
60 | / pub trait Decode<'r, DB: Database>: Sized {
61 | |     /// Decode a new value of this type using a raw value from the database.
62 | |     fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError>;
63 | | }
   | |_- trait `sqlx::Decode` defined here
   |
   = note: `Foo` must implement `sqlx::Decode<'0, sqlx::Postgres>`, for any lifetime `'0`...
   = note: ...but `Foo` actually implements `sqlx::Decode<'1, sqlx::Postgres>`, for some specific lifetime `'1`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

I used cargo-expand to see what the derive macro expands to and Decode impl is like this:

impl<'r> sqlx::decode::Decode<'r, sqlx::Postgres> for Bar
where
    String: sqlx::decode::Decode<'r, sqlx::Postgres>,
    String: sqlx::types::Type<sqlx::Postgres>,
    Foo: sqlx::decode::Decode<'r, sqlx::Postgres>,
    Foo: sqlx::types::Type<sqlx::Postgres>,
{
    fn decode(
        value: sqlx::postgres::PgValueRef<'r>,
    ) -> std::result::Result<Self, Box<dyn std::error::Error + 'static + Send + Sync>> {
        let mut decoder = sqlx::postgres::types::PgRecordDecoder::new(value)?;
        let three = decoder.try_decode::<String>()?;
        let foo = decoder.try_decode::<Foo>()?;
        Ok(Bar { three, foo })
    }
}

If I change Foo: sqlx::decode::Decode<'r, sqlx::Postgres>, to Foo: for<'q> sqlx::decode::Decode<'q, sqlx::Postgres>, then it compiles well. Could this be a bug in the sqlx::Type macro?

feymartynov avatar Sep 06 '20 00:09 feymartynov

I'm having the same issue with:

#[derive(sqlx::Type)]
struct Contract {
    id: i32,
    user_id: i32,
    order_id: i32,
    tags: Vec<i32>,
}

Manually implementing decode using your strategy worked.

KaiserKarel avatar Dec 14 '20 11:12 KaiserKarel

From what I noticed, it seems like the fact that composite types in Postgres do not have constraints means that all composite fields are automatically not null. And that means that the output type has to have all it's fields as Option<_>.

pintert3 avatar Jul 04 '21 16:07 pintert3

This issue is a major blocker for me :(

Hope will be resolved soon. I can't use a workaround impl Decode since I am migrating from the 0.5.2 version to 0.6.2 and there is a lot of Option<T>.

grv07 avatar Dec 06 '22 08:12 grv07

Is this still an issue in sqlx 0.8? It seems fixed for me.

FSMaxB avatar Jul 31 '24 13:07 FSMaxB

I can also confirm that it works on 0.8.

manifest avatar Aug 21 '24 20:08 manifest