This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 187e4547c0c Fix table delete with renamed time column (#17841)
187e4547c0c is described below
commit 187e4547c0ccd9b8ff88e4b1ae243ba4846a3d7d
Author: Jiang Tian <[email protected]>
AuthorDate: Thu Jun 4 10:33:43 2026 +0800
Fix table delete with renamed time column (#17841)
* reviewed AnalyzeUtils
* Fix delete with non-default time column
---
.../relational/it/db/it/IoTDBDeletionTableIT.java | 21 +++++++++
.../iotdb/db/i18n/DataNodeQueryMessages.java | 2 +
.../iotdb/db/i18n/DataNodeQueryMessages.java | 2 +
.../db/queryengine/plan/analyze/AnalyzeUtils.java | 14 +++++-
.../plan/relational/planner/PredicateUtils.java | 28 +++++++----
.../queryengine/plan/analyze/AnalyzeUtilsTest.java | 55 ++++++++++++++++++++++
.../relational/planner/PredicateUtilsTest.java | 13 +++++
7 files changed, 126 insertions(+), 9 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
index e6939c5226a..66d0e3a6a0d 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBDeletionTableIT.java
@@ -414,6 +414,27 @@ public class IoTDBDeletionTableIT {
}
}
+ @Test
+ public void testDeleteWithRenamedTimeColumn() throws SQLException {
+ try (Connection connection =
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE time_column_rename");
+ statement.execute("use time_column_rename");
+ statement.execute("CREATE TABLE vehicle(ts time, deviceId STRING TAG, s0
INT32 FIELD)");
+ statement.execute("INSERT INTO vehicle(ts, deviceId, s0) VALUES(1, 'd0',
1)");
+ statement.execute("INSERT INTO vehicle(ts, deviceId, s0) VALUES(2, 'd0',
2)");
+
+ statement.execute("DELETE FROM vehicle WHERE ts <= 1");
+
+ try (ResultSet resultSet = statement.executeQuery("SELECT ts, s0 FROM
vehicle")) {
+ assertTrue(resultSet.next());
+ assertEquals(2, resultSet.getLong("ts"));
+ assertEquals(2, resultSet.getInt("s0"));
+ assertFalse(resultSet.next());
+ }
+ }
+ }
+
@Test
public void testRangeDelete() throws SQLException {
prepareData(4, 1);
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 c1c26550f9d..2c4978618a6 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
@@ -259,6 +259,8 @@ public final class DataNodeQueryMessages {
"Left hand expression is not an identifier: ";
public static final String THE_LEFT_HAND_VALUE_MUST_BE_AN_IDENTIFIER =
"The left hand value must be an identifier: ";
+ public static final String THE_TABLE_S_DOES_NOT_CONTAIN_A_TIME_COLUMN =
+ "The table '%s' does not contain a time column";
public static final String THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR =
"The operator of tag predicate must be '=' for ";
public static final String ONLY_TIME_FILTERS_ARE_SUPPORTED_IN_LAST_QUERY =
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 3d2783c81b6..7a7de06471b 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
@@ -258,6 +258,8 @@ public final class DataNodeQueryMessages {
"左侧表达式不是标识符:";
public static final String THE_LEFT_HAND_VALUE_MUST_BE_AN_IDENTIFIER =
"左侧值必须是标识符:";
+ public static final String THE_TABLE_S_DOES_NOT_CONTAIN_A_TIME_COLUMN =
+ "表 '%s' 不包含时间列";
public static final String THE_OPERATOR_OF_TAG_PREDICATE_MUST_BE_FOR =
"标签谓词的运算符必须为 '=',目标:";
public static final String ONLY_TIME_FILTERS_ARE_SUPPORTED_IN_LAST_QUERY =
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
index c9cf278051f..d00ee54428b 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
@@ -37,6 +37,7 @@ import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NullLiteral;
import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.StringLiteral;
import org.apache.iotdb.commons.schema.table.TsTable;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics;
import org.apache.iotdb.confignode.rpc.thrift.TRegionRouteMapResp;
import org.apache.iotdb.db.i18n.DataNodeQueryMessages;
@@ -520,7 +521,7 @@ public class AnalyzeUtils {
}
Identifier identifier = (Identifier) left;
// time predicate
- if (identifier.getValue().equalsIgnoreCase("time")) {
+ if (identifier.getValue().equalsIgnoreCase(getTimeColumnName(table))) {
long rightHandValue;
if (right instanceof LongLiteral) {
rightHandValue = ((LongLiteral) right).getParsedValue();
@@ -567,6 +568,17 @@ public class AnalyzeUtils {
return combinePredicates(oldPredicate, newPredicate);
}
+ private static String getTimeColumnName(final TsTable table) {
+ final TsTableColumnSchema timeColumnSchema = table.getTimeColumnSchema();
+ if (Objects.isNull(timeColumnSchema)) {
+ throw new SemanticException(
+ String.format(
+ DataNodeQueryMessages.THE_TABLE_S_DOES_NOT_CONTAIN_A_TIME_COLUMN,
+ table.getTableName()));
+ }
+ return timeColumnSchema.getColumnName();
+ }
+
private static IDPredicate getTagPredicate(
ComparisonExpression comparisonExpression, Expression right, int
tagColumnOrdinal) {
if (comparisonExpression.getOperator() !=
ComparisonExpression.Operator.EQUAL) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtils.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtils.java
index d7748e2347d..041be454c92 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtils.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtils.java
@@ -57,14 +57,25 @@ public class PredicateUtils {
*/
public static Pair<Expression, Boolean> extractGlobalTimePredicate(
Expression predicate, boolean canRewrite, boolean isFirstOr) {
+ return extractGlobalTimePredicate(predicate, canRewrite, isFirstOr, TIME);
+ }
+
+ public static Pair<Expression, Boolean> extractGlobalTimePredicate(
+ Expression predicate, boolean canRewrite, boolean isFirstOr, String
timeColumnName) {
if (predicate instanceof LogicalExpression
&& ((LogicalExpression) predicate).getOperator().equals(AND)) {
Pair<Expression, Boolean> leftResultPair =
extractGlobalTimePredicate(
- ((LogicalExpression) predicate).getTerms().get(0), canRewrite,
isFirstOr);
+ ((LogicalExpression) predicate).getTerms().get(0),
+ canRewrite,
+ isFirstOr,
+ timeColumnName);
Pair<Expression, Boolean> rightResultPair =
extractGlobalTimePredicate(
- ((LogicalExpression) predicate).getTerms().get(1), canRewrite,
isFirstOr);
+ ((LogicalExpression) predicate).getTerms().get(1),
+ canRewrite,
+ isFirstOr,
+ timeColumnName);
// rewrite predicate to avoid duplicate calculation on time filter
// If Left-child or Right-child does not contain value filter
@@ -104,10 +115,10 @@ public class PredicateUtils {
&& ((LogicalExpression) predicate).getOperator().equals(OR)) {
Pair<Expression, Boolean> leftResultPair =
extractGlobalTimePredicate(
- ((LogicalExpression) predicate).getTerms().get(0), false, false);
+ ((LogicalExpression) predicate).getTerms().get(0), false, false,
timeColumnName);
Pair<Expression, Boolean> rightResultPair =
extractGlobalTimePredicate(
- ((LogicalExpression) predicate).getTerms().get(1), false, false);
+ ((LogicalExpression) predicate).getTerms().get(1), false, false,
timeColumnName);
if (leftResultPair.left != null && rightResultPair.left != null) {
if (Boolean.TRUE.equals(isFirstOr && !leftResultPair.right &&
!rightResultPair.right)) {
@@ -132,8 +143,8 @@ public class PredicateUtils {
else if (predicate instanceof ComparisonExpression) {
Expression leftExpression = ((ComparisonExpression) predicate).getLeft();
Expression rightExpression = ((ComparisonExpression)
predicate).getRight();
- if (checkIsTimeFilter(leftExpression, rightExpression)
- || checkIsTimeFilter(rightExpression, leftExpression)) {
+ if (checkIsTimeFilter(leftExpression, rightExpression, timeColumnName)
+ || checkIsTimeFilter(rightExpression, leftExpression,
timeColumnName)) {
return new Pair<>(predicate, false);
}
return new Pair<>(null, true);
@@ -190,9 +201,10 @@ public class PredicateUtils {
}
}
- private static boolean checkIsTimeFilter(Expression timeExpression,
Expression valueExpression) {
+ private static boolean checkIsTimeFilter(
+ Expression timeExpression, Expression valueExpression, String
timeColumnName) {
return timeExpression instanceof Identifier
- && ((Identifier) timeExpression).getValue().equalsIgnoreCase(TIME)
+ && ((Identifier)
timeExpression).getValue().equalsIgnoreCase(timeColumnName)
&& valueExpression instanceof LongLiteral;
}
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
new file mode 100644
index 00000000000..5d0ccd74f45
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.iotdb.db.queryengine.plan.analyze;
+
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Identifier;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
+import org.apache.iotdb.commons.schema.table.TsTable;
+import org.apache.iotdb.commons.schema.table.column.TimeColumnSchema;
+import
org.apache.iotdb.db.storageengine.dataregion.modification.TableDeletionEntry;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class AnalyzeUtilsTest {
+
+ @Test
+ public void testParseDeletePredicateWithRenamedTimeColumn() {
+ TsTable table = new TsTable("table1");
+ table.addColumnSchema(new TimeColumnSchema("ts", TSDataType.TIMESTAMP));
+ Expression expression =
+ new ComparisonExpression(
+ ComparisonExpression.Operator.LESS_THAN_OR_EQUAL,
+ new Identifier("ts"),
+ new LongLiteral("100"));
+
+ List<TableDeletionEntry> entries =
AnalyzeUtils.parseExpressions2ModEntries(expression, table);
+
+ assertEquals(1, entries.size());
+ assertEquals(Long.MIN_VALUE, entries.get(0).getStartTime());
+ assertEquals(100, entries.get(0).getEndTime());
+ }
+}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtilsTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtilsTest.java
index 51df25a68d0..48a012a21ca 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtilsTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PredicateUtilsTest.java
@@ -22,7 +22,10 @@ package
org.apache.iotdb.db.queryengine.plan.relational.planner;
import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.queryengine.common.SessionInfo;
import org.apache.iotdb.commons.queryengine.common.SqlDialect;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.ComparisonExpression;
import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Expression;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Identifier;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.LongLiteral;
import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
import org.apache.iotdb.db.queryengine.common.QueryId;
import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
@@ -36,6 +39,7 @@ import java.time.ZoneId;
import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.AnalyzerTest.analyzeSQL;
import static
org.apache.iotdb.db.queryengine.plan.relational.planner.PredicateUtils.extractGlobalTimePredicate;
+import static org.junit.Assert.assertNotNull;
public class PredicateUtilsTest {
@Test
@@ -69,4 +73,13 @@ public class PredicateUtilsTest {
actualAnalysis.getWhereMap().values().iterator().next(), true,
true);
System.out.println(ret.getLeft());
}
+
+ @Test
+ public void extractGlobalTimePredicateWithCustomTimeColumnTest() {
+ Expression expression =
+ new ComparisonExpression(
+ ComparisonExpression.Operator.GREATER_THAN, new Identifier("ts"),
new LongLiteral("1"));
+ Pair<Expression, Boolean> ret = extractGlobalTimePredicate(expression,
true, true, "ts");
+ assertNotNull(ret.getLeft());
+ }
}