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