Skip to content

Preserve aggregate results for outer joins without criteria - #30989

Open
bvolpato wants to merge 1 commit into
trinodb:masterfrom
bvolpato:bvolpato/fix-outer-join-empty-aggregation
Open

Preserve aggregate results for outer joins without criteria#30989
bvolpato wants to merge 1 commit into
trinodb:masterfrom
bvolpato:bvolpato/fix-outer-join-empty-aggregation

Conversation

@bvolpato

@bvolpato bvolpato commented Sep 3, 2026

Copy link
Copy Markdown
Member

Description

Preserve aggregate results when an outer join has no equijoin criteria. Pushing aggregation below such a join creates a global aggregation, which emits a row even when its input is empty. That changes count(*) over the preserved outer row from 1 to 0.

SELECT count(*)
FROM (VALUES 1) l(a)
LEFT JOIN (
    SELECT * FROM UNNEST(CAST(ARRAY[] AS array(integer)))
) r(b) ON true;

This returns 0 before the fix and 1 afterward. The rule now skips joins without equijoin criteria. Regressions cover LEFT and RIGHT joins and grouped counts.

Additional context and related issues

Existing equijoin aggregation pushdown remains covered by the rule tests. The five new regressions failed before the production change. Afterward, all 25 tests in TestJoin and TestPushAggregationThroughOuterJoin passed. Maven validate, including Checkstyle and Airstyle, passed for core/trino-main.

./mvnw -pl core/trino-main -Dtest=TestJoin,TestPushAggregationThroughOuterJoin -Dair.check.skip-all=true test
./mvnw -pl core/trino-main validate

Downsides

Aggregations over outer joins without equijoin criteria can process more intermediate rows because this pushdown is no longer applied.

Release notes

(x) Release notes are required, with the following suggested text:

## General
* Fix incorrect aggregate results for outer joins without equijoin criteria when the inner input is empty.

Skip aggregation pushdown when the join has no equijoin criteria.
Otherwise the pushed global aggregation produces a row for empty
input, changing the result over the null-extended outer row.
}

@Test
public void testCountOverEmptyLeftJoin()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"empty left join" is kinda misleading. the join result is not empty. it's the right side of the left join who's empty.

let's call it testCountOverOuterJoinWithEmptyInnerSide and let's merge the RIGHT JOIN test case (testCountOverEmptyRightJoin) with this test mehod

}

@Test
public void testCountOverEmptyLeftJoin()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
public void testCountOverEmptyLeftJoin()
public void testCountOverOuterJoinWithEmptyInnerSide()

Comment on lines +389 to +393
}

@Test
public void testCountOverEmptyRightJoin()
{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
}
@Test
public void testCountOverEmptyRightJoin()
{

}

@Test
public void testGroupedCountOverEmptyOuterJoin()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

rename appropriately, see above

@findepi
findepi marked this pull request as ready for review September 4, 2026 08:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

2 participants