diff --git a/plugin/trino-iceberg/pom.xml b/plugin/trino-iceberg/pom.xml
index d52279b25df7..9bf224dc3e28 100644
--- a/plugin/trino-iceberg/pom.xml
+++ b/plugin/trino-iceberg/pom.xml
@@ -382,6 +382,12 @@
provided
+
+ com.azure
+ azure-security-keyvault-keys
+ runtime
+
+
com.azure
azure-storage-blob
@@ -406,6 +412,13 @@
+
+ com.google.cloud.gcs.analytics
+ gcs-analytics-core
+ 1.2.1
+ runtime
+
+
io.airlift
http-client
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergFileWriterFactory.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergFileWriterFactory.java
index 332b3117ecdc..68b23b38685b 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergFileWriterFactory.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergFileWriterFactory.java
@@ -87,14 +87,12 @@
import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
-import static org.apache.iceberg.TableProperties.DEFAULT_WRITE_METRICS_MODE;
import static org.apache.iceberg.io.DeleteSchemaUtil.pathPosSchema;
import static org.apache.iceberg.parquet.ParquetSchemaUtil.convert;
public class IcebergFileWriterFactory
{
private static final Schema POSITION_DELETE_SCHEMA = pathPosSchema();
- private static final MetricsConfig FULL_METRICS_CONFIG = MetricsConfig.fromProperties(ImmutableMap.of(DEFAULT_WRITE_METRICS_MODE, "full"));
private static final Splitter COLUMN_NAMES_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private final TypeManager typeManager;
@@ -151,8 +149,8 @@ public IcebergFileWriter createPositionDeleteWriter(
Map storageProperties)
{
return switch (fileFormat) {
- case PARQUET -> createParquetWriter(FULL_METRICS_CONFIG, fileSystem, outputPath, POSITION_DELETE_SCHEMA, session, storageProperties);
- case ORC -> createOrcWriter(FULL_METRICS_CONFIG, fileSystem, outputPath, POSITION_DELETE_SCHEMA, session, storageProperties, DataSize.ofBytes(Integer.MAX_VALUE));
+ case PARQUET -> createParquetWriter(MetricsConfig.forPositionDelete(), fileSystem, outputPath, POSITION_DELETE_SCHEMA, session, storageProperties);
+ case ORC -> createOrcWriter(MetricsConfig.forPositionDelete(), fileSystem, outputPath, POSITION_DELETE_SCHEMA, session, storageProperties, DataSize.ofBytes(Integer.MAX_VALUE));
case AVRO -> createAvroWriter(fileSystem, outputPath, POSITION_DELETE_SCHEMA, storageProperties);
};
}
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java
index fcdf13d1e6e2..9ce674484448 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java
@@ -3324,7 +3324,7 @@ private void finishWrite(ConnectorSession session, IcebergTableHandle table, Col
switch (task.content()) {
case DATA -> dataTasks.add(task);
case POSITION_DELETES -> deleteTasks.add(task);
- case EQUALITY_DELETES -> throw new UnsupportedOperationException("Unsupported task content: " + task.content());
+ case EQUALITY_DELETES, DATA_MANIFEST, DELETE_MANIFEST -> throw new UnsupportedOperationException("Unsupported task content: " + task.content());
}
}
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSink.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSink.java
index 8b51e6f3b3a1..b887d4924f54 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSink.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergPageSink.java
@@ -167,7 +167,7 @@ public IcebergPageSink(
this.jsonCodec = requireNonNull(jsonCodec, "jsonCodec is null");
this.session = requireNonNull(session, "session is null");
this.fileFormat = requireNonNull(fileFormat, "fileFormat is null");
- this.metricsConfig = MetricsConfig.fromProperties(requireNonNull(storageProperties, "storageProperties is null"));
+ this.metricsConfig = MetricsConfig.from(requireNonNull(storageProperties, "storageProperties is null"), null, null);
this.maxOpenWriters = maxOpenWriters;
this.pagePartitioner = new PagePartitioner(pageIndexerFactory, toPartitionColumns(partitionColumns, partitionSpec, outputSchema));
this.targetMaxFileSize = IcebergSessionProperties.getTargetMaxFileSize(session);
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java
index e579cf300bbe..6c0460fc3ae4 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergSplitSource.java
@@ -363,7 +363,7 @@ private synchronized Iterator prepareFileTasksIterator(L
}
yield isUnconstrainedPathAndTimeDomain();
}
- case DATA -> throw new IllegalStateException("Unexpected delete file: " + deleteFile);
+ case DATA, DATA_MANIFEST, DELETE_MANIFEST -> throw new IllegalStateException("Unexpected delete file: " + deleteFile);
})
.collect(toImmutableList());
scannedFiles.add(new DataFileWithDeleteFiles(wholeFileTask.file(), fullyAppliedDeletes));
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/delete/DeleteManager.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/delete/DeleteManager.java
index 3fcc2d94d12a..7823f3a60865 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/delete/DeleteManager.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/delete/DeleteManager.java
@@ -85,7 +85,7 @@ public Optional getDeletePredicate(
}
}
case EQUALITY_DELETES -> equalityDeleteFiles.add(deleteFile);
- case DATA -> throw new VerifyException("DATA is not delete file type");
+ case DATA, DATA_MANIFEST, DELETE_MANIFEST -> throw new VerifyException("DATA is not delete file type");
}
}
diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/fileio/ForwardingFileIo.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/fileio/ForwardingFileIo.java
index 81e58b6b9c2d..dd92a6cc532a 100644
--- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/fileio/ForwardingFileIo.java
+++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/fileio/ForwardingFileIo.java
@@ -23,6 +23,7 @@
import org.apache.iceberg.DataFile;
import org.apache.iceberg.DeleteFile;
import org.apache.iceberg.ManifestFile;
+import org.apache.iceberg.ManifestListFile;
import org.apache.iceberg.io.BulkDeletionFailureException;
import org.apache.iceberg.io.InputFile;
import org.apache.iceberg.io.OutputFile;
@@ -149,6 +150,12 @@ public InputFile newInputFile(DeleteFile file)
return SupportsBulkOperations.super.newInputFile(file);
}
+ @Override
+ public InputFile newInputFile(ManifestListFile manifestList)
+ {
+ return SupportsBulkOperations.super.newInputFile(manifestList);
+ }
+
private void deleteBatch(List filesToDelete)
{
try {
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java
index c0669e11cfb3..28d904a1cc71 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java
@@ -7981,6 +7981,77 @@ public void testDescribeOutputWithVersionedTable()
}
}
+ @Test
+ public void testTimeTravelWithFilterOnRenamedColumn()
+ {
+ testTimeTravelWithFilterOnRenamedColumn(false);
+ testTimeTravelWithFilterOnRenamedColumn(true);
+ }
+
+ private void testTimeTravelWithFilterOnRenamedColumn(boolean partitioned)
+ {
+ String partition = partitioned ? "WITH (partitioning = ARRAY['part'])" : "";
+ try (TestTable table = newTrinoTable("time_travel_with_filter_on_rename_", "(x int, y int, part int)" + partition)) {
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 1, 1), (1, 2, 2), (2, 2, 2)", 3);
+ assertThat(query("SELECT * FROM " + table.getName()))
+ .matches("VALUES (1, 1, 1), (1, 2, 2), (2, 2, 2)");
+ long firstSnapshotId = getCurrentSnapshotId(table.getName());
+
+ assertUpdate("ALTER TABLE " + table.getName() + " RENAME COLUMN x TO renamed_x");
+
+ // generate a new version
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 2, 3)", 1);
+
+ assertThat(query("SELECT * FROM " + table.getName() + " FOR VERSION AS OF " + firstSnapshotId + " WHERE x = 1"))
+ .matches("VALUES (1, 1, 1), (1, 2, 2)");
+ }
+ }
+
+ @Test
+ public void testTimeTravelWithFilterOnDroppedColumn()
+ {
+ testTimeTravelWithFilterOnDroppedColumn(false);
+ testTimeTravelWithFilterOnDroppedColumn(true);
+ }
+
+ private void testTimeTravelWithFilterOnDroppedColumn(boolean partitioned)
+ {
+ String partition = partitioned ? "WITH (partitioning = ARRAY['part'])" : "";
+ try (TestTable table = newTrinoTable("time_travel_with_filter_on_drop_", "(x int, y int, part int)" + partition)) {
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 1, 1), (1, 2, 2), (2, 2, 2)", 3);
+ assertThat(query("SELECT * FROM " + table.getName()))
+ .matches("VALUES (1, 1, 1), (1, 2, 2), (2, 2, 2)");
+ long firstSnapshotId = getCurrentSnapshotId(table.getName());
+
+ assertUpdate("ALTER TABLE " + table.getName() + " DROP COLUMN x");
+
+ // generate a new version
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 2)", 1);
+
+ assertThat(query("SELECT * FROM " + table.getName() + " FOR VERSION AS OF " + firstSnapshotId + " WHERE x = 1"))
+ .matches("VALUES (1, 1, 1), (1, 2, 2)");
+ }
+ }
+
+ @Test
+ public void testTimeTravelWithFilterOnRenamedPartitionColumn()
+ {
+ try (TestTable table = newTrinoTable("time_travel_with_filter_on_drop_", "(x int, part1 int, part2 int) WITH (partitioning = ARRAY['part1', 'part2'])")) {
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 1, 1), (1, 1, 2), (2, 2, 2)", 3);
+ assertThat(query("SELECT * FROM " + table.getName()))
+ .matches("VALUES (1, 1, 1), (1, 1, 2), (2, 2, 2)");
+ long firstSnapshotId = getCurrentSnapshotId(table.getName());
+
+ assertUpdate("ALTER TABLE " + table.getName() + " RENAME COLUMN part1 TO renamed_part");
+
+ // generate a new version
+ assertUpdate("INSERT INTO " + table.getName() + " VALUES (1, 1, 3)", 1);
+
+ assertThat(query("SELECT * FROM " + table.getName() + " FOR VERSION AS OF " + firstSnapshotId + " WHERE part1 = 1"))
+ .matches("VALUES (1, 1, 1), (1, 1, 2)");
+ }
+ }
+
@Test
public void testDeleteRetainsTableHistory()
{
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV2.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV2.java
index 468c8bfd556e..88600ae80579 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV2.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV2.java
@@ -1781,6 +1781,45 @@ void testAnalyzeNoSnapshot()
catalog.dropTable(SESSION, schemaTableName);
}
+ @Test // regression test for https://github.com/trinodb/trino/issues/20511
+ void testRequiredField()
+ {
+ testRequiredField(true);
+ testRequiredField(false);
+ }
+
+ private void testRequiredField(boolean projectionPushdown)
+ {
+ Session projectionPushdownEnabled = Session.builder(getSession())
+ .setCatalogSessionProperty("iceberg", "projection_pushdown_enabled", Boolean.toString(projectionPushdown))
+ .build();
+
+ String table = "test_required_field" + randomNameSuffix();
+ SchemaTableName schemaTableName = new SchemaTableName("tpch", table);
+
+ catalog.newCreateTableTransaction(
+ SESSION,
+ schemaTableName,
+ new Schema(
+ Types.NestedField.optional(1, "id", Types.IntegerType.get()),
+ Types.NestedField.optional(2, "struct", Types.StructType.of(
+ Types.NestedField.required(3, "field", Types.IntegerType.get())))),
+ PartitionSpec.unpartitioned(),
+ SortOrder.unsorted(),
+ Optional.ofNullable(catalog.defaultTableLocation(SESSION, schemaTableName)),
+ ImmutableMap.of())
+ .commitTransaction();
+
+ assertUpdate("INSERT INTO " + table + " VALUES (1, row(10)), (2, NULL)", 2);
+
+ assertThat(query(projectionPushdownEnabled, "SELECT id FROM " + table + " WHERE struct.field IS NOT NULL"))
+ .matches("VALUES 1");
+ assertThat(query(projectionPushdownEnabled, "SELECT id FROM " + table + " WHERE struct.field IS NULL"))
+ .matches("VALUES 2");
+
+ catalog.dropTable(SESSION, schemaTableName);
+ }
+
private void testHighlyNestedFieldPartitioningWithTimestampTransform(String partitioning, String partitionDirectoryRegex, Set expectedPartitionDirectories)
{
String tableName = "test_highly_nested_field_partitioning_with_timestamp_transform_" + randomNameSuffix();
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV3.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV3.java
index 8a591a88d3e6..f4dd5c8ff33f 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV3.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/TestIcebergV3.java
@@ -37,6 +37,7 @@
import org.apache.iceberg.PartitionSpec;
import org.apache.iceberg.Schema;
import org.apache.iceberg.Snapshot;
+import org.apache.iceberg.SnapshotChanges;
import org.apache.iceberg.SortOrder;
import org.apache.iceberg.Table;
import org.apache.iceberg.TableMetadata;
@@ -704,7 +705,7 @@ void testV3WriteDefault()
BaseTable tempTable = loadTable(temp);
loadTable(tableName).newFastAppend()
- .appendFile(getOnlyElement(tempTable.currentSnapshot().addedDataFiles(tempTable.io())))
+ .appendFile(getOnlyElement(SnapshotChanges.builderFor(tempTable).build().addedDataFiles()))
.commit();
// The 'value' column is missing from the data file and has no initial-default, so it should return NULL
@@ -989,7 +990,7 @@ private void assertV3InsertProducesRowLineageMetadata(String fileFormat)
long totalRecords = 0;
Long expectedLastUpdatedSequenceNumber = null;
- for (DataFile file : snapshot.addedDataFiles(table.io())) {
+ for (DataFile file : SnapshotChanges.builderFor(table).build().addedDataFiles()) {
fileCount++;
totalRecords += file.recordCount();
@@ -1291,7 +1292,7 @@ void testV3RejectsEncryptionKeysInMetadata()
hadoopTableLocation.toString());
icebergTable.newFastAppend()
- .appendFile(getOnlyElement(tempTable.currentSnapshot().addedDataFiles(tempTable.io())))
+ .appendFile(getOnlyElement(SnapshotChanges.builderFor(tempTable).build().addedDataFiles()))
.commit();
// Inject encryption-keys + snapshot key-id into the current metadata.json.
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergPolarisCatalogConnectorSmokeTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergPolarisCatalogConnectorSmokeTest.java
index 95770faa4d4c..d99292c886f2 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergPolarisCatalogConnectorSmokeTest.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergPolarisCatalogConnectorSmokeTest.java
@@ -219,7 +219,7 @@ public void testCreateTableWithTrailingSpaceInLocation()
public void testDropTableWithMissingMetadataFile()
{
assertThatThrownBy(super::testDropTableWithMissingMetadataFile)
- .hasMessageMatching(".* Table '.*' does not exist");
+ .hasMessageMatching("Failed to load table: (.*)");
}
@Test
@@ -243,7 +243,7 @@ public void testDropTableWithMissingManifestListFile()
public void testDropTableWithNonExistentTableLocation()
{
assertThatThrownBy(super::testDropTableWithNonExistentTableLocation)
- .hasMessageMatching(".* Table '.*' does not exist");
+ .hasMessageMatching("Failed to load table: (.*)");
}
@Test
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergRestCatalogNestedNamespaceConnectorSmokeTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergRestCatalogNestedNamespaceConnectorSmokeTest.java
index e98f0e2daea4..61e5a40f13be 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergRestCatalogNestedNamespaceConnectorSmokeTest.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergRestCatalogNestedNamespaceConnectorSmokeTest.java
@@ -234,7 +234,7 @@ public void testDropTableWithMissingSnapshotFile()
assertThatThrownBy(super::testDropTableWithMissingSnapshotFile)
.isInstanceOf(QueryFailedException.class)
.cause()
- .hasMessageContaining("Failed to drop table")
+ .hasMessageMatching("Failed to open input stream for file: .*avro")
.hasNoCause();
}
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergS3TablesConnectorSmokeTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergS3TablesConnectorSmokeTest.java
index 7138f0b08ffe..4a0b588b17d9 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergS3TablesConnectorSmokeTest.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergS3TablesConnectorSmokeTest.java
@@ -180,7 +180,7 @@ public void testCreateTableWithTrailingSpaceInLocation()
public void testRenameTable()
{
assertThatThrownBy(super::testRenameTable)
- .hasStackTraceContaining("Unable to process: RenameTable endpoint is not supported for Glue Catalog");
+ .hasStackTraceContaining("RenameTable endpoint is not supported for Glue Catalog");
}
@Test
diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergTrinoRestCatalogConnectorSmokeTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergTrinoRestCatalogConnectorSmokeTest.java
index ccc9fa724fc5..b3557784d931 100644
--- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergTrinoRestCatalogConnectorSmokeTest.java
+++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/rest/TestIcebergTrinoRestCatalogConnectorSmokeTest.java
@@ -230,7 +230,7 @@ public void testDropTableWithMissingSnapshotFile()
assertThatThrownBy(super::testDropTableWithMissingSnapshotFile)
.isInstanceOf(QueryFailedException.class)
.cause()
- .hasMessageContaining("Failed to drop table")
+ .hasMessageMatching("Failed to open input stream for file: .*avro")
.hasNoCause();
}
diff --git a/plugin/trino-iceberg/src/test/java/org/apache/iceberg/rest/RestCatalogServlet.java b/plugin/trino-iceberg/src/test/java/org/apache/iceberg/rest/RestCatalogServlet.java
index e50c3fb58709..0f38e5ab0711 100644
--- a/plugin/trino-iceberg/src/test/java/org/apache/iceberg/rest/RestCatalogServlet.java
+++ b/plugin/trino-iceberg/src/test/java/org/apache/iceberg/rest/RestCatalogServlet.java
@@ -23,7 +23,6 @@
import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap;
import org.apache.iceberg.relocated.com.google.common.io.CharStreams;
import org.apache.iceberg.rest.HTTPRequest.HTTPMethod;
-import org.apache.iceberg.rest.RESTCatalogAdapter.Route;
import org.apache.iceberg.rest.responses.ErrorResponse;
import org.apache.iceberg.util.Pair;
diff --git a/pom.xml b/pom.xml
index 98f88ee75225..b212988ff73a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -198,7 +198,7 @@
v24.12.0
11.7.0
5.4.2
- 1.10.1
+ 1.11.0
5.18.1
0.13.0
1.20.0
@@ -2365,6 +2365,14 @@
+
+
+ iceberg-release-candidate
+ Iceberg Release Candidate
+ https://repository.apache.org/content/repositories/orgapacheiceberg-1278/
+
+
+