Skip to content

Commit 9940453

Browse files
alambclaude
andcommitted
test(arrow-select): add take_bytes coverage for sliced values and nullable offset overflow
Adds two tests to `take_bytes`: - `test_take_bytes_sliced_values`: takes from a byte array that has been sliced so its value offsets no longer start at zero, covering both the no-null fast path and the nullable path (null indices and selected null values). - `test_take_bytes_offset_overflow_nullable`: verifies the `OffsetOverflowError` is also produced on the nullable code path, not only the no-null fast path. Both pass on the current implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 1ae2469 commit 9940453

1 file changed

Lines changed: 57 additions & 0 deletions

File tree

arrow-select/src/take.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,41 @@ mod tests {
17281728
assert_eq!(result.as_ref(), &expected);
17291729
}
17301730

1731+
/// Take from a *sliced* byte array, i.e. one whose value offsets do not
1732+
/// start at zero. This exercises copying byte data out of an array with a
1733+
/// non-zero base offset for both the no-null fast path and the nullable
1734+
/// path (null indices and selected null values).
1735+
#[test]
1736+
fn test_take_bytes_sliced_values() {
1737+
let values = StringArray::from(vec![
1738+
Some("aaa"),
1739+
Some("bbb"),
1740+
None,
1741+
Some("ccccc"),
1742+
Some("dd"),
1743+
None,
1744+
Some("eeee"),
1745+
]);
1746+
// Slice so the underlying value offsets no longer start at 0:
1747+
// sliced == [None, "ccccc", "dd", None, "eeee"]
1748+
let sliced = values.slice(2, 5);
1749+
1750+
// Fast path: every output slot is valid (no null indices, no null
1751+
// values selected).
1752+
let indices = Int32Array::from(vec![1, 2, 4, 1]);
1753+
let result = take(&sliced, &indices, None).unwrap();
1754+
let expected =
1755+
StringArray::from(vec![Some("ccccc"), Some("dd"), Some("eeee"), Some("ccccc")]);
1756+
assert_eq!(result.as_string::<i32>(), &expected);
1757+
1758+
// Nullable path: a null index (position 1) and selected null values
1759+
// (sliced indices 0 and 3 are null).
1760+
let indices = Int32Array::from(vec![Some(1), None, Some(0), Some(4), Some(3)]);
1761+
let result = take(&sliced, &indices, None).unwrap();
1762+
let expected = StringArray::from(vec![Some("ccccc"), None, None, Some("eeee"), None]);
1763+
assert_eq!(result.as_string::<i32>(), &expected);
1764+
}
1765+
17311766
fn _test_byte_view<T>()
17321767
where
17331768
T: ByteViewType,
@@ -2819,6 +2854,28 @@ mod tests {
28192854
));
28202855
}
28212856

2857+
/// The offset-overflow error must also be produced on the nullable code
2858+
/// path (when the output contains nulls), not only on the no-null fast path.
2859+
#[test]
2860+
fn test_take_bytes_offset_overflow_nullable() {
2861+
// A single large value selected enough times to overflow an i32 offset,
2862+
// which keeps the index array small. One null index forces the output
2863+
// to contain nulls and therefore exercises the nullable code path.
2864+
let value_len = 100_000;
2865+
let big = "a".repeat(value_len);
2866+
let values = StringArray::from(vec![Some(big.as_str())]);
2867+
2868+
let n = i32::MAX as usize / value_len + 2;
2869+
let validity =
2870+
NullBuffer::from_iter(std::iter::once(false).chain(std::iter::repeat_n(true, n)));
2871+
let indices = Int32Array::new(vec![0i32; n + 1].into(), Some(validity));
2872+
2873+
assert!(matches!(
2874+
take(&values, &indices, None),
2875+
Err(ArrowError::OffsetOverflowError(_))
2876+
));
2877+
}
2878+
28222879
#[test]
28232880
fn test_take_run_empty_indices() {
28242881
let mut builder = PrimitiveRunBuilder::<Int32Type, Int32Type>::new();

0 commit comments

Comments
 (0)