Dwrite commented on code in PR #5036:
URL: https://github.com/apache/calcite/pull/5036#discussion_r3566443105


##########
core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java:
##########
@@ -547,9 +552,97 @@ public Result visit(Correlate e) {
     return result(join, leftResult, rightResult);
   }
 
+  /**
+   * Returns whether any expression (including nested sub-queries) references 
a correlated variable
+   * defined by the given input and having the same row type.
+   */
+  private static boolean inputRowTypeMatchesCorrelVariable(
+      RelNode input,
+      Set<CorrelationId> correlIds,
+      Iterable<? extends RexNode> exprs) {
+
+    if (correlIds.isEmpty()) {
+      return false;
+    }
+
+    final RelDataType inputRowType = input.getRowType();
+
+    /** Visitor that detects matching correlated variables. */
+    class Finder extends RexVisitorImpl<Void> {
+      boolean found;
+
+      Finder() {
+        super(true);
+      }
+
+      @Override public Void visitCorrelVariable(RexCorrelVariable v) {
+        if (correlIds.contains(v.id)

Review Comment:
   Hi @mihaibudiu
   
   I add new code `!(definedHere.isEmpty()) && e.getInput().getInputs().size() 
<= 1`. and  here's explaination.
   The purpose of calling `resetAliasForCorrelation` in `visit(Filter)` / 
`visit(Project)`
   is narrow: give `e.getInput()` a canonical alias so that a correlated
   sub-query embedded in this node's condition/projects can resolve
   `$corX.field` against that alias.
   
   This is only safe/necessary when `e.getInput()`'s rendered SQL already
   represents a single addressable relation:
   
   - `e.getInput().getInputs().size() <= 1` (TableScan, Values, Project,
     Filter, Aggregate, ...): these nodes always render as a single
     identity -- either a bare table reference or a freshly wrapped SELECT
     derived table. Assigning an alias here is safe, and is exactly the
     step the original bug was missing.
   
   - `e.getInput().getInputs().size() > 1` (Join, Correlate): `visit(Join)`
     already builds a multi-alias context via
     `joinContext(leftContext, rightContext)` (e.g. `{EMP: ..., DEPT: ...}`),
     keeping each side independently addressable -- which is exactly what a
     correlated sub-query needs to resolve `$corX.field`. Calling
     `resetAliasForCorrelation` here would collapse that multi-alias context
     into a single alias, destroying field resolution that was already
     correct (this is the root cause of the `DEPTNO0` leakage observed in
     the FilterIntoJoinRule / FilterCorrelateRule regression cases).
   
   Scope: this check only governs whether `visit(Filter)` / `visit(Project)`
   should invoke `resetAliasForCorrelation` for the purpose of correlation
   variable binding. It says nothing about the general contract of
   `resetAliasForCorrelation` itself, and does not affect other call sites
   (e.g. `visitAntiOrSemiJoin`, which legitimately calls it on a
   multi-alias Result for an unrelated purpose -- wrapping a complex
   anti-join input). That is why the check belongs at this call site rather
   than as an assertion inside `resetAliasForCorrelation`: an assertion
   inside the shared method would incorrectly impose this call site's
   precondition on every caller.
   
   Verified against three concrete scenarios:
   - EXISTS in WHERE: e.getInput() = TableScan (0 inputs) -> pushed=true, 
correct
   - FilterIntoJoinRule / FilterCorrelateRule residual filter: e.getInput() = 
Join
     (2 inputs) -> pushed=false, correct (avoids DEPTNO0 leakage)
   - foodmart correlated IN, e.getInput() = Project over Join: Project itself
     has 1 input -> pushed=true, correct



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to