Skip to content

Commit 0a45e8a

Browse files
authored
fix: Fuzzer generates valid DecimalArrays (#3424)
Signed-off-by: Robert Kruszewski <[email protected]>
1 parent df76d32 commit 0a45e8a

File tree

5 files changed

+115
-40
lines changed

5 files changed

+115
-40
lines changed

vortex-array/src/arrays/arbitrary.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ use builders::ListBuilder;
77
use vortex_buffer::Buffer;
88
use vortex_dtype::{DType, NativePType, Nullability, PType};
99
use vortex_error::{VortexExpect, VortexUnwrap};
10-
use vortex_scalar::Scalar;
11-
use vortex_scalar::arbitrary::random_scalar;
10+
use vortex_scalar::arbitrary::{random_decimal, random_scalar};
11+
use vortex_scalar::{Scalar, match_each_decimal_value_type};
1212

1313
use super::{
14-
BoolArray, ChunkedArray, DecimalArray, NullArray, OffsetPType, PrimitiveArray, StructArray,
14+
BoolArray, ChunkedArray, NullArray, OffsetPType, PrimitiveArray, StructArray,
15+
smallest_storage_type,
1516
};
1617
use crate::arrays::{VarBinArray, VarBinViewArray};
17-
use crate::builders::ArrayBuilder;
18+
use crate::builders::{ArrayBuilder, ArrayBuilderExt, DecimalBuilder};
1819
use crate::validity::Validity;
1920
use crate::{Array, ArrayRef, IntoArray, ToCanonical, builders};
2021

@@ -69,13 +70,17 @@ fn random_array(u: &mut Unstructured, dtype: &DType, len: Option<usize>) -> Resu
6970
PType::F64 => random_primitive::<f64>(u, *n, chunk_len),
7071
},
7172
DType::Decimal(decimal, n) => {
72-
// TODO(aduffy): also do i256.
73-
let chunk: Vec<i128> = arbitrary_vec_of_len(u, chunk_len)?;
74-
let validity = random_validity(u, *n, chunk.len())?;
75-
Ok(
76-
DecimalArray::new(Buffer::from_iter(chunk), *decimal, validity)
77-
.into_array(),
78-
)
73+
let elem_len = u.int_in_range(0..=20)?;
74+
match_each_decimal_value_type!(smallest_storage_type(decimal), |DVT| {
75+
let mut builder =
76+
DecimalBuilder::new::<DVT>(decimal.precision(), decimal.scale(), *n);
77+
for _i in 0..elem_len {
78+
builder
79+
.append_scalar_value(random_decimal(u, decimal)?)
80+
.vortex_unwrap();
81+
}
82+
Ok(builder.finish())
83+
})
7984
}
8085
DType::Utf8(n) => random_string(u, *n, chunk_len),
8186
DType::Binary(n) => random_bytes(u, *n, chunk_len),

vortex-scalar/src/arbitrary/decimal.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,62 @@
1+
use arbitrary::unstructured::Int;
12
use arbitrary::{Result, Unstructured};
3+
use num_traits::{CheckedAdd, WrappingAdd, WrappingSub};
24
use vortex_dtype::{DECIMAL128_MAX_PRECISION, DecimalDType};
3-
use vortex_error::VortexUnwrap;
45

56
use crate::{DecimalValue, InnerScalarValue, ScalarValue, i256};
67

7-
/// Generate an arbitrary decimal scalar that is confined to the bounds of
8+
#[allow(clippy::same_name_method)]
9+
impl Int for i256 {
10+
type Unsigned = i256;
11+
const ZERO: Self = i256::ZERO;
12+
const ONE: Self = i256::ONE;
13+
const MAX: Self = i256::MAX;
14+
15+
fn from_u8(b: u8) -> Self {
16+
Self::from_i128(b as i128)
17+
}
18+
19+
fn from_usize(u: usize) -> Self {
20+
Self::from_i128(u as i128)
21+
}
22+
23+
fn checked_add(self, rhs: Self) -> Option<Self> {
24+
<Self as CheckedAdd>::checked_add(&self, &rhs)
25+
}
26+
27+
fn wrapping_add(self, rhs: Self) -> Self {
28+
<Self as WrappingAdd>::wrapping_add(&self, &rhs)
29+
}
30+
31+
fn wrapping_sub(self, rhs: Self) -> Self {
32+
<Self as WrappingSub>::wrapping_sub(&self, &rhs)
33+
}
34+
35+
fn to_unsigned(self) -> Self::Unsigned {
36+
self
37+
}
38+
39+
fn from_unsigned(unsigned: Self::Unsigned) -> Self {
40+
unsigned
41+
}
42+
}
43+
44+
/// Generate an arbitrary decimal scalar confined to the bounds of
845
pub fn random_decimal(u: &mut Unstructured, decimal_type: &DecimalDType) -> Result<ScalarValue> {
946
let precision = decimal_type.precision();
10-
if decimal_type.precision() <= DECIMAL128_MAX_PRECISION {
47+
if precision <= DECIMAL128_MAX_PRECISION {
1148
Ok(ScalarValue(InnerScalarValue::Decimal(DecimalValue::I128(
1249
u.int_in_range(
1350
MIN_DECIMAL128_FOR_EACH_PRECISION[precision as usize]
1451
..=MAX_DECIMAL128_FOR_EACH_PRECISION[precision as usize],
1552
)?,
1653
))))
1754
} else {
18-
// Generate a random i256 value in between the min/max range, inclusive
19-
let min = MIN_DECIMAL256_FOR_EACH_PRECISION[precision as usize];
20-
let max = MAX_DECIMAL256_FOR_EACH_PRECISION[precision as usize];
21-
let delta = (max - min) + i256::ONE;
22-
23-
let rand_bytes = i256::from_le_bytes(u.bytes(32)?.try_into().vortex_unwrap());
24-
let value = (rand_bytes % delta) + min;
2555
Ok(ScalarValue(InnerScalarValue::Decimal(DecimalValue::I256(
26-
value,
56+
u.int_in_range(
57+
MIN_DECIMAL256_FOR_EACH_PRECISION[precision as usize]
58+
..=MAX_DECIMAL256_FOR_EACH_PRECISION[precision as usize],
59+
)?,
2760
))))
2861
}
2962
}

vortex-scalar/src/arbitrary/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::iter;
44
use std::sync::Arc;
55

66
use arbitrary::{Result, Unstructured};
7-
use decimal::random_decimal;
7+
pub use decimal::random_decimal;
88
use vortex_buffer::{BufferString, ByteBuffer};
99
use vortex_dtype::half::f16;
1010
use vortex_dtype::{DType, PType};

vortex-scalar/src/bigint/mod.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
mod bigcast;
22

33
use std::fmt::Display;
4-
use std::ops::{Add, Div, Mul, Rem, Sub};
4+
use std::ops::{Add, BitOr, Div, Mul, Rem, Shl, Shr, Sub};
55

66
pub use bigcast::*;
7-
use num_traits::{CheckedAdd, CheckedSub, ConstZero, One, Zero};
7+
use num_traits::{CheckedAdd, CheckedSub, ConstZero, One, WrappingAdd, WrappingSub, Zero};
8+
use vortex_error::VortexExpect;
89

910
/// Signed 256-bit integer type.
1011
///
@@ -15,6 +16,7 @@ use num_traits::{CheckedAdd, CheckedSub, ConstZero, One, Zero};
1516
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
1617
pub struct i256(arrow_buffer::i256);
1718

19+
#[allow(clippy::same_name_method)]
1820
impl i256 {
1921
pub const ZERO: Self = Self(arrow_buffer::i256::ZERO);
2022
pub const ONE: Self = Self(arrow_buffer::i256::ONE);
@@ -157,12 +159,60 @@ impl CheckedAdd for i256 {
157159
}
158160
}
159161

162+
impl WrappingAdd for i256 {
163+
fn wrapping_add(&self, v: &Self) -> Self {
164+
Self(self.0.wrapping_add(v.0))
165+
}
166+
}
167+
160168
impl CheckedSub for i256 {
161169
fn checked_sub(&self, v: &Self) -> Option<Self> {
162170
self.0.checked_sub(v.0).map(Self)
163171
}
164172
}
165173

174+
impl WrappingSub for i256 {
175+
fn wrapping_sub(&self, v: &Self) -> Self {
176+
Self(self.0.wrapping_sub(v.0))
177+
}
178+
}
179+
180+
impl Shr<Self> for i256 {
181+
type Output = Self;
182+
183+
fn shr(self, rhs: Self) -> Self::Output {
184+
use num_traits::ToPrimitive;
185+
186+
Self(
187+
self.0.shr(
188+
rhs.0
189+
.to_u8()
190+
.vortex_expect("Can't shift more than 256 bits"),
191+
),
192+
)
193+
}
194+
}
195+
196+
impl Shl<usize> for i256 {
197+
type Output = Self;
198+
199+
fn shl(self, rhs: usize) -> Self::Output {
200+
use num_traits::ToPrimitive;
201+
Self(
202+
self.0
203+
.shl(rhs.to_u8().vortex_expect("Can't shift more than 256 bits")),
204+
)
205+
}
206+
}
207+
208+
impl BitOr<Self> for i256 {
209+
type Output = Self;
210+
211+
fn bitor(self, rhs: Self) -> Self::Output {
212+
Self(self.0.bitor(rhs.0))
213+
}
214+
}
215+
166216
impl num_traits::ToPrimitive for i256 {
167217
fn to_i64(&self) -> Option<i64> {
168218
self.maybe_i128().and_then(|v| v.to_i64())

vortex-scalar/src/decimal.rs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::cmp::Ordering;
22
use std::fmt;
3-
use std::fmt::{Display, Formatter};
3+
use std::fmt::{Debug, Display, Formatter};
44
use std::hash::Hash;
55

66
use vortex_dtype::{DType, DecimalDType, Nullability};
@@ -148,20 +148,7 @@ impl Hash for DecimalValue {
148148

149149
/// Type of decimal scalar values.
150150
pub trait NativeDecimalType:
151-
Copy
152-
+ Eq
153-
+ Ord
154-
+ Default
155-
+ Send
156-
+ Sync
157-
+ BigCast
158-
// + AsPrimitive<i8>
159-
// + AsPrimitive<i16>
160-
// + AsPrimitive<i32>
161-
// + AsPrimitive<i64>
162-
// + AsPrimitive<i128>
163-
// + AsPrimitive<i256>
164-
+ 'static
151+
Copy + Eq + Ord + Default + Send + Sync + BigCast + Debug + Display + 'static
165152
{
166153
const VALUES_TYPE: DecimalValueType;
167154

0 commit comments

Comments
 (0)