Skip to content

Double left join -> wrong NULL inference -> runtime exception. #2796

Open
@Palmik

Description

@Palmik

Bug Description

Double left join leads to faulty Option / nullability inference. I have provided a minimal repro.
There are two nullability inference issues:

  1. LEFT JOIN-ed table columns are inferred as not Optional
  2. The FROM table columns are inferred as Optional

Minimal Reproduction

https://github.com/Palmik/sqlx-issue-repro

The schema:

CREATE TABLE foo (
    id BIGINT PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE baz (
    id BIGINT PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE bar (
    id BIGINT PRIMARY KEY,
    foo_id BIGINT NOT NULL REFERENCES foo(id),
    baz_id BIGINT REFERENCES baz(id),
    name TEXT NOT NULL
);

The sql query:

        SELECT
            foo.id,
            foo.name,
            bar.id AS "bar_id",
            bar.name AS "bar_name",
            baz.id AS "baz_id",
            baz.name AS "baz_name"
        FROM foo
        LEFT JOIN bar ON bar.foo_id = foo.id
        LEFT JOIN baz ON baz.id = bar.baz_id 

You would expect foo.id and foo.name to not be optional, and the other columns to be optional. SQLx infers foo.* and baz.* as optional, and bar.* as not optional. One can circumvent the issue by using the AS "bar_id?" syntax to mark the columns as optional manually.

Running the code produces:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ColumnDecode { index: "2", source: UnexpectedNullError }', src/main.rs:77:6

If you make sure that all rows exist, e.g. commenting out line 27, you get:

Record { id: Some(1), name: Some("foo1"), bar_id: 1, bar_name: "bar1", baz_id: Some(1), baz_name: Some("baz1") }
Record { id: Some(2), name: Some("foo2"), bar_id: 2, bar_name: "bar2", baz_id: Some(2), baz_name: Some("baz2") }
Record { id: Some(1), name: Some("foo1"), bar_id: 3, bar_name: "bar3", baz_id: None, baz_name: None }
Record { id: Some(2), name: Some("foo2"), bar_id: 4, bar_name: "bar4", baz_id: None, baz_name: None }

Info

  • SQLx version: 0.7.2
  • SQLx features enabled: sqlx = { version = "0.7.2", features = ["postgres", "uuid", "runtime-tokio-native-tls"] }
  • Database server and version: PostgreSQL 16, but also reproducible on 15.2
  • Operating system: linux
  • rustc --version: rustc 1.71.1 (eb26296b5 2023-08-03)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions