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 438da702f1 [#12009] improvement(core): Support custom case sensitivity 
normalization in Capability (#12010)
438da702f1 is described below

commit 438da702f194cf441a421b354073ac8eb843d688
Author: Yuhui <[email protected]>
AuthorDate: Wed Jul 15 11:35:40 2026 +0800

    [#12009] improvement(core): Support custom case sensitivity normalization 
in Capability (#12010)
    
    ### What type of PR is it?
    Improvement
    
    ### What does this PR do?
    Adds a `default String normalizeName(Scope scope, String name)` method
    to `Capability`, and updates `CapabilityHelpers` to call it instead of
    hardcoding a lowercase fold. This lets catalogs override normalization
    for case sensitivity behavior that isn't a simple lowercase fold (e.g.
    uppercase-by-default catalogs, or ones supporting both quoted
    case-sensitive and unquoted case-insensitive forms).
    
    ### Related issues
    #12009
    
    ### Does this PR introduce any user-facing change?
    No
    
    ### How was this patch tested?
    `./gradlew :core:test --tests
    "org.apache.gravitino.connector.capability.TestCapability"`
---
 .../gravitino/catalog/CapabilityHelpers.java       | 28 +++++--
 .../gravitino/connector/capability/Capability.java | 20 +++++
 .../gravitino/catalog/TestCapabilityHelpers.java   | 95 ++++++++++++++++++++++
 .../connector/capability/TestCapability.java       |  8 ++
 4 files changed, 142 insertions(+), 9 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java 
b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
index 5825a97142..d6d0a90e7e 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java
@@ -173,9 +173,7 @@ public class CapabilityHelpers {
 
   public static Partition applyCaseSensitive(Partition partition, Capability 
capabilities) {
     String newName =
-        
capabilities.caseSensitiveOnName(Capability.Scope.PARTITION).supported()
-            ? partition.name()
-            : partition.name().toLowerCase();
+        applyCaseSensitiveOnName(Capability.Scope.PARTITION, partition.name(), 
capabilities);
     if (partition instanceof IdentityPartition) {
       IdentityPartition identityPartition = (IdentityPartition) partition;
       return Partitions.identity(
@@ -507,16 +505,28 @@ public class CapabilityHelpers {
 
   public static String applyCaseSensitiveOnName(
       Capability.Scope scope, String name, Capability capabilities) {
-    return capabilities.caseSensitiveOnName(scope).supported() ? name : 
name.toLowerCase();
+    if (name == null) {
+      return null;
+    }
+    String normalizedName = capabilities.normalizeName(scope, name);
+    if (normalizedName == null) {
+      throw new IllegalArgumentException(
+          String.format(
+              "Capability.normalizeName(%s, %s) must not return null for a 
non-null name",
+              scope, name));
+    }
+    return normalizedName;
   }
 
   private static String[] applyCaseSensitiveOnColumnName(String[] name, 
Capability capabilities) {
-    if 
(!capabilities.caseSensitiveOnName(Capability.Scope.COLUMN).supported()) {
-      String[] standardizeColumnName = Arrays.copyOf(name, name.length);
-      standardizeColumnName[0] = name[0].toLowerCase();
-      return standardizeColumnName;
+    String normalizedFirstName =
+        applyCaseSensitiveOnName(Capability.Scope.COLUMN, name[0], 
capabilities);
+    if (normalizedFirstName.equals(name[0])) {
+      return name;
     }
-    return name;
+    String[] standardizeColumnName = Arrays.copyOf(name, name.length);
+    standardizeColumnName[0] = normalizedFirstName;
+    return standardizeColumnName;
   }
 
   private static void applyColumnNotNull(Column column, Capability 
capabilities) {
diff --git 
a/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java 
b/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
index f787a16d4f..66a6e11d61 100644
--- 
a/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
+++ 
b/core/src/main/java/org/apache/gravitino/connector/capability/Capability.java
@@ -19,6 +19,7 @@
 package org.apache.gravitino.connector.capability;
 
 import com.google.common.collect.ImmutableSet;
+import java.util.Locale;
 import java.util.Set;
 import org.apache.gravitino.MetadataObjects;
 import org.apache.gravitino.annotation.Evolving;
@@ -73,6 +74,25 @@ public interface Capability {
     return DEFAULT.caseSensitiveOnName(scope);
   }
 
+  /**
+   * Normalizes the given name to the catalog's canonical form before it is 
used to call the
+   * underlying catalog operation and persisted as the Gravitino entity name. 
This is invoked
+   * instead of the simple lowercase folding implied by {@link 
#caseSensitiveOnName(Scope)} whenever
+   * a catalog needs custom folding logic (for example, a catalog whose native 
folding direction is
+   * uppercase, or one that supports a case-sensitive quoted form alongside a 
case-insensitive
+   * unquoted form).
+   *
+   * @param scope The scope of the capability.
+   * @param name The name to normalize.
+   * @return The normalized, canonical name.
+   */
+  default String normalizeName(Scope scope, String name) {
+    if (name == null) {
+      return null;
+    }
+    return caseSensitiveOnName(scope).supported() ? name : 
name.toLowerCase(Locale.ROOT);
+  }
+
   /**
    * Check if the name is illegal in the scope, such as special characters, 
reserved words, etc.
    *
diff --git 
a/core/src/test/java/org/apache/gravitino/catalog/TestCapabilityHelpers.java 
b/core/src/test/java/org/apache/gravitino/catalog/TestCapabilityHelpers.java
new file mode 100644
index 0000000000..02c0c051fd
--- /dev/null
+++ b/core/src/test/java/org/apache/gravitino/catalog/TestCapabilityHelpers.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gravitino.catalog;
+
+import java.util.Locale;
+import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.connector.capability.CapabilityResult;
+import org.apache.gravitino.rel.expressions.literals.Literal;
+import org.apache.gravitino.rel.expressions.literals.Literals;
+import org.apache.gravitino.rel.partitions.IdentityPartition;
+import org.apache.gravitino.rel.partitions.Partition;
+import org.apache.gravitino.rel.partitions.Partitions;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestCapabilityHelpers {
+
+  private static final Capability UPPERCASE_CAPABILITY =
+      new Capability() {
+        @Override
+        public CapabilityResult caseSensitiveOnName(Scope scope) {
+          return CapabilityResult.unsupported("not case sensitive");
+        }
+
+        @Override
+        public String normalizeName(Scope scope, String name) {
+          return name.toUpperCase(Locale.ROOT);
+        }
+      };
+
+  private static final Capability NULL_NORMALIZING_CAPABILITY =
+      new Capability() {
+        @Override
+        public String normalizeName(Scope scope, String name) {
+          return null;
+        }
+      };
+
+  @Test
+  void testApplyCaseSensitiveOnNameHonorsCustomNormalizeName() {
+    String normalized =
+        CapabilityHelpers.applyCaseSensitiveOnName(
+            Capability.Scope.TABLE, "myTable", UPPERCASE_CAPABILITY);
+    Assertions.assertEquals("MYTABLE", normalized);
+  }
+
+  @Test
+  void testApplyCaseSensitivePartitionHonorsCustomNormalizeName() {
+    IdentityPartition partition =
+        Partitions.identity(
+            "myPartition",
+            new String[][] {{"col1"}},
+            new Literal<?>[] {Literals.stringLiteral("val1")},
+            null);
+
+    Partition result = CapabilityHelpers.applyCaseSensitive(partition, 
UPPERCASE_CAPABILITY);
+    Assertions.assertEquals("MYPARTITION", result.name());
+  }
+
+  @Test
+  void testApplyCaseSensitiveOnNameRejectsNullNormalizeNameResult() {
+    Assertions.assertThrows(
+        IllegalArgumentException.class,
+        () ->
+            CapabilityHelpers.applyCaseSensitiveOnName(
+                Capability.Scope.TABLE, "myTable", 
NULL_NORMALIZING_CAPABILITY));
+  }
+
+  @Test
+  void testApplyCaseSensitiveOnNameAllowsNullNameToPassThrough() {
+    // A null name (e.g. an auto-generated identity partition name yet to be 
assigned) must be
+    // allowed to pass through as null rather than being rejected as an 
invalid normalizeName
+    // result.
+    String normalized =
+        CapabilityHelpers.applyCaseSensitiveOnName(
+            Capability.Scope.PARTITION, null, Capability.DEFAULT);
+    Assertions.assertNull(normalized);
+  }
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/connector/capability/TestCapability.java
 
b/core/src/test/java/org/apache/gravitino/connector/capability/TestCapability.java
index 2a4390cc61..e5e4893aab 100644
--- 
a/core/src/test/java/org/apache/gravitino/connector/capability/TestCapability.java
+++ 
b/core/src/test/java/org/apache/gravitino/connector/capability/TestCapability.java
@@ -94,4 +94,12 @@ public class TestCapability {
       Assertions.assertTrue(result.unsupportedMessage().contains("is 
illegal"));
     }
   }
+
+  @Test
+  void testDefaultNormalizeNameMatchesCaseSensitiveOnName() {
+    for (Capability.Scope scope : Capability.Scope.values()) {
+      // The default capability is case-sensitive, so normalizeName must be a 
no-op.
+      Assertions.assertEquals("MixedCase", 
Capability.DEFAULT.normalizeName(scope, "MixedCase"));
+    }
+  }
 }

Reply via email to