[ 
https://issues.apache.org/jira/browse/CALCITE-7883?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

krooswu reassigned CALCITE-7883:
--------------------------------

    Assignee: krooswu

> RelToSqlConverter strips outer qualifier in visitAntiOrSemiJoin, so 
> INTERSECT/EXCEPT results compare against the wrong column
> -----------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CALCITE-7883
>                 URL: https://issues.apache.org/jira/browse/CALCITE-7883
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 1.42.0
>         Environment: macOS / Linux, JDK 17/21, Calcite 1.42.0 and main branch
>            Reporter: Cheng Wang
>            Assignee: krooswu
>            Priority: Major
>
> h3. Description
> When CoreRules.INTERSECT_TO_SEMI_JOIN or CoreRules.MINUS_TO_ANTI_JOIN rewrite 
> a set operation into a semi/anti join and RelToSqlConverter unparses it, 
> visitAntiOrSemiJoin (RelToSqlConverter.java:200-232) runs 
> AliasReplacementShuttle (RelToSqlConverter.java:387-392) over the join 
> condition.
> The shuttle strips the outer table alias ("t") from the correlated 
> references, so an unqualified "DEPTNO" inside the generated EXISTS / NOT 
> EXISTS resolves to the inner relation ("t0"."DEPTNO") instead of the outer 
> one. The resulting SQL compares each outer column with the wrong inner 
> column, and the enclosing GROUP BY does not repair it.
> h3. Reproducer (Calcite 1.42.0 and current main)
> Schema and data:
> * EMP(EMPNO, ENAME, JOB, MGR, SAL, COMM, DEPTNO) with rows:
>   (1, 'ALICE', 'CLERK', null, 100, null, 10),
>   (2, 'ALICE', 'ANALYST', 1, 150, 0, 10),
>   (3, 'CAROL', 'MGR', null, 200, null, 20),
>   (4, 'DAVE', null, 3, 300, 50, null)
> * DEPT(DEPTNO, NAME) with rows:
>   (10, 'ACC'), (10, 'ACC_DUP'), (20, 'RES'), (null, 'NULL_DEPT')
> Queries:
> {code:sql}
> -- INTERSECT case
> SELECT DEPTNO, MGR FROM EMP INTERSECT SELECT DEPTNO, DEPTNO FROM DEPT;
> -- EXCEPT case
> SELECT DEPTNO, MGR FROM EMP EXCEPT SELECT DEPTNO, DEPTNO FROM DEPT;
> {code}
> Apply CoreRules.INTERSECT_TO_SEMI_JOIN / CoreRules.MINUS_TO_ANTI_JOIN, then 
> unparse using RelToSqlConverter.
> h3. Expected vs Actual
> * INTERSECT:
>   Original RelNode result: [] (correct)
>   Generated SQL round-trip result: [[10, null], [20, null]] (WRONG: 
> unqualified "DEPTNO" binds to "t0"."DEPTNO")
> * EXCEPT:
>   Original RelNode result: [[10, 1], [10, null], [20, null], [null, 3]] 
> (correct)
>   Generated SQL round-trip result: [[10, 1], [null, 3]] (WRONG: rows silently 
> dropped)
> Generated SQL for INTERSECT:
> {code:sql}
> SELECT "DEPTNO", "MGR"
> FROM "EMP" AS "t"
> WHERE EXISTS (SELECT 1
>   FROM (SELECT "DEPTNO", "DEPTNO" AS "DEPTNO0" FROM "DEPT") AS "t0"
>   WHERE "DEPTNO" IS NOT DISTINCT FROM "t0"."DEPTNO"
>     AND "MGR" IS NOT DISTINCT FROM "t0"."DEPTNO0")
> GROUP BY "DEPTNO", "MGR"
> {code}
> h3. Root Cause
> AliasReplacementShuttle replaces qualified references with unqualified 
> aliases from the inner subquery (RelToSqlConverter.java:387-392). 
> visitAntiOrSemiJoin emits the condition without re-qualifying the outer side, 
> causing variable capture.
> This is distinct from closed issues CALCITE-5394 / CALCITE-5395 / 
> CALCITE-6804 (which covered SELECT * and corrupted NOT EXISTS condition for a 
> subquery alias, not qualifier stripping in set-op rewrites).
> h3. Suggested Fix
> Do not strip a qualifier that becomes ambiguous inside the generated 
> subquery; retain the outer alias (e.g. "t"."DEPTNO") or assign the inner 
> relation fresh aliases before running AliasReplacementShuttle.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to