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]

Reply via email to