Skip to content

[python] Fix $files reporting empty min/max value stats - #10111

Merged
JingsongLi merged 2 commits into
apache:masterfrom
jackylee-ch:python-files-table-value-stats
Sep 24, 2026
Merged

JingsongLi merged 2 commits into
apache:masterfrom
jackylee-ch:python-files-table-value-stats

Conversation

@jackylee-ch

@jackylee-ch jackylee-ch commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

The $files system table rendered min_value_stats and max_value_stats as {} for every file: on the read path the stats rows are BinaryRow, which has no values attribute.

Stats are now read via get_field and evolved to the current schema by field id with SimpleStatsEvolutions, as the Java FilesTable does, so a dropped or re-added column no longer inherits another column's stats. A file written with no value stats lists each column as null, matching Java. Decode errors propagate instead of rendering {}.

Tests

files_table_test: real min/max with metadata.stats-mode=full; a dropped-and-re-added column shows null on the old file (test_value_stats_follow_field_ids_after_drop_and_re_add); a decode error is not swallowed.

Written with Claude Code; verification is mine.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a real end-to-end problem: persisted manifest stats are BinaryRow, and the current $files rendering drops their min/max values. I ran the full files_table_test.py suite (5 passed) and reproduced the new output with a real local table.

[P1] Do not label historical file stats with the current table's column names. The new _row_values(...) path exposes a pre-existing _stats_columns fallback that uses self.base_table.field_names whenever value_stats_cols is absent. But ManifestFileManager decodes the BinaryRow with meta.schema_id's historical fields. In a real metadata.stats-mode=full table I wrote id=1, v='old', dropped v, then added a new v INT. The old file now appears in $files as min_value_stats={"id":1,"v":"old"} and the same max stats: the string value of the deleted column is attributed to a different integer column. The prior behavior displayed {}, so this PR introduces misleading stats. Please align the names and identity with the file's historical stats fields, and cover drop/re-add (or otherwise omit stats for a column whose identity no longer matches). The existing test only covers a stable schema.

This change has user-facing value; I am requesting changes for the schema-evolution correctness issue before production use.

return values
try:
return [row.get_field(i) for i in range(len(row))]
except Exception:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This broad catch turns every stats decoding failure into a valid-looking  {}  result. For example, if  BinaryRow.get_field()  raises because the manifest bytes are malformed, a type cannot be decoded, or the resolved fields do not match the stored row,  $files  silently reports empty statistics - the same success-shaped symptom this PR is fixing. Java’s  FilesTable  does not suppress these decoding failures.

Please let the actionable exception propagate, or catch only a specifically expected compatibility condition and surface it with the affected file/schema context. A focused test can use a row whose  get_field()  raises and assert that  _row_values  does not return [] .

The $files system table rendered min_value_stats and max_value_stats as {}
for every file, even when the manifest carries per-column value stats.

_render_stats_map read the row values via getattr(row, "values", []), but on
the read path the min/max rows are BinaryRow (built in manifest_file_manager),
which exposes get_field/__len__ and has no values attribute -- so getattr
yields [] and the map renders empty. Only GenericRow, the write-path type, has
values, which is why null_value_counts and min_key/max_key render correctly.

Add a _row_values helper that falls back to get_field(i) when values is
absent, and use it for the two value-stats maps. min_key/max_key are
GenericRow, so _render_key is left unchanged. A malformed or schema-evolved
stats row degrades to {} rather than aborting the whole $files listing.
Render min/max/null-count stats through SimpleStatsEvolutions, matching the Java FilesTable, so a dropped or re-added column no longer inherits stats from a column with the same name. Stats decode errors now propagate instead of being rendered as {}.
@jackylee-ch
jackylee-ch force-pushed the python-files-table-value-stats branch from 06fa3df to 1cd4c82 Compare September 24, 2026 07:10
@jackylee-ch

Copy link
Copy Markdown
Contributor Author

Fixed in the latest push. Value stats now evolve to the current schema by field id via SimpleStatsEvolutions, as the Java FilesTable does, so a dropped/re-added column no longer inherits another column's stats — in your repro the old file shows {"id":1,"v":null} (null count 1), and a file written after the re-add shows v:20; covered by test_value_stats_follow_field_ids_after_drop_and_re_add.

The broad except is gone: _row_values now lets a decode error propagate, with a test asserting it.

One visible change: a file written with no value stats (Python's default metadata.stats-mode=none) now lists each column as null rather than {}, which matches Java. Rebased on master.

@JingsongLi JingsongLi left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@JingsongLi
JingsongLi merged commit 48ad4e2 into apache:master Sep 24, 2026
12 of 14 checks passed
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.

3 participants