Repository navigation
Description
What is the problem the feature request solves?
Spark 4.x can replace a query stage that produces no rows with EmptyRelationExec during adaptive execution. Comet does not currently recognize this operator as a native input, so supported operators above it can fall back to Spark.
For example, an aggregate over an empty grouped result acquires an EmptyRelationExec input after the shuffle completes. Keeping the Range input and its shuffle in Spark makes this AQE transition easy to reproduce:
SET spark.sql.adaptive.enabled=true;
SET spark.comet.sparkToColumnar.enabled=false;
SET spark.comet.shuffle.convertFromSparkPlan.enabled=false;
SELECT count(*), sum(v)
FROM (
SELECT id % 2 AS k, sum(id) AS v
FROM range(0, 10, 1, 2)
WHERE id < 0
GROUP BY id % 2
)The result must remain one row containing 0 and NULL. A grouped aggregate over the same empty input must produce no rows.
Describe the potential solution
Add a columnar CometEmptyRelationExec using the existing native Arrow-input path. Preserve the output attributes and zero partitions of Spark's operator, without executing the eliminated logical subtree. Register it on Spark 4.x through a version shim and add an operator enable/disable setting.
Test both the results and the executed plans for aggregates, supported joins, AQE and reused exchanges. Preserve existing type and operator fallback rules.
Parquet writes need an additional guard: admitting this zero-partition input must not route a write through the native writer when that would omit the schema-bearing empty file. Keep Spark's writer for those inputs and add an overwrite/readback regression test.
Additional context
Related to #5303, which tracks native Parquet writes producing no output file for a zero-partition RDD. The writer fallback here would protect the newly supported input; it would not resolve all cases in that issue.
This adds support for consuming Spark's empty relation. It does not change Spark's rules for discovering or propagating empty relations.
In particular, the grouped-aggregate example above has a separate AQE row-count inference gap: AQEPropagateEmptyRelation.getEstimatedRowCount recognizes QueryStageExec and BaseAggregateExec, but not CometHashAggregateExec. With default Comet conversion settings, the converted inner aggregate prevents this shape from producing an empty relation. PR #5821 adds support for consuming EmptyRelationExec once Spark creates it; it does not close this inference gap. The aggregate case needs the BaseAggregateExec gap addressed separately.
Activity
jeffw13 commentedon Sep 9, 2026
take