Repository navigation
fix(isthmus)!: translate array indexes correctly - #1261
Conversation
8659726 to
125c546
Compare
Normalize safe array indexes by their operator base. Preserve null and out-of-range low indexes without introducing negative list offsets, and check wide indexes before narrowing. BREAKING CHANGE: Unsafe array operators and unrepresentable offsets now reach unsupported-function handling instead of changing semantics.
Folding an invalid or null index to NULL can remove a throwing cast or another computed array operand. Restrict this fold to literals and references into an existing record; reject other operands when their evaluation cannot be preserved. Cover throwing casts, nested array selections, computed operands, and safe literal and column references for ITEM, SAFE_OFFSET, and SAFE_ORDINAL.
125c546 to
bafd921
Compare
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Summary
Merge Risk: ⚪ Minimal · up to The change updates array-index offsets, and the nested-query tests check the expected zero-based offsets. No concrete user-facing failure is established, so the PR appears mergeable subject to normal checks. Security Architecture Review
Pre-merge checks |
|
nielspardon
left a comment
There was a problem hiding this comment.
Thanks, the offset translation itself looks right. Before a line-by-line pass I'd like to settle one design question on the NULL-folding path (inline).
Use a guaranteed out-of-range Substrait list offset for null and below-base indexes, retaining computed operand evaluation and collection composition. Infer nullable list-selection results consistently through nested references and protobuf conversion. BREAKING CHANGE: List selections now report nullable element types, and selections through nullable struct or map parents propagate that nullability.
Merge current main and reuse the field-type finders in nonthrowing reference resolution, preserving bounds and map-key validation. Cover nullable parents and nested list selections, and update the retained reference expectation to its nullable type.
|
I went with the reference approach so the array operand is still evaluated. The spec’s list-length limit makes the chosen positive offset out of range. Nullable result types now survive nested selections and protobuf conversion, including the collection, ROW, and UDT cases you raised. |
nielspardon
left a comment
There was a problem hiding this comment.
Thanks, the out-of-range offset works: the spec caps lists at 2,147,483,647 elements, so no element can be at that offset. Please move the :core nullability change into its own fix(core)! PR for this one to stack on. It changes derived types for every core user, only this PR's title reaches the changelog, and the spec doesn't say how selection nullability should work, so that PR should say so.
|
Good catches. Unbound fields and map values now stay unchanged when the parent is required. Untyped NULL indexes bypass literal conversion, and oversized offsets use the out-of-range sentinel. I reproduced the required-schema virtual-table failure too. You're right that the core nullability change needs separate review; its compatibility with Go-produced plans is still open. |
nielspardon
left a comment
There was a problem hiding this comment.
Please move the :core nullability commits out of this PR into a separate fix(core)! PR for #1338. Every remaining problem I found comes from them: plans with a list selection in a virtual table no longer load, ARRAY[ARRAY[a[1]], ARRAY[0]] no longer converts, and an identity RelCopyOnWriteVisitor pass retypes scans whose schema struct is nullable. None of that is needed for the index fix, and the isthmus side is close.
The change to the derived type of list, struct and map selections needs its own review. The array index translation in isthmus does not depend on it.
Check the array operator before the array operand is converted, so a declined call does not convert the operand first. Assert that the converted segment is a list element before the tests read its offset. Protobuf returns offset 0 for a missing segment. Remove the type assertions that depended on the core nullability change.
|
This PR now contains only the isthmus index translation. I removed the |
nielspardon
left a comment
There was a problem hiding this comment.
Thanks, the split and both fixes look good. Please correct the BREAKING CHANGE footer, since the release notes publish it verbatim: SAFE_OFFSET already wrote a zero-based offset, and what consumers will actually notice is that a[0], negative indexes, a NULL index and indexes of 2³¹ or more now return null instead of an element.
|
Thanks, I've addressed these. The footer now describes the invalid-index changes and makes clear that |
Calcite's safe array operators use zero- or one-based indexes, while Substrait list references use zero-based offsets and interpret negative offsets from the end. Copying the literal index changes the selected value: for a = [10, 20, 30], a[1] becomes the second element and a[-1] becomes the last instead of null.
Normalize indexes using each operator's declared base and check wide integer indexes before narrowing to the protobuf offset. For null or below-base indexes, or offsets at or above the list-length limit, retain the list reference with offset 2,147,483,647. The Substrait list-length limit makes that offset out of range, so the result is null while the array operand is still evaluated.
Downsides
Substrait list references cannot preserve the throwing behavior of unsafe OFFSET/ORDINAL operators. Those operators fall through to unsupported-function handling. Converting ordinary list selections back to Calcite remains unsupported.
This change does not alter the derived type of a list selection. The type stays the declared element type, so a selection that returns null can still derive a required type. The spec does not define selection type nullability. #1338 tracks that question for
:core.BREAKING CHANGE: Unsafe array access operators (
OFFSET,ORDINAL) now fail conversion. Safe accesses such asa[0], negative indexes, NULL indexes, and indexes of 2^31 or more now return null; invalid indexes are no longer copied or narrowed into a potentially valid list offset.SAFE_OFFSETremains zero-based. Callers must handle unsupported unsafe operators and remove workarounds that depended on the previous invalid-index behavior.