This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-4314-2549061f904af87f85e1201f1bd6dc3ab2f1ba2c in repository https://gitbox.apache.org/repos/asf/iceberg-python.git
commit 2ed79b696120467c4733c52a55c8302a9a2052d9 Author: David <[email protected]> AuthorDate: Mon Nov 9 19:48:37 2026 +0000 Add inspect regression test for bounds after type promotion (#4314) Bounds written before an int->long or float->double promotion keep their 4-byte encoding. #3293 fixed decoding them, but only covered the metrics evaluators. Cover the inspect metadata tables too, so the crash reported in #3744 can't regress. Closes #3744 --- tests/table/test_inspect.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/table/test_inspect.py b/tests/table/test_inspect.py index 2404eadc7..306579754 100644 --- a/tests/table/test_inspect.py +++ b/tests/table/test_inspect.py @@ -29,7 +29,7 @@ from pyiceberg.table.inspect import InspectTable, _readable_bound 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 DoubleType, FloatType, IntegerType, LongType, NestedField, PrimitiveType, StringType from tests.catalog.test_base import InMemoryCatalog @@ -106,3 +106,35 @@ def test_inspect_manifests_preserves_empty_string_bounds(catalog: InMemoryCatalo partition_summary = tbl.inspect.manifests().to_pydict()["partition_summaries"][0][0] assert partition_summary["lower_bound"] == "" assert partition_summary["upper_bound"] == "" + + +def test_inspect_bounds_after_type_promotion(catalog: InMemoryCatalog) -> None: + # Promotion does not rewrite existing files, so their bounds keep the narrower 4-byte encoding + # while the current schema type is the 8-byte one. + cases: list[tuple[str, PrimitiveType, PrimitiveType, pa.DataType, list[float]]] = [ + ("int_to_long", IntegerType(), LongType(), pa.int32(), [1, 2]), + ("float_to_double", FloatType(), DoubleType(), pa.float32(), [1.5, 2.5]), + ] + for name, file_type, promoted_type, arrow_type, values in cases: + schema = Schema(NestedField(1, "v", file_type, required=False)) + spec = PartitionSpec(PartitionField(1, 1000, IdentityTransform(), "v")) + tbl = catalog.create_table(f"default.{name}", schema, partition_spec=spec) + tbl.append(pa.table({"v": values}, schema=pa.schema([pa.field("v", arrow_type, nullable=True)]))) + + with tbl.update_schema() as update: + update.update_column("v", field_type=promoted_type) + + for files_table in [ + tbl.inspect.entries(), + tbl.inspect.files(), + tbl.inspect.data_files(), + tbl.inspect.all_files(), + tbl.inspect.all_data_files(), + ]: + metrics = [m["v"] for m in files_table.to_pydict()["readable_metrics"]] + assert sorted(m["lower_bound"] for m in metrics) == values, name + assert sorted(m["upper_bound"] for m in metrics) == values, name + + partition_summaries = tbl.inspect.manifests().to_pydict()["partition_summaries"][0] + assert partition_summaries[0]["lower_bound"] == str(values[0]), name + assert partition_summaries[0]["upper_bound"] == str(values[-1]), name
