You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polaris.apache.org by ad...@apache.org on 2026/05/21 12:08:36 UTC
(polaris) branch main updated: Replace deprecated encodeNamespace and decodeNamespace methods (cherry-pick of #3713) (#4508)
This is an automated email from the ASF dual-hosted git repository.
adutra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new f2eb8ad225 Replace deprecated encodeNamespace and decodeNamespace methods (cherry-pick of #3713) (#4508)
f2eb8ad225 is described below
commit f2eb8ad225d7ef8a50cbf53bf582365e75027334
Author: Alexandre Dutra <ad...@apache.org>
AuthorDate: Thu May 21 14:08:30 2026 +0200
Replace deprecated encodeNamespace and decodeNamespace methods (cherry-pick of #3713) (#4508)
---
.../test/PolarisPolicyServiceIntegrationTest.java | 8 +++--
.../it/test/PolarisRestCatalogIntegrationBase.java | 29 +++++++++++------
.../spark/v3.5/regtests/suites/spark_sql_delta.ref | 2 +-
.../spark/v3.5/regtests/suites/spark_sql_hudi.ref | 2 +-
.../polaris/core/entity/PolarisEntityUtils.java | 4 +--
.../apache/polaris/core/rest/NamespaceUtils.java | 13 ++++++++
.../polaris/core/rest/PolarisResourcePaths.java | 38 +++++++++++++++++++---
.../core/entity/PolarisEntityUtilsTest.java | 2 ++
.../t_spark_sql/ref/spark_sql_azure_blob.sh.ref | 2 +-
.../t_spark_sql/ref/spark_sql_azure_dfs.sh.ref | 2 +-
regtests/t_spark_sql/ref/spark_sql_basic.sh.ref | 2 +-
regtests/t_spark_sql/ref/spark_sql_gcp.sh.ref | 2 +-
regtests/t_spark_sql/ref/spark_sql_s3.sh.ref | 2 +-
regtests/t_spark_sql/ref/spark_sql_views.sh.ref | 2 +-
.../catalog/iceberg/IcebergCatalogAdapter.java | 8 +++--
.../catalog/iceberg/IcebergCatalogHandler.java | 12 +++++--
16 files changed, 98 insertions(+), 32 deletions(-)
diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisPolicyServiceIntegrationTest.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisPolicyServiceIntegrationTest.java
index f8016508a9..1389d019a8 100644
--- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisPolicyServiceIntegrationTest.java
+++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisPolicyServiceIntegrationTest.java
@@ -38,7 +38,6 @@ import org.apache.iceberg.Schema;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.rest.RESTCatalog;
-import org.apache.iceberg.rest.RESTUtil;
import org.apache.iceberg.types.Types;
import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
import org.apache.polaris.core.admin.model.Catalog;
@@ -61,6 +60,7 @@ import org.apache.polaris.core.admin.model.TablePrivilege;
import org.apache.polaris.core.catalog.PolarisCatalogHelpers;
import org.apache.polaris.core.policy.PredefinedPolicyTypes;
import org.apache.polaris.core.policy.exceptions.PolicyInUseException;
+import org.apache.polaris.core.rest.NamespaceUtils;
import org.apache.polaris.service.it.env.CatalogConfig;
import org.apache.polaris.service.it.env.ClientCredentials;
import org.apache.polaris.service.it.env.IcebergHelper;
@@ -109,7 +109,7 @@ public class PolarisPolicyServiceIntegrationTest {
private static final PolicyIdentifier NS1_P3 = new PolicyIdentifier(NS1, "P3");
private static final TableIdentifier NS2_T1 = TableIdentifier.of(NS2, "T1");
- private static final String NS1_NAME = RESTUtil.encodeNamespace(NS1);
+ private static final String NS1_NAME = NS1.level(0);
private static final String INVALID_NAMESPACE = "INVALID_NAMESPACE";
private static final String INVALID_POLICY = "INVALID_POLICY";
private static final String INVALID_TABLE = "INVALID_TABLE";
@@ -290,7 +290,9 @@ public class PolarisPolicyServiceIntegrationTest {
restCatalog.createNamespace(NS1);
PolicyIdentifier policyIdentifier = new PolicyIdentifier(NS1, policyName);
- String ns = RESTUtil.encodeNamespace(policyIdentifier.namespace());
+ String ns =
+ NamespaceUtils.joinNamespace(
+ policyIdentifier.namespace(), NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
CreatePolicyRequest request =
CreatePolicyRequest.builder()
.setType(PredefinedPolicyTypes.DATA_COMPACTION.getName())
diff --git a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationBase.java b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationBase.java
index f879d81d90..0f02e3f8a7 100644
--- a/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationBase.java
+++ b/integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisRestCatalogIntegrationBase.java
@@ -97,6 +97,7 @@ import org.apache.polaris.core.admin.model.ViewGrant;
import org.apache.polaris.core.admin.model.ViewPrivilege;
import org.apache.polaris.core.config.FeatureConfiguration;
import org.apache.polaris.core.entity.PolarisEntityConstants;
+import org.apache.polaris.core.rest.NamespaceUtils;
import org.apache.polaris.service.it.env.CatalogApi;
import org.apache.polaris.service.it.env.CatalogConfig;
import org.apache.polaris.service.it.env.ClientCredentials;
@@ -1669,7 +1670,9 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
TableIdentifier tableIdentifier = TableIdentifier.of(namespace, "tbl1");
try {
- String ns = RESTUtil.encodeNamespace(tableIdentifier.namespace());
+ String ns =
+ NamespaceUtils.joinNamespace(
+ tableIdentifier.namespace(), NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
try (Response res =
genericTableApi
.request(
@@ -1750,7 +1753,9 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
restCatalog.createNamespace(namespace);
TableIdentifier tableIdentifier = TableIdentifier.of(namespace, "tbl1");
- String ns = RESTUtil.encodeNamespace(tableIdentifier.namespace());
+ String ns =
+ NamespaceUtils.joinNamespace(
+ tableIdentifier.namespace(), NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
try (Response res =
genericTableApi
.request(
@@ -2454,12 +2459,13 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
Namespace ns = Namespace.of("ns_create_table_bad");
restCatalog.createNamespace(ns);
try {
- String nsEncoded = RESTUtil.encodeNamespace(ns);
+ String nsJoined =
+ NamespaceUtils.joinNamespace(ns, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
try (Response res =
catalogApi
.request(
"v1/{cat}/namespaces/{ns}/tables",
- Map.of("cat", currentCatalogName, "ns", nsEncoded))
+ Map.of("cat", currentCatalogName, "ns", nsJoined))
.post(Entity.json(Map.of("name", badName, "schema", SCHEMA)))) {
assertThat(res.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode());
assertThat(res.readEntity(String.class)).contains("Entity name");
@@ -2475,12 +2481,13 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
Namespace ns = Namespace.of("ns_register_bad");
restCatalog.createNamespace(ns);
try {
- String nsEncoded = RESTUtil.encodeNamespace(ns);
+ String nsJoined =
+ NamespaceUtils.joinNamespace(ns, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
try (Response res =
catalogApi
.request(
"v1/{cat}/namespaces/{ns}/register",
- Map.of("cat", currentCatalogName, "ns", nsEncoded))
+ Map.of("cat", currentCatalogName, "ns", nsJoined))
.post(
Entity.json(
Map.of("name", badName, "metadata-location", "file:/tmp/nowhere.json")))) {
@@ -2498,7 +2505,8 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
Namespace ns = Namespace.of("ns_create_view_bad");
restCatalog.createNamespace(ns);
try {
- String nsEncoded = RESTUtil.encodeNamespace(ns);
+ String nsJoined =
+ NamespaceUtils.joinNamespace(ns, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
Map<String, Object> viewVersion =
Map.of(
"version-id",
@@ -2517,7 +2525,7 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
catalogApi
.request(
"v1/{cat}/namespaces/{ns}/views",
- Map.of("cat", currentCatalogName, "ns", nsEncoded))
+ Map.of("cat", currentCatalogName, "ns", nsJoined))
.post(
Entity.json(
Map.of("name", badName, "schema", SCHEMA, "view-version", viewVersion)))) {
@@ -2588,12 +2596,13 @@ public abstract class PolarisRestCatalogIntegrationBase extends CatalogTests<RES
Namespace ns = Namespace.of("ns_generic_bad");
restCatalog.createNamespace(ns);
try {
- String nsEncoded = RESTUtil.encodeNamespace(ns);
+ String nsJoined =
+ NamespaceUtils.joinNamespace(ns, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
try (Response res =
genericTableApi
.request(
"polaris/v1/{cat}/namespaces/{ns}/generic-tables",
- Map.of("cat", currentCatalogName, "ns", nsEncoded))
+ Map.of("cat", currentCatalogName, "ns", nsJoined))
.post(
Entity.json(
CreateGenericTableRequest.builder()
diff --git a/plugins/spark/v3.5/regtests/suites/spark_sql_delta.ref b/plugins/spark/v3.5/regtests/suites/spark_sql_delta.ref
index 01aa77e6d1..5daf5d344e 100755
--- a/plugins/spark/v3.5/regtests/suites/spark_sql_delta.ref
+++ b/plugins/spark/v3.5/regtests/suites/spark_sql_delta.ref
@@ -1,4 +1,4 @@
-{"defaults":{"default-base-location":"file:///tmp/spark_catalog"},"overrides":{"prefix":"spark_sql_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{namespace}/tables/{table}","HEAD /v1/{prefix}/ [...]
+{"defaults":{"default-base-location":"file:///tmp/spark_catalog"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{namespace}/tables/ [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> create namespace db1;
diff --git a/plugins/spark/v3.5/regtests/suites/spark_sql_hudi.ref b/plugins/spark/v3.5/regtests/suites/spark_sql_hudi.ref
index a40b4ddac9..df9179e645 100644
--- a/plugins/spark/v3.5/regtests/suites/spark_sql_hudi.ref
+++ b/plugins/spark/v3.5/regtests/suites/spark_sql_hudi.ref
@@ -1,4 +1,4 @@
-{"defaults":{"default-base-location":"file:///tmp/spark_hudi_catalog"},"overrides":{"prefix":"spark_hudi_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{namespace}/tables/{table}","HEAD /v1/{pr [...]
+{"defaults":{"default-base-location":"file:///tmp/spark_hudi_catalog"},"overrides":{"namespace-separator":"%1F","prefix":"spark_hudi_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{namespace}/t [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> create namespace hudi_db1;
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityUtils.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityUtils.java
index e9a2132c5d..2dd360172b 100644
--- a/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityUtils.java
+++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityUtils.java
@@ -52,7 +52,7 @@ public final class PolarisEntityUtils {
* Returns a String representation of a namespace that is suitable for storage in entity internal
* properties.
*
- * <p>This method is similar to {@link RESTUtil#encodeNamespace(Namespace)}.
+ * <p>This method is similar to {@link RESTUtil#encodeNamespace(Namespace, String)}.
*/
public static String encodeNamespace(Namespace ns) {
Preconditions.checkArgument(ns != null, "Invalid namespace: null");
@@ -68,7 +68,7 @@ public final class PolarisEntityUtils {
* Returns a Namespace object from a String representation that was encoded using {@link
* #encodeNamespace(Namespace)}.
*
- * <p>This method is similar to {@link RESTUtil#decodeNamespace(String)}.
+ * <p>This method is similar to {@link RESTUtil#decodeNamespace(String, String)}.
*/
public static Namespace decodeNamespace(String encodedNs) {
Preconditions.checkArgument(encodedNs != null, "Invalid namespace: null");
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/rest/NamespaceUtils.java b/polaris-core/src/main/java/org/apache/polaris/core/rest/NamespaceUtils.java
index 3de87edf06..64c06b5b72 100644
--- a/polaris-core/src/main/java/org/apache/polaris/core/rest/NamespaceUtils.java
+++ b/polaris-core/src/main/java/org/apache/polaris/core/rest/NamespaceUtils.java
@@ -35,6 +35,19 @@ public final class NamespaceUtils {
*/
public static final String DEFAULT_NAMESPACE_SEPARATOR = "\u001f";
+ /**
+ * The default namespace separator, percent-encoded to "%1F".
+ *
+ * <p>This is the same encoded separator declared in Iceberg's {@link
+ * org.apache.iceberg.rest.RESTUtil} class, but it's package-private there and cannot be
+ * referenced.
+ *
+ * <p>This value is also exposed in {@link
+ * org.apache.iceberg.rest.RESTCatalogProperties#NAMESPACE_SEPARATOR_DEFAULT} – but here we avoid
+ * referencing that field directly since we don't own it.
+ */
+ public static final String DEFAULT_NAMESPACE_SEPARATOR_ENCODED = "%1F";
+
private NamespaceUtils() {}
/** Joins the levels of a namespace into a single string using the given {@code separator}. */
diff --git a/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
index 16eea08da2..87e29bbd99 100644
--- a/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
+++ b/polaris-core/src/main/java/org/apache/polaris/core/rest/PolarisResourcePaths.java
@@ -22,9 +22,15 @@ import com.google.common.base.Joiner;
import java.util.Map;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.rest.RESTCatalogProperties;
import org.apache.iceberg.rest.RESTUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class PolarisResourcePaths {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PolarisResourcePaths.class);
+
private static final Joiner SLASH = Joiner.on("/").skipNulls();
public static final String PREFIX = "prefix";
@@ -43,18 +49,34 @@ public class PolarisResourcePaths {
public static final String V1_APPLICABLE_POLICIES = "/polaris/v1/{prefix}/applicable-policies";
private final String prefix;
+ private final String namespaceSeparatorEncoded;
- public PolarisResourcePaths(String prefix) {
+ public PolarisResourcePaths(String prefix, String namespaceSeparatorEncoded) {
this.prefix = prefix;
+ this.namespaceSeparatorEncoded = namespaceSeparatorEncoded;
}
public static PolarisResourcePaths forCatalogProperties(Map<String, String> properties) {
- return new PolarisResourcePaths(properties.get(PREFIX));
+ String namespaceSeparatorEncoded =
+ properties.getOrDefault(
+ RESTCatalogProperties.NAMESPACE_SEPARATOR,
+ RESTCatalogProperties.NAMESPACE_SEPARATOR_DEFAULT);
+ if (!namespaceSeparatorEncoded.equals(RESTCatalogProperties.NAMESPACE_SEPARATOR_DEFAULT)) {
+ LOGGER.warn("Using non-default namespace separator '{}'", namespaceSeparatorEncoded);
+ }
+ return new PolarisResourcePaths(properties.get(PREFIX), namespaceSeparatorEncoded);
}
public String genericTables(Namespace ns) {
return SLASH.join(
- "polaris", "v1", prefix, "namespaces", RESTUtil.encodeNamespace(ns), "generic-tables");
+ "polaris",
+ "v1",
+ prefix,
+ "namespaces",
+ // FIXME use RESTUtil.encodeNamespaceAsPathSegment(), see
+ // https://github.com/apache/iceberg/pull/15989
+ RESTUtil.encodeNamespace(ns, namespaceSeparatorEncoded),
+ "generic-tables");
}
public String credentialsPath(TableIdentifier ident) {
@@ -62,8 +84,11 @@ public class PolarisResourcePaths {
"v1",
prefix,
"namespaces",
- RESTUtil.encodeNamespace(ident.namespace()),
+ // FIXME use RESTUtil.encodeNamespaceAsPathSegment(), see
+ // https://github.com/apache/iceberg/pull/15989
+ RESTUtil.encodeNamespace(ident.namespace(), namespaceSeparatorEncoded),
"tables",
+ // FIXME use RESTUtil.encodePathSegment(), see https://github.com/apache/iceberg/pull/15989
RESTUtil.encodeString(ident.name()),
"credentials");
}
@@ -74,8 +99,11 @@ public class PolarisResourcePaths {
"v1",
prefix,
"namespaces",
- RESTUtil.encodeNamespace(ident.namespace()),
+ // FIXME use RESTUtil.encodeNamespaceAsPathSegment(), see
+ // https://github.com/apache/iceberg/pull/15989
+ RESTUtil.encodeNamespace(ident.namespace(), namespaceSeparatorEncoded),
"generic-tables",
+ // FIXME use RESTUtil.encodePathSegment(), see https://github.com/apache/iceberg/pull/15989
RESTUtil.encodeString(ident.name()));
}
}
diff --git a/polaris-core/src/test/java/org/apache/polaris/core/entity/PolarisEntityUtilsTest.java b/polaris-core/src/test/java/org/apache/polaris/core/entity/PolarisEntityUtilsTest.java
index 21400389f2..713993ee47 100644
--- a/polaris-core/src/test/java/org/apache/polaris/core/entity/PolarisEntityUtilsTest.java
+++ b/polaris-core/src/test/java/org/apache/polaris/core/entity/PolarisEntityUtilsTest.java
@@ -52,6 +52,7 @@ public class PolarisEntityUtilsTest {
@ParameterizedTest
@MethodSource("encodeNamespaceCases")
+ @SuppressWarnings("deprecation")
public void testEncodeNamespace(Namespace ns, String expected) {
assertThat(PolarisEntityUtils.encodeNamespace(ns))
.isEqualTo(RESTUtil.encodeNamespace(ns))
@@ -86,6 +87,7 @@ public class PolarisEntityUtilsTest {
@ParameterizedTest
@MethodSource("decodeNamespaceCases")
+ @SuppressWarnings("deprecation")
public void testDecodeNamespace(String encoded, Namespace expected) {
assertThat(PolarisEntityUtils.decodeNamespace(encoded))
.isEqualTo(RESTUtil.decodeNamespace(encoded))
diff --git a/regtests/t_spark_sql/ref/spark_sql_azure_blob.sh.ref b/regtests/t_spark_sql/ref/spark_sql_azure_blob.sh.ref
index a6d831f814..0b95ef5807 100755
--- a/regtests/t_spark_sql/ref/spark_sql_azure_blob.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_azure_blob.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"abfss://polaris-container@polarisadls.blob.core.windows.net/polaris-test/spark_sql_blob_catalog/"},"overrides":{"prefix":"spark_sql_azure_blob_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{ [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"abfss://polaris-container@polarisadls.blob.core.windows.net/polaris-test/spark_sql_blob_catalog/"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_azure_blob_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namesp [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/regtests/t_spark_sql/ref/spark_sql_azure_dfs.sh.ref b/regtests/t_spark_sql/ref/spark_sql_azure_dfs.sh.ref
index b37ef2603f..d0d7e51ae9 100755
--- a/regtests/t_spark_sql/ref/spark_sql_azure_dfs.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_azure_dfs.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"abfss://polaris-container@polarisadls.dfs.core.windows.net/polaris-test/spark_sql_dfs_catalog/"},"overrides":{"prefix":"spark_sql_azure_dfs_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{pre [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"abfss://polaris-container@polarisadls.dfs.core.windows.net/polaris-test/spark_sql_dfs_catalog/"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_azure_dfs_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespace [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/regtests/t_spark_sql/ref/spark_sql_basic.sh.ref b/regtests/t_spark_sql/ref/spark_sql_basic.sh.ref
index d9657c7482..f129c395fe 100755
--- a/regtests/t_spark_sql/ref/spark_sql_basic.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_basic.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"file:///tmp/spark_sql_s3_catalog"},"overrides":{"prefix":"spark_sql_basic_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{ [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"file:///tmp/spark_sql_s3_catalog"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_basic_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","G [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/regtests/t_spark_sql/ref/spark_sql_gcp.sh.ref b/regtests/t_spark_sql/ref/spark_sql_gcp.sh.ref
index 114169d38d..207ea61c8f 100755
--- a/regtests/t_spark_sql/ref/spark_sql_gcp.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_gcp.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"gs://polaris-test1/polaris_test/spark_sql_gcp_catalog/"},"overrides":{"prefix":"spark_sql_gcp_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{ [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"gs://polaris-test1/polaris_test/spark_sql_gcp_catalog/"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_gcp_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{n [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/regtests/t_spark_sql/ref/spark_sql_s3.sh.ref b/regtests/t_spark_sql/ref/spark_sql_s3.sh.ref
index b55f1648c6..bee0562503 100755
--- a/regtests/t_spark_sql/ref/spark_sql_s3.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_s3.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"s3://test_bucket/polaris_test/spark_sql_s3_catalog"},"overrides":{"prefix":"spark_sql_s3_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefi [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"s3://test_bucket/polaris_test/spark_sql_s3_catalog"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_s3_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namesp [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/regtests/t_spark_sql/ref/spark_sql_views.sh.ref b/regtests/t_spark_sql/ref/spark_sql_views.sh.ref
index cf2b22d196..3ecfb05995 100755
--- a/regtests/t_spark_sql/ref/spark_sql_views.sh.ref
+++ b/regtests/t_spark_sql/ref/spark_sql_views.sh.ref
@@ -1,4 +1,4 @@
-{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"file:///tmp/spark_sql_s3_catalog"},"overrides":{"prefix":"spark_sql_views_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{ [...]
+{"defaults":{"drop-with-purge.enabled":"true","default-base-location":"file:///tmp/spark_sql_s3_catalog"},"overrides":{"namespace-separator":"%1F","prefix":"spark_sql_views_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","G [...]
Catalog created
spark-sql (default)> use polaris;
spark-sql ()> show namespaces;
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
index f48de53c67..f38efeb448 100644
--- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java
@@ -370,7 +370,9 @@ public class IcebergCatalogAdapter
String prefix,
TableIdentifier tableIdentifier) {
return delegationModes.contains(AccessDelegationMode.VENDED_CREDENTIALS)
- ? Optional.of(new PolarisResourcePaths(prefix).credentialsPath(tableIdentifier))
+ ? Optional.of(
+ new PolarisResourcePaths(prefix, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR_ENCODED)
+ .credentialsPath(tableIdentifier))
: Optional.empty();
}
@@ -548,7 +550,9 @@ public class IcebergCatalogAdapter
NamespaceUtils.splitNamespace(namespace, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR);
TableIdentifier tableIdentifier = TableIdentifier.of(ns, table);
Optional<String> refreshEndpoint =
- Optional.of(new PolarisResourcePaths(prefix).credentialsPath(tableIdentifier));
+ Optional.of(
+ new PolarisResourcePaths(prefix, NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR_ENCODED)
+ .credentialsPath(tableIdentifier));
return withCatalog(
securityContext,
prefix,
diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
index 575a0010ad..46c0af5915 100644
--- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
+++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java
@@ -70,6 +70,7 @@ import org.apache.iceberg.exceptions.NoSuchTableException;
import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.metrics.ScanReport;
import org.apache.iceberg.rest.Endpoint;
+import org.apache.iceberg.rest.RESTCatalogProperties;
import org.apache.iceberg.rest.credentials.ImmutableCredential;
import org.apache.iceberg.rest.requests.CommitTransactionRequest;
import org.apache.iceberg.rest.requests.CreateNamespaceRequest;
@@ -112,6 +113,7 @@ import org.apache.polaris.core.persistence.resolver.ResolvedPathKey;
import org.apache.polaris.core.persistence.resolver.Resolver;
import org.apache.polaris.core.persistence.resolver.ResolverFactory;
import org.apache.polaris.core.persistence.resolver.ResolverStatus;
+import org.apache.polaris.core.rest.NamespaceUtils;
import org.apache.polaris.core.rest.PolarisEndpoints;
import org.apache.polaris.core.storage.PolarisStorageActions;
import org.apache.polaris.core.storage.StorageAccessConfig;
@@ -1436,10 +1438,16 @@ public abstract class IcebergCatalogHandler extends CatalogHandler implements Au
Map<String, String> properties =
PolarisEntity.of(resolvedReferenceCatalog.getEntity()).getPropertiesAsMap();
- String prefix = prefixParser().catalogNameToPrefix(catalogName());
return ConfigResponse.builder()
.withDefaults(properties) // catalog properties are defaults
- .withOverrides(ImmutableMap.of("prefix", prefix))
+ .withOverrides(
+ ImmutableMap.of(
+ "prefix",
+ prefixParser().catalogNameToPrefix(catalogName()),
+ // Polaris does not handle custom namespace separators;
+ // always communicate the default namespace separator to clients.
+ RESTCatalogProperties.NAMESPACE_SEPARATOR,
+ NamespaceUtils.DEFAULT_NAMESPACE_SEPARATOR_ENCODED))
.withEndpoints(
ImmutableList.<Endpoint>builder()
.addAll(DEFAULT_ENDPOINTS)