Skip to content

Wrong results: eliminate_cross_join drops an equi-join key that spans two cross-joined relations #26058

Closed

Description

@alexy

Describe the bug

An inner join whose ON has an ordinary equi-key plus a second equality whose one side spans two relations of a cross join loses the second equality. The query returns rows that do not satisfy the join condition.

extract_equijoin_predicate turns b.id = CASE k.k WHEN 0 THEN a.x ELSE a.y END into an equi-join key whose left expression references both a and k. eliminate_cross_join then flattens (a CROSS JOIN k) JOIN b into one join graph and rebuilds it as (a JOIN b ON a.t = b.t) CROSS JOIN k. The key that references both a and k is not attached to any join and is not kept as a filter.

Reproduced on 55.1.0 and on main (8248a57). Running the query with each logical optimizer rule removed in turn, it is correct without extract_equijoin_predicate or without eliminate_cross_join, and wrong without any other single rule.

To Reproduce

SELECT k.k, b.id
FROM (SELECT 1 AS t, 17 AS x, 18 AS y) a
CROSS JOIN (SELECT 0 AS k UNION ALL SELECT 1) k
JOIN (SELECT 1 AS t, CAST(value AS INT) AS id FROM generate_series(0, 20)) b
  ON b.t = a.t AND b.id = CASE k.k WHEN 0 THEN a.x ELSE a.y END
ORDER BY k.k, b.id;

returns 42 rows: every b.id from 0 to 20, once for each k.

The optimized logical plan has no trace of the CASE predicate:

Sort: k.k ASC NULLS LAST, b.id ASC NULLS LAST
  Projection: k.k, b.id
    Cross Join:
      Projection: b.id
        Inner Join: a.t = b.t
          SubqueryAlias: a
            Projection: Int64(1) AS t
              EmptyRelation: rows=1
          SubqueryAlias: b
            Projection: Int64(1) AS t, CAST(generate_series().value AS Int32) AS id
              TableScan: generate_series() projection=[value]
      SubqueryAlias: k
        Union
          ...

Expected behavior

Two rows, (0, 17) and (1, 18).

Without b.t = a.t in the ON clause the result is correct, as it is when the CASE predicate is written in a WHERE instead of the ON clause.

Additional context

Found through Sail, where a query joining a table on s.tic = rt.tic AND s.id = CASE k.k WHEN 0 THEN rt.sector_id ELSE COALESCE(rt.spawn_sector_id, rt.sector_id) END over a cross join with k returned every row for that tic.

Activity

  1. added a commit that references this issue on Oct 5, 2026
  2. Phoenix500526 commented on Nov 3, 2026

    @Phoenix500526
    Contributor

    take

  3. added 6 commits that reference this issue on Nov 3, 2026
    8b40f9a
    3185bf9
    0ec42b9
    66e050c
    c55ed54
    2b948e9
  4. added a commit that references this issue on Nov 8, 2026
    5191bcc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions