This is an automated email from the ASF dual-hosted git repository.
mchades pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 5259e9c5f6 [#10909] fix(client): Fix list/load inconsistency for
schema and table names containing dots (#11597)
5259e9c5f6 is described below
commit 5259e9c5f63d2efe1dedb9d3a041ed21995d2735
Author: mchades <[email protected]>
AuthorDate: Thu Jun 11 17:13:29 2026 +0800
[#10909] fix(client): Fix list/load inconsistency for schema and table
names containing dots (#11597)
### What changes were proposed in this pull request?
Fix the `list*`/`load*` inconsistency for MySQL schemas and tables whose
names contain dots (e.g. a MySQL database named `db.v2` created
externally with `` CREATE DATABASE `db.v2` ``).
- `GenericSchema`: replace `MetadataObjects.of(String parent, String
name, Type)` with `MetadataObjects.of(List, Type)`, consistent with
`GenericFileset`, `GenericTopic`, and `GenericModel`.
- `RelationalTable`: replace `MetadataObjects.parse(tableFullName(...),
TABLE)` with `MetadataObjects.of(List, TABLE)`, and remove the
now-unused `tableFullName()` helper and `DOT_JOINER` field.
### Why are the changes needed?
Fix: #10909
`MetadataObjects.of(String parent, String name, Type)` joins parent and
name with `.` then re-parses by splitting on `.`. When `name` itself
contains a dot (e.g. `"db.v2"`), the split produces too many segments
and the length check throws `IllegalArgumentException`:
```
IllegalArgumentException: If the type is SCHEMA, the length of names must
be 2
at GenericSchema.<init>(GenericSchema.java:48)
at BaseSchemaCatalog.loadSchema(BaseSchemaCatalog.java:216)
```
`listSchemas()` had already returned the dotted name successfully, so
`loadSchema()` crashing breaks the `list*`/`load*` contract.
### Does this PR introduce _any_ user-facing change?
No API changes. Bug fix only: `loadSchema` and `loadTable` now correctly
handle names containing dots, consistent with what
`listSchemas`/`listTables` already returns.
### How was this patch tested?
Added `testObjectNamesWithDots()` to `CatalogMysqlIT`
(`@Tag("gravitino-docker-test")`). The test:
1. Creates a MySQL database with a dot in its name directly via JDBC.
2. Asserts `listSchemas()` returns that schema.
3. Asserts `loadSchema()` succeeds for the same name.
4. Creates a table in that schema directly via JDBC.
5. Asserts `listTables()` returns the table.
6. Asserts `loadTable()` succeeds for the same name.
Co-authored-by: Copilot <[email protected]>
---
.../mysql/integration/test/CatalogMysqlIT.java | 46 ++++++++++++++++++++++
.../org/apache/gravitino/client/GenericSchema.java | 3 +-
.../apache/gravitino/client/RelationalTable.java | 11 ++----
3 files changed, 51 insertions(+), 9 deletions(-)
diff --git
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
index 59a83b8e85..db9035d6c3 100644
---
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
+++
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlIT.java
@@ -2380,4 +2380,50 @@ public class CatalogMysqlIT extends BaseIT {
}
}
}
+
+ @Test
+ void testObjectNamesWithDots() {
+ // Create a MySQL database with a dot in its name directly (bypassing
Gravitino)
+ String dottedSchemaName = GravitinoITUtils.genRandomName("db") + ".nested";
+ mysqlService.executeQuery("CREATE DATABASE `" + dottedSchemaName + "`");
+ try {
+ // listSchemas should include the schema with dot in name
+ String[] listedSchemas = catalog.asSchemas().listSchemas();
+ Set<String> schemaNames = Sets.newHashSet(listedSchemas);
+ Assertions.assertTrue(
+ schemaNames.contains(dottedSchemaName),
+ "listSchemas should return schema with dot in name: " +
dottedSchemaName);
+
+ // loadSchema must succeed for a name that appeared in listSchemas
+ Schema loadedSchema = catalog.asSchemas().loadSchema(dottedSchemaName);
+ Assertions.assertEquals(
+ dottedSchemaName,
+ loadedSchema.name(),
+ "loadSchema must succeed for schema that appeared in listSchemas");
+
+ // Create a table directly in MySQL (bypassing Gravitino, since dotted
schema names are not
+ // allowed at creation time, but may exist externally)
+ String testTableName = GravitinoITUtils.genRandomName("test_table");
+ mysqlService.executeQuery(
+ "CREATE TABLE `" + dottedSchemaName + "`.`" + testTableName + "` (id
INT)");
+
+ // listTables under the dotted schema should include the created table
+ NameIdentifier[] listedTables =
+ catalog.asTableCatalog().listTables(Namespace.of(dottedSchemaName));
+ Set<String> tableNames =
+
Arrays.stream(listedTables).map(NameIdentifier::name).collect(Collectors.toSet());
+ Assertions.assertTrue(
+ tableNames.contains(testTableName), "listTables should return table
under dotted schema");
+
+ // loadTable must succeed for a table that appeared in listTables
+ Table loadedTable =
+
catalog.asTableCatalog().loadTable(NameIdentifier.of(dottedSchemaName,
testTableName));
+ Assertions.assertEquals(
+ testTableName,
+ loadedTable.name(),
+ "loadTable must succeed for table that appeared in listTables");
+ } finally {
+ mysqlService.executeQuery("DROP DATABASE IF EXISTS `" + dottedSchemaName
+ "`");
+ }
+ }
}
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
index 79a90d3d4a..bbb5f84300 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
@@ -18,6 +18,7 @@
*/
package org.apache.gravitino.client;
+import java.util.Arrays;
import java.util.Map;
import org.apache.gravitino.Audit;
import org.apache.gravitino.MetadataObject;
@@ -45,7 +46,7 @@ class GenericSchema implements Schema, SupportsTags,
SupportsRoles, SupportsPoli
GenericSchema(SchemaDTO schemaDTO, RESTClient restClient, String metalake,
String catalog) {
this.schemaDTO = schemaDTO;
MetadataObject schemaObject =
- MetadataObjects.of(catalog, schemaDTO.name(),
MetadataObject.Type.SCHEMA);
+ MetadataObjects.of(Arrays.asList(catalog, schemaDTO.name()),
MetadataObject.Type.SCHEMA);
this.objectTagOperations = new MetadataObjectTagOperations(metalake,
schemaObject, restClient);
this.objectRoleOperations =
new MetadataObjectRoleOperations(metalake, schemaObject, restClient);
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
index 7a3dd671d9..fac38e76ac 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
@@ -22,7 +22,6 @@ import static
org.apache.gravitino.dto.util.DTOConverters.fromDTO;
import static org.apache.gravitino.dto.util.DTOConverters.toDTO;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -81,8 +80,6 @@ class RelationalTable
SupportsStatistics,
SupportsPartitionStatistics {
- private static final Joiner DOT_JOINER = Joiner.on(".");
-
private final Table table;
private final RESTClient restClient;
@@ -119,7 +116,9 @@ class RelationalTable
this.restClient = restClient;
this.table = fromDTO(tableDTO);
MetadataObject tableObject =
- MetadataObjects.parse(tableFullName(namespace, tableDTO.name()),
MetadataObject.Type.TABLE);
+ MetadataObjects.of(
+ Arrays.asList(namespace.level(1), namespace.level(2),
tableDTO.name()),
+ MetadataObject.Type.TABLE);
this.objectTagOperations =
new MetadataObjectTagOperations(namespace.level(0), tableObject,
restClient);
this.objectRoleOperations =
@@ -373,10 +372,6 @@ class RelationalTable
return this;
}
- private static String tableFullName(Namespace tableNS, String tableName) {
- return DOT_JOINER.join(tableNS.level(1), tableNS.level(2), tableName);
- }
-
@Override
public String[] listTags() {
return objectTagOperations.listTags();