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

amashenkov pushed a commit to branch ignite-24675
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 238011f0ace6ad73e6fb7711da8ecf118e74c2f8
Author: amashenkov <[email protected]>
AuthorDate: Fri Feb 28 17:42:39 2025 +0300

    Add tests for non-equi join.
---
 .../sql/engine/exec/rel/HashJoinExecutionTest.java | 239 ++++++++++++++++-----
 1 file changed, 188 insertions(+), 51 deletions(-)

diff --git 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinExecutionTest.java
 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinExecutionTest.java
index 8de6ec7e288..08908342541 100644
--- 
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinExecutionTest.java
+++ 
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/HashJoinExecutionTest.java
@@ -184,67 +184,151 @@ public class HashJoinExecutionTest extends 
AbstractJoinExecutionTest {
 
     @ParameterizedTest
     @EnumSource(JoinRelType.class)
-    void checkHashJoinNodeWithDifferentBufferSize(JoinRelType joinType) {
-        validateDistinctData(executionContext(1), joinType, 0, 0);
-        validateDistinctData(executionContext(1), joinType, 0, 1);
-        validateDistinctData(executionContext(1), joinType, 0, 10);
-        validateDistinctData(executionContext(1), joinType, 1, 0);
-        validateDistinctData(executionContext(1), joinType, 1, 10);
-        validateDistinctData(executionContext(1), joinType, 10, 0);
-        validateDistinctData(executionContext(1), joinType, 10, 1);
-        validateDistinctData(executionContext(1), joinType, 10, 10);
-
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
0, 0);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
0, DEFAULT_BUFFER_SIZE - 1);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
0, DEFAULT_BUFFER_SIZE);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
0, DEFAULT_BUFFER_SIZE + 1);
-
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE - 1, 0);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE - 1, DEFAULT_BUFFER_SIZE - 1);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE - 1, DEFAULT_BUFFER_SIZE);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE - 1, DEFAULT_BUFFER_SIZE + 1);
-
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE, 0);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE - 1);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE + 1);
-
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE + 1, 0);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE + 1, DEFAULT_BUFFER_SIZE - 1);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE + 1, DEFAULT_BUFFER_SIZE);
-        validateDistinctData(executionContext(DEFAULT_BUFFER_SIZE), joinType, 
DEFAULT_BUFFER_SIZE + 1, DEFAULT_BUFFER_SIZE + 1);
+    void equiJoinWithDifferentBufferSize(JoinRelType joinType) {
+        int buffSize = 1;
+        validateEquiJoin(executionContext(buffSize), joinType, 0, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, 1);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, 10);
+        validateEquiJoin(executionContext(buffSize), joinType, 1, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, 1, 1);
+        validateEquiJoin(executionContext(buffSize), joinType, 1, 10);
+        validateEquiJoin(executionContext(buffSize), joinType, 10, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, 10, 1);
+        validateEquiJoin(executionContext(buffSize), joinType, 10, 10);
+
+        buffSize = DEFAULT_BUFFER_SIZE;
+        validateEquiJoin(executionContext(buffSize), joinType, 0, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, buffSize - 
1);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, buffSize);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, buffSize + 
1);
+
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize - 1, 
0);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize - 1, 
buffSize - 1);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize - 1, 
buffSize);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize - 1, 
buffSize + 1);
+
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize - 1);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize + 1);
+
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize + 1, 
0);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize + 1, 
buffSize - 1);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize + 1, 
buffSize);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize + 1, 
buffSize + 1);
+
+        buffSize = 2 * DEFAULT_BUFFER_SIZE;
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 0);
+        validateEquiJoin(executionContext(buffSize), joinType, 0, buffSize);
+        validateEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize);
     }
 
-    private static void validateDistinctData(
+    @ParameterizedTest
+    @EnumSource(value = JoinRelType.class, names = {"INNER", "SEMI"})
+    void nonEquiJoinWithDifferentBufferSize(JoinRelType joinType) {
+        int buffSize = 1;
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, 10);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 1, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 1, 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 1, 10);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 10, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 10, 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 10, 10);
+
+        buffSize = DEFAULT_BUFFER_SIZE;
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, buffSize 
- 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, buffSize);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, buffSize 
+ 1);
+
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize - 
1, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize - 
1, buffSize - 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize - 
1, buffSize);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize - 
1, buffSize + 1);
+
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize - 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize + 1);
+
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize + 
1, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize + 
1, buffSize - 1);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize + 
1, buffSize);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize + 
1, buffSize + 1);
+
+        buffSize = 2 * DEFAULT_BUFFER_SIZE;
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 0);
+        validateNonEquiJoin(executionContext(buffSize), joinType, 0, buffSize);
+        validateNonEquiJoin(executionContext(buffSize), joinType, buffSize, 
buffSize);
+    }
+
+    private static void validateEquiJoin(
             ExecutionContext<Object[]> ctx,
             JoinRelType joinType,
             int leftSize,
             int rightSize
     ) {
-        int resultSize = estimateResultSizeForDistinctInputs(joinType, 
leftSize, rightSize);
-
-        Object[] department = {1, "department"};
-        Object[] person = {1, "name", 2};
-        Iterable<Object[]> leftSource = IntStream.range(0, 
leftSize).mapToObj(i -> department)::iterator;
-        Iterable<Object[]> rightSource = IntStream.range(0, 
rightSize).mapToObj(i -> person)::iterator;
-
-        ScanNode<Object[]> left = new ScanNode<>(ctx, leftSource);
-        ScanNode<Object[]> right = new ScanNode<>(ctx, rightSource);
-
-        HashJoinNode<Object[]> join = createJoinNode(ctx, joinType, null);
-
-        join.register(asList(left, right));
-
-        RootNode<Object[]> node = new RootNode<>(ctx);
-        node.register(join);
+        { // Distinct inputs
+            Object[] person = {1, "name", 2};
+            Object[] department = {1, "department"};
+            int resultSize = estimateResultSizeForDistinctInputs(joinType, 
leftSize, rightSize);
+
+            validate(
+                    ctx,
+                    joinType,
+                    null,
+                    IntStream.range(0, leftSize).mapToObj(i -> 
department)::iterator,
+                    IntStream.range(0, rightSize).mapToObj(i -> 
person)::iterator,
+                    resultSize
+            );
+        }
 
-        int count = 0;
-        while (node.hasNext()) {
-            node.next();
-            count++;
+        { // Matching inputs
+            Object[] person = {1, "name", 2};
+            Object[] department = {2, "department"};
+            int resultSize = estimateResultSizeForEqualInputs(joinType, 
leftSize, rightSize);
+
+            validate(
+                    ctx,
+                    joinType,
+                    null,
+                    IntStream.range(0, leftSize).mapToObj(i -> 
department)::iterator,
+                    IntStream.range(0, rightSize).mapToObj(i -> 
person)::iterator,
+                    resultSize
+            );
         }
+    }
 
-        assertEquals(resultSize, count);
+    private static void validateNonEquiJoin(
+            ExecutionContext<Object[]> ctx,
+            JoinRelType joinType,
+            int leftSize,
+            int rightSize
+    ) {
+        Object[] person = {1, "name", 2};
+        Object[] department = {2, "department"};
+
+        int resultSize = estimateResultSizeForEqualInputs(joinType, leftSize, 
rightSize);
+
+        validate(
+                ctx,
+                joinType,
+                (l, r) -> true,
+                IntStream.range(0, leftSize).mapToObj(i -> 
department)::iterator,
+                IntStream.range(0, rightSize).mapToObj(i -> person)::iterator,
+                resultSize
+        );
+
+        validate(
+                ctx,
+                joinType,
+                (l, r) -> false,
+                IntStream.range(0, leftSize).mapToObj(i -> 
department)::iterator,
+                IntStream.range(0, rightSize).mapToObj(i -> person)::iterator,
+                0
+        );
     }
 
     private static int estimateResultSizeForDistinctInputs(
@@ -271,6 +355,59 @@ public class HashJoinExecutionTest extends 
AbstractJoinExecutionTest {
         }
     }
 
+    private static int estimateResultSizeForEqualInputs(
+            JoinRelType joinType,
+            int leftSize,
+            int rightSize
+    ) {
+        switch (joinType) {
+            case SEMI:
+                return rightSize == 0 ? 0 : leftSize;
+            case ANTI:
+                return rightSize == 0 ? leftSize : 0;
+            case LEFT:
+                return rightSize == 0 ? leftSize : leftSize * rightSize;
+            case RIGHT:
+                return leftSize == 0 ? rightSize : leftSize * rightSize;
+            case FULL:
+                return leftSize == 0 ? rightSize : rightSize == 0 ? leftSize : 
leftSize * rightSize;
+            case INNER:
+                return leftSize * rightSize;
+            case ASOF:
+            case LEFT_ASOF:
+                return Assumptions.abort("Unsupported join type: " + joinType);
+            default:
+                throw new IllegalArgumentException("Unsupported join type: " + 
joinType);
+        }
+    }
+
+    private static void validate(
+            ExecutionContext<Object[]> ctx,
+            JoinRelType joinType,
+            @Nullable BiPredicate<Object[], Object[]> condition,
+            Iterable<Object[]> leftSource,
+            Iterable<Object[]> rightSource,
+            int resultSize
+    ) {
+        ScanNode<Object[]> left = new ScanNode<>(ctx, leftSource);
+        ScanNode<Object[]> right = new ScanNode<>(ctx, rightSource);
+
+        HashJoinNode<Object[]> join = createJoinNode(ctx, joinType, condition);
+
+        join.register(asList(left, right));
+
+        RootNode<Object[]> node = new RootNode<>(ctx);
+        node.register(join);
+
+        int count = 0;
+        while (node.hasNext()) {
+            node.next();
+            count++;
+        }
+
+        assertEquals(resultSize, count);
+    }
+
     private void validate(
             JoinRelType joinType,
             @Nullable BiPredicate<Object[], Object[]> condition,

Reply via email to