Search code examples
rustrust-tokiorust-sqlx

Sqlx mismatched types, Rust type () is incompatible with SQL type CHAR


I'm trying to execute an SQL query to a Postgres database, using query_as. The error I get is:

panicked at 'called `Result::unwrap()` on an `Err` value:
ColumnDecode { index: "\"id\"", source: "mismatched types; Rust type `()`
(as SQL type `VOID`) is not compatible with SQL type `CHAR`" }'

In the database the id column is of type bpchar(36) and in Rust I'm using a UuidWrapper struct for it.

#[derive(PartialEq, Debug, Clone)]
pub struct UuidWrapper(pub(crate) Uuid);

I want to execute the query and store the output into a Post type:

pub struct Post {
    pub id: UuidWrapper,
    /* ... */

This struct implements the needed FromRow trait but the code never enters from_row

impl<'r> FromRow<'r, PgRow> for Post {
    fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
        Ok(Post {
            id: UuidWrapper::from(row.try_get("id")?),
            /* ... */
        })
    }
}

This is how I do the query:

async fn select_where(pool: &Pool<Postgres>, query: &str) -> Vec<Self>
where
    Self: Sized,
{
    let res = query_as!(Post, "select * from posts WHERE owner_id = 'b0648b6c-f3a7-4789-bf57-3b44e15029d9' AND CAST(create_time AS DATE) = '2023-05-14'").fetch_all(pool).await;
    let q = format!("SELECT * FROM posts {query}");
    query_as::<_, Self>(&q).fetch_all(pool).await.unwrap()
}

The string literal in query_as! is the same as the value of q, however this query succeeds and returns a Vec<Post>, but the query_as(not macro, with variables instead of hardcoded values) crashes.

Dependencies:

sqlx = { version = "0.7.1",
    features = [ "runtime-tokio-rustls", "postgres", "time", "chrono", "uuid", "macros", "chrono" ] }

I tried debugging, changing the UuidWrapper struct and other wrapper structs, double checking data types to be compatible with the database.


Solution

  • I fixed the issue by changing the FromRow implementation to:

    impl<'r> FromRow<'r, PgRow> for Post {
        fn from_row(row: &PgRow) -> Result<Self, sqlx::Error> {
            let s: String = row.try_get("id")?;
            let id: UuidWrapper = UuidWrapper(Uuid::from_str(s.as_str()).unwrap());
            let s: String = row.try_get("owner_id")?;
            let owner_id: UuidWrapper = UuidWrapper(Uuid::from_str(s.as_str()).unwrap());
            let create_time_str: NaiveDateTime = row.try_get("create_time")?;
    
            Ok(Post {
                id,
                owner_id,
                create_time: DateTimeWrapper(create_time_str),
    

    Now every object is explicitly formed