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 39e2ad07882 [Load] Handle empty path nodes during database 
auto-creation (#18307)
39e2ad07882 is described below

commit 39e2ad078820d6c435010c5f582092c23e169ff1
Author: Caideyipi <[email protected]>
AuthorDate: Mon Jul 27 18:04:45 2026 +0800

    [Load] Handle empty path nodes during database auto-creation (#18307)
    
    * Reject illegal device paths during LOAD
    
    * Remove duplicate ConfigNode validation
    
    * Handle legacy invalid databases during LOAD
---
 .../load/TreeSchemaAutoCreatorAndVerifier.java     | 36 +++++++++--
 .../plan/analyze/load/LoadTsFileAnalyzerTest.java  | 75 ++++++++++++++++++++++
 2 files changed, 104 insertions(+), 7 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java
index 6d1704c0cfd..3e968073cc0 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java
@@ -359,6 +359,12 @@ public class TreeSchemaAutoCreatorAndVerifier {
       final PartialPath devicePath = new PartialPath(device);
 
       final String[] devicePrefixNodes = devicePath.getNodes();
+      for (final String node : devicePrefixNodes) {
+        if (node == null || node.isEmpty()) {
+          throw new LoadAnalyzeException(
+              new IllegalPathException(devicePath.getFullPath()).getMessage());
+        }
+      }
       if (devicePrefixNodes.length < databasePrefixNodesLength) {
         throw new LoadAnalyzeException(
             String.format(
@@ -387,13 +393,7 @@ public class TreeSchemaAutoCreatorAndVerifier {
                 SchemaConstant.ALL_MATCH_SCOPE.serialize());
         final TShowDatabaseResp resp = configNodeClient.showDatabase(req);
 
-        for (final String databaseName : resp.getDatabaseInfoMap().keySet()) {
-          schemaCache.addAlreadySetDatabase(new PartialPath(databaseName));
-          databasesNeededToBeSet.removeIf(
-              database ->
-                  database.startsWith(databaseName)
-                      || databaseName.startsWith(database.getFullPath()));
-        }
+        filterAlreadySetDatabases(databasesNeededToBeSet, 
resp.getDatabaseInfoMap().keySet());
       } catch (IOException | TException | ClientManagerException e) {
         throw new LoadFileException(e);
       }
@@ -413,6 +413,28 @@ public class TreeSchemaAutoCreatorAndVerifier {
     }
   }
 
+  void filterAlreadySetDatabases(
+      final Set<PartialPath> databasesNeededToBeSet, final Set<String> 
alreadySetDatabaseNames) {
+    for (final String databaseName : alreadySetDatabaseNames) {
+      final PartialPath databasePath;
+      try {
+        databasePath = new PartialPath(databaseName);
+      } catch (final IllegalPathException e) {
+        // Ignore malformed databases left by older versions so they do not 
block valid loads.
+        continue;
+      }
+
+      // The path parser normalizes a trailing separator away, for example, 
"root." to "root".
+      if (!databaseName.equals(databasePath.getFullPath())) {
+        continue;
+      }
+
+      schemaCache.addAlreadySetDatabase(databasePath);
+      databasesNeededToBeSet.removeIf(
+          database -> database.startsWithOrPrefixOf(databasePath.getNodes()));
+    }
+  }
+
   private void executeSetDatabaseStatement(Statement statement)
       throws LoadFileException, AuthException {
     // 1.check Authority
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
index 68f4bde29e6..1a11de1eab5 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/analyze/load/LoadTsFileAnalyzerTest.java
@@ -200,6 +200,74 @@ public class LoadTsFileAnalyzerTest {
     }
   }
 
+  @Test
+  public void testTreeSchemaVerifierShouldRejectDeviceWithEmptyPathNode() 
throws Exception {
+    final File tsFile = File.createTempFile("load-tree-illegal-device", 
".tsfile");
+
+    try (final LoadTsFileAnalyzer analyzer =
+        new LoadTsFileAnalyzer(
+            LoadTsFileStatement.createUnchecked(tsFile.getAbsolutePath()),
+            false,
+            new MPPQueryContext(new 
QueryId("load_tree_illegal_device_test")))) {
+      final TreeSchemaAutoCreatorAndVerifier verifier =
+          new TreeSchemaAutoCreatorAndVerifier(analyzer);
+      try {
+        final IDeviceID device = new StringArrayDeviceID(new String[] {"root", 
""});
+        getTreeSchemaCache(verifier)
+            .addTimeSeries(device, new MeasurementSchema("s1", 
TSDataType.INT32));
+
+        final InvocationTargetException exception =
+            Assert.assertThrows(
+                InvocationTargetException.class,
+                () -> getAutoCreateDatabaseMethod().invoke(verifier));
+        Assert.assertTrue(exception.getCause() instanceof 
LoadAnalyzeException);
+      } finally {
+        verifier.close();
+      }
+    } finally {
+      Assert.assertTrue(tsFile.delete());
+    }
+  }
+
+  @Test
+  public void 
testTreeSchemaVerifierShouldIgnoreLegacyDatabaseWithEmptyPathNode() throws 
Exception {
+    final File tsFile = File.createTempFile("load-tree-legacy-database", 
".tsfile");
+
+    try (final LoadTsFileAnalyzer analyzer =
+        new LoadTsFileAnalyzer(
+            LoadTsFileStatement.createUnchecked(tsFile.getAbsolutePath()),
+            false,
+            new MPPQueryContext(new 
QueryId("load_tree_legacy_database_test")))) {
+      final TreeSchemaAutoCreatorAndVerifier verifier =
+          new TreeSchemaAutoCreatorAndVerifier(analyzer);
+      try {
+        final PartialPath database = new PartialPath("root.sg");
+        final PartialPath databaseWithSameStringPrefix = new 
PartialPath("root.sg1");
+        final Set<PartialPath> databasesNeededToBeSet =
+            new HashSet<>(Arrays.asList(database, 
databaseWithSameStringPrefix));
+
+        verifier.filterAlreadySetDatabases(databasesNeededToBeSet, 
Collections.singleton("root."));
+
+        Assert.assertEquals(
+            new HashSet<>(Arrays.asList(database, 
databaseWithSameStringPrefix)),
+            databasesNeededToBeSet);
+        
Assert.assertTrue(getTreeSchemaCache(verifier).getAlreadySetDatabases().isEmpty());
+
+        verifier.filterAlreadySetDatabases(
+            databasesNeededToBeSet, 
Collections.singleton(database.getFullPath()));
+
+        Assert.assertEquals(
+            Collections.singleton(databaseWithSameStringPrefix), 
databasesNeededToBeSet);
+        Assert.assertEquals(
+            Collections.singleton(database), 
getTreeSchemaCache(verifier).getAlreadySetDatabases());
+      } finally {
+        verifier.close();
+      }
+    } finally {
+      Assert.assertTrue(tsFile.delete());
+    }
+  }
+
   @Test
   public void 
testPipeGeneratedLoadMissingSchemaShouldBeTemporaryWhenAutoCreateDisabled()
       throws Exception {
@@ -316,6 +384,13 @@ public class LoadTsFileAnalyzerTest {
     return method;
   }
 
+  private Method getAutoCreateDatabaseMethod() throws NoSuchMethodException {
+    final Method method =
+        
TreeSchemaAutoCreatorAndVerifier.class.getDeclaredMethod("autoCreateDatabase");
+    method.setAccessible(true);
+    return method;
+  }
+
   private 
org.apache.iotdb.commons.queryengine.plan.relational.metadata.TableSchema
       createTableSchema(final TSDataType fieldType) {
     return new 
org.apache.iotdb.commons.queryengine.plan.relational.metadata.TableSchema(

Reply via email to