diff --git a/aws/src/integration/java/org/apache/iceberg/aws/glue/GlueTestBase.java b/aws/src/integration/java/org/apache/iceberg/aws/glue/GlueTestBase.java index 122deda1a7ff..a47f7a1e5e66 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/glue/GlueTestBase.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/glue/GlueTestBase.java @@ -61,6 +61,7 @@ public class GlueTestBase { // iceberg static GlueCatalog glueCatalog; static GlueCatalog glueCatalogWithSkipNameValidation; + static GlueCatalog glueCatalogWithForceRegisterTable; static Schema schema = new Schema(Types.NestedField.required(1, "c1", Types.StringType.get(), "c1")); @@ -95,6 +96,17 @@ public static void beforeClass() { null, fileIO, ImmutableMap.of()); + glueCatalogWithForceRegisterTable = new GlueCatalog(); + AwsProperties propertiesForceRegisterTable = new AwsProperties(); + propertiesForceRegisterTable.setGlueCatalogForceRegisterTable(true); + glueCatalogWithForceRegisterTable.initialize( + catalogName, + testBucketPath, + propertiesForceRegisterTable, + glue, + LockManagers.defaultLockManager(), + fileIO, + ImmutableMap.of()); } @AfterClass diff --git a/aws/src/integration/java/org/apache/iceberg/aws/glue/TestGlueCatalogTable.java b/aws/src/integration/java/org/apache/iceberg/aws/glue/TestGlueCatalogTable.java index 4ea625e486bd..ee33788b9484 100644 --- a/aws/src/integration/java/org/apache/iceberg/aws/glue/TestGlueCatalogTable.java +++ b/aws/src/integration/java/org/apache/iceberg/aws/glue/TestGlueCatalogTable.java @@ -42,8 +42,8 @@ import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.exceptions.AlreadyExistsException; +import org.apache.iceberg.exceptions.NoSuchNamespaceException; import org.apache.iceberg.exceptions.NoSuchTableException; -import org.apache.iceberg.exceptions.ValidationException; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableList; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; import org.apache.iceberg.relocated.com.google.common.collect.Maps; @@ -269,14 +269,6 @@ public void testRenameTableFailsToDeleteOldTable() { .databaseName(namespace) .tableInput(TableInput.builder().name(tableName).parameters(Maps.newHashMap()).build()) .build()); - AssertHelpers.assertThrows( - "should fail to rename", - ValidationException.class, - "Input Glue table is not an iceberg table", - () -> - glueCatalog.renameTable( - TableIdentifier.of(namespace, tableName), - TableIdentifier.of(namespace, newTableName))); AssertHelpers.assertThrows( "renamed table should be deleted", EntityNotFoundException.class, @@ -575,11 +567,60 @@ public void testRegisterTable() { String expectedMetadataLocation = ((BaseTable) table).operations().current().metadataFileLocation(); Assertions.assertThat(metadataLocation).isEqualTo(expectedMetadataLocation); + Assertions.assertThat(glueCatalog.loadTable(identifier)).isNotNull(); Assertions.assertThat(glueCatalog.dropTable(identifier, true)).isTrue(); Assertions.assertThat(glueCatalog.dropNamespace(Namespace.of(namespace))).isTrue(); } + @Test + public void testRegisterTableForceRegister() { + String namespace = createNamespace(); + String tableName = getRandomName(); + createTable(namespace, tableName); + TableIdentifier identifier = TableIdentifier.of(namespace, tableName); + Table table = glueCatalogWithForceRegisterTable.loadTable(identifier); + String metadataLocation = ((BaseTable) table).operations().current().metadataFileLocation(); + Assertions.assertThat(glueCatalogWithForceRegisterTable.dropTable(identifier, false)).isTrue(); + Table registeredTable = + glueCatalogWithForceRegisterTable.registerTable(identifier, metadataLocation); + Assertions.assertThat(registeredTable).isNotNull(); + String expectedMetadataLocation = + ((BaseTable) table).operations().current().metadataFileLocation(); + Assertions.assertThat(metadataLocation).isEqualTo(expectedMetadataLocation); + + GetTableResponse response = + glue.getTable(GetTableRequest.builder().databaseName(namespace).name(tableName).build()); + String actualMetadataLocationGlue = + response.table().parameters().get(BaseMetastoreTableOperations.METADATA_LOCATION_PROP); + + Assert.assertEquals( + "Glue Catalog Register Table should not submit a new commit", + expectedMetadataLocation, + actualMetadataLocationGlue); + + Assertions.assertThat(glueCatalogWithForceRegisterTable.loadTable(identifier)).isNotNull(); + Assertions.assertThat(glueCatalogWithForceRegisterTable.dropTable(identifier, true)).isTrue(); + Assertions.assertThat(glueCatalog.dropNamespace(Namespace.of(namespace))).isTrue(); + } + + @Test + public void testRegisterTableNamespaceNotFound() { + String namespace = createNamespace(); + String tableName = getRandomName(); + createTable(namespace, tableName); + Table table = + glueCatalogWithForceRegisterTable.loadTable(TableIdentifier.of(namespace, tableName)); + String metadataLocation = ((BaseTable) table).operations().current().metadataFileLocation(); + AssertHelpers.assertThrows( + "Should fail to register to an unknown namespace", + NoSuchNamespaceException.class, + "not found in Glue", + () -> + glueCatalogWithForceRegisterTable.registerTable( + TableIdentifier.of(getRandomName(), getRandomName()), metadataLocation)); + } + @Test public void testRegisterTableAlreadyExists() { String namespace = createNamespace(); @@ -594,6 +635,55 @@ public void testRegisterTableAlreadyExists() { Assertions.assertThat(glueCatalog.dropNamespace(Namespace.of(namespace))).isTrue(); } + @Test + public void testRegisterTableAlreadyExistsForceRegister() { + String namespace = createNamespace(); + String tableName = getRandomName(); + createTable(namespace, tableName); + TableIdentifier identifier = TableIdentifier.of(namespace, tableName); + Table table = glueCatalogWithForceRegisterTable.loadTable(identifier); + String metadataLocation = ((BaseTable) table).operations().current().metadataFileLocation(); + Assertions.assertThat(glueCatalogWithForceRegisterTable.dropTable(identifier, false)).isTrue(); + Table registeredTable = + glueCatalogWithForceRegisterTable.registerTable(identifier, metadataLocation); + Assertions.assertThat(registeredTable).isNotNull(); + + GetTableResponse response = + glue.getTable(GetTableRequest.builder().databaseName(namespace).name(tableName).build()); + Assert.assertEquals( + "external table type is set after register", + "EXTERNAL_TABLE", + response.table().tableType()); + String actualMetadataLocation = + response.table().parameters().get(BaseMetastoreTableOperations.METADATA_LOCATION_PROP); + Assert.assertEquals( + "metadata location should be updated with registerTable call", + metadataLocation, + actualMetadataLocation); + + // commit new transaction, should create a new metadata file + DataFile dataFile = + DataFiles.builder(partitionSpec) + .withPath("/path/to/data-a.parquet") + .withFileSizeInBytes(10) + .withRecordCount(1) + .build(); + table.newAppend().appendFile(dataFile).commit(); + + metadataLocation = ((BaseTable) table).operations().current().metadataFileLocation(); + // update metadata location + glueCatalogWithForceRegisterTable.registerTable(identifier, metadataLocation); + response = + glue.getTable(GetTableRequest.builder().databaseName(namespace).name(tableName).build()); + String updatedMetadataLocation = + response.table().parameters().get(BaseMetastoreTableOperations.METADATA_LOCATION_PROP); + Assert.assertEquals( + "metadata location should be updated with registerTable call", + metadataLocation, + updatedMetadataLocation); + Assert.assertEquals("Table Version should be updated", "2", response.table().versionId()); + } + @Test public void testTableLevelS3Tags() { String testBucketPath = "s3://" + testBucketName + "/" + testPathPrefix; diff --git a/aws/src/main/java/org/apache/iceberg/aws/AssumeRoleAwsClientFactory.java b/aws/src/main/java/org/apache/iceberg/aws/AssumeRoleAwsClientFactory.java index c7e93879921a..562b205f2e3f 100644 --- a/aws/src/main/java/org/apache/iceberg/aws/AssumeRoleAwsClientFactory.java +++ b/aws/src/main/java/org/apache/iceberg/aws/AssumeRoleAwsClientFactory.java @@ -52,6 +52,7 @@ public GlueClient glue() { return GlueClient.builder() .applyMutation(this::applyAssumeRoleConfigurations) .applyMutation(awsProperties::applyHttpClientConfigurations) + .applyMutation(awsProperties::applyGlueEndpointConfigurations) .build(); } diff --git a/aws/src/main/java/org/apache/iceberg/aws/AwsProperties.java b/aws/src/main/java/org/apache/iceberg/aws/AwsProperties.java index afb44bb2d15a..8cdd54bc65b6 100644 --- a/aws/src/main/java/org/apache/iceberg/aws/AwsProperties.java +++ b/aws/src/main/java/org/apache/iceberg/aws/AwsProperties.java @@ -187,6 +187,18 @@ public class AwsProperties implements Serializable { */ public static final String GLUE_CATALOG_ENDPOINT = "glue.endpoint"; + /** + * If set, Glue will always update the catalog table if the table already exists in glue catalog. + * By default, Glue catalog will only be able to create new table and will throw + * AlreadyExistsException when register an existing table name. + */ + public static final String GLUE_CATALOG_FORCE_REGISTER_TABLE = "glue.force-register-table"; + + public static final boolean GLUE_CATALOG_FORCE_REGISTER_TABLE_DEFAULT = false; + + /** Configure the Glue Catalog S3 FileIO Region to allow cross region s3 access */ + public static final String GLUE_CATALOG_FILE_IO_REGION = "glue.catalog-file-io-region"; + /** * Number of threads to use for uploading parts to S3 (shared pool across all output streams), * default to {@link Runtime#availableProcessors()} @@ -911,6 +923,8 @@ public class AwsProperties implements Serializable { private boolean glueCatalogSkipArchive; private boolean glueCatalogSkipNameValidation; private boolean glueLakeFormationEnabled; + private boolean glueCatalogForceRegisterTable; + private String glueCatalogFileIORegion; private String dynamoDbTableName; private String dynamoDbEndpoint; @@ -970,6 +984,8 @@ public AwsProperties() { this.glueCatalogSkipArchive = GLUE_CATALOG_SKIP_ARCHIVE_DEFAULT; this.glueCatalogSkipNameValidation = GLUE_CATALOG_SKIP_NAME_VALIDATION_DEFAULT; this.glueLakeFormationEnabled = GLUE_LAKEFORMATION_ENABLED_DEFAULT; + this.glueCatalogForceRegisterTable = GLUE_CATALOG_FORCE_REGISTER_TABLE_DEFAULT; + this.glueCatalogFileIORegion = null; this.dynamoDbEndpoint = null; this.dynamoDbTableName = DYNAMODB_TABLE_NAME_DEFAULT; @@ -1030,6 +1046,13 @@ public AwsProperties(Map properties) { this.glueLakeFormationEnabled = PropertyUtil.propertyAsBoolean( properties, GLUE_LAKEFORMATION_ENABLED, GLUE_LAKEFORMATION_ENABLED_DEFAULT); + this.glueCatalogForceRegisterTable = + PropertyUtil.propertyAsBoolean( + properties, + GLUE_CATALOG_FORCE_REGISTER_TABLE, + GLUE_CATALOG_FORCE_REGISTER_TABLE_DEFAULT); + this.glueCatalogFileIORegion = properties.get(GLUE_CATALOG_FILE_IO_REGION); + this.s3FileIoMultipartUploadThreads = PropertyUtil.propertyAsInt( properties, @@ -1252,6 +1275,24 @@ public void setGlueLakeFormationEnabled(boolean glueLakeFormationEnabled) { this.glueLakeFormationEnabled = glueLakeFormationEnabled; } + + public boolean glueCatalogForceRegisterTable() { + return glueCatalogForceRegisterTable; + } + + public void setGlueCatalogForceRegisterTable(boolean glueCatalogForceRegisterTable) { + this.glueCatalogForceRegisterTable = glueCatalogForceRegisterTable; + } + + public String getGlueCatalogFileIORegion() { + return glueCatalogFileIORegion; + } + + public void setGlueCatalogFileIORegion(String glueCatalogFileIORegion) { + this.glueCatalogFileIORegion = glueCatalogFileIORegion; + } + + /** * @deprecated will be removed in 1.4.0, use {@link org.apache.iceberg.aws.s3.S3FileIOProperties} * instead diff --git a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java index db6e8ead7ea4..943bc15accdd 100644 --- a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java +++ b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java @@ -20,7 +20,9 @@ import java.io.Closeable; import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -31,7 +33,9 @@ import org.apache.iceberg.CatalogUtil; import org.apache.iceberg.LockManager; import org.apache.iceberg.TableMetadata; +import org.apache.iceberg.TableMetadataParser; import org.apache.iceberg.TableOperations; +import org.apache.iceberg.aws.AssumeRoleAwsClientFactory; import org.apache.iceberg.aws.AwsClientFactories; import org.apache.iceberg.aws.AwsClientFactory; import org.apache.iceberg.aws.AwsProperties; @@ -49,6 +53,7 @@ import org.apache.iceberg.hadoop.Configurable; import org.apache.iceberg.io.CloseableGroup; import org.apache.iceberg.io.FileIO; +import org.apache.iceberg.io.InputFile; import org.apache.iceberg.relocated.com.google.common.annotations.VisibleForTesting; import org.apache.iceberg.relocated.com.google.common.base.Preconditions; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; @@ -78,6 +83,7 @@ import software.amazon.awssdk.services.glue.model.Table; import software.amazon.awssdk.services.glue.model.TableInput; import software.amazon.awssdk.services.glue.model.UpdateDatabaseRequest; +import software.amazon.awssdk.services.glue.model.UpdateTableRequest; public class GlueCatalog extends BaseMetastoreCatalog implements Closeable, SupportsNamespaces, Configurable { @@ -111,7 +117,8 @@ public GlueCatalog() {} @Override public void initialize(String name, Map properties) { - this.catalogProperties = ImmutableMap.copyOf(properties); + this.catalogProperties = new HashMap<>(); + catalogProperties.putAll(properties); AwsClientFactory awsClientFactory; FileIO catalogFileIO; if (PropertyUtil.propertyAsBoolean( @@ -437,6 +444,81 @@ public void renameTable(TableIdentifier from, TableIdentifier to) { LOG.info("Successfully renamed table from {} to {}", from, to); } + @Override + public org.apache.iceberg.Table registerTable( + TableIdentifier identifier, String metadataFileLocation) { + Preconditions.checkArgument( + isValidIdentifier(identifier), "Table identifier to register is invalid: " + identifier); + Preconditions.checkArgument( + metadataFileLocation != null && !metadataFileLocation.isEmpty(), + "Cannot register an empty metadata file location as a table"); + + // keep the original behavior when force-register-table flag is off + if (!awsProperties.glueCatalogForceRegisterTable()) { + return super.registerTable(identifier, metadataFileLocation); + } + + String factoryImpl = + PropertyUtil.propertyAsString(catalogProperties, AwsProperties.CLIENT_FACTORY, null); + if (factoryImpl != null && factoryImpl.equals(AssumeRoleAwsClientFactory.class.getName())) { + // overwrite client assume_role_region for file IO to make cross region call + String catalogFileIORegion = awsProperties.getGlueCatalogFileIORegion(); + if (catalogFileIORegion != null) { + catalogProperties.put(AwsProperties.CLIENT_ASSUME_ROLE_REGION, catalogFileIORegion); + } + } + + TableOperations ops = newTableOps(identifier); + InputFile metadataFile = ops.io().newInputFile(metadataFileLocation); + TableMetadata metadata = TableMetadataParser.read(ops.io(), metadataFile); + + Map tableParameters = + ImmutableMap.of( + BaseMetastoreTableOperations.TABLE_TYPE_PROP, + BaseMetastoreTableOperations.ICEBERG_TABLE_TYPE_VALUE.toLowerCase(Locale.ENGLISH), + BaseMetastoreTableOperations.METADATA_LOCATION_PROP, + metadataFileLocation); + + String databaseName = + IcebergToGlueConverter.getDatabaseName( + identifier, awsProperties.glueCatalogSkipNameValidation()); + String tableName = + IcebergToGlueConverter.getTableName( + identifier, awsProperties.glueCatalogSkipNameValidation()); + + TableInput tableInput = + TableInput.builder() + .applyMutation( + builder -> + IcebergToGlueConverter.setTableInputInformation( + builder, metadata, tableParameters)) + .name(tableName) + .tableType(GlueTableOperations.GLUE_EXTERNAL_TABLE_TYPE) + .parameters(tableParameters) + .build(); + + try { + glue.createTable( + CreateTableRequest.builder().databaseName(databaseName).tableInput(tableInput).build()); + } catch (software.amazon.awssdk.services.glue.model.AlreadyExistsException e) { + GetTableResponse response = + glue.getTable( + GetTableRequest.builder().databaseName(databaseName).name(tableName).build()); + String versionId = response.table().versionId(); + glue.updateTable( + UpdateTableRequest.builder() + .databaseName(databaseName) + .tableInput(tableInput) + .versionId(versionId) + .build()); + } catch (EntityNotFoundException e) { + throw new NoSuchNamespaceException( + e, "Namespace %s is not found in Glue", identifier.namespace()); + } + + return loadTable(identifier); + } + @Override public void createNamespace(Namespace namespace, Map metadata) { try { diff --git a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueTableOperations.java b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueTableOperations.java index 6e53e707aa09..2550c4dd805e 100644 --- a/aws/src/main/java/org/apache/iceberg/aws/glue/GlueTableOperations.java +++ b/aws/src/main/java/org/apache/iceberg/aws/glue/GlueTableOperations.java @@ -65,7 +65,7 @@ class GlueTableOperations extends BaseMetastoreTableOperations { // same as org.apache.hadoop.hive.metastore.TableType.EXTERNAL_TABLE // more details: https://docs.aws.amazon.com/glue/latest/webapi/API_TableInput.html - private static final String GLUE_EXTERNAL_TABLE_TYPE = "EXTERNAL_TABLE"; + static final String GLUE_EXTERNAL_TABLE_TYPE = "EXTERNAL_TABLE"; private final GlueClient glue; private final AwsProperties awsProperties; @@ -318,7 +318,8 @@ void persistGlueTable( TableInput.builder() .applyMutation( builder -> - IcebergToGlueConverter.setTableInputInformation(builder, metadata)) + IcebergToGlueConverter.setTableInputInformation( + builder, metadata, parameters)) .name(tableName) .tableType(GLUE_EXTERNAL_TABLE_TYPE) .parameters(parameters) @@ -340,7 +341,8 @@ void persistGlueTable( TableInput.builder() .applyMutation( builder -> - IcebergToGlueConverter.setTableInputInformation(builder, metadata)) + IcebergToGlueConverter.setTableInputInformation( + builder, metadata, parameters)) .name(tableName) .tableType(GLUE_EXTERNAL_TABLE_TYPE) .parameters(parameters) diff --git a/aws/src/main/java/org/apache/iceberg/aws/glue/IcebergToGlueConverter.java b/aws/src/main/java/org/apache/iceberg/aws/glue/IcebergToGlueConverter.java index b2ed64974582..d4d726884930 100644 --- a/aws/src/main/java/org/apache/iceberg/aws/glue/IcebergToGlueConverter.java +++ b/aws/src/main/java/org/apache/iceberg/aws/glue/IcebergToGlueConverter.java @@ -216,7 +216,9 @@ static String getTableName(TableIdentifier tableIdentifier, boolean skipNameVali * @param metadata Iceberg table metadata */ static void setTableInputInformation( - TableInput.Builder tableInputBuilder, TableMetadata metadata) { + TableInput.Builder tableInputBuilder, + TableMetadata metadata, + Map parameters) { try { StorageDescriptor.Builder storageDescriptor = StorageDescriptor.builder(); if (!SET_ADDITIONAL_LOCATIONS.isNoop()) { @@ -229,7 +231,11 @@ static void setTableInputInformation( } tableInputBuilder.storageDescriptor( - storageDescriptor.location(metadata.location()).columns(toColumns(metadata)).build()); + storageDescriptor + .location(metadata.location()) + .columns(toColumns(metadata)) + .parameters(parameters) + .build()); } catch (RuntimeException e) { LOG.warn( "Encountered unexpected exception while converting Iceberg metadata to Glue table information", diff --git a/aws/src/test/java/org/apache/iceberg/aws/glue/TestGlueCatalog.java b/aws/src/test/java/org/apache/iceberg/aws/glue/TestGlueCatalog.java index a915a97d0ece..3f481658ee79 100644 --- a/aws/src/test/java/org/apache/iceberg/aws/glue/TestGlueCatalog.java +++ b/aws/src/test/java/org/apache/iceberg/aws/glue/TestGlueCatalog.java @@ -386,6 +386,36 @@ public Object answer(InvocationOnMock invocation) throws Throwable { Assert.assertEquals(0, counter.get()); } + @Test + public void testRegisterTableInvalidIdentifier() { + AssertHelpers.assertThrows( + "Should not allow registering table with multi-level namespace", + IllegalArgumentException.class, + "Table identifier to register is invalid", + () -> glueCatalog.registerTable(TableIdentifier.of("a", "b", "name"), "s3://path")); + + AssertHelpers.assertThrows( + "Should not allow registering table with unsupported table name", + IllegalArgumentException.class, + "Table identifier to register is invalid", + () -> glueCatalog.registerTable(TableIdentifier.of("a", "$name"), "s3://path")); + } + + @Test + public void testRegisterTableWithBadLocation() { + AssertHelpers.assertThrows( + "Should not allow registering null location", + IllegalArgumentException.class, + "Cannot register an empty metadata file location as a table", + () -> glueCatalog.registerTable(TableIdentifier.of("a", "name"), null)); + + AssertHelpers.assertThrows( + "Should not allow registering empty location", + IllegalArgumentException.class, + "Cannot register an empty metadata file location as a table", + () -> glueCatalog.registerTable(TableIdentifier.of("a", "name"), "")); + } + @Test public void testCreateNamespace() { Mockito.doReturn(CreateDatabaseResponse.builder().build()) diff --git a/aws/src/test/java/org/apache/iceberg/aws/glue/TestIcebergToGlueConverter.java b/aws/src/test/java/org/apache/iceberg/aws/glue/TestIcebergToGlueConverter.java index b29247af65f7..3c8a62b7560d 100644 --- a/aws/src/test/java/org/apache/iceberg/aws/glue/TestIcebergToGlueConverter.java +++ b/aws/src/test/java/org/apache/iceberg/aws/glue/TestIcebergToGlueConverter.java @@ -18,6 +18,7 @@ */ package org.apache.iceberg.aws.glue; +import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.iceberg.PartitionSpec; @@ -164,7 +165,8 @@ public void testSetTableInputInformation() { PartitionSpec.builderFor(schema).identity("x").withSpecId(1000).build(); TableMetadata tableMetadata = TableMetadata.newTableMetadata(schema, partitionSpec, "s3://test", tableLocationProperties); - IcebergToGlueConverter.setTableInputInformation(actualTableInputBuilder, tableMetadata); + IcebergToGlueConverter.setTableInputInformation( + actualTableInputBuilder, tableMetadata, Collections.emptyMap()); TableInput actualTableInput = actualTableInputBuilder.build(); // Expected TableInput @@ -233,7 +235,8 @@ public void testSetTableInputInformationWithRemovedColumns() { Schema newSchema = new Schema(Types.NestedField.required(1, "x", Types.StringType.get(), "comment1")); tableMetadata = tableMetadata.updateSchema(newSchema, 3); - IcebergToGlueConverter.setTableInputInformation(actualTableInputBuilder, tableMetadata); + IcebergToGlueConverter.setTableInputInformation( + actualTableInputBuilder, tableMetadata, Collections.emptyMap()); TableInput actualTableInput = actualTableInputBuilder.build(); // Expected TableInput