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

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


The following commit(s) were added to refs/heads/master by this push:
     new 00805951af3 HIVE-29608: Support exclusion for automatic column 
statistics deletion (#6767)
00805951af3 is described below

commit 00805951af3ad6dbc1c931a135d73dcdede05aae
Author: Daniel (Hongdan) Zhu <[email protected]>
AuthorDate: Mon Sep 14 19:34:33 2026 -0700

    HIVE-29608: Support exclusion for automatic column statistics deletion 
(#6767)
    
    Co-authored-by: Hongdan Zhu <[email protected]>
---
 .../hive/metastore/StatisticsManagementTask.java   |  17 ++-
 .../hive/metastore/TestStatisticsManagement.java   | 157 ++++++++++++++++++++-
 2 files changed, 164 insertions(+), 10 deletions(-)

diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/StatisticsManagementTask.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/StatisticsManagementTask.java
index f376aa041df..2af90eff6ff 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/StatisticsManagementTask.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/StatisticsManagementTask.java
@@ -47,6 +47,9 @@
  * is older than {@code metastore.column.statistics.retention.period}, and 
deletes them.
  * Individual tables may opt out by setting the table property
  * {@value #STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY} to {@code "true"}.
+ * Entire databases may opt out by setting the database property
+ * {@value #STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY} to {@code "true"},
+ * which excludes all tables in that database regardless of their individual 
table property.
  */
 public class StatisticsManagementTask extends ObjectStore implements 
MetastoreTaskThread {
 
@@ -58,6 +61,8 @@ public class StatisticsManagementTask extends ObjectStore 
implements MetastoreTa
    */
   public static final String STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY =
       "statistics.auto.deletion.exclude";
+  public static final String STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY =
+      "statistics.auto.deletion.exclude";
 
   /** Separator used when building composite map keys; chosen to be safe in 
HMS identifiers. */
   private static final String KEY_SEP = "\0";
@@ -146,7 +151,8 @@ private List<Object[]> 
collectExpiredTableColStats(PersistenceManager pm,
               + "table.database.name, "
               + "table.tableName, "
               + "colName, "
-              + "table.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY + "\")");
+              + "table.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY + "\"), "
+              + "table.database.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY + "\")");
       @SuppressWarnings("unchecked")
       List<Object[]> rows = (List<Object[]>) 
tblQuery.execute(lastAnalyzedThreshold);
       return new ArrayList<>(rows);
@@ -174,7 +180,8 @@ private void deleteExpiredTableColStats(List<Object[]> 
expiredTblRows) throws Ex
       String tblName   = (String) row[2];
       String colName   = (String) row[3];
       String excludeVal = (String) row[4];
-      if (Boolean.parseBoolean(excludeVal)) {
+      String dbExcludeVal = (String) row[5];
+      if (Boolean.parseBoolean(excludeVal) || 
Boolean.parseBoolean(dbExcludeVal)) {
         LOG.info("Skipping auto deletion of table stats for {}.{} due to 
exclude property.",
             dbName, tblName);
         continue;
@@ -222,7 +229,8 @@ private List<Object[]> 
collectExpiredPartitionColStats(PersistenceManager pm,
               + "partition.table.tableName, "
               + "partition.partitionName, "
               + "colName, "
-              + "partition.table.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY + "\")");
+              + "partition.table.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY + "\"), "
+              + "partition.table.database.parameters.get(\"" + 
STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY + "\")");
       @SuppressWarnings("unchecked")
       List<Object[]> rows = (List<Object[]>) 
partQuery.execute(lastAnalyzedThreshold);
       return new ArrayList<>(rows);
@@ -249,7 +257,8 @@ private void deleteExpiredPartitionColStats(List<Object[]> 
expiredPartRows) thro
       String partName  = (String) row[3];
       String colName   = (String) row[4];
       String excludeVal = (String) row[5];
-      if (Boolean.parseBoolean(excludeVal)) {
+      String dbExcludeVal = (String) row[6];
+      if (Boolean.parseBoolean(excludeVal) || 
Boolean.parseBoolean(dbExcludeVal)) {
         LOG.info("Skipping auto deletion of partition stats for {}.{} due to 
exclude property.",
             dbName, tblName);
         continue;
diff --git 
a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestStatisticsManagement.java
 
b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestStatisticsManagement.java
index 8afffd059bf..b3913ba2129 100644
--- 
a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestStatisticsManagement.java
+++ 
b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestStatisticsManagement.java
@@ -206,6 +206,115 @@ public void 
testExcludedTablePartitionStatsAreNotDeleted() throws Exception {
     assertHasPartitionColStats(dbName, tableName, partName, "c1");
   }
 
+  /**
+   * Verifies that setting {@value 
StatisticsManagementTask#STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY}
+   * to {@code "true"} on a database excludes all tables in that database from 
table-level
+   * column statistics auto-deletion, even if the individual table has no 
exclude property set.
+   */
+  @Test
+  public void testExcludedDatabaseTableStatsAreNotDeleted() throws Exception {
+    String dbName = "stats_db8";
+    String tableName = "tbl8";
+    createDbAndTable(dbName, tableName, false, true); // table not excluded, 
db excluded
+    writeTableLevelColStats(dbName, tableName, "c1");
+    assertHasTableColStats(dbName, tableName, "c1");
+    makeAllTableColStatsOlderThanRetention(dbName, tableName);
+
+    runStatisticsManagementTask(conf);
+
+    // Stats must still be present because the database is marked as excluded.
+    assertHasTableColStats(dbName, tableName, "c1");
+  }
+
+  /**
+   * Verifies that setting {@value 
StatisticsManagementTask#STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY}
+   * to {@code "true"} on a database excludes all partition-level column 
statistics for tables in
+   * that database from auto-deletion, even if the individual table has no 
exclude property set.
+   */
+  @Test
+  public void testExcludedDatabasePartitionStatsAreNotDeleted() throws 
Exception {
+    String dbName = "stats_db9";
+    String tableName = "tbl9";
+    String partVal = "p1";
+    createDbAndPartitionedTable(dbName, tableName, false, true); // table not 
excluded, db excluded
+    createPartition(dbName, tableName, partVal);
+
+    String partName = "part_col=" + partVal;
+    writePartitionLevelColStats(dbName, tableName, partName, "c1");
+    assertHasPartitionColStats(dbName, tableName, partName, "c1");
+    makeAllPartitionColStatsOlderThanRetention(dbName, tableName);
+
+    runStatisticsManagementTask(conf);
+
+    // Stats must still be present because the database is marked as excluded.
+    assertHasPartitionColStats(dbName, tableName, partName, "c1");
+  }
+
+  /**
+   * Verifies that setting {@value 
StatisticsManagementTask#STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY}
+   * to {@code "false"} on a database does not prevent deletion. Only an 
explicit {@code "true"}
+   * value on the database excludes its tables; any other value (including 
{@code "false"}) is
+   * treated as not excluded.
+   */
+  @Test
+  public void testDatabaseExcludePropertySetToFalseDoesNotPreventDeletion() 
throws Exception {
+    String dbName = "stats_db10";
+    String tableName = "tbl10";
+
+    Database db = new DatabaseBuilder()
+        .setName(dbName)
+        .setCatalogName(DEFAULT_CATALOG_NAME)
+        .create(client, conf);
+    db.getParameters().put(
+        StatisticsManagementTask.STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY, 
"false");
+    client.alterDatabase(dbName, db);
+
+    TableBuilder tb = new TableBuilder()
+        .inDb(db)
+        .setTableName(tableName)
+        .addCol("c1", "double")
+        .addCol("c2", "string")
+        .setInputFormat("org.apache.hadoop.hive.ql.io.orc.OrcInputFormat")
+        .setOutputFormat("org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat");
+    client.createTable(tb.build(conf));
+    client.flushCache();
+
+    writeTableLevelColStats(dbName, tableName, "c1");
+    assertHasTableColStats(dbName, tableName, "c1");
+    makeAllTableColStatsOlderThanRetention(dbName, tableName);
+
+    runStatisticsManagementTask(conf);
+
+    // "false" is not a valid exclude signal; stats should be deleted.
+    assertNoTableColStats(dbName, tableName, "c1");
+  }
+
+  /**
+   * Verifies that when both the database and the table set the exclude 
property to conflicting
+   * values (db excluded, table explicitly not excluded), the database-level 
exclusion still wins
+   * and stats are preserved.
+   */
+  @Test
+  public void testDatabaseExclusionOverridesTableNonExclusion() throws 
Exception {
+    String dbName = "stats_db11";
+    String tableName = "tbl11";
+    // table's own exclude flag is "false" (explicitly not excluded), but db 
is excluded
+    createDbAndTable(dbName, tableName, false, true);
+    Table t = client.getTable(dbName, tableName);
+    t.getParameters().put(
+        StatisticsManagementTask.STATISTICS_AUTO_DELETION_EXCLUDE_TBLPROPERTY, 
"false");
+    client.alter_table(dbName, tableName, t);
+
+    writeTableLevelColStats(dbName, tableName, "c1");
+    assertHasTableColStats(dbName, tableName, "c1");
+    makeAllTableColStatsOlderThanRetention(dbName, tableName);
+
+    runStatisticsManagementTask(conf);
+
+    // Database-level exclusion should still protect the stats.
+    assertHasTableColStats(dbName, tableName, "c1");
+  }
+
   /**
    * Verifies that fresh table-level column statistics whose {@code 
lastAnalyzed} is within the
    * retention period are not deleted when the task runs.
@@ -257,12 +366,30 @@ public void testFreshPartitionColStatsAreNotDeleted() 
throws Exception {
    * @param exclude   if {@code true}, sets the auto-deletion exclude property 
on the table
    */
   private void createDbAndTable(String dbName, String tableName, boolean 
exclude) throws Exception {
+    createDbAndTable(dbName, tableName, exclude, false);
+  }
+
+  /**
+   * Creates a database (unless it is the default database) and a simple 
two-column test table.
+   *
+   * @param dbName    name of the database to create
+   * @param tableName name of the table to create
+   * @param exclude   if {@code true}, sets the auto-deletion exclude property 
on the table
+   * @param dbExclude if {@code true}, sets the auto-deletion exclude property 
on the database
+   */
+  private void createDbAndTable(String dbName, String tableName, boolean 
exclude,
+                                boolean dbExclude) throws Exception {
     Database db;
     if (!DEFAULT_DATABASE_NAME.equals(dbName)) {
-      db = new DatabaseBuilder()
+      DatabaseBuilder dbBuilder = new DatabaseBuilder()
           .setName(dbName)
-          .setCatalogName(DEFAULT_CATALOG_NAME)
-          .create(client, conf);
+          .setCatalogName(DEFAULT_CATALOG_NAME);
+      db = dbBuilder.create(client, conf);
+      if (dbExclude) {
+        db.getParameters().put(
+            
StatisticsManagementTask.STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY, "true");
+        client.alterDatabase(dbName, db);
+      }
     } else {
       db = client.getDatabase(DEFAULT_CATALOG_NAME, DEFAULT_DATABASE_NAME);
     }
@@ -293,10 +420,28 @@ private void createDbAndTable(String dbName, String 
tableName, boolean exclude)
    */
   private void createDbAndPartitionedTable(String dbName, String tableName,
                                            boolean exclude) throws Exception {
-    Database db = new DatabaseBuilder()
+    createDbAndPartitionedTable(dbName, tableName, exclude, false);
+  }
+
+  /**
+   * Creates a database and a partitioned test table with one partition key 
{@code part_col}.
+   *
+   * @param dbName    name of the database to create
+   * @param tableName name of the table to create
+   * @param exclude   if {@code true}, sets the auto-deletion exclude property 
on the table
+   * @param dbExclude if {@code true}, sets the auto-deletion exclude property 
on the database
+   */
+  private void createDbAndPartitionedTable(String dbName, String tableName,
+                                           boolean exclude, boolean dbExclude) 
throws Exception {
+    DatabaseBuilder dbBuilder = new DatabaseBuilder()
         .setName(dbName)
-        .setCatalogName(DEFAULT_CATALOG_NAME)
-        .create(client, conf);
+        .setCatalogName(DEFAULT_CATALOG_NAME);
+    Database db = dbBuilder.create(client, conf);
+    if (dbExclude) {
+      db.getParameters().put(
+          
StatisticsManagementTask.STATISTICS_AUTO_DELETION_EXCLUDE_DBPROPERTY, "true");
+      client.alterDatabase(dbName, db);
+    }
 
     TableBuilder tb = new TableBuilder()
         .inDb(db)

Reply via email to