launchbadge / sqlx

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.
Apache License 2.0
13.13k stars 1.24k forks source link

Nested postgres custom composite types #672

Open feymartynov opened 4 years ago

feymartynov commented 4 years ago

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?

KaiserKarel commented 3 years ago

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.

pintert3 commented 3 years ago

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<_>.

grv07 commented 1 year ago

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>.

FSMaxB commented 1 month ago

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

manifest commented 1 month ago

I can also confirm that it works on 0.8.