This is an automated email from the ASF dual-hosted git repository.

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new fbf6c081eee branch-3.0: [feature](vault) Support database level 
storage vault #45325 (#49305)
fbf6c081eee is described below

commit fbf6c081eee472d084a1db0e3efebd9d1a47c5b4
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Mar 22 17:01:36 2025 +0800

    branch-3.0: [feature](vault) Support database level storage vault #45325 
(#49305)
    
    Cherry-picked from #45325
    
    Co-authored-by: Ganlin Zhao <36554565+glzha...@users.noreply.github.com>
---
 .../java/org/apache/doris/catalog/Database.java    |  24 ++
 .../apache/doris/common/util/PropertyAnalyzer.java |  53 +++--
 .../apache/doris/datasource/InternalCatalog.java   |   3 +-
 .../vault_p0/database/test_database_vault.groovy   | 246 +++++++++++++++++++++
 4 files changed, 310 insertions(+), 16 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
index 4a6bc249b4a..bb7b540a131 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Database.java
@@ -19,6 +19,7 @@ package org.apache.doris.catalog;
 
 import org.apache.doris.analysis.FunctionName;
 import org.apache.doris.catalog.TableIf.TableType;
+import org.apache.doris.cloud.catalog.CloudEnv;
 import org.apache.doris.cluster.ClusterNamespace;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
@@ -39,6 +40,7 @@ import org.apache.doris.persist.gson.GsonPostProcessable;
 import org.apache.doris.persist.gson.GsonUtils;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -52,6 +54,7 @@ import java.io.DataOutput;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -1000,6 +1003,7 @@ public class Database extends MetaObject implements 
Writable, DatabaseIf<Table>,
             }
         }
 
+        checkStorageVault(properties);
         replayUpdateDbProperties(properties);
         return true;
     }
@@ -1008,6 +1012,26 @@ public class Database extends MetaObject implements 
Writable, DatabaseIf<Table>,
         return binlogConfig;
     }
 
+    public void checkStorageVault(Map<String, String> properties) throws 
DdlException {
+        Env env = Env.getCurrentEnv();
+        if (!Config.isCloudMode() || !((CloudEnv) 
env).getEnableStorageVault()) {
+            return;
+        }
+
+        Map<String, String> propertiesCheck = new HashMap<>(properties);
+        String storageVaultName = 
PropertyAnalyzer.analyzeStorageVaultName(propertiesCheck);
+        if (Strings.isNullOrEmpty(storageVaultName)) {
+            return;
+        }
+
+        String storageVaultId = 
env.getStorageVaultMgr().getVaultIdByName(storageVaultName);
+        if (Strings.isNullOrEmpty(storageVaultId)) {
+            throw new DdlException("Storage vault '" + storageVaultName + "' 
does not exist. "
+                    + "You can use `SHOW STORAGE VAULT` to get all available 
vaults, "
+                    + "or create a new one with `CREATE STORAGE VAULT`.");
+        }
+    }
+
     public String toJson() {
         return GsonUtils.GSON.toJson(this);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
index e74d909e1ff..ec9b986915c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/util/PropertyAnalyzer.java
@@ -21,6 +21,7 @@ import org.apache.doris.analysis.DataSortInfo;
 import org.apache.doris.analysis.DateLiteral;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DataProperty;
+import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.DatabaseIf;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.EnvFactory;
@@ -61,6 +62,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -1187,28 +1189,49 @@ public class PropertyAnalyzer {
         return storagePolicy;
     }
 
-    /**
-     * @param properties
-     * @return <storageVaultName, storageVaultId>
-     * @throws AnalysisException
-     */
-    public static Pair<String, String> analyzeStorageVault(Map<String, String> 
properties) throws AnalysisException {
+    public static String analyzeStorageVaultName(Map<String, String> 
properties) {
         String storageVaultName = null;
         if (properties != null && 
properties.containsKey(PROPERTIES_STORAGE_VAULT_NAME)) {
             storageVaultName = properties.get(PROPERTIES_STORAGE_VAULT_NAME);
             properties.remove(PROPERTIES_STORAGE_VAULT_NAME);
         }
 
+        return storageVaultName;
+    }
+
+    /**
+     * @param properties, db
+     * @return <storageVaultName, storageVaultId>
+     * @throws AnalysisException
+     */
+    public static Pair<String, String> analyzeStorageVault(Map<String, String> 
properties, Database db)
+            throws AnalysisException {
+        String storageVaultName = analyzeStorageVaultName(properties);
+        String storageVaultId = null;
+
         if (Strings.isNullOrEmpty(storageVaultName)) {
-            // If user does not specify one storage vault then FE would use 
the default vault
-            Pair<String, String> info = 
Env.getCurrentEnv().getStorageVaultMgr().getDefaultStorageVault();
-            if (info == null) {
-                throw new AnalysisException("No default storage vault."
-                        + " You can use `SHOW STORAGE VAULT` to get all 
available vaults,"
-                        + " and pick one set default vault with `SET 
<vault_name> AS DEFAULT STORAGE VAULT`");
+            // If user does not specify one storage vault then FE would check 
db's storage vault then the default vault
+            // the storage vault inherit order is as follows: table -> db -> 
default
+            if (db.getDbProperties() != null) {
+                Map<String, String> dbProperties = new 
HashMap<>(db.getDbProperties().getProperties());
+                storageVaultName = 
PropertyAnalyzer.analyzeStorageVaultName(dbProperties);
+            }
+
+            if (!Strings.isNullOrEmpty(storageVaultName)) {
+                storageVaultId = 
Env.getCurrentEnv().getStorageVaultMgr().getVaultIdByName(storageVaultName);
+                LOG.info("Using database[{}] storage vault: name={}, id={}",
+                        db.getName(), storageVaultName, storageVaultId);
+            } else {
+                // continue to check default vault
+                Pair<String, String> info = 
Env.getCurrentEnv().getStorageVaultMgr().getDefaultStorageVault();
+                if (info == null) {
+                    throw new AnalysisException("No default storage vault."
+                            + " You can use `SHOW STORAGE VAULT` to get all 
available vaults,"
+                            + " and pick one set default vault with `SET 
<vault_name> AS DEFAULT STORAGE VAULT`");
+                }
+                storageVaultName = info.first;
+                LOG.info("Using default storage vault, name:{} id:{}", 
info.first, info.second);
             }
-            storageVaultName = info.first;
-            LOG.info("Using default storage vault, name:{} id:{}", info.first, 
info.second);
         }
 
         if (Strings.isNullOrEmpty(storageVaultName)) {
@@ -1217,7 +1240,7 @@ public class PropertyAnalyzer {
                     + " and pick one to set the table property 
`\"storage_vault_name\" = \"<vault_name>\"`");
         }
 
-        String storageVaultId = 
Env.getCurrentEnv().getStorageVaultMgr().getVaultIdByName(storageVaultName);
+        storageVaultId = 
Env.getCurrentEnv().getStorageVaultMgr().getVaultIdByName(storageVaultName);
         if (Strings.isNullOrEmpty(storageVaultId)) {
             throw new AnalysisException("Storage vault '" + storageVaultName + 
"' does not exist. "
                     + "You can use `SHOW STORAGE VAULT` to get all available 
vaults, "
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
index 0419c219629..c01443caa4c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
@@ -431,6 +431,7 @@ public class InternalCatalog implements CatalogIf<Database> 
{
         long id = Env.getCurrentEnv().getNextId();
         Database db = new Database(id, fullDbName);
         // check and analyze database properties before create database
+        db.checkStorageVault(properties);
         db.setDbProperties(new DatabaseProperty(properties));
 
         if (!tryLock(false)) {
@@ -2738,7 +2739,7 @@ public class InternalCatalog implements 
CatalogIf<Database> {
 
         if (Config.isCloudMode() && ((CloudEnv) env).getEnableStorageVault()) {
             // <storageVaultName, storageVaultId>
-            Pair<String, String> storageVaultInfoPair = 
PropertyAnalyzer.analyzeStorageVault(properties);
+            Pair<String, String> storageVaultInfoPair = 
PropertyAnalyzer.analyzeStorageVault(properties, db);
 
             // Check if user has storage vault usage privilege
             if (ConnectContext.get() != null && !env.getAccessManager()
diff --git 
a/regression-test/suites/vault_p0/database/test_database_vault.groovy 
b/regression-test/suites/vault_p0/database/test_database_vault.groovy
new file mode 100644
index 00000000000..16c45dfbd88
--- /dev/null
+++ b/regression-test/suites/vault_p0/database/test_database_vault.groovy
@@ -0,0 +1,246 @@
+// 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.
+
+import java.util.stream.Collectors;
+
+suite("test_database_vault", "nonConcurrent") {
+    if (!isCloudMode()) {
+        logger.info("skip test_database_vault case because not cloud mode")
+        return
+    }
+
+    if (!enableStoragevault()) {
+        logger.info("skip test_database_vault case")
+        return
+    }
+
+    def db1 = "test_db_vault_db1"
+    def db2 = "test_db_vault_db2"
+    def vault1 = "test_db_vault_vault1"
+    def vault2 = "test_db_vault_vault2"
+    def vault3 = "test_db_vault_vault3"
+    def table1 = "test_db_vault_table1"
+    def table2 = "test_db_vault_table2"
+    def table3 = "test_db_vault_table3"
+
+    try {
+
+        sql """
+            UNSET DEFAULT STORAGE VAULT
+        """
+
+        sql """
+            CREATE STORAGE VAULT IF NOT EXISTS ${vault1}
+            PROPERTIES (
+                "type"="hdfs",
+                "fs.defaultFS"="${getHmsHdfsFs()}",
+                "path_prefix" = "test_db_vault_vault1_prefix",
+                "hadoop.username" = "${getHmsUser()}"
+            );
+        """
+
+        def storageVaults = (sql " SHOW STORAGE VAULT; ").stream().map(row -> 
row[0]).collect(Collectors.toSet())
+        assertTrue(storageVaults.contains(vault1))
+
+        sql """
+            CREATE STORAGE VAULT IF NOT EXISTS ${vault2}
+            PROPERTIES (
+                "type"="hdfs",
+                "fs.defaultFS"="${getHmsHdfsFs()}",
+                "path_prefix" = "test_db_vault_vault2_prefix",
+                "hadoop.username" = "${getHmsUser()}"
+            );
+        """
+
+        storageVaults = (sql " SHOW STORAGE VAULT; ").stream().map(row -> 
row[0]).collect(Collectors.toSet())
+        assertTrue(storageVaults.contains(vault2))
+
+        sql "DROP DATABASE IF EXISTS ${db1}"
+        sql "DROP DATABASE IF EXISTS ${db2}"
+        // Create database with vault, vault should exist
+        expectExceptionLike({
+            sql """
+                CREATE DATABASE IF NOT EXISTS ${db1}
+                PROPERTIES (
+                    "storage_vault_name" = "non_exist_vault"
+                )
+            """
+        }, "not exist")
+
+        sql """
+            CREATE DATABASE IF NOT EXISTS ${db1}
+            PROPERTIES (
+                "storage_vault_name" = "${vault2}"
+            )
+        """
+
+        sql """
+            CREATE DATABASE IF NOT EXISTS ${db2}
+        """
+
+        def createDbSql = (sql " SHOW CREATE DATABASE ${db1}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createDbSql.first().contains(vault2))
+
+        // Alter database property with storage vault
+        expectExceptionLike({
+            sql """
+                ALTER DATABASE ${db1} SET
+                PROPERTIES (
+                    "storage_vault_name" = "non_exist_vault"
+                )
+            """
+        }, "not exist")
+
+        sql """
+            ALTER DATABASE ${db1} SET
+            PROPERTIES (
+                "storage_vault_name" = "${vault1}"
+            )
+        """
+
+        createDbSql = (sql " SHOW CREATE DATABASE ${db1}; ").stream().map(row 
-> row[1]).collect(Collectors.toSet())
+        assertTrue(createDbSql.first().contains(vault1))
+
+        sql """
+            ALTER DATABASE ${db2} SET
+            PROPERTIES (
+                "storage_vault_name" = "${vault2}"
+            )
+        """
+
+        createDbSql = (sql " SHOW CREATE DATABASE ${db2}; ").stream().map(row 
-> row[1]).collect(Collectors.toSet())
+        assertTrue(createDbSql.first().contains(vault2))
+
+        // Create table with/without specifying vault
+        sql """
+            CREATE TABLE IF NOT EXISTS ${db1}.${table1} (
+                C_CUSTKEY     INTEGER NOT NULL,
+                C_NAME        INTEGER NOT NULL
+            )
+            DUPLICATE KEY(C_CUSTKEY, C_NAME)
+            DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+            PROPERTIES (
+                "storage_vault_name" = ${vault2}
+            )
+        """
+        def createTableSql = (sql " SHOW CREATE TABLE ${db1}.${table1}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createTableSql.first().contains(vault2))
+
+        sql """
+            CREATE TABLE IF NOT EXISTS ${db1}.${table2} (
+                C_CUSTKEY     INTEGER NOT NULL,
+                C_NAME        INTEGER NOT NULL
+            )
+            DUPLICATE KEY(C_CUSTKEY, C_NAME)
+            DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+        """
+        createTableSql = (sql " SHOW CREATE TABLE ${db1}.${table2}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createTableSql.first().contains(vault1))
+
+        // Unset Database storage vault
+        sql """
+            ALTER DATABASE ${db2} SET
+            PROPERTIES (
+                "storage_vault_name" = ""
+            )
+        """
+        expectExceptionLike({
+            sql """
+                CREATE TABLE IF NOT EXISTS ${db2}.${table1} (
+                    C_CUSTKEY     INTEGER NOT NULL,
+                    C_NAME        INTEGER NOT NULL
+                )
+                DUPLICATE KEY(C_CUSTKEY, C_NAME)
+                DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+            """
+        }, "No default storage vault")
+
+        sql """
+            CREATE TABLE IF NOT EXISTS ${db2}.${table2} (
+                C_CUSTKEY     INTEGER NOT NULL,
+                C_NAME        INTEGER NOT NULL
+            )
+            DUPLICATE KEY(C_CUSTKEY, C_NAME)
+            DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+            PROPERTIES (
+                "storage_vault_name" = ${vault1}
+            )
+        """
+        createTableSql = (sql " SHOW CREATE TABLE ${db2}.${table2}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createTableSql.first().contains(vault1))
+
+        sql """
+            ALTER DATABASE ${db2} SET
+            PROPERTIES (
+                "storage_vault_name" = "${vault2}"
+            )
+        """
+        sql """
+            CREATE TABLE IF NOT EXISTS ${db2}.${table1} (
+                C_CUSTKEY     INTEGER NOT NULL,
+                C_NAME        INTEGER NOT NULL
+            )
+            DUPLICATE KEY(C_CUSTKEY, C_NAME)
+            DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+        """
+        createTableSql = (sql " SHOW CREATE TABLE ${db2}.${table1}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createTableSql.first().contains(vault2))
+
+        // Rename storage vault
+        sql """
+            ALTER STORAGE VAULT ${vault2}
+            PROPERTIES (
+                "type" = "hdfs",
+                "VAULT_NAME" = "${vault3}"
+            );
+        """
+
+        expectExceptionLike({
+            sql """
+                CREATE TABLE IF NOT EXISTS ${db2}.${table3} (
+                    C_CUSTKEY     INTEGER NOT NULL,
+                    C_NAME        INTEGER NOT NULL
+                )
+                DUPLICATE KEY(C_CUSTKEY, C_NAME)
+                DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+            """
+        }, "does not exist")
+
+        sql """
+            ALTER DATABASE ${db2} SET
+            PROPERTIES (
+                "storage_vault_name" = "${vault3}"
+            );
+        """
+
+        sql """
+            CREATE TABLE IF NOT EXISTS ${db2}.${table3} (
+                C_CUSTKEY     INTEGER NOT NULL,
+                C_NAME        INTEGER NOT NULL
+            )
+            DUPLICATE KEY(C_CUSTKEY, C_NAME)
+            DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+        """
+
+        createTableSql = (sql " SHOW CREATE TABLE ${db2}.${table3}; 
").stream().map(row -> row[1]).collect(Collectors.toSet())
+        assertTrue(createTableSql.first().contains(vault3))
+    } finally {
+        sql "DROP DATABASE IF EXISTS ${db1}"
+        sql "DROP DATABASE IF EXISTS ${db2}"
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to