vortex_array/scalar_fn/fns/
is_not_null.rs1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use vortex_error::VortexResult;
8use vortex_session::VortexSession;
9use vortex_session::registry::CachedId;
10
11use crate::ArrayRef;
12use crate::ExecutionCtx;
13use crate::IntoArray;
14use crate::arrays::ConstantArray;
15use crate::dtype::DType;
16use crate::dtype::Nullability;
17use crate::expr::display::ExprDisplay;
18use crate::scalar_fn::Arity;
19use crate::scalar_fn::ChildName;
20use crate::scalar_fn::EmptyOptions;
21use crate::scalar_fn::ExecutionArgs;
22use crate::scalar_fn::ScalarFnId;
23use crate::scalar_fn::ScalarFnVTable;
24use crate::validity::Validity;
25
26#[derive(Clone)]
28pub struct IsNotNull;
29
30impl ScalarFnVTable for IsNotNull {
31 type Options = EmptyOptions;
32
33 fn id(&self) -> ScalarFnId {
34 static ID: CachedId = CachedId::new("vortex.is_not_null");
35 *ID
36 }
37
38 fn serialize(&self, _instance: &Self::Options) -> VortexResult<Option<Vec<u8>>> {
39 Ok(Some(vec![]))
40 }
41
42 fn deserialize(
43 &self,
44 _metadata: &[u8],
45 _session: &VortexSession,
46 ) -> VortexResult<Self::Options> {
47 Ok(EmptyOptions)
48 }
49
50 fn arity(&self, _options: &Self::Options) -> Arity {
51 Arity::Exact(1)
52 }
53
54 fn child_name(&self, _instance: &Self::Options, child_idx: usize) -> ChildName {
55 match child_idx {
56 0 => ChildName::from("input"),
57 _ => unreachable!("Invalid child index {} for IsNotNull expression", child_idx),
58 }
59 }
60
61 fn fmt_sql(
62 &self,
63 _options: &Self::Options,
64 expr: &dyn ExprDisplay,
65 f: &mut Formatter<'_>,
66 ) -> std::fmt::Result {
67 write!(f, "is_not_null(")?;
68 Display::fmt(expr.display_child(0), f)?;
69 write!(f, ")")
70 }
71
72 fn return_dtype(&self, _options: &Self::Options, _arg_dtypes: &[DType]) -> VortexResult<DType> {
73 Ok(DType::Bool(Nullability::NonNullable))
74 }
75
76 fn execute(
77 &self,
78 _data: &Self::Options,
79 args: &dyn ExecutionArgs,
80 _ctx: &mut ExecutionCtx,
81 ) -> VortexResult<ArrayRef> {
82 let child = args.get(0)?;
83 match child.validity()? {
84 Validity::NonNullable | Validity::AllValid => {
85 Ok(ConstantArray::new(true, args.row_count()).into_array())
86 }
87 Validity::AllInvalid => Ok(ConstantArray::new(false, args.row_count()).into_array()),
88 Validity::Array(a) => Ok(a),
89 }
90 }
91
92 fn is_strict(&self, _instance: &Self::Options) -> bool {
93 false
95 }
96
97 fn is_fallible(&self, _instance: &Self::Options) -> bool {
98 false
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use std::sync::LazyLock;
105
106 use vortex_buffer::buffer;
107 use vortex_error::VortexExpect as _;
108 use vortex_error::VortexResult;
109 use vortex_session::VortexSession;
110
111 use crate::IntoArray;
112 use crate::VortexSessionExecute;
113 use crate::array_session;
114 use crate::arrays::PrimitiveArray;
115 use crate::arrays::StructArray;
116 use crate::dtype::DType;
117 use crate::dtype::Nullability;
118 use crate::expr::col;
119 use crate::expr::eq;
120 use crate::expr::get_item;
121 use crate::expr::is_not_null;
122 use crate::expr::or;
123 use crate::expr::root;
124 use crate::expr::test_harness;
125 use crate::scalar::Scalar;
126 use crate::scalar_fn::EmptyOptions;
127 use crate::scalar_fn::ScalarFnVTableExt;
128 use crate::scalar_fn::internal::row_count::RowCount;
129 use crate::stats::StatsSession;
130 use crate::stats::all_null;
131 use crate::stats::null_count;
132
133 static STATS_SESSION: LazyLock<VortexSession> =
134 LazyLock::new(|| VortexSession::empty().with::<StatsSession>());
135
136 #[test]
137 fn dtype() {
138 let dtype = test_harness::struct_dtype();
139 assert_eq!(
140 is_not_null(root()).return_dtype(&dtype).unwrap(),
141 DType::Bool(Nullability::NonNullable)
142 );
143 }
144
145 #[test]
146 fn replace_children() {
147 let expr = is_not_null(root());
148 expr.with_children([root()])
149 .vortex_expect("operation should succeed in test");
150 }
151
152 #[test]
153 fn evaluate_mask() {
154 let test_array =
155 PrimitiveArray::from_option_iter(vec![Some(1), None, Some(2), None, Some(3)])
156 .into_array();
157 let expected = [true, false, true, false, true];
158
159 let result = test_array.clone().apply(&is_not_null(root())).unwrap();
160
161 assert_eq!(result.len(), test_array.len());
162 assert_eq!(result.dtype(), &DType::Bool(Nullability::NonNullable));
163
164 for (i, expected_value) in expected.iter().enumerate() {
165 assert_eq!(
166 result
167 .execute_scalar(i, &mut array_session().create_execution_ctx())
168 .unwrap(),
169 Scalar::bool(*expected_value, Nullability::NonNullable)
170 );
171 }
172 }
173
174 #[test]
175 fn evaluate_all_true() {
176 let test_array = buffer![1, 2, 3, 4, 5].into_array();
177
178 let result = test_array.clone().apply(&is_not_null(root())).unwrap();
179
180 assert_eq!(result.len(), test_array.len());
181 for i in 0..result.len() {
182 assert_eq!(
183 result
184 .execute_scalar(i, &mut array_session().create_execution_ctx())
185 .unwrap(),
186 Scalar::bool(true, Nullability::NonNullable)
187 );
188 }
189 }
190
191 #[test]
192 fn evaluate_all_false() {
193 let test_array =
194 PrimitiveArray::from_option_iter(vec![None::<i32>, None, None, None, None])
195 .into_array();
196
197 let result = test_array.clone().apply(&is_not_null(root())).unwrap();
198
199 assert_eq!(result.len(), test_array.len());
200 for i in 0..result.len() {
201 assert_eq!(
202 result
203 .execute_scalar(i, &mut array_session().create_execution_ctx())
204 .unwrap(),
205 Scalar::bool(false, Nullability::NonNullable)
206 );
207 }
208 }
209
210 #[test]
211 fn evaluate_struct() {
212 let test_array = StructArray::from_fields(&[(
213 "a",
214 PrimitiveArray::from_option_iter(vec![Some(1), None, Some(2), None, Some(3)])
215 .into_array(),
216 )])
217 .unwrap()
218 .into_array();
219 let expected = [true, false, true, false, true];
220
221 let result = test_array
222 .clone()
223 .apply(&is_not_null(get_item("a", root())))
224 .unwrap();
225
226 assert_eq!(result.len(), test_array.len());
227 assert_eq!(result.dtype(), &DType::Bool(Nullability::NonNullable));
228
229 for (i, expected_value) in expected.iter().enumerate() {
230 assert_eq!(
231 result
232 .execute_scalar(i, &mut array_session().create_execution_ctx())
233 .unwrap(),
234 Scalar::bool(*expected_value, Nullability::NonNullable)
235 );
236 }
237 }
238
239 #[test]
240 fn test_display() {
241 let expr = is_not_null(get_item("name", root()));
242 assert_eq!(expr.to_string(), "is_not_null($.name)");
243
244 let expr2 = is_not_null(root());
245 assert_eq!(expr2.to_string(), "is_not_null($)");
246 }
247
248 #[test]
249 fn test_is_not_null_is_not_strict() {
250 assert!(!is_not_null(col("a")).signature().is_strict());
251 }
252
253 #[test]
254 fn test_is_not_null_falsification() -> VortexResult<()> {
255 let expr = is_not_null(col("a"));
256 let dtype = test_harness::struct_dtype();
257
258 assert_eq!(
259 expr.bind(&dtype)?.falsify(&STATS_SESSION)?,
260 Some(
261 or(
262 eq(null_count(col("a")), RowCount.new_expr(EmptyOptions, []),),
263 all_null(col("a")),
264 )
265 .bind(&dtype)?
266 )
267 );
268 Ok(())
269 }
270}