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 d722183caf9100..39e7d840e4aff8 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 @@ -43,6 +43,8 @@ import org.apache.doris.nereids.trees.plans.commands.info.DropTagInfo; import org.apache.doris.nereids.trees.plans.commands.info.TagOptions; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.iceberg.ManageSnapshots; import org.apache.iceberg.PartitionSpec; @@ -66,6 +68,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -794,7 +797,8 @@ public View loadView(String dbName, String tblName) { } try { ViewCatalog viewCatalog = (ViewCatalog) catalog; - return executionAuthenticator.execute(() -> viewCatalog.loadView(TableIdentifier.of(dbName, tblName))); + return executionAuthenticator.execute( + () -> viewCatalog.loadView(TableIdentifier.of(getNamespace(dbName), tblName))); } catch (Exception e) { throw new RuntimeException("Failed to load view, error message is:" + e.getMessage(), e); } @@ -807,7 +811,7 @@ public List listViewNames(String db) { } try { return executionAuthenticator.execute(() -> - ((ViewCatalog) catalog).listViews(Namespace.of(db)) + ((ViewCatalog) catalog).listViews(getNamespace(db)) .stream().map(TableIdentifier::name).collect(Collectors.toList())); } catch (Exception e) { throw new RuntimeException("Failed to list view names, error message is:" + e.getMessage(), e); @@ -815,15 +819,22 @@ public List listViewNames(String db) { } private TableIdentifier getTableIdentifier(String dbName, String tblName) { - return externalCatalogName - .map(s -> TableIdentifier.of(s, dbName, tblName)) - .orElseGet(() -> TableIdentifier.of(dbName, tblName)); + Namespace ns = getNamespace(dbName); + return TableIdentifier.of(ns, tblName); } private Namespace getNamespace(String dbName) { - return externalCatalogName - .map(s -> Namespace.of(s, dbName)) - .orElseGet(() -> Namespace.of(dbName)); + return getNamespace(externalCatalogName, dbName); + } + + @VisibleForTesting + public static Namespace getNamespace(Optional catalogName, String dbName) { + String[] splits = Splitter.on(".").omitEmptyStrings().trimResults().splitToList(dbName).toArray(new String[0]); + if (catalogName.isPresent()) { + splits = Arrays.copyOf(splits, splits.length + 1); + splits[splits.length - 1] = catalogName.get(); + } + return Namespace.of(splits); } private Namespace getNamespace() { @@ -843,3 +854,4 @@ private void performDropView(String remoteDbName, String remoteViewName) throws } } + 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 3110745b91e045..b899116a1a835e 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 @@ -200,11 +200,7 @@ private ParamRules buildRules() { ParamRules rules = new ParamRules() // OAuth2 requires either credential or token, but not both .mutuallyExclusive(icebergRestOauth2Credential, icebergRestOauth2Token, - "OAuth2 cannot have both credential and token configured") - // If using credential flow, server URI is required - .requireAllIfPresent(icebergRestOauth2Credential, - new String[] {icebergRestOauth2ServerUri}, - "OAuth2 credential flow requires server-uri"); + "OAuth2 cannot have both credential and token configured"); // Custom validation: OAuth2 scope should not be used with token if (Strings.isNotBlank(icebergRestOauth2Token) && Strings.isNotBlank(icebergRestOauth2Scope)) { @@ -274,7 +270,9 @@ private void addOAuth2Properties() { if (Strings.isNotBlank(icebergRestOauth2Credential)) { // Client Credentials Flow icebergRestCatalogProperties.put(OAuth2Properties.CREDENTIAL, icebergRestOauth2Credential); - icebergRestCatalogProperties.put(OAuth2Properties.OAUTH2_SERVER_URI, icebergRestOauth2ServerUri); + if (Strings.isNotBlank(icebergRestOauth2ServerUri)) { + icebergRestCatalogProperties.put(OAuth2Properties.OAUTH2_SERVER_URI, icebergRestOauth2ServerUri); + } if (Strings.isNotBlank(icebergRestOauth2Scope)) { icebergRestCatalogProperties.put(OAuth2Properties.SCOPE, icebergRestOauth2Scope); } 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 new file mode 100644 index 00000000000000..3ecdb9ce437086 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/iceberg/IcebergMetadataOpTest.java @@ -0,0 +1,48 @@ +// 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.doris.datasource.iceberg; + +import org.apache.iceberg.catalog.Namespace; +import org.junit.Assert; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +public class IcebergMetadataOpTest { + + @Test + public void testGetNamespaces() { + Namespace ns = IcebergMetadataOps.getNamespace(Optional.empty(), "db1"); + Assert.assertEquals(1, ns.length()); + + ns = IcebergMetadataOps.getNamespace(Optional.empty(), "db1.db2.db3"); + Assert.assertEquals(3, ns.length()); + + ns = IcebergMetadataOps.getNamespace(Optional.empty(), "db1..db2"); + Assert.assertEquals(2, ns.length()); + + ns = IcebergMetadataOps.getNamespace(Optional.of("p1"), "db1"); + Assert.assertEquals(2, ns.length()); + + ns = IcebergMetadataOps.getNamespace(Optional.of("p1"), ""); + Assert.assertEquals(1, ns.length()); + + ns = IcebergMetadataOps.getNamespace(Optional.empty(), ""); + Assert.assertEquals(0, ns.length()); + } +} 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 d1199df2ae647a..226f6dd65511b5 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 @@ -131,14 +131,14 @@ public void testOAuth2ValidationErrors() { IcebergRestProperties restProps2 = new IcebergRestProperties(props2); Assertions.assertThrows(IllegalArgumentException.class, restProps2::initNormalizeAndCheckProps); - // Test: credential flow without server URI + // Test: credential flow without server URI is ok Map props3 = new HashMap<>(); props3.put("iceberg.rest.uri", "http://localhost:8080"); props3.put("iceberg.rest.security.type", "oauth2"); props3.put("iceberg.rest.oauth2.credential", "client_credentials"); IcebergRestProperties restProps3 = new IcebergRestProperties(props3); - Assertions.assertThrows(IllegalArgumentException.class, restProps3::initNormalizeAndCheckProps); + Assertions.assertDoesNotThrow(restProps3::initNormalizeAndCheckProps); // Test: scope with token (should fail) Map props4 = new HashMap<>();