Repository navigation
Conversation
|
The integration failure was caused by this PR: float defaults are now stored as float32 values, so the float case in The |
39a7ff3 to
09a4e86
Compare
FloatLiteral kept the double value of the literal, so the evaluators compared it with float32 bounds and partition values. Scans skipped files with matching rows and delete() removed or kept the wrong rows. Narrow the literal to float32 like Java, and do the same for a float initial-default read from metadata. Generated-by: Claude Code (Claude Opus 5.5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Float defaults are now stored as float32 values, like Java, so the REST round-trip test's float default of 22.27 read back as 22.270000457763672. Use 22.25, which is exact in float32, and add a unit test for reading a float default from JSON. Generated-by: Claude Code (Claude Opus 5.5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
09a4e86 to
810e974
Compare
|
Hi @Fokko, a gentle ping on this one. Filtering a |
Rationale for this change
Filters on
floatcolumns can miss rows, anddelete()can delete the wrong rows:Why. A
floatcolumn stores 32-bit values, and0.1as a 32-bit float is0.10000000149011612. PyIceberg keeps the filter value as the 64-bit0.1. To decide which files to read, it compares the filter value with each file's min and max (or with the partition value). These two numbers are not equal, so the file with0.1is skipped. Reading rows inside a file is fine, because the row filter turns the value into a 32-bit Arrow scalar (_convert_scalar). So the bug shows up when the value is a file's min or max, or a partition value.Deletes use the same min/max check. For
delete("value != 0.1"), PyIceberg decides that no row in the file is0.1and drops the whole file.Data
[0.1, 0.4, 0.7]in one file:value = 0.1[][1]value <= 0.1[][1]value >= 0.7[][3]delete("value != 0.1"), then scan[][1]delete("value = 0.1"), then scan[1, 2, 3][2, 3]Fix. Convert the value to 32 bits when the float literal is created, in
FloatLiteral.__init__. The spec definesfloatas a 32-bit IEEE 754 value (spec.md#L281). Java does the same: binding a double literal to a float column createsnew FloatLiteral(value().floatValue())(Literals.java#L380). This also removes_value32, which is now the same as.value.A float
initial-defaultread from table metadata is converted too, like Java (SingleValueParser.java#L79). Otherwise a filter on a column that is missing from older files would stop matching its default.Reproduced on 0.12.0 and
main.Are these changes tested?
Yes.
tests/catalog/test_catalog_behaviors.py: new scan and delete tests on a float column, with and without an identity partition, for each catalog fixture. All 18 fail onmainand pass with this change.tests/expressions/test_literals.py::test_double_to_float_within_boundexpected the old 64-bit value. It now expects the 32-bit value of 34.56 (34.560001373291016).tests/expressions/test_visitors.py: new test for a missing float column withinitial_default=0.1. It fails if only the filter value is converted.tests/test_conversions.py::test_json_float_is_read_as_float32: a float default read from JSON (22.27) now comes back as its float32 value, while a double stays22.27. It fails onmain.pyiceberg-core), after rebasing on currentmain(4ba3ba4): 4351 passed, 4331 onmain. The difference is the 20 tests above that fail onmain. 12 other failures are the same on both, from optional packages I don't have (Kerberos, Azure). My setup has no Cython Avro decoder, sotests/avrowas not collected, and I ignored the pure-Python fallback warning on both sides. I couldn't run the Docker integration tests locally, so I rely on CI for them.tests/integration/test_rest_schema.py::test_initial_default_all_columns: the float case set a default of22.27, which is not exact in float32 and now reads back as22.270000457763672(CI caught this). It now uses22.25, which is exact. The double case still uses22.27.prek run --fileson the changed files passes.Are there any user-facing changes?
Yes. Filters and deletes on
floatcolumns now return and remove the right rows.doublecolumns are not affected.Small visible changes:
FloatLiteral(0.1).value(and itsrepr) is now0.10000000149011612, and float default values are rounded to float32 too, like Java: a default of0.1set withadd_columnorset_default_value, passed toNestedField, or read from table metadata becomes0.10000000149011612. Onmain,DoubleLiteral(0.1) == FloatLiteral(0.1)wasTrueand the other way round wasFalse; now both areFalse, like Java.Written with AI assistance (Claude Code, Claude Opus 5.5). The Java references were read from
apache/icebergmain, and the tests above were run on this branch.