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

krisztiankasa 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 c18d0df2702 Enable using column identifiers with special characters 
when deleting table column statistics. (#6149)
c18d0df2702 is described below

commit c18d0df2702130cf5d0f050e516eb8999aa56301
Author: Krisztian Kasa <[email protected]>
AuthorDate: Mon Nov 3 15:19:52 2025 +0100

    Enable using column identifiers with special characters when deleting table 
column statistics. (#6149)
---
 .../hadoop/hive/metastore/MetaStoreDirectSql.java  | 44 +++++++++++++++-------
 .../hadoop/hive/metastore/TestObjectStore.java     | 34 +++++++++++++----
 2 files changed, 57 insertions(+), 21 deletions(-)

diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java
index 12c78c347e4..415d7c7f557 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/MetaStoreDirectSql.java
@@ -133,6 +133,7 @@
  */
 class MetaStoreDirectSql {
   private static final int NO_BATCHING = -1, DETECT_BATCHING = 0;
+  private static final Set<String> ALLOWED_TABLES_TO_LOCK = 
Set.of("NOTIFICATION_SEQUENCE");
 
   private static final Logger LOG = 
LoggerFactory.getLogger(MetaStoreDirectSql.class);
   private final PersistenceManager pm;
@@ -3203,6 +3204,11 @@ private void getStatsTableListResult(
   }
 
   public void lockDbTable(String tableName) throws MetaException {
+    // Only certain tables are allowed to be locked, and the API should 
restrict them.
+    if (!ALLOWED_TABLES_TO_LOCK.contains(tableName)) {
+      throw new MetaException("Error while locking table " + tableName);
+    }
+
     String lockCommand = "lock table \"" + tableName + "\" in exclusive mode";
     try {
       executeNoResult(lockCommand);
@@ -3243,19 +3249,26 @@ public void deleteColumnStatsState(long tbl_id) throws 
MetaException {
   }
 
   public boolean deleteTableColumnStatistics(long tableId, List<String> 
colNames, String engine) {
-    String deleteSql = "delete from " + TAB_COL_STATS + " where \"TBL_ID\" = " 
+ tableId;
+    String deleteSql = "delete from " + TAB_COL_STATS + " where \"TBL_ID\" = 
?";
+    List<Object> params = new ArrayList<>(colNames == null ? 2 : 
colNames.size() + 2);
+    params.add(tableId);
+
     if (colNames != null && !colNames.isEmpty()) {
-      deleteSql += " and \"COLUMN_NAME\" in (" + colNames.stream().map(col -> 
"'" + col + "'").collect(Collectors.joining(",")) + ")";
+      deleteSql += " and \"COLUMN_NAME\" in (" + makeParams(colNames.size()) + 
")";
+      params.addAll(colNames);
     }
+
     if (engine != null) {
-      deleteSql += " and \"ENGINE\" = '" + engine + "'";
+      deleteSql += " and \"ENGINE\" = ?";
+      params.add(engine);
     }
-    try {
-      executeNoResult(deleteSql);
-    } catch (SQLException e) {
-      LOG.warn("Error removing table column stats. ", e);
+
+    try (QueryWrapper queryParams = new 
QueryWrapper(pm.newQuery("javax.jdo.query.SQL", deleteSql))) {
+      executeWithArray(queryParams.getInnerQuery(), params.toArray(), 
deleteSql);
+    } catch (MetaException e) {
       return false;
     }
+
     return true;
   }
 
@@ -3269,17 +3282,20 @@ public List<Void> run(List<String> input) throws 
Exception {
             input, Collections.emptyList(), -1);
         if (!partitionIds.isEmpty()) {
           String deleteSql = "delete from " + PART_COL_STATS + " where 
\"PART_ID\" in ( " + getIdListForIn(partitionIds) + ")";
+          List<Object> params = new ArrayList<>(colNames == null ? 1 : 
colNames.size() + 1);
+
           if (colNames != null && !colNames.isEmpty()) {
-            deleteSql += " and \"COLUMN_NAME\" in (" + 
colNames.stream().map(col -> "'" + col + "'").collect(Collectors.joining(",")) 
+ ")";
+            deleteSql += " and \"COLUMN_NAME\" in (" + 
makeParams(colNames.size()) + ")";
+            params.addAll(colNames);
           }
+
           if (engine != null) {
-            deleteSql += " and \"ENGINE\" = '" + engine + "'";
+            deleteSql += " and \"ENGINE\" = ?";
+            params.add(engine);
           }
-          try {
-            executeNoResult(deleteSql);
-          } catch (SQLException e) {
-            LOG.warn("Error removing partition column stats. ", e);
-            throw new MetaException("Error removing partition column stats: " 
+ e.getMessage());
+
+          try (QueryWrapper queryParams = new 
QueryWrapper(pm.newQuery("javax.jdo.query.SQL", deleteSql))) {
+            executeWithArray(queryParams.getInnerQuery(), params.toArray(), 
deleteSql);
           }
         }
         return null;
diff --git 
a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java
 
b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java
index 4a2408b69e2..ae96fdaf67b 100644
--- 
a/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java
+++ 
b/standalone-metastore/metastore-server/src/test/java/org/apache/hadoop/hive/metastore/TestObjectStore.java
@@ -919,14 +919,14 @@ public void testTableStatisticsOps() throws Exception {
     List<ColumnStatistics> tabColStats;
     try (AutoCloseable c = deadline()) {
       tabColStats = objectStore.getTableColumnStatistics(DEFAULT_CATALOG_NAME, 
DB1, TABLE1,
-          Arrays.asList("test_col1", "test_col2"));
+          Arrays.asList("test_col1", "test_col' 2"));
     }
     Assert.assertEquals(0, tabColStats.size());
 
     ColumnStatisticsDesc statsDesc = new ColumnStatisticsDesc(true, DB1, 
TABLE1);
     ColumnStatisticsObj statsObj1 = new ColumnStatisticsObj("test_col1", "int",
         new ColumnStatisticsData(ColumnStatisticsData._Fields.DECIMAL_STATS, 
new DecimalColumnStatsData(100, 1000)));
-    ColumnStatisticsObj statsObj2 = new ColumnStatisticsObj("test_col2", "int",
+    ColumnStatisticsObj statsObj2 = new ColumnStatisticsObj("test_col' 2", 
"int",
         new ColumnStatisticsData(ColumnStatisticsData._Fields.DECIMAL_STATS, 
new DecimalColumnStatsData(200, 2000)));
     ColumnStatistics colStats = new ColumnStatistics(statsDesc, 
Arrays.asList(statsObj1, statsObj2));
     colStats.setEngine(ENGINE);
@@ -934,7 +934,7 @@ public void testTableStatisticsOps() throws Exception {
 
     try (AutoCloseable c = deadline()) {
       tabColStats = objectStore.getTableColumnStatistics(DEFAULT_CATALOG_NAME, 
DB1, TABLE1,
-          Arrays.asList("test_col1", "test_col2"));
+          Arrays.asList("test_col1", "test_col' 2"));
     }
     Assert.assertEquals(1, tabColStats.size());
     Assert.assertEquals(2, tabColStats.get(0).getStatsObjSize());
@@ -942,19 +942,25 @@ public void testTableStatisticsOps() throws Exception {
     objectStore.deleteTableColumnStatistics(DEFAULT_CATALOG_NAME, DB1, TABLE1, 
"test_col1", ENGINE);
     try (AutoCloseable c = deadline()) {
       tabColStats = objectStore.getTableColumnStatistics(DEFAULT_CATALOG_NAME, 
DB1, TABLE1,
-          Arrays.asList("test_col1", "test_col2"));
+          Arrays.asList("test_col1", "test_col' 2"));
     }
     Assert.assertEquals(1, tabColStats.size());
     Assert.assertEquals(1, tabColStats.get(0).getStatsObjSize());
 
-    objectStore.deleteTableColumnStatistics(DEFAULT_CATALOG_NAME, DB1, TABLE1, 
"test_col2", ENGINE);
+    objectStore.deleteTableColumnStatistics(DEFAULT_CATALOG_NAME, DB1, TABLE1, 
"test_col' 2", ENGINE);
     try (AutoCloseable c = deadline()) {
       tabColStats = objectStore.getTableColumnStatistics(DEFAULT_CATALOG_NAME, 
DB1, TABLE1,
-          Arrays.asList("test_col1", "test_col2"));
+          Arrays.asList("test_col1", "test_col' 2"));
     }
     Assert.assertEquals(0, tabColStats.size());
   }
 
+  @Test
+  public void testDeleteTableColumnStatisticsWhenEngineHasSpecialCharacter() 
throws Exception {
+    createPartitionedTable(true, true);
+    objectStore.deleteTableColumnStatistics(DEFAULT_CATALOG_NAME, DB1, TABLE1, 
"test_col1", "special '");
+  }
+
   @Test
   public void testPartitionStatisticsOps() throws Exception {
     createPartitionedTable(true, true);
@@ -1006,6 +1012,14 @@ public void testPartitionStatisticsOps() throws 
Exception {
     Assert.assertEquals(0, stat.size());
   }
 
+  @Test
+  public void 
testDeletePartitionColumnStatisticsWhenEngineHasSpecialCharacter() throws 
Exception {
+    createPartitionedTable(true, true);
+    objectStore.deletePartitionColumnStatistics(DEFAULT_CATALOG_NAME, DB1, 
TABLE1,
+            "test_part_col=a2", List.of("a2"), null, "special '");
+  }
+
+
   @Test
   public void testAggrStatsUseDB() throws Exception {
     Configuration conf2 = MetastoreConf.newMetastoreConf(conf);
@@ -1051,7 +1065,7 @@ private void createPartitionedTable(boolean 
withPrivileges, boolean withStatisti
             .setDbName(DB1)
             .setTableName(TABLE1)
             .addCol("test_col1", "int")
-            .addCol("test_col2", "int")
+            .addCol("test_col' 2", "int")
             .addPartCol("test_part_col", "int")
             .addCol("test_bucket_col", "int", "test bucket col comment")
             .addCol("test_skewed_col", "int", "test skewed col comment")
@@ -1239,6 +1253,12 @@ protected Database 
getJdoResult(ObjectStore.GetHelper<Database> ctx) throws Meta
     Assert.assertEquals(1, directSqlErrors.getCount());
   }
 
+  @Test(expected = MetaException.class)
+  public void testLockDbTableThrowsExceptionWhenTableIsNotAllowedToLock() 
throws Exception {
+    MetaStoreDirectSql metaStoreDirectSql = new 
MetaStoreDirectSql(objectStore.getPersistenceManager(), conf, null);
+    metaStoreDirectSql.lockDbTable("TBLS");
+  }
+
   @Deprecated
   private static void dropAllStoreObjects(RawStore store)
       throws MetaException, InvalidObjectException, InvalidInputException {

Reply via email to