Skip to content

adapter: Prevent enable_variadic_left_join from firing on cross-joins#36640

Open
mgree wants to merge 3 commits into
MaterializeInc:mainfrom
mgree:sql-286-variadic-outer-join-bugfix
Open

adapter: Prevent enable_variadic_left_join from firing on cross-joins#36640
mgree wants to merge 3 commits into
MaterializeInc:mainfrom
mgree:sql-286-variadic-outer-join-bugfix

Conversation

@mgree
Copy link
Copy Markdown
Contributor

@mgree mgree commented May 20, 2026

Motivation

Fixes https://linear.app/materializeinc/issue/SQL-286/enable-variadic-left-join-lowering-causes-wrong-results.

Description

When trying to apply the variadic left join lowering (attempt_left_join_magic), we try to identify a column in the right-hand side of the LEFT JOIN that is equated to a bound_input in the left-hand side. The code correctly checked that the right-hand side was in the right relation, but only ensured that the left-hand side was uncorrelated. When presented with a cross join (as in the original bug report, SELECT a.k, b.x, b.y, b.k, c.k FROM (a LEFT JOIN b ON b.x = b.y) LEFT JOIN c ON b.k = c.k, we must plan a LEFT JOIN b ON b.x = b.y as a cross-join), we will attempt the lowering---when in fact we should not.

The fix is simple: make sure that the left column is actually in the left-hand side.

Verification

Repro from the bug report went red to green. No need for other SLT rewrites (i.e., doesn't break existing examples).

@mgree mgree marked this pull request as ready for review May 20, 2026 18:50
@mgree mgree requested a review from a team as a code owner May 20, 2026 18:50
@mgree mgree requested review from antiguru and ggevay May 20, 2026 18:50
@mgree mgree enabled auto-merge (squash) May 20, 2026 20:08
Copy link
Copy Markdown
Contributor

@def- def- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an extra test that fails:

--- test/sqllogictest/variadic_outer_join.slt
2026-05-21T01:56:57.225736Z ERROR mz_transform: caught a panic during query optimization: type error in transient query:
In the MIR term:
Project (#0, #1)
  Union
    Get l1
    Constant
      - (null)


projection of non-existant columns [0, 1] from type (r_int32?)

With
  cte l0 =
    Constant
      - ()
Return
  Project (#0, #3, #1, #4)
    With
      cte l1 =
        CrossJoin
          Get l0
          Get u1
      cte l3 =
        Project (#0..=#3)
          Map (true)
            With
              cte l2 =
                CrossJoin
                  Get l0
                  Get u2
            Return
              Get l2
      cte l6 =
        Project (#0, #1)
          Map (true)
            With
              cte l5 =
                CrossJoin
                  Get l0
                  Get u3
            Return
              Get l5
      cte l4 =
        Union
          Filter (#1) IS NOT NULL AND (#2) IS NOT NULL
            Get l3
          Project (#2, #1, #0, #5)
            Map (null, null, null, null)
              Threshold
                Union
                  Negate
                    Project (#2, #1)
                      Filter (#1) IS NOT NULL AND (#2) IS NOT NULL
                        Get l3
                  Distinct project=[#0, #1]
                    Project (#0, #1)
                      Union
                        Get l1
                        Constant
                          - (null)
      cte l7 =
        Union
          Filter (#0) IS NOT NULL
            Get l6
          Project (#0, #2)
            Map (null, null)
              Threshold
                Union
                  Negate
                    Project (#0)
                      Filter (#0) IS NOT NULL
                        Get l6
                  Distinct project=[#0]
                    Project (#0)
                      Union
                        Get l1
                        Constant
                          - (null)
    Return
      Project (#0..=#3, #6)
        Map (case when (#5) IS NULL then null else #4 end)
          Filter ((#0 = #4) OR ((#0) IS NULL AND (#4) IS NULL))
            Project (#0..=#5)
              CrossJoin
                Project (#0, #5..=#7)
                  Map (case when (#4) IS NULL then null else #1 end, case when (#4) IS NULL then null else #2 end, case when (#4) IS NULL then null else #3 end)
                    Filter ((#0 = #3) OR ((#0) IS NULL AND (#3) IS NULL)) AND ((#1 = #2) OR ((#1) IS NULL AND (#2) IS NULL))
                      Project (#0..=#4)
                        CrossJoin
                          Get l1
                          Get l4
                Get l7

    SELECT a.k, b.k, b.x, c.k
    FROM (a LEFT JOIN b ON a.k = b.k AND b.x = b.y) LEFT JOIN c ON a.k = c.k
    PlanFailure:test/sqllogictest/variadic_outer_join.slt:197:
    db error: ERROR: internal error: internal error in optimizer: internal transform error: unexpected panic during query optimization: type error in transient query:
    In the MIR term:
    Project (#0, #1)
      Union
        Get l1
        Constant
          - (null)


    projection of non-existant columns [0, 1] from type (r_int32?)

    With
      cte l0 =
        Constant
          - ()
    Return
      Project (#0, #3, #1, #4)
        With
          cte l1 =
            CrossJoin
              Get l0
              Get u1
          cte l3 =
            Project (#0..=#3)
              Map (true)
                With
                  cte l2 =
                    CrossJoin
                      Get l0
                      Get u2
                Return
                  Get l2
          cte l6 =
            Project (#0, #1)
              Map (true)
                With
                  cte l5 =
                    CrossJoin
                      Get l0
                      Get u3
                Return
                  Get l5
          cte l4 =
            Union
              Filter (#1) IS NOT NULL AND (#2) IS NOT NULL
                Get l3
              Project (#2, #1, #0, #5)
                Map (null, null, null, null)
                  Threshold
                    Union
                      Negate
                        Project (#2, #1)
                          Filter (#1) IS NOT NULL AND (#2) IS NOT NULL
                            Get l3
                      Distinct project=[#0, #1]
                        Project (#0, #1)
                          Union
                            Get l1
                            Constant
                              - (null)
          cte l7 =
            Union
              Filter (#0) IS NOT NULL
                Get l6
              Project (#0, #2)
                Map (null, null)
                  Threshold
                    Union
                      Negate
                        Project (#0)
                          Filter (#0) IS NOT NULL
                            Get l6
                      Distinct project=[#0]
                        Project (#0)
                          Union
                            Get l1
                            Constant
                              - (null)
        Return
          Project (#0..=#3, #6)
            Map (case when (#5) IS NULL then null else #4 end)
              Filter ((#0 = #4) OR ((#0) IS NULL AND (#4) IS NULL))
                Project (#0..=#5)
                  CrossJoin
                    Project (#0, #5..=#7)
                      Map (case when (#4) IS NULL then null else #1 end, case when (#4) IS NULL then null else #2 end, case when (#4) IS NULL then null else #3 end)
                        Filter ((#0 = #3) OR ((#0) IS NULL AND (#3) IS NULL)) AND ((#1 = #2) OR ((#1) IS NULL AND (#2) IS NULL))
                          Project (#0..=#4)
                            CrossJoin
                              Get l1
                              Get l4
                    Get l7

    ----
    FAIL: plan-failure=1 success=12 total=13

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants