IGNITE-28844 Calcite. Improve type checking in LIMIT / OFFSET clauses - #13311
Conversation
There was a problem hiding this comment.
Pull request overview
This PR (“IGNITE-28844 Calcite. Improve type checking in LIMIT / OFFSET clauses”) tightens validation for LIMIT / OFFSET values (including dynamic parameters) and aligns execution nodes to accept long-based fetch/offset values, with accompanying tests.
Changes:
- Added stricter validator checks for
LIMIT/OFFSETliterals and dynamic parameters (numeric-only, long-range, non-negative). - Introduced a planner-test helper (
StatementChecker) and added new planner/integration tests for dynamic parameters inLIMIT/OFFSET. - Updated execution nodes (
LimitNode,SortNode) and implementor wiring to uselongfetch/offset values and centralize “illegal fetch/offset” error reporting.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/calcite/src/test/sql/order/test_limit.test | Adds negative SQL cases for invalid FETCH FIRST / LIMIT values and expressions. |
| modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java | Registers the new dynamic-parameters planner test in the suite. |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/StatementChecker.java | Adds a new test helper for validating statements/plans (currently has critical correctness gaps). |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/DynamicParametersPlannerTest.java | Adds planner-level tests for dynamic parameter typing/range checks in LIMIT/OFFSET. |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java | Adds checkStatement(...) helper and PlanChecker implementation to drive planner validation in tests. |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java | Adds integration coverage for LIMIT ? with integer and BigDecimal parameters. |
| modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/LimitExecutionTest.java | Updates execution tests to new LimitNode/SortNode APIs (currently inconsistent for “unlimited” fetch). |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java | Adds new validator error messages for illegal fetch/offset and dynamic parameter type mismatch. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java | Implements the new fetch/offset validation logic for literals and dynamic params. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/SortNode.java | Switches limited-sort parameter types to long and adjusts bounded/unbounded buffer selection. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/LimitNode.java | Switches limit/offset parameter types to long and updates request/push logic for new semantics. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/AbstractNode.java | Introduces a shared NOT_WAITING sentinel constant. |
| modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementor.java | Wires runtime fetch/offset evaluation and validation into LimitNode / SortNode construction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
79554b4 to
6201e62
Compare
|
Hi, regarding https://issues.apache.org/jira/browse/CALCITE-7624, I implemented support for BigDecimal for the OFFSET/FETCH and LIMIT in #13375. |
| private final long fetch; | ||
|
|
||
| /** Fetch can be unset. */ | ||
| private final boolean fetchUndefined; |
There was a problem hiding this comment.
Let's add long limit = fetch == -1 ? Long.MAX_VALUE : IgniteMath.addExact(fetch, offset) instead. To execute IgniteMath.addExact(fetch, offset) only once (and not for each request and each push), to simplify hasMoreData (rowsProcessed < limit).
| private final boolean fetchUndefined; | ||
|
|
||
| /** Already processed (pushed to upstream) rows count. */ | ||
| private int rowsProcessed; |
There was a problem hiding this comment.
Comparing int with long, can overflow
| if (fetchNode == null || (fetchNode != null && rowsProcessed <= fetch + offset)) | ||
| downstream().push(row); | ||
| if (rowsProcessed >= offset && hasMoreData()) { | ||
| // this two rows can`t be swapped, cause if all requested rows have been pushed it will trigger further request call. |
There was a problem hiding this comment.
Comment should be started with an uppercase letter.
| */ | ||
| private void checkIntegerLimit(SqlNode n, String nodeName) { | ||
| private void invalidateFetchOffset(@Nullable SqlNode n, String nodeName) { | ||
| if (n == null) { |
| } | ||
|
|
||
| /** */ | ||
| protected <T extends RelNode> void assertPlan( |
There was a problem hiding this comment.
Redundant method. Used in assertThrows, but it's better to write own assertThrows method with parameters like PlannerContext or PlannerContextBuilder.
|
|
||
| /** */ | ||
| @SuppressWarnings("ThrowableNotThrown") | ||
| static void assertThrows( |
There was a problem hiding this comment.
Let's rewrite to assertThrows(PlannerContext, cls, msg) or assertThrows(PlannerContextBuilder, cls, msg) and use physicalPlan(...) method inside (not assertPlan)
| TestPlanningContextBuilder ctxBuilder, | ||
| Predicate<T> predicate | ||
| ) throws Exception { | ||
| IgniteRel plan = physicalPlan(plannerCtx(ctxBuilder.query, ctxBuilder.schemas, ctxBuilder.planListener, |
There was a problem hiding this comment.
Move plannerCtx(...) to TestPlanningContextBuilder.build() method.
| import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions; | ||
| import org.apache.ignite.internal.util.typedef.F; | ||
| import org.apache.ignite.internal.util.typedef.internal.CU; | ||
| import org.apache.ignite.testframework.GridTestUtils; |
There was a problem hiding this comment.
otherwize it resolves as : org.apache.ignite.internal.processors.query.calcite.planner.AbstractPlannerTest#assertThrows
| limit = fetch == -1 ? -1 : (fetch > Long.MAX_VALUE - offset ? -1 : fetch + offset); | ||
|
|
||
| if (limit < 0) | ||
| if (limit < 1 || limit > Integer.MAX_VALUE) |
There was a problem hiding this comment.
Can we rich this line with limit == 0? If yes, it's more correct to use `limit <= 1' or add other logic to return empty result
There was a problem hiding this comment.
no we can`t add assert
| waiting = -1; | ||
| waiting = NOT_WAITING; | ||
|
|
||
| downstream().end(); |
There was a problem hiding this comment.
requested field check required here. And requested = 0 before downstream().end(). See related problem https://issues.apache.org/jira/browse/IGNITE-28925
TCBot Test Analysis
Possible Blockers (0)No blockers found. New Tests (1)
|
| idxBndRel.first() ? cmp : cmp.reversed(), | ||
| null, | ||
| () -> 1 | ||
| 0, |
There was a problem hiding this comment.
| 0, | |
| SortNode.OFFSET_DEFAULT, |
https://issues.apache.org/jira/browse/IGNITE-28844