From 8efd27a88ee7ddaeeaf00d1a615141409ef04a61 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Thu, 22 Jan 2026 12:37:25 -0500 Subject: [PATCH 1/3] Rewrite delete logic to fix transformed partition types --- .gitignore | 4 + .../altinity/ice/cli/internal/cmd/Delete.java | 73 ++++++++++++++----- .../cli/internal/iceberg/Partitioning.java | 63 ++++++++++++++++ 3 files changed, 122 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 52ecf198..3e63c13b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ target .devbox /.envrc *.credentials +/.idea +/examples/docker-compose/data +devbox.lock +/var/cache diff --git a/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Delete.java b/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Delete.java index 9d773ccb..b88484a8 100644 --- a/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Delete.java +++ b/ice/src/main/java/com/altinity/ice/cli/internal/cmd/Delete.java @@ -9,17 +9,25 @@ */ package com.altinity.ice.cli.internal.cmd; +import com.altinity.ice.cli.Main.PartitionFilter; +import com.altinity.ice.cli.internal.iceberg.Partitioning; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.apache.iceberg.DataFile; -import org.apache.iceberg.FileScanTask; +import org.apache.iceberg.ManifestFile; +import org.apache.iceberg.ManifestFiles; +import org.apache.iceberg.ManifestReader; +import org.apache.iceberg.PartitionSpec; import org.apache.iceberg.RewriteFiles; +import org.apache.iceberg.Snapshot; import org.apache.iceberg.Table; -import org.apache.iceberg.TableScan; import org.apache.iceberg.catalog.TableIdentifier; -import org.apache.iceberg.io.CloseableIterable; +import org.apache.iceberg.expressions.Expression; +import org.apache.iceberg.expressions.Expressions; +import org.apache.iceberg.io.FileIO; import org.apache.iceberg.rest.RESTCatalog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,30 +41,57 @@ private Delete() {} public static void run( RESTCatalog catalog, TableIdentifier tableId, - List partitions, + List partitions, boolean dryRun) throws IOException, URISyntaxException { Table table = catalog.loadTable(tableId); - TableScan scan = table.newScan(); - if (partitions != null && !partitions.isEmpty()) { - org.apache.iceberg.expressions.Expression expr = null; - for (com.altinity.ice.cli.Main.PartitionFilter pf : partitions) { - org.apache.iceberg.expressions.Expression e = null; + + Snapshot currentSnapshot = table.currentSnapshot(); + if (currentSnapshot == null) { + logger.error("There are no snapshots in this table"); + return; + } + + FileIO io = table.io(); + Map specsById = table.specs(); + + List dataManifests = currentSnapshot.dataManifests(io); + List filesToDelete = new ArrayList<>(); + + Expression expression = null; + + if (partitions != null) { + for (PartitionFilter pf : partitions) { + String fieldName = pf.name(); + + Expression fieldExpr = null; for (Object value : pf.values()) { - org.apache.iceberg.expressions.Expression valueExpr = - org.apache.iceberg.expressions.Expressions.equal(pf.name(), value); - e = (e == null) ? valueExpr : org.apache.iceberg.expressions.Expressions.or(e, valueExpr); + Integer transformed = Partitioning.applyTimestampTransform(table, fieldName, value); + if (transformed != null) { + value = transformed; + } + + Expression singleValueExpr = Expressions.equal(fieldName, value); + fieldExpr = + fieldExpr == null ? singleValueExpr : Expressions.or(fieldExpr, singleValueExpr); + } + if (fieldExpr == null) { + continue; } - expr = (expr == null) ? e : org.apache.iceberg.expressions.Expressions.and(expr, e); + expression = expression == null ? fieldExpr : Expressions.and(expression, fieldExpr); } - scan = scan.filter(expr); } - List filesToDelete = new ArrayList<>(); - try (CloseableIterable tasks = scan.planFiles()) { - for (FileScanTask task : tasks) { - filesToDelete.add(task.file()); + for (ManifestFile manifest : dataManifests) { + ManifestReader reader = ManifestFiles.read(manifest, io, specsById); + if (expression != null) { + reader.filterPartitions(expression); + } + try (reader) { + for (DataFile dataFile : reader) { + filesToDelete.add(dataFile); + } } } @@ -73,6 +108,8 @@ public static void run( } rewrite.commit(); } + } else { + logger.info("No files to delete"); } } } diff --git a/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java b/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java index 3a8e8267..c5eb69c4 100644 --- a/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java +++ b/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java @@ -16,6 +16,9 @@ import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.HashMap; @@ -27,6 +30,7 @@ import org.apache.iceberg.PartitionKey; import org.apache.iceberg.PartitionSpec; import org.apache.iceberg.Schema; +import org.apache.iceberg.Table; import org.apache.iceberg.UpdatePartitionSpec; import org.apache.iceberg.data.GenericRecord; import org.apache.iceberg.data.Record; @@ -37,6 +41,7 @@ import org.apache.iceberg.parquet.Parquet; import org.apache.iceberg.transforms.Transform; import org.apache.iceberg.types.Type; +import org.apache.iceberg.types.Types; import org.apache.iceberg.util.SerializableFunction; import org.apache.parquet.column.statistics.Statistics; import org.apache.parquet.hadoop.metadata.BlockMetaData; @@ -49,6 +54,23 @@ public final class Partitioning { private Partitioning() {} + // Formatter with optional time component (2025-01-01 or 2025-01-01T00:00:00) + private static final DateTimeFormatter DATE_TIME_INPUT_FORMATTER = + new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ISO_LOCAL_DATE) + .optionalStart() + .appendLiteral("T") + .append(DateTimeFormatter.ISO_LOCAL_TIME) + .optionalEnd() + .optionalStart() + .appendOffsetId() + .optionalEnd() + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.NANO_OF_SECOND, 0) + .toFormatter(); + public record InferPartitionKeyResult( @Nullable PartitionKey partitionKey, @Nullable String failureReason) { public boolean success() { @@ -347,4 +369,45 @@ public static long toEpochMicros(Object tsValue) { throw new UnsupportedOperationException("unexpected value type: " + tsValue.getClass()); } } + + /** + * Converts a datetime string input value to the table's partition transform unit if it is a + * timestamp transform. The transformed value is the Iceberg internal representation (e.g. days + * since Unix epoch). + * + * @return The timestamp converted to the partition unit as an integer, or null if not + * convertible. + */ + @Nullable + public static Integer applyTimestampTransform(Table table, String fieldName, Object value) { + PartitionField partitionField = getPartitionField(table, fieldName); + if (partitionField == null) return null; + + Transform transform = partitionField.transform(); + if (transform.isIdentity() || !(value instanceof String s)) { + return null; + } + + Type sourceType = table.schema().findType(partitionField.sourceId()); + if (!(sourceType instanceof Types.TimestampType)) { + return null; + } + + long timestampMicros = toEpochMicros(LocalDateTime.parse(s, DATE_TIME_INPUT_FORMATTER)); + + @SuppressWarnings("unchecked") + Transform typedTransform = (Transform) transform; + + return typedTransform.bind(sourceType).apply(timestampMicros); + } + + @Nullable + private static PartitionField getPartitionField(Table table, String fieldName) { + for (PartitionField field : table.spec().fields()) { + if (field.name().equals(fieldName)) { + return field; + } + } + return null; + } } From 0d5fa9e016888a7f7bb8804288ac9541715bf150 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Tue, 17 Feb 2026 13:56:22 -0500 Subject: [PATCH 2/3] Add scenario test for delete partition --- .../scenarios/delete-partition/run.sh.tmpl | 44 +++++++++++++++++++ .../scenarios/delete-partition/scenario.yaml | 14 ++++++ .../cli/internal/iceberg/Partitioning.java | 3 ++ 3 files changed, 61 insertions(+) create mode 100644 ice-rest-catalog/src/test/resources/scenarios/delete-partition/run.sh.tmpl create mode 100644 ice-rest-catalog/src/test/resources/scenarios/delete-partition/scenario.yaml diff --git a/ice-rest-catalog/src/test/resources/scenarios/delete-partition/run.sh.tmpl b/ice-rest-catalog/src/test/resources/scenarios/delete-partition/run.sh.tmpl new file mode 100644 index 00000000..45fc268d --- /dev/null +++ b/ice-rest-catalog/src/test/resources/scenarios/delete-partition/run.sh.tmpl @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +echo "Running delete partition test..." + +# Create namespace +{{ICE_CLI}} --config {{CLI_CONFIG}} create-namespace ${NAMESPACE_NAME} +echo "OK Created namespace: ${NAMESPACE_NAME}" + +# Get the full path to the input file +SCENARIO_DIR="{{SCENARIO_DIR}}" +INPUT_PATH="${SCENARIO_DIR}/${INPUT_FILE}" + +# Create table with partitioning and insert data +{{ICE_CLI}} --config {{CLI_CONFIG}} insert --create-table ${TABLE_NAME} ${INPUT_PATH} --partition="${PARTITION_SPEC}" +echo "OK Inserted data with partitioning into table ${TABLE_NAME}" + +# Delete partition for a specific day +output=$({{ICE_CLI}} --config {{CLI_CONFIG}} delete ${TABLE_NAME} --partition "[{\"name\": \"${TRANSFORMED_PARTITION_COLUMN}\", \"values\": [\"${DELETE_PARTITION_DAY}\"]}]" 2>&1) + +echo "$output" + +line_count=$(echo "$output" | wc -l) + +if [ "$line_count" -ne 1 ]; then + echo "FAIL Expected 1 line, got $line_count" + exit 1 +fi + +if ! echo "$output" | grep -qF "${TRANSFORMED_PARTITION_COLUMN}=${DELETE_PARTITION_DAY}"; then + echo "FAIL Expected output to contain '${TRANSFORMED_PARTITION_COLUMN}=${DELETE_PARTITION_DAY}'" + exit 1 +fi + +echo "OK Deleted partition with value ${DELETE_PARTITION_DAY}" + +# Cleanup +{{ICE_CLI}} --config {{CLI_CONFIG}} delete-table ${TABLE_NAME} +echo "OK Deleted table: ${TABLE_NAME}" + +{{ICE_CLI}} --config {{CLI_CONFIG}} delete-namespace ${NAMESPACE_NAME} +echo "OK Deleted namespace: ${NAMESPACE_NAME}" + +echo "Delete partition test completed successfully" diff --git a/ice-rest-catalog/src/test/resources/scenarios/delete-partition/scenario.yaml b/ice-rest-catalog/src/test/resources/scenarios/delete-partition/scenario.yaml new file mode 100644 index 00000000..9e851803 --- /dev/null +++ b/ice-rest-catalog/src/test/resources/scenarios/delete-partition/scenario.yaml @@ -0,0 +1,14 @@ +name: Delete partition +description: Tests deleting individual partitions by value + +catalogConfig: + warehouse: s3://test-bucket/warehouse + +env: + NAMESPACE_NAME: test_delete_partition + TABLE_NAME: test_delete_partition.tripdata_p_by_day + INPUT_FILE: tripdata.parquet + PARTITION_SPEC: '[{"column":"tpep_pickup_datetime","transform":"day"}]' + PARTITION_COLUMN: tpep_pickup_datetime + TRANSFORMED_PARTITION_COLUMN: tpep_pickup_datetime_day + DELETE_PARTITION_DAY: 2025-01-02 diff --git a/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java b/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java index c5eb69c4..63760825 100644 --- a/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java +++ b/ice/src/main/java/com/altinity/ice/cli/internal/iceberg/Partitioning.java @@ -387,6 +387,9 @@ public static Integer applyTimestampTransform(Table table, String fieldName, Obj if (transform.isIdentity() || !(value instanceof String s)) { return null; } + if (s.isEmpty()) { + return null; + } Type sourceType = table.schema().findType(partitionField.sourceId()); if (!(sourceType instanceof Types.TimestampType)) { From 1a9654fa77b92c80f02511a606751153e6337ca4 Mon Sep 17 00:00:00 2001 From: Andrew Xie Date: Tue, 17 Feb 2026 14:06:37 -0500 Subject: [PATCH 3/3] Fix missing file --- .../scenarios/delete-partition/tripdata.parquet | Bin 0 -> 34142 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ice-rest-catalog/src/test/resources/scenarios/delete-partition/tripdata.parquet diff --git a/ice-rest-catalog/src/test/resources/scenarios/delete-partition/tripdata.parquet b/ice-rest-catalog/src/test/resources/scenarios/delete-partition/tripdata.parquet new file mode 100644 index 0000000000000000000000000000000000000000..35c58987437e2fd8e18e9b864791c3d8ce78f834 GIT binary patch literal 34142 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