Fix aggregate functions on empty tables by preserving AggregateExec in optimizer #1593
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a critical regression where aggregate functions like COUNT(*) on empty tables were returning empty result sets instead of the correct SQL-compliant results.
Problem
The
RemoveExecAboveEmpty
physical optimizer rule introduced in commit 671d82c was incorrectly replacing all execution plan nodes above empty tables withEmptyExec
, including aggregate operations. This violated SQL standards where:COUNT(*)
on empty tables should return0
SUM/AVG/MIN/MAX
on empty tables should returnNULL
Instead, queries like
SELECT COUNT(*) FROM empty_table
were returning completely empty result sets.Root Cause
The original implementation in #756 included comprehensive tests for various execution plan types (Sort, Filter, Projection, Join, Limit) but completely missed testing aggregate operations. This oversight allowed the bug to slip through.
Solution
Modified the
RemoveExecAboveEmpty
optimizer rule to excludeAggregateExec
operations from being replaced withEmptyExec
. Aggregate functions have special semantics and must be preserved even when their inputs are empty.Changes Made
Fixed the optimizer rule (
remove_exec_above_empty.rs
):AggregateExec
Added missing test coverage (
count.rs
):COUNT(*)
test case that was missing from the original implementationTest Results
Before fix:
After fix:
Impact
🤖 Generated with Claude Code