This is an automated email from the ASF dual-hosted git repository.
jiangtian 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 366726f8259 Fixed the path check semantic for object (#16983)
366726f8259 is described below
commit 366726f82590d3898ca2549bc9bc06711bf23b88
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jan 6 10:30:04 2026 +0800
Fixed the path check semantic for object (#16983)
* create-check
* init-windows
* windows
* windows
* partial
* IT-change
* create-table
* po
* fix
* bypass-sit
* finally
---
.../iotdb/relational/it/schema/IoTDBTableIT.java | 80 ++++++++++++----------
.../execution/config/TableConfigTaskVisitor.java | 2 +-
.../fetcher/TableHeaderSchemaValidator.java | 13 +++-
.../apache/iotdb/commons/schema/table/TsTable.java | 22 ++++--
.../apache/iotdb/commons/utils/WindowsOSUtils.java | 64 +++++++++++++++++
.../iotdb/commons/utils/WindowsOSUtilsTest.java | 41 +++++++++++
6 files changed, 177 insertions(+), 45 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
index 7ad5172fca4..7915d64a680 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.relational.it.schema;
+import org.apache.iotdb.commons.utils.WindowsOSUtils;
import org.apache.iotdb.db.it.utils.TestUtils;
import org.apache.iotdb.isession.ITableSession;
import org.apache.iotdb.it.env.EnvFactory;
@@ -31,6 +32,7 @@ import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.tsfile.enums.ColumnCategory;
import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.external.commons.lang3.SystemUtils;
import org.apache.tsfile.write.record.Tablet;
import org.apache.tsfile.write.schema.IMeasurementSchema;
import org.apache.tsfile.write.schema.MeasurementSchema;
@@ -784,6 +786,11 @@ public class IoTDBTableIT {
@Test
public void testTableObjectCheck() throws Exception {
final Set<String> illegal = new HashSet<>(Arrays.asList("./", ".", "..",
".\\", "../hack"));
+ if (SystemUtils.IS_OS_WINDOWS) {
+ illegal.add("C.");
+ illegal.add("a:b<|");
+ illegal.add("COM1");
+ }
for (final String single : illegal) {
testObject4SingleIllegalPath(single);
}
@@ -796,21 +803,9 @@ public class IoTDBTableIT {
final ITableSession session =
EnvFactory.getEnv().getTableSessionConnection()) {
statement.execute("create database if not exists db2");
statement.execute("use db2");
- statement.execute(String.format("create table \"%s\" ()", illegal));
-
- try {
- statement.execute(String.format("alter table \"%s\" add column a
object", illegal));
- fail();
- } catch (final SQLException e) {
- Assert.assertEquals(
- String.format(
- "701: When there are object fields, the tableName %s shall not
be '.', '..' or contain './', '.\\'",
- illegal),
- e.getMessage());
- }
- // Test auto-create
- String testObject =
+ // Test auto-create table
+ final String testObject =
System.getProperty("user.dir")
+ File.separator
+ "target"
@@ -819,7 +814,7 @@ public class IoTDBTableIT {
+ File.separator
+ "object-example.pt";
- List<IMeasurementSchema> schemaList = new ArrayList<>();
+ final List<IMeasurementSchema> schemaList = new ArrayList<>();
schemaList.add(new MeasurementSchema("a", TSDataType.STRING));
schemaList.add(new MeasurementSchema("b", TSDataType.STRING));
schemaList.add(new MeasurementSchema("c", TSDataType.INT32));
@@ -843,26 +838,46 @@ public class IoTDBTableIT {
tablet.addValue(schemaList.get(2).getMeasurementName(), 0, 0);
tablet.addValue(0, 3, true, 0,
Files.readAllBytes(Paths.get(testObject)));
+ final String expectedTableError =
+ String.format(
+ "701: When there are object fields, the tableName %s shall not
be '.', '..' or contain './', '.\\'."
+ + (SystemUtils.IS_OS_WINDOWS ? " " +
WindowsOSUtils.OS_SEGMENT_ERROR : ""),
+ illegal.toLowerCase());
+ final String expectedObjectError =
+ String.format(
+ "701: When there are object fields, the objectName %s shall not
be '.', '..' or contain './', '.\\'."
+ + (SystemUtils.IS_OS_WINDOWS ? " " +
WindowsOSUtils.OS_SEGMENT_ERROR : ""),
+ illegal.toLowerCase());
+
try {
session.executeNonQueryStatement("use db2");
session.insert(tablet);
} catch (final Exception e) {
- Assert.assertEquals(
- String.format(
- "701: When there are object fields, the tableName %s shall not
be '.', '..' or contain './', '.\\'",
- illegal),
- e.getMessage());
+ Assert.assertEquals(expectedTableError, e.getMessage());
+ }
+
+ statement.execute(String.format("create table \"%s\" ()", illegal));
+
+ try {
+ statement.execute(String.format("alter table \"%s\" add column a
object", illegal));
+ fail();
+ } catch (final SQLException e) {
+ Assert.assertEquals(expectedTableError, e.getMessage());
+ }
+
+ // Test auto-create column
+ try {
+ session.executeNonQueryStatement("use db2");
+ session.insert(tablet);
+ } catch (final Exception e) {
+ Assert.assertEquals(expectedTableError, e.getMessage());
}
try {
statement.execute(String.format("create table test (\"%s\" object)",
illegal));
fail();
} catch (final SQLException e) {
- Assert.assertEquals(
- String.format(
- "701: When there are object fields, the objectName %s shall
not be '.', '..' or contain './', '.\\'",
- illegal),
- e.getMessage());
+ Assert.assertEquals(expectedObjectError, e.getMessage());
}
statement.execute("create table test (a tag, b attribute, c int32, d
object)");
@@ -873,11 +888,7 @@ public class IoTDBTableIT {
session.executeNonQueryStatement("use db2");
session.insert(tablet);
} catch (final Exception e) {
- Assert.assertEquals(
- String.format(
- "701: When there are object fields, the objectName %s shall
not be '.', '..' or contain './', '.\\'",
- illegal),
- e.getMessage());
+ Assert.assertEquals(expectedObjectError, e.getMessage());
}
// It's OK if you don't write object
@@ -891,7 +902,8 @@ public class IoTDBTableIT {
} catch (final SQLException e) {
Assert.assertEquals(
String.format(
- "507: When there are object fields, the deviceId [test, %s]
shall not be '.', '..' or contain './', '.\\'",
+ "507: When there are object fields, the deviceId [test, %s]
shall not be '.', '..' or contain './', '.\\'."
+ + (SystemUtils.IS_OS_WINDOWS ? " " +
WindowsOSUtils.OS_SEGMENT_ERROR : ""),
illegal),
e.getMessage());
}
@@ -900,11 +912,7 @@ public class IoTDBTableIT {
statement.execute(String.format("alter table test add column \"%s\"
object", illegal));
fail();
} catch (final SQLException e) {
- Assert.assertEquals(
- String.format(
- "701: When there are object fields, the objectName %s shall
not be '.', '..' or contain './', '.\\'",
- illegal),
- e.getMessage());
+ Assert.assertEquals(expectedObjectError, e.getMessage());
}
statement.execute("drop database db2");
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
index 0f6c4bdb538..4b609aa87eb 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
@@ -580,7 +580,7 @@ public class TableConfigTaskVisitor extends
AstVisitor<IConfigTask, MPPQueryCont
final TsTableColumnCategory category =
columnDefinition.getColumnCategory();
final String columnName = columnDefinition.getName().getValue();
final TSDataType dataType = getDataType(columnDefinition.getType());
- hasObject |= dataType.equals(TSDataType.OBJECT);
+ hasObject |= dataType == TSDataType.OBJECT;
final String comment = columnDefinition.getComment();
if (checkTimeColumnIdempotent(category, columnName, dataType, comment,
table)
&& !hasTimeColumn) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
index daea9a9e9da..99da3d005f7 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java
@@ -20,6 +20,8 @@
package org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher;
import org.apache.iotdb.commons.exception.IoTDBException;
+import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
+import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.commons.schema.table.InsertNodeMeasurementInfo;
import org.apache.iotdb.commons.schema.table.TreeViewSchema;
import org.apache.iotdb.commons.schema.table.TsTable;
@@ -629,6 +631,7 @@ public class TableHeaderSchemaValidator {
return tsTable;
}
+ boolean hasObject = false;
for (int i = 0; i < measurements.length; i++) {
if (measurements[i] == null) {
continue;
@@ -657,9 +660,17 @@ public class TableHeaderSchemaValidator {
throw new ColumnCreationFailException(
"Cannot create column " + columnName + " datatype is not
provided");
}
-
+ hasObject |= dataType == TSDataType.OBJECT;
tsTable.addColumnSchema(generateColumnSchema(category, columnName,
dataType, null, null));
}
+ if (hasObject) {
+ try {
+ tsTable.checkTableNameAndObjectNames4Object();
+ } catch (final MetadataException e) {
+ throw new IoTDBRuntimeException(
+ e.getMessage(), TSStatusCode.SEMANTIC_ERROR.getStatusCode());
+ }
+ }
return tsTable;
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
index 8f5f5f44f60..0a28cae2ea0 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
@@ -30,10 +30,12 @@ import
org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchemaUtil;
import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
+import org.apache.iotdb.commons.utils.WindowsOSUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import com.google.common.collect.ImmutableList;
import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.external.commons.lang3.SystemUtils;
import org.apache.tsfile.utils.Pair;
import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -71,7 +73,7 @@ public class TsTable {
public static final String TTL_PROPERTY = "ttl";
public static final Set<String> TABLE_ALLOWED_PROPERTIES =
Collections.singleton(TTL_PROPERTY);
private static final String OBJECT_STRING_ERROR =
- "When there are object fields, the %s %s shall not be '.', '..' or
contain './', '.\\'";
+ "When there are object fields, the %s %s shall not be '.', '..' or
contain './', '.\\'.";
protected String tableName;
private final Map<String, TsTableColumnSchema> columnSchemaMap = new
LinkedHashMap<>();
@@ -435,15 +437,21 @@ public class TsTable {
}
}
- public static boolean isInvalid4ObjectType(final String column) {
- return column.equals(".")
- || column.equals("..")
- || column.contains("./")
- || column.contains(".\\");
+ public static boolean isInvalid4ObjectType(final String path) {
+ return path.equals(".")
+ || path.equals("..")
+ || path.contains("./")
+ || path.contains(".\\")
+ || !WindowsOSUtils.isLegalPathSegment4Windows(path);
}
public static String getObjectStringError(final String columnType, final
String columnName) {
- return String.format(OBJECT_STRING_ERROR, columnType, columnName);
+ return String.format(
+ SystemUtils.IS_OS_WINDOWS
+ ? OBJECT_STRING_ERROR + " " + WindowsOSUtils.OS_SEGMENT_ERROR
+ : OBJECT_STRING_ERROR,
+ columnType,
+ columnName);
}
@Override
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java
new file mode 100644
index 00000000000..3e8155ed759
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/WindowsOSUtils.java
@@ -0,0 +1,64 @@
+/*
+ * 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.commons.utils;
+
+import org.apache.tsfile.external.commons.lang3.SystemUtils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class WindowsOSUtils {
+ private static final String ILLEGAL_WINDOWS_CHARS = "\\/:*?\"<>|";
+ private static final Set<String> ILLEGAL_WINDOWS_NAMES =
+ new HashSet<>(Arrays.asList("CON", "PRN", "AUX", "NUL", "COM1-COM9,
LPT1-LPT9"));
+
+ static {
+ for (int i = 0; i < 10; ++i) {
+ ILLEGAL_WINDOWS_NAMES.add("COM" + i);
+ ILLEGAL_WINDOWS_NAMES.add("LPT" + i);
+ }
+ }
+
+ public static final String OS_SEGMENT_ERROR =
+ String.format(
+ "In Windows System, the path shall not contains %s, equals one of
%s, or ends with '.' or ' '.",
+ ILLEGAL_WINDOWS_CHARS, ILLEGAL_WINDOWS_NAMES);
+
+ public static boolean isLegalPathSegment4Windows(final String pathSegment) {
+ if (!SystemUtils.IS_OS_WINDOWS) {
+ return true;
+ }
+ for (final char illegalChar : ILLEGAL_WINDOWS_CHARS.toCharArray()) {
+ if (pathSegment.indexOf(illegalChar) != -1) {
+ return false;
+ }
+ }
+ if (pathSegment.endsWith(".") || pathSegment.endsWith(" ")) {
+ return false;
+ }
+ for (final String illegalName : ILLEGAL_WINDOWS_NAMES) {
+ if (pathSegment.equalsIgnoreCase(illegalName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java
new file mode 100644
index 00000000000..bbe847b6f8e
--- /dev/null
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/WindowsOSUtilsTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.commons.utils;
+
+import org.apache.tsfile.external.commons.lang3.SystemUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static
org.apache.iotdb.commons.utils.WindowsOSUtils.isLegalPathSegment4Windows;
+
+public class WindowsOSUtilsTest {
+ @Test
+ public void testIllegalDetection() {
+ if (!SystemUtils.IS_OS_WINDOWS) {
+ return;
+ }
+ Assert.assertTrue(isLegalPathSegment4Windows("abc"));
+ Assert.assertTrue(isLegalPathSegment4Windows(".A!"));
+
+ Assert.assertFalse(isLegalPathSegment4Windows("C."));
+ Assert.assertFalse(isLegalPathSegment4Windows("a:b<|"));
+ Assert.assertFalse(isLegalPathSegment4Windows("COM1"));
+ }
+}