vortex_array/scalar_fn/fns/fill_null/
mod.rs1mod kernel;
5
6use std::fmt::Formatter;
7
8pub use kernel::*;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_error::vortex_ensure;
12use vortex_error::vortex_err;
13use vortex_session::VortexSession;
14
15use crate::AnyColumnar;
16use crate::ArrayRef;
17use crate::CanonicalView;
18use crate::ColumnarView;
19use crate::ExecutionCtx;
20use crate::arrays::BoolVTable;
21use crate::arrays::DecimalVTable;
22use crate::arrays::PrimitiveVTable;
23use crate::builtins::ArrayBuiltins;
24use crate::dtype::DType;
25use crate::expr::Expression;
26use crate::scalar::Scalar;
27use crate::scalar_fn::Arity;
28use crate::scalar_fn::ChildName;
29use crate::scalar_fn::EmptyOptions;
30use crate::scalar_fn::ExecutionArgs;
31use crate::scalar_fn::ScalarFnId;
32use crate::scalar_fn::ScalarFnVTable;
33
34#[derive(Clone)]
36pub struct FillNull;
37
38impl ScalarFnVTable for FillNull {
39 type Options = EmptyOptions;
40
41 fn id(&self) -> ScalarFnId {
42 ScalarFnId::from("vortex.fill_null")
43 }
44
45 fn serialize(&self, _options: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
46 Ok(Some(vec![]))
47 }
48
49 fn deserialize(
50 &self,
51 _metadata: &[u8],
52 _session: &VortexSession,
53 ) -> VortexResult<Self::Options> {
54 Ok(EmptyOptions)
55 }
56
57 fn arity(&self, _options: &Self::Options) -> Arity {
58 Arity::Exact(2)
59 }
60
61 fn child_name(&self, _options: &Self::Options, child_idx: usize) -> ChildName {
62 match child_idx {
63 0 => ChildName::from("input"),
64 1 => ChildName::from("fill_value"),
65 _ => unreachable!("Invalid child index {} for FillNull expression", child_idx),
66 }
67 }
68
69 fn fmt_sql(
70 &self,
71 _options: &Self::Options,
72 expr: &Expression,
73 f: &mut Formatter<'_>,
74 ) -> std::fmt::Result {
75 write!(f, "fill_null(")?;
76 expr.child(0).fmt_sql(f)?;
77 write!(f, ", ")?;
78 expr.child(1).fmt_sql(f)?;
79 write!(f, ")")
80 }
81
82 fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult<DType> {
83 vortex_ensure!(
84 arg_dtypes[0].eq_ignore_nullability(&arg_dtypes[1]),
85 "fill_null requires input and fill value to have the same base type, got {} and {}",
86 arg_dtypes[0],
87 arg_dtypes[1]
88 );
89 Ok(arg_dtypes[0]
91 .clone()
92 .with_nullability(arg_dtypes[1].nullability()))
93 }
94
95 fn execute(
96 &self,
97 _options: &Self::Options,
98 args: &dyn ExecutionArgs,
99 ctx: &mut ExecutionCtx,
100 ) -> VortexResult<ArrayRef> {
101 let input = args.get(0)?;
102 let fill_value = args.get(1)?;
103
104 let fill_scalar = fill_value
105 .as_constant()
106 .ok_or_else(|| vortex_err!("fill_null fill_value must be a constant/scalar"))?;
107
108 let Some(columnar) = input.as_opt::<AnyColumnar>() else {
109 return input.execute::<ArrayRef>(ctx)?.fill_null(fill_scalar);
110 };
111
112 match columnar {
113 ColumnarView::Canonical(canonical) => fill_null_canonical(canonical, &fill_scalar, ctx),
114 ColumnarView::Constant(constant) => fill_null_constant(constant, &fill_scalar),
115 }
116 }
117
118 fn simplify(
119 &self,
120 _options: &Self::Options,
121 expr: &Expression,
122 ctx: &dyn crate::scalar_fn::SimplifyCtx,
123 ) -> VortexResult<Option<Expression>> {
124 let input_dtype = ctx.return_dtype(expr.child(0))?;
125
126 if !input_dtype.is_nullable() {
127 return Ok(Some(expr.child(0).clone()));
128 }
129
130 Ok(None)
131 }
132
133 fn validity(
134 &self,
135 _options: &Self::Options,
136 expression: &Expression,
137 ) -> VortexResult<Option<Expression>> {
138 Ok(Some(expression.child(1).validity()?))
141 }
142
143 fn is_null_sensitive(&self, _options: &Self::Options) -> bool {
144 true
145 }
146
147 fn is_fallible(&self, _options: &Self::Options) -> bool {
148 false
149 }
150}
151
152fn fill_null_canonical(
156 canonical: CanonicalView<'_>,
157 fill_value: &Scalar,
158 ctx: &mut ExecutionCtx,
159) -> VortexResult<ArrayRef> {
160 let arr = canonical.as_ref().to_array();
161 if let Some(result) = precondition(&arr, fill_value)? {
162 return result.execute::<ArrayRef>(ctx);
167 }
168 match canonical {
169 CanonicalView::Bool(a) => <BoolVTable as FillNullKernel>::fill_null(a, fill_value, ctx)?
170 .ok_or_else(|| vortex_err!("FillNullKernel for BoolArray returned None")),
171 CanonicalView::Primitive(a) => {
172 <PrimitiveVTable as FillNullKernel>::fill_null(a, fill_value, ctx)?
173 .ok_or_else(|| vortex_err!("FillNullKernel for PrimitiveArray returned None"))
174 }
175 CanonicalView::Decimal(a) => {
176 <DecimalVTable as FillNullKernel>::fill_null(a, fill_value, ctx)?
177 .ok_or_else(|| vortex_err!("FillNullKernel for DecimalArray returned None"))
178 }
179 other => vortex_bail!(
180 "No FillNullKernel for canonical array {}",
181 other.as_ref().encoding_id()
182 ),
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use vortex_buffer::buffer;
189 use vortex_error::VortexExpect;
190
191 use crate::IntoArray;
192 use crate::arrays::PrimitiveArray;
193 use crate::arrays::StructArray;
194 use crate::assert_arrays_eq;
195 use crate::dtype::DType;
196 use crate::dtype::Nullability;
197 use crate::dtype::PType;
198 use crate::expr::fill_null;
199 use crate::expr::get_item;
200 use crate::expr::lit;
201 use crate::expr::root;
202
203 #[test]
204 fn dtype() {
205 let dtype = DType::Primitive(PType::I32, Nullability::Nullable);
206 assert_eq!(
207 fill_null(root(), lit(0i32)).return_dtype(&dtype).unwrap(),
208 DType::Primitive(PType::I32, Nullability::NonNullable)
209 );
210 }
211
212 #[test]
213 fn replace_children() {
214 let expr = fill_null(root(), lit(0i32));
215 expr.with_children(vec![root(), lit(0i32)])
216 .vortex_expect("operation should succeed in test");
217 }
218
219 #[test]
220 fn evaluate() {
221 let test_array =
222 PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), None, Some(5)])
223 .into_array();
224
225 let expr = fill_null(root(), lit(42i32));
226 let result = test_array.apply(&expr).unwrap();
227
228 assert_eq!(
229 result.dtype(),
230 &DType::Primitive(PType::I32, Nullability::NonNullable)
231 );
232 assert_arrays_eq!(result, PrimitiveArray::from_iter([1i32, 42, 3, 42, 5]));
233 }
234
235 #[test]
236 fn evaluate_struct_field() {
237 let test_array = StructArray::from_fields(&[(
238 "a",
239 PrimitiveArray::from_option_iter([Some(1i32), None, Some(3)]).into_array(),
240 )])
241 .unwrap()
242 .into_array();
243
244 let expr = fill_null(get_item("a", root()), lit(0i32));
245 let result = test_array.apply(&expr).unwrap();
246
247 assert_eq!(
248 result.dtype(),
249 &DType::Primitive(PType::I32, Nullability::NonNullable)
250 );
251 assert_arrays_eq!(result, PrimitiveArray::from_iter([1i32, 0, 3]));
252 }
253
254 #[test]
255 fn evaluate_non_nullable_input() {
256 let test_array = buffer![1i32, 2, 3].into_array();
257 let expr = fill_null(root(), lit(0i32));
258 let result = test_array.apply(&expr).unwrap();
259 assert_arrays_eq!(result, PrimitiveArray::from_iter([1i32, 2, 3]));
260 }
261
262 #[test]
263 fn test_display() {
264 let expr = fill_null(get_item("value", root()), lit(0i32));
265 assert_eq!(expr.to_string(), "fill_null($.value, 0i32)");
266 }
267}