This is an automated email from the ASF dual-hosted git repository.

SpriCoder pushed a commit to branch fs/inner-view
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/fs/inner-view by this push:
     new d9de3fa4538 [FS] escape virtual table directory path segments
d9de3fa4538 is described below

commit d9de3fa45387208e6c596cc68406a0769d85ff68
Author: spricoder <[email protected]>
AuthorDate: Thu Sep 17 07:17:40 2026 +0800

    [FS] escape virtual table directory path segments
---
 .../ByDatabaseVirtualDirectoryResolver.java        | 21 +++++++----
 .../TableByTableVirtualDirectoryResolver.java      | 43 ++++++++++++++++------
 .../TableVirtualDirectoryResolverTest.java         | 42 +++++++++++++++++++++
 3 files changed, 87 insertions(+), 19 deletions(-)

diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/ByDatabaseVirtualDirectoryResolver.java
 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/ByDatabaseVirtualDirectoryResolver.java
index 16b9d326272..ba10a23a0df 100644
--- 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/ByDatabaseVirtualDirectoryResolver.java
+++ 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/ByDatabaseVirtualDirectoryResolver.java
@@ -64,7 +64,8 @@ public class ByDatabaseVirtualDirectoryResolver implements 
VirtualDirectoryResol
     }
     List<FsNode> nodes = new ArrayList<>();
     for (FsNode node : delegate.list(canonicalPath(segments))) {
-      nodes.add(VirtualDirectoryNodes.rewrite(node, 
path.resolve(node.getName()), NAME));
+      String encodedName = VirtualDirectorySegments.encode(node.getName());
+      nodes.add(VirtualDirectoryNodes.rewrite(node, path.resolve(encodedName), 
NAME));
     }
     return nodes;
   }
@@ -114,20 +115,22 @@ public class ByDatabaseVirtualDirectoryResolver 
implements VirtualDirectoryResol
       if (database == null) {
         continue;
       }
-      FsPath path = 
VirtualDirectoryPaths.resolverRootPath(NAME).resolve(database);
+      String encodedDatabase = VirtualDirectorySegments.encode(database);
+      FsPath path = 
VirtualDirectoryPaths.resolverRootPath(NAME).resolve(encodedDatabase);
       nodes.add(
           VirtualDirectoryNodes.directory(
-              database, path, NAME, 
canonicalDatabasePath(database).toString(), "database"));
+              encodedDatabase, path, NAME, 
canonicalDatabasePath(database).toString(), "database"));
     }
     return nodes;
   }
 
   private FsNode describeDatabase(String database) throws SQLException {
+    String decodedDatabase = VirtualDirectorySegments.decode(database);
     for (SqlRow row : executor.query("SHOW DATABASES")) {
-      if (database.equals(row.get("Database"))) {
+      if (decodedDatabase.equals(row.get("Database"))) {
         FsPath path = 
VirtualDirectoryPaths.resolverRootPath(NAME).resolve(database);
         return VirtualDirectoryNodes.directory(
-            database, path, NAME, canonicalDatabasePath(database).toString(), 
"database");
+            database, path, NAME, 
canonicalDatabasePath(decodedDatabase).toString(), "database");
       }
     }
     return VirtualDirectoryPaths.unknown(
@@ -144,13 +147,15 @@ public class ByDatabaseVirtualDirectoryResolver 
implements VirtualDirectoryResol
     return canonicalPath(segments);
   }
 
-  private FsPath canonicalPath(List<String> segments) {
+  private FsPath canonicalPath(List<String> segments) throws SQLException {
     if (segments.isEmpty()) {
       return FsPath.absolute("/");
     }
-    StringBuilder builder = new 
StringBuilder(canonicalDatabasePath(segments.get(0)).toString());
+    StringBuilder builder =
+        new StringBuilder(
+            
canonicalDatabasePath(VirtualDirectorySegments.decode(segments.get(0))).toString());
     for (int i = 1; i < segments.size(); i++) {
-      builder.append('/').append(segments.get(i));
+      
builder.append('/').append(VirtualDirectorySegments.decode(segments.get(i)));
     }
     return FsPath.absolute(builder.toString());
   }
diff --git 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/TableByTableVirtualDirectoryResolver.java
 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/TableByTableVirtualDirectoryResolver.java
index 32fcc40fdef..736d2a97a14 100644
--- 
a/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/TableByTableVirtualDirectoryResolver.java
+++ 
b/iotdb-client/cli/src/main/java/org/apache/iotdb/cli/fs/virtualdir/TableByTableVirtualDirectoryResolver.java
@@ -63,10 +63,13 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
       return listTableDirectories();
     }
     if (segments.size() == 1) {
-      return listDatabaseDirectories(path, segments.get(0));
+      return listDatabaseDirectories(path, 
VirtualDirectorySegments.decode(segments.get(0)));
     }
     if (segments.size() == 2) {
-      return listTableFiles(path, segments.get(0), segments.get(1));
+      return listTableFiles(
+          path,
+          VirtualDirectorySegments.decode(segments.get(0)),
+          VirtualDirectorySegments.decode(segments.get(1)));
     }
     return new ArrayList<>();
   }
@@ -78,12 +81,15 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
       return rootNode();
     }
     if (segments.size() == 1) {
-      return tableExistsInAnyDatabase(segments.get(0))
+      String table = VirtualDirectorySegments.decode(segments.get(0));
+      return tableExistsInAnyDatabase(table)
           ? VirtualDirectoryNodes.directory(segments.get(0), path, NAME, "", 
"table")
           : VirtualDirectoryPaths.unknown(path);
     }
     if (segments.size() == 2) {
-      return tableExists(segments.get(1), segments.get(0))
+      String table = VirtualDirectorySegments.decode(segments.get(0));
+      String database = VirtualDirectorySegments.decode(segments.get(1));
+      return tableExists(database, table)
           ? VirtualDirectoryNodes.directory(segments.get(1), path, NAME, "", 
"database")
           : VirtualDirectoryPaths.unknown(path);
     }
@@ -126,10 +132,11 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
     }
     List<FsNode> nodes = new ArrayList<>();
     for (String table : tables) {
+      String encodedTable = VirtualDirectorySegments.encode(table);
       nodes.add(
           VirtualDirectoryNodes.directory(
-              table,
-              VirtualDirectoryPaths.resolverRootPath(NAME).resolve(table),
+              encodedTable,
+              
VirtualDirectoryPaths.resolverRootPath(NAME).resolve(encodedTable),
               NAME,
               "",
               "table"));
@@ -143,8 +150,10 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
       if (!tables(database).contains(table)) {
         continue;
       }
+      String encodedDatabase = VirtualDirectorySegments.encode(database);
       nodes.add(
-          VirtualDirectoryNodes.directory(database, path.resolve(database), 
NAME, "", "database"));
+          VirtualDirectoryNodes.directory(
+              encodedDatabase, path.resolve(encodedDatabase), NAME, "", 
"database"));
     }
     return nodes;
   }
@@ -159,7 +168,9 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
     for (String fileName : fileNames) {
       FsPath canonicalPath = FsPath.absolute("/" + database + "/" + fileName);
       FsNode node = delegate.describe(canonicalPath);
-      nodes.add(VirtualDirectoryNodes.rewrite(node, path.resolve(fileName), 
NAME));
+      nodes.add(
+          VirtualDirectoryNodes.rewrite(
+              node, path.resolve(VirtualDirectorySegments.encode(fileName)), 
NAME));
     }
     return nodes;
   }
@@ -206,11 +217,21 @@ public class TableByTableVirtualDirectoryResolver 
implements VirtualDirectoryRes
       throw new SQLException(
           
String.format(FsVirtualMessages.EXCEPTION_PATH_IS_NOT_READABLE_ARG_4B338AD7, 
path));
     }
-    return canonicalTableFilePath(segments);
+    return canonicalTableFilePath(
+        VirtualDirectorySegments.decode(segments.get(0)),
+        VirtualDirectorySegments.decode(segments.get(1)),
+        VirtualDirectorySegments.decode(segments.get(2)));
+  }
+
+  private static FsPath canonicalTableFilePath(List<String> segments) throws 
SQLException {
+    return canonicalTableFilePath(
+        VirtualDirectorySegments.decode(segments.get(0)),
+        VirtualDirectorySegments.decode(segments.get(1)),
+        VirtualDirectorySegments.decode(segments.get(2)));
   }
 
-  private static FsPath canonicalTableFilePath(List<String> segments) {
-    return FsPath.absolute("/" + segments.get(1) + "/" + segments.get(2));
+  private static FsPath canonicalTableFilePath(String table, String database, 
String fileName) {
+    return FsPath.absolute("/" + database + "/" + fileName);
   }
 
   private static String identifier(String value) {
diff --git 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/virtualdir/TableVirtualDirectoryResolverTest.java
 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/virtualdir/TableVirtualDirectoryResolverTest.java
index 8e2680bb0e9..e675c3ac279 100644
--- 
a/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/virtualdir/TableVirtualDirectoryResolverTest.java
+++ 
b/iotdb-client/cli/src/test/java/org/apache/iotdb/cli/fs/virtualdir/TableVirtualDirectoryResolverTest.java
@@ -66,6 +66,27 @@ public class TableVirtualDirectoryResolverTest {
     assertEquals("/db1", nodes.get(0).getMetadata().get("canonicalPath"));
   }
 
+  @Test
+  public void byDatabaseEscapesDatabaseAndChildNames() throws SQLException {
+    when(executor.query("SHOW DATABASES"))
+        .thenReturn(SqlRow.list(SqlRow.of("Database", "db name")));
+    when(delegate.list(FsPath.absolute("/db name")))
+        .thenReturn(
+            Arrays.asList(
+                new FsNode(
+                    "table name.csv",
+                    FsPath.absolute("/db name/table name.csv"),
+                    FsNodeType.TABLE_DATA_FILE)));
+
+    List<FsNode> databases = 
byDatabase.list(FsPath.absolute("/.virtual/by-database"));
+    List<FsNode> children = 
byDatabase.list(FsPath.absolute("/.virtual/by-database/db%20name"));
+
+    assertEquals("db%20name", databases.get(0).getName());
+    assertEquals("/.virtual/by-database/db%20name", 
databases.get(0).getPath().toString());
+    assertEquals(
+        "/.virtual/by-database/db%20name/table%20name.csv", 
children.get(0).getPath().toString());
+  }
+
   @Test
   public void byDatabaseListsCanonicalChildrenUnderVirtualPath() throws 
SQLException {
     when(delegate.list(FsPath.absolute("/db1")))
@@ -118,4 +139,25 @@ public class TableVirtualDirectoryResolverTest {
     assertEquals("1,42", lines.get(1));
     verify(delegate).readLines(FsPath.absolute("/db1/t1.csv"), 5);
   }
+
+  @Test
+  public void byTableEscapesTableAndDatabaseNames() throws SQLException {
+    when(executor.query("SHOW DATABASES"))
+        .thenReturn(SqlRow.list(SqlRow.of("Database", "db name")));
+    when(executor.query("SHOW TABLES FROM \"db name\""))
+        .thenReturn(SqlRow.list(SqlRow.of("TableName", "table name")));
+    when(delegate.readLines(FsPath.absolute("/db name/table name.csv"), 5))
+        .thenReturn(Arrays.asList("Time,value", "1,42"));
+
+    List<FsNode> tables = byTable.list(FsPath.absolute("/.virtual/by-table"));
+    List<FsNode> databases = 
byTable.list(FsPath.absolute("/.virtual/by-table/table%20name"));
+    List<String> lines =
+        byTable.readLines(
+            
FsPath.absolute("/.virtual/by-table/table%20name/db%20name/table%20name.csv"), 
5);
+
+    assertEquals("table%20name", tables.get(0).getName());
+    assertEquals("db%20name", databases.get(0).getName());
+    assertEquals("1,42", lines.get(1));
+    verify(delegate).readLines(FsPath.absolute("/db name/table name.csv"), 5);
+  }
 }

Reply via email to