diff --git a/vortex-array/src/arrays/dict/vtable/mod.rs b/vortex-array/src/arrays/dict/vtable/mod.rs index fa8515dd986..34aa8198cf7 100644 --- a/vortex-array/src/arrays/dict/vtable/mod.rs +++ b/vortex-array/src/arrays/dict/vtable/mod.rs @@ -46,7 +46,6 @@ use crate::executor::ExecutionResult; use crate::require_child; use crate::scalar::Scalar; use crate::serde::ArrayChildren; -use crate::validity::Validity; mod kernel; mod operations; @@ -179,7 +178,7 @@ impl VTable for Dict { let array = require_child!(array, array.codes(), DictSlots::CODES => Primitive); - if matches!(array.codes().validity()?, Validity::AllInvalid) { + if array.codes().validity()?.definitely_all_invalid() { return Ok(ExecutionResult::done(ConstantArray::new( Scalar::null(array.dtype().as_nullable()), array.codes().len(), diff --git a/vortex-array/src/arrays/masked/vtable/mod.rs b/vortex-array/src/arrays/masked/vtable/mod.rs index 4409c8ff3c6..6fcb1a617b0 100644 --- a/vortex-array/src/arrays/masked/vtable/mod.rs +++ b/vortex-array/src/arrays/masked/vtable/mod.rs @@ -165,7 +165,7 @@ impl VTable for Masked { let validity = array.masked_validity(); // Fast path: all masked means result is all nulls. - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(ExecutionResult::done( ConstantArray::new(Scalar::null(array.dtype().as_nullable()), array.len()) .into_array(), diff --git a/vortex-array/src/arrays/primitive/array/top_value.rs b/vortex-array/src/arrays/primitive/array/top_value.rs index d3ee5eb5a65..e3ff2346f40 100644 --- a/vortex-array/src/arrays/primitive/array/top_value.rs +++ b/vortex-array/src/arrays/primitive/array/top_value.rs @@ -17,7 +17,6 @@ use crate::arrays::primitive::NativeValue; use crate::dtype::NativePType; use crate::match_each_native_ptype; use crate::scalar::PValue; -use crate::validity::Validity; impl PrimitiveArray { /// Compute most common present value of this array @@ -26,7 +25,7 @@ impl PrimitiveArray { return Ok(None); } - if matches!(self.validity()?, Validity::AllInvalid) { + if self.validity()?.definitely_all_invalid() { return Ok(None); } diff --git a/vortex-array/src/arrays/varbin/array.rs b/vortex-array/src/arrays/varbin/array.rs index 4d435471ce0..798336e9c4a 100644 --- a/vortex-array/src/arrays/varbin/array.rs +++ b/vortex-array/src/arrays/varbin/array.rs @@ -255,7 +255,7 @@ impl VarBinData { } _ => None, }; - let all_invalid = matches!(validity, Validity::AllInvalid); + let all_invalid = validity.definitely_all_invalid(); match_each_integer_ptype!(primitive_offsets.dtype().as_ptype(), |O| { let offsets_slice = primitive_offsets.as_slice::(); diff --git a/vortex-array/src/scalar_fn/fns/fill_null/kernel.rs b/vortex-array/src/scalar_fn/fns/fill_null/kernel.rs index eea3dd6ef7b..9b957d86710 100644 --- a/vortex-array/src/scalar_fn/fns/fill_null/kernel.rs +++ b/vortex-array/src/scalar_fn/fns/fill_null/kernel.rs @@ -79,7 +79,7 @@ pub(super) fn precondition( } // If all values are null, replace the entire array with the fill value. - if matches!(array.validity()?, Validity::AllInvalid) { + if array.validity()?.definitely_all_invalid() { return Ok(Some( ConstantArray::new(fill_value.clone(), array.len()).into_array(), )); diff --git a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs index 978a1da1caf..4d65ecd83dd 100644 --- a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs +++ b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs @@ -415,7 +415,7 @@ fn list_is_not_empty( ctx: &mut ExecutionCtx, ) -> VortexResult { // Short-circuit for all invalid. - if matches!(list_array.validity()?, Validity::AllInvalid) { + if list_array.validity()?.definitely_all_invalid() { return Ok(ConstantArray::new( Scalar::null(DType::Bool(Nullability::Nullable)), list_array.len(), diff --git a/vortex-array/src/validity.rs b/vortex-array/src/validity.rs index 73b5b08d5cb..d5e5592e2af 100644 --- a/vortex-array/src/validity.rs +++ b/vortex-array/src/validity.rs @@ -123,6 +123,17 @@ impl Validity { matches!(self, Self::NonNullable | Self::AllValid) } + /// Returns `true` if this validity is *definitely* all-invalid, i.e. it is + /// [`Validity::AllInvalid`]. + /// + /// Returning `false` does not prove the presence of valid values: a [`Validity::Array`] may + /// still resolve to all-invalid once executed. Callers must treat `false` as "unknown + /// without compute". This is the all-invalid counterpart to [`Self::definitely_no_nulls`]. + #[inline] + pub fn definitely_all_invalid(&self) -> bool { + matches!(self, Self::AllInvalid) + } + /// Returns whether this validity contains no null values, executing the validity array if /// necessary. /// diff --git a/vortex-cuda/src/dynamic_dispatch/mod.rs b/vortex-cuda/src/dynamic_dispatch/mod.rs index 186f2297db1..66dd8de7371 100644 --- a/vortex-cuda/src/dynamic_dispatch/mod.rs +++ b/vortex-cuda/src/dynamic_dispatch/mod.rs @@ -32,7 +32,6 @@ use vortex::array::buffer::BufferHandle; use vortex::array::buffer::DeviceBufferExt; use vortex::array::match_each_unsigned_integer_ptype; use vortex::array::scalar::Scalar; -use vortex::array::validity::Validity; use vortex::buffer::Alignment; use vortex::buffer::ByteBuffer; use vortex::buffer::ByteBufferMut; @@ -434,7 +433,7 @@ impl MaterializedPlan { let output_ptype = self.dispatch_plan.output_ptype(); // All values are null — no need to touch the GPU. - if matches!(self.validity, Validity::AllInvalid) { + if self.validity.definitely_all_invalid() { let dtype = DType::Primitive(output_ptype, Nullability::Nullable); return ConstantArray::new(Scalar::null(dtype), len) .into_array() diff --git a/vortex-cuda/src/kernel/encodings/date_time_parts.rs b/vortex-cuda/src/kernel/encodings/date_time_parts.rs index d8c655c2558..3334ee34b9b 100644 --- a/vortex-cuda/src/kernel/encodings/date_time_parts.rs +++ b/vortex-cuda/src/kernel/encodings/date_time_parts.rs @@ -72,7 +72,7 @@ impl CudaExecute for DateTimePartsExecutor { return Ok(Canonical::empty(array.dtype())); } - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { let storage_ptype = ext.storage_dtype().as_ptype(); return Ok(Canonical::Extension( TemporalArray::new_timestamp( diff --git a/vortex-cuda/src/kernel/encodings/fsst.rs b/vortex-cuda/src/kernel/encodings/fsst.rs index 5d3d66eaf04..f511e6aabba 100644 --- a/vortex-cuda/src/kernel/encodings/fsst.rs +++ b/vortex-cuda/src/kernel/encodings/fsst.rs @@ -23,7 +23,6 @@ use vortex::array::arrays::varbinview::build_views::build_views; use vortex::array::buffer::DeviceBuffer; use vortex::array::match_each_integer_ptype; use vortex::array::match_each_unsigned_integer_ptype; -use vortex::array::validity::Validity; use vortex::buffer::Alignment; use vortex::buffer::Buffer; use vortex::dtype::NativePType; @@ -62,7 +61,7 @@ impl CudaExecute for FSSTExecutor { let dtype = fsst.dtype().clone(); let validity = fsst.codes().validity()?; - if fsst.is_empty() || matches!(validity, Validity::AllInvalid) { + if fsst.is_empty() || validity.definitely_all_invalid() { let empty = unsafe { VarBinViewArray::new_unchecked( Buffer::::zeroed(fsst.len()), diff --git a/vortex-cuda/src/kernel/encodings/runend.rs b/vortex-cuda/src/kernel/encodings/runend.rs index fca435478d8..e86e8917edc 100644 --- a/vortex-cuda/src/kernel/encodings/runend.rs +++ b/vortex-cuda/src/kernel/encodings/runend.rs @@ -75,7 +75,7 @@ impl CudaExecute for RunEndExecutor { ))); } - if matches!(values.validity()?, Validity::AllInvalid) { + if values.validity()?.definitely_all_invalid() { return ConstantArray::new(Scalar::null(values.dtype().clone()), output_len) .into_array() .execute::(ctx.execution_ctx()); diff --git a/vortex-duckdb/src/exporter/bool.rs b/vortex-duckdb/src/exporter/bool.rs index 84fd17f0789..5b977ec7375 100644 --- a/vortex-duckdb/src/exporter/bool.rs +++ b/vortex-duckdb/src/exporter/bool.rs @@ -4,7 +4,6 @@ use vortex::array::ExecutionCtx; use vortex::array::arrays::BoolArray; use vortex::array::arrays::bool::BoolArrayExt; -use vortex::array::validity::Validity; use vortex::buffer::BitBuffer; use vortex::error::VortexResult; use vortex::mask::Mask; @@ -26,7 +25,7 @@ pub(crate) fn new_exporter( let bits = array.to_bit_buffer(); let validity = array.validity()?; - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } let validity = validity.to_array(len).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/decimal.rs b/vortex-duckdb/src/exporter/decimal.rs index f6b8d9607e7..674e4ce11ff 100644 --- a/vortex-duckdb/src/exporter/decimal.rs +++ b/vortex-duckdb/src/exporter/decimal.rs @@ -8,7 +8,6 @@ use vortex::array::ExecutionCtx; use vortex::array::arrays::DecimalArray; use vortex::array::arrays::decimal::DecimalDataParts; use vortex::array::match_each_decimal_value_type; -use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::dtype::BigCast; use vortex::dtype::DecimalDType; @@ -49,7 +48,7 @@ pub(crate) fn new_exporter( } = array.into_data_parts(); let dest_values_type = precision_to_duckdb_storage_size(&decimal_dtype)?; - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } let validity = validity.to_array(len).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/fixed_size_list.rs b/vortex-duckdb/src/exporter/fixed_size_list.rs index ed93ad2b9c1..d9be4978223 100644 --- a/vortex-duckdb/src/exporter/fixed_size_list.rs +++ b/vortex-duckdb/src/exporter/fixed_size_list.rs @@ -11,7 +11,6 @@ use vortex::array::ExecutionCtx; use vortex::array::arrays::FixedSizeListArray; use vortex::array::arrays::fixed_size_list::FixedSizeListArrayExt; -use vortex::array::validity::Validity; use vortex::error::VortexResult; use vortex::mask::Mask; @@ -43,7 +42,7 @@ pub(crate) fn new_exporter( let elements = parts.elements; let validity = parts.validity; - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } diff --git a/vortex-duckdb/src/exporter/list.rs b/vortex-duckdb/src/exporter/list.rs index a0eb65ffd64..dbc2985d560 100644 --- a/vortex-duckdb/src/exporter/list.rs +++ b/vortex-duckdb/src/exporter/list.rs @@ -11,7 +11,6 @@ use vortex::array::arrays::ListArray; use vortex::array::arrays::PrimitiveArray; use vortex::array::arrays::list::ListDataParts; use vortex::array::match_each_integer_ptype; -use vortex::array::validity::Validity; use vortex::dtype::IntegerPType; use vortex::error::VortexResult; use vortex::error::vortex_ensure; @@ -55,7 +54,7 @@ pub(crate) fn new_exporter( } = array.into_data_parts(); let num_elements = elements.len(); - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } let validity = validity.to_array(array_len).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/list_view.rs b/vortex-duckdb/src/exporter/list_view.rs index a4cb61895f3..8c667c86f85 100644 --- a/vortex-duckdb/src/exporter/list_view.rs +++ b/vortex-duckdb/src/exporter/list_view.rs @@ -16,7 +16,6 @@ use vortex::array::arrays::listview::ListViewArrayExt; use vortex::array::arrays::listview::ListViewDataParts; use vortex::array::arrays::listview::ListViewRebuildMode; use vortex::array::match_each_integer_ptype; -use vortex::array::validity::Validity; use vortex::dtype::IntegerPType; use vortex::error::VortexExpect; use vortex::error::VortexResult; @@ -92,7 +91,7 @@ pub(crate) fn new_exporter( // Cache an `elements` vector up front so that future exports can reference it. let num_elements = elements.len(); - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } let validity = validity.to_array(len).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/primitive.rs b/vortex-duckdb/src/exporter/primitive.rs index a0b08c80e4b..803ad2fca9f 100644 --- a/vortex-duckdb/src/exporter/primitive.rs +++ b/vortex-duckdb/src/exporter/primitive.rs @@ -6,7 +6,6 @@ use std::marker::PhantomData; use vortex::array::ExecutionCtx; use vortex::array::arrays::PrimitiveArray; use vortex::array::match_each_native_ptype; -use vortex::array::validity::Validity; use vortex::dtype::NativePType; use vortex::error::VortexResult; use vortex::mask::Mask; @@ -29,7 +28,7 @@ pub fn new_exporter( ctx: &mut ExecutionCtx, ) -> VortexResult> { let validity = array.validity()?; - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); }; let validity = validity.to_array(array.len()).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/struct_.rs b/vortex-duckdb/src/exporter/struct_.rs index 76c07d672a3..f1df6899a00 100644 --- a/vortex-duckdb/src/exporter/struct_.rs +++ b/vortex-duckdb/src/exporter/struct_.rs @@ -8,7 +8,6 @@ use vortex::array::arrays::StructArray; use vortex::array::arrays::bool::BoolArrayExt; use vortex::array::arrays::struct_::StructDataParts; use vortex::array::builtins::ArrayBuiltins; -use vortex::array::validity::Validity; use vortex::error::VortexResult; use crate::duckdb::VectorRef; @@ -35,7 +34,7 @@ pub(crate) fn new_exporter( .. } = array.into_data_parts(); - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); }; let validity = validity.to_array(len).execute::(ctx)?; diff --git a/vortex-duckdb/src/exporter/varbinview.rs b/vortex-duckdb/src/exporter/varbinview.rs index 557795f45f3..cc99daafeb7 100644 --- a/vortex-duckdb/src/exporter/varbinview.rs +++ b/vortex-duckdb/src/exporter/varbinview.rs @@ -9,7 +9,6 @@ use vortex::array::arrays::VarBinViewArray; use vortex::array::arrays::varbinview::BinaryView; use vortex::array::arrays::varbinview::Inlined; use vortex::array::arrays::varbinview::VarBinViewDataParts; -use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::buffer::ByteBuffer; use vortex::error::VortexResult; @@ -39,7 +38,7 @@ pub(crate) fn new_exporter( buffers, } = array.into_data_parts(); - if matches!(validity, Validity::AllInvalid) { + if validity.definitely_all_invalid() { return Ok(all_invalid::new_exporter()); } let validity = validity.to_array(len).execute::(ctx)?;