Skip to content

Navigation Menu

Sign in
Sign up

fix: render float and double Iceberg partition values like iceberg-java - #5840

Open
andygrove wants to merge 2 commits into
apache:main from
andygrove:fix/iceberg-float-partition-path
Open

fix: render float and double Iceberg partition values like iceberg-java #5840
andygrove wants to merge 2 commits into
apache:main from
andygrove:fix/iceberg-float-partition-path

Conversation

@andygrove

@andygrove andygrove commented Sep 10, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #5836.

Rationale for this change

iceberg-java renders a float or double partition value with Float.toString / Double.toString: a fractional digit is always present, and the value switches to scientific notation outside [1e-3, 1e7). Comet's partition-path renderer overrode the arms where iceberg-rust disagrees with iceberg-java but deliberately left float and double delegating, on the grounds that the divergence was cosmetic.

It is not. Rust's Display never uses an exponent, so Double.MAX_VALUE renders as 309 digits and Double.MIN_VALUE as 324. A directory name that long exceeds the 255-byte limit on a single path component, and the write fails:

CometNativeException: Unexpected => Failed to finish parquet writer.,
source: ... path: .../d=1797693134862315700000000...0000/00000-00007-....parquet
=> invalid filename, source: File name too long (os error 36)

That is TestSparkDataFile.testValueConversionWithEmptyStats and .testValueConversionPartitionedTable failing on all four Iceberg versions in the #5677 run. Below the length limit the directory name still diverges from iceberg-java's, so a table written through both writers has two spellings of the same partition.

Comet already implements Java's rules: cast(float as string) needs exactly the same rendering, and the cast_float_to_string! macro has spelled it since it was written.

What changes are included in this PR?

  • native/spark-expr/src/conversion_funcs/numeric.rs: extract the formatting out of the cast_float_to_string! macro into write_java_float_string, generic over a JavaFloatString trait implemented for f32 and f64, and export both from the crate. The macro becomes spark_cast_float_to_utf8, a generic function over the arrow float types. The cast still formats straight into the string builder with no per-row allocation; the coefficient inspection the scientific-notation branch needs now uses a stack buffer rather than a reused String, so the shared function does not have to take scratch space as a parameter.
  • native/core/src/execution/operators/iceberg_partition_path.rs: render float and double partition values through that function instead of delegating to iceberg-rust, and update the module documentation, which previously recorded the divergence as accepted.

Two behaviours are unchanged and worth naming: this is the pre-JDK-19 Double.toString, which is not shortest-round-trip for every value, and only the smallest subnormal is corrected for. Iceberg deprecated float and double partitioning in 1.3, so this matters for tables that already carry such a field.

How are these changes tested?

  • New Rust unit tests in iceberg_partition_path.rs pin the rendering of both widths against Double.toString / Float.toString output taken from a JDK: whole values, the boundaries of the plain-notation window, both extremes, NaN and the infinities.
  • A new case in CometIcebergWriteActionSuite writes a table partitioned by a FLOAT and a DOUBLE through the native writer and through the JVM writer and asserts the two produce the same partition directories, then pins the spelling of each. Without the fix it reproduces Native Iceberg write renders float/double partition values differently from iceberg-java, and fails with "File name too long" for large values #5836 exactly, failing with File name too long.
  • CometIcebergWriteActionSuite (63 tests), CometNativeCastSuite (185 tests), the expressions/cast SQL file tests (21 tests) and the datafusion-comet-spark-expr unit tests (726 tests) all pass, covering the refactored cast path.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BtAqq4YJsk8uk42c7vHk8B

iceberg-java renders a float or double partition value with
`Float.toString` / `Double.toString`, which keeps a fractional digit on a
whole value and switches to scientific notation outside [1e-3, 1e7).
Comet's partition-path renderer delegated both types to iceberg-rust,
whose `Display` does neither, so `Double.MAX_VALUE` became a 309-digit
directory name: past the 255-byte limit on a single path component, which
failed the write with `File name too long`.
Comet already spells Java's rules in the `cast(float as string)` path, so
extract that formatting from `cast_float_to_string!` into
`write_java_float_string` and call it from both places. The macro becomes
a generic function over the arrow float types, and the cast keeps
formatting straight into the string builder: the coefficient inspection
the scientific branch needs now uses a stack buffer instead of a reused
`String`.
Closes apache#5836
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BtAqq4YJsk8uk42c7vHk8B 
@github-actions github-actions Bot added bug Something isn't working area:expressions Expression evaluation area:Iceberg labels Sep 10, 2026
Build `JavaFloatString` on `num::Float`, the bound this crate already uses
to abstract f32/f64 next door in `cast_string_to_float_impl`, so the trait
keeps only what `num` does not supply: the plain-notation window and the
smallest subnormal Java does not render shortest. That drops the second
macro and six forwarding methods.
Also read the scientific-notation scratch once rather than twice, restore
the source citation the format rules lost when they moved out of the
macro, note why `identity` is the only transform that reaches the new
float and double arms, and assert the partition directories as a set so
an unexpected fifth one fails the test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BtAqq4YJsk8uk42c7vHk8B 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

area:expressions Expression evaluation area:Iceberg bug Something isn't working

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Native Iceberg write renders float/double partition values differently from iceberg-java, and fails with "File name too long" for large values

1 participant

AltStyle によって変換されたページ (->オリジナル) /