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

xiedeyantu 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 a183b26d64 [CALCITE-7624] Support BigDecimal for FETCH and OFFSET in 
Enumerable
a183b26d64 is described below

commit a183b26d64c97f75d1555e2925f2d13a4a5ad8ac
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Thu Jul 9 10:05:50 2026 +0300

    [CALCITE-7624] Support BigDecimal for FETCH and OFFSET in Enumerable
---
 .../calcite/adapter/enumerable/EnumUtils.java      |  34 +++
 .../adapter/enumerable/EnumerableLimit.java        |  38 ++-
 .../adapter/enumerable/EnumerableLimitSort.java    |  12 +-
 .../enumerable/EnumerableRelImplementor.java       |   3 +
 .../enumerable/FetchOffsetRoundingPolicy.java      |  29 +++
 .../apache/calcite/prepare/CalcitePrepareImpl.java |   9 +
 .../calcite/sql/validate/SqlValidatorImpl.java     |   4 +-
 .../org/apache/calcite/util/BuiltInMethod.java     |   9 +-
 .../adapter/enumerable/CodeGeneratorTest.java      |  60 +++++
 .../calcite/adapter/enumerable/EnumUtilsTest.java  |  15 ++
 .../java/org/apache/calcite/test/JdbcTest.java     | 254 ++++++++++++++++++++-
 .../org/apache/calcite/test/SqlValidatorTest.java  |  18 ++
 .../test/enumerable/EnumerableLimitSortTest.java   | 190 +++++++++++++++
 core/src/test/resources/sql/sort.iq                |   9 +-
 .../apache/calcite/linq4j/DefaultEnumerable.java   |  16 ++
 .../apache/calcite/linq4j/EnumerableDefaults.java  | 204 +++++++++++++++--
 .../apache/calcite/linq4j/ExtendedEnumerable.java  |  27 +++
 .../java/org/apache/calcite/linq4j/Linq4j.java     |  22 ++
 .../org/apache/calcite/linq4j/test/Linq4jTest.java |  44 ++++
 site/_docs/reference.md                            |   5 +-
 20 files changed, 962 insertions(+), 40 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
index 2ed244dfa8..129180e584 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumUtils.java
@@ -72,6 +72,7 @@
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.math.RoundingMode;
 import java.sql.Date;
 import java.sql.Time;
@@ -112,6 +113,39 @@ private EnumUtils() {}
   public static final List<String> LEFT_RIGHT =
       ImmutableList.of("left", "right");
 
+  /** Converts a FETCH or OFFSET runtime value to {@link BigDecimal}.
+   *
+   * <p>The value must be numeric and non-negative. */
+  public static BigDecimal numberToBigDecimal(Object value, String kind) {
+    return numberToBigDecimal(value, kind, FetchOffsetRoundingPolicy.NONE);
+  }
+
+  /** Converts a FETCH or OFFSET runtime value to {@link BigDecimal}.
+   *
+   * <p>The value must be numeric and non-negative. The result is adjusted by
+   * the configured rounding policy. */
+  public static BigDecimal numberToBigDecimal(Object value, String kind,
+      FetchOffsetRoundingPolicy roundingPolicy) {
+    if (!(value instanceof Number)) {
+      throw new IllegalArgumentException(kind + " must be a number");
+    }
+    final Number number = (Number) value;
+    final BigDecimal decimal;
+    if (number instanceof BigDecimal) {
+      decimal = (BigDecimal) number;
+    } else if (number instanceof BigInteger) {
+      decimal = new BigDecimal((BigInteger) number);
+    } else if (number instanceof Float || number instanceof Double) {
+      decimal = BigDecimal.valueOf(number.doubleValue());
+    } else {
+      decimal = BigDecimal.valueOf(number.longValue());
+    }
+    if (decimal.signum() < 0) {
+      throw new IllegalArgumentException(kind + " must not be negative");
+    }
+    return roundingPolicy.round(decimal);
+  }
+
   /** Declares a method that overrides another method. */
   public static MethodDeclaration overridingMethodDecl(Method method,
       Iterable<ParameterExpression> parameters,
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
index 3c046de922..02fd54bdad 100644
--- 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
@@ -101,36 +101,50 @@ public static EnumerableLimit create(final RelNode input, 
@Nullable RexNode offs
             result.format);
 
     Expression v = builder.append("child", result.block);
+    Expression roundingPolicyExp = getRoundingPolicy(implementor);
     if (offset != null) {
       v =
           builder.append("offset",
-              Expressions.call(v, BuiltInMethod.SKIP.method,
-                  getExpression(offset)));
+              Expressions.call(BuiltInMethod.SKIP_BIG_DECIMAL.method, v,
+                  getExpression(offset, "OFFSET", roundingPolicyExp)));
     }
     if (fetch != null) {
       v =
           builder.append("fetch",
-              Expressions.call(v, BuiltInMethod.TAKE.method,
-                  getExpression(fetch)));
+              Expressions.call(BuiltInMethod.TAKE_BIG_DECIMAL.method, v,
+                  getExpression(fetch, "FETCH", roundingPolicyExp)));
     }
 
     builder.add(Expressions.return_(null, v));
     return implementor.result(physType, builder.toBlock());
   }
 
-  static Expression getExpression(RexNode rexNode) {
+  static Expression getExpression(RexNode rexNode, String kind,
+      Expression roundingPolicy) {
+    final Expression value;
     if (rexNode instanceof RexDynamicParam) {
       final RexDynamicParam param = (RexDynamicParam) rexNode;
-      return Expressions.convert_(
+      value =
           Expressions.call(DataContext.ROOT,
               BuiltInMethod.DATA_CONTEXT_GET.method,
-              Expressions.constant("?" + param.getIndex())),
-          Integer.class);
+              Expressions.constant("?" + param.getIndex()));
     } else {
-      // TODO: Enumerable runtime only supports INT types for FETCH and 
OFFSET, not BIGINT types.
-      //  Currently, using BIGINT types for execution will result in an error 
message.
-      //  This issue needs to be fixed. For more information, see CALCITE-7156.
-      return Expressions.constant(RexLiteral.intValue(rexNode));
+      value = Expressions.constant(RexLiteral.bigDecimalValue(rexNode));
     }
+    return Expressions.call(
+        BuiltInMethod.NUMBER_TO_BIG_DECIMAL_LIMIT.method,
+        value,
+        Expressions.constant(kind),
+        roundingPolicy);
+  }
+
+  static Expression getRoundingPolicy(EnumerableRelImplementor implementor) {
+    final Object roundingPolicy =
+        
implementor.map.get(EnumerableRelImplementor.FETCH_OFFSET_ROUNDING_POLICY);
+    if (roundingPolicy == null) {
+      return Expressions.field(null, FetchOffsetRoundingPolicy.class, "NONE");
+    }
+    return implementor.stash((FetchOffsetRoundingPolicy) roundingPolicy,
+        FetchOffsetRoundingPolicy.class);
   }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java
 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java
index defc28f69a..325fe687ba 100644
--- 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimitSort.java
@@ -30,7 +30,10 @@
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.math.BigDecimal;
+
 import static 
org.apache.calcite.adapter.enumerable.EnumerableLimit.getExpression;
+import static 
org.apache.calcite.adapter.enumerable.EnumerableLimit.getRoundingPolicy;
 
 /**
  * Implementation of {@link org.apache.calcite.rel.core.Sort} in
@@ -95,19 +98,20 @@ public static EnumerableLimitSort create(
     final PhysType inputPhysType = result.physType;
     final Pair<Expression, Expression> pair =
         
inputPhysType.generateCollationKey(this.collation.getFieldCollations());
+    final Expression roundingPolicyExp = getRoundingPolicy(implementor);
 
     final Expression fetchVal;
     if (this.fetch == null) {
-      fetchVal = Expressions.constant(Integer.MAX_VALUE);
+      fetchVal = Expressions.constant(BigDecimal.valueOf(Integer.MAX_VALUE));
     } else {
-      fetchVal = getExpression(this.fetch);
+      fetchVal = getExpression(this.fetch, "FETCH", roundingPolicyExp);
     }
 
     final Expression offsetVal;
     if (this.offset == null) {
-      offsetVal = Expressions.constant(0);
+      offsetVal = Expressions.constant(BigDecimal.ZERO);
     } else {
-      offsetVal = getExpression(this.offset);
+      offsetVal = getExpression(this.offset, "OFFSET", roundingPolicyExp);
     }
 
     builder.add(
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java
 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java
index 8d27d6f74c..ae380578c4 100644
--- 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRelImplementor.java
@@ -78,6 +78,9 @@
  * operators of {@link EnumerableConvention} calling convention.
  */
 public class EnumerableRelImplementor extends JavaRelImplementor {
+  public static final String FETCH_OFFSET_ROUNDING_POLICY =
+      "_fetchOffsetRoundingPolicy";
+
   public final Map<String, Object> map;
   private final Map<String, RexToLixTranslator.InputGetter> corrVars =
       new HashMap<>();
diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/FetchOffsetRoundingPolicy.java
 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/FetchOffsetRoundingPolicy.java
new file mode 100644
index 0000000000..027abcade6
--- /dev/null
+++ 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/FetchOffsetRoundingPolicy.java
@@ -0,0 +1,29 @@
+/*
+ * 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.calcite.adapter.enumerable;
+
+import java.math.BigDecimal;
+
+/** Defines how enumerable FETCH and OFFSET values are rounded. */
+public interface FetchOffsetRoundingPolicy {
+  /** Default policy that preserves the value produced by validation or
+   * parameter binding. */
+  FetchOffsetRoundingPolicy NONE = value -> value;
+
+  /** Rounds a FETCH or OFFSET value. */
+  BigDecimal round(BigDecimal value);
+}
diff --git 
a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java 
b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 8439fabd5f..06e27e361c 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -21,7 +21,9 @@
 import org.apache.calcite.adapter.enumerable.EnumerableConvention;
 import org.apache.calcite.adapter.enumerable.EnumerableInterpretable;
 import org.apache.calcite.adapter.enumerable.EnumerableRel;
+import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
 import org.apache.calcite.adapter.enumerable.EnumerableRules;
+import org.apache.calcite.adapter.enumerable.FetchOffsetRoundingPolicy;
 import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.AvaticaParameter;
@@ -1196,6 +1198,13 @@ protected SqlValidator createSqlValidator(CatalogReader 
catalogReader,
           CatalogReader.THREAD_LOCAL.set(catalogReader);
           final SqlConformance conformance = context.config().conformance();
           internalParameters.put("_conformance", conformance);
+          final FetchOffsetRoundingPolicy fetchOffsetRoundingPolicy =
+              planner.getContext().unwrap(FetchOffsetRoundingPolicy.class);
+          if (fetchOffsetRoundingPolicy != null) {
+            internalParameters.put(
+                EnumerableRelImplementor.FETCH_OFFSET_ROUNDING_POLICY,
+                fetchOffsetRoundingPolicy);
+          }
           bindable =
               EnumerableInterpretable.toBindable(internalParameters,
                   context.spark(), enumerable,
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index d8fd81a827..331c8849a6 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1763,11 +1763,11 @@ SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier 
id,
   private void handleOffsetFetch(@Nullable SqlNode offset, @Nullable SqlNode 
fetch) {
     if (offset instanceof SqlDynamicParam) {
       setValidatedNodeType(offset,
-          typeFactory.createSqlType(SqlTypeName.INTEGER));
+          typeFactory.createSqlType(SqlTypeName.DECIMAL));
     }
     if (fetch instanceof SqlDynamicParam) {
       setValidatedNodeType(fetch,
-          typeFactory.createSqlType(SqlTypeName.INTEGER));
+          typeFactory.createSqlType(SqlTypeName.DECIMAL));
     }
   }
 
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 295d7d662e..2b07069b65 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -21,6 +21,7 @@
 import org.apache.calcite.adapter.enumerable.BasicAggregateLambdaFactory;
 import org.apache.calcite.adapter.enumerable.BasicLazyAccumulator;
 import org.apache.calcite.adapter.enumerable.EnumUtils;
+import org.apache.calcite.adapter.enumerable.FetchOffsetRoundingPolicy;
 import org.apache.calcite.adapter.enumerable.LazyAggregateLambdaFactory;
 import org.apache.calcite.adapter.enumerable.MatchUtils;
 import org.apache.calcite.adapter.enumerable.SourceSorter;
@@ -296,7 +297,7 @@ public enum BuiltInMethod {
   ORDER_BY(ExtendedEnumerable.class, "orderBy", Function1.class,
       Comparator.class),
   ORDER_BY_WITH_FETCH_AND_OFFSET(EnumerableDefaults.class, "orderBy", 
Enumerable.class,
-      Function1.class, Comparator.class, int.class, int.class),
+      Function1.class, Comparator.class, BigDecimal.class, BigDecimal.class),
   UNION(ExtendedEnumerable.class, "union", Enumerable.class),
   CONCAT(ExtendedEnumerable.class, "concat", Enumerable.class),
   REPEAT_UNION(EnumerableDefaults.class, "repeatUnion", Enumerable.class,
@@ -308,7 +309,11 @@ public enum BuiltInMethod {
   INTERSECT(ExtendedEnumerable.class, "intersect", Enumerable.class, 
boolean.class),
   EXCEPT(ExtendedEnumerable.class, "except", Enumerable.class, boolean.class),
   SKIP(ExtendedEnumerable.class, "skip", int.class),
+  SKIP_BIG_DECIMAL(EnumerableDefaults.class, "skip", Enumerable.class,
+      BigDecimal.class),
   TAKE(ExtendedEnumerable.class, "take", int.class),
+  TAKE_BIG_DECIMAL(EnumerableDefaults.class, "take", Enumerable.class,
+      BigDecimal.class),
   SINGLETON_ENUMERABLE(Linq4j.class, "singletonEnumerable", Object.class),
   EMPTY_ENUMERABLE(Linq4j.class, "emptyEnumerable"),
   NULLS_COMPARATOR(Functions.class, "nullsComparator", boolean.class,
@@ -390,6 +395,8 @@ public enum BuiltInMethod {
       Calendar.class),
   TIME_ZONE_GET_OFFSET(TimeZone.class, "getOffset", long.class),
   LONG_VALUE(Number.class, "longValue"),
+  NUMBER_TO_BIG_DECIMAL_LIMIT(EnumUtils.class, "numberToBigDecimal",
+      Object.class, String.class, FetchOffsetRoundingPolicy.class),
   STRING_TO_UPPER(String.class, "toUpperCase"),
   COMPARATOR_COMPARE(Comparator.class, "compare", Object.class, Object.class),
   COLLECTIONS_REVERSE_ORDER(Collections.class, "reverseOrder"),
diff --git 
a/core/src/test/java/org/apache/calcite/adapter/enumerable/CodeGeneratorTest.java
 
b/core/src/test/java/org/apache/calcite/adapter/enumerable/CodeGeneratorTest.java
index 1a32d8de01..d8e605e391 100644
--- 
a/core/src/test/java/org/apache/calcite/adapter/enumerable/CodeGeneratorTest.java
+++ 
b/core/src/test/java/org/apache/calcite/adapter/enumerable/CodeGeneratorTest.java
@@ -16,7 +16,9 @@
  */
 package org.apache.calcite.adapter.enumerable;
 
+import org.apache.calcite.DataContexts;
 import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.tree.ClassDeclaration;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.plan.ConventionTraitDef;
@@ -26,19 +28,32 @@
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.runtime.Bindable;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParseException;
 import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.test.SqlTestFactory;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.jupiter.api.Test;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 
 /**
@@ -97,4 +112,49 @@ public class CodeGeneratorTest {
     assertFalse(javaCode.contains("case_when_value3"));
     assertFalse(javaCode.contains("case_when_value4"));
   }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test public void testFetchOffsetRoundingPolicy() {
+    final JavaTypeFactoryImpl typeFactory =
+        new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+    final RexBuilder rexBuilder = new RexBuilder(typeFactory);
+    final VolcanoPlanner planner = new VolcanoPlanner();
+    planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
+    final RelOptCluster cluster = RelOptCluster.create(planner, rexBuilder);
+    final RelDataType rowType =
+        typeFactory.builder().add("i", SqlTypeName.INTEGER).build();
+    final RelDataType integerType = rowType.getFieldList().get(0).getType();
+    final ImmutableList<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.of(
+            ImmutableList.of(
+                rexBuilder.makeExactLiteral(BigDecimal.ONE,
+                integerType)),
+            ImmutableList.of(
+                rexBuilder.makeExactLiteral(BigDecimal.valueOf(2),
+                integerType)),
+            ImmutableList.of(
+                rexBuilder.makeExactLiteral(BigDecimal.valueOf(3),
+                integerType)));
+    final EnumerableValues values =
+        EnumerableValues.create(cluster, rowType, tuples);
+    final RelDataType decimalType =
+        typeFactory.createSqlType(SqlTypeName.DECIMAL, 2, 1);
+    final EnumerableLimit limit =
+        EnumerableLimit.create(values, null,
+            rexBuilder.makeExactLiteral(new BigDecimal("1.5"), decimalType));
+
+    final Map<String, Object> parameters = new HashMap<>();
+    parameters.put(EnumerableRelImplementor.FETCH_OFFSET_ROUNDING_POLICY,
+        (FetchOffsetRoundingPolicy) value ->
+            value.setScale(0, RoundingMode.DOWN));
+    final Bindable bindable =
+        EnumerableInterpretable.toBindable(parameters, null, limit,
+            EnumerableRel.Prefer.ARRAY);
+
+    final Enumerable<?> enumerable = 
bindable.bind(DataContexts.of(parameters));
+    final List<?> result = enumerable.toList();
+    assertThat(result, hasSize(1));
+  }
 }
diff --git 
a/core/src/test/java/org/apache/calcite/adapter/enumerable/EnumUtilsTest.java 
b/core/src/test/java/org/apache/calcite/adapter/enumerable/EnumUtilsTest.java
index 267aa39ca1..40b825939d 100644
--- 
a/core/src/test/java/org/apache/calcite/adapter/enumerable/EnumUtilsTest.java
+++ 
b/core/src/test/java/org/apache/calcite/adapter/enumerable/EnumUtilsTest.java
@@ -29,6 +29,7 @@
 import org.junit.jupiter.api.Test;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.Arrays;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -171,6 +172,20 @@ public final class EnumUtilsTest {
     assertThat(Expressions.toString(e2), is("(String) (Object) null"));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testNumberToBigDecimalPreservesFractionalNumber() {
+    final FetchOffsetRoundingPolicy ceiling =
+        value -> value.setScale(0, RoundingMode.CEILING);
+    assertThat(EnumUtils.numberToBigDecimal(1.5D, "FETCH"),
+        is(new BigDecimal("1.5")));
+    assertThat(EnumUtils.numberToBigDecimal(1.5F, "OFFSET"),
+        is(new BigDecimal("1.5")));
+    assertThat(EnumUtils.numberToBigDecimal(1.5D, "FETCH", ceiling),
+        is(BigDecimal.valueOf(2)));
+  }
+
   @Test void testMethodCallExpression() {
     // test for Object.class method parameter type
     final ConstantExpression arg0 = Expressions.constant(1, int.class);
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 8ebc5b87c9..0c5ab19b2e 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -5808,6 +5808,256 @@ private CalciteAssert.AssertQuery withEmpDept(String 
sql) {
         Matchers.returnsUnordered("name=Eric"));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetFetchWithBigDecimal() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.valueOf(3));
+          p.setBigDecimal(2, BigDecimal.valueOf(4));
+        })
+        .returnsUnordered("name=Eric");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedFetchWithFractionalBigDecimal() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.ZERO);
+          p.setBigDecimal(2, new BigDecimal("1.5"));
+        })
+        .returnsUnordered("name=Bill", "name=Theodore");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetWithFractionalBigDecimal() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, new BigDecimal("1.5"));
+          p.setBigDecimal(2, BigDecimal.ONE);
+        })
+        .returnsUnordered("name=Sebastian");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testFetchLiteralFractionalBigDecimal() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset 0 fetch next 1.5 rows only")
+        .explainContains("EnumerableLimit(offset=[0], fetch=[1.5:DECIMAL(2, 
1)])")
+        .returnsUnordered("name=Bill", "name=Theodore");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testOffsetLiteralFractionalBigDecimal() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset 1.5 fetch next 1 rows only")
+        .explainContains("EnumerableLimit(offset=[1.5:DECIMAL(2, 1)], 
fetch=[1])")
+        .returnsUnordered("name=Sebastian");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetFetchWithIntegerParameters() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setInt(1, 1);
+          p.setInt(2, 2);
+        })
+        .returnsUnordered("name=Theodore", "name=Sebastian");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetFetchWithLongParameters() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setLong(1, 1L);
+          p.setLong(2, 2L);
+        })
+        .returnsUnordered("name=Theodore", "name=Sebastian");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetWithBigDecimalAboveIntegerMax() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1,
+              BigDecimal.valueOf(Integer.MAX_VALUE).add(BigDecimal.ONE));
+          p.setBigDecimal(2, BigDecimal.ONE);
+        })
+        .returnsUnordered();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testOffsetLiteralAboveIntegerMax() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "order by \"empid\" offset 2147483648 fetch next 1 rows only")
+        .explainContains("EnumerableLimit(offset=[2147483648:BIGINT], 
fetch=[1])")
+        .returnsUnordered();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedOffsetFetchWithBigDecimalWithoutOrderBy() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.ZERO);
+          p.setBigDecimal(2, BigDecimal.ZERO);
+        })
+        .returnsUnordered();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedNegativeFetchWithBigDecimalWithoutOrderBy()
+      throws Exception {
+    CalciteAssert.hr()
+        .doWithConnection(connection -> {
+          final String sql = "select \"name\"\n"
+              + "from \"hr\".\"emps\"\n"
+              + "offset ? fetch next ? rows only";
+          try (PreparedStatement p = connection.prepareStatement(sql)) {
+            p.setBigDecimal(1, BigDecimal.ZERO);
+            p.setBigDecimal(2, BigDecimal.valueOf(-1));
+            final SQLException e = assertThrows(SQLException.class, 
p::executeQuery);
+            assertThat(e.getMessage(), containsString("FETCH must not be 
negative"));
+          } catch (SQLException e) {
+            throw TestUtil.rethrow(e);
+          }
+        });
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedNegativeOffsetWithBigDecimalWithoutOrderBy()
+      throws Exception {
+    CalciteAssert.hr()
+        .doWithConnection(connection -> {
+          final String sql = "select \"name\"\n"
+              + "from \"hr\".\"emps\"\n"
+              + "offset ? fetch next ? rows only";
+          try (PreparedStatement p = connection.prepareStatement(sql)) {
+            p.setBigDecimal(1, BigDecimal.valueOf(-1));
+            p.setBigDecimal(2, BigDecimal.valueOf(2));
+            final SQLException e = assertThrows(SQLException.class, 
p::executeQuery);
+            assertThat(e.getMessage(), containsString("OFFSET must not be 
negative"));
+          } catch (SQLException e) {
+            throw TestUtil.rethrow(e);
+          }
+        });
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedNegativeLimitWithBigDecimalWithoutOrderBy()
+      throws Exception {
+    CalciteAssert.hr()
+        .doWithConnection(connection -> {
+          final String sql = "select \"name\"\n"
+              + "from \"hr\".\"emps\"\n"
+              + "limit ? offset ?";
+          try (PreparedStatement p = connection.prepareStatement(sql)) {
+            p.setBigDecimal(1, BigDecimal.valueOf(-1));
+            p.setBigDecimal(2, BigDecimal.ZERO);
+            final SQLException e = assertThrows(SQLException.class, 
p::executeQuery);
+            assertThat(e.getMessage(), containsString("FETCH must not be 
negative"));
+          } catch (SQLException e) {
+            throw TestUtil.rethrow(e);
+          }
+        });
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testPreparedFetchWithBigDecimalAboveLongMaxWithoutOrderBy() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimit(offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.ZERO);
+          p.setBigDecimal(2,
+              BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE));
+        })
+        .returnsUnordered(
+            "name=Bill",
+            "name=Eric",
+            "name=Sebastian",
+            "name=Theodore");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testFetchLiteralAboveLongMaxWithoutOrderBy() {
+    CalciteAssert.hr()
+        .query("select \"name\"\n"
+            + "from \"hr\".\"emps\"\n"
+            + "offset 0 fetch next 9223372036854775808 rows only")
+        .explainContains("EnumerableLimit(offset=[0], "
+            + "fetch=[9223372036854775808:DECIMAL(19, 0)])")
+        .returnsUnordered(
+            "name=Bill",
+            "name=Eric",
+            "name=Sebastian",
+            "name=Theodore");
+  }
+
   private void checkPreparedOffsetFetch(final int offset, final int fetch,
       final Matcher<? super ResultSet> matcher) throws Exception {
     CalciteAssert.hr()
@@ -5819,8 +6069,8 @@ private void checkPreparedOffsetFetch(final int offset, 
final int fetch,
                    connection.prepareStatement(sql)) {
             final ParameterMetaData pmd = p.getParameterMetaData();
             assertThat(pmd.getParameterCount(), is(2));
-            assertThat(pmd.getParameterType(1), is(Types.INTEGER));
-            assertThat(pmd.getParameterType(2), is(Types.INTEGER));
+            assertThat(pmd.getParameterType(1), is(Types.DECIMAL));
+            assertThat(pmd.getParameterType(2), is(Types.DECIMAL));
             p.setInt(1, offset);
             p.setInt(2, fetch);
             try (ResultSet r = p.executeQuery()) {
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 094e0447b1..efa86f9c0b 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -10630,6 +10630,24 @@ void testGroupExpressionEquivalenceParams() {
         .rewritesTo(expected);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testNegativeFetchOffsetLimit() {
+    sql("select name from dept limit ^-^1")
+        .fails("(?s).*Encountered \"-\".*");
+    sql("select name from dept offset ^-^1")
+        .fails("(?s).*Encountered \"-\".*");
+    sql("select name from dept fetch next ^-^1 rows only")
+        .fails("(?s).*Encountered \"-\".*");
+    sql("select name from dept order by name limit ^-^1")
+        .fails("(?s).*Encountered \"-\".*");
+    sql("select name from dept order by name offset ^-^1")
+        .fails("(?s).*Encountered \"-\".*");
+    sql("select name from dept order by name fetch next ^-^1 rows only")
+        .fails("(?s).*Encountered \"-\".*");
+  }
+
   @Test void testRewriteWithUnionFetchWithoutOrderBy() {
     final String sql =
         "select name from dept union all select name from dept limit 2";
diff --git 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableLimitSortTest.java
 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableLimitSortTest.java
index 5c0d35ca4b..90256f8fdd 100644
--- 
a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableLimitSortTest.java
+++ 
b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableLimitSortTest.java
@@ -24,11 +24,19 @@
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.test.CalciteAssert;
 import org.apache.calcite.test.schemata.hr.HrSchemaBig;
+import org.apache.calcite.util.TestUtil;
 
 import org.junit.jupiter.api.Test;
 
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
 import java.util.function.Consumer;
 
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
 /** Tests for
  * {@link org.apache.calcite.adapter.enumerable.EnumerableLimitSort}. */
 public class EnumerableLimitSortTest {
@@ -158,6 +166,165 @@ public class EnumerableLimitSortTest {
             "commission=250; empid=36");
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void limitWithoutOffsetUsesBigDecimalDefaultOffset() {
+    tester("select empid from emps order by empid limit 2")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "fetch=[2])")
+        .returnsOrdered(
+            "empid=1",
+            "empid=2");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void offsetWithoutLimitUsesBigDecimalDefaultFetch() {
+    tester("select empid from emps where empid <= 4 order by empid offset 2")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[2])")
+        .returnsOrdered(
+            "empid=3",
+            "empid=4");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void fractionalOffsetAndFetchUseIndependentRowCounts() {
+    tester("select empid from emps where empid <= 4 order by empid "
+        + "offset 1.5 fetch next 1.5 rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[1.5:DECIMAL(2, 1)], fetch=[1.5:DECIMAL(2, 1)])")
+        .returnsOrdered(
+            "empid=3",
+            "empid=4");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithNegativeBigDecimalFetch() throws Exception {
+    final String sql = "select empid from emps where empid <= 2 order by empid 
"
+        + "offset ? fetch next ? rows only";
+    tester(sql)
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?0], fetch=[?1])");
+    failsWithNegativeParameter(sql, p -> {
+      p.setBigDecimal(1, BigDecimal.ZERO);
+      p.setBigDecimal(2, BigDecimal.valueOf(-1));
+    }, "FETCH must not be negative");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithNegativeBigDecimalOffset() throws Exception {
+    final String sql = "select empid from emps where empid <= 3 order by empid 
"
+        + "offset ? fetch next ? rows only";
+    tester(sql)
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?0], fetch=[?1])");
+    failsWithNegativeParameter(sql, p -> {
+      p.setBigDecimal(1, BigDecimal.valueOf(-1));
+      p.setBigDecimal(2, BigDecimal.valueOf(2));
+    }, "OFFSET must not be negative");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithNegativeBigDecimalLimit() throws Exception {
+    final String sql = "select empid from emps where empid <= 2 order by empid 
"
+        + "limit ? offset ?";
+    tester(sql)
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?1], fetch=[?0])");
+    failsWithNegativeParameter(sql, p -> {
+      p.setBigDecimal(1, BigDecimal.valueOf(-1));
+      p.setBigDecimal(2, BigDecimal.ZERO);
+    }, "FETCH must not be negative");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithBigDecimal() {
+    tester("select commission from emps order by commission nulls last "
+        + "offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?0], fetch=[?1])\n"
+            + "  EnumerableCalc(expr#0..4=[{inputs}], commission=[$t4])\n"
+            + "    EnumerableTableScan(table=[[s, emps]])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.valueOf(1));
+          p.setBigDecimal(2, BigDecimal.valueOf(2));
+        })
+        .returnsOrdered(
+            "commission=250",
+            "commission=250");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithBigDecimalAboveIntegerMax() {
+    tester("select commission from emps order by commission nulls last "
+        + "offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1,
+              BigDecimal.valueOf(Integer.MAX_VALUE).add(BigDecimal.ONE));
+          p.setBigDecimal(2, BigDecimal.ONE);
+        })
+        .returnsOrdered();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void offsetLiteralAboveIntegerMax() {
+    tester("select commission from emps order by commission nulls last "
+        + "offset 2147483648 fetch next 1 rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$4], dir0=[ASC], "
+            + "offset=[2147483648:BIGINT], fetch=[1])")
+        .returnsOrdered();
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void dynamicParametersWithBigDecimalAboveLongMax() {
+    tester("select empid from emps where empid <= 2 order by empid "
+        + "offset ? fetch next ? rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[?0], fetch=[?1])")
+        .consumesPreparedStatement(p -> {
+          p.setBigDecimal(1, BigDecimal.ZERO);
+          p.setBigDecimal(2,
+              BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE));
+        })
+        .returnsOrdered(
+            "empid=1",
+            "empid=2");
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void fetchLiteralAboveLongMax() {
+    tester("select empid from emps where empid <= 2 order by empid "
+        + "offset 0 fetch next 9223372036854775808 rows only")
+        .explainContains("EnumerableLimitSort(sort0=[$0], dir0=[ASC], "
+            + "offset=[0], fetch=[9223372036854775808:DECIMAL(19, 0)])")
+        .returnsOrdered(
+            "empid=1",
+            "empid=2");
+  }
+
   private CalciteAssert.AssertQuery tester(String sqlQuery) {
     return CalciteAssert.that()
         .with(CalciteConnectionProperty.LEX, Lex.JAVA)
@@ -169,4 +336,27 @@ private CalciteAssert.AssertQuery tester(String sqlQuery) {
           planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
         });
   }
+
+  private void failsWithNegativeParameter(String sql,
+      CalciteAssert.PreparedStatementConsumer consumer, String message)
+      throws Exception {
+    CalciteAssert.that()
+        .with(CalciteConnectionProperty.LEX, Lex.JAVA)
+        .with(CalciteConnectionProperty.FORCE_DECORRELATE, false)
+        .withSchema("s", new ReflectiveSchema(new HrSchemaBig()))
+        .doWithConnection(connection -> {
+          try (Hook.Closeable ignored =
+                   Hook.PLANNER.addThread((Consumer<RelOptPlanner>) planner -> 
{
+                     planner.removeRule(EnumerableRules.ENUMERABLE_SORT_RULE);
+                     
planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_SORT_RULE);
+                   });
+               PreparedStatement p = connection.prepareStatement(sql)) {
+            consumer.accept(p);
+            final SQLException e = assertThrows(SQLException.class, 
p::executeQuery);
+            assertThat(e.getMessage(), containsString(message));
+          } catch (SQLException e) {
+            throw TestUtil.rethrow(e);
+          }
+        });
+  }
 }
diff --git a/core/src/test/resources/sql/sort.iq 
b/core/src/test/resources/sql/sort.iq
index f9149a5482..0e8d84cff9 100644
--- a/core/src/test/resources/sql/sort.iq
+++ b/core/src/test/resources/sql/sort.iq
@@ -419,8 +419,13 @@ order by arr desc;
 
 # [CALCITE-7156] OFFSET and FETCH in EnumerableLimit need to support BIGINT
 select * from "hr"."emps" limit 3000000000 offset 2500000000;
-java.lang.ArithmeticException: Integer overflow: 2500000000 is out of range 
for INT
-!error
++-------+--------+------+--------+------------+
+| empid | deptno | name | salary | commission |
++-------+--------+------+--------+------------+
++-------+--------+------+--------+------------+
+(0 rows)
+
+!ok
 
 # [CALCITE-7367] NULLS FIRST throws ClassCastException when sorting arrays
 select * from
diff --git 
a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java
index 54dcc20f41..c02001b346 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/DefaultEnumerable.java
@@ -693,6 +693,10 @@ protected OrderedQueryable<T> asOrderedQueryable() {
     return EnumerableDefaults.skip(getThis(), count);
   }
 
+  @Override public Enumerable<T> skip(BigDecimal count) {
+    return EnumerableDefaults.skip(getThis(), count);
+  }
+
   @Override public Enumerable<T> skipWhile(Predicate1<T> predicate) {
     return EnumerableDefaults.skipWhile(getThis(), predicate);
   }
@@ -701,6 +705,10 @@ protected OrderedQueryable<T> asOrderedQueryable() {
     return EnumerableDefaults.skipWhile(getThis(), predicate);
   }
 
+  @Override public Enumerable<T> skipWhileBigDecimal(Predicate2<T, BigDecimal> 
predicate) {
+    return EnumerableDefaults.skipWhileBigDecimal(getThis(), predicate);
+  }
+
   @Override public BigDecimal sum(BigDecimalFunction1<T> selector) {
     return EnumerableDefaults.sum(getThis(), selector);
   }
@@ -745,6 +753,10 @@ protected OrderedQueryable<T> asOrderedQueryable() {
     return EnumerableDefaults.take(getThis(), count);
   }
 
+  @Override public Enumerable<T> take(BigDecimal count) {
+    return EnumerableDefaults.take(getThis(), count);
+  }
+
   @Override public Enumerable<T> takeWhile(Predicate1<T> predicate) {
     return EnumerableDefaults.takeWhile(getThis(), predicate);
   }
@@ -753,6 +765,10 @@ protected OrderedQueryable<T> asOrderedQueryable() {
     return EnumerableDefaults.takeWhile(getThis(), predicate);
   }
 
+  @Override public Enumerable<T> takeWhileBigDecimal(Predicate2<T, BigDecimal> 
predicate) {
+    return EnumerableDefaults.takeWhileBigDecimal(getThis(), predicate);
+  }
+
   @Override public <TKey extends Comparable<TKey>> OrderedEnumerable<T> thenBy(
       Function1<T, TKey> keySelector) {
     return EnumerableDefaults.thenBy(getThisOrdered(), keySelector);
diff --git 
a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
index ae26a60213..0ff4afddb6 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
@@ -48,6 +48,7 @@
 import org.checkerframework.framework.qual.HasQualifierParameter;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.AbstractList;
 import java.util.AbstractMap;
 import java.util.AbstractSet;
@@ -3244,26 +3245,43 @@ public static <TSource, TKey> Enumerable<TSource> 
orderBy(
       Function1<TSource, TKey> keySelector,
       Comparator<TKey> comparator,
       int offset, int fetch) {
+    return orderBy(source, keySelector, comparator,
+        BigDecimal.valueOf(offset), BigDecimal.valueOf(fetch));
+  }
+
+  /**
+   * A sort implementation optimized for a sort with a fetch size (LIMIT).
+   *
+   * @param offset how many rows are skipped from the sorted output.
+   *               Must be greater than or equal to 0.
+   * @param fetch how many rows are retrieved. Must be greater than or equal 
to 0.
+   */
+  public static <TSource, TKey> Enumerable<TSource> orderBy(
+      Enumerable<TSource> source,
+      Function1<TSource, TKey> keySelector,
+      Comparator<TKey> comparator,
+      BigDecimal offset, BigDecimal fetch) {
     // As discussed in CALCITE-3920 and CALCITE-4157, this method avoids to 
sort the complete input,
     // if only the first N rows are actually needed. A TreeMap implementation 
has been chosen,
     // so that it behaves similar to the orderBy method without fetch/offset.
     // The TreeMap has a better performance if there are few distinct sort 
keys.
     return new AbstractEnumerable<TSource>() {
       @Override public Enumerator<TSource> enumerator() {
-        if (fetch == 0) {
+        if (fetch.compareTo(BigDecimal.ZERO) <= 0) {
           return Linq4j.emptyEnumerator();
         }
 
         TreeMap<TKey, List<TSource>> map = new TreeMap<>(comparator);
-        long size = 0;
-        long needed = fetch + (long) offset;
+        BigDecimal size = BigDecimal.ZERO;
+        BigDecimal actualOffset = offset.max(BigDecimal.ZERO);
+        BigDecimal needed = 
rowsRequired(actualOffset).add(rowsRequired(fetch));
 
         // read the input into a tree map
         try (Enumerator<TSource> os = source.enumerator()) {
           while (os.moveNext()) {
             TSource o = os.current();
             TKey key = keySelector.apply(o);
-            if (needed >= 0 && size >= needed) {
+            if (needed.signum() >= 0 && size.compareTo(needed) >= 0) {
               // the current row will never appear in the output, so just skip 
it
               @KeyFor("map") TKey lastKey = map.lastKey();
               if (comparator.compare(key, lastKey) >= 0) {
@@ -3277,7 +3295,7 @@ public static <TSource, TKey> Enumerable<TSource> orderBy(
               } else {
                 l.remove(l.size() - 1);
               }
-              size--;
+              size = size.subtract(BigDecimal.ONE);
             }
             // add the current element to the map
             map.compute(key, (k, l) -> {
@@ -3292,24 +3310,30 @@ public static <TSource, TKey> Enumerable<TSource> 
orderBy(
               l.add(o);
               return l;
             });
-            size++;
+            size = size.add(BigDecimal.ONE);
           }
         }
 
         // skip the first 'offset' rows by deleting them from the map
-        if (offset > 0) {
-          // search the key up to (but excluding) which we have to remove 
entries from the map
-          int skipped = 0;
+        if (actualOffset.compareTo(BigDecimal.ZERO) > 0) {
+          // search the key up to which we have to remove entries from the map
+          BigDecimal skipped = BigDecimal.ZERO;
+          BigDecimal rowsToSkip = rowsRequired(actualOffset);
           TKey until = (TKey) DUMMY;
+          boolean removeUntilInclusive = false;
           for (Map.Entry<TKey, List<TSource>> e : map.entrySet()) {
-            skipped += e.getValue().size();
+            skipped = skipped.add(BigDecimal.valueOf(e.getValue().size()));
 
-            if (skipped > offset) {
+            if (skipped.compareTo(rowsToSkip) >= 0) {
               // we might need to remove entries from the list
               List<TSource> l = e.getValue();
-              int toKeep = skipped - offset;
-              if (toKeep < l.size()) {
-                l.subList(0, l.size() - toKeep).clear();
+              BigDecimal toKeep = skipped.subtract(rowsToSkip);
+              if (toKeep.compareTo(BigDecimal.valueOf(l.size())) < 0) {
+                if (toKeep.signum() == 0) {
+                  removeUntilInclusive = true;
+                } else {
+                  l.subList(0, l.size() - toKeep.intValueExact()).clear();
+                }
               }
 
               until = e.getKey();
@@ -3320,7 +3344,7 @@ public static <TSource, TKey> Enumerable<TSource> orderBy(
             // the offset is bigger than the number of rows in the map
             return Linq4j.emptyEnumerator();
           }
-          map.headMap(until, false).clear();
+          map.headMap(until, removeUntilInclusive).clear();
         }
 
         return new LookupImpl<>(map).valuesEnumerable().enumerator();
@@ -3790,6 +3814,10 @@ public static <TSource> TSource 
single(Enumerable<TSource> source,
     return toRet;
   }
 
+  private static BigDecimal rowsRequired(BigDecimal count) {
+    return count.max(BigDecimal.ZERO).setScale(0, RoundingMode.CEILING);
+  }
+
   /**
    * Bypasses a specified number of elements in a
    * sequence and then returns the remaining elements.
@@ -3802,6 +3830,18 @@ public static <TSource> Enumerable<TSource> 
skip(Enumerable<TSource> source,
     });
   }
 
+  /**
+   * Bypasses a specified number of elements in a
+   * sequence and then returns the remaining elements.
+   */
+  public static <TSource> Enumerable<TSource> skip(Enumerable<TSource> source,
+      final BigDecimal count) {
+    return skipWhileBigDecimal(source, (v1, v2) -> {
+      // Count is 1-based
+      return v2.compareTo(count) < 0;
+    });
+  }
+
   /**
    * Bypasses elements in a sequence as long as a
    * specified condition is true and then returns the remaining
@@ -3957,6 +3997,19 @@ public static <TSource> Enumerable<TSource> 
take(Enumerable<TSource> source,
         });
   }
 
+  /**
+   * Returns a specified number of contiguous elements
+   * from the start of a sequence.
+   */
+  public static <TSource> Enumerable<TSource> take(Enumerable<TSource> source,
+      final BigDecimal count) {
+    return takeWhileBigDecimal(
+        source, (v1, v2) -> {
+          // Count is 1-based
+          return v2.compareTo(count) < 0;
+        });
+  }
+
   /**
    * Returns elements from a sequence as long as a
    * specified condition is true.
@@ -3997,6 +4050,36 @@ public static <TSource> Enumerable<TSource> 
takeWhileLong(
     };
   }
 
+  /**
+   * Returns elements from a sequence as long as a
+   * specified condition is true. The element's index is used in the
+   * logic of the predicate function.
+   */
+  public static <TSource> Enumerable<TSource> takeWhileBigDecimal(
+      final Enumerable<TSource> source,
+      final Predicate2<TSource, BigDecimal> predicate) {
+    return new AbstractEnumerable<TSource>() {
+      @Override public Enumerator<TSource> enumerator() {
+        return new TakeWhileBigDecimalEnumerator<>(source.enumerator(), 
predicate);
+      }
+    };
+  }
+
+  /**
+   * Bypasses elements in a sequence as long as a
+   * specified condition is true. The element's index is used in the
+   * logic of the predicate function.
+   */
+  public static <TSource> Enumerable<TSource> skipWhileBigDecimal(
+      final Enumerable<TSource> source,
+      final Predicate2<TSource, BigDecimal> predicate) {
+    return new AbstractEnumerable<TSource>() {
+      @Override public Enumerator<TSource> enumerator() {
+        return new SkipWhileBigDecimalEnumerator<>(source.enumerator(), 
predicate);
+      }
+    };
+  }
+
   /**
    * Performs a subsequent ordering of the elements in a sequence according
    * to a key.
@@ -4577,6 +4660,50 @@ static class TakeWhileLongEnumerator<TSource> implements 
Enumerator<TSource> {
     }
   }
 
+  /** Enumerable that implements take-while.
+   *
+   * @param <TSource> element type */
+  static class TakeWhileBigDecimalEnumerator<TSource> implements 
Enumerator<TSource> {
+    private final Enumerator<TSource> enumerator;
+    private final Predicate2<TSource, BigDecimal> predicate;
+
+    boolean done = false;
+    BigDecimal n = BigDecimal.valueOf(-1);
+
+    TakeWhileBigDecimalEnumerator(Enumerator<TSource> enumerator,
+        Predicate2<TSource, BigDecimal> predicate) {
+      this.enumerator = enumerator;
+      this.predicate = predicate;
+    }
+
+    @Override public TSource current() {
+      return enumerator.current();
+    }
+
+    @Override public boolean moveNext() {
+      if (!done) {
+        if (enumerator.moveNext()) {
+          n = n.add(BigDecimal.ONE);
+          if (predicate.apply(enumerator.current(), n)) {
+            return true;
+          }
+        }
+        done = true;
+      }
+      return false;
+    }
+
+    @Override public void reset() {
+      enumerator.reset();
+      done = false;
+      n = BigDecimal.valueOf(-1);
+    }
+
+    @Override public void close() {
+      enumerator.close();
+    }
+  }
+
   /** Enumerator that implements skip-while.
    *
    * @param <TSource> element type */
@@ -4623,6 +4750,53 @@ static class SkipWhileEnumerator<TSource> implements 
Enumerator<TSource> {
     }
   }
 
+  /** Enumerator that implements skip-while.
+   *
+   * @param <TSource> element type */
+  static class SkipWhileBigDecimalEnumerator<TSource> implements 
Enumerator<TSource> {
+    private final Enumerator<TSource> enumerator;
+    private final Predicate2<TSource, BigDecimal> predicate;
+
+    boolean started = false;
+    BigDecimal n = BigDecimal.valueOf(-1);
+
+    SkipWhileBigDecimalEnumerator(Enumerator<TSource> enumerator,
+        Predicate2<TSource, BigDecimal> predicate) {
+      this.enumerator = enumerator;
+      this.predicate = predicate;
+    }
+
+    @Override public TSource current() {
+      return enumerator.current();
+    }
+
+    @Override public boolean moveNext() {
+      for (;;) {
+        if (!enumerator.moveNext()) {
+          return false;
+        }
+        if (started) {
+          return true;
+        }
+        n = n.add(BigDecimal.ONE);
+        if (!predicate.apply(enumerator.current(), n)) {
+          started = true;
+          return true;
+        }
+      }
+    }
+
+    @Override public void reset() {
+      enumerator.reset();
+      started = false;
+      n = BigDecimal.valueOf(-1);
+    }
+
+    @Override public void close() {
+      enumerator.close();
+    }
+  }
+
   /** Enumerator that casts each value.
    *
    * <p>If the source type {@code F} is not nullable, the target element type
diff --git 
a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java 
b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java
index ff28c3fe82..362372464a 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/ExtendedEnumerable.java
@@ -1095,6 +1095,12 @@ boolean sequenceEqual(Enumerable<TSource> enumerable1,
    */
   Enumerable<TSource> skip(int count);
 
+  /**
+   * Bypasses a specified number of elements in a
+   * sequence and then returns the remaining elements.
+   */
+  Enumerable<TSource> skip(BigDecimal count);
+
   /**
    * Bypasses elements in a sequence as long as a
    * specified condition is true and then returns the remaining
@@ -1110,6 +1116,14 @@ boolean sequenceEqual(Enumerable<TSource> enumerable1,
    */
   Enumerable<TSource> skipWhile(Predicate2<TSource, Integer> predicate);
 
+  /**
+   * Bypasses elements in a sequence as long as a
+   * specified condition is true and then returns the remaining
+   * elements. The element's index is used in the logic of the
+   * predicate function.
+   */
+  Enumerable<TSource> skipWhileBigDecimal(Predicate2<TSource, BigDecimal> 
predicate);
+
   /**
    * Computes the sum of the sequence of Decimal values
    * that are obtained by invoking a transform function on each
@@ -1186,6 +1200,12 @@ boolean sequenceEqual(Enumerable<TSource> enumerable1,
    */
   Enumerable<TSource> take(int count);
 
+  /**
+   * Returns a specified number of contiguous elements
+   * from the start of a sequence.
+   */
+  Enumerable<TSource> take(BigDecimal count);
+
   /**
    * Returns elements from a sequence as long as a
    * specified condition is true.
@@ -1199,6 +1219,13 @@ boolean sequenceEqual(Enumerable<TSource> enumerable1,
    */
   Enumerable<TSource> takeWhile(Predicate2<TSource, Integer> predicate);
 
+  /**
+   * Returns elements from a sequence as long as a
+   * specified condition is true. The element's index is used in the
+   * logic of the predicate function.
+   */
+  Enumerable<TSource> takeWhileBigDecimal(Predicate2<TSource, BigDecimal> 
predicate);
+
   /**
    * Creates a {@code Map<TKey, TValue>} from an
    * {@code Enumerable<TSource>} according to a specified key selector
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 9e2fa64a4c..df9e39e35b 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/Linq4j.java
@@ -21,6 +21,8 @@
 import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -591,6 +593,16 @@ static class ListEnumerable<T> extends 
CollectionEnumerable<T> {
       return new ListEnumerable<>(list.subList(count, list.size()));
     }
 
+    @Override public Enumerable<T> skip(BigDecimal count) {
+      final List<T> list = toList();
+      BigDecimal rows = count.max(BigDecimal.ZERO)
+          .setScale(0, RoundingMode.CEILING);
+      if (rows.compareTo(BigDecimal.valueOf(list.size())) >= 0) {
+        return Linq4j.emptyEnumerable();
+      }
+      return new ListEnumerable<>(list.subList(rows.intValueExact(), 
list.size()));
+    }
+
     @Override public Enumerable<T> take(int count) {
       final List<T> list = toList();
       if (count >= list.size()) {
@@ -599,6 +611,16 @@ static class ListEnumerable<T> extends 
CollectionEnumerable<T> {
       return new ListEnumerable<>(list.subList(0, count));
     }
 
+    @Override public Enumerable<T> take(BigDecimal count) {
+      final List<T> list = toList();
+      BigDecimal rows = count.max(BigDecimal.ZERO)
+          .setScale(0, RoundingMode.CEILING);
+      if (rows.compareTo(BigDecimal.valueOf(list.size())) >= 0) {
+        return this;
+      }
+      return new ListEnumerable<>(list.subList(0, rows.intValueExact()));
+    }
+
     @Override public T elementAt(int index) {
       return toList().get(index);
     }
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 bacb6da3c8..62e7dd9f5e 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
@@ -1264,6 +1264,28 @@ private <T> List<T> contentsOf(Enumerator<T> enumerator) 
{
             .toList(), hasSize(0));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testTakeEnumerableDefaultsBigDecimalSize() {
+    assertThat(
+        EnumerableDefaults.take(Linq4j.asEnumerable(depts),
+        new BigDecimal("1.5")).toList(), hasSize(2));
+    assertThat(
+        EnumerableDefaults.take(Linq4j.asEnumerable(depts),
+        BigDecimal.valueOf(-2)).toList(), hasSize(0));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testTakeListEnumerableBigDecimalSize() {
+    assertThat(Linq4j.asEnumerable(depts).take(new BigDecimal("1.5"))
+        .toList(), hasSize(2));
+    assertThat(Linq4j.asEnumerable(depts).take(BigDecimal.valueOf(-2))
+        .toList(), hasSize(0));
+  }
+
   @Test void testTakeQueryableZeroOrNegativeSize() {
     
assertThat(QueryableDefaults.take(Linq4j.asEnumerable(depts).asQueryable(), 0)
             .toList(), hasSize(0));
@@ -1424,6 +1446,28 @@ public boolean apply(Department v1, Integer v2) {
                 || v2 == 1)).count(), is(1));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testSkipEnumerableDefaultsBigDecimalSize() {
+    assertThat(
+        EnumerableDefaults.skip(Linq4j.asEnumerable(depts),
+        new BigDecimal("1.5")).count(), is(1));
+    assertThat(
+        EnumerableDefaults.skip(Linq4j.asEnumerable(depts),
+        BigDecimal.valueOf(-2)).count(), is(3));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7624";>[CALCITE-7624]
+   * Support BigDecimal for FETCH and OFFSET in Enumerable</a>. */
+  @Test void testSkipListEnumerableBigDecimalSize() {
+    assertThat(Linq4j.asEnumerable(depts).skip(new BigDecimal("1.5"))
+        .count(), is(1));
+    assertThat(Linq4j.asEnumerable(depts).skip(BigDecimal.valueOf(-2))
+        .count(), is(3));
+  }
+
   @Test void testOrderBy() {
     // Note: sort is stable. Records occur Fred, Eric, Janet in input.
     assertThat(Linq4j.asEnumerable(emps).orderBy(EMP_DEPTNO_SELECTOR)
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 90511aa406..8ff9857cb8 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -427,8 +427,9 @@ ## Grammar
 "SELECT x, y FROM t ORDER BY x, y"
 An optional trailing ASC / DESC and NULLS FIRST / NULLS LAST applies to all 
keys.
 
-In *query*, *count* and *start* may each be either an unsigned integer literal
-or a dynamic parameter whose value is an integer.
+In *query*, *count* and *start* may each be either an unsigned numeric literal
+or a dynamic parameter whose value is numeric.
+Support for decimal or non-integer values is adapter-dependent.
 
 An aggregate query is a query that contains a GROUP BY or a HAVING
 clause, or aggregate functions in the SELECT clause. In the SELECT,

Reply via email to