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

mihaibudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 50447b3a52 [CALCITE-7557] Linq4j.ListEnumerable.take(int) / skip(int) 
diverge from EnumerableDefaults on negative counts
50447b3a52 is described below

commit 50447b3a525c20bf4c8f0d5b16178679fed2fe19
Author: microbluey <[email protected]>
AuthorDate: Mon Jul 27 11:51:47 2026 +0800

    [CALCITE-7557] Linq4j.ListEnumerable.take(int) / skip(int) diverge from 
EnumerableDefaults on negative counts
    
    ListEnumerable specializes take(int) and skip(int) for lists, but unlike
    the adjacent BigDecimal overloads, which clamp via 
count.max(BigDecimal.ZERO),
    the int versions pass the count straight to List.subList. A negative count
    therefore threw IllegalArgumentException from take and
    IndexOutOfBoundsException from skip, while the generic EnumerableDefaults
    path returns an empty enumerable and the original sequence respectively.
    
    Clamp the count to zero in both methods so that the optimized list path
    agrees with the generic path. Math.max is used rather than negating the
    count so that Integer.MIN_VALUE does not overflow.
---
 .../src/main/java/org/apache/calcite/linq4j/Linq4j.java  | 16 ++++++++++++----
 .../java/org/apache/calcite/linq4j/test/Linq4jTest.java  | 16 ++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
index df9e39e35b..c59303d4fd 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
@@ -587,10 +587,14 @@ static class ListEnumerable<T> extends 
CollectionEnumerable<T> {
 
     @Override public Enumerable<T> skip(int count) {
       final List<T> list = toList();
-      if (count >= list.size()) {
+      // Clamp to zero, as the BigDecimal overload does, so that a negative
+      // count skips nothing and matches EnumerableDefaults.skip rather than
+      // throwing from List.subList.
+      final int rows = Math.max(count, 0);
+      if (rows >= list.size()) {
         return Linq4j.emptyEnumerable();
       }
-      return new ListEnumerable<>(list.subList(count, list.size()));
+      return new ListEnumerable<>(list.subList(rows, list.size()));
     }
 
     @Override public Enumerable<T> skip(BigDecimal count) {
@@ -605,10 +609,14 @@ static class ListEnumerable<T> extends 
CollectionEnumerable<T> {
 
     @Override public Enumerable<T> take(int count) {
       final List<T> list = toList();
-      if (count >= list.size()) {
+      // Clamp to zero, as the BigDecimal overload does, so that a negative
+      // count yields an empty enumerable and matches EnumerableDefaults.take
+      // rather than throwing from List.subList.
+      final int rows = Math.max(count, 0);
+      if (rows >= list.size()) {
         return this;
       }
-      return new ListEnumerable<>(list.subList(0, count));
+      return new ListEnumerable<>(list.subList(0, rows));
     }
 
     @Override public Enumerable<T> take(BigDecimal count) {
diff --git 
a/linq4j/src/test/java/org/apache/calcite/linq4j/test/Linq4jTest.java 
b/linq4j/src/test/java/org/apache/calcite/linq4j/test/Linq4jTest.java
index 62e7dd9f5e..cd894f964b 100644
--- a/linq4j/src/test/java/org/apache/calcite/linq4j/test/Linq4jTest.java
+++ b/linq4j/src/test/java/org/apache/calcite/linq4j/test/Linq4jTest.java
@@ -2238,4 +2238,20 @@ public String toString() {
       new Department("HR", 20, ImmutableList.of()),
       new Department("Marketing", 30, ImmutableList.of(emps[1])),
   };
+
+  @Test void testTakeListEnumerableNegativeSize() {
+    final List<Integer> values = Arrays.asList(1, 2, 3);
+
+    assertThat(EnumerableDefaults.take(Linq4j.asEnumerable(values), 
-1).toList(),
+        is(empty()));
+    assertThat(Linq4j.asEnumerable(values).take(-1).toList(), is(empty()));
+  }
+
+  @Test void testSkipListEnumerableNegativeSize() {
+    final List<Integer> values = Arrays.asList(1, 2, 3);
+
+    assertThat(EnumerableDefaults.skip(Linq4j.asEnumerable(values), 
-1).toList(),
+        is(values));
+    assertThat(Linq4j.asEnumerable(values).skip(-1).toList(), is(values));
+  }
 }

Reply via email to