6

I am trying to load an array field in Postgres to a Rust struct as follows

use sqlx::{Pool, PgConnection, PgPool, FromRow};
use sqlx::postgres::PgQueryAs;

#[derive(Copy, Clone, sqlx::Type)]
#[sqlx(rename = "VARCHAR")]
#[sqlx(rename_all = "snake_case")]
enum Coupon {
    ChristmasSaleCoupon,
    NewYearSaleCoupon,
}

#[derive(FromRow, Clone)]
struct UserCouponMap {
    pub id: i64,
    pub user_id: i64,
    pub coupons: Vec<Coupon>,
}

impl UserCouponMap {
    pub async fn get_for_userid(db_pool: Pool<PgConnection>, user_id: i64) -> Vec<UserCouponMap> {
        let user_coupon_map: Vec<UserCouponMap> = sqlx::query_as("SELECT * FROM user_coupon_map WHERE user_id = $1")
            .bind(user_id)
            .fetch_all(db_pool)
            .await
            .expect("failed to fetch user coupon map");
        user_coupon_map
    }
}


#[tokio::main]
async fn fetch_coupons_for_user_id(user_id: i64) {
    let pool = PgPool::new("postgresql://asnim@dbhost:5732").await.expect("expected unwrap to succeed");
    let user_coupon_map = UserCouponMap::get_for_userid(pool, user_id).await;
}

fn main() {
    fetch_coupons_for_user_id(20);
}

As per the Array documentation, I have implemented sqlx::Type for Coupon.

Still, the compiler says, some trait is not satisfied.

   Compiling playground v0.1.0 (/Users/asnimansari/CLionProjects/playground)
error[E0277]: the trait bound `Vec<Coupon>: Type<Postgres>` is not satisfied
  --> src/main.rs:23:14
   |
23 |             .fetch_all(db_pool)
   |              ^^^^^^^^^ the trait `Type<Postgres>` is not implemented for `Vec<Coupon>`
   |
   = help: the following implementations were found:
             <Vec<&[u8]> as Type<Postgres>>
             <Vec<&str> as Type<Postgres>>
             <Vec<(T1, T2)> as Type<Postgres>>
             <Vec<(T1, T2, T3)> as Type<Postgres>>
           and 29 others
   = note: required because of the requirements on the impl of `for<'c> FromRow<'c, PgRow<'c>>` for `UserCouponMap`

error[E0277]: the trait bound `[Coupon]: Type<Postgres>` is not satisfied
  --> src/main.rs:23:14
   |
23 |             .fetch_all(db_pool)
   |              ^^^^^^^^^ the trait `Type<Postgres>` is not implemented for `[Coupon]`
   |
   = help: the following implementations were found:
             <[&[u8]] as Type<Postgres>>
             <[&str] as Type<Postgres>>
             <[(T1, T2)] as Type<Postgres>>
             <[(T1, T2, T3)] as Type<Postgres>>
           and 29 others
   = note: required because of the requirements on the impl of `sqlx::decode::Decode<'_, Postgres>` for `Vec<Coupon>`
   = note: required because of the requirements on the impl of `for<'c> FromRow<'c, PgRow<'c>>` for `UserCouponMap`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`

To learn more, run the command again with --verbose.


What am I missing here?

My Cargo file has the following dependencies

sqlx = { version = "0.3.5", default-features = false, features = ["runtime-tokio", "macros", "postgres", "all-type"] }
tokio = { version = "0.2.21", features = ["full"] }
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Asnim P Ansari
  • 1,932
  • 1
  • 18
  • 41

1 Answers1

2

One way it should work is to use explicit column list and declare rust type in the query. For OP case:

let user_coupon_map: Vec<UserCouponMap> = sqlx::query_as!(
    r#"SELECT id, user_id, coupons as "coupons: Vec<Coupon>"
    FROM user_coupon_map 
    WHERE user_id = $1"#
)...

It may not cover all your needs. If you use query builder like sea_query, then it won't do.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
im__
  • 677
  • 1
  • 5
  • 11
  • The error is happening at compile time, so fiddling with the query, which _isn't_ parsed at compile time in this example, won't help. – Dave Rolsky Dec 04 '21 at 18:03
  • Sorry, you are correct. I have edited the post for macro solution. This will only work with query_as!() macro rather than function query_as(). The generated macro code should take the rust type hint "coupons: Vec" and use correct type. – im__ Dec 05 '21 at 19:15
  • I'm not sure this will work either. Have you tested this. I have some very similar code and I get this error: `^ the trait \`Type\` is not implemented for \`Vec\`` – Dave Rolsky Dec 05 '21 at 23:04
  • 1
    I do have instances of this in the project and for me works fine for following cases: pg array[] to `Vec` as op's case, JSONB to `Json`, and coercing `NOT NULL` field into rust `Option` rather than just `Type`. Tested with sqlx 0.5.9 – im__ Dec 06 '21 at 11:47