Skip to content

fix(arrow-array): deprecate FixedSizeListArray::value_offset, which wraps past i32::MAX - #11070

Open
Cintu07 wants to merge 2 commits into
apache:mainfrom
Cintu07:fix/fixed-size-list-value-offset
Open

Cintu07 wants to merge 2 commits into
apache:mainfrom
Cintu07:fix/fixed-size-list-value-offset

Conversation

@Cintu07

@Cintu07 Cintu07 commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

FixedSizeListArray::value_offset casts a usize offset to i32, so once a row starts past i32::MAX child values it returns a negative number. the avro FixedSizeList encoder cast that back to usize and panicked writing the batch.

What changes are included in this PR?

  • deprecate value_offset in favour of i * value_length() as usize, same as FixedSizeBinaryArray::value_offset in [arrow-array] use usize arithmetic in FixedSizeBinaryArray, aggressive overflow checks #9910, and panic instead of wrapping
  • the avro encoder uses idx * elem_len instead
  • take_value_indices_from_fixed_size_list computes the offset in usize and returns an error when it doesn't fit u32
  • the value_offset asserts come out of the tests, and the slice test checks row values instead

Are these changes tested?

yes. fixed_size_list_encoder_int32_sliced is new and covers the avro change. the overflow itself needs 384 MiB so it isn't a unit test, but the repro from the issue panics on 59.3.0 and writes the batch on this branch. arrow-array 972, arrow-select 446 and arrow-avro 515 tests pass, and clippy is clean.

Are there any user-facing changes?

FixedSizeListArray::value_offset is deprecated and panics past i32::MAX. take on a FixedSizeList returns an error past u32::MAX instead of wrapping.

…::MAX

value_offset narrows a usize offset to i32 with as, so past i32::MAX
child values it returns a negative number. The avro FixedSizeList
encoder cast that back to usize and panicked writing the batch.

Deprecate it in favour of i * value_length() as usize, as was done for
FixedSizeBinaryArray::value_offset, and panic instead of wrapping. The
avro encoder and take work the offset out in usize, and take returns an
error when it does not fit its u32 indices.

Closes apache#11059.
@github-actions github-actions Bot added arrow Changes to the arrow crate arrow-avro arrow-avro crate arrow-select arrow-array labels Sep 13, 2026
/// # Panics
///
/// Panics if the offset exceeds `i32::MAX`.
#[deprecated(since = "60.1.0", note = "Use i * value_length() as usize instead")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
#[deprecated(since = "60.1.0", note = "Use i * value_length() as usize instead")]
#[deprecated(since = "60.0.0", note = "Use i * value_length() as usize instead")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we could also make value_offset_at public and point to that, but considering its just a thin wrapper over the multiplication and we don't use value_offset much in the codebase, its probably fine to leave it private to not expand our API surface 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do think the wrapping / offset calculations is somewhat tricky -- so exposing a function that does it the right way seems good to me

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.

taken, and value_offset_at is public now with the note pointing at it. the multiplication is thin but the i32 narrowing is exactly what this issue was, so a public usize accessor is the thing to point people at rather than asking every caller to rewrite it correctly themselves.

@Jefffrey Jefffrey added the bug label Sep 15, 2026

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @Cintu07 and @Jefffrey

/// # Panics
///
/// Panics if the offset exceeds `i32::MAX`.
#[deprecated(since = "60.1.0", note = "Use i * value_length() as usize instead")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do think the wrapping / offset calculations is somewhat tricky -- so exposing a function that does it the right way seems good to me

#[inline]
pub fn value_offset(&self, i: usize) -> i32 {
self.value_offset_at(i) as i32
i32::try_from(self.value_offset_at(i)).expect("offset overflow")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It would be somewhat annoying if I were a user to start seeing panics (even though you could argue that is better than silent overflows 🤔 )

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.

fair. the deprecated method only panics past i32::MAX, where it used to hand back a negative offset and the avro writer turned that into an index near u64::MAX. FixedSizeBinaryArray::value_offset documents the same panic since 59.0.0. anyone who wants no panic at all can move to value_offset_at, which stays in usize. happy to leave the wrapping cast alone and only deprecate if you would rather not add a panic at all.

@alamb alamb Sep 16, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for a minor release I think we should just deprecate the function and not change its behavior

Jefffrey asked for 60.0.0 rather than 60.1.0, and alamb asked for a public
function that works the offset out correctly, so value_offset_at is public now
and the deprecation note points at it.

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should change the implementation of

    pub fn value_offset(&self, i: usize) -> i32 {

Other than that this PR looks good to go to me -- thank you @Cintu07

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

Labels

arrow Changes to the arrow crate arrow-array arrow-avro arrow-avro crate arrow-select bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FixedSizeListArray::value_offset wraps past i32::MAX, and the avro writer panics on it

3 participants