Skip to content

[python] Render min/max_partition_stats in the $manifests system table#8842

Open
wombatu-kun wants to merge 2 commits into
apache:masterfrom
wombatu-kun:python/manifests-partition-stats
Open

[python] Render min/max_partition_stats in the $manifests system table#8842
wombatu-kun wants to merge 2 commits into
apache:masterfrom
wombatu-kun:python/manifests-partition-stats

Conversation

@wombatu-kun

@wombatu-kun wombatu-kun commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Purpose

This closes a TODO in pypaimon/table/system/manifests_table.py:

# TODO: render min/max_partition_stats by casting partition
# rows to their string form. pypaimon
# has SimpleStats but no shared partition-row-to-string
# helper yet; emit NULL to preserve the column shape.
min_partition_stats.append(None)
max_partition_stats.append(None)

So $manifests returns NULL for min_partition_stats and max_partition_stats in pypaimon while Java renders them, and inspecting which manifests cover which partitions gives nothing on the Python side. The data is already parsed: ManifestListManager builds SimpleStats from the manifest list, only the rendering was missing.

This adds cast_row_to_string, the helper the TODO asks for: a port of RowToStringCastRule and the per type *ToStringCastRule rules of paimon-common, with fields joined by ", " inside braces, the literal null for a null field, and {} for an unpartitioned table. The types that need more than str() are boolean (lower case), binary (decoded as UTF-8), date, time, timestamp, float and double. Timestamps follow DateTimeUtils.formatTimestamp, which separates date and time with a space and strips trailing zeros of the fraction down to the declared precision, so datetime.isoformat() can not be used. Float and double follow Java's Float.toString / Double.toString: the shortest decimal that round trips (through float32 for float, so a float32 0.1 widened to a Python float stays 0.1 rather than 0.10000000149011612), laid out in Java's notation with an upper case E, no + sign and no zero-padded exponent, so 1e7 renders as 1.0E7 and 1e-5 as 1.0E-5 rather than Python's 1e+07 / 1e-05.

One point for a maintainer opinion, deliberately not changed here: pypaimon renders the partition column of $files and $buckets as pt=v/pt2=v2, while Java renders those two with the brace form and keeps pt=v only for $partitions. Aligning them would change values these tables already return, so it is out of scope for this PR.

Tests

New pypaimon/tests/row_to_string_test.py covers the rendering per type, including a null field, an empty row, the timestamp fraction at precision 0/3/6, and the float/double forms checked against Java's Float/Double.toString (shortest float32 form, scientific notation like 1.0E7 / 1.0E-5, signed zero and the non-finite values NaN / Infinity).

pypaimon/tests/system/manifests_table_test.py replaces the assertions that pinned the NULL placeholder: an unpartitioned table now yields {}, and a table partitioned by INT and STRING yields {1, 2024-01-01} and {2, 2024-01-02}.

Cross checked against Java on a single warehouse: a table written by pypaimon and partitioned by INT, STRING, DATE, BOOLEAN and DECIMAL renders identically on both sides.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JingsongLi

Copy link
Copy Markdown
Contributor
  • Scientific notation does not match Java’s Float/Double.toString.
  • Local regression: Java returns 1.0E7 / 1.0E-5, while Python currently outputs 1e+07 / 1e-05.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wombatu-kun

Copy link
Copy Markdown
Contributor Author

Done 9df763e - FLOAT/DOUBLE now render via Java's Float/Double.toString format (uppercase E, no + sign, no zero-padded exponent; e.g. 1.0E7 / 1.0E-5).

@JingsongLi

Copy link
Copy Markdown
Contributor

The new floating-point format implementation does not fully match Java 8’s Float/Double.toString. When comparing the bit patterns of 300,000 float values and 300,000 double values, there are also significant differences in ordinary finite values; for example, Java outputs 2.68873286E11, while Python outputs 2.6887329E11. Furthermore, a valid Float.MAX_VALUE triggers an OverflowError.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants