Visitar URL original
Spec: Add stats-only fields to table metadata by rdblue · Pull Request #18405 · apache/iceberg · GitHub
Skip to content

Spec: Add stats-only fields to table metadata - #18405

Open
rdblue wants to merge 2 commits into
apache:mainfrom
rdblue:spec-add-stats-only-fields
Open

rdblue wants to merge 2 commits into
apache:mainfrom
rdblue:spec-add-stats-only-fields

Conversation

@rdblue

@rdblue rdblue commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

This adds "stats-only fields" to table metadata. A stats-only field is used to track stats for a non-materialized derived value. For example, one could be used to track stats for to_lower_case(name).

This introduces two cases: partition fields for the result of bucket partitioning, and value expressions for use cases like the one above.

This was discussed on the dev list in the thread, "[DISCUSS] Field ID tracking for non-materialized columns in v4".

@github-actions github-actions Bot added the Specification Issues that may introduce spec changes. label Oct 6, 2026
Comment thread format/spec.md Outdated
Comment thread format/spec.md
Comment thread format/spec.md
Comment thread format/spec.md Outdated

Readers must not fail when an unsupported stats-only field `type` is found; stats for unsupported types must be ignored.

Writers must preserve existing stats for stats-only fields listed in a table's `stats-only-fields`. Writers should produce stats when possible for stats-only fields. If an expression is not supported or produces a different output type when bound, a writer should produce no stats.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The only issue here is the type used to build the content stats schema.

Right now, data-type is not required for partition-value because the type may be promoted (int -> long) when the source column is widened and I didn't want to include metadata that could be stale.

However, by not having data-type for all cases, the requirement to preserve existing stats becomes harder because the output type for a new stats-only field type may not be known.

It might be a good idea to require data-type for all types except partition-value. But if we add a sort-value later we would want to do the same thing.

@stevenzwu stevenzwu Oct 7, 2026 •

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.

Could we require data-type unless the stats-only field type defines how to derive its current result

A stats-only field stores its current result type in `data-type` 
unless its `type` defines how to derive that result type. 
`data-type` must be a primitive or variant. 
`partition-value` derives its result type from the partition field's 
result type and must omit `data-type`. The result type 
may change only according to the type promotion rules above.

@pvary pvary Oct 7, 2026 •

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.

How should we handle schema evolution which drops source columns for the expression?

Maybe:
If an expression *cannot be bound* or produces a different output type when bound, a writer *must* not produce stats.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@stevenzwu, the problem with what you suggest is that it is only possible when the reader understands the stats-only field type. We can only assume that at format version boundaries where we specify what all readers must understand.

The only real solution that I can see is to say that readers and writers may omit stats-only fields with an unknown type unless data-type is present.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@pvary, I agree with updating the last sentence to cover that case.

Comment thread format/spec.md
Comment thread format/spec.md Outdated
Comment thread format/spec.md Outdated

Readers must not fail when an unsupported stats-only field `type` is found; stats for unsupported types must be ignored.

Writers must preserve existing stats for stats-only fields listed in a table's `stats-only-fields`. Writers should produce stats when possible for stats-only fields. If an expression is not supported or produces a different output type when bound, a writer should produce no stats.

@stevenzwu stevenzwu Oct 7, 2026 •

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.

Could we require data-type unless the stats-only field type defines how to derive its current result

A stats-only field stores its current result type in `data-type` 
unless its `type` defines how to derive that result type. 
`data-type` must be a primitive or variant. 
`partition-value` derives its result type from the partition field's 
result type and must omit `data-type`. The result type 
may change only according to the type promotion rules above.

Comment thread format/spec.md Outdated
Comment thread format/spec.md Outdated

The `partition-value` type stores stats for the output of a partition field, identified by a `partition-field-id` type-specific field. The lower and upper bound type is the partition field's result type.

The `expr-value` type stores stats for the result of a [value expression](https://iceberg.apache.org/expressions-spec#value-expressions), stored in the `expr` field. The output type of the value expression is stored in the `data-type` field and must be a primitive or variant.

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.

Do we want to codify the restrictions of the expr discussed on the dev thread, like deterministic functions, ID based fields.

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.

nit: link should be relative

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why relative? Then it doesn't work when viewing the spec in github.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated to specify ID references are required. The discussion about deterministic functions was that the functions should not have hidden inputs, which is not a concern here.

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.

Why relative? Then it doesn't work when viewing the spec in github.

If github link is a concern the we should fix this in expression-spec.md ([bucket-ref], [truncate-ref]), and in spec-md ([Puffin file format])

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.

Did some checking and this one works too in github, and on the site as well as MkDocs changes them:

[value expression](expressions-spec.md#value-expressions)

But cleaning up this could be another PR. Don't waste loops on it here. Opened #18441.

Comment thread format/spec.md Outdated
Comment thread format/spec.md Outdated
Comment thread format/spec.md
Stats-only fields are used to track stats for derived values that are not part of the table schema and are not materialized. A stats-only field consists of:

* A **`field-id`** assigned by incrementing the table's `last-field-id`
* A **`type`** that can be `partition-value` or `expr-value`

@danielcweeks danielcweeks Oct 7, 2026 •

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.

Is the primary function of partition-value to tie the partition-field-id to a "stats" field-id? Would this only be used for bucket/multi-arg transforms?

I assume we wouldn't track identity columns this way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's correct. This is to be able to store the output of a non-monotonic function. I would not expect anyone to use this for identity because the easier path is to store the identity value in the field's stats directly.

Comment thread format/spec.md Outdated
Comment thread format/spec.md Outdated
* A **`type`** that can be `partition-value` or `expr-value`
* Type-specific fields that defines how derived values are produced

The `partition-value` type stores stats for the output of a partition field, identified by a `partition-field-id` type-specific field. The lower and upper bound type is the partition field's result type.

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.

It would be good to clarify if we intend to track ALL partition fields this way or just ones that require a non-identity transform.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm updating this to add "This may be used in v4 to filter by bucket partition values".

I'm reluctant to over-specify what it can be used for because this should be mostly independent of the partition transforms that we add over time, and we don't typically put recommendations in the spec (like "don't use this for monotonic functions"). Hopefully this clarification / example works.

Comment thread format/spec.md
* Type-specific fields that define how derived values are produced

The `partition-value` type stores stats for the output of a partition field, identified by a `partition-field-id` type-specific field. The lower and upper bound type is the partition field's result type.
The `partition-value` type stores stats for the output of a partition field, identified by a `partition-field-id` type-specific field. The bound type is the partition field's result type and `data-type` is omitted. This may be used in v4 to filter by bucket partition values.

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 may be used in v4 to filter by bucket partition values.

do we need to mention "v4" here? the stats-only fields are allowed in v3. if v3 writer populates these partition stats fields, do we need to restrict v3 readers from using them (although they don't need to).

if we keep it, maybe v4 can be renamed to v4+ for future proof?

Comment thread format/spec.md
Readers must not fail when an unsupported stats-only field `type` is found; stats for unsupported types must be ignored.

Writers must preserve existing stats for stats-only fields listed in a table's `stats-only-fields`. Writers should produce stats when possible for stats-only fields. If an expression is not supported or produces a different output type when bound, a writer should produce no stats.
Writers must preserve existing stats for stats-only fields listed in a table's `stats-fields` if the `data-type` is known (either set or specified by a supported type). Writers should produce stats when possible for stats-only fields. A writer should produce no stats by setting the field stats struct to null when an expression is not supported, produces a different output type when bound, or binding fails.

@stevenzwu stevenzwu Oct 7, 2026 •

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.

produces a different output type when bound

nit: this reads a bit awkward to me. it is one of the 3 conditions for when. maybe sth like

binding produces a different output type

This branch has not been deployed

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

Labels

Specification Issues that may introduce spec changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants