diff --git a/vortex-array/src/arrays/masked/array.rs b/vortex-array/src/arrays/masked/array.rs index 6062fa4e0d7..59c7a408d4a 100644 --- a/vortex-array/src/arrays/masked/array.rs +++ b/vortex-array/src/arrays/masked/array.rs @@ -75,18 +75,26 @@ impl MaskedData { } } +#[expect(clippy::disallowed_methods)] +pub(super) fn child_all_valid(child: &ArrayRef) -> VortexResult { + if child.is_empty() { + return Ok(true); + } + + match child.validity()? { + Validity::NonNullable | Validity::AllValid => Ok(true), + Validity::AllInvalid => Ok(false), + Validity::Array(_) => child.all_valid(&mut legacy_session().create_execution_ctx()), + } +} + impl Array { /// Constructs a new `MaskedArray`. - #[allow(clippy::disallowed_methods)] pub fn try_new(child: ArrayRef, validity: Validity) -> VortexResult { let dtype = child.dtype().as_nullable(); let len = child.len(); let validity_slot = validity_to_child(&validity, len); - let data = MaskedData::try_new( - len, - child.all_valid(&mut legacy_session().create_execution_ctx())?, - validity, - )?; + let data = MaskedData::try_new(len, child_all_valid(&child)?, validity)?; Ok(unsafe { Array::from_parts_unchecked( ArrayParts::new(Masked, dtype, len, data) diff --git a/vortex-array/src/arrays/masked/tests.rs b/vortex-array/src/arrays/masked/tests.rs index b326380bbf4..1061160c4de 100644 --- a/vortex-array/src/arrays/masked/tests.rs +++ b/vortex-array/src/arrays/masked/tests.rs @@ -7,6 +7,7 @@ use vortex_buffer::Buffer; use vortex_error::VortexExpect; use vortex_error::VortexResult; +use super::array::child_all_valid; use super::*; use crate::Canonical; use crate::IntoArray; @@ -20,6 +21,25 @@ use crate::dtype::DType; use crate::dtype::Nullability; use crate::validity::Validity; +#[rstest] +#[case(Validity::NonNullable, true)] +#[case(Validity::AllValid, true)] +#[case(Validity::AllInvalid, false)] +#[case(Validity::from_iter([true, true, true]), true)] +#[case(Validity::from_iter([true, false, true]), false)] +fn test_child_all_valid(#[case] validity: Validity, #[case] expected: bool) -> VortexResult<()> { + let child = PrimitiveArray::new(vortex_buffer::buffer![1i32, 2, 3], validity).into_array(); + assert_eq!(child_all_valid(&child)?, expected); + Ok(()) +} + +#[test] +fn test_empty_child_all_valid() -> VortexResult<()> { + let child = PrimitiveArray::new(Buffer::::empty(), Validity::AllInvalid).into_array(); + assert!(child_all_valid(&child)?); + Ok(()) +} + #[rstest] #[case(Validity::AllValid, Nullability::Nullable)] #[case(Validity::from_iter([true, false, true]), Nullability::Nullable)] diff --git a/vortex-array/src/arrays/masked/vtable/mod.rs b/vortex-array/src/arrays/masked/vtable/mod.rs index ccadee27e98..9c2bb6a5f91 100644 --- a/vortex-array/src/arrays/masked/vtable/mod.rs +++ b/vortex-array/src/arrays/masked/vtable/mod.rs @@ -23,7 +23,6 @@ use crate::ArrayRef; use crate::Canonical; use crate::EqMode; use crate::IntoArray; -use crate::VortexSessionExecute; use crate::array::Array; use crate::array::ArrayId; use crate::array::ArrayView; @@ -35,13 +34,13 @@ use crate::arrays::masked::MaskedArrayExt; use crate::arrays::masked::MaskedArraySlotsExt; use crate::arrays::masked::MaskedData; use crate::arrays::masked::array::MaskedSlots; +use crate::arrays::masked::array::child_all_valid; use crate::arrays::masked::compute::rules::PARENT_RULES; use crate::arrays::masked::mask_validity_canonical; use crate::buffer::BufferHandle; use crate::dtype::DType; use crate::executor::ExecutionCtx; use crate::executor::ExecutionResult; -use crate::legacy_session; use crate::require_child; use crate::scalar::Scalar; use crate::serde::ArrayChildren; @@ -73,7 +72,6 @@ impl VTable for Masked { *ID } - #[expect(clippy::disallowed_methods)] fn validate( &self, _data: &MaskedData, @@ -94,7 +92,7 @@ impl VTable for Masked { "MaskedArray dtype does not match child and validity" ); vortex_ensure!( - child.all_valid(&mut legacy_session().create_execution_ctx())?, + child_all_valid(child)?, "MaskedArray children must not have nulls", ); Ok(()) @@ -127,7 +125,6 @@ impl VTable for Masked { Ok(Some(vec![])) } - #[allow(clippy::disallowed_methods)] fn deserialize( &self, dtype: &DType, @@ -164,11 +161,7 @@ impl VTable for Masked { }; let validity_slot = validity_to_child(&validity, len); - let data = MaskedData::try_new( - len, - child.all_valid(&mut legacy_session().create_execution_ctx())?, - validity, - )?; + let data = MaskedData::try_new(len, child_all_valid(&child)?, validity)?; Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data) .with_slots(smallvec![Some(child), validity_slot])) }