[UUID 4a] CAST to and from the logical UUID type - #19181
Merged
Merged
Conversation
Adds CAST support for the logical UUID type, in both directions and for single- and multi-value columns: - CAST(<string|bytes> AS UUID) parses the value into the 16-byte stored form - a UUID result read as a string renders the canonical dashed form rather than the hex a BYTES column would produce The conversions live in ArrayCopyUtils as copyToUuid / copyFromUuid, next to copyFromTimestamp which they mirror -- a logical type parsed into, or rendered from, its stored representation. Note the two directions are distinct and both are needed: apache#19165 handles a UUID *source* cast to STRING, while this handles rendering a UUID *result*, e.g. reading CAST(x AS UUID) as a string. Split out of apache#18872 to keep that PR to predicate evaluation. Depends only on UuidUtils (apache#18869) and the UUID stored type (apache#18870), both already on master.
This was referenced Aug 7, 2026
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 7, 2026
Adds UUID handling to the predicate evaluators, so =, !=, IN, NOT IN and range predicates work against a UUID column on both the raw and the dictionary path. UUID follows the pattern TIMESTAMP already uses: a logical type whose stored type does the work. The literal is parsed to its 16-byte stored form once, when the evaluator is built, and from there the existing BYTES evaluators apply -- no per-value conversion in the scan loop. The dictionary path needs no UUID branch: Dictionary#getStoredValue returns hex for a UUID column and indexOf(String) hex-decodes, so the existing String-keyed lookup is already correct. PredicateUtils renders the literal to that hex form for those String-typed lookup APIs. Split into apache#19181 (CAST), apache#19182 (bloom filter pruning) and apache#19183 (transform functions); this PR is now just the predicate evaluators.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #19181 +/- ##
=========================================
Coverage 66.62% 66.62%
Complexity 1423 1423
=========================================
Files 3443 3443
Lines 218577 218633 +56
Branches 34792 34795 +3
=========================================
+ Hits 145624 145675 +51
- Misses 61218 61232 +14
+ Partials 11735 11726 -9
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 7, 2026
Adds UUID handling to the predicate evaluators, so =, !=, IN, NOT IN and range predicates work against a UUID column on both the raw and the dictionary path. UUID follows the pattern TIMESTAMP already uses: a logical type whose stored type does the work. The literal is parsed to its 16-byte stored form once, when the evaluator is built, and from there the existing BYTES evaluators apply -- no per-value conversion in the scan loop. The dictionary path needs no UUID branch: Dictionary#getStoredValue returns hex for a UUID column and indexOf(String) hex-decodes, so the existing String-keyed lookup is already correct. PredicateUtils renders the literal to that hex form for those String-typed lookup APIs. Split into apache#19181 (CAST), apache#19182 (bloom filter pruning) and apache#19183 (transform functions); this PR is now just the predicate evaluators.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 7, 2026
Three transform functions did not handle a UUID operand: - BinaryOperatorTransformFunction compared UUID columns via the STRING path, so the comparison ran over the rendered form instead of the stored bytes. The fix is generic -- it routes any BYTES-stored logical type through the bytes comparison -- and is not UUID-specific. - CaseTransformFunction returned UUID branches through the string path, so a bare STRING literal branch was not coerced to UUID bytes. - InTransformFunction parsed the IN list as hex, which rejects the canonical dashed form users actually write. Split out of apache#18872. Stacked on apache#19181 -- both touch BaseTransformFunctionTest, whose UUID column and assertion branch these tests build on.
Jackie-Jiang
approved these changes
Aug 7, 2026
xiangfu0
added a commit
to pinot-contrib/pinot-docs
that referenced
this pull request
Aug 7, 2026
Documents the CAST behavior added by apache/pinot#19181. - Adds UUID to the CAST target types. - Documents string and 16-byte inputs, canonical output, validation, and SV/MV support. Co-authored-by: Xiang Fu <xiangfu@Xiang-mac-mtv-2.local>
Contributor
Author
|
Documentation follow-up: pinot-contrib/pinot-docs#968 (merged). |
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 7, 2026
Three transform functions did not handle a UUID operand: - BinaryOperatorTransformFunction compared UUID columns via the STRING path, so the comparison ran over the rendered form instead of the stored bytes. The fix is generic -- it routes any BYTES-stored logical type through the bytes comparison -- and is not UUID-specific. - CaseTransformFunction returned UUID branches through the string path, so a bare STRING literal branch was not coerced to UUID bytes. - InTransformFunction parsed the IN list as hex, which rejects the canonical dashed form users actually write. Split out of apache#18872. Stacked on apache#19181 -- both touch BaseTransformFunctionTest, whose UUID column and assertion branch these tests build on.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 8, 2026
Add UUID transform coverage and stored-byte handling for CASE and IN. - CASE validates bare UUID branches as fixed-width hex and initializes the bytes result buffer for null-aware evaluation; canonical dashed values require CAST(... AS UUID). - IN parses all-literal UUID lists to the 16-byte stored form so dashed and dashless spellings compare by value. - Binary comparisons already dispatch through BYTES stored types; add dictionary/raw regression coverage and improve diagnostics and comparator documentation. Split out of apache#18872. Prerequisite UUID changes apache#19181 and apache#19182 are now on master.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 8, 2026
Add UUID transform regression coverage and stored-byte handling for CASE. - CASE validates bare UUID branches as fixed-width hex and initializes the bytes result buffer for null-aware evaluation; canonical dashed values require CAST(... AS UUID).\n- IN already uses the BYTES stored path; cover uppercase hex, explicit UUID casts, and rejection of bare canonical strings.\n- Binary comparisons already dispatch through BYTES stored types; add dictionary/raw regression coverage and improve diagnostics and comparator documentation. Split out of apache#18872. Prerequisite UUID changes apache#19181 and apache#19182 are now on master.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 8, 2026
Add UUID transform regression coverage and stored-byte handling for CASE. - CASE validates bare UUID branches as fixed-width hex and initializes the bytes result buffer for null-aware evaluation; canonical dashed values require CAST(... AS UUID). - IN already uses the BYTES stored path; cover uppercase hex, explicit UUID casts, and rejection of bare canonical strings. - Binary comparisons already dispatch through BYTES stored types; add dictionary/raw regression coverage and improve diagnostics and comparator documentation. Split out of apache#18872. Prerequisite UUID changes apache#19181 and apache#19182 are now on master.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 8, 2026
Add UUID transform regression coverage and stored-byte handling for CASE. - CASE validates bare UUID branches as fixed-width hex and initializes the bytes result buffer for null-aware evaluation; canonical dashed values require CAST(... AS UUID). - IN already uses the BYTES stored path; cover uppercase hex, explicit UUID casts, and rejection of bare canonical strings. - Binary comparisons already dispatch through BYTES stored types; add dictionary/raw regression coverage and improve diagnostics and comparator documentation. Split out of apache#18872. Prerequisite UUID changes apache#19181 and apache#19182 are now on master.
xiangfu0
added a commit
to xiangfu0/pinot
that referenced
this pull request
Aug 8, 2026
Adds UUID handling to the predicate evaluators, so =, !=, IN, NOT IN and range predicates work against a UUID column on both the raw and the dictionary path. UUID follows the pattern TIMESTAMP already uses: a logical type whose stored type does the work. The literal is parsed to its 16-byte stored form once, when the evaluator is built, and from there the existing BYTES evaluators apply -- no per-value conversion in the scan loop. The dictionary path needs no UUID branch: Dictionary#getStoredValue returns hex for a UUID column and indexOf(String) hex-decodes, so the existing String-keyed lookup is already correct. PredicateUtils renders the literal to that hex form for those String-typed lookup APIs. Split into apache#19181 (CAST), apache#19182 (bloom filter pruning) and apache#19183 (transform functions); this PR is now just the predicate evaluators.
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
CASTsupport for the logicalUUIDtype, in both directions, single- and multi-value.CAST(<string|bytes> AS UUID)parses into the 16-byte stored formWhy both directions
These are genuinely distinct and both are needed:
CAST(uuidCol AS STRING)CAST(x AS UUID)back as a stringThey live in different switches in
CastTransformFunction(one dispatches on the source type, the other on the result type). Removing either producesIllegalStateException: Cannot cast from SV UUID to STRING.Where the conversions live
ArrayCopyUtils.copyToUuid()/copyFromUuid(), SV and MV, placed with their src/dest neighbours. They mirror the existingcopyFromTimestamp— a logical type parsed into, or rendered from, its stored representation.Testing
CastTransformFunctionTestcovers both directions, the round trip through BYTES, and rejection of invalid literals, invalid byte lengths and MV sources.BaseTransformFunctionTestgains a UUID single-value column and the matching assertion branch, which the other UUID transform tests build on.About this PR
Split out of #18872 to keep that one to predicate evaluation. Depends only on
UuidUtils(#18869) and the UUID stored type (#18870), both already on master — it does not depend on #18872 and can merge independently.Part of the #18140 UUID split.