Copilot commented on code in PR #6872:
URL: https://github.com/apache/hive/pull/6872#discussion_r4274058554


##########
iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java:
##########
@@ -171,13 +171,27 @@ public class HiveIcebergMetaHook extends 
BaseHiveIcebergMetaHook {
   private AlterTableType currentAlterTableOp;
   private HiveLock commitLock;
   private List<SQLDefaultConstraint> sqlDefaultConstraints;
+  private boolean commitPhaseStarted;

Review Comment:
   `commitPhaseStarted` is set to `true` in `commitCreateTable` but is never 
reset. If the same `HiveIcebergMetaHook` instance is reused across multiple 
CREATE operations, a later rollback could incorrectly satisfy 
`commitPhaseStarted` and reuse stale `tableProperties`, potentially dropping 
the wrong table. Consider resetting the hook state at the start/end of a CREATE 
lifecycle (e.g., set `commitPhaseStarted=false` and clear `tableProperties` in 
the appropriate pre/commit/rollback paths) so rollback decisions cannot be 
influenced by a prior operation.



##########
iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java:
##########
@@ -171,13 +171,27 @@ public class HiveIcebergMetaHook extends 
BaseHiveIcebergMetaHook {
   private AlterTableType currentAlterTableOp;
   private HiveLock commitLock;
   private List<SQLDefaultConstraint> sqlDefaultConstraints;
+  private boolean commitPhaseStarted;
 
   public HiveIcebergMetaHook(Configuration conf) {
     super(conf);
   }
 
+  @Override
+  public void rollbackCreateTable(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) {
+    if (commitPhaseStarted && tableProperties != null) {
+      try {
+        Catalogs.dropTable(conf, tableProperties);
+      } catch (Exception e) {
+        LOG.warn("Failed to clean up table during rollback of CREATE TABLE for 
{}.{}",
+            hmsTable.getDbName(), hmsTable.getTableName(), e);
+      }
+    }
+  }
+
   @Override
   public void commitCreateTable(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) {

Review Comment:
   `commitPhaseStarted` is set to `true` in `commitCreateTable` but is never 
reset. If the same `HiveIcebergMetaHook` instance is reused across multiple 
CREATE operations, a later rollback could incorrectly satisfy 
`commitPhaseStarted` and reuse stale `tableProperties`, potentially dropping 
the wrong table. Consider resetting the hook state at the start/end of a CREATE 
lifecycle (e.g., set `commitPhaseStarted=false` and clear `tableProperties` in 
the appropriate pre/commit/rollback paths) so rollback decisions cannot be 
influenced by a prior operation.



##########
iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/HiveIcebergMetaHook.java:
##########
@@ -171,13 +171,27 @@ public class HiveIcebergMetaHook extends 
BaseHiveIcebergMetaHook {
   private AlterTableType currentAlterTableOp;
   private HiveLock commitLock;
   private List<SQLDefaultConstraint> sqlDefaultConstraints;
+  private boolean commitPhaseStarted;
 
   public HiveIcebergMetaHook(Configuration conf) {
     super(conf);
   }
 
+  @Override
+  public void rollbackCreateTable(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) {
+    if (commitPhaseStarted && tableProperties != null) {
+      try {
+        Catalogs.dropTable(conf, tableProperties);
+      } catch (Exception e) {
+        LOG.warn("Failed to clean up table during rollback of CREATE TABLE for 
{}.{}",
+            hmsTable.getDbName(), hmsTable.getTableName(), e);
+      }
+    }
+  }
+
   @Override
   public void commitCreateTable(org.apache.hadoop.hive.metastore.api.Table 
hmsTable) {
+    commitPhaseStarted = true;
     if (HiveTableUtil.isIcebergView(hmsTable)) {
       tableProperties = IcebergTableProperties.getTableProperties(hmsTable, 
conf);

Review Comment:
   Rollback cleanup is skipped when `tableProperties` is null, even if the 
commit phase has started. If the failure happens before `tableProperties` is 
populated for this operation, the HMS orphan problem may persist. A more robust 
approach is to derive the identifier/properties needed to drop from `hmsTable` 
(db/table name) when `tableProperties` is not available, or ensure 
`tableProperties` is always initialized before entering commit work.



##########
iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergStorageHandlerNoScan.java:
##########
@@ -720,6 +720,51 @@ public void testCreateTableError() {
     }
   }
 
+  @Test
+  public void testCreateTableRollbackCleansUpHMSEntry() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    // Creating with format-version=2 should fail, and the HMS entry should be 
cleaned up.
+    Assertions.assertThatThrownBy(
+        () -> shell.executeStatement(
+            "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+                "STORED BY ICEBERG STORED AS PARQUET " +
+                "TBLPROPERTIES ('format-version'='2')"))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("timestamp_ns is not supported until v3");

Review Comment:
   This assertion is fairly brittle (exact exception type and a very specific 
message fragment). If upstream/driver layers wrap exceptions differently, the 
test may fail for non-behavioral reasons. Consider asserting on the root cause 
(or a broader exception contract) and/or checking a more stable portion of the 
error (e.g., format-version incompatibility) rather than a specific wording.



##########
iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergStorageHandlerNoScan.java:
##########
@@ -720,6 +720,51 @@ public void testCreateTableError() {
     }
   }
 
+  @Test
+  public void testCreateTableRollbackCleansUpHMSEntry() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    // Creating with format-version=2 should fail, and the HMS entry should be 
cleaned up.
+    Assertions.assertThatThrownBy(
+        () -> shell.executeStatement(
+            "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+                "STORED BY ICEBERG STORED AS PARQUET " +
+                "TBLPROPERTIES ('format-version'='2')"))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("timestamp_ns is not supported until v3");
+
+    // Without the rollback fix, this would fail with AlreadyExistsException
+    // because the HMS entry from the failed create would still exist.
+    shell.executeStatement(
+        "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+            "STORED BY ICEBERG STORED AS PARQUET " +
+            "TBLPROPERTIES ('format-version'='3')");
+
+    shell.executeStatement("DROP TABLE rollback_test");

Review Comment:
   The tests use fixed table names and perform cleanup only at the end. If an 
assertion fails mid-test (or tests run concurrently), these names can collide 
and/or leave residue that affects subsequent tests. Prefer unique table names 
(e.g., with a random/UUID suffix) and ensure cleanup via `try/finally` 
(drop-if-exists) so the metastore is left clean even on failure.



##########
iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergStorageHandlerNoScan.java:
##########
@@ -720,6 +720,51 @@ public void testCreateTableError() {
     }
   }
 
+  @Test
+  public void testCreateTableRollbackCleansUpHMSEntry() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    // Creating with format-version=2 should fail, and the HMS entry should be 
cleaned up.
+    Assertions.assertThatThrownBy(
+        () -> shell.executeStatement(
+            "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+                "STORED BY ICEBERG STORED AS PARQUET " +
+                "TBLPROPERTIES ('format-version'='2')"))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("timestamp_ns is not supported until v3");
+
+    // Without the rollback fix, this would fail with AlreadyExistsException
+    // because the HMS entry from the failed create would still exist.
+    shell.executeStatement(
+        "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+            "STORED BY ICEBERG STORED AS PARQUET " +
+            "TBLPROPERTIES ('format-version'='3')");
+
+    shell.executeStatement("DROP TABLE rollback_test");
+  }
+
+  @Test
+  public void testCreateTableRollbackDoesNotDropExistingTable() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    shell.executeStatement(
+        "CREATE TABLE existing_tbl (id INT) STORED BY ICEBERG STORED AS 
PARQUET");

Review Comment:
   The tests use fixed table names and perform cleanup only at the end. If an 
assertion fails mid-test (or tests run concurrently), these names can collide 
and/or leave residue that affects subsequent tests. Prefer unique table names 
(e.g., with a random/UUID suffix) and ensure cleanup via `try/finally` 
(drop-if-exists) so the metastore is left clean even on failure.



##########
iceberg/iceberg-handler/src/test/java/org/apache/iceberg/mr/hive/TestHiveIcebergStorageHandlerNoScan.java:
##########
@@ -720,6 +720,51 @@ public void testCreateTableError() {
     }
   }
 
+  @Test
+  public void testCreateTableRollbackCleansUpHMSEntry() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    // Creating with format-version=2 should fail, and the HMS entry should be 
cleaned up.
+    Assertions.assertThatThrownBy(
+        () -> shell.executeStatement(
+            "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+                "STORED BY ICEBERG STORED AS PARQUET " +
+                "TBLPROPERTIES ('format-version'='2')"))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("timestamp_ns is not supported until v3");
+
+    // Without the rollback fix, this would fail with AlreadyExistsException
+    // because the HMS entry from the failed create would still exist.
+    shell.executeStatement(
+        "CREATE TABLE rollback_test (id INT, ts TIMESTAMP(9)) " +
+            "STORED BY ICEBERG STORED AS PARQUET " +
+            "TBLPROPERTIES ('format-version'='3')");
+
+    shell.executeStatement("DROP TABLE rollback_test");
+  }
+
+  @Test
+  public void testCreateTableRollbackDoesNotDropExistingTable() {
+    Assume.assumeTrue("Rollback cleanup is only relevant for HiveCatalog",
+        testTableType == TestTableType.HIVE_CATALOG);
+
+    shell.executeStatement(
+        "CREATE TABLE existing_tbl (id INT) STORED BY ICEBERG STORED AS 
PARQUET");
+
+    // Attempting to create a table with the same name should fail with 
AlreadyExistsException.
+    // The rollback must NOT drop the existing table.
+    Assertions.assertThatThrownBy(
+        () -> shell.executeStatement(
+            "CREATE TABLE existing_tbl (id INT) STORED BY ICEBERG STORED AS 
PARQUET"))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessageContaining("already exists");
+
+    // The original table should still be intact.
+    shell.executeStatement("DESCRIBE existing_tbl");
+    shell.executeStatement("DROP TABLE existing_tbl");

Review Comment:
   The tests use fixed table names and perform cleanup only at the end. If an 
assertion fails mid-test (or tests run concurrently), these names can collide 
and/or leave residue that affects subsequent tests. Prefer unique table names 
(e.g., with a random/UUID suffix) and ensure cleanup via `try/finally` 
(drop-if-exists) so the metastore is left clean even on failure.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to