This is an automated email from the ASF dual-hosted git repository. tkalkirill pushed a commit to branch ignite-28971 in repository https://gitbox.apache.org/repos/asf/ignite.git
commit ef7140019687d987205af07cb32198e5b6a9b201 Author: Kirill Tkalenko <[email protected]> AuthorDate: Wed Aug 12 13:31:53 2026 +0300 IGNITE-28971 Wip --- .../query/calcite/exec/LogicalRelImplementor.java | 6 +++- .../query/calcite/prepare/IgnitePlanner.java | 17 ++++++++++-- .../query/calcite/prepare/IgniteSqlValidator.java | 22 ++++++++++----- .../calcite/sql/IgniteSqlPaginationPolicy.java | 32 ++++++++++++++++++++++ .../DynamicParametersIntegrationTest.java | 9 +++--- .../integration/LimitOffsetIntegrationTest.java | 12 ++++---- .../OperatorsExtensionIntegrationTest.java | 20 ++++++++++++++ modules/calcite/src/test/sql/order/test_limit.test | 4 +++ 8 files changed, 101 insertions(+), 21 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 b5c1c06f1ce..71af0b907d2 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 @@ -125,6 +125,7 @@ import org.apache.ignite.internal.processors.query.calcite.rule.LogicalScanConve import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDescriptor; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteIndex; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable; +import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlPaginationPolicy; import org.apache.ignite.internal.processors.query.calcite.trait.Destination; import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution; import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils; @@ -1084,7 +1085,10 @@ 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); + RoundingMode roundingMode = pagPlc == null ? IgniteMath.NUMERIC_ROUNDING_MODE : pagPlc.roundingMode(); + + return IgniteMath.convertToLongExact(paramAsDecimal, roundingMode); } 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/IgnitePlanner.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java index 553ca3ba89a..2a73046f941 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 @@ -91,6 +91,7 @@ import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.metadata.IgniteMetadata; import org.apache.ignite.internal.processors.query.calcite.metadata.RelMetadataQueryEx; +import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlPaginationPolicy; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.processors.query.calcite.util.Commons; import org.apache.ignite.internal.util.typedef.F; @@ -365,7 +366,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)); @@ -426,7 +427,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(catalogReader); return validator; } @@ -798,4 +799,16 @@ 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) + ); + } } 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 22d9be1f083..57ceb927de0 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 @@ -75,6 +75,7 @@ import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDesc import org.apache.ignite.internal.processors.query.calcite.schema.IgniteCacheTable; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable; import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral; +import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlPaginationPolicy; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.processors.query.calcite.type.OtherType; import org.apache.ignite.internal.processors.query.calcite.util.IgniteMath; @@ -113,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); } @@ -306,7 +313,8 @@ public class IgniteSqlValidator extends SqlValidatorImpl { if (val.signum() < 0) throw new IllegalArgumentException("Negative value for " + nodeName); - IgniteMath.convertToLongExact(val, RoundingMode.DOWN); + RoundingMode roundingMode = pagPlc == null ? IgniteMath.NUMERIC_ROUNDING_MODE : pagPlc.roundingMode(); + IgniteMath.convertToLongExact(val, roundingMode); } 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/sql/IgniteSqlPaginationPolicy.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlPaginationPolicy.java new file mode 100644 index 00000000000..47eb4d15393 --- /dev/null +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/sql/IgniteSqlPaginationPolicy.java @@ -0,0 +1,32 @@ +/* + * 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.ignite.internal.processors.query.calcite.sql; + +import java.math.RoundingMode; +import org.apache.calcite.plan.Context; +import org.apache.calcite.tools.Frameworks; + +/** + * 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> + */ +@FunctionalInterface +public interface IgniteSqlPaginationPolicy { + /** Returns the rounding mode for FETCH, LIMIT and OFFSET values. */ + RoundingMode roundingMode(); +} 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 45f6db66f08..cea49a96c10 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 @@ -171,15 +171,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)) @@ -188,13 +188,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) @@ -208,7 +208,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 04eeb5e543e..74b15d6e476 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 @@ -134,18 +134,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(); } /** 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 4d4e402f1e5..0ad8cd90e5c 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; @@ -64,6 +65,7 @@ import org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.Accumula 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.IgniteSqlValidator; +import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlPaginationPolicy; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.plugin.AbstractTestPluginProvider; import org.apache.ignite.plugin.PluginContext; @@ -93,6 +95,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(); @@ -178,6 +181,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/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
