Skip to main content

iceberg/catalog/
utils.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Utility functions for catalog operations.
19
20use std::collections::{HashMap, HashSet};
21
22use futures::{TryStreamExt, stream};
23
24use crate::Result;
25use crate::io::FileIO;
26use crate::spec::ManifestFile;
27use crate::table::Table;
28
29const DELETE_CONCURRENCY: usize = 10;
30
31/// Deletes all data and metadata files referenced by the given table metadata.
32///
33/// This mirrors the Java implementation's `CatalogUtil.dropTableData`.
34/// It collects all manifest files, manifest lists, previous metadata files,
35/// statistics files, and partition statistics files, then deletes them.
36///
37/// Data files within manifests are only deleted if the `gc.enabled` table
38/// property is `true` (the default), to avoid corrupting other tables that
39/// may share the same data files.
40pub async fn drop_table_data(table_info: &Table) -> Result<()> {
41    let mut manifest_lists_to_delete: HashSet<String> = HashSet::new();
42    let mut manifests_to_delete: HashMap<String, ManifestFile> = HashMap::new();
43
44    let metadata = table_info.metadata_ref();
45    let io = table_info.file_io();
46    // Load all manifest lists concurrently
47    let results: Vec<_> =
48        futures::future::try_join_all(metadata.snapshots().map(|snapshot| async {
49            let manifest_list = table_info.manifest_list_reader(snapshot).load().await?;
50            Ok::<_, crate::Error>((snapshot.manifest_list().to_string(), manifest_list))
51        }))
52        .await?;
53
54    for (manifest_list_location, manifest_list) in results {
55        if !manifest_list_location.is_empty() {
56            manifest_lists_to_delete.insert(manifest_list_location);
57        }
58        for manifest_file in manifest_list.entries() {
59            manifests_to_delete.insert(manifest_file.manifest_path.clone(), manifest_file.clone());
60        }
61    }
62
63    // Delete data files only if gc.enabled is true, to avoid corrupting shared tables
64    if metadata.table_properties()?.gc_enabled {
65        delete_data_files(io, &manifests_to_delete).await?;
66    }
67
68    // Delete manifest files
69    let manifest_paths: Vec<String> = manifests_to_delete.into_keys().collect();
70    io.delete_stream(stream::iter(manifest_paths)).await?;
71
72    // Delete manifest lists
73    io.delete_stream(stream::iter(manifest_lists_to_delete))
74        .await?;
75
76    // Delete previous metadata files
77    let prev_metadata_paths: Vec<String> = metadata
78        .metadata_log()
79        .iter()
80        .map(|m| m.metadata_file.clone())
81        .collect();
82    io.delete_stream(stream::iter(prev_metadata_paths)).await?;
83
84    // Delete statistics files
85    let stats_paths: Vec<String> = metadata
86        .statistics_iter()
87        .map(|s| s.statistics_path.clone())
88        .collect();
89    io.delete_stream(stream::iter(stats_paths)).await?;
90
91    // Delete partition statistics files
92    let partition_stats_paths: Vec<String> = metadata
93        .partition_statistics_iter()
94        .map(|s| s.statistics_path.clone())
95        .collect();
96    io.delete_stream(stream::iter(partition_stats_paths))
97        .await?;
98
99    // Delete the current metadata file
100    if let Some(location) = table_info.metadata_location() {
101        io.delete(location).await?;
102    }
103
104    Ok(())
105}
106
107/// Reads manifests concurrently and deletes the data files referenced within.
108async fn delete_data_files(
109    io: &FileIO,
110    manifest_files: &HashMap<String, ManifestFile>,
111) -> Result<()> {
112    stream::iter(manifest_files.values().map(Ok))
113        .try_for_each_concurrent(DELETE_CONCURRENCY, |manifest_file| async move {
114            let manifest = manifest_file.load_manifest(io).await?;
115            let data_file_paths = manifest
116                .entries()
117                .iter()
118                .map(|entry| entry.data_file.file_path().to_string())
119                .collect::<Vec<_>>();
120
121            io.delete_stream(stream::iter(data_file_paths)).await
122        })
123        .await
124}