diff --git a/pyiceberg/table/__init__.py b/pyiceberg/table/__init__.py index 63b87d290e..50596ff25f 100644 --- a/pyiceberg/table/__init__.py +++ b/pyiceberg/table/__init__.py @@ -2471,8 +2471,9 @@ def plan_files(self) -> Iterable[FileScanTask]: options=self.options, ).plan_files( manifests=manifests, - manifest_entry_filter=lambda manifest_entry: manifest_entry.snapshot_id in append_snapshot_ids - and manifest_entry.status == ManifestEntryStatus.ADDED, + manifest_entry_filter=lambda manifest_entry: ( + manifest_entry.snapshot_id in append_snapshot_ids and manifest_entry.status == ManifestEntryStatus.ADDED + ), ) def to_arrow(self) -> pa.Table: diff --git a/pyiceberg/types.py b/pyiceberg/types.py index 3c98215366..2449421841 100644 --- a/pyiceberg/types.py +++ b/pyiceberg/types.py @@ -324,6 +324,8 @@ class DecimalType(PrimitiveType): root: tuple[int, int] def __init__(self, precision: int, scale: int) -> None: + if not (1 <= precision <= 38): + raise ValidationError(f"Precision must be between 1 and 38 (inclusive), got: {precision}") super().__init__(root=(precision, scale)) @model_serializer diff --git a/tests/test_types.py b/tests/test_types.py index 5e95687ba2..76353f4a19 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -521,6 +521,31 @@ def test_deserialization_decimal_failure() -> None: assert "Could not parse decimal(abc, def) into a DecimalType" in str(exc_info.value) +def test_decimal_precision_above_38_raises() -> None: + """Precision > 38 must be rejected per the Iceberg spec (https://iceberg.apache.org/spec/#primitive-types).""" + with pytest.raises(ValidationError, match="Precision must be between 1 and 38"): + DecimalType(39, 0) + + +def test_decimal_precision_zero_raises() -> None: + """Precision 0 is not valid; minimum precision is 1.""" + with pytest.raises(ValidationError, match="Precision must be between 1 and 38"): + DecimalType(0, 0) + + +def test_decimal_precision_boundary_38_accepted() -> None: + """Precision == 38 is the maximum allowed and must be accepted.""" + d = DecimalType(38, 0) + assert d.precision == 38 + assert d.scale == 0 + + +def test_decimal_deserialization_precision_above_38_raises() -> None: + """Deserialization of decimal(39, 0) must also raise a ValidationError.""" + with pytest.raises(Exception, match="Precision must be between 1 and 38"): + DecimalType.model_validate_json('"decimal(39, 0)"') + + def test_str_decimal() -> None: assert str(DecimalType(19, 25)) == "decimal(19, 25)"