From 47b4ab4dc851ad774635850af5ecd846b823855d Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Fri, 14 Aug 2026 13:13:11 -0400 Subject: [PATCH] Fix empty array validity predicates Signed-off-by: Connor Tsui --- vortex-array/src/array/erased.rs | 8 ++++++++ vortex-array/src/arrays/masked/tests.rs | 21 ++++++++++++++++++++ vortex-array/src/arrays/masked/vtable/mod.rs | 5 +++++ 3 files changed, 34 insertions(+) diff --git a/vortex-array/src/array/erased.rs b/vortex-array/src/array/erased.rs index 6674c4d9429..a7fb4fc67dd 100644 --- a/vortex-array/src/array/erased.rs +++ b/vortex-array/src/array/erased.rs @@ -301,6 +301,10 @@ impl ArrayRef { /// Returns whether all items in the array are valid. pub fn all_valid(&self, ctx: &mut ExecutionCtx) -> VortexResult { + if self.is_empty() { + return Ok(true); + } + match self.validity()? { Validity::NonNullable | Validity::AllValid => Ok(true), Validity::AllInvalid => Ok(false), @@ -310,6 +314,10 @@ impl ArrayRef { /// Returns whether the array is all invalid. pub fn all_invalid(&self, ctx: &mut ExecutionCtx) -> VortexResult { + if self.is_empty() { + return Ok(true); + } + match self.validity()? { Validity::NonNullable | Validity::AllValid => Ok(false), Validity::AllInvalid => Ok(true), diff --git a/vortex-array/src/arrays/masked/tests.rs b/vortex-array/src/arrays/masked/tests.rs index 92ec4eb474f..b326380bbf4 100644 --- a/vortex-array/src/arrays/masked/tests.rs +++ b/vortex-array/src/arrays/masked/tests.rs @@ -2,6 +2,8 @@ // SPDX-FileCopyrightText: Copyright the Vortex contributors use rstest::rstest; +use vortex_buffer::BitBuffer; +use vortex_buffer::Buffer; use vortex_error::VortexExpect; use vortex_error::VortexResult; @@ -10,6 +12,7 @@ use crate::Canonical; use crate::IntoArray; use crate::VortexSessionExecute; use crate::array_session; +use crate::arrays::BoolArray; use crate::arrays::ListViewArray; use crate::arrays::PrimitiveArray; use crate::assert_arrays_eq; @@ -41,6 +44,24 @@ fn test_dtype_nullability_with_nullable_child() { assert!(child.dtype().is_nullable()); } +#[test] +fn test_empty_child_with_array_validity() -> VortexResult<()> { + let child_validity = + Validity::Array(BoolArray::new(BitBuffer::new_set(0), Validity::NonNullable).into_array()); + let child = PrimitiveArray::new(Buffer::::empty(), child_validity).into_array(); + let validity = + Validity::Array(BoolArray::new(BitBuffer::new_set(0), Validity::NonNullable).into_array()); + + let mut ctx = array_session().create_execution_ctx(); + assert!(child.all_valid(&mut ctx)?); + assert!(child.all_invalid(&mut ctx)?); + + let array = MaskedArray::try_new(child, validity)?; + + assert!(array.is_empty()); + Ok(()) +} + #[test] fn test_canonical_dtype_matches_array_dtype() -> VortexResult<()> { // The canonical form should have the same nullability as the array's dtype. diff --git a/vortex-array/src/arrays/masked/vtable/mod.rs b/vortex-array/src/arrays/masked/vtable/mod.rs index c7e32a7ee3c..ccadee27e98 100644 --- a/vortex-array/src/arrays/masked/vtable/mod.rs +++ b/vortex-array/src/arrays/masked/vtable/mod.rs @@ -73,6 +73,7 @@ impl VTable for Masked { *ID } + #[expect(clippy::disallowed_methods)] fn validate( &self, _data: &MaskedData, @@ -92,6 +93,10 @@ impl VTable for Masked { child.dtype().as_nullable() == *dtype, "MaskedArray dtype does not match child and validity" ); + vortex_ensure!( + child.all_valid(&mut legacy_session().create_execution_ctx())?, + "MaskedArray children must not have nulls", + ); Ok(()) }