opensearch-project / opensearch-project/sql

verifyPPLToSparkSQL pins invalid Spark SQL in CalcitePPLScalarSubqueryTest.testCorrelatedScalarSubqueryInWhere

Open
#5,470 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

PPL
Dominant language
Java
Stars
176
Forks
229
Avg merge
2d 21h
Merged PRs (30d)
43

Description

Summary

The expectedSparkSql pinned by CalcitePPLScalarSubqueryTest.testCorrelatedScalarSubqueryInWhere is not valid Spark SQL — Spark, DuckDB, and SQLite all refuse to analyze it. The unit test passes only because verifyPPLToSparkSQL checks the printed SQL string and never executes it. This makes the assertion misleading: the Spark in the helper name implies an executable contract that doesn't exist.

Where
  • File: ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLScalarSubqueryTest.java
  • Test: testCorrelatedScalarSubqueryInWhere (around line 119)
  • Helper: CalcitePPLAbstractTest.verifyPPLToSparkSQL (line 185) — only string-equals on sqlNode.toSqlString(OpenSearchSparkSqlDialect.DEFAULT); never analyzes/runs against Spark.
The PPL under test
source=EMP
| where SAL > [
    source=SALGRADE | where SAL = HISAL | stats AVG(SAL)
  ]
The expected Spark SQL pinned by the test
SELECT *
FROM `scott`.`EMP`
WHERE `SAL` > (((SELECT AVG(`EMP`.`SAL`) AS `AVG(SAL)`
                 FROM `scott`.`SALGRADE`
                 WHERE `EMP`.`SAL` = `HISAL`)))
Why it is invalid

The inner SELECT has FROM scott.SALGRADE, but SALGRADE does not have a SAL column. Both SAL references inside the subquery silently bind to the outer EMP.SAL (a correlated reference). The aggregate AVG(EMP.SAL) is therefore an aggregate over an outer column, which the SQL standard treats as an aggregate of the outer query block, not the inner one. That cannot be placed inside the outer WHERE clause.

The Calcite logical plan agrees that both SAL references are outer references:

LogicalFilter(condition=[>($7, $SCALAR_QUERY({
  LogicalAggregate(group=[{}], AVG(SAL)=[AVG($0)])
    LogicalProject($f0=[$cor0.SAL])              <-- AVG over outer column
      LogicalFilter(condition=[=($cor0.SAL, $0)]) <-- outer = SALGRADE.HISAL
        ...SALGRADE...
})], variablesSet=[[$cor0]])
  ...EMP...
Reproduction with real engines
Spark 4.1 (PySpark, the dialect the test claims to emit)
[UNSUPPORTED_SUBQUERY_EXPRESSION_CATEGORY.CORRELATED_REFERENCE]
Unsupported subquery expression: Expressions referencing the outer query
are not supported outside of WHERE/HAVING clauses:
"avg(SAL) AS `AVG(SAL)`". SQLSTATE: 0A000

Spark plan fragment before analysis fails:

Filter (SAL > scalar-subquery [avg(SAL) && SAL])
  Aggregate [avg(outer(SAL)) AS AVG(SAL)]
    Filter (outer(SAL) = HISAL)
      Relation scott.salgrade[GRADE,LOSAL,HISAL]
A valid SQL query equivalent to the physical plan

The optimized physical plan from opensearch _explain already de-correlates the query into a flat join (nested-loop because of the > predicate). The following SQL is accepted by Spark and matches that physical plan:

SELECT e.*
FROM   scott.EMP AS e
JOIN (
  SELECT s.SAL              AS sal_key,
         AVG(s.SAL)         AS avg_sal
  FROM   (SELECT DISTINCT SAL FROM scott.EMP) AS s
  CROSS JOIN scott.SALGRADE AS g
  WHERE  g.HISAL IS NOT NULL
  GROUP  BY s.SAL
) AS sub
  ON e.SAL <=> sub.sal_key   -- IS NOT DISTINCT FROM (null-safe equi-key, matches plan)
 AND e.SAL  > sub.avg_sal    -- nested-loop range predicate, matches plan

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLScalarSubqueryTest.java at testCorrelatedScalarSubqueryInWhere, then inspect CalcitePPLAbstractTest.verifyPPLToSparkSQL. Run the named test and compare its emitted SQL with Spark analysis; done when the test no longer pins or accepts invalid Spark SQL and its expectation reflects valid behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spark, sql
Domain
backend-api-design, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.