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"));
+  }
+}

Reply via email to