Skip to content

Commit 42a65ff

Browse files
chore[array]: fill_null cleanup + decimal impl (#5175)
Signed-off-by: Joe Isaacs <[email protected]>
1 parent a64c7ae commit 42a65ff

File tree

6 files changed

+161
-26
lines changed

6 files changed

+161
-26
lines changed

vortex-array/src/arrays/bool/compute/fill_null.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use vortex_error::{VortexResult, vortex_err};
55
use vortex_scalar::Scalar;
66

7-
use crate::arrays::{BoolArray, BoolVTable, ConstantArray};
7+
use crate::arrays::{BoolArray, BoolVTable};
88
use crate::compute::{FillNullKernel, FillNullKernelAdapter};
99
use crate::validity::Validity;
1010
use crate::vtable::ValidityHelper;
@@ -18,14 +18,6 @@ impl FillNullKernel for BoolVTable {
1818
.ok_or_else(|| vortex_err!("Fill value must be non null"))?;
1919

2020
Ok(match array.validity() {
21-
Validity::NonNullable | Validity::AllValid => BoolArray::from_bit_buffer(
22-
array.bit_buffer().clone(),
23-
fill_value.dtype().nullability().into(),
24-
)
25-
.into_array(),
26-
Validity::AllInvalid => {
27-
ConstantArray::new(fill_value.clone(), array.len()).into_array()
28-
}
2921
Validity::Array(v) => {
3022
let bool_buffer = if fill {
3123
array.bit_buffer() | &!v.to_bool().bit_buffer()
@@ -35,6 +27,7 @@ impl FillNullKernel for BoolVTable {
3527
BoolArray::from_bit_buffer(bool_buffer, fill_value.dtype().nullability().into())
3628
.into_array()
3729
}
30+
_ => unreachable!("checked in entry point"),
3831
})
3932
}
4033
}

vortex-array/src/arrays/decimal/array.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,19 @@ impl DecimalArray {
204204
self.decimal_dtype().scale()
205205
}
206206

207+
pub fn from_iter<T: NativeDecimalType, I: IntoIterator<Item = T>>(
208+
iter: I,
209+
decimal_dtype: DecimalDType,
210+
) -> Self {
211+
let iter = iter.into_iter();
212+
213+
Self::new(
214+
BufferMut::from_iter(iter).freeze(),
215+
decimal_dtype,
216+
Validity::NonNullable,
217+
)
218+
}
219+
207220
pub fn from_option_iter<T: NativeDecimalType, I: IntoIterator<Item = Option<T>>>(
208221
iter: I,
209222
decimal_dtype: DecimalDType,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::ops::Not;
5+
6+
use vortex_error::{VortexExpect, VortexResult};
7+
use vortex_scalar::{Scalar, match_each_decimal_value_type};
8+
9+
use crate::arrays::DecimalVTable;
10+
use crate::arrays::decimal::DecimalArray;
11+
use crate::compute::{FillNullKernel, FillNullKernelAdapter};
12+
use crate::validity::Validity;
13+
use crate::vtable::ValidityHelper;
14+
use crate::{ArrayRef, IntoArray, ToCanonical, register_kernel};
15+
16+
impl FillNullKernel for DecimalVTable {
17+
fn fill_null(&self, array: &DecimalArray, fill_value: &Scalar) -> VortexResult<ArrayRef> {
18+
let result_validity = Validity::from(fill_value.dtype().nullability());
19+
20+
Ok(match array.validity() {
21+
Validity::Array(is_valid) => {
22+
let is_invalid = is_valid.to_bool().bit_buffer().not();
23+
match_each_decimal_value_type!(array.values_type(), |T| {
24+
let mut buffer = array.buffer::<T>().into_mut();
25+
let fill_value = fill_value
26+
.as_decimal()
27+
.decimal_value()
28+
.and_then(|v| v.cast::<T>())
29+
.vortex_expect("top-level fill_null ensure non-null fill value");
30+
for invalid_index in is_invalid.set_indices() {
31+
buffer[invalid_index] = fill_value;
32+
}
33+
DecimalArray::new(buffer.freeze(), array.decimal_dtype(), result_validity)
34+
.into_array()
35+
})
36+
}
37+
_ => unreachable!("checked in entry point"),
38+
})
39+
}
40+
}
41+
42+
register_kernel!(FillNullKernelAdapter(DecimalVTable).lift());
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use vortex_buffer::buffer;
47+
use vortex_dtype::{DecimalDType, Nullability};
48+
use vortex_scalar::{DecimalValue, Scalar};
49+
50+
use crate::arrays::decimal::DecimalArray;
51+
use crate::assert_arrays_eq;
52+
use crate::canonical::ToCanonical;
53+
use crate::compute::fill_null;
54+
use crate::validity::Validity;
55+
56+
#[test]
57+
fn fill_null_leading_none() {
58+
let decimal_dtype = DecimalDType::new(19, 2);
59+
let arr = DecimalArray::from_option_iter(
60+
[None, Some(800i128), None, Some(1000i128), None],
61+
decimal_dtype,
62+
);
63+
let p = fill_null(
64+
arr.as_ref(),
65+
&Scalar::decimal(
66+
DecimalValue::I128(4200i128),
67+
DecimalDType::new(19, 2),
68+
Nullability::NonNullable,
69+
),
70+
)
71+
.unwrap()
72+
.to_decimal();
73+
assert_arrays_eq!(
74+
p,
75+
DecimalArray::from_iter([4200, 800, 4200, 1000, 4200], decimal_dtype)
76+
);
77+
assert_eq!(
78+
p.buffer::<i128>().as_slice(),
79+
vec![4200, 800, 4200, 1000, 4200]
80+
);
81+
assert!(p.validity_mask().all_true());
82+
}
83+
84+
#[test]
85+
fn fill_null_all_none() {
86+
let decimal_dtype = DecimalDType::new(19, 2);
87+
88+
let arr = DecimalArray::from_option_iter(
89+
[Option::<i128>::None, None, None, None, None],
90+
decimal_dtype,
91+
);
92+
93+
let p = fill_null(
94+
arr.as_ref(),
95+
&Scalar::decimal(
96+
DecimalValue::I128(25500i128),
97+
DecimalDType::new(19, 2),
98+
Nullability::NonNullable,
99+
),
100+
)
101+
.unwrap()
102+
.to_decimal();
103+
assert_arrays_eq!(
104+
p,
105+
DecimalArray::from_iter([25500, 25500, 25500, 25500, 25500], decimal_dtype)
106+
);
107+
}
108+
109+
#[test]
110+
fn fill_null_non_nullable() {
111+
let decimal_dtype = DecimalDType::new(19, 2);
112+
113+
let arr = DecimalArray::new(
114+
buffer![800i128, 1000i128, 1200i128, 1400i128, 1600i128],
115+
decimal_dtype,
116+
Validity::NonNullable,
117+
);
118+
let p = fill_null(
119+
arr.as_ref(),
120+
&Scalar::decimal(
121+
DecimalValue::I128(25500i128),
122+
DecimalDType::new(19, 2),
123+
Nullability::NonNullable,
124+
),
125+
)
126+
.unwrap()
127+
.to_decimal();
128+
assert_arrays_eq!(
129+
p,
130+
DecimalArray::from_iter([800i128, 1000, 1200, 1400, 1600], decimal_dtype)
131+
);
132+
}
133+
}

vortex-array/src/arrays/decimal/compute/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
mod between;
55
mod cast;
6+
mod fill_null;
67
mod filter;
78
mod is_constant;
89
mod is_sorted;

vortex-array/src/arrays/primitive/compute/fill_null.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,26 @@
33

44
use std::ops::Not;
55

6-
use vortex_buffer::BufferMut;
7-
use vortex_dtype::{Nullability, match_each_native_ptype};
6+
use vortex_dtype::match_each_native_ptype;
87
use vortex_error::{VortexExpect, VortexResult};
98
use vortex_scalar::Scalar;
109

10+
use crate::arrays::PrimitiveVTable;
1111
use crate::arrays::primitive::PrimitiveArray;
12-
use crate::arrays::{ConstantArray, PrimitiveVTable};
1312
use crate::compute::{FillNullKernel, FillNullKernelAdapter};
1413
use crate::validity::Validity;
1514
use crate::vtable::ValidityHelper;
1615
use crate::{ArrayRef, IntoArray, ToCanonical, register_kernel};
1716

1817
impl FillNullKernel for PrimitiveVTable {
1918
fn fill_null(&self, array: &PrimitiveArray, fill_value: &Scalar) -> VortexResult<ArrayRef> {
20-
let result_validity = match fill_value.dtype().nullability() {
21-
Nullability::NonNullable => Validity::NonNullable,
22-
Nullability::Nullable => Validity::AllValid,
23-
};
19+
let result_validity = Validity::from(fill_value.dtype().nullability());
2420

2521
Ok(match array.validity() {
26-
Validity::NonNullable | Validity::AllValid => {
27-
match_each_native_ptype!(array.ptype(), |T| {
28-
PrimitiveArray::new::<T>(array.buffer(), result_validity).into_array()
29-
})
30-
}
31-
Validity::AllInvalid => {
32-
ConstantArray::new(fill_value.clone(), array.len()).into_array()
33-
}
3422
Validity::Array(is_valid) => {
35-
// TODO(danking): when we take PrimitiveArray by value, we should mutate in-place
3623
let is_invalid = is_valid.to_bool().bit_buffer().not();
3724
match_each_native_ptype!(array.ptype(), |T| {
38-
let mut buffer = BufferMut::copy_from(array.as_slice::<T>());
25+
let mut buffer = array.buffer::<T>().into_mut();
3926
let fill_value = fill_value
4027
.as_primitive()
4128
.typed_value::<T>()
@@ -46,6 +33,7 @@ impl FillNullKernel for PrimitiveVTable {
4633
PrimitiveArray::new(buffer.freeze(), result_validity).into_array()
4734
})
4835
}
36+
_ => unreachable!("checked in entry point"),
4937
})
5038
}
5139
}

vortex-array/src/compute/fill_null.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use vortex_dtype::DType;
88
use vortex_error::{VortexError, VortexResult, vortex_bail, vortex_err};
99
use vortex_scalar::Scalar;
1010

11+
use crate::arrays::ConstantArray;
1112
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Output, cast};
1213
use crate::vtable::VTable;
1314
use crate::{Array, ArrayRef, IntoArray};
@@ -89,6 +90,12 @@ impl ComputeFnVTable for FillNull {
8990
return Ok(cast(array, fill_value.dtype())?.into());
9091
}
9192

93+
if array.all_invalid() {
94+
return Ok(ConstantArray::new(fill_value.clone(), array.len())
95+
.into_array()
96+
.into());
97+
}
98+
9299
if fill_value.is_null() {
93100
vortex_bail!("Cannot fill_null with a null value")
94101
}

0 commit comments

Comments
 (0)