diff --git a/pyiceberg/table/inspect.py b/pyiceberg/table/inspect.py index e580b66dc1..5672931899 100644 --- a/pyiceberg/table/inspect.py +++ b/pyiceberg/table/inspect.py @@ -885,6 +885,8 @@ def _files(self, snapshot_id: int | None = None, data_file_filter: set[DataFileC lambda manifest_list: self._get_files_from_manifest(manifest_list, data_file_filter), snapshot.manifests(io) ) ) + if not results: + return self._get_files_schema().empty_table() return pa.concat_tables(results) def files(self, snapshot_id: int | None = None) -> pa.Table: @@ -965,14 +967,12 @@ def _all_files(self, data_file_filter: set[DataFileContent] | None = None) -> pa """ import pyarrow as pa - snapshots = self.tbl.snapshots() - if not snapshots: - return pa.Table.from_pylist([], schema=self._get_files_schema()) - executor = ExecutorFactory.get_or_create() - manifest_lists = executor.map(lambda snapshot: snapshot.manifests(self.tbl.io), snapshots) + manifest_lists = executor.map(lambda snapshot: snapshot.manifests(self.tbl.io), self.tbl.snapshots()) unique_manifests = {(manifest.manifest_path, manifest) for manifest_list in manifest_lists for manifest in manifest_list} + if not unique_manifests: + return self._get_files_schema().empty_table() file_lists = executor.map( lambda args: self._get_files_from_manifest(*args), [(manifest, data_file_filter) for _, manifest in unique_manifests] diff --git a/tests/table/test_inspect.py b/tests/table/test_inspect.py index 2404eadc74..46c098e647 100644 --- a/tests/table/test_inspect.py +++ b/tests/table/test_inspect.py @@ -29,7 +29,7 @@ from pyiceberg.table.snapshots import Snapshot from pyiceberg.transforms import IdentityTransform from pyiceberg.typedef import Record -from pyiceberg.types import NestedField, StringType +from pyiceberg.types import LongType, NestedField, StringType from tests.catalog.test_base import InMemoryCatalog @@ -76,6 +76,23 @@ def test_inspect_entries_and_files_render_null_bound(catalog: InMemoryCatalog) - assert files_metrics["upper_bound"] is None +@pytest.mark.parametrize("method", ["files", "data_files", "delete_files", "all_files", "all_data_files", "all_delete_files"]) +def test_inspect_files_of_snapshot_without_manifests(catalog: InMemoryCatalog, method: str) -> None: + tbl = catalog.create_table("default.no_manifests", Schema(NestedField(1, "x", LongType(), required=False))) + tbl.append(pa.table({"x": pa.array([], pa.int64())})) + + assert getattr(tbl.inspect, method)() == tbl.inspect._get_files_schema().empty_table() + + +def test_inspect_all_files_keeps_history_when_current_snapshot_has_no_manifests(catalog: InMemoryCatalog) -> None: + tbl = catalog.create_table("default.overwritten_with_empty", Schema(NestedField(1, "x", LongType(), required=False))) + tbl.append(pa.table({"x": pa.array([1], pa.int64())})) + tbl.overwrite(pa.table({"x": pa.array([], pa.int64())})) + + assert tbl.inspect.files().num_rows == 0 + assert tbl.inspect.all_files().num_rows == 1 + + @pytest.mark.parametrize("newest_first", [False, True]) def test_partitions_last_updated_uses_latest_snapshot_regardless_of_order(newest_first: bool) -> None: # Manifest entries are visited in manifest order, which is not chronological, so the