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

alexpl 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 fda34191ddb IGNITE-27907 SQL Calcite: Fix EXCEPT set op operator wrong 
result - Fixes #12768.
fda34191ddb is described below

commit fda34191ddb91cf46ad012bc02ca51774084d7e1
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Thu Feb 19 12:15:45 2026 +0300

    IGNITE-27907 SQL Calcite: Fix EXCEPT set op operator wrong result - Fixes 
#12768.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 .../query/calcite/exec/rel/MinusNode.java          |  2 +-
 .../exec/rel/AbstractSetOpExecutionTest.java       | 70 ++++++++++++++++++++++
 .../calcite/exec/rel/IntersectExecutionTest.java   | 13 ++++
 .../query/calcite/exec/rel/MinusExecutionTest.java | 12 ++++
 .../integration/MemoryQuotasIntegrationTest.java   |  2 +-
 5 files changed, 97 insertions(+), 2 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusNode.java
index a989b050008..5f24fb50bc8 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusNode.java
@@ -78,7 +78,7 @@ public class MinusNode<Row> extends AbstractSetOpNode<Row> {
 
         /** {@inheritDoc} */
         @Override protected boolean affectResult(int[] cntrs) {
-            return cntrs[0] != cntrs[1];
+            return !all || cntrs[0] != cntrs[1];
         }
 
         /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractSetOpExecutionTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractSetOpExecutionTest.java
index 938071c0211..452e1633c84 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractSetOpExecutionTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractSetOpExecutionTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.query.calcite.exec.rel;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
@@ -169,6 +170,75 @@ public abstract class AbstractSetOpExecutionTest extends 
AbstractExecutionTest {
         assertFalse(root.hasNext());
     }
 
+    /** */
+    @Test
+    public void testDistributedInputs() {
+        // Check all variants containing from 0 to 2 duplicated rows for 2 
inputs.
+        int[][] inputVariants = {
+            {0, 0}, {0, 1}, {0, 2},
+            {1, 0}, {1, 1}, {1, 2},
+            {2, 0}, {2, 1}, {2, 2}
+        };
+
+        // Check 2 cluster nodes.
+        int[][] rowsCnt = new int[2][];
+
+        for (int node0input = 0; node0input < inputVariants.length; 
node0input++) {
+            for (int node1input = 0; node1input < inputVariants.length; 
node1input++) {
+                rowsCnt[0] = inputVariants[node0input];
+                rowsCnt[1] = inputVariants[node1input];
+                checkDistributedSetOp(false, rowsCnt);
+                checkDistributedSetOp(true, rowsCnt);
+            }
+        }
+    }
+
+    /**
+     * @param all All.
+     * @param rowsCnt Count of duplicated rows per node per input.
+     */
+    protected void checkDistributedSetOp(boolean all, int[][] rowsCnt) {
+        ExecutionContext<Object[]> ctx = executionContext();
+        RelDataType rowType = TypeUtils.createRowType(ctx.getTypeFactory(), 
String.class);
+
+        List<Node<Object[]>> mapNodes = new ArrayList<>();
+        int[] totalRowsCnt = new int[rowsCnt[0].length];
+
+        for (int i = 0; i < rowsCnt.length; i++) {
+            for (int j = 0; j < rowsCnt[i].length; j++)
+                totalRowsCnt[j] += rowsCnt[i][j];
+
+            List<Node<Object[]>> inputs = Arrays.stream(rowsCnt[i])
+                .mapToObj(cnt -> new ScanNode<>(ctx, rowType, new 
TestTable(cnt, rowType, r -> "test")))
+                .collect(Collectors.toList());
+
+            AbstractSetOpNode<Object[]> mapNode;
+
+            mapNode = setOpNodeFactory(ctx, rowType, MAP, all, inputs.size());
+
+            mapNode.register(inputs);
+
+            mapNodes.add(mapNode);
+        }
+
+        // Use union all to emulate streams from different cluster nodes.
+        Node<Object[]> unionNode = new UnionAllNode<>(ctx, rowType);
+        unionNode.register(mapNodes);
+
+        AbstractSetOpNode<Object[]> reduceNode = setOpNodeFactory(ctx, 
rowType, REDUCE, all, 1);
+
+        reduceNode.register(Collections.singletonList(unionNode));
+
+        RootNode<Object[]> root = new RootNode<>(ctx, rowType);
+        root.register(reduceNode);
+
+        assertEquals("Unexpected result [rowsCnt=" + 
Arrays.deepToString(rowsCnt) + ", all=" + all + ']',
+            expectedResultSize(totalRowsCnt, all), F.size(root));
+    }
+
+    /** */
+    protected abstract int expectedResultSize(int[] totalRowsCnt, boolean all);
+
     /** */
     protected abstract AbstractSetOpNode<Object[]> 
setOpNodeFactory(ExecutionContext<Object[]> ctx, RelDataType rowType,
         AggregateType type, boolean all, int inputsCnt);
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/IntersectExecutionTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/IntersectExecutionTest.java
index 31e61698c7a..172f698169e 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/IntersectExecutionTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/IntersectExecutionTest.java
@@ -79,4 +79,17 @@ public class IntersectExecutionTest extends 
AbstractSetOpExecutionTest {
 
         checkSetOp(single, all, Arrays.asList(ds1, ds2, ds3), expectedResult);
     }
+
+    /** {@inheritDoc} */
+    @Override protected int expectedResultSize(int[] totalRowsCnt, boolean 
all) {
+        int min = totalRowsCnt[0];
+
+        for (int i = 1; i < totalRowsCnt.length; i++)
+            min = Math.min(totalRowsCnt[i], min);
+
+        if (all)
+            return min;
+        else
+            return min > 0 ? 1 : 0;
+    }
 }
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusExecutionTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusExecutionTest.java
index 8708c98dca5..6e9532c6731 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusExecutionTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/MinusExecutionTest.java
@@ -79,4 +79,16 @@ public class MinusExecutionTest extends 
AbstractSetOpExecutionTest {
 
         checkSetOp(single, all, Arrays.asList(ds1, ds2, ds3), expectedResult);
     }
+
+    /** {@inheritDoc} */
+    @Override protected int expectedResultSize(int[] totalRowsCnt, boolean 
all) {
+        int sum1 = 0;
+        for (int i = 1; i < totalRowsCnt.length; i++)
+            sum1 += totalRowsCnt[i];
+
+        if (all)
+            return Math.max(totalRowsCnt[0] - sum1, 0);
+        else
+            return (totalRowsCnt[0] > 0 && sum1 == 0) ? 1 : 0;
+    }
 }
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MemoryQuotasIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MemoryQuotasIntegrationTest.java
index 1861a9aed61..19bb28fd566 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MemoryQuotasIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MemoryQuotasIntegrationTest.java
@@ -116,7 +116,7 @@ public class MemoryQuotasIntegrationTest extends 
AbstractBasicIntegrationTest {
             sql("INSERT INTO tbl3 VALUES (?, ?)", i, new byte[1000]);
 
         assertQuery("SELECT /*+ DISABLE_RULE('ColocatedMinusConverterRule') */ 
* FROM " +
-            "(SELECT id, b FROM tbl2 EXCEPT SELECT id, b FROM tbl3 WHERE id < 
800)")
+            "(SELECT id, b FROM tbl2 WHERE id < 800 EXCEPT SELECT id, b FROM 
tbl3 WHERE id < 600)")
             .matches(QueryChecker.containsSubPlan("IgniteMapMinus"))
             .resultSize(200)
             .check();

Reply via email to