Skip to content

Commit eeb1ffe

Browse files
chore[fuzzer]: nullable take indices and compare scalar (#3582)
Signed-off-by: Joe Isaacs <[email protected]>
1 parent c0c0ff7 commit eeb1ffe

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

fuzz/src/filter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use vortex_dtype::{DType, match_each_native_ptype};
99
use vortex_error::VortexResult;
1010
use vortex_scalar::match_each_decimal_value_type;
1111

12-
use crate::take::take_canonical_array;
12+
use crate::take::take_canonical_array_non_nullable_indices;
1313

1414
pub fn filter_canonical_array(array: &dyn Array, filter: &[bool]) -> VortexResult<ArrayRef> {
1515
let validity = if array.dtype().is_nullable() {
@@ -103,7 +103,7 @@ pub fn filter_canonical_array(array: &dyn Array, filter: &[bool]) -> VortexResul
103103
indices.push(idx);
104104
}
105105
}
106-
take_canonical_array(array, &indices)
106+
take_canonical_array_non_nullable_indices(array, indices.as_slice())
107107
}
108108
d @ (DType::Null | DType::Extension(_)) => {
109109
unreachable!("DType {d} not supported for fuzzing")

fuzz/src/lib.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ use std::ops::{Range, RangeInclusive};
1515
use libfuzzer_sys::arbitrary::Error::EmptyChoose;
1616
use libfuzzer_sys::arbitrary::{Arbitrary, Result, Unstructured};
1717
pub use sort::sort_canonical_array;
18+
use vortex_array::arrays::PrimitiveArray;
1819
use vortex_array::arrays::arbitrary::ArbitraryArray;
1920
use vortex_array::compute::Operator;
2021
use vortex_array::search_sorted::{SearchResult, SearchSortedSide};
2122
use vortex_array::{Array, ArrayRef, IntoArray};
2223
use vortex_btrblocks::BtrBlocksCompressor;
23-
use vortex_buffer::Buffer;
24-
use vortex_dtype::DType;
24+
use vortex_dtype::{DType, Nullability};
2525
use vortex_error::{VortexUnwrap, vortex_panic};
2626
use vortex_mask::Mask;
2727
use vortex_scalar::Scalar;
@@ -117,11 +117,11 @@ impl<'a> Arbitrary<'a> for FuzzArrayAction {
117117

118118
let indices = random_vec_in_range(u, 0, current_array.len() - 1)?;
119119
current_array = take_canonical_array(&current_array, &indices).vortex_unwrap();
120-
let indices_array = indices
121-
.iter()
122-
.map(|i| *i as u64)
123-
.collect::<Buffer<u64>>()
124-
.into_array();
120+
let indices_array = PrimitiveArray::from_option_iter(
121+
indices.iter().map(|i| i.map(|i| i as u64)),
122+
)
123+
.into_array();
124+
125125
let compressed = BtrBlocksCompressor.compress(&indices_array).vortex_unwrap();
126126
(
127127
Action::Take(compressed),
@@ -175,7 +175,9 @@ impl<'a> Arbitrary<'a> for FuzzArrayAction {
175175
.scalar_at(u.choose_index(current_array.len())?)
176176
.vortex_unwrap()
177177
} else {
178-
random_scalar(u, current_array.dtype())?
178+
// We can compare arrays with different nullability
179+
let null: Nullability = u.arbitrary()?;
180+
random_scalar(u, &current_array.dtype().union_nullability(null))?
179181
};
180182

181183
let op = u.arbitrary()?;
@@ -194,11 +196,19 @@ impl<'a> Arbitrary<'a> for FuzzArrayAction {
194196
}
195197
}
196198

197-
fn random_vec_in_range(u: &mut Unstructured<'_>, min: usize, max: usize) -> Result<Vec<usize>> {
199+
fn random_vec_in_range(
200+
u: &mut Unstructured<'_>,
201+
min: usize,
202+
max: usize,
203+
) -> Result<Vec<Option<usize>>> {
198204
iter::from_fn(|| {
199-
u.arbitrary()
200-
.unwrap_or(false)
201-
.then(|| u.int_in_range(min..=max))
205+
u.arbitrary().unwrap_or(false).then(|| {
206+
if u.arbitrary()? {
207+
Ok(None)
208+
} else {
209+
Ok(Some(u.int_in_range(min..=max)?))
210+
}
211+
})
202212
})
203213
.collect::<Result<Vec<_>>>()
204214
}

fuzz/src/sort.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use vortex_dtype::{DType, NativePType, match_each_native_ptype};
77
use vortex_error::{VortexExpect, VortexResult, VortexUnwrap};
88
use vortex_scalar::match_each_decimal_value_type;
99

10-
use crate::take::take_canonical_array;
10+
use crate::take::take_canonical_array_non_nullable_indices;
1111

1212
pub fn sort_canonical_array(array: &dyn Array) -> VortexResult<ArrayRef> {
1313
match array.dtype() {
@@ -67,7 +67,7 @@ pub fn sort_canonical_array(array: &dyn Array) -> VortexResult<ArrayRef> {
6767
.partial_cmp(&array.scalar_at(*b).vortex_unwrap())
6868
.vortex_expect("must be a valid comparison")
6969
});
70-
take_canonical_array(array, &sort_indices)
70+
take_canonical_array_non_nullable_indices(array, &sort_indices)
7171
}
7272
DType::List(..) => {
7373
let mut sort_indices = (0..array.len()).collect::<Vec<_>>();
@@ -78,7 +78,7 @@ pub fn sort_canonical_array(array: &dyn Array) -> VortexResult<ArrayRef> {
7878
.partial_cmp(&array.scalar_at(*b).vortex_unwrap())
7979
.vortex_expect("must be a valid comparison")
8080
});
81-
take_canonical_array(array, &sort_indices)
81+
take_canonical_array_non_nullable_indices(array, &sort_indices)
8282
}
8383
d @ (DType::Null | DType::Extension(_)) => {
8484
unreachable!("DType {d} not supported for fuzzing")

fuzz/src/take.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,39 @@ use vortex_dtype::{DType, DecimalDType, NativePType, match_each_native_ptype};
99
use vortex_error::VortexResult;
1010
use vortex_scalar::{NativeDecimalType, match_each_decimal_value_type};
1111

12-
pub fn take_canonical_array(array: &dyn Array, indices: &[usize]) -> VortexResult<ArrayRef> {
12+
pub fn take_canonical_array_non_nullable_indices(
13+
array: &dyn Array,
14+
indices: &[usize],
15+
) -> VortexResult<ArrayRef> {
16+
take_canonical_array(
17+
array,
18+
indices
19+
.iter()
20+
.map(|i| Some(*i))
21+
.collect::<Vec<_>>()
22+
.as_slice(),
23+
)
24+
}
25+
26+
pub fn take_canonical_array(
27+
array: &dyn Array,
28+
indices: &[Option<usize>],
29+
) -> VortexResult<ArrayRef> {
1330
let validity = if array.dtype().is_nullable() {
1431
let validity_idx = array.validity_mask()?.to_boolean_buffer();
1532

16-
Validity::from_iter(indices.iter().map(|i| validity_idx.value(*i)))
33+
Validity::from_iter(
34+
indices
35+
.iter()
36+
.map(|i| i.is_some_and(|i| validity_idx.value(i))),
37+
)
1738
} else {
1839
Validity::NonNullable
1940
};
2041

42+
let indices = indices.iter().map(|i| i.unwrap_or(0)).collect::<Vec<_>>();
43+
let indices = indices.as_slice();
44+
2145
match array.dtype() {
2246
DType::Bool(_) => {
2347
let bool_array = array.to_bool()?;
@@ -55,7 +79,7 @@ pub fn take_canonical_array(array: &dyn Array, indices: &[usize]) -> VortexResul
5579
let taken_children = struct_array
5680
.fields()
5781
.iter()
58-
.map(|c| take_canonical_array(c, indices))
82+
.map(|c| take_canonical_array_non_nullable_indices(c, indices))
5983
.collect::<VortexResult<Vec<_>>>()?;
6084

6185
StructArray::try_new(

0 commit comments

Comments
 (0)