Repository navigation
[FLINK-40936][table] Reject VARIANT elements and keys in functions and operators that compare them - #29416
Merged
raminqaf merged 1 commit intoOct 26, 2026
Conversation
1 task done
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
from
October 7, 2026 15:33
28b0b86 to
c6eaae4
Compare
davidradl
reviewed
Oct 7, 2026
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
4 times, most recently
from
October 8, 2026 15:04
784bb5a to
ba1fc84
Compare
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
2 times, most recently
from
October 23, 2026 14:30
7b3c4f1 to
6ab4620
Compare
VasShabu
suggested changes
Oct 23, 2026
VasShabu
left a comment
Contributor
There was a problem hiding this comment.
Hi Ramin
First review so please let me know if my logic is flawed
Noticed that your comment key type of a MAP are the first child however if this changes in the future so that "key type of a MAP are the second child" some builtin functions may not give the intended output like Map_Contains_Key.
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
2 times, most recently
from
October 25, 2026 19:45
7fd3775 to
2491ce9
Compare
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
from
October 26, 2026 07:05
2491ce9 to
1c96af0
Compare
twalthr
approved these changes
Oct 26, 2026
twalthr
left a comment
Contributor
There was a problem hiding this comment.
LGTM, I just had one last doc simplification comment.
…d operators that compare them Flink compares VARIANT values by their binary encoding, but one value has many encodings. So functions and operators that compare array elements, map keys or collected values treated equal VARIANT values as different and silently returned wrong results. ComparableTypeValidator now checks the array elements and map keys of ARRAY_CONTAINS, ARRAY_DISTINCT, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_UNION, ARRAY_EXCEPT, ARRAY_INTERSECT, MAP_CONTAINS_KEY and MAP_UNION, the map key of an item access m[k], and the argument of COLLECT, also in an OVER window. An index into an array and a field access on a VARIANT compare nothing and stay allowed. The validator names what it checks in sets: the functions, the operators that compare elements, and the comparison operators that need equality or order. The comparison operator sets hold the same kinds that the validator matched before. The check lives in the validator and not in the input type strategy of each function. That would need a new argument strategy and a flag in three collection strategies, for a check that a future VARIANT equality may remove. LogicalTypeChecks.areComparable rejects every type that is not comparable, see LogicalTypeChecks.isComparableType. This also covers a structured type with a VARIANT attribute. GREATEST, LEAST, MAP_FROM_ENTRIES and Table API comparisons reject VARIANT through it during type inference. Ten functions build their equality check again during code generation: ARRAY_CONTAINS, ARRAY_DISTINCT, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_UNION, ARRAY_EXCEPT, ARRAY_INTERSECT, MAP_CONTAINS_KEY, MAP_UNION and MAP_FROM_ENTRIES. That check goes through LogicalTypeChecks.areComparable, not through the validator, which only runs when a query is planned. So a job restored from a compiled plan that calls one of them on VARIANT elements or keys now fails when it is executed, before the job is submitted. Such a plan returned wrong results before. A user SpecializedFunction that builds an = evaluator on VARIANT values now fails too, in new and in restored plans. The VARIANT section of the data types page gives a few examples of where a query compares values, instead of a full list that would soon be out of date. KeyTypeValidator is now ComparableTypeValidator. The planner also uses key for upsert, unique and primary keys, and the validator rejects every comparison of a type that is not comparable, for equality or for order. LogicalTypeChecks.isComparableKeyType is now isComparableType, and the Javadocs say which comparisons need equality and which need order.
raminqaf
force-pushed
the
FLINK-40936-variant-function-equality
branch
from
October 26, 2026 09:15
1c96af0 to
25e6104
Compare
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What is the purpose of the change
Flink compares VARIANT values by their binary encoding, but one value has many encodings. So functions and operators that compare array elements, map keys or collected values treat equal VARIANT values as different and return wrong results, without an error. #29411 rejects VARIANT in GROUP BY, joins and comparisons. This PR does the same for these functions and operators.
ARRAY_CONTAINSabovefalseAn array element of type VARIANT cannot be used in ARRAY_CONTAINS, because the type has no equality. Cast the value to a comparable type first.The same validator rejects VARIANT in GROUP BY and joins, so the message has the same form.
Brief change log
ComparableTypeValidatorchecks the array elements and map keys of ARRAY_CONTAINS, ARRAY_DISTINCT, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_UNION, ARRAY_EXCEPT, ARRAY_INTERSECT, MAP_CONTAINS_KEY and MAP_UNION. It also checks the map key of an item accessm[k]and the argument ofCOLLECT. An index into an array and a field access on a VARIANT compare nothing and stay allowed. The query fails during planning.LogicalTypeChecks#areComparablerejects every type that is not comparable, seeLogicalTypeChecks#isComparableType. This also covers a structured type with a VARIANT attribute. GREATEST, LEAST, MAP_FROM_ENTRIES and Table API comparisons reject VARIANT through it.KeyTypeValidatorfrom [FLINK-40910][table] Reject VARIANT where a query needs equality or ordering #29411 is nowComparableTypeValidator, andisComparableKeyTypeis nowisComparableType. The planner also uses key for upsert, unique and primary keys, and the validator checks every comparison, not only keys.An earlier version declared the check in the input type strategy of each function. That needed a new argument strategy and a flag in three collection strategies, about 160 lines of main code, for a check that a future VARIANT equality may remove. The validator does it in a few lines. The trade-off is a function list kept apart from the function definitions. A new function that compares elements or keys has to be added to it.
Ten functions build their equality check again during code generation: ARRAY_CONTAINS, ARRAY_DISTINCT, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_UNION, ARRAY_EXCEPT, ARRAY_INTERSECT, MAP_CONTAINS_KEY, MAP_UNION and MAP_FROM_ENTRIES. That check goes through
LogicalTypeChecks#areComparable, not through the validator, which only runs when a query is planned. So a job restored from a compiled plan that calls one of them on VARIANT elements or keys now fails when it is executed, before the job is submitted. Such a plan returned wrong results before. A userSpecializedFunctionthat builds an=evaluator on VARIANT values now fails too, in new and in restored plans. Other restored plans are not affected. A restored plan withm[k]orCOLLECTon VARIANT still runs and matches by bytes, because the validator never sees it. No test restores such a plan.ComparableInputTypeStrategyTestcovers the check that makes it fail.Not in this PR:
MAP[...]andMAP_FROM_ARRAYSstill build maps with VARIANT keys, and such a map can hold the same key twice. The data types page already says so. A follow-up issue will track them, together withMAP<VARIANT, ...>andMULTISET<VARIANT>as types.Verifying this change
This change added tests and can be verified as follows:
ComparableTypeValidatorTestrejects a call of each of the nine functions with VARIANT elements or keys,m[k]with a VARIANT key, andCOLLECTon VARIANT values in an aggregate and in an OVER window. It acceptsMAP_CONTAINS_KEYon a map with VARIANT values, an index into anARRAY<VARIANT>and a field access on a VARIANT.ComparableInputTypeStrategyTestcovers VARIANT inareComparable, which GREATEST, LEAST, MAP_FROM_ENTRIES and restored plans go through.LogicalTypeChecksTestcovers VARIANT nested in other types, including a structured type with a VARIANT attribute.Does this pull request potentially affect one of the following parts:
@Public(Evolving): no. Calls of the functions and operators above with VARIANT elements or keys now fail, which needs a release note.Documentation
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code 2.1.310 (Claude Opus 5.5)