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

michaelsmith pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/impala.git

commit f0ac9a4b7d3b7c1eb9e9b5cc36aae80031ce9340
Author: Balazs Hevele <[email protected]>
AuthorDate: Mon Jan 26 14:50:14 2026 +0100

    IMPALA-12844: Support setting DBPROPERTIES
    
    Added support for ALTER DATABASE <db> SET DBPROPERTIES(...)
    Added support for [WITH DBPROPERTIES(...)] for CREATE DATABASE queries
    
    Database properties can now be set by Impala through these queries
    either at database creation or for already existing databases.
    
    Testing:
    - Added frontend tests for parsing/analyzing/authorizing relevant queries
    - Added E2E tests to verify that altering properties does take effect
    
    Change-Id: I628c7bed4f0c39aed7f5f4ffea52421caf501933
    Reviewed-on: http://gerrit.cloudera.org:8080/23905
    Reviewed-by: Impala Public Jenkins <[email protected]>
    Tested-by: Impala Public Jenkins <[email protected]>
---
 common/thrift/JniCatalog.thrift                    | 12 +++++
 fe/src/main/cup/sql-parser.cup                     | 22 ++++++++-
 .../analysis/AlterDbSetDbPropertiesStmt.java       | 57 ++++++++++++++++++++++
 .../org/apache/impala/analysis/CreateDbStmt.java   | 10 +++-
 .../apache/impala/service/CatalogOpExecutor.java   | 56 +++++++++++++++------
 .../org/apache/impala/analysis/AnalyzeDDLTest.java |  9 ++++
 .../org/apache/impala/analysis/ParserTest.java     | 36 ++++++++++++++
 .../authorization/AuthorizationStmtTest.java       | 34 +++++++++++++
 .../authorization/ranger/RangerAuditLogTest.java   | 16 ++++++
 .../queries/QueryTest/create-database.test         | 24 +++++++++
 tests/custom_cluster/test_events_custom_configs.py |  2 +
 tests/metadata/test_ddl.py                         |  6 +++
 tests/metadata/test_ddl_base.py                    |  4 ++
 13 files changed, 268 insertions(+), 20 deletions(-)

diff --git a/common/thrift/JniCatalog.thrift b/common/thrift/JniCatalog.thrift
index b3c871fa7..ebd49dd38 100644
--- a/common/thrift/JniCatalog.thrift
+++ b/common/thrift/JniCatalog.thrift
@@ -67,6 +67,7 @@ enum TOwnerType {
 // Types of ALTER DATABASE commands supported.
 enum TAlterDbType {
   SET_OWNER = 0
+  SET_DBPROPERTIES = 1
 }
 
 // Parameters for ALTER DATABASE SET OWNER commands.
@@ -82,6 +83,11 @@ struct TAlterDbSetOwnerParams {
   3: optional string server_name
 }
 
+// Parameters for ALTER DATABASE SET DBPROPERTIES commands.
+struct TAlterDbSetDbPropertiesParams {
+  1: map<string, string> properties
+}
+
 struct TAlterDbParams {
   // The type of ALTER DATABASE command.
   1: required TAlterDbType alter_type
@@ -91,6 +97,9 @@ struct TAlterDbParams {
 
   // Parameters for ALTER DATABASE SET OWNER commands.
   3: optional TAlterDbSetOwnerParams set_owner_params
+
+  // Parameters for ALTER DATABASE SET DBPROPERTIES commands.
+  4: optional TAlterDbSetDbPropertiesParams set_dbproperties_params
 }
 
 // Types of ALTER TABLE commands supported.
@@ -145,6 +154,9 @@ struct TCreateDbParams {
   // Optional HDFS path for the database. Overrides location as the default 
location for
   // all managed tables created in the database.
   7: optional string managed_location
+
+  // Optional dbparams to be set after creation
+  8: optional map<string, string> properties
 }
 
 // Parameters of CREATE DATA SOURCE commands
diff --git a/fe/src/main/cup/sql-parser.cup b/fe/src/main/cup/sql-parser.cup
index cb4449230..d1772268c 100755
--- a/fe/src/main/cup/sql-parser.cup
+++ b/fe/src/main/cup/sql-parser.cup
@@ -527,6 +527,7 @@ nonterminal ComputeStatsStmt compute_stats_stmt;
 nonterminal DropDbStmt drop_db_stmt;
 nonterminal DropStatsStmt drop_stats_stmt;
 nonterminal DropTableOrViewStmt drop_tbl_or_view_stmt;
+nonterminal HashMap opt_with_dbproperties;
 nonterminal CreateDbStmt create_db_stmt;
 nonterminal CreateTableAsSelectStmt create_tbl_as_select_stmt;
 nonterminal CreateTableAsSelectStmt.CtasParams create_tbl_as_select_params;
@@ -1414,6 +1415,12 @@ alter_db_stmt ::=
     parser.checkIdentKeyword("OWNER", owner_id);
     RESULT = new AlterDbSetOwnerStmt(db, new Owner(TOwnerType.ROLE, role));
   :}
+  | KW_ALTER KW_DATABASE ident_or_unreserved:db KW_SET IDENT:dbproperties_id
+  LPAREN properties_map:properties RPAREN
+  {:
+    parser.checkIdentKeyword("DBPROPERTIES", dbproperties_id);
+    RESULT = new AlterDbSetDbPropertiesStmt(db, properties);
+  :}
   ;
 
 // In some places, the opt_partition_set is used to avoid conflicts even though
@@ -1581,11 +1588,22 @@ opt_kw_column ::=
   | /* empty */
   ;
 
+opt_with_dbproperties ::=
+  KW_WITH IDENT:dbproperties_id LPAREN properties_map:properties RPAREN
+  {:
+    parser.checkIdentKeyword("DBPROPERTIES", dbproperties_id);
+    RESULT = properties;
+  :}
+  | /* empty */
+  {: RESULT = null; :}
+  ;
+
 create_db_stmt ::=
   KW_CREATE db_or_schema_kw if_not_exists_val:if_not_exists 
ident_or_unreserved:db_name
-  opt_comment_val:comment location_val:location 
managed_location_val:managed_location
+  opt_comment_val:comment opt_with_dbproperties:dbproperties
+  location_val:location managed_location_val:managed_location
   {: RESULT = new CreateDbStmt(db_name, comment, location,
-                               managed_location, if_not_exists);
+                               managed_location, if_not_exists, dbproperties);
   :}
   ;
 
diff --git 
a/fe/src/main/java/org/apache/impala/analysis/AlterDbSetDbPropertiesStmt.java 
b/fe/src/main/java/org/apache/impala/analysis/AlterDbSetDbPropertiesStmt.java
new file mode 100644
index 000000000..b766c0e80
--- /dev/null
+++ 
b/fe/src/main/java/org/apache/impala/analysis/AlterDbSetDbPropertiesStmt.java
@@ -0,0 +1,57 @@
+// 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.impala.analysis;
+
+import java.util.Map;
+
+import org.apache.impala.authorization.Privilege;
+import org.apache.impala.common.AnalysisException;
+import org.apache.impala.thrift.TAlterDbParams;
+import org.apache.impala.thrift.TAlterDbSetDbPropertiesParams;
+import org.apache.impala.thrift.TAlterDbType;
+
+/**
+ * Represents an ALTER DATABASE db SET DBPROPERTIES ('key'='value', ...) 
statement.
+ */
+public class AlterDbSetDbPropertiesStmt extends AlterDbStmt {
+  private final Map<String, String> dbProperties_;
+
+  public AlterDbSetDbPropertiesStmt(String dbName, Map<String, String> 
properties) {
+    super(dbName);
+    this.dbProperties_ = properties;
+  }
+
+  @Override
+  public void analyze(Analyzer analyzer) throws AnalysisException {
+    analyzer.getDb(dbName_, Privilege.ALTER);
+  }
+
+  @Override
+  public TAlterDbParams toThrift() {
+    TAlterDbParams params = super.toThrift();
+    params.setAlter_type(TAlterDbType.SET_DBPROPERTIES);
+
+    TAlterDbSetDbPropertiesParams setDbPropertiesParams
+        = new TAlterDbSetDbPropertiesParams();
+
+    setDbPropertiesParams.setProperties(dbProperties_);
+    params.setSet_dbproperties_params(setDbPropertiesParams);
+
+    return params;
+  }
+}
\ No newline at end of file
diff --git a/fe/src/main/java/org/apache/impala/analysis/CreateDbStmt.java 
b/fe/src/main/java/org/apache/impala/analysis/CreateDbStmt.java
index 40c6e9417..974b31892 100644
--- a/fe/src/main/java/org/apache/impala/analysis/CreateDbStmt.java
+++ b/fe/src/main/java/org/apache/impala/analysis/CreateDbStmt.java
@@ -17,6 +17,8 @@
 
 package org.apache.impala.analysis;
 
+import java.util.Map;
+
 import com.google.common.base.Preconditions;
 import org.apache.hadoop.fs.permission.FsAction;
 import org.apache.impala.authorization.Privilege;
@@ -35,6 +37,7 @@ public class CreateDbStmt extends StatementBase {
   private final HdfsUri managedLocation_;
   private final String comment_;
   private final boolean ifNotExists_;
+  private final Map<String, String> dbProperties_;
   // Database owner. Set during analysis.
   private String owner_;
 
@@ -45,7 +48,7 @@ public class CreateDbStmt extends StatementBase {
    * Creates a database with the given name.
    */
   public CreateDbStmt(String dbName) {
-    this(dbName, null, null, null, false);
+    this(dbName, null, null, null, false, null);
   }
 
   /**
@@ -55,12 +58,14 @@ public class CreateDbStmt extends StatementBase {
    * unless the ifNotExists is true.
    */
   public CreateDbStmt(String dbName, String comment, HdfsUri location,
-       HdfsUri managedlocation, boolean ifNotExists) {
+       HdfsUri managedlocation, boolean ifNotExists,
+       Map<String, String> properties) {
     this.dbName_ = dbName;
     this.comment_ = comment;
     this.location_ = location;
     this.managedLocation_ = managedlocation;
     this.ifNotExists_ = ifNotExists;
+    this.dbProperties_ = properties;
   }
 
   public String getComment() { return comment_; }
@@ -92,6 +97,7 @@ public class CreateDbStmt extends StatementBase {
     params.setIf_not_exists(getIfNotExists());
     params.setOwner(getOwner());
     params.setServer_name(serverName_);
+    params.setProperties(dbProperties_);
     return params;
   }
 
diff --git a/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java 
b/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
index 79ca38519..3b3bbbf9c 100644
--- a/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
+++ b/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
@@ -177,7 +177,9 @@ import org.apache.impala.hive.executor.HiveJavaFunction;
 import org.apache.impala.hive.executor.HiveJavaFunctionFactory;
 import org.apache.impala.thrift.JniCatalogConstants;
 import org.apache.impala.thrift.TAlterDbParams;
+import org.apache.impala.thrift.TAlterDbType;
 import org.apache.impala.thrift.TAlterDbSetOwnerParams;
+import org.apache.impala.thrift.TAlterDbSetDbPropertiesParams;
 import org.apache.impala.thrift.TAlterTableAddColsParams;
 import org.apache.impala.thrift.TAlterTableAddDropRangePartitionParams;
 import org.apache.impala.thrift.TAlterTableAddPartitionParams;
@@ -2341,6 +2343,11 @@ public class CatalogOpExecutor {
     if (params.getComment() != null) {
       db.setDescription(params.getComment());
     }
+    if (params.getProperties() != null) {
+        for (Map.Entry<String, String> property : 
params.getProperties().entrySet()) {
+          db.putToParameters(property.getKey(), property.getValue());
+        }
+    }
     if (params.getLocation() != null) {
       db.setLocationUri(params.getLocation());
     }
@@ -2724,7 +2731,7 @@ public class CatalogOpExecutor {
         // parameters. If the applyAlterDatabase method below throws an 
exception,
         // catalog might end up in a inconsistent state. Ideally, we should 
make a copy
         // of hms Database object and then update the Db once the HMS 
operation succeeds
-        // similar to what happens in alterDatabaseSetOwner method.
+        // similar to what happens in alterDatabase method.
         if (catalog_.addFunction(fn)) {
           addCatalogServiceIdentifiers(db.getMetaStoreDb(),
               catalog_.getCatalogServiceId(), newCatalogVersion);
@@ -8454,23 +8461,29 @@ public class CatalogOpExecutor {
     if (db == null) {
       throw new CatalogException("Database: " + dbName + " does not exist.");
     }
+    TAlterDbSetOwnerParams setOwnerParams = null;
+    TAlterDbSetDbPropertiesParams setDbPropertiesParams = null;
     switch (params.getAlter_type()) {
-      case SET_OWNER:
-        alterDatabaseSetOwner(db, params.getSet_owner_params(), 
wantMinimalResult,
-            response, catalogTimeline);
+      case SET_OWNER: {
+        setOwnerParams = params.getSet_owner_params();
+        Preconditions.checkNotNull(setOwnerParams);
+        Preconditions.checkNotNull(setOwnerParams.owner_name);
+        Preconditions.checkNotNull(setOwnerParams.owner_type);
+        tryLock(db, "altering the owner", catalogTimeline);
+        break;
+      }
+      case SET_DBPROPERTIES: {
+        setDbPropertiesParams = params.getSet_dbproperties_params();
+        Preconditions.checkNotNull(setDbPropertiesParams);
+        Preconditions.checkNotNull(setDbPropertiesParams.properties);
+        tryLock(db, "altering dbproperties", catalogTimeline);
         break;
+      }
       default:
         throw new UnsupportedOperationException(
             "Unknown ALTER DATABASE operation type: " + 
params.getAlter_type());
     }
-  }
 
-  private void alterDatabaseSetOwner(Db db, TAlterDbSetOwnerParams params,
-      boolean wantMinimalResult, TDdlExecResponse response,
-      EventSequence catalogTimeline) throws ImpalaException {
-    Preconditions.checkNotNull(params.owner_name);
-    Preconditions.checkNotNull(params.owner_type);
-    tryLock(db, "altering the owner", catalogTimeline);
     // Get a new catalog version to assign to the database being altered.
     long newCatalogVersion = catalog_.incrementAndGetCatalogVersion();
     catalog_.getLock().writeLock().unlock();
@@ -8480,16 +8493,27 @@ public class CatalogOpExecutor {
           newCatalogVersion);
       String originalOwnerName = msDb.getOwnerName();
       PrincipalType originalOwnerType = msDb.getOwnerType();
-      msDb.setOwnerName(params.owner_name);
-      msDb.setOwnerType(PrincipalType.valueOf(params.owner_type.name()));
+      switch (params.getAlter_type()) {
+        case SET_OWNER: {
+          msDb.setOwnerName(setOwnerParams.owner_name);
+          
msDb.setOwnerType(PrincipalType.valueOf(setOwnerParams.owner_type.name()));
+          break;
+        }
+        case SET_DBPROPERTIES: {
+          for (Map.Entry<String, String> property :
+              setDbPropertiesParams.properties.entrySet())
+            msDb.putToParameters(property.getKey(), property.getValue());
+          break;
+        }
+      }
       try {
         applyAlterDatabase(msDb, catalogTimeline);
       } catch (ImpalaRuntimeException e) {
         throw e;
       }
-      if (authzConfig_.isEnabled()) {
-        authzManager_.updateDatabaseOwnerPrivilege(params.server_name, 
db.getName(),
-            originalOwnerName, originalOwnerType, msDb.getOwnerName(),
+      if (params.getAlter_type() == TAlterDbType.SET_OWNER && 
authzConfig_.isEnabled()) {
+        authzManager_.updateDatabaseOwnerPrivilege(setOwnerParams.server_name,
+            db.getName(), originalOwnerName, originalOwnerType, 
msDb.getOwnerName(),
             msDb.getOwnerType(), response);
       }
       Db updatedDb = catalog_.updateDb(msDb);
diff --git a/fe/src/test/java/org/apache/impala/analysis/AnalyzeDDLTest.java 
b/fe/src/test/java/org/apache/impala/analysis/AnalyzeDDLTest.java
index 7369aff50..ae0529e4f 100644
--- a/fe/src/test/java/org/apache/impala/analysis/AnalyzeDDLTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/AnalyzeDDLTest.java
@@ -2421,6 +2421,8 @@ public class AnalyzeDDLTest extends FrontendTestBase {
     AnalysisError("create database new_db managedlocation " +
         "'blah://bucket/test-warehouse/new_db'",
         "No FileSystem for scheme: blah");
+
+    AnalyzesOk("create database new_db with dbproperties('a'='b')");
   }
 
   @Test
@@ -4843,6 +4845,13 @@ public class AnalyzeDDLTest extends FrontendTestBase {
     }
   }
 
+  @Test
+  public void TestAlterDatabaseSetDbProperties() {
+    AnalyzesOk("alter database functional set dbproperties('a'='b')");
+    AnalysisError("alter database doesntexist set dbproperties('a'='b')",
+          "Database does not exist: doesntexist");
+  }
+
   @Test
   public void TestAlterTableSetOwner() {
     String[] ownerTypes = new String[]{"user", "role"};
diff --git a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java 
b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
index 4905851f5..b81a7bac8 100644
--- a/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
+++ b/fe/src/test/java/org/apache/impala/analysis/ParserTest.java
@@ -2262,6 +2262,9 @@ public class ParserTest extends FrontendTestBase {
       ParsesOk(String.format("CREATE %s Foo LOCATION '/hdfs_location'", kw));
       ParsesOk(String.format(
           "CREATE %s Foo COMMENT 'comment' LOCATION '/hdfs_location'", kw));
+      ParsesOk(String.format("CREATE %s Foo WITH DBPROPERTIES('k'='v')", kw));
+      ParsesOk(String.format(
+          "CREATE %s Foo WITH DBPROPERTIES('k1'='v1', 'k2'='v2')", kw));
 
       // Only string literals are supported
       ParserError(String.format("CREATE %s Foo COMMENT mytable", kw));
@@ -2271,10 +2274,27 @@ public class ParserTest extends FrontendTestBase {
       ParserError(String.format(
           "CREATE %s Foo LOCATION '/hdfs/location' COMMENT 'comment'", kw));
 
+      ParserError(String.format("CREATE %s Foo DBPROPERTIES('a'='b')", kw));
+      ParserError(String.format("CREATE %s Foo WITH DBPROPERTIES()", kw));
+      ParserError(String.format("CREATE %s Foo WITH DBPROPERTIES(a=)", kw));
+      ParserError(String.format("CREATE %s Foo WITH DBPROPERTIES('a'=b)", kw));
+
+      // WITH DBPROPERTIES needs to be *before* LOCATION
+      ParserError(String.format(
+          "CREATE %s Foo LOCATION '/hdfs/location' WITH 
DBPROPERTIES('a'='b')", kw));
+      ParsesOk(String.format(
+          "CREATE %s Foo WITH DBPROPERTIES('a'='b') LOCATION 
'/hdfs/location'", kw));
+
       ParserError(String.format("CREATE %s Foo COMMENT LOCATION 
'/hdfs_location'", kw));
       ParserError(String.format("CREATE %s Foo LOCATION", kw));
       ParserError(String.format("CREATE %s Foo LOCATION 'dfsd' 'dafdsf'", kw));
 
+      ParsesOk(String.format(
+          "CREATE %s Foo COMMENT 'comment' WITH DBPROPERTIES('a'='b')", kw));
+      ParsesOk(String.format(
+          "CREATE %s Foo COMMENT 'comment' WITH DBPROPERTIES('a'='b') " +
+          "LOCATION '/hdfs/location'", kw));
+
       ParserError(String.format("CREATE Foo"));
       ParserError(String.format("CREATE %s 'Foo'", kw));
       ParserError(String.format("CREATE %s", kw));
@@ -4458,6 +4478,22 @@ public class ParserTest extends FrontendTestBase {
     ParserError("ALTER DATABASE SET OWNER");
   }
 
+  @Test
+  public void TestAlterDatabaseSetDbProperties() {
+    ParsesOk("ALTER DATABASE db SET DBPROPERTIES('a'='b')");
+    ParsesOk("ALTER DATABASE db SET DBPROPERTIES('a'='b', 'b'='c')");
+    ParsesOk("ALTER DATABASE db SET DBPROPERTIES('a'='b', 'b'='c', 'c'='d')");
+
+    ParserError("ALTER DATABASE db SET DBPROPERTIESS('a'='b')");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES()");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES('a')");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES('a'=)");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES('a'=b)");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES(a='b')");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES('a'='b' 'c'='d')");
+    ParserError("ALTER DATABASE db SET DBPROPERTIES('a'='b',)");
+  }
+
   @Test
   public void TestAlterTableOrViewSetOwner() {
     for (String type : new String[]{"TABLE", "VIEW"}) {
diff --git 
a/fe/src/test/java/org/apache/impala/authorization/AuthorizationStmtTest.java 
b/fe/src/test/java/org/apache/impala/authorization/AuthorizationStmtTest.java
index 70dc27ef7..977f32cc9 100644
--- 
a/fe/src/test/java/org/apache/impala/authorization/AuthorizationStmtTest.java
+++ 
b/fe/src/test/java/org/apache/impala/authorization/AuthorizationStmtTest.java
@@ -341,6 +341,11 @@ public class AuthorizationStmtTest extends 
AuthorizationTestBase {
         expectedAuthorizables);
     verifyPrivilegeReqs(createAnalysisCtx("functional"),
         "alter view alltypes_view as select 1", expectedAuthorizables);
+
+    // Alter database set dbproperties.
+    expectedAuthorizables = Sets.newHashSet("functional");
+    verifyPrivilegeReqs("alter database functional set dbproperties ('a'='b')",
+        expectedAuthorizables);
   }
 
   @Test
@@ -2738,6 +2743,35 @@ public class AuthorizationStmtTest extends 
AuthorizationTestBase {
     assertTrue(exceptionThrown);
   }
 
+  @Test
+  public void testAlterDatabaseSetDbProperties() throws ImpalaException {
+    authorize("alter database functional set dbproperties('a'='b')")
+        .ok(onServer(TPrivilegeLevel.ALL))
+        .ok(onServer(TPrivilegeLevel.OWNER))
+        .ok(onServer(TPrivilegeLevel.ALTER))
+        .ok(onDatabase("functional", TPrivilegeLevel.ALL))
+        .ok(onDatabase("functional", TPrivilegeLevel.OWNER))
+        .ok(onDatabase("functional", TPrivilegeLevel.ALTER))
+        .error(alterError("functional"))
+        .error(alterError("functional"), onServer(allExcept(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.ALTER)))
+        .error(alterError("functional"), onDatabase("functional",
+            allExcept(TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+                TPrivilegeLevel.ALTER)));
+
+    authorize("alter database functional set dbproperties('a'='b')")
+        .ok(onServer(TPrivilegeLevel.values()))
+        .ok(onDatabase("functional", TPrivilegeLevel.values()));
+
+    // Database does not exist.
+    authorize("alter database nodb set dbproperties('a'='b')")
+        .error(alterError("nodb"))
+        .error(alterError("nodb"), onServer(allExcept(
+            TPrivilegeLevel.ALL, TPrivilegeLevel.OWNER,
+            TPrivilegeLevel.ALTER)));
+  }
+
   @Test
   public void testUpdate() throws ImpalaException {
     // Update is only supported on Kudu tables.
diff --git 
a/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
 
b/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
index 0130f944c..f7bb3438e 100644
--- 
a/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
+++ 
b/fe/src/test/java/org/apache/impala/authorization/ranger/RangerAuditLogTest.java
@@ -76,6 +76,14 @@ public class RangerAuditLogTest extends 
AuthorizationTestBase {
       assertEquals("create database test_db", events.get(0).getRequestData());
     }, "create database test_db", onServer(TPrivilegeLevel.CREATE));
 
+    authzOk(events -> {
+      assertEquals(1, events.size());
+      assertEventEquals("@database", "alter", "functional", 1, events.get(0));
+      assertEquals("alter database functional set dbproperties ('a'='b')",
+          events.get(0).getRequestData());
+    }, "alter database functional set dbproperties ('a'='b')",
+        onDatabase("functional", TPrivilegeLevel.ALTER));
+
     authzOk(events -> {
       assertEquals(1, events.size());
       assertEventEquals("@table", "create", "functional/test_tbl", 1, 
events.get(0));
@@ -433,6 +441,14 @@ public class RangerAuditLogTest extends 
AuthorizationTestBase {
       assertEquals("create database test_db", events.get(0).getRequestData());
     }, "create database test_db");
 
+    authzError(events -> {
+      assertEquals(1, events.size());
+      assertEventEquals("@database", "alter", "functional", 0, events.get(0));
+      assertEquals("alter database functional set dbproperties ('a'='b')",
+          events.get(0).getRequestData());
+    }, "alter database functional set dbproperties ('a'='b')",
+        onDatabase("functional", TPrivilegeLevel.SELECT));
+
     authzError(events -> {
       assertEquals(1, events.size());
       assertEventEquals("@table", "create", "functional/test_tbl", 0, 
events.get(0));
diff --git 
a/testdata/workloads/functional-query/queries/QueryTest/create-database.test 
b/testdata/workloads/functional-query/queries/QueryTest/create-database.test
index 650db4dab..2cc99803c 100644
--- a/testdata/workloads/functional-query/queries/QueryTest/create-database.test
+++ b/testdata/workloads/functional-query/queries/QueryTest/create-database.test
@@ -231,3 +231,27 @@ drop database $DATABASE_loc
 ---- RESULTS
 'Database has been dropped.'
 ====
+---- QUERY
+# Test CREATE DATABASE WITH DBPROPERTIES
+create database $DATABASE_loc WITH DBPROPERTIES('key0'='value0', 
'key1'='value1')
+---- RESULTS
+'Database has been created.'
+====
+---- QUERY
+# Test that DESCRIBE EXTENDED also has all properties.
+describe database extended $DATABASE_loc
+---- RESULTS
+'$DATABASE_loc','$NAMENODE/$EXTERNAL_WAREHOUSE_DIR/$DATABASE_loc.db',''
+'Owner: ','',''
+'','$USER','USER'
+'Parameter: ','',''
+'','key0','value0'
+'','key1','value1'
+---- TYPES
+string, string, string
+====
+---- QUERY
+drop database $DATABASE_loc
+---- RESULTS
+'Database has been dropped.'
+====
diff --git a/tests/custom_cluster/test_events_custom_configs.py 
b/tests/custom_cluster/test_events_custom_configs.py
index 1c2209209..93099e934 100644
--- a/tests/custom_cluster/test_events_custom_configs.py
+++ b/tests/custom_cluster/test_events_custom_configs.py
@@ -122,6 +122,8 @@ class 
TestEventProcessingCustomConfigsBase(CustomClusterTestSuite):
         # ALTER_DATABASE case
         "comment on database {0} is 'self-event test 
database'".format(db_name),
         "alter database {0} set owner user `test-user`".format(db_name),
+        "alter database {0} set dbproperties ('comment'='self-event test "
+        "database')".format(db_name),
         "create function {0}.f() returns int location '{1}/libTestUdfs.so' "
         "symbol='NoArgs'".format(db_name, WAREHOUSE),
         "drop function {0}.f()".format(db_name),
diff --git a/tests/metadata/test_ddl.py b/tests/metadata/test_ddl.py
index 54989b84a..0c56dd3f4 100644
--- a/tests/metadata/test_ddl.py
+++ b/tests/metadata/test_ddl.py
@@ -267,6 +267,12 @@ class TestDdlStatements(TestDdlBase):
       unique_database)).get_data()
     assert "INT\tf()\tNATIVE\ttrue" == func_names
 
+  def test_alter_database_set_db_properties(self, unique_database):
+    self.client.execute("alter database {0} set dbproperties('k'='v')".format(
+      unique_database))
+    properties = self._get_db_properties(unique_database)
+    assert properties['k'] == 'v'
+
   def test_alter_table_set_owner(self, unique_database):
     table_name = "{0}.test_owner_tbl".format(unique_database)
     self.client.execute("create table {0}(i int)".format(table_name))
diff --git a/tests/metadata/test_ddl_base.py b/tests/metadata/test_ddl_base.py
index f35a7c7da..c37630761 100644
--- a/tests/metadata/test_ddl_base.py
+++ b/tests/metadata/test_ddl_base.py
@@ -74,6 +74,10 @@ class TestDdlBase(ImpalaTestSuite):
     """Extracts the DB properties mapping from the output of DESCRIBE 
FORMATTED"""
     return self._get_properties("Owner:", db_name, True)
 
+  def _get_db_properties(self, db_name):
+    """Extracts the DB properties mapping from the output of DESCRIBE 
FORMATTED"""
+    return self._get_properties("Parameter:", db_name, True)
+
   def _get_property(self, property_name, name, is_db=False):
     """Extracts a db/table property value from the output of DESCRIBE 
FORMATTED."""
     result = self.client.execute("describe {0} formatted {1}".format(

Reply via email to