Skip to content

Commit

Permalink
Converting LogicalValidity into Validity requires passing Nullability (
Browse files Browse the repository at this point in the history
…#1834)

Without nullability we can't accurately reconstruct Validity as
LogicalValidity losses nonnullable state
  • Loading branch information
robert3005 authored Jan 7, 2025
1 parent 116de09 commit 0e61bb5
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 24 deletions.
6 changes: 4 additions & 2 deletions encodings/alp/src/alp_rd/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ impl IntoCanonical for ALPRDArray {
right_parts.into_buffer_mut::<u32>(),
self.left_parts_patches(),
)?,
self.logical_validity().into_validity(),
self.logical_validity()
.into_validity(self.dtype().nullability()),
)
} else {
PrimitiveArray::new(
Expand All @@ -218,7 +219,8 @@ impl IntoCanonical for ALPRDArray {
right_parts.into_buffer_mut::<u64>(),
self.left_parts_patches(),
)?,
self.logical_validity().into_validity(),
self.logical_validity()
.into_validity(self.dtype().nullability()),
)
};

Expand Down
8 changes: 3 additions & 5 deletions encodings/datetime-parts/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,9 @@ impl DateTimePartsArray {
}

pub fn validity(&self) -> Validity {
if self.dtype().is_nullable() {
self.days().logical_validity().into_validity()
} else {
Validity::NonNullable
}
self.days()
.logical_validity()
.into_validity(self.dtype().nullability())
}
}

Expand Down
8 changes: 3 additions & 5 deletions vortex-array/src/array/chunked/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ use crate::{

impl IntoCanonical for ChunkedArray {
fn into_canonical(self) -> VortexResult<Canonical> {
let validity = if self.dtype().is_nullable() {
self.logical_validity().into_validity()
} else {
Validity::NonNullable
};
let validity = self
.logical_validity()
.into_validity(self.dtype().nullability());
try_canonicalize_chunks(self.chunks().collect(), validity, self.dtype())
}
}
Expand Down
51 changes: 39 additions & 12 deletions vortex-array/src/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,12 @@ impl Validity {
_ => {}
};

let own_nullability = if self == Validity::NonNullable {
Nullability::NonNullable
} else {
Nullability::Nullable
};

let source = match self {
Validity::NonNullable => BoolArray::from(BooleanBuffer::new_set(len)),
Validity::AllValid => BoolArray::from(BooleanBuffer::new_set(len)),
Expand All @@ -317,7 +323,7 @@ impl Validity {

let patches = Patches::new(len, indices.clone(), patch_values.into_array());

Validity::try_from(source.patch(patches)?.into_array())
Self::from_array(source.patch(patches)?.into_array(), own_nullability)
}

/// Convert into a nullable variant
Expand All @@ -327,6 +333,14 @@ impl Validity {
_ => self,
}
}

/// Create Validity from boolean array with given nullability of the array.
///
/// Note: You want to pass the nullability of parent array and not the nullability of the validity array itself
/// as that is always nonnullable
pub fn from_array(value: ArrayData, nullability: Nullability) -> VortexResult<Self> {
LogicalValidity::try_from(value).map(|a| a.into_validity(nullability))
}
}

impl PartialEq for Validity {
Expand Down Expand Up @@ -371,14 +385,6 @@ impl From<NullBuffer> for Validity {
}
}

impl TryFrom<ArrayData> for Validity {
type Error = VortexError;

fn try_from(value: ArrayData) -> Result<Self, Self::Error> {
LogicalValidity::try_from(value).map(|a| a.into_validity())
}
}

impl FromIterator<LogicalValidity> for Validity {
fn from_iter<T: IntoIterator<Item = LogicalValidity>>(iter: T) -> Self {
let validities: Vec<LogicalValidity> = iter.into_iter().collect();
Expand Down Expand Up @@ -489,9 +495,16 @@ impl LogicalValidity {
}
}

pub fn into_validity(self) -> Validity {
pub fn into_validity(self, nullability: Nullability) -> Validity {
assert!(
nullability == Nullability::Nullable || matches!(self, Self::AllValid(_)),
"NonNullable validity must be AllValid",
);
match self {
Self::AllValid(_) => Validity::AllValid,
Self::AllValid(_) => match nullability {
Nullability::NonNullable => Validity::NonNullable,
Nullability::Nullable => Validity::AllValid,
},
Self::AllInvalid(_) => Validity::AllInvalid,
Self::Array(a) => Validity::Array(a),
}
Expand Down Expand Up @@ -533,9 +546,10 @@ impl IntoArrayData for LogicalValidity {
mod tests {
use rstest::rstest;
use vortex_buffer::{buffer, Buffer};
use vortex_dtype::Nullability;

use crate::array::{BoolArray, PrimitiveArray};
use crate::validity::Validity;
use crate::validity::{LogicalValidity, Validity};
use crate::IntoArrayData;

#[rstest]
Expand Down Expand Up @@ -574,4 +588,17 @@ mod tests {
.patch(2, &buffer![4].into_array(), Validity::AllInvalid)
.unwrap();
}

#[test]
#[should_panic]
fn into_validity_nullable() {
LogicalValidity::AllInvalid(10).into_validity(Nullability::NonNullable);
}

#[test]
#[should_panic]
fn into_validity_nullable_array() {
LogicalValidity::Array(BoolArray::from_iter(vec![true, false]).into_array())
.into_validity(Nullability::NonNullable);
}
}

0 comments on commit 0e61bb5

Please sign in to comment.