You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@spark.apache.org by "Cheng Wang (Jira)" <ji...@apache.org> on 2026/10/23 05:19:00 UTC
[jira] [Created] (SPARK-60501) [SQL] PullupCorrelatedPredicates returns 0 instead of 1 for COUNT(*) over a correlated scalar aggregate
Cheng Wang created SPARK-60501:
----------------------------------
Summary: [SQL] PullupCorrelatedPredicates returns 0 instead of 1 for COUNT(*) over a correlated scalar aggregate
Key: SPARK-60501
URL: https://issues.apache.org/jira/browse/SPARK-60501
Project: Spark
Issue Type: Bug
Components: SQL
Affects Versions: 4.0.0
Environment: macOS / Linux, PySpark / Spark 4.0.0 and master branch
Reporter: Cheng Wang
h3. What happened?
PullupCorrelatedPredicates.rewriteSubQueries uses mayHaveCountBugAgg (sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/subquery.scala:488-513) to determine if a correlated scalar aggregate needs count-bug domain join handling:
{code:scala}
def mayHaveCountBugAgg(a: Aggregate): Boolean = {
a.groupingExpressions.isEmpty && a.aggregateExpressions.exists(_.exists {
case a: AggregateExpression => a.aggregateFunction.defaultResult.isDefined
case _ => false
})
}
{code}
defaultResult.isDefined is true only for COUNT-like functions. However, ANY ungrouped aggregate (including MAX, MIN, SUM, AVG) always produces exactly one row on empty input (evaluating to NULL).
When mayHaveCountBugAgg returns false, the inner aggregate is decorrelated without the LeftOuter domain join, so non-matching outer keys produce zero rows instead of one row (NULL). An enclosing COUNT(*) then counts 0 instead of 1.
h3. To Reproduce (PySpark 4.0.0 / master)
{code:sql}
CREATE OR REPLACE TEMP VIEW a AS SELECT * FROM VALUES (1, 10), (2, 20) AS a(k, x);
CREATE OR REPLACE TEMP VIEW b AS SELECT * FROM VALUES (1, 100), (1, 200) AS b(k, v);
SELECT a.k, (
SELECT COUNT(*)
FROM (SELECT MAX(b.v) AS m FROM b WHERE b.k = a.k) sub
) AS cnt
FROM a
ORDER BY a.k;
{code}
h3. Expected vs Actual
For k = 2, table b has no matching rows. The scalar subquery SELECT MAX(b.v) FROM b WHERE b.k = 2 produces 1 row: (NULL). The outer COUNT(*) over this 1 row must return 1.
* Expected:
{noformat}
+---+---+
| k|cnt|
+---+---+
| 1| 1|
| 2| 1|
+---+---+
{noformat}
* Actual in Spark 4.0.0 / master:
{noformat}
+---+---+
| k|cnt|
+---+---+
| 1| 1|
| 2| 0| <-- Silent Wrong Result
+---+---+
{noformat}
h3. Root Cause
mayHaveCountBugAgg checks aggregateFunction.defaultResult.isDefined instead of checking whether the aggregate is ungrouped. An ungrouped aggregate always evaluates to 1 row regardless of whether the function returns NULL or a default value.
h3. Suggested Fix
Treat every ungrouped subquery aggregate as vulnerable, or check whether the parent expression is sensitive to row existence (such as COUNT(*)).
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@spark.apache.org
For additional commands, e-mail: issues-help@spark.apache.org