Skip to content

Commit 50ee773

Browse files
authored
Merge pull request #26 from Altinity/7-ice-support-deletes
ice: Added support for deleting partitions
2 parents f44d762 + ee0c915 commit 50ee773

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

examples/scratch/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ ice create-table flowers.iris_no_copy --schema-from-parquet=file://iris.parquet
5050
local-mc cp iris.parquet local/bucket1/flowers/iris_no_copy/
5151
ice insert flowers.iris_no_copy --no-copy s3://bucket1/flowers/iris_no_copy/iris.parquet
5252

53+
# delete partition(By default --dry-run=true is enabled to print the list of partitions that will be deleted)
54+
ice delete nyc.taxis --dry-run=false
55+
5356
# inspect
5457
ice describe
5558

ice/src/main/java/com/altinity/ice/cli/Main.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.altinity.ice.cli.internal.cmd.Check;
1414
import com.altinity.ice.cli.internal.cmd.CreateNamespace;
1515
import com.altinity.ice.cli.internal.cmd.CreateTable;
16+
import com.altinity.ice.cli.internal.cmd.Delete;
1617
import com.altinity.ice.cli.internal.cmd.DeleteNamespace;
1718
import com.altinity.ice.cli.internal.cmd.DeleteTable;
1819
import com.altinity.ice.cli.internal.cmd.Describe;
@@ -27,6 +28,7 @@
2728
import com.fasterxml.jackson.databind.ObjectMapper;
2829
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
2930
import java.io.IOException;
31+
import java.net.URISyntaxException;
3032
import java.util.ArrayList;
3133
import java.util.Arrays;
3234
import java.util.Base64;
@@ -426,6 +428,43 @@ void deleteNamespace(
426428
}
427429
}
428430

431+
@CommandLine.Command(name = "delete", description = "Delete data from catalog.")
432+
void delete(
433+
@CommandLine.Parameters(
434+
arity = "1",
435+
paramLabel = "<name>",
436+
description = "Table name (e.g. ns1.table1)")
437+
String name,
438+
@CommandLine.Option(
439+
names = {"--partition"},
440+
description =
441+
"JSON array of partition filters: [{\"name\": \"vendorId\", \"values\": [5, 6]}]. "
442+
+ "For timestamp columns, use ISO Datetime format YYYY-MM-ddTHH:mm:ss")
443+
String partitionJson,
444+
@CommandLine.Option(
445+
names = "--dry-run",
446+
description = "Log files that would be deleted without actually deleting them",
447+
defaultValue = "true")
448+
boolean dryRun)
449+
throws IOException {
450+
try (RESTCatalog catalog = loadCatalog(this.configFile())) {
451+
List<PartitionFilter> partitions = new ArrayList<>();
452+
if (partitionJson != null && !partitionJson.isEmpty()) {
453+
ObjectMapper mapper = newObjectMapper();
454+
PartitionFilter[] parts = mapper.readValue(partitionJson, PartitionFilter[].class);
455+
partitions = Arrays.asList(parts);
456+
}
457+
TableIdentifier tableId = TableIdentifier.parse(name);
458+
459+
Delete.run(catalog, tableId, partitions, dryRun);
460+
} catch (URISyntaxException e) {
461+
throw new RuntimeException(e);
462+
}
463+
}
464+
465+
public record PartitionFilter(
466+
@JsonProperty("name") String name, @JsonProperty("values") List<Object> values) {}
467+
429468
private RESTCatalog loadCatalog() throws IOException {
430469
return loadCatalog(this.configFile());
431470
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2025 Altinity Inc and/or its affiliates. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*/
10+
package com.altinity.ice.cli.internal.cmd;
11+
12+
import java.io.IOException;
13+
import java.net.URISyntaxException;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
import org.apache.iceberg.DataFile;
17+
import org.apache.iceberg.FileScanTask;
18+
import org.apache.iceberg.RewriteFiles;
19+
import org.apache.iceberg.Table;
20+
import org.apache.iceberg.TableScan;
21+
import org.apache.iceberg.catalog.TableIdentifier;
22+
import org.apache.iceberg.io.CloseableIterable;
23+
import org.apache.iceberg.rest.RESTCatalog;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
27+
public final class Delete {
28+
29+
private static final Logger logger = LoggerFactory.getLogger(Delete.class);
30+
31+
private Delete() {}
32+
33+
public static void run(
34+
RESTCatalog catalog,
35+
TableIdentifier tableId,
36+
List<com.altinity.ice.cli.Main.PartitionFilter> partitions,
37+
boolean dryRun)
38+
throws IOException, URISyntaxException {
39+
40+
Table table = catalog.loadTable(tableId);
41+
TableScan scan = table.newScan();
42+
if (partitions != null && !partitions.isEmpty()) {
43+
org.apache.iceberg.expressions.Expression expr = null;
44+
for (com.altinity.ice.cli.Main.PartitionFilter pf : partitions) {
45+
org.apache.iceberg.expressions.Expression e = null;
46+
for (Object value : pf.values()) {
47+
org.apache.iceberg.expressions.Expression valueExpr =
48+
org.apache.iceberg.expressions.Expressions.equal(pf.name(), value);
49+
e = (e == null) ? valueExpr : org.apache.iceberg.expressions.Expressions.or(e, valueExpr);
50+
}
51+
expr = (expr == null) ? e : org.apache.iceberg.expressions.Expressions.and(expr, e);
52+
}
53+
scan = scan.filter(expr);
54+
}
55+
List<DataFile> filesToDelete = new ArrayList<>();
56+
57+
try (CloseableIterable<FileScanTask> tasks = scan.planFiles()) {
58+
for (FileScanTask task : tasks) {
59+
filesToDelete.add(task.file());
60+
}
61+
}
62+
63+
if (!filesToDelete.isEmpty()) {
64+
if (dryRun) {
65+
logger.info("Dry run: The following files would be deleted:");
66+
for (DataFile file : filesToDelete) {
67+
logger.info(" {}", file.path());
68+
}
69+
} else {
70+
RewriteFiles rewrite = table.newRewrite();
71+
for (DataFile deleteFile : filesToDelete) {
72+
rewrite.deleteFile(deleteFile);
73+
}
74+
rewrite.commit();
75+
logger.info("Partition(s) deleted.");
76+
}
77+
} else {
78+
logger.info("No files found for the partition(s).");
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)