This is an automated email from the ASF dual-hosted git repository. Wei-hao-Li pushed a commit to branch fixLimit0 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit aca66c88e4d4db7fb904a4dfa6e521f44856b147 Author: Weihao Li <[email protected]> AuthorDate: Wed Oct 14 10:43:10 2026 +0800 fix Signed-off-by: Weihao Li <[email protected]> --- .../it/query/old/IoTDBSimpleQueryTableIT.java | 24 +++++++ .../iotdb/relational/it/schema/IoTDBDeviceIT.java | 13 ++-- .../iotdb/db/i18n/DataNodeQueryMessages.java | 5 +- .../iotdb/db/i18n/DataNodeQueryMessages.java | 5 +- .../relational/analyzer/StatementAnalyzer.java | 4 +- .../plan/relational/analyzer/AnalyzerTest.java | 9 +++ .../analyzer/EvaluateEmptyIntersectTest.java | 25 ++----- .../analyzer/RemoveEmptyExceptBranchesTest.java | 82 +++++----------------- .../relational/analyzer/UnionOptimizeTest.java | 28 +++----- 9 files changed, 86 insertions(+), 109 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java index f8cc92699e7..47e7655ff44 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/IoTDBSimpleQueryTableIT.java @@ -95,6 +95,30 @@ public class IoTDBSimpleQueryTableIT { } } + @Test + public void testLimitZeroRejected() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + statement.execute("CREATE DATABASE " + DATABASE_NAME); + statement.execute("USE " + DATABASE_NAME); + statement.execute("CREATE TABLE table1(device STRING TAG, s1 INT32 FIELD)"); + + assertLimitZeroRejected(statement, "SELECT * FROM table1 LIMIT 0"); + assertLimitZeroRejected(statement, "SELECT * FROM table1 ORDER BY time LIMIT 0"); + } + } + + private static void assertLimitZeroRejected(Statement statement, String sql) { + try { + statement.executeQuery(sql); + fail("Expected LIMIT 0 to be rejected"); + } catch (SQLException e) { + assertTrue( + e.getMessage(), + e.getMessage().contains("LIMIT row count must be greater than 0 (actual value: 0)")); + } + } + @Ignore // TODO After last query supported @Test public void testLastQueryNonCached() { diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceIT.java index 1eb2da9d100..75bf4646a50 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceIT.java @@ -38,6 +38,7 @@ import java.sql.Statement; import java.util.Collections; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(IoTDBTestRunner.class) @@ -106,10 +107,14 @@ public class IoTDBDeviceIT { "show devices from table0 where region_id < plant_id offset 0 limit 1"), "region_id,plant_id,device_id,model,", Collections.singleton("1,木兰,3,A,")); - TestUtils.assertResultSetEqual( - statement.executeQuery("show devices from table0 where region_id < plant_id limit 0"), - "region_id,plant_id,device_id,model,", - Collections.emptySet()); + try { + statement.executeQuery("show devices from table0 where region_id < plant_id limit 0"); + fail("Expected LIMIT 0 to be rejected"); + } catch (SQLException e) { + assertTrue( + e.getMessage(), + e.getMessage().contains("LIMIT row count must be greater than 0 (actual value: 0)")); + } TestUtils.assertResultSetEqual( statement.executeQuery("count devices from table0 where region_id < plant_id"), "count(devices),", diff --git a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index 73b27b06223..b030af64cfa 100644 --- a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -2261,8 +2261,9 @@ public final class DataNodeQueryMessages { "Type %s is not orderable, and therefore cannot be used in ORDER BY: %s"; public static final String OFFSET_ROW_COUNT_MUST_BE_GREATER_OR_EQUAL_TO_0_ACTUAL_VALUE_S = "OFFSET row count must be greater or equal to 0 (actual value: %s)"; - public static final String LIMIT_ROW_COUNT_MUST_BE_GREATER_OR_EQUAL_TO_0_ACTUAL_VALUE_S = - "LIMIT row count must be greater or equal to 0 (actual value: %s)"; + public static final String + EXCEPTION_LIMIT_ROW_COUNT_MUST_BE_GREATER_THAN_0_ACTUAL_VALUE_ARG_79F8F1A3 = + "LIMIT row count must be greater than 0 (actual value: %s)"; public static final String NON_CONSTANT_PARAMETER_VALUE_FOR_S_S = "Non constant parameter value for %s: %s"; public static final String PARAMETER_VALUE_PROVIDED_FOR_S_IS_NULL_S = diff --git a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java index ed1fefe479a..5a504e2a017 100644 --- a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java +++ b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodeQueryMessages.java @@ -2518,9 +2518,10 @@ public final class DataNodeQueryMessages { public static final String OFFSET_ROW_COUNT_MUST_BE_GREATER_OR_EQUAL_TO_0_ACTUAL_VALUE_S = "OFFSET 行数必须大于或等于 0(实际值:%s)"; - public static final String LIMIT_ROW_COUNT_MUST_BE_GREATER_OR_EQUAL_TO_0_ACTUAL_VALUE_S = + public static final String + EXCEPTION_LIMIT_ROW_COUNT_MUST_BE_GREATER_THAN_0_ACTUAL_VALUE_ARG_79F8F1A3 = - "LIMIT 行数必须大于或等于 0(实际值:%s)"; + "LIMIT 行数必须大于 0(实际值:%s)"; public static final String NON_CONSTANT_PARAMETER_VALUE_FOR_S_S = "%s 的参数值不是常量:%s"; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index 88f605f6dfd..e1c9db8d0f2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -5058,11 +5058,11 @@ public class StatementAnalyzer { } rowCount.ifPresent( count -> { - if (count < 0) { + if (count <= 0) { throw new SemanticException( String.format( DataNodeQueryMessages - .LIMIT_ROW_COUNT_MUST_BE_GREATER_OR_EQUAL_TO_0_ACTUAL_VALUE_S, + .EXCEPTION_LIMIT_ROW_COUNT_MUST_BE_GREATER_THAN_0_ACTUAL_VALUE_ARG_79F8F1A3, count)); } }); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java index 5cc6261b4a6..9c9918ddbc8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java @@ -182,6 +182,15 @@ public class AnalyzerTest { IoTDBDescriptor.getInstance().getConfig().setDataNodeId(1); } + @Test + public void testLimitZeroRejected() { + TestUtils.assertAnalyzeSemanticException( + "SELECT * FROM table1 LIMIT 0", "LIMIT row count must be greater than 0 (actual value: 0)"); + TestUtils.assertAnalyzeSemanticException( + "SELECT * FROM table1 ORDER BY time LIMIT 0", + "LIMIT row count must be greater than 0 (actual value: 0)"); + } + @Test public void testMockQuery() throws OperatorNotFoundException { final String sql = diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/EvaluateEmptyIntersectTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/EvaluateEmptyIntersectTest.java index a69b4af288c..3464ffcbc20 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/EvaluateEmptyIntersectTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/EvaluateEmptyIntersectTest.java @@ -19,30 +19,19 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer; -import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; - import org.junit.Test; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.limit; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.tableScan; +import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertAnalyzeSemanticException; public class EvaluateEmptyIntersectTest { @Test public void EvaluateEmptyIntersectTest() { - // Normal case - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) intersect (select tag1 from t2) intersect (select tag1 from t3)"), - output((limit(0, tableScan("testdb.t1"))))); - - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 ) intersect all (select tag1 from t2 limit 0) intersect all (select tag1 from t3)"), - output((limit(0, tableScan("testdb.t2"))))); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) intersect (select tag1 from t2) intersect (select tag1 from t3)", + "LIMIT row count must be greater than 0 (actual value: 0)"); + assertAnalyzeSemanticException( + "(select tag1 from t1) intersect all (select tag1 from t2 limit 0) intersect all (select tag1 from t3)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RemoveEmptyExceptBranchesTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RemoveEmptyExceptBranchesTest.java index 914a9bdae0b..9602aaeb29c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RemoveEmptyExceptBranchesTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/RemoveEmptyExceptBranchesTest.java @@ -19,83 +19,39 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer; -import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; - import org.junit.Test; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.aggregation; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.filter; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.limit; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.project; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.sort; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.tableScan; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.union; -import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.window; +import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertAnalyzeSemanticException; public class RemoveEmptyExceptBranchesTest { @Test public void removeEmptyExceptBranchesTest1() { - // Normal case - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) except (select tag1 from t2) except (select tag1 from t3)"), - output((limit(0, tableScan("testdb.t1"))))); - - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) except all (select tag1 from t2) except all (select tag1 from t3)"), - output((limit(0, tableScan("testdb.t1"))))); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) except (select tag1 from t2) except (select tag1 from t3)", + "LIMIT row count must be greater than 0 (actual value: 0)"); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) except all (select tag1 from t2) except all (select tag1 from t3)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } @Test public void removeEmptyExceptBranchesTest2() { - // Normal case - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 ) except (select tag1 from t2 limit 0) except (select tag1 from t3 limit 0)"), - output(aggregation(tableScan("testdb.t1")))); - - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1) except all (select tag1 from t2 limit 0) except all (select tag1 from t3 limit 0)"), - output((tableScan("testdb.t1")))); + assertAnalyzeSemanticException( + "(select tag1 from t1) except (select tag1 from t2 limit 0) except (select tag1 from t3 limit 0)", + "LIMIT row count must be greater than 0 (actual value: 0)"); + assertAnalyzeSemanticException( + "(select tag1 from t1) except all (select tag1 from t2 limit 0) except all (select tag1 from t3 limit 0)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } @Test public void removeEmptyExceptBranchesTest3() { - // Normal case - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1) except (select tag1 from t2) except (select tag1 from t3 limit 0)"), - output( - aggregation( - project( - filter( - aggregation( - union( - project(tableScan("testdb.t1")), - project(tableScan("testdb.t2"))))))))); - - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1) except all (select tag1 from t2 ) except all (select tag1 from t3 limit 0)"), - output( - project( - filter( - project( - window( - sort( - union( - project(tableScan("testdb.t1")), - project(tableScan("testdb.t2")))))))))); + assertAnalyzeSemanticException( + "(select tag1 from t1) except (select tag1 from t2) except (select tag1 from t3 limit 0)", + "LIMIT row count must be greater than 0 (actual value: 0)"); + assertAnalyzeSemanticException( + "(select tag1 from t1) except all (select tag1 from t2) except all (select tag1 from t3 limit 0)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/UnionOptimizeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/UnionOptimizeTest.java index 4fdbdb27b3d..f55b357051d 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/UnionOptimizeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/UnionOptimizeTest.java @@ -31,6 +31,7 @@ import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.QUERY_CONTEXT; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.SESSION_INFO; import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.TEST_MATADATA; +import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertAnalyzeSemanticException; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.limit; import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output; @@ -128,31 +129,22 @@ public class UnionOptimizeTest { @Test public void removeEmptyUnionBranchesTest1() { - // Normal case - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) union all (select tag1 from t2) union all (select tag1 from t3)"), - output((union(tableScan("testdb.t2"), tableScan("testdb.t3"))))); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) union all (select tag1 from t2) union all (select tag1 from t3)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } @Test public void removeEmptyUnionBranchesTest2() { - // One non-empty branch - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) union all (select tag1 from t2 limit 0) union all (select tag1 from t3 limit 1)"), - output(limit(1, tableScan("testdb.t3")))); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) union all (select tag1 from t2 limit 0) union all (select tag1 from t3 limit 1)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } @Test public void removeEmptyUnionBranchesTest3() { - // All branches are empty - assertPlan( - new PlanTester() - .createPlan( - "(select tag1 from t1 limit 0) union all (select tag1 from t2 limit 0) union all (select tag1 from t3 limit 0)"), - output(limit(0, tableScan("testdb.t1")))); + assertAnalyzeSemanticException( + "(select tag1 from t1 limit 0) union all (select tag1 from t2 limit 0) union all (select tag1 from t3 limit 0)", + "LIMIT row count must be greater than 0 (actual value: 0)"); } }
