• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16937708903

13 Aug 2025 12:48PM UTC coverage: 87.684% (+0.03%) from 87.658%
16937708903

push

github

web-flow
perf: Use sum compute function (#4207)

This patch finishes two todo items to replace with `SUM` compute
function in `Validity`'s `all_valid` and `all_invalid` functions.

Signed-off-by: Liang-Chi Hsieh <viirya@gmail.com>

5 of 5 new or added lines in 1 file covered. (100.0%)

11 existing lines in 1 file now uncovered.

56464 of 64395 relevant lines covered (87.68%)

627631.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.1
/vortex-array/src/validity.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
//! Array validity and nullability behavior, used by arrays and compute functions.
5

6
use std::fmt::Debug;
7
use std::ops::{BitAnd, Not};
8

9
use arrow_buffer::{BooleanBuffer, NullBuffer};
10
use vortex_dtype::{DType, Nullability};
11
use vortex_error::{VortexExpect as _, VortexResult, vortex_bail, vortex_err, vortex_panic};
12
use vortex_mask::{AllOr, Mask, MaskValues};
13
use vortex_scalar::Scalar;
14

15
use crate::arrays::{BoolArray, ConstantArray};
16
use crate::compute::{fill_null, filter, sum, take};
17
use crate::patches::Patches;
18
use crate::{Array, ArrayRef, IntoArray, ToCanonical};
19

20
/// Validity information for an array
21
#[derive(Clone, Debug)]
22
pub enum Validity {
23
    /// Items *can't* be null
24
    NonNullable,
25
    /// All items are valid
26
    AllValid,
27
    /// All items are null
28
    AllInvalid,
29
    /// Specified items are null
30
    Array(ArrayRef),
31
}
32

33
impl Validity {
34
    /// The [`DType`] of the underlying validity array (if it exists).
35
    pub const DTYPE: DType = DType::Bool(Nullability::NonNullable);
36

37
    pub fn null_count(&self, length: usize) -> VortexResult<usize> {
×
38
        match self {
×
39
            Self::NonNullable | Self::AllValid => Ok(0),
×
40
            Self::AllInvalid => Ok(length),
×
41
            Self::Array(a) => {
×
42
                let validity_len = a.len();
×
43
                if validity_len != length {
×
44
                    vortex_bail!(
×
45
                        "Validity array length {} doesn't match array length {}",
×
46
                        validity_len,
47
                        length
48
                    )
49
                }
×
50
                let true_count = sum(a)?
×
51
                    .as_primitive()
×
52
                    .as_::<usize>()?
×
53
                    .ok_or_else(|| vortex_err!("Failed to compute true count"))?;
×
54
                Ok(length - true_count)
×
55
            }
56
        }
57
    }
×
58

59
    /// If Validity is [`Validity::Array`], returns the array, otherwise returns `None`.
60
    pub fn into_array(self) -> Option<ArrayRef> {
×
61
        match self {
×
62
            Self::Array(a) => Some(a),
×
63
            _ => None,
×
64
        }
65
    }
×
66

67
    /// If Validity is [`Validity::Array`], returns a reference to the array array, otherwise returns `None`.
68
    pub fn as_array(&self) -> Option<&ArrayRef> {
×
69
        match self {
×
70
            Self::Array(a) => Some(a),
×
71
            _ => None,
×
72
        }
73
    }
×
74

75
    pub fn nullability(&self) -> Nullability {
5,090,583✔
76
        match self {
5,090,583✔
77
            Self::NonNullable => Nullability::NonNullable,
4,104,172✔
78
            _ => Nullability::Nullable,
986,411✔
79
        }
80
    }
5,090,583✔
81

82
    /// The union nullability and validity.
83
    pub fn union_nullability(self, nullability: Nullability) -> Self {
907✔
84
        match nullability {
907✔
85
            Nullability::NonNullable => self,
406✔
86
            Nullability::Nullable => self.into_nullable(),
501✔
87
        }
88
    }
907✔
89

90
    pub fn all_valid(&self) -> VortexResult<bool> {
19,408,721✔
91
        Ok(match self {
19,408,721✔
92
            Validity::NonNullable | Validity::AllValid => true,
19,395,771✔
93
            Validity::AllInvalid => false,
46✔
94
            Validity::Array(array) => {
12,904✔
95
                // TODO(ngates): replace with SUM compute function
96
                array.to_bool()?.boolean_buffer().count_set_bits() == array.len()
12,904✔
97
            }
98
        })
99
    }
19,408,673✔
100

101
    pub fn all_invalid(&self) -> VortexResult<bool> {
494,023✔
102
        Ok(match self {
494,071✔
103
            Validity::NonNullable | Validity::AllValid => false,
461,901✔
104
            Validity::AllInvalid => true,
923✔
105
            Validity::Array(array) => {
31,515✔
106
                // TODO(ngates): replace with SUM compute function
158✔
107
                array.to_bool()?.boolean_buffer().count_set_bits() == 0
31,357✔
108
            }
109
        })
110
    }
494,023✔
111

112
    /// Returns whether the `index` item is valid.
113
    #[inline]
114
    pub fn is_valid(&self, index: usize) -> VortexResult<bool> {
73,907,453✔
115
        Ok(match self {
73,907,453✔
116
            Self::NonNullable | Self::AllValid => true,
67,523,533✔
117
            Self::AllInvalid => false,
173,882✔
118
            Self::Array(a) => {
6,210,196✔
119
                let scalar = a.scalar_at(index)?;
6,210,196✔
120
                scalar
6,210,206✔
121
                    .as_bool()
6,210,206✔
122
                    .value()
6,210,206✔
123
                    .vortex_expect("Validity must be non-nullable")
6,210,196✔
124
            }
125
        })
126
    }
73,907,453✔
127

128
    #[inline]
129
    pub fn is_null(&self, index: usize) -> VortexResult<bool> {
147,615✔
130
        Ok(!self.is_valid(index)?)
147,615✔
131
    }
147,615✔
132

10✔
133
    pub fn slice(&self, start: usize, stop: usize) -> VortexResult<Self> {
3,942,958✔
134
        match self {
3,942,958✔
135
            Self::Array(a) => Ok(Self::Array(a.slice(start, stop)?)),
9,885✔
136
            _ => Ok(self.clone()),
3,933,073✔
137
        }
138
    }
3,942,958✔
139

160✔
140
    pub fn take(&self, indices: &dyn Array) -> VortexResult<Self> {
76,085✔
141
        match self {
75,925✔
142
            Self::NonNullable => match indices.validity_mask()?.boolean_buffer() {
59,747✔
143
                AllOr::All => {
144
                    if indices.dtype().is_nullable() {
52,805✔
145
                        Ok(Self::AllValid)
277✔
146
                    } else {
147
                        Ok(Self::NonNullable)
52,368✔
148
                    }
149
                }
150
                AllOr::None => Ok(Self::AllInvalid),
1✔
151
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
6,941✔
152
            },
153
            Self::AllValid => match indices.validity_mask()?.boolean_buffer() {
6,698✔
154
                AllOr::All => Ok(Self::AllValid),
3,262✔
155
                AllOr::None => Ok(Self::AllInvalid),
1✔
156
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
3,435✔
157
            },
158
            Self::AllInvalid => Ok(Self::AllInvalid),
123✔
159
            Self::Array(is_valid) => {
9,517✔
160
                let maybe_is_valid = take(is_valid, indices)?;
9,517✔
161
                // Null indices invalidate that position.
162
                let is_valid = fill_null(&maybe_is_valid, &Scalar::from(false))?;
9,517✔
163
                Ok(Self::Array(is_valid))
9,517✔
164
            }
165
        }
166
    }
75,925✔
167

168
    /// Keep only the entries for which the mask is true.
169
    ///
170
    /// The result has length equal to the number of true values in mask.
171
    pub fn filter(&self, mask: &Mask) -> VortexResult<Self> {
58,746✔
172
        // NOTE(ngates): we take the mask as a reference to avoid the caller cloning unnecessarily
173
        //  if we happen to be NonNullable, AllValid, or AllInvalid.
174
        match self {
58,746✔
175
            v @ (Validity::NonNullable | Validity::AllValid | Validity::AllInvalid) => {
46,899✔
176
                Ok(v.clone())
46,899✔
177
            }
178
            Validity::Array(arr) => Ok(Validity::Array(filter(arr, mask)?)),
11,847✔
179
        }
180
    }
58,746✔
181

182
    /// Set to false any entries for which the mask is true.
183
    ///
184
    /// The result is always nullable. The result has the same length as self.
185
    pub fn mask(&self, mask: &Mask) -> VortexResult<Self> {
11,585✔
186
        match mask.boolean_buffer() {
11,585✔
187
            AllOr::All => Ok(Validity::AllInvalid),
188
            AllOr::None => Ok(self.clone()),
189
            AllOr::Some(make_invalid) => Ok(match self {
11,585✔
190
                Validity::NonNullable | Validity::AllValid => {
191
                    Validity::Array(BoolArray::from(make_invalid.not()).into_array())
4,748✔
192
                }
193
                Validity::AllInvalid => Validity::AllInvalid,
55✔
194
                Validity::Array(is_valid) => {
6,782✔
195
                    let is_valid = is_valid.to_bool()?;
6,782✔
196
                    let keep_valid = make_invalid.not();
6,782✔
197
                    Validity::from(is_valid.boolean_buffer().bitand(&keep_valid))
6,782✔
198
                }
199
            }),
200
        }
201
    }
11,585✔
202

203
    pub fn to_mask(&self, length: usize) -> VortexResult<Mask> {
1,443,984✔
204
        Ok(match self {
1,443,984✔
205
            Self::NonNullable | Self::AllValid => Mask::AllTrue(length),
1,372,795✔
206
            Self::AllInvalid => Mask::AllFalse(length),
499✔
207
            Self::Array(is_valid) => {
70,690✔
208
                assert_eq!(
70,690✔
209
                    is_valid.len(),
71,204✔
210
                    length,
514✔
211
                    "Validity::Array length must equal to_logical's argument: {}, {}.",
514✔
UNCOV
212
                    is_valid.len(),
×
213
                    length,
214
                );
215
                Mask::try_from(&is_valid.to_bool()?)?
70,690✔
216
            }
217
        })
218
    }
1,443,984✔
219

220
    /// Logically & two Validity values of the same length
221
    pub fn and(self, rhs: Validity) -> VortexResult<Validity> {
55✔
222
        let validity = match (self, rhs) {
55✔
223
            // Should be pretty clear
224
            (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable,
569✔
225
            // Any `AllInvalid` makes the output all invalid values
226
            (Validity::AllInvalid, _) | (_, Validity::AllInvalid) => Validity::AllInvalid,
227
            // All truthy values on one side, which makes no effect on an `Array` variant
UNCOV
228
            (Validity::Array(a), Validity::AllValid)
×
229
            | (Validity::Array(a), Validity::NonNullable)
UNCOV
230
            | (Validity::NonNullable, Validity::Array(a))
×
231
            | (Validity::AllValid, Validity::Array(a)) => Validity::Array(a),
232
            // Both sides are all valid
233
            (Validity::NonNullable, Validity::AllValid)
234
            | (Validity::AllValid, Validity::NonNullable)
235
            | (Validity::AllValid, Validity::AllValid) => Validity::AllValid,
×
236
            // Here we actually have to do some work
237
            (Validity::Array(lhs), Validity::Array(rhs)) => {
×
238
                let lhs = lhs.to_bool()?;
239
                let rhs = rhs.to_bool()?;
240

241
                let lhs = lhs.boolean_buffer();
×
242
                let rhs = rhs.boolean_buffer();
243

244
                Validity::from(lhs.bitand(rhs))
×
245
            }
246
        };
247

248
        Ok(validity)
55✔
249
    }
55✔
250

251
    pub fn patch(
12,051✔
252
        self,
12,051✔
253
        len: usize,
12,051✔
254
        indices_offset: usize,
12,051✔
255
        indices: &dyn Array,
12,051✔
256
        patches: &Validity,
12,051✔
257
    ) -> VortexResult<Self> {
12,051✔
258
        match (&self, patches) {
12,051✔
259
            (Validity::NonNullable, Validity::NonNullable) => return Ok(Validity::NonNullable),
9,264✔
260
            (Validity::NonNullable, _) => {
261
                vortex_bail!("Can't patch a non-nullable validity with nullable validity")
1✔
262
            }
263
            (_, Validity::NonNullable) => {
UNCOV
264
                vortex_bail!("Can't patch a nullable validity with non-nullable validity")
×
265
            }
266
            (Validity::AllValid, Validity::AllValid) => return Ok(Validity::AllValid),
1✔
267
            (Validity::AllInvalid, Validity::AllInvalid) => return Ok(Validity::AllInvalid),
1✔
268
            _ => {}
2,784✔
269
        };
270

271
        let own_nullability = if self == Validity::NonNullable {
2,784✔
UNCOV
272
            Nullability::NonNullable
×
273
        } else {
274
            Nullability::Nullable
2,784✔
275
        };
276

277
        let source = match self {
2,784✔
278
            Validity::NonNullable => BoolArray::from(BooleanBuffer::new_set(len)),
×
279
            Validity::AllValid => BoolArray::from(BooleanBuffer::new_set(len)),
392✔
280
            Validity::AllInvalid => BoolArray::from(BooleanBuffer::new_unset(len)),
2,311✔
281
            Validity::Array(a) => a.to_bool()?,
81✔
282
        };
283

284
        let patch_values = match patches {
2,784✔
UNCOV
285
            Validity::NonNullable => BoolArray::from(BooleanBuffer::new_set(indices.len())),
×
286
            Validity::AllValid => BoolArray::from(BooleanBuffer::new_set(indices.len())),
1,804✔
287
            Validity::AllInvalid => BoolArray::from(BooleanBuffer::new_unset(indices.len())),
2✔
288
            Validity::Array(a) => a.to_bool()?,
978✔
289
        };
290

291
        let patches = Patches::new(
2,784✔
292
            len,
2,784✔
293
            indices_offset,
2,784✔
294
            indices.to_array(),
2,784✔
295
            patch_values.into_array(),
2,784✔
296
        );
297

298
        Ok(Self::from_array(
2,784✔
299
            source.patch(&patches)?.into_array(),
2,784✔
300
            own_nullability,
2,784✔
301
        ))
302
    }
12,051✔
303

304
    /// Convert into a nullable variant
305
    pub fn into_nullable(self) -> Validity {
19,228✔
306
        match self {
19,228✔
307
            Self::NonNullable => Self::AllValid,
18,991✔
308
            _ => self,
237✔
309
        }
310
    }
19,228✔
311

312
    /// Convert into a non-nullable variant
313
    pub fn into_non_nullable(self) -> Option<Validity> {
2,555✔
314
        match self {
2,555✔
315
            Self::NonNullable => Some(Self::NonNullable),
508✔
316
            Self::AllValid => Some(Self::NonNullable),
1,282✔
317
            Self::AllInvalid => None,
318
            Self::Array(is_valid) => {
765✔
319
                is_valid
765✔
320
                    .statistics()
765✔
321
                    .compute_min::<bool>()
765✔
322
                    .vortex_expect("validity array must support min")
765✔
323
                    .then(|| {
765✔
324
                        // min true => all true
325
                        Self::NonNullable
238✔
326
                    })
238✔
327
            }
328
        }
329
    }
2,555✔
330

331
    /// Convert into a variant compatible with the given nullability, if possible.
332
    pub fn cast_nullability(self, nullability: Nullability) -> VortexResult<Validity> {
9,010✔
333
        match nullability {
9,010✔
334
            Nullability::NonNullable => self.into_non_nullable().ok_or_else(|| {
2,555✔
335
                vortex_err!("Cannot cast array with invalid values to non-nullable type.")
527✔
336
            }),
527✔
337
            Nullability::Nullable => Ok(self.into_nullable()),
6,455✔
338
        }
339
    }
9,010✔
340

341
    /// Create Validity by copying the given array's validity.
342
    pub fn copy_from_array(array: &dyn Array) -> VortexResult<Self> {
23,477✔
343
        Ok(Validity::from_mask(
23,477✔
344
            array.validity_mask()?,
23,477✔
345
            array.dtype().nullability(),
23,477✔
346
        ))
347
    }
23,477✔
348

349
    /// Create Validity from boolean array with given nullability of the array.
350
    ///
351
    /// Note: You want to pass the nullability of parent array and not the nullability of the validity array itself
352
    ///     as that is always nonnullable
353
    fn from_array(value: ArrayRef, nullability: Nullability) -> Self {
2,784✔
354
        if !matches!(value.dtype(), DType::Bool(Nullability::NonNullable)) {
2,784✔
355
            vortex_panic!("Expected a non-nullable boolean array")
356
        }
2,784✔
357
        match nullability {
2,784✔
358
            Nullability::NonNullable => Self::NonNullable,
359
            Nullability::Nullable => Self::Array(value),
2,784✔
360
        }
361
    }
2,784✔
362

363
    /// Returns the length of the validity array, if it exists.
364
    pub fn maybe_len(&self) -> Option<usize> {
6,203,679✔
365
        match self {
6,203,679✔
366
            Self::NonNullable | Self::AllValid | Self::AllInvalid => None,
6,075,421✔
367
            Self::Array(a) => Some(a.len()),
128,258✔
368
        }
369
    }
6,203,679✔
370

730✔
371
    pub fn uncompressed_size(&self) -> usize {
730✔
372
        if let Validity::Array(a) = self {
730✔
UNCOV
373
            a.len().div_ceil(8)
×
374
        } else {
375
            0
730✔
376
        }
377
    }
×
378

379
    pub fn is_array(&self) -> bool {
×
380
        matches!(self, Validity::Array(_))
381
    }
×
382
}
383

384
impl PartialEq for Validity {
385
    fn eq(&self, other: &Self) -> bool {
63,757✔
386
        match (self, other) {
63,757✔
387
            (Self::NonNullable, Self::NonNullable) => true,
36,442✔
388
            (Self::AllValid, Self::AllValid) => true,
126✔
389
            (Self::AllInvalid, Self::AllInvalid) => true,
123✔
390
            (Self::Array(a), Self::Array(b)) => {
526✔
391
                let a = a
526✔
392
                    .to_bool()
526✔
393
                    .vortex_expect("Failed to get Validity Array as BoolArray");
526✔
394
                let b = b
526✔
395
                    .to_bool()
526✔
396
                    .vortex_expect("Failed to get Validity Array as BoolArray");
526✔
397
                a.boolean_buffer() == b.boolean_buffer()
526✔
398
            }
399
            _ => false,
26,540✔
400
        }
401
    }
63,757✔
402
}
403

404
impl From<BooleanBuffer> for Validity {
405
    fn from(value: BooleanBuffer) -> Self {
68,346✔
406
        if value.count_set_bits() == value.len() {
68,346✔
407
            Self::AllValid
374✔
408
        } else if value.count_set_bits() == 0 {
67,972✔
409
            Self::AllInvalid
2,235✔
410
        } else {
411
            Self::Array(BoolArray::from(value).into_array())
65,737✔
412
        }
413
    }
68,346✔
414
}
415

416
impl From<NullBuffer> for Validity {
417
    fn from(value: NullBuffer) -> Self {
14,942✔
418
        value.into_inner().into()
14,942✔
419
    }
14,942✔
420
}
421

422
impl FromIterator<Mask> for Validity {
UNCOV
423
    fn from_iter<T: IntoIterator<Item = Mask>>(iter: T) -> Self {
×
UNCOV
424
        Validity::from_mask(iter.into_iter().collect(), Nullability::Nullable)
×
UNCOV
425
    }
×
426
}
427

428
impl FromIterator<bool> for Validity {
429
    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
3,391✔
430
        Validity::from(BooleanBuffer::from_iter(iter))
3,391✔
431
    }
3,391✔
432
}
433

434
impl From<Nullability> for Validity {
435
    fn from(value: Nullability) -> Self {
48,705✔
436
        match value {
48,705✔
437
            Nullability::NonNullable => Validity::NonNullable,
43,280✔
438
            Nullability::Nullable => Validity::AllValid,
5,425✔
439
        }
440
    }
48,705✔
441
}
6✔
442

6✔
443
impl Validity {
6✔
444
    pub fn from_mask(mask: Mask, nullability: Nullability) -> Self {
24,243✔
445
        assert!(
24,243✔
446
            nullability == Nullability::Nullable || matches!(mask, Mask::AllTrue(_)),
24,249✔
447
            "NonNullable validity must be AllValid",
2✔
448
        );
449
        match mask {
24,241✔
450
            Mask::AllTrue(_) => match nullability {
20,018✔
451
                Nullability::NonNullable => Validity::NonNullable,
19,740✔
452
                Nullability::Nullable => Validity::AllValid,
278✔
453
            },
454
            Mask::AllFalse(_) => Validity::AllInvalid,
78✔
455
            Mask::Values(values) => Validity::Array(values.into_array()),
4,145✔
456
        }
457
    }
24,241✔
458
}
459

460
impl IntoArray for Mask {
461
    fn into_array(self) -> ArrayRef {
40✔
462
        match self {
40✔
UNCOV
463
            Self::AllTrue(len) => ConstantArray::new(true, len).into_array(),
×
464
            Self::AllFalse(len) => ConstantArray::new(false, len).into_array(),
465
            Self::Values(a) => a.into_array(),
40✔
466
        }
467
    }
40✔
468
}
469

470
impl IntoArray for &MaskValues {
471
    fn into_array(self) -> ArrayRef {
4,224✔
472
        BoolArray::new(self.boolean_buffer().clone(), Validity::NonNullable).into_array()
4,224✔
473
    }
4,224✔
474
}
475

476
#[cfg(test)]
477
mod tests {
478
    use rstest::rstest;
479
    use vortex_buffer::{Buffer, buffer};
480
    use vortex_dtype::Nullability;
481
    use vortex_mask::Mask;
482

483
    use crate::arrays::{BoolArray, PrimitiveArray};
484
    use crate::validity::Validity;
485
    use crate::{ArrayRef, IntoArray};
486

487
    #[rstest]
488
    #[case(Validity::AllValid, 5, &[2, 4], Validity::AllValid, Validity::AllValid)]
489
    #[case(Validity::AllValid, 5, &[2, 4], Validity::AllInvalid, Validity::Array(BoolArray::from_iter([true, true, false, true, false]).into_array())
490
    )]
491
    #[case(Validity::AllValid, 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([true, true, true, true, false]).into_array())
492
    )]
493
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::AllValid, Validity::Array(BoolArray::from_iter([false, false, true, false, true]).into_array())
494
    )]
495
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::AllInvalid, Validity::AllInvalid)]
496
    #[case(Validity::AllInvalid, 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([false, false, true, false, false]).into_array())
497
    )]
498
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::AllValid, Validity::Array(BoolArray::from_iter([false, true, true, true, true]).into_array())
499
    )]
500
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::AllInvalid, Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array())
501
    )]
502
    #[case(Validity::Array(BoolArray::from_iter([false, true, false, true, false]).into_array()), 5, &[2, 4], Validity::Array(BoolArray::from_iter([true, false]).into_array()), Validity::Array(BoolArray::from_iter([false, true, true, true, false]).into_array())
503
    )]
504
    fn patch_validity(
505
        #[case] validity: Validity,
506
        #[case] len: usize,
507
        #[case] positions: &[u64],
508
        #[case] patches: Validity,
509
        #[case] expected: Validity,
510
    ) {
511
        let indices =
512
            PrimitiveArray::new(Buffer::copy_from(positions), Validity::NonNullable).into_array();
513
        assert_eq!(
514
            validity.patch(len, 0, &indices, &patches).unwrap(),
515
            expected
516
        );
517
    }
518

519
    #[test]
520
    #[should_panic]
521
    fn out_of_bounds_patch() {
1✔
522
        Validity::NonNullable
1✔
523
            .patch(2, 0, &buffer![4].into_array(), &Validity::AllInvalid)
1✔
524
            .unwrap();
1✔
525
    }
1✔
526

527
    #[test]
528
    #[should_panic]
529
    fn into_validity_nullable() {
1✔
530
        Validity::from_mask(Mask::AllFalse(10), Nullability::NonNullable);
1✔
531
    }
1✔
532

533
    #[test]
534
    #[should_panic]
535
    fn into_validity_nullable_array() {
1✔
536
        Validity::from_mask(Mask::from_iter(vec![true, false]), Nullability::NonNullable);
1✔
537
    }
1✔
538

539
    #[rstest]
540
    #[case(Validity::AllValid, PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(), Validity::from_iter(vec![true, false]))]
541
    #[case(Validity::AllValid, buffer![0, 1].into_array(), Validity::AllValid)]
542
    #[case(Validity::AllValid, PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(), Validity::AllInvalid)]
543
    #[case(Validity::NonNullable, PrimitiveArray::new(buffer![0, 1], Validity::from_iter(vec![true, false])).into_array(), Validity::from_iter(vec![true, false]))]
544
    #[case(Validity::NonNullable, buffer![0, 1].into_array(), Validity::NonNullable)]
545
    #[case(Validity::NonNullable, PrimitiveArray::new(buffer![0, 1], Validity::AllInvalid).into_array(), Validity::AllInvalid)]
546
    fn validity_take(
547
        #[case] validity: Validity,
548
        #[case] indices: ArrayRef,
549
        #[case] expected: Validity,
550
    ) {
551
        assert_eq!(validity.take(&indices).unwrap(), expected);
552
    }
553
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc