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

vortex-data / vortex / 16387130526

19 Jul 2025 09:05AM UTC coverage: 81.512% (-0.008%) from 81.52%
16387130526

push

github

web-flow
feat: duckdb workstealing (#3927)

Signed-off-by: Alexander Droste <alexander.droste@protonmail.com>

16 of 17 new or added lines in 1 file covered. (94.12%)

185 existing lines in 8 files now uncovered.

42000 of 51526 relevant lines covered (81.51%)

171508.11 hits per line

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

77.43
/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 {
315,382✔
76
        match self {
315,382✔
77
            Self::NonNullable => Nullability::NonNullable,
251,858✔
78
            _ => Nullability::Nullable,
63,524✔
79
        }
80
    }
315,382✔
81

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

90
    pub fn all_valid(&self) -> VortexResult<bool> {
3,234,894✔
91
        Ok(match self {
3,234,894✔
92
            Validity::NonNullable | Validity::AllValid => true,
3,232,649✔
93
            Validity::AllInvalid => false,
37✔
94
            Validity::Array(array) => {
2,208✔
95
                // TODO(ngates): replace with SUM compute function
96
                array.to_bool()?.boolean_buffer().count_set_bits() == array.len()
2,208✔
97
            }
98
        })
99
    }
3,234,894✔
100

101
    pub fn all_invalid(&self) -> VortexResult<bool> {
153,580✔
102
        Ok(match self {
153,580✔
103
            Validity::NonNullable | Validity::AllValid => false,
147,063✔
104
            Validity::AllInvalid => true,
412✔
105
            Validity::Array(array) => {
6,105✔
106
                // TODO(ngates): replace with SUM compute function
107
                array.to_bool()?.boolean_buffer().count_set_bits() == 0
6,105✔
108
            }
109
        })
110
    }
153,580✔
111

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

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

133
    pub fn slice(&self, start: usize, stop: usize) -> VortexResult<Self> {
50,632✔
134
        match self {
50,632✔
135
            Self::Array(a) => Ok(Self::Array(a.slice(start, stop)?)),
1,334✔
136
            _ => Ok(self.clone()),
49,298✔
137
        }
138
    }
50,632✔
139

140
    pub fn take(&self, indices: &dyn Array) -> VortexResult<Self> {
3,793✔
141
        match self {
3,793✔
142
            Self::NonNullable => match indices.validity_mask()?.boolean_buffer() {
2,490✔
143
                AllOr::All => {
144
                    if indices.dtype().is_nullable() {
2,184✔
145
                        Ok(Self::AllValid)
149✔
146
                    } else {
147
                        Ok(Self::NonNullable)
2,035✔
148
                    }
149
                }
150
                AllOr::None => Ok(Self::AllInvalid),
1✔
151
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
305✔
152
            },
153
            Self::AllValid => match indices.validity_mask()?.boolean_buffer() {
1,112✔
154
                AllOr::All => Ok(Self::AllValid),
222✔
155
                AllOr::None => Ok(Self::AllInvalid),
1✔
156
                AllOr::Some(buf) => Ok(Validity::from(buf.clone())),
889✔
157
            },
158
            Self::AllInvalid => Ok(Self::AllInvalid),
×
159
            Self::Array(is_valid) => {
191✔
160
                let maybe_is_valid = take(is_valid, indices)?;
191✔
161
                // Null indices invalidate that position.
162
                let is_valid = fill_null(&maybe_is_valid, &Scalar::from(false))?;
191✔
163
                Ok(Self::Array(is_valid))
191✔
164
            }
165
        }
166
    }
3,793✔
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> {
5,163✔
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 {
5,163✔
175
            v @ (Validity::NonNullable | Validity::AllValid | Validity::AllInvalid) => {
4,717✔
176
                Ok(v.clone())
4,717✔
177
            }
178
            Validity::Array(arr) => Ok(Validity::Array(filter(arr, mask)?)),
446✔
179
        }
180
    }
5,163✔
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> {
607✔
186
        match mask.boolean_buffer() {
607✔
UNCOV
187
            AllOr::All => Ok(Validity::AllInvalid),
×
UNCOV
188
            AllOr::None => Ok(self.clone()),
×
189
            AllOr::Some(make_invalid) => Ok(match self {
607✔
190
                Validity::NonNullable | Validity::AllValid => {
191
                    Validity::Array(BoolArray::from(make_invalid.not()).into_array())
96✔
192
                }
193
                Validity::AllInvalid => Validity::AllInvalid,
4✔
194
                Validity::Array(is_valid) => {
507✔
195
                    let is_valid = is_valid.to_bool()?;
507✔
196
                    let keep_valid = make_invalid.not();
507✔
197
                    Validity::from(is_valid.boolean_buffer().bitand(&keep_valid))
507✔
198
                }
199
            }),
200
        }
201
    }
607✔
202

203
    pub fn to_mask(&self, length: usize) -> VortexResult<Mask> {
315,181✔
204
        Ok(match self {
315,181✔
205
            Self::NonNullable | Self::AllValid => Mask::AllTrue(length),
293,057✔
206
            Self::AllInvalid => Mask::AllFalse(length),
234✔
207
            Self::Array(is_valid) => {
21,890✔
208
                assert_eq!(
21,890✔
209
                    is_valid.len(),
21,890✔
210
                    length,
UNCOV
211
                    "Validity::Array length must equal to_logical's argument: {}, {}.",
×
UNCOV
212
                    is_valid.len(),
×
213
                    length,
214
                );
215
                Mask::try_from(&is_valid.to_bool()?)?
21,890✔
216
            }
217
        })
218
    }
315,181✔
219

220
    /// Logically & two Validity values of the same length
221
    pub fn and(self, rhs: Validity) -> VortexResult<Validity> {
1✔
222
        let validity = match (self, rhs) {
1✔
223
            // Should be pretty clear
224
            (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable,
1✔
225
            // Any `AllInvalid` makes the output all invalid values
UNCOV
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)
×
UNCOV
229
            | (Validity::Array(a), Validity::NonNullable)
×
UNCOV
230
            | (Validity::NonNullable, Validity::Array(a))
×
UNCOV
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

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

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

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

251
    pub fn patch(
2,131✔
252
        self,
2,131✔
253
        len: usize,
2,131✔
254
        indices_offset: usize,
2,131✔
255
        indices: &dyn Array,
2,131✔
256
        patches: &Validity,
2,131✔
257
    ) -> VortexResult<Self> {
2,131✔
258
        match (&self, patches) {
2,131✔
259
            (Validity::NonNullable, Validity::NonNullable) => return Ok(Validity::NonNullable),
1,373✔
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
            _ => {}
755✔
269
        };
270

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

277
        let source = match self {
755✔
UNCOV
278
            Validity::NonNullable => BoolArray::from(BooleanBuffer::new_set(len)),
×
279
            Validity::AllValid => BoolArray::from(BooleanBuffer::new_set(len)),
372✔
280
            Validity::AllInvalid => BoolArray::from(BooleanBuffer::new_unset(len)),
343✔
281
            Validity::Array(a) => a.to_bool()?,
40✔
282
        };
283

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

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

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

304
    /// Convert into a nullable variant
305
    pub fn into_nullable(self) -> Validity {
1,878✔
306
        match self {
1,878✔
307
            Self::NonNullable => Self::AllValid,
1,690✔
308
            _ => self,
188✔
309
        }
310
    }
1,878✔
311

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

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

341
    /// Create Validity by copying the given array's validity.
342
    pub fn copy_from_array(array: &dyn Array) -> VortexResult<Self> {
1,900✔
343
        Ok(Validity::from_mask(
1,900✔
344
            array.validity_mask()?,
1,900✔
345
            array.dtype().nullability(),
1,900✔
346
        ))
347
    }
1,900✔
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 {
755✔
354
        if !matches!(value.dtype(), DType::Bool(Nullability::NonNullable)) {
755✔
UNCOV
355
            vortex_panic!("Expected a non-nullable boolean array")
×
356
        }
755✔
357
        match nullability {
755✔
UNCOV
358
            Nullability::NonNullable => Self::NonNullable,
×
359
            Nullability::Nullable => Self::Array(value),
755✔
360
        }
361
    }
755✔
362

363
    /// Returns the length of the validity array, if it exists.
364
    pub fn maybe_len(&self) -> Option<usize> {
551,534✔
365
        match self {
551,534✔
366
            Self::NonNullable | Self::AllValid | Self::AllInvalid => None,
526,381✔
367
            Self::Array(a) => Some(a.len()),
25,153✔
368
        }
369
    }
551,534✔
370

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

UNCOV
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 {
37,466✔
386
        match (self, other) {
37,466✔
387
            (Self::NonNullable, Self::NonNullable) => true,
21,339✔
388
            (Self::AllValid, Self::AllValid) => true,
119✔
389
            (Self::AllInvalid, Self::AllInvalid) => true,
117✔
390
            (Self::Array(a), Self::Array(b)) => {
500✔
391
                let a = a
500✔
392
                    .to_bool()
500✔
393
                    .vortex_expect("Failed to get Validity Array as BoolArray");
500✔
394
                let b = b
500✔
395
                    .to_bool()
500✔
396
                    .vortex_expect("Failed to get Validity Array as BoolArray");
500✔
397
                a.boolean_buffer() == b.boolean_buffer()
500✔
398
            }
399
            _ => false,
15,391✔
400
        }
401
    }
37,466✔
402
}
403

404
impl From<BooleanBuffer> for Validity {
405
    fn from(value: BooleanBuffer) -> Self {
11,763✔
406
        if value.count_set_bits() == value.len() {
11,763✔
407
            Self::AllValid
263✔
408
        } else if value.count_set_bits() == 0 {
11,500✔
409
            Self::AllInvalid
491✔
410
        } else {
411
            Self::Array(BoolArray::from(value).into_array())
11,009✔
412
        }
413
    }
11,763✔
414
}
415

416
impl From<NullBuffer> for Validity {
417
    fn from(value: NullBuffer) -> Self {
3,812✔
418
        value.into_inner().into()
3,812✔
419
    }
3,812✔
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 {
192✔
430
        Validity::from(BooleanBuffer::from_iter(iter))
192✔
431
    }
192✔
432
}
433

434
impl From<Nullability> for Validity {
435
    fn from(value: Nullability) -> Self {
18,965✔
436
        match value {
18,965✔
437
            Nullability::NonNullable => Validity::NonNullable,
14,846✔
438
            Nullability::Nullable => Validity::AllValid,
4,119✔
439
        }
440
    }
18,965✔
441
}
442

443
impl Validity {
444
    pub fn from_mask(mask: Mask, nullability: Nullability) -> Self {
2,277✔
445
        assert!(
2,277✔
446
            nullability == Nullability::Nullable || matches!(mask, Mask::AllTrue(_)),
2,277✔
447
            "NonNullable validity must be AllValid",
2✔
448
        );
449
        match mask {
2,275✔
450
            Mask::AllTrue(_) => match nullability {
831✔
451
                Nullability::NonNullable => Validity::NonNullable,
793✔
452
                Nullability::Nullable => Validity::AllValid,
38✔
453
            },
454
            Mask::AllFalse(_) => Validity::AllInvalid,
74✔
455
            Mask::Values(values) => Validity::Array(values.into_array()),
1,370✔
456
        }
457
    }
2,275✔
458
}
459

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

470
impl IntoArray for &MaskValues {
471
    fn into_array(self) -> ArrayRef {
1,407✔
472
        BoolArray::new(self.boolean_buffer().clone(), Validity::NonNullable).into_array()
1,407✔
473
    }
1,407✔
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

© 2025 Coveralls, Inc