Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

query_as! does not recognize Option<T> in some left join queries #3408

Open
ahmed-said-jax opened this issue Aug 6, 2024 · 5 comments
Open
Labels

Comments

@ahmed-said-jax
Copy link

ahmed-said-jax commented Aug 6, 2024

Bug Description

If you run a query with a left join and a where condition outside the left join, sqlx yells at you during runtime (not compile time) with the following error:

error occurred while decoding column <col_number>: unexpected null; try decoding as an `Option`

even if you read the column into an Option<T>. However, if you put that condition inside the left join, it works fine.

Minimal Reproduction

Here's a contrived example with two tables, employee and department:

schema.sql:

create table department (
    id serial primary key,
    name text not null
);

create table employee (
    name text primary key,
    department_id integer references department
);

main.rs:

struct QueryResult {
    employee_name: String,
    department_name: Option<String>,
}

#[tokio::main]
async fn main() {
    let pool = sqlx::postgres::PgPool::connect("postgres://localhost/sqlx-mre")
        .await
        .unwrap();

    sqlx::query!("insert into employee (name) values ('ahmed')")
        .execute(&pool)
        .await
        .unwrap();


    // This works!
    let good_query_result = sqlx::query_as!(
        QueryResult,

        "
        select
            employee.name as employee_name,
            department.name as department_name
        from employee
        left join
            department
            on
                employee.department_id = department.id
                and employee.name = $1
        ",

        "ahmed"
    )
    .fetch_one(&pool)
    .await
    .unwrap();

    // This doesn't
    let bad_query_result = sqlx::query_as!(
        QueryResult,

        "select
            employee.name as employee_name,
            department.name as department_name
        from employee
        left join
            department
            on employee.department_id = department.id
        where employee.name = $1",
        
        "ahmed"
    )
    .fetch_one(&pool)
    .await
    .unwrap();
}
    /*
    thread 'main' panicked at src/main.rs:57:6:
    called `Result::unwrap()` on an `Err` value: ColumnDecode { index: "1", source: UnexpectedNullError }
    */

I believe this is a bug with sqlx because each query returns the expected (and same) result when run in a psql shell.

PS: I know issue #3336 exists, but I don't think it's quite the same.

Info

  • SQLx version: 0.8.0
  • SQLx features enabled: ["postgres", "macros", "uuid", "runtime-tokio"]
  • Database server and version: Postgres 16.3
  • Operating system: macos Sonoma 14.5
  • rustc --version: 1.80
@ahmed-said-jax
Copy link
Author

ahmed-said-jax commented Aug 6, 2024

This issue seems related but I don't think it's the same.

@ahmed-said-jax ahmed-said-jax changed the title sqlx does not recognize Option<T> columns when using query_as! macro with a query with an outer join and where outside the join query_as! does not recognize Option<T> in some left join queries Aug 6, 2024
@kennetpostigo
Copy link

I just ran into this, is there a work around?

@kennetpostigo
Copy link

Work around is to add as "col_name!" to the problematic fields.

@ahmed-said-jax
Copy link
Author

Work around is to add as "col_name!" to the problematic fields.

The thing is, that tells the compiler that the thing you're referencing is not null, which is not what you want on a left join. In the example I made, doing select employee.name as employee_name, department.name as "department_name!" implies that there will always be a department_name, which is not true in my case.

@musjj
Copy link

musjj commented Oct 2, 2024

I'm also having the same issue and it's driving me nuts. For some reason having a where condition transforms all left-joined columns from Option<T> -> T. Is there a reason behind this behavior?

But at least it looks like that you can work around this issue by using and instead of where, at least for simple cases.

It looks like that this is the same issue as #2127 I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants