dimas-b commented on code in PR #4023:
URL: https://github.com/apache/polaris/pull/4023#discussion_r3376500905


##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java:
##########
@@ -1937,6 +2002,88 @@ private String newTableMetadataFilePath(TableMetadata 
meta, int newVersion) {
     }
   }
 
+  /**
+   * Process the user-facing {@link #POLARIS_STORAGE_NAME_PROPERTY} on a write 
request: validate the
+   * value, gate against {@link 
FeatureConfiguration#ALLOW_STORAGE_NAME_OVERRIDE} when the value
+   * would change, write the resolved value into the internal-property map (or 
remove it when
+   * cleared), and strip the user-facing key from the user property map.
+   *
+   * <p>The flag is only enforced when the requested value would actually 
mutate the persisted
+   * {@code storage_name_override}. An idempotent re-set with the prior value 
is always allowed, so
+   * flipping the flag off does not break existing entities.
+   *
+   * <p>Property semantics:
+   *
+   * <ul>
+   *   <li>{@code polaris.storage.name=foo} (and {@code foo} is non-blank, 
valid) sets the override.
+   *   <li>{@code polaris.storage.name=""} (or whitespace) clears the override.
+   *   <li>Property absent from the user map: no change to the override.
+   * </ul>
+   *
+   * @param userProperties user-facing property map being persisted; the {@link
+   *     #POLARIS_STORAGE_NAME_PROPERTY} key is removed in place if present.
+   * @param internalProperties internal-property map that will be persisted 
onto the entity; the
+   *     {@code storage_name_override} key is set or removed in place based on 
the request.
+   * @param priorOverride the override value already persisted on the entity 
(or {@code null} for a
+   *     fresh create). Used to decide whether the request is an idempotent 
re-set.
+   */
+  @VisibleForTesting
+  void processStorageNameOverrideOnWrite(
+      Map<String, String> userProperties,
+      Map<String, String> internalProperties,
+      @Nullable String priorOverride) {

Review Comment:
   Is `priorOverride` actually an "override"? What does it override? 🤔 



##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java:
##########
@@ -1937,6 +2002,88 @@ private String newTableMetadataFilePath(TableMetadata 
meta, int newVersion) {
     }
   }
 
+  /**
+   * Process the user-facing {@link #POLARIS_STORAGE_NAME_PROPERTY} on a write 
request: validate the
+   * value, gate against {@link 
FeatureConfiguration#ALLOW_STORAGE_NAME_OVERRIDE} when the value
+   * would change, write the resolved value into the internal-property map (or 
remove it when
+   * cleared), and strip the user-facing key from the user property map.
+   *
+   * <p>The flag is only enforced when the requested value would actually 
mutate the persisted
+   * {@code storage_name_override}. An idempotent re-set with the prior value 
is always allowed, so
+   * flipping the flag off does not break existing entities.
+   *
+   * <p>Property semantics:
+   *
+   * <ul>
+   *   <li>{@code polaris.storage.name=foo} (and {@code foo} is non-blank, 
valid) sets the override.
+   *   <li>{@code polaris.storage.name=""} (or whitespace) clears the override.
+   *   <li>Property absent from the user map: no change to the override.
+   * </ul>
+   *
+   * @param userProperties user-facing property map being persisted; the {@link
+   *     #POLARIS_STORAGE_NAME_PROPERTY} key is removed in place if present.
+   * @param internalProperties internal-property map that will be persisted 
onto the entity; the
+   *     {@code storage_name_override} key is set or removed in place based on 
the request.
+   * @param priorOverride the override value already persisted on the entity 
(or {@code null} for a
+   *     fresh create). Used to decide whether the request is an idempotent 
re-set.
+   */
+  @VisibleForTesting
+  void processStorageNameOverrideOnWrite(
+      Map<String, String> userProperties,
+      Map<String, String> internalProperties,
+      @Nullable String priorOverride) {
+    if (!userProperties.containsKey(POLARIS_STORAGE_NAME_PROPERTY)) {
+      return;
+    }
+    String rawValue = userProperties.remove(POLARIS_STORAGE_NAME_PROPERTY);

Review Comment:
   Modifying parameters is not intuitive... Can we avoid this?



##########
polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigOverrideResolver.java:
##########
@@ -0,0 +1,83 @@
+/*
+ * 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.polaris.core.storage;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.polaris.core.entity.PolarisEntity;
+import org.apache.polaris.core.entity.PolarisEntityConstants;
+import org.jspecify.annotations.NonNull;
+
+/**
+ * Resolves the effective {@link PolarisStorageConfigurationInfo} for an 
entity hierarchy by
+ * applying any nearest-ancestor {@code storage_name_override} on top of the 
catalog's base storage
+ * configuration.
+ *
+ * <p>The input list is in root-to-leaf order (the catalog is at index 0). The 
walk runs
+ * leaf-to-root and:
+ *
+ * <ol>
+ *   <li>Captures the first {@code storage_name_override} encountered (nearest 
wins).
+ *   <li>Returns the first {@code storage_configuration_info} encountered — 
the base config.
+ * </ol>
+ *
+ * <p>If a base config exists and an override was captured, the result is 
{@link
+ * PolarisStorageConfigurationInfo#withStorageName} applied to the base. If no 
base config exists,
+ * the result is empty regardless of any override.
+ */
+public final class StorageConfigOverrideResolver {
+
+  private StorageConfigOverrideResolver() {}
+
+  /**
+   * Walk {@code entityPath} leaf-to-root and return the effective storage 
configuration. The base
+   * config is sourced from the nearest entity that has {@code 
storage_configuration_info} in its
+   * internal properties; if any closer ancestor (including the leaf itself) 
carries {@code
+   * storage_name_override}, that name replaces the base config's {@code 
storageName}.
+   *
+   * @param entityPath entities in root-to-leaf order; the catalog is at index 0
+   * @return effective config, or empty if no entity in the chain has a base 
storage config
+   */
+  public static Optional<PolarisStorageConfigurationInfo> 
resolveEffectiveConfig(
+      @NonNull List<? extends PolarisEntity> entityPath) {
+    String override = null;
+    for (int i = entityPath.size() - 1; i >= 0; i--) {
+      Map<String, String> internalProps = 
entityPath.get(i).getInternalPropertiesAsMap();

Review Comment:
   nit: we assume indexed access is efficient here... WDYT about 
`List.reversed()`?



##########
polaris-core/src/test/java/org/apache/polaris/core/storage/WithStorageNameTest.java:
##########
@@ -0,0 +1,154 @@
+/*
+ * 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.polaris.core.storage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo;
+import org.apache.polaris.core.storage.azure.AzureStorageConfigurationInfo;
+import org.apache.polaris.core.storage.gcp.GcpStorageConfigurationInfo;
+import org.junit.jupiter.api.Test;
+
+class WithStorageNameTest {

Review Comment:
   This test class name is a bit confusing IMHO 🤔 Why not add the new test 
cases to `PolarisStorageConfigurationInfoTest`?



##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java:
##########
@@ -1937,6 +2002,88 @@ private String newTableMetadataFilePath(TableMetadata 
meta, int newVersion) {
     }
   }
 
+  /**
+   * Process the user-facing {@link #POLARIS_STORAGE_NAME_PROPERTY} on a write 
request: validate the
+   * value, gate against {@link 
FeatureConfiguration#ALLOW_STORAGE_NAME_OVERRIDE} when the value
+   * would change, write the resolved value into the internal-property map (or 
remove it when
+   * cleared), and strip the user-facing key from the user property map.
+   *
+   * <p>The flag is only enforced when the requested value would actually 
mutate the persisted
+   * {@code storage_name_override}. An idempotent re-set with the prior value 
is always allowed, so
+   * flipping the flag off does not break existing entities.
+   *
+   * <p>Property semantics:
+   *
+   * <ul>
+   *   <li>{@code polaris.storage.name=foo} (and {@code foo} is non-blank, 
valid) sets the override.
+   *   <li>{@code polaris.storage.name=""} (or whitespace) clears the override.
+   *   <li>Property absent from the user map: no change to the override.
+   * </ul>
+   *
+   * @param userProperties user-facing property map being persisted; the {@link
+   *     #POLARIS_STORAGE_NAME_PROPERTY} key is removed in place if present.
+   * @param internalProperties internal-property map that will be persisted 
onto the entity; the
+   *     {@code storage_name_override} key is set or removed in place based on 
the request.
+   * @param priorOverride the override value already persisted on the entity 
(or {@code null} for a
+   *     fresh create). Used to decide whether the request is an idempotent 
re-set.
+   */
+  @VisibleForTesting
+  void processStorageNameOverrideOnWrite(
+      Map<String, String> userProperties,
+      Map<String, String> internalProperties,
+      @Nullable String priorOverride) {
+    if (!userProperties.containsKey(POLARIS_STORAGE_NAME_PROPERTY)) {
+      return;
+    }
+    String rawValue = userProperties.remove(POLARIS_STORAGE_NAME_PROPERTY);
+    String requested;
+    try {
+      requested = StorageNameValidator.normalizeAndValidate(rawValue);
+    } catch (IllegalArgumentException e) {
+      throw new BadRequestException(
+          e, "Invalid %s: %s", POLARIS_STORAGE_NAME_PROPERTY, e.getMessage());
+    }
+    boolean changing = !Objects.equal(priorOverride, requested);
+    if (changing && 
!realmConfig.getConfig(FeatureConfiguration.ALLOW_STORAGE_NAME_OVERRIDE)) {
+      throw new BadRequestException(
+          "Setting %s requires feature %s to be enabled",
+          POLARIS_STORAGE_NAME_PROPERTY, 
FeatureConfiguration.ALLOW_STORAGE_NAME_OVERRIDE.key());
+    }
+    String storageOverrideKey = 
PolarisEntityConstants.getStorageNameOverridePropertyName();
+    if (requested == null) {
+      internalProperties.remove(storageOverrideKey);
+    } else {
+      internalProperties.put(storageOverrideKey, requested);

Review Comment:
   This logic effectively make the table metadata control the storage name 
value on the server side. I'm not sure it is ideal.
   
   Ultimately, the catalog owns storage configuration. Why should table 
metadata have precedence over catalog-side settings?
   
   I'd imagine Polaris CLI being the tool for setting storage name 🤔 
   
   Perhaps we need to dive deeper into this on the `dev` ML 🤔 



##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalog.java:
##########
@@ -1937,6 +2002,88 @@ private String newTableMetadataFilePath(TableMetadata 
meta, int newVersion) {
     }
   }
 
+  /**
+   * Process the user-facing {@link #POLARIS_STORAGE_NAME_PROPERTY} on a write 
request: validate the
+   * value, gate against {@link 
FeatureConfiguration#ALLOW_STORAGE_NAME_OVERRIDE} when the value
+   * would change, write the resolved value into the internal-property map (or 
remove it when
+   * cleared), and strip the user-facing key from the user property map.
+   *
+   * <p>The flag is only enforced when the requested value would actually 
mutate the persisted
+   * {@code storage_name_override}. An idempotent re-set with the prior value 
is always allowed, so
+   * flipping the flag off does not break existing entities.
+   *
+   * <p>Property semantics:
+   *
+   * <ul>
+   *   <li>{@code polaris.storage.name=foo} (and {@code foo} is non-blank, 
valid) sets the override.
+   *   <li>{@code polaris.storage.name=""} (or whitespace) clears the override.
+   *   <li>Property absent from the user map: no change to the override.
+   * </ul>
+   *
+   * @param userProperties user-facing property map being persisted; the {@link
+   *     #POLARIS_STORAGE_NAME_PROPERTY} key is removed in place if present.
+   * @param internalProperties internal-property map that will be persisted 
onto the entity; the
+   *     {@code storage_name_override} key is set or removed in place based on 
the request.
+   * @param priorOverride the override value already persisted on the entity 
(or {@code null} for a
+   *     fresh create). Used to decide whether the request is an idempotent 
re-set.
+   */
+  @VisibleForTesting
+  void processStorageNameOverrideOnWrite(
+      Map<String, String> userProperties,
+      Map<String, String> internalProperties,
+      @Nullable String priorOverride) {
+    if (!userProperties.containsKey(POLARIS_STORAGE_NAME_PROPERTY)) {
+      return;
+    }
+    String rawValue = userProperties.remove(POLARIS_STORAGE_NAME_PROPERTY);
+    String requested;
+    try {
+      requested = StorageNameValidator.normalizeAndValidate(rawValue);
+    } catch (IllegalArgumentException e) {
+      throw new BadRequestException(

Review Comment:
   `IllegalArgumentException` should automatically result in a 400 response at 
the REST API layer... Why do we need `BadRequestException` here?



##########
polaris-core/src/main/java/org/apache/polaris/core/config/FeatureConfiguration.java:
##########
@@ -657,6 +657,18 @@ public static void enforceFeatureEnabledOrThrow(
           .defaultValue(false)
           .buildFeatureConfiguration();
 
+  public static final FeatureConfiguration<Boolean> 
ALLOW_STORAGE_NAME_OVERRIDE =
+      PolarisConfiguration.<Boolean>builder()
+          .key("ALLOW_STORAGE_NAME_OVERRIDE")
+          .description(
+              "If set to true, allows namespaces and tables to set the 
polaris.storage.name "

Review Comment:
   What is the value of this flag? If a users points to a non-existent storage 
name, the adverse effect is localized to that user's change.
   
   Adding storage names is a Catalog Management action and should probably be 
covered by AuthZ checks.
   
   I'd think setting/changing `polaris.storage.name` probably needs an new 
AuthZ operation rather than a global feature flag.
   
   WDYT?



##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java:
##########
@@ -18,35 +18,57 @@
  */
 package org.apache.polaris.service.catalog.io;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import org.apache.polaris.core.entity.PolarisEntity;
 import org.apache.polaris.core.entity.PolarisEntityConstants;
 import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper;
+import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo;
+import org.apache.polaris.core.storage.StorageConfigOverrideResolver;
 
 public class FileIOUtil {
 
   private FileIOUtil() {}
 
   /**
-   * Finds storage configuration information in the hierarchy of the resolved 
storage entity.
+   * Finds storage configuration information in the hierarchy of the resolved 
storage entity, with
+   * any nearest-ancestor {@code storage_name_override} applied to the 
catalog's base config.
    *
-   * <p>This method starts at the "leaf" level (e.g., table) and walks 
"upwards" through namespaces
-   * in the hierarchy to the "root." It searches for the first entity 
containing storage config
-   * properties, identified using a key from {@link
-   * PolarisEntityConstants#getStorageConfigInfoPropertyName()}.
+   * <p>This method walks the path leaf-to-root, locates the first entity 
carrying {@code
+   * storage_configuration_info} (the catalog), and — if any closer ancestor 
carries a {@code
+   * storage_name_override} — returns a synthetic copy of the base entity 
whose serialized
+   * configuration has its {@code storageName} replaced. Callers that read the 
resulting entity's
+   * internal properties (e.g., to stash on a purge task) therefore see the 
effective config.
    *
    * @param resolvedStorageEntity the resolved entity wrapper containing the 
hierarchical path
-   * @return an {@link Optional} containing the entity with storage config, or 
empty if not found
+   * @return an {@link Optional} containing the (possibly synthetic) entity 
with the effective
+   *     storage config, or empty if no entity in the chain has a base storage 
config
    */
   public static Optional<PolarisEntity> findStorageInfoFromHierarchy(
       PolarisResolvedPathWrapper resolvedStorageEntity) {
-    Optional<PolarisEntity> storageInfoEntity =
-        resolvedStorageEntity.getRawFullPath().reversed().stream()
+    List<PolarisEntity> path = resolvedStorageEntity.getRawFullPath();
+    Optional<PolarisStorageConfigurationInfo> effective =
+        StorageConfigOverrideResolver.resolveEffectiveConfig(path);
+    if (effective.isEmpty()) {
+      return Optional.empty();
+    }
+    Optional<PolarisEntity> baseEntity =
+        path.reversed().stream()
             .filter(
                 e ->
                     e.getInternalPropertiesAsMap()
                         
.containsKey(PolarisEntityConstants.getStorageConfigInfoPropertyName()))
             .findFirst();
-    return storageInfoEntity;
+    if (baseEntity.isEmpty()) {

Review Comment:
   Why repeat the search already performed by 
`StorageConfigOverrideResolver.resolveEffectiveConfig()`? Can we be sure the 
outcome is / will be the same?
   
   Why not have `StorageConfigOverrideResolver` return a tuple of all the data 
we need here?



##########
polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigOverrideResolver.java:
##########
@@ -0,0 +1,83 @@
+/*
+ * 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.polaris.core.storage;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.polaris.core.entity.PolarisEntity;
+import org.apache.polaris.core.entity.PolarisEntityConstants;
+import org.jspecify.annotations.NonNull;
+
+/**
+ * Resolves the effective {@link PolarisStorageConfigurationInfo} for an 
entity hierarchy by
+ * applying any nearest-ancestor {@code storage_name_override} on top of the 
catalog's base storage
+ * configuration.
+ *
+ * <p>The input list is in root-to-leaf order (the catalog is at index 0). The 
walk runs
+ * leaf-to-root and:
+ *
+ * <ol>
+ *   <li>Captures the first {@code storage_name_override} encountered (nearest 
wins).
+ *   <li>Returns the first {@code storage_configuration_info} encountered — 
the base config.
+ * </ol>
+ *
+ * <p>If a base config exists and an override was captured, the result is 
{@link
+ * PolarisStorageConfigurationInfo#withStorageName} applied to the base. If no 
base config exists,
+ * the result is empty regardless of any override.
+ */
+public final class StorageConfigOverrideResolver {
+
+  private StorageConfigOverrideResolver() {}
+
+  /**
+   * Walk {@code entityPath} leaf-to-root and return the effective storage 
configuration. The base
+   * config is sourced from the nearest entity that has {@code 
storage_configuration_info} in its
+   * internal properties; if any closer ancestor (including the leaf itself) 
carries {@code
+   * storage_name_override}, that name replaces the base config's {@code 
storageName}.
+   *
+   * @param entityPath entities in root-to-leaf order; the catalog is at index 0
+   * @return effective config, or empty if no entity in the chain has a base 
storage config
+   */
+  public static Optional<PolarisStorageConfigurationInfo> 
resolveEffectiveConfig(
+      @NonNull List<? extends PolarisEntity> entityPath) {
+    String override = null;
+    for (int i = entityPath.size() - 1; i >= 0; i--) {
+      Map<String, String> internalProps = 
entityPath.get(i).getInternalPropertiesAsMap();
+      if (override == null) {
+        String candidate =
+            
internalProps.get(PolarisEntityConstants.getStorageNameOverridePropertyName());
+        if (candidate != null && !candidate.isEmpty()) {
+          override = candidate;
+        }
+      }
+      String serializedBase =
+          
internalProps.get(PolarisEntityConstants.getStorageConfigInfoPropertyName());
+      if (serializedBase != null) {
+        PolarisStorageConfigurationInfo base =
+            PolarisStorageConfigurationInfo.deserialize(serializedBase);
+        if (override != null) {
+          return 
Optional.of(PolarisStorageConfigurationInfo.withStorageName(base, override));

Review Comment:
   I'm not sure I understand the purpose of reusing the same config with a 
different name in different sub-trees 🤔 How do you envision this to be used?
   
   My impression from the ML 
[discussion](https://lists.apache.org/thread/nwcc8krhvm35olgc22676mm12cjkpgn5) 
is that the idea was to define multiple distinct Storage Configurations at the 
Catalog level and let entities choose one of them by name 🤔 



##########
runtime/service/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java:
##########
@@ -18,35 +18,57 @@
  */
 package org.apache.polaris.service.catalog.io;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import org.apache.polaris.core.entity.PolarisEntity;
 import org.apache.polaris.core.entity.PolarisEntityConstants;
 import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper;
+import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo;
+import org.apache.polaris.core.storage.StorageConfigOverrideResolver;
 
 public class FileIOUtil {
 
   private FileIOUtil() {}
 
   /**
-   * Finds storage configuration information in the hierarchy of the resolved 
storage entity.
+   * Finds storage configuration information in the hierarchy of the resolved 
storage entity, with
+   * any nearest-ancestor {@code storage_name_override} applied to the 
catalog's base config.
    *
-   * <p>This method starts at the "leaf" level (e.g., table) and walks 
"upwards" through namespaces
-   * in the hierarchy to the "root." It searches for the first entity 
containing storage config
-   * properties, identified using a key from {@link
-   * PolarisEntityConstants#getStorageConfigInfoPropertyName()}.
+   * <p>This method walks the path leaf-to-root, locates the first entity 
carrying {@code
+   * storage_configuration_info} (the catalog), and — if any closer ancestor 
carries a {@code
+   * storage_name_override} — returns a synthetic copy of the base entity 
whose serialized
+   * configuration has its {@code storageName} replaced. Callers that read the 
resulting entity's
+   * internal properties (e.g., to stash on a purge task) therefore see the 
effective config.
    *
    * @param resolvedStorageEntity the resolved entity wrapper containing the 
hierarchical path
-   * @return an {@link Optional} containing the entity with storage config, or 
empty if not found
+   * @return an {@link Optional} containing the (possibly synthetic) entity 
with the effective
+   *     storage config, or empty if no entity in the chain has a base storage 
config

Review Comment:
   Callers of this method only need to know the presence of this entity and if 
present, its properties... Why bother with synthetic entities? I'd propose to 
refactor this code to return an `Optional<some properties classs>`... WDYT?



##########
polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java:
##########
@@ -89,6 +92,44 @@ public boolean validatePrefix() {
 
   public abstract StorageType getStorageType();
 
+  /**
+   * Return a copy of {@code base} with its {@code storageName} replaced by 
{@code storageName}. All
+   * other fields are preserved. Used by the storage-name override path so 
that an entity-level
+   * override flows through credential vending and into the per-backend cache 
key without mutating
+   * the original config.
+   *
+   * @throws IllegalArgumentException if {@code base} is not one of the four 
supported subtypes
+   */
+  public static PolarisStorageConfigurationInfo withStorageName(
+      PolarisStorageConfigurationInfo base, @Nullable String storageName) {
+    if (base instanceof AwsStorageConfigurationInfo aws) {
+      return ImmutableAwsStorageConfigurationInfo.builder()

Review Comment:
   I'd propose to make an polymorphic `.toBuilder()` instance method and then 
do `base.toBuilder().storageName(storageName).build()` without `instanceof` 
checks.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to