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

asf-gitbox-commits 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 e59277bfcf2 IGNITE-28925 SQL Calcite: Fix unexpected 
downstream().end() call for execution nodes - Fixes #13407.
e59277bfcf2 is described below

commit e59277bfcf27099dee9802be3ac37014d017c35c
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Jul 31 09:46:29 2026 +0300

    IGNITE-28925 SQL Calcite: Fix unexpected downstream().end() call for 
execution nodes - Fixes #13407.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 .../query/calcite/exec/rel/AbstractNode.java       |  2 +-
 .../query/calcite/exec/rel/CollectNode.java        | 11 ++++-
 .../exec/rel/CorrelatedNestedLoopJoinNode.java     | 12 +++--
 .../query/calcite/exec/rel/SortAggregateNode.java  | 10 ++++-
 .../query/calcite/exec/rel/SortNode.java           |  7 +--
 .../integration/CollectIntegrationTest.java        | 52 ++++++++++++++++++++++
 .../integration/SortAggregateIntegrationTest.java  | 26 +++++++++++
 .../ignite/testsuites/IntegrationTestSuite.java    |  2 +
 8 files changed, 112 insertions(+), 10 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
index ef372b62f2a..e426069a32d 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java
@@ -38,7 +38,7 @@ import static 
org.apache.ignite.IgniteSystemProperties.IGNITE_CALCITE_EXEC_MODIF
  */
 public abstract class AbstractNode<Row> implements Node<Row> {
     /** */
-    protected static final int IN_BUFFER_SIZE = 
IgniteSystemProperties.getInteger(IGNITE_CALCITE_EXEC_IN_BUFFER_SIZE, 512);
+    public static final int IN_BUFFER_SIZE = 
IgniteSystemProperties.getInteger(IGNITE_CALCITE_EXEC_IN_BUFFER_SIZE, 512);
 
     /** */
     protected static final int MODIFY_BATCH_SIZE = 
IgniteSystemProperties.getInteger(IGNITE_CALCITE_EXEC_MODIFY_BATCH_SIZE, 100);
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CollectNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CollectNode.java
index af13ba2b415..468d7b3a837 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CollectNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CollectNode.java
@@ -114,6 +114,11 @@ public class CollectNode<Row> extends 
MemoryTrackingNode<Row> implements SingleN
 
         if (waiting == 0)
             source().request(waiting = IN_BUFFER_SIZE);
+        else if (waiting < 0) {
+            requested = 0;
+
+            downstream().end();
+        }
     }
 
     /** {@inheritDoc} */
@@ -138,6 +143,7 @@ public class CollectNode<Row> extends 
MemoryTrackingNode<Row> implements SingleN
     @Override public void end() throws Exception {
         assert downstream() != null;
         assert waiting > 0;
+        assert requested > 0;
 
         checkState();
 
@@ -146,10 +152,13 @@ public class CollectNode<Row> extends 
MemoryTrackingNode<Row> implements SingleN
         if (isClosed())
             return;
 
+        requested--;
+
+        downstream().push(collector.get());
+
         if (requested > 0) {
             requested = 0;
 
-            downstream().push(collector.get());
             downstream().end();
         }
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CorrelatedNestedLoopJoinNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CorrelatedNestedLoopJoinNode.java
index 8860cc10570..48ddd9fe628 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CorrelatedNestedLoopJoinNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/CorrelatedNestedLoopJoinNode.java
@@ -282,6 +282,8 @@ public class CorrelatedNestedLoopJoinNode<Row> extends 
AbstractNode<Row> {
                 break;
 
             case END:
+                requested = 0;
+
                 downstream().end();
                 break;
 
@@ -338,8 +340,11 @@ public class CorrelatedNestedLoopJoinNode<Row> extends 
AbstractNode<Row> {
 
             state = State.END;
 
-            if (requested > 0)
+            if (requested > 0) {
+                requested = 0;
+
                 downstream().end();
+            }
         }
         else {
             prepareCorrelations();
@@ -469,8 +474,9 @@ public class CorrelatedNestedLoopJoinNode<Row> extends 
AbstractNode<Row> {
 
                 state = State.END;
 
-                if (requested > 0)
-                    downstream().end();
+                requested = 0;
+
+                downstream().end();
 
                 return;
             }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortAggregateNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortAggregateNode.java
index 3b261b936b3..e03552427d1 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortAggregateNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortAggregateNode.java
@@ -101,8 +101,11 @@ public class SortAggregateNode<Row> extends 
AggregateNode<Row> {
 
             source().request(IN_BUFFER_SIZE);
         }
-        else if (waiting < 0)
+        else if (waiting < 0) {
+            requested = 0;
+
             downstream().end();
+        }
     }
 
     /** {@inheritDoc} */
@@ -163,8 +166,11 @@ public class SortAggregateNode<Row> extends 
AggregateNode<Row> {
             doPush();
         }
 
-        if (requested > 0)
+        if (requested > 0) {
+            requested = 0;
+
             downstream().end();
+        }
 
         grp = null;
         prevRow = null;
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortNode.java
index 09376b210f6..1347fced4b6 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortNode.java
@@ -205,10 +205,11 @@ public class SortNode<Row> extends 
MemoryTrackingNode<Row> implements SingleNode
             }
 
             if (reversed == null ? rows.isEmpty() : reversed.isEmpty()) {
-                if (requested > 0)
-                    downstream().end();
+                if (requested > 0) {
+                    requested = 0;
 
-                requested = 0;
+                    downstream().end();
+                }
             }
         }
         finally {
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CollectIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CollectIntegrationTest.java
new file mode 100644
index 00000000000..726a3453e1b
--- /dev/null
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/CollectIntegrationTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.calcite.integration;
+
+import 
org.apache.ignite.internal.processors.query.calcite.exec.rel.AbstractNode;
+import org.junit.Test;
+
+/**
+ * Integration test for collect node.
+ */
+public class CollectIntegrationTest extends AbstractBasicIntegrationTest {
+    /**
+     * Tests that collect node correctly handles the case when downstream 
requests
+     * limited number of rows, where collect must push one row and then
+     * properly terminate downstream.
+     */
+    @Test
+    public void testRequestLimitedRowsCountFromCollect() {
+        sql("CREATE TABLE t(a INT)");
+
+        sql("INSERT INTO t (a) VALUES (?)", 0);
+
+        String sql = "SELECT /*+ CNL_JOIN */ ARRAY(SELECT a FROM t) FROM t 
LIMIT 1";
+
+        assertQuery(sql).resultSize(1).check();
+
+        /**
+         * The data source size of (buffer size + 1) is used to ensure that 
multiple batches are needed
+         * on right hand of CNLJ to process all input rows, in this case left 
hand is not requested
+         * immediately after endLeft() call.
+         */
+        for (int i = 1; i < AbstractNode.IN_BUFFER_SIZE + 1; i++)
+            sql("INSERT INTO t (a) VALUES (?)", i);
+
+        assertQuery(sql).resultSize(1).check();
+    }
+}
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SortAggregateIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SortAggregateIntegrationTest.java
index 8d9f9cc8535..dd802750931 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SortAggregateIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/SortAggregateIntegrationTest.java
@@ -28,6 +28,7 @@ import org.apache.ignite.cache.QueryIndex;
 import org.apache.ignite.cache.QueryIndexType;
 import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import 
org.apache.ignite.internal.processors.query.calcite.exec.rel.AbstractNode;
 import org.apache.ignite.internal.util.typedef.F;
 import org.junit.Test;
 
@@ -155,6 +156,31 @@ public class SortAggregateIntegrationTest extends 
AbstractBasicIntegrationTransa
             .check();
     }
 
+    /**
+     * Tests that sort aggregate node correctly handles the case when input 
data
+     * ends exactly when the requested number of rows is satisfied.
+     */
+    @Test
+    public void testRequestRowsAfterInputEnds() {
+        /**
+         * With input rows count equals to the buffer size, the last row 
completes both
+         * the input data and the requested count in the same cycle. This 
triggers
+         * a synchronous request() call from within push() to fill the buffer, 
and
+         * the node must properly handle the termination on the subsequent 
request()
+         * call rather than on end().
+         */
+        int bufSize = AbstractNode.IN_BUFFER_SIZE;
+
+        sql("CREATE TABLE t0(a INTEGER PRIMARY KEY, b INTEGER) WITH 
template=replicated," + atomicity());
+
+        for (int i = 0; i < bufSize; i++)
+            sql("INSERT INTO t0 VALUES (?, ?)", i, i);
+
+        assertQuery("SELECT t1.a FROM t0 AS t1 JOIN (SELECT a, count(a) FROM 
t0 GROUP BY a) AS t2 ON t1.a = t2.a")
+            .resultSize(bufSize)
+            .check();
+    }
+
     /**
      * @param c Cache.
      * @param rows Rows count.
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
 
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
index e392d7c78bb..7c32feb3186 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java
@@ -32,6 +32,7 @@ import 
org.apache.ignite.internal.processors.query.calcite.integration.CacheWith
 import 
org.apache.ignite.internal.processors.query.calcite.integration.CalciteBasicSecondaryIndexIntegrationTest;
 import 
org.apache.ignite.internal.processors.query.calcite.integration.CalciteErrorHandlilngIntegrationTest;
 import 
org.apache.ignite.internal.processors.query.calcite.integration.CalcitePlanningDumpTest;
+import 
org.apache.ignite.internal.processors.query.calcite.integration.CollectIntegrationTest;
 import 
org.apache.ignite.internal.processors.query.calcite.integration.CorrelatesIntegrationTest;
 import 
org.apache.ignite.internal.processors.query.calcite.integration.DataTypesTest;
 import 
org.apache.ignite.internal.processors.query.calcite.integration.DateTimeTest;
@@ -187,6 +188,7 @@ import org.junit.runners.Suite;
     SelectByKeyFieldTest.class,
     WindowIntegrationTest.class,
     CalciteMessageUnmarshalThreadIntegrationTest.class,
+    CollectIntegrationTest.class,
 })
 public class IntegrationTestSuite {
 }

Reply via email to