From 52faa776f2fd9d345a3fe7294def1632224021c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BE=8A=E5=B7=9D?= Date: Fri, 24 Jul 2026 17:57:28 +0800 Subject: [PATCH 1/2] fix: use case-sensitive option keys in BaseLanceNamespaceSparkCatalog.initialize() CaseInsensitiveStringMap lowercases all keys when used directly, causing case-sensitive storage backend configs to fail. Use asCaseSensitiveMap() to preserve original key casing, consistent with other code paths. Closes #717 --- .../java/org/lance/spark/BaseLanceNamespaceSparkCatalog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lance-spark-base_2.12/src/main/java/org/lance/spark/BaseLanceNamespaceSparkCatalog.java b/lance-spark-base_2.12/src/main/java/org/lance/spark/BaseLanceNamespaceSparkCatalog.java index 435e1b6eb..fd8daf8e8 100644 --- a/lance-spark-base_2.12/src/main/java/org/lance/spark/BaseLanceNamespaceSparkCatalog.java +++ b/lance-spark-base_2.12/src/main/java/org/lance/spark/BaseLanceNamespaceSparkCatalog.java @@ -234,7 +234,7 @@ public void initialize(String name, CaseInsensitiveStringMap options) { } // Initialize the namespace with proper configuration - Map namespaceOptions = new HashMap<>(options); + Map namespaceOptions = new HashMap<>(options.asCaseSensitiveMap()); // Save namespace impl and properties for serialization to workers this.namespaceImpl = impl; From e2df67108b644581a2632e970ab7b2539c23e9e8 Mon Sep 17 00:00:00 2001 From: Ionut Scheianu <54133156+ivscheianu@users.noreply.github.com> Date: Tue, 28 Jul 2026 05:00:22 +0300 Subject: [PATCH 2/2] test: add unit test for case-sensitive option key preservation in catalog initialize (#1) --- ...ceNamespaceSparkCatalogInitializeTest.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 lance-spark-base_2.12/src/test/java/org/lance/spark/BaseLanceNamespaceSparkCatalogInitializeTest.java diff --git a/lance-spark-base_2.12/src/test/java/org/lance/spark/BaseLanceNamespaceSparkCatalogInitializeTest.java b/lance-spark-base_2.12/src/test/java/org/lance/spark/BaseLanceNamespaceSparkCatalogInitializeTest.java new file mode 100644 index 000000000..959915c65 --- /dev/null +++ b/lance-spark-base_2.12/src/test/java/org/lance/spark/BaseLanceNamespaceSparkCatalogInitializeTest.java @@ -0,0 +1,96 @@ +/* + * Licensed 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.lance.spark; + +import org.lance.memwal.ShardingSpec; +import org.lance.spark.write.StagedCommit; + +import org.apache.spark.sql.types.StructType; +import org.apache.spark.sql.util.CaseInsensitiveStringMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BaseLanceNamespaceSparkCatalogInitializeTest { + + @TempDir private Path tempDir; + + @Test + public void testInitializePreservesCaseSensitiveOptionKeys() { + Map rawOptions = new HashMap<>(); + rawOptions.put("impl", "dir"); + rawOptions.put("root", tempDir.toString()); + rawOptions.put("MyMixedCaseKey", "value1"); + rawOptions.put("ALLCAPS_KEY", "value2"); + rawOptions.put("camelCaseOption", "value3"); + + CaseInsensitiveStringMap options = new CaseInsensitiveStringMap(rawOptions); + + TestCatalog catalog = new TestCatalog(); + catalog.initialize("test", options); + + Map properties = catalog.getNamespaceProperties(); + + assertTrue( + properties.containsKey("MyMixedCaseKey"), + "Should preserve mixed-case key 'MyMixedCaseKey', got keys: " + properties.keySet()); + assertTrue( + properties.containsKey("ALLCAPS_KEY"), + "Should preserve all-caps key 'ALLCAPS_KEY', got keys: " + properties.keySet()); + assertTrue( + properties.containsKey("camelCaseOption"), + "Should preserve camelCase key 'camelCaseOption', got keys: " + properties.keySet()); + + assertEquals("value1", properties.get("MyMixedCaseKey")); + assertEquals("value2", properties.get("ALLCAPS_KEY")); + assertEquals("value3", properties.get("camelCaseOption")); + } + + private static class TestCatalog extends BaseLanceNamespaceSparkCatalog { + @Override + public LanceDataset createDataset( + LanceSparkReadOptions readOptions, + StructType sparkSchema, + Map initialStorageOptions, + String namespaceImpl, + Map namespaceProperties, + boolean managedVersioning, + String fileFormatVersion, + Map tableProperties, + ShardingSpec shardingSpec) { + return null; + } + + @Override + public LanceDataset createStagedDataset( + LanceSparkReadOptions readOptions, + StructType sparkSchema, + Map initialStorageOptions, + String namespaceImpl, + Map namespaceProperties, + boolean managedVersioning, + StagedCommit stagedCommit, + String fileFormatVersion, + Map tableProperties, + ShardingSpec shardingSpec) { + return null; + } + } +}