1use arrow_array::{Array, ArrayRef, BooleanArray, UInt64Array, make_array};
21use arrow_buffer::buffer::bitwise_bin_op_helper;
22use arrow_buffer::{BooleanBuffer, NullBuffer, bitwise_unary_op_helper};
23use arrow_schema::{ArrowError, DataType};
24
25pub fn nullif(left: &dyn Array, right: &BooleanArray) -> Result<ArrayRef, ArrowError> {
45 if left.len() != right.len() {
46 return Err(ArrowError::ComputeError(
47 "Cannot perform comparison operation on arrays of different length".to_string(),
48 ));
49 }
50 let len = left.len();
51
52 if len == 0 || left.data_type() == &DataType::Null {
53 return Ok(make_array(left.to_data()));
54 }
55
56 let should_null = match right.nulls() {
59 Some(nulls) => right.values() & nulls.inner(),
60 None => right.values().clone(),
61 };
62
63 match left.data_type() {
66 DataType::RunEndEncoded(_, values) => {
67 if !values.is_nullable() && should_null.has_true() {
68 return Err(ArrowError::ComputeError(
69 "Cannot introduce nulls into a RunEndEncoded array with a non-nullable values field".into(),
70 ));
71 }
72 return nullif_take(left, &should_null);
73 }
74 DataType::Union(_, _) => return nullif_take(left, &should_null),
75 _ => {}
76 }
77
78 let left_data = left.to_data();
79
80 let (combined, null_count) = match left_data.nulls() {
92 Some(left) => {
93 let mut valid_count = 0;
94 let b = bitwise_bin_op_helper(
95 left.buffer(),
96 left.offset(),
97 should_null.inner(),
98 should_null.offset(),
99 len,
100 |l, r| {
101 let t = l & !r;
102 valid_count += t.count_ones() as usize;
103 t
104 },
105 );
106 (b, len - valid_count)
107 }
108 None => {
109 let mut null_count = 0;
110 let buffer =
111 bitwise_unary_op_helper(should_null.inner(), should_null.offset(), len, |b| {
112 let t = !b;
113 null_count += t.count_zeros() as usize;
114 t
115 });
116 (buffer, null_count)
117 }
118 };
119
120 let combined = BooleanBuffer::new(combined, 0, len);
121 let nulls = unsafe { NullBuffer::new_unchecked(combined, null_count) };
124 let data = left_data.into_builder().nulls(Some(nulls));
125
126 Ok(make_array(unsafe { data.build_unchecked() }))
129}
130
131fn nullif_take(left: &dyn Array, should_null: &BooleanBuffer) -> Result<ArrayRef, ArrowError> {
133 let indices = UInt64Array::from_iter((0..left.len()).map(|index| {
134 if should_null.value(index) {
135 None
136 } else {
137 Some(index as u64)
138 }
139 }));
140 crate::take::take(left, &indices, None)
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use arrow_array::builder::{BooleanBuilder, Int32Builder, StructBuilder};
147 use arrow_array::cast::AsArray;
148 use arrow_array::types::Int16Type;
149 use arrow_array::types::Int32Type;
150 use arrow_array::{
151 Int16Array, Int32Array, NullArray, RunArray, StringArray, StructArray, UnionArray,
152 };
153 use arrow_buffer::{RunEndBuffer, ScalarBuffer};
154 use arrow_data::ArrayData;
155 use arrow_schema::{Field, Fields, UnionFields, UnionMode};
156 use rand::prelude::StdRng;
157 use rand::{RngExt, SeedableRng};
158 use std::sync::Arc;
159
160 #[test]
161 fn test_nullif_int_array() {
162 let a = Int32Array::from(vec![Some(15), None, Some(8), Some(1), Some(9)]);
163 let comp = BooleanArray::from(vec![Some(false), None, Some(true), Some(false), None]);
164 let res = nullif(&a, &comp).unwrap();
165
166 let expected = Int32Array::from(vec![
167 Some(15),
168 None,
169 None, Some(1),
171 Some(9),
174 ]);
175
176 let res = res.as_primitive::<Int32Type>();
177 assert_eq!(&expected, res);
178 }
179
180 #[test]
181 fn test_nullif_null_array() {
182 assert_eq!(
183 nullif(&NullArray::new(0), &BooleanArray::new_null(0))
184 .unwrap()
185 .as_ref(),
186 &NullArray::new(0)
187 );
188
189 assert_eq!(
190 nullif(
191 &NullArray::new(3),
192 &BooleanArray::from(vec![Some(false), Some(true), None]),
193 )
194 .unwrap()
195 .as_ref(),
196 &NullArray::new(3)
197 );
198 }
199
200 #[test]
201 fn test_nullif_run_end_encoded() {
202 let values = Int32Array::from(vec![10, 20]);
203 let ree = RunArray::<Int16Type>::try_new(&Int16Array::from(vec![1, 2]), &values).unwrap();
204 let mask = BooleanArray::from(vec![Some(false), Some(true)]);
205
206 let result = nullif(&ree, &mask).unwrap();
207 let result = result.as_run::<Int16Type>();
208 assert_eq!(result.logical_null_count(), 1);
209 assert_eq!(
210 result
211 .values()
212 .as_primitive::<Int32Type>()
213 .iter()
214 .collect::<Vec<_>>(),
215 vec![Some(10), None]
216 );
217 }
218
219 #[test]
220 fn test_nullif_run_end_encoded_mask_nulls() {
221 let values = Int32Array::from(vec![10, 20, 30]);
222 let ree =
223 RunArray::<Int16Type>::try_new(&Int16Array::from(vec![1, 2, 3]), &values).unwrap();
224 let mask = BooleanArray::from(vec![Some(true), Some(false), None, Some(true)]);
225 let mask = mask.slice(1, 3); let result = nullif(&ree, &mask).unwrap();
228 let result = result.as_run::<Int16Type>();
229 assert_eq!(result.logical_null_count(), 1);
230 assert_eq!(
231 result
232 .values()
233 .as_primitive::<Int32Type>()
234 .iter()
235 .collect::<Vec<_>>(),
236 vec![Some(10), Some(20), None]
237 );
238 }
239
240 #[test]
241 fn test_nullif_run_end_encoded_non_nullable_values() {
242 let ree = unsafe {
243 RunArray::<Int16Type>::new_unchecked(
244 DataType::RunEndEncoded(
245 Arc::new(Field::new("run_ends", DataType::Int16, false)),
246 Arc::new(Field::new("values", DataType::Int32, false)),
247 ),
248 RunEndBuffer::new(vec![1_i16, 2].into(), 0, 2),
249 Arc::new(Int32Array::from(vec![10, 20])),
250 )
251 };
252 let mask = BooleanArray::from(vec![Some(false), Some(true)]);
253
254 let error = nullif(&ree, &mask).unwrap_err();
255 assert_eq!(
256 error.to_string(),
257 "Compute error: Cannot introduce nulls into a RunEndEncoded array with a non-nullable values field"
258 );
259 }
260
261 #[test]
262 fn test_nullif_union() {
263 let fields =
264 UnionFields::try_new(vec![0], vec![Field::new("i", DataType::Int32, true)]).unwrap();
265 let union = UnionArray::try_new(
266 fields,
267 ScalarBuffer::from(vec![0_i8, 0]),
268 Some(ScalarBuffer::from(vec![0_i32, 1])),
269 vec![Arc::new(Int32Array::from(vec![10, 20]))],
270 )
271 .unwrap();
272 let mask = BooleanArray::from(vec![Some(false), Some(true)]);
273
274 let result = nullif(&union, &mask).unwrap();
275 let result = result.as_union();
276 assert_eq!(result.logical_null_count(), 1);
277 assert_eq!(result.type_ids(), &ScalarBuffer::from(vec![0_i8, 0]));
278 assert_eq!(
279 result
280 .child(0)
281 .as_primitive::<Int32Type>()
282 .iter()
283 .collect::<Vec<_>>(),
284 vec![Some(10), None]
285 );
286 assert!(matches!(
287 result.data_type(),
288 DataType::Union(_, UnionMode::Dense)
289 ));
290 }
291
292 #[test]
293 fn test_nullif_union_without_nullable_child() {
294 let fields =
295 UnionFields::try_new(vec![0], vec![Field::new("i", DataType::Int32, false)]).unwrap();
296 let union = UnionArray::try_new(
297 fields,
298 ScalarBuffer::from(vec![0_i8, 0]),
299 None,
300 vec![Arc::new(Int32Array::from(vec![10, 20]))],
301 )
302 .unwrap();
303 let mask = BooleanArray::from(vec![Some(false), Some(true)]);
304
305 let error = nullif(&union, &mask).unwrap_err();
306 assert_eq!(
307 error.to_string(),
308 "Compute error: Cannot take null indices from a union with no field that can represent nulls"
309 );
310 }
311
312 #[test]
313 fn test_nullif_int_array_offset() {
314 let a = Int32Array::from(vec![None, Some(15), Some(8), Some(1), Some(9)]);
315 let a = a.slice(1, 3); let comp = BooleanArray::from(vec![
317 Some(false),
318 Some(false),
319 Some(false),
320 None,
321 Some(true),
322 Some(false),
323 None,
324 ]);
325 let comp = comp.slice(2, 3); let res = nullif(&a, &comp).unwrap();
327
328 let expected = Int32Array::from(vec![
329 Some(15), Some(8), None, ]);
333 let res = res.as_primitive::<Int32Type>();
334 assert_eq!(&expected, res)
335 }
336
337 #[test]
338 fn test_nullif_string() {
339 let s = StringArray::from_iter([
340 Some("hello"),
341 None,
342 Some("world"),
343 Some("a"),
344 Some("b"),
345 None,
346 None,
347 ]);
348 let select = BooleanArray::from_iter([
349 Some(true),
350 Some(true),
351 Some(false),
352 Some(true),
353 Some(false),
354 Some(false),
355 None,
356 ]);
357
358 let a = nullif(&s, &select).unwrap();
359 let r: Vec<_> = a.as_string::<i32>().iter().collect();
360 assert_eq!(
361 r,
362 vec![None, None, Some("world"), None, Some("b"), None, None]
363 );
364
365 let s = s.slice(2, 3);
366 let select = select.slice(1, 3);
367 let a = nullif(&s, &select).unwrap();
368 let r: Vec<_> = a.as_string::<i32>().iter().collect();
369 assert_eq!(r, vec![None, Some("a"), None]);
370 }
371
372 #[test]
373 fn test_nullif_int_large_left_offset() {
374 let a = Int32Array::from(vec![
375 Some(-1), Some(-1),
377 Some(-1),
378 Some(-1),
379 Some(-1),
380 Some(-1),
381 Some(-1),
382 Some(-1),
383 Some(-1), Some(-1),
385 Some(-1),
386 Some(-1),
387 Some(-1),
388 Some(-1),
389 Some(-1),
390 Some(-1),
391 None, Some(15), Some(8),
394 Some(1),
395 Some(9),
396 ]);
397 let a = a.slice(17, 3); let comp = BooleanArray::from(vec![
400 Some(false),
401 Some(false),
402 Some(false),
403 None,
404 Some(true),
405 Some(false),
406 None,
407 ]);
408 let comp = comp.slice(2, 3); let res = nullif(&a, &comp).unwrap();
410 let res = res.as_primitive::<Int32Type>();
411
412 let expected = Int32Array::from(vec![
413 Some(15), Some(8), None, ]);
417 assert_eq!(&expected, res)
418 }
419
420 #[test]
421 fn test_nullif_int_large_right_offset() {
422 let a = Int32Array::from(vec![
423 None, Some(15), Some(8),
426 Some(1),
427 Some(9),
428 ]);
429 let a = a.slice(1, 3); let comp = BooleanArray::from(vec![
432 Some(false), Some(false),
434 Some(false),
435 Some(false),
436 Some(false),
437 Some(false),
438 Some(false),
439 Some(false),
440 Some(false), Some(false),
442 Some(false),
443 Some(false),
444 Some(false),
445 Some(false),
446 Some(false),
447 Some(false),
448 Some(false), Some(false), Some(false), None,
452 Some(true),
453 Some(false),
454 None,
455 ]);
456 let comp = comp.slice(18, 3); let res = nullif(&a, &comp).unwrap();
458 let res = res.as_primitive::<Int32Type>();
459
460 let expected = Int32Array::from(vec![
461 Some(15), Some(8), None, ]);
465 assert_eq!(&expected, res)
466 }
467
468 #[test]
469 fn test_nullif_boolean_offset() {
470 let a = BooleanArray::from(vec![
471 None, Some(true), Some(false),
474 Some(true),
475 Some(true),
476 ]);
477 let a = a.slice(1, 3); let comp = BooleanArray::from(vec![
480 Some(false), Some(false), Some(false), None,
484 Some(true),
485 Some(false),
486 None,
487 ]);
488 let comp = comp.slice(2, 3); let res = nullif(&a, &comp).unwrap();
490 let res = res.as_boolean();
491
492 let expected = BooleanArray::from(vec![
493 Some(true), Some(false), None, ]);
497 assert_eq!(&expected, res)
498 }
499
500 struct Foo {
501 a: Option<i32>,
502 b: Option<bool>,
503 is_valid: bool,
505 }
506
507 impl Foo {
508 fn new_valid(a: i32, b: bool) -> Foo {
509 Self {
510 a: Some(a),
511 b: Some(b),
512 is_valid: true,
513 }
514 }
515
516 fn new_null() -> Foo {
517 Self {
518 a: None,
519 b: None,
520 is_valid: false,
521 }
522 }
523 }
524
525 fn create_foo_struct(values: Vec<Foo>) -> StructArray {
529 let mut struct_array = StructBuilder::new(
530 Fields::from(vec![
531 Field::new("a", DataType::Int32, true),
532 Field::new("b", DataType::Boolean, true),
533 ]),
534 vec![
535 Box::new(Int32Builder::with_capacity(values.len())),
536 Box::new(BooleanBuilder::with_capacity(values.len())),
537 ],
538 );
539
540 for value in values {
541 struct_array
542 .field_builder::<Int32Builder>(0)
543 .unwrap()
544 .append_option(value.a);
545 struct_array
546 .field_builder::<BooleanBuilder>(1)
547 .unwrap()
548 .append_option(value.b);
549 struct_array.append(value.is_valid);
550 }
551
552 struct_array.finish()
553 }
554
555 #[test]
556 fn test_nullif_struct_slices() {
557 let struct_array = create_foo_struct(vec![
558 Foo::new_valid(7, true),
559 Foo::new_valid(15, false),
560 Foo::new_valid(8, true),
561 Foo::new_valid(12, false),
562 Foo::new_null(),
563 Foo::new_null(),
564 Foo::new_valid(42, true),
565 ]);
566
567 let struct_array = struct_array.slice(1, 5);
570 let comp = BooleanArray::from(vec![
571 Some(false), Some(false), Some(false), None,
575 Some(true),
576 Some(false),
577 None,
578 ]);
579 let comp = comp.slice(2, 5); let res = nullif(&struct_array, &comp).unwrap();
581 let res = res.as_struct();
582
583 let expected = create_foo_struct(vec![
584 Foo::new_valid(15, false),
586 Foo::new_valid(8, true),
588 Foo {
590 a: Some(12),
591 b: Some(false),
592 is_valid: false,
593 },
594 Foo::new_null(),
596 Foo::new_null(),
598 ]);
599
600 assert_eq!(&expected, res);
601 }
602
603 #[test]
604 fn test_nullif_no_nulls() {
605 let a = Int32Array::from(vec![Some(15), Some(7), Some(8), Some(1), Some(9)]);
606 let comp = BooleanArray::from(vec![Some(false), None, Some(true), Some(false), None]);
607 let res = nullif(&a, &comp).unwrap();
608 let res = res.as_primitive::<Int32Type>();
609
610 let expected = Int32Array::from(vec![Some(15), Some(7), None, Some(1), Some(9)]);
611 assert_eq!(res, &expected);
612 }
613
614 #[test]
615 fn nullif_empty() {
616 let a = Int32Array::from(ArrayData::new_empty(&DataType::Int32));
617 let mask = BooleanArray::from(ArrayData::new_empty(&DataType::Boolean));
618 let res = nullif(&a, &mask).unwrap();
619 assert_eq!(res.as_ref(), &a);
620 }
621
622 fn test_nullif(values: &Int32Array, filter: &BooleanArray) {
623 let expected: Int32Array = values
624 .iter()
625 .zip(filter.iter())
626 .map(|(a, b)| match b {
627 Some(true) => None,
628 Some(false) | None => a,
629 })
630 .collect();
631
632 let r = nullif(values, filter).unwrap();
633 let r_data = r.to_data();
634 r_data.validate().unwrap();
635
636 assert_eq!(
637 r.as_ref(),
638 &expected,
639 "expected nulls: {:#?}\n\n\
640 result nulls: {:#?}\n\n\\
641 expected values: {:#?}\n\n\
642 result values: {:#?}",
643 expected.nulls(),
644 r.nulls(),
645 expected.values(),
646 r.as_primitive::<Int32Type>().values()
647 );
648 validate_nulls(expected.nulls());
649 validate_nulls(r.nulls());
650 }
651
652 fn validate_nulls(nulls: Option<&NullBuffer>) {
654 let Some(nulls) = nulls else {
655 return;
656 };
657 let mut actual_null_count = 0;
658 for i in 0..nulls.len() {
659 if nulls.is_null(i) {
660 actual_null_count += 1;
661 }
662 }
663 assert_eq!(actual_null_count, nulls.null_count());
664 }
665
666 #[test]
667 #[cfg_attr(miri, ignore)] fn nullif_fuzz() {
669 let mut rng = StdRng::seed_from_u64(7337);
670
671 let arrays = [
672 Int32Array::from(vec![0; 1024]), (0..1024) .map(|_| rng.random_bool(0.5).then_some(1))
675 .collect(),
676 ];
677
678 for a in arrays {
679 let a_slices = [
680 (0, 128),
681 (0, 129),
682 (64, 64),
683 (0, 64),
684 (32, 32),
685 (0, 0),
686 (32, 0),
687 (5, 800),
688 (33, 53),
689 (77, 101),
690 ];
691 for (a_offset, a_length) in a_slices {
692 let a = a.slice(a_offset, a_length);
693
694 for i in 1..65 {
695 let b_start_offset = rng.random_range(0..i);
696 let b_end_offset = rng.random_range(0..i);
697
698 let b: BooleanArray = (0..a_length + b_start_offset + b_end_offset)
700 .map(|_| rng.random_bool(0.5).then(|| rng.random_bool(0.5)))
701 .collect();
702 let b_sliced = b.slice(b_start_offset, a_length);
703 test_nullif(&a, &b_sliced);
704
705 let b = remove_null_buffer(&b);
707 let b_sliced = b.slice(b_start_offset, a_length);
708 test_nullif(&a, &b_sliced);
709
710 let b = remove_null_values(&b);
712 let b_sliced = b.slice(b_start_offset, a_length);
713 test_nullif(&a, &b_sliced);
714 }
715 }
716 }
717 }
718
719 fn remove_null_buffer(array: &BooleanArray) -> BooleanArray {
721 BooleanArray::new(array.values().clone(), None)
722 }
723
724 fn remove_null_values(array: &BooleanArray) -> BooleanArray {
726 let len = array.len();
727 let new_nulls = NullBuffer::from_iter(std::iter::repeat_n(true, len));
728 BooleanArray::new(array.values().clone(), Some(new_nulls))
729 }
730}