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

tkalkirill 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 798eb3e064b IGNITE-28971 SQL Calcite: Introduce value resolver for 
FETCH, OFFSET, and LIMIT (#13467)
798eb3e064b is described below

commit 798eb3e064be0b980df596fd27f8c41cfef541cc
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Thu Aug 20 22:52:42 2026 +0300

    IGNITE-28971 SQL Calcite: Introduce value resolver for FETCH, OFFSET, and 
LIMIT (#13467)
---
 .../query/calcite/exec/LogicalRelImplementor.java  |  5 ++--
 .../calcite/prepare/AbstractQueryContext.java      |  3 ++-
 .../query/calcite/prepare/IgnitePlanner.java       | 21 +++++++++++++--
 ...Context.java => IgniteSqlPaginationPolicy.java} | 30 ++++++++++------------
 .../query/calcite/prepare/IgniteSqlValidator.java  | 21 +++++++++------
 .../query/calcite/prepare/PlanningContext.java     |  2 +-
 .../query/calcite/rule/SortConverterRule.java      | 14 +++++++++-
 .../DynamicParametersIntegrationTest.java          |  9 +++----
 .../integration/LimitOffsetIntegrationTest.java    | 14 +++++-----
 .../OperatorsExtensionIntegrationTest.java         | 20 +++++++++++++++
 .../query/calcite/planner/AbstractPlannerTest.java | 21 ++++++++++++---
 .../calcite/planner/LimitOffsetPlannerTest.java    | 16 +++++++++++-
 modules/calcite/src/test/sql/order/test_limit.test |  4 +++
 13 files changed, 132 insertions(+), 48 deletions(-)

diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
index a828c607417..5b7d70934ab 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.query.calcite.exec;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -86,6 +85,7 @@ import 
org.apache.ignite.internal.processors.query.calcite.exec.rel.UnionAllNode
 import org.apache.ignite.internal.processors.query.calcite.exec.rel.WindowNode;
 import 
org.apache.ignite.internal.processors.query.calcite.metadata.AffinityService;
 import 
org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
+import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlPaginationPolicy;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.bounds.SearchBounds;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteCollect;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin;
@@ -1094,7 +1094,8 @@ public class LogicalRelImplementor<Row> implements 
IgniteRelVisitor<Node<Row>> {
             if (paramAsDecimal.signum() < 0)
                 throw new IllegalArgumentException("Negative value for " + op);
 
-            return IgniteMath.convertToLongExact(paramAsDecimal, 
RoundingMode.DOWN);
+            IgniteSqlPaginationPolicy pagPlc = 
ctx.unwrap(IgniteSqlPaginationPolicy.class);
+            return 
IgniteSqlPaginationPolicy.convertToLongExact(paramAsDecimal, pagPlc);
         }
         catch (RuntimeException ex) {
             throw new 
IgniteSQLException(IgniteResource.INSTANCE.illegalFetchLimit(op).str(),
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
index 7fd4d73efc7..b892136f5b7 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
 import org.apache.calcite.plan.Context;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Abstract query context.
@@ -32,7 +33,7 @@ public class AbstractQueryContext implements Context {
     }
 
     /** {@inheritDoc} */
-    @Override public <C> C unwrap(Class<C> aCls) {
+    @Override public <C> @Nullable C unwrap(Class<C> aCls) {
         if (aCls == getClass())
             return aCls.cast(this);
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
index 92e9764839e..2ed2fe2ce42 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java
@@ -365,7 +365,7 @@ public class IgnitePlanner implements Planner, 
RelOptTable.ViewExpander {
         }
 
         CalciteCatalogReader catalogReader = 
this.catalogReader.withSchemaPath(schemaPath);
-        SqlValidator validator = new IgniteSqlValidator(operatorTbl, 
catalogReader, typeFactory, validatorCfg, ctx.parameters());
+        SqlValidator validator = createSqlValidator(catalogReader);
         SqlToRelConverter sqlToRelConverter = sqlToRelConverter(validator, 
catalogReader, sqlToRelConverterCfg);
         RelRoot root = sqlToRelConverter.convertQuery(sqlNode, true, false);
         root = root.withRel(sqlToRelConverter.decorrelate(sqlNode, root.rel));
@@ -435,7 +435,7 @@ public class IgnitePlanner implements Planner, 
RelOptTable.ViewExpander {
     /** */
     private SqlValidator validator() {
         if (validator == null)
-            validator = new IgniteSqlValidator(operatorTbl, catalogReader, 
typeFactory, validatorCfg, ctx.parameters());
+            validator = createSqlValidator();
 
         return validator;
     }
@@ -807,4 +807,21 @@ public class IgnitePlanner implements Planner, 
RelOptTable.ViewExpander {
             super.checkCancel();
         }
     }
+
+    /** */
+    private SqlValidator createSqlValidator(CalciteCatalogReader 
catalogReader) {
+        return new IgniteSqlValidator(
+            operatorTbl,
+            catalogReader,
+            typeFactory,
+            validatorCfg,
+            ctx.parameters(),
+            ctx.unwrap(IgniteSqlPaginationPolicy.class)
+        );
+    }
+
+    /** */
+    private SqlValidator createSqlValidator() {
+        return createSqlValidator(catalogReader);
+    }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlPaginationPolicy.java
similarity index 51%
copy from 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
copy to 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlPaginationPolicy.java
index 7fd4d73efc7..e7f81e9456e 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/AbstractQueryContext.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlPaginationPolicy.java
@@ -14,28 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
+import java.math.RoundingMode;
 import org.apache.calcite.plan.Context;
+import org.apache.calcite.tools.Frameworks;
+import org.apache.ignite.internal.processors.query.calcite.util.IgniteMath;
+import org.jetbrains.annotations.Nullable;
 
 /**
- * Abstract query context.
+ * Defines a policy for processing values of SQL pagination clauses: LIMIT, 
FETCH, and OFFSET.
+ *
+ * <p>Custom instance can be supplied through {@link 
Frameworks.ConfigBuilder#context(Context)}.</p>
  */
-public class AbstractQueryContext implements Context {
-    /** */
-    private final Context parentCtx;
-
-    /** */
-    public AbstractQueryContext(Context parentCtx) {
-        this.parentCtx = parentCtx;
-    }
-
-    /** {@inheritDoc} */
-    @Override public <C> C unwrap(Class<C> aCls) {
-        if (aCls == getClass())
-            return aCls.cast(this);
+@FunctionalInterface
+public interface IgniteSqlPaginationPolicy {
+    /** Returns the rounding mode for FETCH, LIMIT and OFFSET values. */
+    RoundingMode roundingMode();
 
-        return parentCtx.unwrap(aCls);
+    /** Rounds the given value according to the specified policy and converts 
it to {@code long}. */
+    static long convertToLongExact(Number value, @Nullable 
IgniteSqlPaginationPolicy policy) {
+        return policy == null ? IgniteMath.convertToLongExact(value) : 
IgniteMath.convertToLongExact(value, policy.roundingMode());
     }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
index ac03d696d1f..b3c643b7b9b 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -115,25 +114,31 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
     /** */
     private final RelDataType nullType;
 
+    /** */
+    private final @Nullable IgniteSqlPaginationPolicy pagPlc;
+
     /**
      * Creates a validator.
      *
-     * @param opTab         Operator table
-     * @param catalogReader Catalog reader
-     * @param typeFactory   Type factory
-     * @param cfg           Config
-     * @param parameters    Dynamic parameters
+     * @param opTab Operator table.
+     * @param catalogReader Catalog reader.
+     * @param typeFactory Type factory.
+     * @param cfg Config.
+     * @param parameters Dynamic parameters.
+     * @param pagPlc Pagination policy.
      */
     public IgniteSqlValidator(
         SqlOperatorTable opTab,
         CalciteCatalogReader catalogReader,
         IgniteTypeFactory typeFactory,
         SqlValidator.Config cfg,
-        @Nullable Object[] parameters
+        @Nullable Object[] parameters,
+        @Nullable IgniteSqlPaginationPolicy pagPlc
     ) {
         super(opTab, catalogReader, typeFactory, cfg);
 
         this.parameters = parameters;
+        this.pagPlc = pagPlc;
 
         nullType = typeFactory.createSqlType(SqlTypeName.NULL);
     }
@@ -385,7 +390,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
             if (val.signum() < 0)
                 throw new IllegalArgumentException("Negative value for " + 
nodeName);
 
-            IgniteMath.convertToLongExact(val, RoundingMode.DOWN);
+            IgniteSqlPaginationPolicy.convertToLongExact(val, pagPlc);
         }
         catch (RuntimeException e) {
             throw newValidationError(n, 
IgniteResource.INSTANCE.illegalFetchLimit(nodeName));
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
index 4b401dabfad..b522f1a0db3 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
@@ -161,7 +161,7 @@ public final class PlanningContext implements Context {
     }
 
     /** {@inheritDoc} */
-    @Override public <C> C unwrap(Class<C> aCls) {
+    @Override public <C> @Nullable C unwrap(Class<C> aCls) {
         if (aCls == getClass())
             return aCls.cast(this);
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/SortConverterRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/SortConverterRule.java
index 6025930981e..9cd4c23cc3f 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/SortConverterRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/SortConverterRule.java
@@ -35,6 +35,7 @@ import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
+import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlPaginationPolicy;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteLimit;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSort;
@@ -139,8 +140,19 @@ public class SortConverterRule extends 
RelRule<SortConverterRule.Config> {
 
         BigDecimal fetchVal = 
((RexLiteral)reducedFetch.get(0)).getValueAs(BigDecimal.class);
 
+        if (fetchVal == null)
+            return false;
+
         // SortNode does not accept zero FETCH; the outer IgniteLimit handles 
it.
-        return fetchVal != null && fetchVal.compareTo(BigDecimal.ONE) >= 0;
+        IgniteSqlPaginationPolicy pagPlc = 
sort.getCluster().getPlanner().getContext().unwrap(IgniteSqlPaginationPolicy.class);
+
+        try {
+            return IgniteSqlPaginationPolicy.convertToLongExact(fetchVal, 
pagPlc) > 0;
+        }
+        catch (ArithmeticException ignored) {
+            // The outer IgniteLimit will report invalid FETCH during 
execution.
+            return false;
+        }
     }
 
     /** Returns {@code true} if the expression contains a dynamic parameter. */
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
index 6e5d0a4b845..8a130f5a2e1 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
@@ -193,15 +193,15 @@ public class DynamicParametersIntegrationTest extends 
AbstractBasicIntegrationTe
     public void testFractionalLimitOffset() {
         createAndPopulateTable();
 
-        assertQuery("SELECT id FROM person ORDER BY id LIMIT 
?").withParams(0.5).resultSize(0).check();
+        assertQuery("SELECT id FROM person ORDER BY id LIMIT 
?").withParams(0.5).returns(0).check();
         assertQuery("SELECT id FROM person ORDER BY id LIMIT 
?").withParams(1.4).returns(0).check();
-        assertQuery("SELECT id FROM person ORDER BY id LIMIT 
?").withParams(1.6).returns(0).check();
+        assertQuery("SELECT id FROM person ORDER BY id LIMIT 
?").withParams(1.6).returns(0).returns(1).check();
         assertThrowsSqlException("SELECT id FROM person ORDER BY id LIMIT ?", 
null, BigDecimal.valueOf(-1.5));
         assertThrowsSqlException("SELECT id FROM person ORDER BY id LIMIT ?", 
null, BigDecimal.valueOf(-0.5));
 
         assertQuery("SELECT id FROM person ORDER BY id FETCH FIRST ? ROWS 
ONLY")
             .withParams(BigDecimal.valueOf(0.5))
-            .resultSize(0)
+            .returns(0)
             .check();
         assertQuery("SELECT id FROM person ORDER BY id FETCH FIRST ? ROWS 
ONLY")
             .withParams(BigDecimal.valueOf(1.3))
@@ -210,13 +210,13 @@ public class DynamicParametersIntegrationTest extends 
AbstractBasicIntegrationTe
         assertQuery("SELECT id FROM person ORDER BY id FETCH FIRST ? ROWS 
ONLY")
             .withParams(BigDecimal.valueOf(1.6))
             .returns(0)
+            .returns(1)
             .check();
         assertThrowsSqlException("SELECT id FROM person ORDER BY id FETCH 
FIRST ? ROWS ONLY", null, BigDecimal.valueOf(-1.5));
         assertThrowsSqlException("SELECT id FROM person ORDER BY id FETCH 
FIRST ? ROWS ONLY", null, BigDecimal.valueOf(-0.5));
 
         assertQuery("SELECT id FROM person ORDER BY id OFFSET ? ROWS")
             .withParams(BigDecimal.valueOf(0.5))
-            .returns(0)
             .returns(1)
             .returns(2)
             .returns(3)
@@ -230,7 +230,6 @@ public class DynamicParametersIntegrationTest extends 
AbstractBasicIntegrationTe
             .check();
         assertQuery("SELECT id FROM person ORDER BY id OFFSET ? ROWS")
             .withParams(BigDecimal.valueOf(2.6))
-            .returns(2)
             .returns(3)
             .returns(4)
             .check();
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/LimitOffsetIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/LimitOffsetIntegrationTest.java
index 72053f73429..ac0f38e46cc 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/LimitOffsetIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/LimitOffsetIntegrationTest.java
@@ -149,18 +149,18 @@ public class LimitOffsetIntegrationTest extends 
AbstractBasicIntegrationTransact
     public void testFractionalLimitOffset() throws Exception {
         fillCache(cacheRepl, 4);
 
-        assertQuery("SELECT id FROM TEST_REPL ORDER BY id LIMIT 0.5").check();
+        assertQuery("SELECT id FROM TEST_REPL ORDER BY id LIMIT 
0.5").returns(0).check();
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id LIMIT 
1.2").returns(0).check();
-        assertQuery("SELECT id FROM TEST_REPL ORDER BY id LIMIT 
1.5").returns(0).check();
+        assertQuery("SELECT id FROM TEST_REPL ORDER BY id LIMIT 
1.5").returns(0).returns(1).check();
 
-        assertQuery("SELECT id FROM TEST_REPL ORDER BY id FETCH FIRST 0.5 ROWS 
ONLY").check();
+        assertQuery("SELECT id FROM TEST_REPL ORDER BY id FETCH FIRST 0.5 ROWS 
ONLY").returns(0).check();
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id FETCH FIRST 1.3 ROWS 
ONLY").returns(0).check();
-        assertQuery("SELECT id FROM TEST_REPL ORDER BY id FETCH FIRST 1.6 ROWS 
ONLY").returns(0).check();
+        assertQuery("SELECT id FROM TEST_REPL ORDER BY id FETCH FIRST 1.6 ROWS 
ONLY").returns(0).returns(1).check();
 
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 0.5 ROWS")
-            .returns(0).returns(1).returns(2).returns(3).check();
+            .returns(1).returns(2).returns(3).check();
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 2.3 
ROWS").returns(2).returns(3).check();
-        assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 2.6 
ROWS").returns(2).returns(3).check();
+        assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 2.6 
ROWS").returns(3).check();
     }
 
     /**
@@ -352,7 +352,7 @@ public class LimitOffsetIntegrationTest extends 
AbstractBasicIntegrationTransact
 
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 1 ROWS "
                 + "FETCH FIRST (ABS(0.5)) ROWS ONLY")
-            .resultSize(0)
+            .returns(1)
             .check();
 
         assertQuery("SELECT id FROM TEST_REPL ORDER BY id OFFSET 1 ROWS "
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java
index 68ada8d64ce..8825c2b4eb3 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java
@@ -17,6 +17,7 @@
 package org.apache.ignite.internal.processors.query.calcite.integration;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.util.HashSet;
 import java.util.List;
@@ -66,6 +67,7 @@ import 
org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.Accumula
 import 
org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.Accumulators;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteConvertletTable;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlNodeRewriter;
+import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlPaginationPolicy;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlValidator;
 import 
org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import org.apache.ignite.plugin.AbstractTestPluginProvider;
@@ -96,6 +98,7 @@ public class OperatorsExtensionIntegrationTest extends 
AbstractBasicIntegrationT
                                     .withSqlNodeRewriter(new SqlRewriter()))
                             .context(Contexts.chain(
                                 
CalciteQueryProcessor.FRAMEWORK_CONFIG.getContext(),
+                                Contexts.of((IgniteSqlPaginationPolicy)() -> 
RoundingMode.DOWN),
                                 Contexts.of(new 
AccumulatorFactoryProviderImpl())))
                             .build();
 
@@ -216,6 +219,23 @@ public class OperatorsExtensionIntegrationTest extends 
AbstractBasicIntegrationT
             .check();
     }
 
+    /** */
+    @Test
+    public void testPaginationRoundingPolicy() {
+        assertQuery("SELECT x FROM (VALUES (0), (1), (2)) t(x) ORDER BY x 
LIMIT 1.9")
+            .returns(0)
+            .check();
+
+        assertQuery("SELECT x FROM (VALUES (0), (1), (2)) t(x) ORDER BY x 
FETCH FIRST 1.9 ROWS ONLY")
+            .returns(0)
+            .check();
+
+        assertQuery("SELECT x FROM (VALUES (0), (1), (2)) t(x) ORDER BY x 
OFFSET 1.9 ROWS")
+            .returns(1)
+            .returns(2)
+            .check();
+    }
+
     /** Rewrites LTRIM with 2 parameters. */
     public static SqlCall rewriteLtrim(SqlValidator validator, SqlCall call) {
         if (call.operandCount() != 2)
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
index 3da09de1218..6c29252f6bd 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
@@ -28,6 +28,7 @@ import java.util.UUID;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import com.google.common.collect.ImmutableSet;
+import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.Contexts;
 import org.apache.calcite.plan.RelOptListener;
 import org.apache.calcite.plan.RelOptUtil;
@@ -220,7 +221,7 @@ public abstract class AbstractPlannerTest extends 
GridCommonAbstractTest {
         @Nullable RelOptListener planLsnr,
         String... disabledRules
     ) {
-        return plannerCtx(sql, Collections.singleton(publicSchema), planLsnr, 
null, ImmutableSet.copyOf(disabledRules));
+        return plannerCtx(sql, Collections.singleton(publicSchema), planLsnr, 
null, ImmutableSet.copyOf(disabledRules), null);
     }
 
     /** */
@@ -229,10 +230,13 @@ public abstract class AbstractPlannerTest extends 
GridCommonAbstractTest {
         Collection<IgniteSchema> schemas,
         @Nullable RelOptListener planLsnr,
         Collection<Object> params,
-        Collection<String> disabledRules
+        Collection<String> disabledRules,
+        @Nullable Context additionalCtx
     ) {
+        Context parentCtx = Contexts.of(baseQueryContext(schemas), planLsnr);
+
         PlanningContext.Builder ctxBuilder = PlanningContext.builder()
-            .parentContext(Contexts.of(baseQueryContext(schemas), planLsnr))
+            .parentContext(additionalCtx == null ? parentCtx : 
Contexts.chain(parentCtx, additionalCtx))
             .query(sql);
 
         if (params != null)
@@ -842,6 +846,9 @@ public abstract class AbstractPlannerTest extends 
GridCommonAbstractTest {
         /** */
         @Nullable private RelOptListener planListener;
 
+        /** */
+        @Nullable private Context additionalCtx;
+
         /** */
         public TestPlanningContextBuilder query(String qry) {
             query = qry;
@@ -884,9 +891,15 @@ public abstract class AbstractPlannerTest extends 
GridCommonAbstractTest {
             return this;
         }
 
+        /** */
+        public TestPlanningContextBuilder additionalCtx(Context additionalCtx) 
{
+            this.additionalCtx = additionalCtx;
+            return this;
+        }
+
         /** */
         PlanningContext build() {
-            return plannerCtx(query, schemas, planListener, params, 
disabledRules);
+            return plannerCtx(query, schemas, planListener, params, 
disabledRules, additionalCtx);
         }
     }
 
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/LimitOffsetPlannerTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/LimitOffsetPlannerTest.java
index 65514db6ea4..dc876471513 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/LimitOffsetPlannerTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/LimitOffsetPlannerTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.processors.query.calcite.planner;
 
+import java.math.RoundingMode;
+import org.apache.calcite.plan.Contexts;
 import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.type.RelDataType;
@@ -25,6 +27,7 @@ import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.util.ImmutableIntList;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePlanner;
+import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlPaginationPolicy;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteIndexScan;
@@ -88,7 +91,18 @@ public class LimitOffsetPlannerTest extends 
AbstractPlannerTest {
                         .and(sort -> sort.fetch != null))))));
 
         assertPlan("SELECT * FROM TEST ORDER BY ID OFFSET 1 ROWS "
-                + "FETCH FIRST (ABS(0.5)) ROWS ONLY", publicSchema,
+                + "FETCH FIRST (ABS(0.6)) ROWS ONLY", publicSchema,
+            isInstanceOf(IgniteLimit.class)
+                .and(limit -> limit.offset() != null && limit.fetch() != null)
+                .and(input(isInstanceOf(IgniteExchange.class)
+                    .and(input(isInstanceOf(IgniteSort.class)
+                        .and(sort -> sort.offset != null && sort.fetch != 
null))))));
+
+        assertPlan(contextBuilder()
+                .query("SELECT * FROM TEST ORDER BY ID OFFSET 1 ROWS "
+                    + "FETCH FIRST (ABS(0.6)) ROWS ONLY")
+                .schema(publicSchema)
+                .additionalCtx(Contexts.of((IgniteSqlPaginationPolicy)() -> 
RoundingMode.DOWN)),
             isInstanceOf(IgniteLimit.class)
                 .and(limit -> limit.offset() != null && limit.fetch() != null)
                 .and(input(isInstanceOf(IgniteExchange.class)
diff --git a/modules/calcite/src/test/sql/order/test_limit.test 
b/modules/calcite/src/test/sql/order/test_limit.test
index 00ce8f9822d..f7fa98caf70 100644
--- a/modules/calcite/src/test/sql/order/test_limit.test
+++ b/modules/calcite/src/test/sql/order/test_limit.test
@@ -28,12 +28,14 @@ query I
 SELECT a FROM test ORDER BY a LIMIT 1.5
 ----
 11
+12
 
 # decimal limit
 query I
 SELECT a FROM test ORDER BY a LIMIT 1.6
 ----
 11
+12
 
 # decimal limit
 query I
@@ -57,12 +59,14 @@ query I
 SELECT a FROM test ORDER BY a FETCH FIRST 1.5 ROWS ONLY
 ----
 11
+12
 
 # decimal limit
 query I
 SELECT a FROM test ORDER BY a FETCH FIRST 1.6 ROWS ONLY
 ----
 11
+12
 
 # decimal offset/limit
 query I

Reply via email to