Repository navigation
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
feat(transaction): support transform-based sort orders #2765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
feat(transaction): support transform-based sort orders #2765
Changes from all commits
0e0082ceee40b8afe2c270fba334203f58aa962dea9082bf7File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
There are no files selected for viewing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exactly the change I asked for last round — the transform branch reports
DataInvalidnow and the test pins it.The one thing I'd tidy while we're in here: the two branches just above (the "Cannot find source column" and "Cannot sort by non-primitive source field" arms around 183/192) still return
Unexpectedfor what's really the same class of user mistake, so the function now hands back two different kinds depending on which check trips. They're pre-existing and untouched by this PR, so I won't hold on it — but flipping both toDataInvalidhere would make the whole function consistent for callers matching on.kind().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 9082bf7. Both the missing-column and non-primitive-source branches now return
DataInvalid. Updated the existing tests for missing columns, list fields, and variant fields to assert the error kind as well as the message.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The guard does exactly what I wanted on the action path.
One gap: it lives in
to_sort_field, so a caller building aSortOrderdirectly viaSortOrder::builder().build()still getsVoid/Unknownthrough unchecked. As the write path grows that second entry point starts to matter. Not blocking here, but if we ever want the invariant to be uniform,check_compatibilityis the spot both paths funnel through. wdyt?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that
check_compatibilityis the shared validation point for both paths. For this PR, I'd keep the guard scoped to the replace-sort-order action as an explicit policy for what this API authors.Given your note about Java accepting
Void, moving the guard into the shared builder would broaden that restriction to existing callers. I'd leave that as a follow-up where we can decide the builder's behavior forUnknownandVoidseparately.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One wrinkle on the
Voidhalf of what I asked for, and this is new to me: Java'sVoidTransform.canTransformreturnstruefor every type, soSortOrder.Builder.build()there happily accepts a void sort field — a Java-authored table can legitimately carry one (e.g. after a partition field was voided). RejectingVoidoutright means we can't round-trip that.I still think rejecting it is defensible as a deliberate "we won't author these" policy, and
Unknownclearly should go regardless. But if we keep theVoidrejection I'd drop a one-line comment here noting it's intentionally stricter than the spec. wdyt — keep it strict, or match Java and letVoidthrough?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd keep the action strict. Added a comment and API documentation in 9082bf7 explaining that rejecting
Voidis intentional: an all-null sort key adds no ordering information.To clarify the scope, the guard only applies when declaring a replacement sort order through this action. It does not affect reading or serializing existing metadata containing
Void, though recreating that order through this action would be rejected.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One footgun the new public surface opens up:
asc_with_transform(name, Transform::Unknown, ...)andTransform::Voidboth sail through commit-time validation.Unknown.result_type()returnsOk(String)andVoid.result_type()returnsOk(input_type), socheck_compatibilityaccepts them and we happily write"transform": "unknown"/"transform": "void"to metadata.Unknownis our read-side forward-compat sentinel — there's no spec string for it, and no other client can execute it (Java'sUnknownTransform.apply()throws, PyIceberg can't apply it at all). Java's builder never lets you construct one; you only reachUnknownTransformby parsing existing metadata, so this is a path Java deliberately doesn't expose.Voidloads everywhere but produces an all-null sort key — a silent no-op.I'd guard both with a
DataInvalidinadd_sort_field(orto_sort_field), something likematches!(transform, Transform::Unknown | Transform::Void). Cheap, and it keeps us from minting sort orders no client can actually order by. wdyt?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0fba334. Added the guard in
PendingSortField::to_sort_field, returningDataInvalidat commit time while preserving the builder API. Added tests covering both transforms through both ascending and descending methods.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also add an integration test that would write a metadata.json with updated sort order, and then another operation would read this json and correctly parse the transform?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea — added in eee40b8:
test_sort_order_transform_survives_metadata_json_round_trip.It commits a transform-based sort order (
bucket[16]asc +truncate[4]desc) through the in-crate memory catalog, which serializes the updated table metadata to an actual metadata.json file (TableMetadata::write_toonFileIO), then reloads the table viacatalog.load_table— which reads and parses that file back (TableMetadata::read_from) — and asserts both transforms and directions survive. So the transform string ("bucket[16]"etc.) is exercised through the real serialize-file-parse path, not just the in-memoryTableUpdate.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this one's billed as the full round-trip, I'd assert
null_ordersurvives too — right now we check transform and direction but not thatFirst/Lastcame back. One moreassert_eq!per field closes the gap.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 0fba334. The round-trip test now also asserts
NullOrder::Firstfor the first field andNullOrder::Lastfor the second field after reloading the metadata.Uh oh!
There was an error while loading. Please reload this page.