This is an automated email from the ASF dual-hosted git repository.
Gabriel39 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new c7629ae23cf [fix](iceberg) Allow disabling REST catalog view
operations (#62986)
c7629ae23cf is described below
commit c7629ae23cf0b808b7374e0d64ccc16b96178e17
Author: Chenjunwei <[email protected]>
AuthorDate: Thu May 7 19:41:15 2026 +0800
[fix](iceberg) Allow disabling REST catalog view operations (#62986)
Some Iceberg REST catalog implementations expose table APIs but fail or
reject view APIs. Doris currently calls `ViewCatalog.listViews()` while
listing tables when the underlying Iceberg catalog implements
`ViewCatalog`, so `SHOW TABLES` and table metadata loading can fail even
when table listing/loading works.
This PR adds a REST catalog property to disable view operations when
needed, while keeping the existing default behavior enabled.
### Changes
- Add `iceberg.rest.view-enabled` to `IcebergRestProperties`, defaulting
to `true`.
- Gate Iceberg view operations in `IcebergMetadataOps` for REST catalogs
when the property is set to `false`.
- Add unit coverage for the new property and for skipping `listViews()`
in table listing.
---
.../datasource/iceberg/IcebergMetadataOps.java | 23 ++++++--
.../property/metastore/IcebergRestProperties.java | 9 +++
.../datasource/iceberg/IcebergMetadataOpTest.java | 69 ++++++++++++++++++++++
.../metastore/IcebergRestPropertiesTest.java | 17 ++++++
4 files changed, 113 insertions(+), 5 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
index f08f3ec899a..58e69e03ff6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/IcebergMetadataOps.java
@@ -190,7 +190,7 @@ public class IcebergMetadataOps implements
ExternalMetadataOps {
// IcebergMetadataOps handles listTableNames and listViewNames
separately.
// listTableNames should only focus on the table type,
// but in reality, Iceberg's return includes views. Therefore,
we added a filter to exclude views.
- if (catalog instanceof ViewCatalog) {
+ if (isViewCatalogEnabled()) {
views = ((ViewCatalog)
catalog).listViews(getNamespace(dbName))
.stream().map(TableIdentifier::name).collect(Collectors.toList());
} else {
@@ -1126,7 +1126,7 @@ public class IcebergMetadataOps implements
ExternalMetadataOps {
@Override
public boolean viewExists(String remoteDbName, String remoteViewName) {
- if (!(catalog instanceof ViewCatalog)) {
+ if (!isViewCatalogEnabled()) {
return false;
}
try {
@@ -1140,7 +1140,7 @@ public class IcebergMetadataOps implements
ExternalMetadataOps {
@Override
public Object loadView(String dbName, String tblName) {
- if (!(catalog instanceof ViewCatalog)) {
+ if (!isViewCatalogEnabled()) {
return null;
}
try {
@@ -1154,7 +1154,7 @@ public class IcebergMetadataOps implements
ExternalMetadataOps {
@Override
public List<String> listViewNames(String db) {
- if (!(catalog instanceof ViewCatalog)) {
+ if (!isViewCatalogEnabled()) {
return Collections.emptyList();
}
try {
@@ -1192,12 +1192,25 @@ public class IcebergMetadataOps implements
ExternalMetadataOps {
return externalCatalogName.map(Namespace::of).orElseGet(() ->
Namespace.empty());
}
+ private boolean isViewCatalogEnabled() {
+ if (!(catalog instanceof ViewCatalog)) {
+ return false;
+ }
+ if (dorisCatalog instanceof IcebergRestExternalCatalog) {
+ MetastoreProperties metaProps =
dorisCatalog.getCatalogProperty().getMetastoreProperties();
+ if (metaProps instanceof IcebergRestProperties) {
+ return ((IcebergRestProperties)
metaProps).isIcebergRestViewEnabled();
+ }
+ }
+ return true;
+ }
+
public ThreadPoolExecutor getThreadPoolWithPreAuth() {
return dorisCatalog.getThreadPoolWithPreAuth();
}
private void performDropView(String remoteDbName, String remoteViewName)
throws DdlException {
- if (!(catalog instanceof ViewCatalog)) {
+ if (!isViewCatalogEnabled()) {
throw new DdlException("Drop Iceberg view is not supported with
not view catalog.");
}
ViewCatalog viewCatalog = (ViewCatalog) catalog;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
index 407c5c58b3e..f457c90ff40 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
@@ -116,6 +116,11 @@ public class IcebergRestProperties extends
AbstractIcebergProperties {
description = "Enable nested namespace for the iceberg rest
catalog service.")
private String icebergRestNestedNamespaceEnabled = "false";
+ @ConnectorProperty(names = {"iceberg.rest.view-enabled"},
+ required = false,
+ description = "Enable view operations for the iceberg rest catalog
service.")
+ private String icebergRestViewEnabled = "true";
+
@ConnectorProperty(names = {"iceberg.rest.case-insensitive-name-matching"},
required = false,
supported = false,
@@ -371,6 +376,10 @@ public class IcebergRestProperties extends
AbstractIcebergProperties {
return Boolean.parseBoolean(icebergRestNestedNamespaceEnabled);
}
+ public boolean isIcebergRestViewEnabled() {
+ return Boolean.parseBoolean(icebergRestViewEnabled);
+ }
+
public enum Security {
NONE,
OAUTH2,
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/iceberg/IcebergMetadataOpTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/iceberg/IcebergMetadataOpTest.java
index 3ecdb9ce437..2bf1ef3deb4 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/iceberg/IcebergMetadataOpTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/iceberg/IcebergMetadataOpTest.java
@@ -17,10 +17,23 @@
package org.apache.doris.datasource.iceberg;
+import org.apache.doris.common.security.authentication.ExecutionAuthenticator;
+import org.apache.doris.datasource.CatalogProperty;
+
+import org.apache.iceberg.catalog.Catalog;
import org.apache.iceberg.catalog.Namespace;
+import org.apache.iceberg.catalog.SupportsNamespaces;
+import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.catalog.ViewCatalog;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
public class IcebergMetadataOpTest {
@@ -45,4 +58,60 @@ public class IcebergMetadataOpTest {
ns = IcebergMetadataOps.getNamespace(Optional.empty(), "");
Assert.assertEquals(0, ns.length());
}
+
+ @Test
+ public void testListTableNamesSkipsViewsWhenRestViewDisabled() {
+ IcebergRestExternalCatalog dorisCatalog =
Mockito.mock(IcebergRestExternalCatalog.class);
+ Catalog icebergCatalog = Mockito.mock(Catalog.class,
+
Mockito.withSettings().extraInterfaces(SupportsNamespaces.class,
ViewCatalog.class));
+
+ Map<String, String> props = new HashMap<>();
+ props.put("type", "iceberg");
+ props.put("iceberg.catalog.type", "rest");
+ props.put("iceberg.rest.uri", "http://localhost:8181");
+ props.put("iceberg.rest.view-enabled", "false");
+
+ Mockito.when(dorisCatalog.getExecutionAuthenticator()).thenReturn(new
ExecutionAuthenticator() {
+ });
+
Mockito.when(dorisCatalog.getProperties()).thenReturn(Collections.emptyMap());
+ Mockito.when(dorisCatalog.getCatalogProperty()).thenReturn(new
CatalogProperty(null, props));
+
+ Namespace namespace = Namespace.of("PUBLIC");
+ TableIdentifier table = TableIdentifier.of(namespace,
"DORIS_HORIZON_T");
+
Mockito.when(icebergCatalog.listTables(namespace)).thenReturn(Collections.singletonList(table));
+
+ IcebergMetadataOps ops = new IcebergMetadataOps(dorisCatalog,
icebergCatalog);
+ List<String> tableNames = ops.listTableNames("PUBLIC");
+
+ Assert.assertEquals(Collections.singletonList("DORIS_HORIZON_T"),
tableNames);
+ Mockito.verify((ViewCatalog) icebergCatalog,
Mockito.never()).listViews(Mockito.any());
+ }
+
+ @Test
+ public void testListTableNamesFiltersViewsWhenRestViewEnabled() {
+ IcebergRestExternalCatalog dorisCatalog =
Mockito.mock(IcebergRestExternalCatalog.class);
+ Catalog icebergCatalog = Mockito.mock(Catalog.class,
+
Mockito.withSettings().extraInterfaces(SupportsNamespaces.class,
ViewCatalog.class));
+
+ Map<String, String> props = new HashMap<>();
+ props.put("type", "iceberg");
+ props.put("iceberg.catalog.type", "rest");
+ props.put("iceberg.rest.uri", "http://localhost:8181");
+
+ Mockito.when(dorisCatalog.getExecutionAuthenticator()).thenReturn(new
ExecutionAuthenticator() {
+ });
+
Mockito.when(dorisCatalog.getProperties()).thenReturn(Collections.emptyMap());
+ Mockito.when(dorisCatalog.getCatalogProperty()).thenReturn(new
CatalogProperty(null, props));
+
+ Namespace namespace = Namespace.of("PUBLIC");
+ TableIdentifier table = TableIdentifier.of(namespace,
"DORIS_HORIZON_T");
+ TableIdentifier view = TableIdentifier.of(namespace,
"DORIS_HORIZON_V");
+
Mockito.when(icebergCatalog.listTables(namespace)).thenReturn(Arrays.asList(table,
view));
+ Mockito.when(((ViewCatalog)
icebergCatalog).listViews(namespace)).thenReturn(Collections.singletonList(view));
+
+ IcebergMetadataOps ops = new IcebergMetadataOps(dorisCatalog,
icebergCatalog);
+ List<String> tableNames = ops.listTableNames("PUBLIC");
+
+ Assert.assertEquals(Collections.singletonList("DORIS_HORIZON_T"),
tableNames);
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
index 47ce0669a6c..cd9820c2722 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
@@ -83,6 +83,23 @@ public class IcebergRestPropertiesTest {
Assertions.assertFalse(catalogProps.containsKey("header.X-Iceberg-Access-Delegation"));
}
+ @Test
+ public void testRestViewEnabled() {
+ Map<String, String> props = new HashMap<>();
+ props.put("iceberg.rest.uri", "http://localhost:8080");
+
+ IcebergRestProperties defaultProps = new IcebergRestProperties(props);
+ defaultProps.initNormalizeAndCheckProps();
+ Assertions.assertTrue(defaultProps.isIcebergRestViewEnabled());
+
+ props.put("iceberg.rest.view-enabled", "false");
+ IcebergRestProperties disabledProps = new IcebergRestProperties(props);
+ disabledProps.initNormalizeAndCheckProps();
+ Assertions.assertFalse(disabledProps.isIcebergRestViewEnabled());
+ Assertions.assertFalse(disabledProps.getIcebergRestCatalogProperties()
+ .containsKey("iceberg.rest.view-enabled"));
+ }
+
@Test
public void testOAuth2CredentialFlow() {
Map<String, String> props = new HashMap<>();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]