This is an automated email from the ASF dual-hosted git repository.
pvary pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg.git
The following commit(s) were added to refs/heads/main by this push:
new c0250c745d Hive: Fix lock selection during table creation to respect
table properties (#14236)
c0250c745d is described below
commit c0250c745d29ddcf892a06294a35019762f51686
Author: s-sanjay <[email protected]>
AuthorDate: Mon Oct 20 16:07:47 2025 +0530
Hive: Fix lock selection during table creation to respect table properties
(#14236)
---
.../apache/iceberg/hive/HiveTableOperations.java | 2 +-
.../org/apache/iceberg/hive/TestHiveCommits.java | 52 ++++++++++++++++++
.../org/apache/iceberg/hive/TestHiveTable.java | 61 ++++++++++++++++++++++
3 files changed, 114 insertions(+), 1 deletion(-)
diff --git
a/hive-metastore/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
b/hive-metastore/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
index f0183fc5c5..3e4be78169 100644
---
a/hive-metastore/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
+++
b/hive-metastore/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java
@@ -143,7 +143,7 @@ public class HiveTableOperations extends
BaseMetastoreTableOperations
BaseMetastoreOperations.CommitStatus.FAILURE;
boolean updateHiveTable = false;
- HiveLock lock = lockObject(base);
+ HiveLock lock = lockObject(base != null ? base : metadata);
try {
lock.lock();
diff --git
a/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveCommits.java
b/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveCommits.java
index 399cead0cb..f78bb5d3e7 100644
--- a/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveCommits.java
+++ b/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveCommits.java
@@ -22,6 +22,7 @@ import static
org.apache.iceberg.TableProperties.HIVE_LOCK_ENABLED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.doAnswer;
@@ -45,6 +46,9 @@ import
org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
import org.apache.iceberg.types.Types;
import org.apache.thrift.TException;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.support.ReflectionSupport;
public class TestHiveCommits extends HiveTableTestBase {
@@ -504,6 +508,54 @@ public class TestHiveCommits extends HiveTableTestBase {
.hasSameClassAs(initialLock);
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ @NullSource
+ public void testFirstHiveCommitWithLockSetting(Boolean lockEnabled) {
+ TableIdentifier newTableIdentifier = TableIdentifier.of(DB_NAME,
"lock_test_table");
+
+ try {
+ HiveTableOperations ops =
+ new HiveTableOperations(
+ catalog.getConf(),
+ catalog.clientPool(),
+ catalog.newTableOps(newTableIdentifier).io(),
+ catalog.name(),
+ newTableIdentifier.namespace().level(0),
+ newTableIdentifier.name());
+
+ AtomicReference<HiveLock> lockRef = new AtomicReference<>();
+ HiveTableOperations spyOps = spy(ops);
+ TableMetadata metadata =
+ TableMetadata.newTableMetadata(
+ SCHEMA,
+ PartitionSpec.unpartitioned(),
+ catalog.defaultWarehouseLocation(newTableIdentifier),
+ lockEnabled == null
+ ? ImmutableMap.of()
+ : ImmutableMap.of(HIVE_LOCK_ENABLED,
String.valueOf(lockEnabled)));
+ doAnswer(
+ i -> {
+ lockRef.set(ops.lockObject(i.getArgument(0)));
+ return lockRef.get();
+ })
+ .when(spyOps)
+ .lockObject(eq(metadata));
+
+ // Commit with base = null (new table creation)
+ spyOps.commit(null, metadata);
+
+ Class<? extends HiveLock> expectedLockClass =
+ Boolean.FALSE.equals(lockEnabled) ? NoLock.class :
MetastoreLock.class;
+ assertThat(lockRef).as("Lock not captured by the
stub").doesNotHaveNullValue();
+ assertThat(lockRef)
+ .as("Lock mechanism should use (%s)",
expectedLockClass.getSimpleName())
+ .hasValueMatching(lock -> lock.getClass().equals(expectedLockClass));
+ } finally {
+ catalog.dropTable(newTableIdentifier, true);
+ }
+ }
+
private void commitAndThrowException(
HiveTableOperations realOperations, HiveTableOperations spyOperations)
throws TException, InterruptedException {
diff --git
a/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveTable.java
b/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveTable.java
index 031015ec5a..9c6a2b216b 100644
--- a/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveTable.java
+++ b/hive-metastore/src/test/java/org/apache/iceberg/hive/TestHiveTable.java
@@ -26,10 +26,14 @@ import static
org.apache.iceberg.BaseMetastoreTableOperations.METADATA_LOCATION_
import static
org.apache.iceberg.BaseMetastoreTableOperations.PREVIOUS_METADATA_LOCATION_PROP;
import static org.apache.iceberg.BaseMetastoreTableOperations.TABLE_TYPE_PROP;
import static org.apache.iceberg.TableMetadataParser.getFileExtension;
+import static org.apache.iceberg.TableProperties.HIVE_LOCK_ENABLED;
import static org.apache.iceberg.types.Types.NestedField.optional;
import static org.apache.iceberg.types.Types.NestedField.required;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
import java.io.File;
import java.io.IOException;
@@ -39,6 +43,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecordBuilder;
@@ -60,7 +65,9 @@ import org.apache.iceberg.ManifestFile;
import org.apache.iceberg.PartitionSpec;
import org.apache.iceberg.Schema;
import org.apache.iceberg.Table;
+import org.apache.iceberg.TableMetadata;
import org.apache.iceberg.TableMetadataParser;
+import org.apache.iceberg.TableOperations;
import org.apache.iceberg.TableProperties;
import org.apache.iceberg.avro.Avro;
import org.apache.iceberg.avro.AvroSchemaUtil;
@@ -74,6 +81,7 @@ import org.apache.iceberg.exceptions.NotFoundException;
import org.apache.iceberg.hadoop.ConfigProperties;
import org.apache.iceberg.hadoop.HadoopCatalog;
import org.apache.iceberg.io.FileAppender;
+import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
import org.apache.iceberg.relocated.com.google.common.collect.Lists;
import org.apache.iceberg.relocated.com.google.common.collect.Maps;
import org.apache.iceberg.types.Types;
@@ -82,6 +90,8 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
public class TestHiveTable extends HiveTableTestBase {
static final String NON_DEFAULT_DATABASE = "nondefault";
@@ -119,6 +129,57 @@ public class TestHiveTable extends HiveTableTestBase {
assertThat(icebergTable.schema().asStruct()).isEqualTo(SCHEMA.asStruct());
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ @NullSource
+ public void testCreateTableEndToEnd(Boolean lockEnabled) {
+ TableIdentifier newTableIdentifier = TableIdentifier.of(DB_NAME,
"lock_test_table");
+
+ try {
+ // Create table with lock setting via catalog
+ // We will spy the call to create table operation and capture the lock
object being returned
+ HiveCatalog spyCatalog = spy(catalog);
+ AtomicReference<HiveLock> lockRef = new AtomicReference<>();
+ doAnswer(
+ (args) -> {
+ TableOperations ops =
+ catalog.newTableOps(args.getArgument(0,
TableIdentifier.class));
+ HiveTableOperations spyOps = (HiveTableOperations) spy(ops);
+ doAnswer(
+ (lockArgs) -> {
+ lockRef.set(
+ ((HiveTableOperations) ops)
+ .lockObject(lockArgs.getArgument(0,
TableMetadata.class)));
+ return lockRef.get();
+ })
+ .when(spyOps)
+ .lockObject(any());
+ return spyOps;
+ })
+ .when(spyCatalog)
+ .newTableOps(newTableIdentifier);
+
+ spyCatalog.createTable(
+ newTableIdentifier,
+ SCHEMA,
+ PartitionSpec.unpartitioned(),
+ lockEnabled == null
+ ? ImmutableMap.of()
+ : ImmutableMap.of(HIVE_LOCK_ENABLED,
String.valueOf(lockEnabled)));
+
+ Class<? extends HiveLock> expectedLockClass =
+ Boolean.FALSE.equals(lockEnabled) ? NoLock.class :
MetastoreLock.class;
+ assertThat(lockRef).as("Lock not captured by the
stub").doesNotHaveNullValue();
+ assertThat(lockRef)
+ .as(
+ "Table %s created with HIVE_LOCK_ENABLED=%s should be created
with lock class (%s)",
+ newTableIdentifier, lockEnabled,
expectedLockClass.getSimpleName())
+ .hasValueMatching(lock -> lock.getClass().equals(expectedLockClass));
+ } finally {
+ catalog.dropTable(newTableIdentifier, true);
+ }
+ }
+
@Test
public void testRename() {
String renamedTableName = "rename_table_name";