This is an automated email from the ASF dual-hosted git repository.

zstan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new c21741a0c78 IGNITE-28959 Calcite. Merge Join occasionally return wrong 
results (#13455)
c21741a0c78 is described below

commit c21741a0c7852ad2ae213dd129d8c7a82a222e4e
Author: Evgeniy Stanilovskiy <[email protected]>
AuthorDate: Tue Aug 11 08:04:13 2026 +0300

    IGNITE-28959 Calcite. Merge Join occasionally return wrong results (#13455)
---
 .../query/calcite/exec/rel/MergeJoinNode.java      | 78 +++++++++++-----------
 .../calcite/exec/rel/JoinBuffersExecutionTest.java |  3 -
 .../integration/CorrelatesIntegrationTest.java     | 24 +++++++
 3 files changed, 64 insertions(+), 41 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MergeJoinNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MergeJoinNode.java
index 800e66e918b..57b8a488130 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MergeJoinNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MergeJoinNode.java
@@ -84,9 +84,6 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
      */
     protected final boolean distributed;
 
-    /** Flag indicating that join is in finishing stage (one of the inputs are 
ended, no more rows will be produced). */
-    protected boolean finishing;
-
     /**
      * @param ctx Execution context.
      * @param comp Join expression.
@@ -178,10 +175,10 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
 
         waitingLeft--;
 
-        if (!finishing)
-            leftInBuf.add(row);
+        leftInBuf.add(row);
 
-        join();
+        if (waitingLeft == 0 && waitingRight <= 0)
+            join();
     }
 
     /** */
@@ -191,10 +188,10 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
 
         waitingRight--;
 
-        if (!finishing)
-            rightInBuf.add(row);
+        rightInBuf.add(row);
 
-        join();
+        if (waitingRight == 0 && waitingLeft <= 0)
+            join();
     }
 
     /** */
@@ -204,7 +201,8 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
 
         waitingLeft = NOT_WAITING;
 
-        join();
+        if (waitingRight <= 0)
+            join();
     }
 
     /** */
@@ -214,7 +212,8 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
 
         waitingRight = NOT_WAITING;
 
-        join();
+        if (waitingLeft <= 0)
+            join();
     }
 
     /** */
@@ -238,27 +237,6 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
             && (!withMaterialization || rightMaterialization == null);
     }
 
-    /** */
-    protected boolean checkJoinFinished() throws Exception {
-        if (!finishing) {
-            finishing = true;
-            leftInBuf.clear();
-            rightInBuf.clear();
-            rightMaterialization = null;
-            rightIdx = 0;
-            drainMaterialization = false;
-        }
-
-        if (!distributed || (waitingLeft == NOT_WAITING && waitingRight == 
NOT_WAITING)) {
-            requested = 0;
-            downstream().end();
-
-            return true;
-        }
-
-        return false;
-    }
-
     /** */
     protected void tryToRequestInputs() throws Exception {
         if (waitingLeft == 0 && leftInBuf.size() <= HALF_BUF_SIZE)
@@ -414,8 +392,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && (leftFinished() || rightFinished(true)) && 
checkJoinFinished())
+            if (requested > 0 && (leftFinished() || rightFinished(true))) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
@@ -568,8 +550,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && leftFinished() && checkJoinFinished())
+            if (requested > 0 && leftFinished()) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
@@ -734,8 +720,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && rightFinished(true) && checkJoinFinished())
+            if (requested > 0 && rightFinished(true)) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
@@ -939,8 +929,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && leftFinished() && rightFinished(true) && 
checkJoinFinished())
+            if (requested > 0 && leftFinished() && rightFinished(true)) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
@@ -995,8 +989,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && (leftFinished() || rightFinished(false)) && 
checkJoinFinished())
+            if (requested > 0 && (leftFinished() || rightFinished(false))) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
@@ -1054,8 +1052,12 @@ public abstract class MergeJoinNode<Row> extends 
AbstractNode<Row> {
                 inLoop = false;
             }
 
-            if (requested > 0 && leftFinished() && checkJoinFinished())
+            if (requested > 0 && leftFinished()) {
+                requested = 0;
+                downstream().end();
+
                 return;
+            }
 
             tryToRequestInputs();
         }
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/JoinBuffersExecutionTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/JoinBuffersExecutionTest.java
index 4ff685131ce..0d52216bd98 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/JoinBuffersExecutionTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/JoinBuffersExecutionTest.java
@@ -34,8 +34,6 @@ import 
org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.testframework.GridTestUtils;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import static org.apache.calcite.rel.core.JoinRelType.ANTI;
 import static org.apache.calcite.rel.core.JoinRelType.FULL;
@@ -45,7 +43,6 @@ import static org.apache.calcite.rel.core.JoinRelType.RIGHT;
 import static org.apache.calcite.rel.core.JoinRelType.SEMI;
 
 /** Tests that buffers of join nodes are cleared at the join end and that a 
join node is not stuck. */
-@RunWith(Parameterized.class)
 public class JoinBuffersExecutionTest extends AbstractExecutionTest {
     /** */
     @Test
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CorrelatesIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CorrelatesIntegrationTest.java
index bc7df8a544a..61be928ac2c 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CorrelatesIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CorrelatesIntegrationTest.java
@@ -105,6 +105,30 @@ public class CorrelatesIntegrationTest extends 
AbstractBasicIntegrationTransacti
             .check();
     }
 
+    /** */
+    @Test
+    public void testMergeJoinUnderCorrelateSurvivesRewind() {
+        sql("CREATE TABLE t0 (a INTEGER, b INTEGER) WITH " + atomicity());
+        sql("CREATE TABLE t1 (a INTEGER, b INTEGER) WITH " + atomicity());
+        sql("CREATE TABLE t2 (a INTEGER, b INTEGER) WITH " + atomicity());
+
+        sql("INSERT INTO t0 VALUES (1, 1), (2, 2), (3, 3)");
+        sql("INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3)");
+        sql("INSERT INTO t2 VALUES (1, 1)");
+
+        String qry = "SELECT t0.a, (SELECT /*+ MERGE_JOIN */ count(*) FROM t1 
LEFT JOIN t2 ON t1.a = t2.a " +
+            "WHERE t1.a = (SELECT t0.a)) FROM t0";
+
+        // The defect is only reachable while the plan keeps a merge join 
under the correlate.
+        assertQuery(qry)
+            
.matches(QueryChecker.containsSubPlan("IgniteCorrelatedNestedLoopJoin"))
+            .matches(QueryChecker.containsSubPlan("IgniteMergeJoin"))
+            .returns(1, 1L)
+            .returns(2, 1L)
+            .returns(3, 1L)
+            .check();
+    }
+
     /**
      * Tests colocated join possible with the help of correlated distribution.
      */

Reply via email to