Skip to content

API: Fix Identity projection for mismatched transform types#16074

Closed
yadavay-amzn wants to merge 1 commit into
apache:mainfrom
yadavay-amzn:fix/15502-identity-projection
Closed

API: Fix Identity projection for mismatched transform types#16074
yadavay-amzn wants to merge 1 commit into
apache:mainfrom
yadavay-amzn:fix/15502-identity-projection

Conversation

@yadavay-amzn

Copy link
Copy Markdown
Contributor

Fixes #15502

Problem

When an Iceberg table has manifests written using a partition spec with identity-transformed timestamp field, queries that filter on that field using a temporal transform like hours() fail with:

ValidationException: Invalid value for conversion to type timestamptz: 490674 (java.lang.Integer)

Identity.projectStrict() creates an unbound predicate with the literal from the input predicate. When the predicate term is a transform (e.g. hours(ts) = 490674), the literal type (integer) does not match the partition field type (timestamptz), causing a ValidationException when the unbound predicate is later bound to the partition schema.

Fix

Return null from projectStrict() when the predicate term is not a BoundReference, indicating the identity transform cannot project transform-based predicates. This causes the projection to fall back to alwaysTrue (inclusive) or alwaysFalse (strict), which is the correct behavior.

The fix is a 4-line guard clause at the top of projectStrict(). Since project() delegates to projectStrict(), both paths are covered.

Testing

Added a regression test in TestProjection that reproduces the exact scenario from the issue: identity-partitioned timestamptz field filtered with hours() transform, verifying both inclusive and strict projections.

@github-actions github-actions Bot added the API label Apr 22, 2026
@Test
public void testIdentityProjectionWithTransformPredicate() {
// Regression test for https://github.com/apache/iceberg/issues/15502
// Identity-partitioned timestamptz field filtered with hours() should not throw

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This sentence looks incomplete. "... should not throw ValidationException"?

@yadavay-amzn yadavay-amzn Apr 22, 2026

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.

Fixed.


// The identity transform cannot project a hours-transform predicate, so it should
// be replaced with alwaysTrue (inclusive) rather than throwing ValidationException
assertThat(projected).isEqualTo(Expressions.alwaysTrue());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ValidationException isn't thrown in this test even if I revert a change of Identity.java‎.

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 exception is caught internally by the projection framework and converted to alwaysTrue/alwaysFalse. Without the fix, projectStrict() creates an UnboundPredicate with an integer literal for a timestamptz field, which fails during binding — the test assertion fails because the projection result is neither alwaysTrue nor alwaysFalse. Updated the comments to explain this more clearly.

@yadavay-amzn yadavay-amzn Apr 29, 2026

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.

@ebyhr I was able to confirm that the test fails without the fix and passes with it.

Without the fix, Projections.inclusive(spec).project(hourFilter) returns ref(name="ts") == 490674; an invalid predicate that binds an integer literal to a timestamptz field. The ValidationException from the original issue occurs downstream when this invalid predicate is evaluated against manifest data.

Our test catches the bug at the projection level (wrong predicate returned instead of alwaysTrue), which is the root cause.

@yadavay-amzn
yadavay-amzn force-pushed the fix/15502-identity-projection branch from 72461b2 to a078b0f Compare April 22, 2026 01:15
@yadavay-amzn

Copy link
Copy Markdown
Contributor Author

@ebyhr Replied to your comment about ValidationException not being thrown. Please let me know if that makes sense to you or if you'd like to test this differently? Thanks!

@yadavay-amzn yadavay-amzn changed the title API: Fix Identity projection for mismatched transform types (#15502) API: Fix Identity projection for mismatched transform types May 12, 2026
…5502)

Identity.project() and projectStrict() delegate to projectStrict() which
creates an unbound predicate with the literal from the input predicate.
When the predicate term is a transform (e.g. hours(ts) = 490674), the
literal type (integer) does not match the partition field type
(timestamptz), causing a ValidationException when the unbound predicate
is later bound to the partition schema.

Fix: Return null from projectStrict() when the predicate term is not a
BoundReference, indicating the identity transform cannot project
transform-based predicates. This causes the projection to fall back to
alwaysTrue (inclusive) or alwaysFalse (strict), which is correct.
@yadavay-amzn
yadavay-amzn force-pushed the fix/15502-identity-projection branch from a078b0f to 6958a88 Compare May 12, 2026 01:07
@yadavay-amzn

Copy link
Copy Markdown
Contributor Author

@ebyhr Updated the test comments to clarify the failure mode. Without the fix, the test fails because the projection returns ts == 490674 (an invalid predicate) instead of alwaysTrue/alwaysFalse. The ValidationException from the original issue occurs downstream when that invalid predicate is evaluated against manifest data. Also fixed the PR title format.

@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 30 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@iceberg.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label Jun 12, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been closed due to lack of activity. This is not a judgement on the merit of the PR in any way. It is just a way of keeping the PR queue manageable. If you think that is incorrect, or the pull request requires review, you can revive the PR at any time.

@github-actions github-actions Bot closed this Jun 19, 2026
@yadavay-amzn

yadavay-amzn commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Update: GitHub won't allow reopening this PR (the branch was force-pushed/recreated, so both the API and the Reopen button are disabled). I've opened a rebased continuation at #17261 and moved the review there.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ValidationException when filtering identity-partitioned timestamp field using other transforms

2 participants