Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

to_bytes silently rescales a Decimal with a negative scale

Đang mở Phù hợp với người mới
#3,996 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
78/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
databases

Hướng nghiên cứu

Bắt đầu tại entry point pyiceberg.conversions.to_bytes và decimal_to_unscaled, sau đó chạy reproduction Decimal được cung cấp cho DecimalType(10, 2). Được xem là hoàn tất khi các scale có dấu không khớp bị từ chối thay vì được rescale, trong khi các giá trị đã khớp với scale của type vẫn round-trip chính xác; kiểm tra tác động lên pyiceberg/manifest.py và pyiceberg/io/pyarrow.py.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

to_bytes for DecimalType takes the absolute value of the exponent before comparing it
against the type's scale:

_, digits, exponent = value.as_tuple()
exponent = abs(int(exponent))
if exponent != primitive_type.scale:
    raise ValueError(...)

A Decimal carries the negated scale as its exponent, so a value with a negative scale
passes this check as if it had the matching positive one. decimal_to_unscaled then uses
only the digits, and the exponent is dropped:

sign, digits, _ = value.as_tuple()
return int(Decimal((sign, digits, 0)).to_integral_value())

The value is written four orders of magnitude off, with no error.

Reproduction

from decimal import Decimal
from pyiceberg.conversions import to_bytes, from_bytes
from pyiceberg.types import DecimalType

t = DecimalType(10, 2)
print(from_bytes(t, to_bytes(t, Decimal("1E+2"))))   # 0.01, expected 100.00
print(from_bytes(t, to_bytes(t, Decimal("5E+1"))))   # 0.50 for decimal(10, 1) -> 0.5, expected 50.0

This is not an exotic input. Decimal.normalize() produces exactly this form:

Decimal("100").normalize()   # Decimal('1E+2')

so a value that has been normalized, or that comes out of arithmetic that trims trailing
zeros, hits it.

Impact

to_bytes writes the lower_bounds and upper_bounds of a data file
(pyiceberg/manifest.py, _write_data_file_statistics), and the same conversion is used
in pyiceberg/io/pyarrow.py. A bound written as 0.01 instead of 100.00 makes scan
planning prune files that do hold matching rows, so a query silently returns fewer rows
than it should.

Values with a positive scale are unaffected: Decimal("100.00") round-trips correctly.

Suggested fix

Compare the signed scale, -exponent, against primitive_type.scale, so a mismatching
value is rejected instead of being silently rescaled. Rescaling the value to the type's
scale would be the other option, but that widens the contract of a function that today
requires an exact match.

Happy to open a PR.

Ngôn ngữ chính
Python
Star
1.1k
Fork
589
Merge trung bình
1 ngày 20 giờ
Pull request đã merge (30 ngày)
68

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của apache/iceberg-python

Tất cả issue của apache/iceberg-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.