use vortex_array::expr::Expression;
use vortex_array::expr::is_root;
use vortex_array::expr::not;
use vortex_array::expr::root;
use vortex_array::scalar_fn::fns::is_not_null::IsNotNull;
use vortex_array::scalar_fn::fns::is_null::IsNull;
use vortex_array::scalar_fn::fns::list_length::ListLength;
use vortex_error::VortexResult;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum ListChildrenNeeded {
Validity,
OffsetsAndValidity,
All,
}
pub(super) fn get_necessary_list_children(expr: &Expression) -> ListChildrenNeeded {
if is_null_root(expr) {
return ListChildrenNeeded::Validity;
}
if is_list_length_root(expr) {
return ListChildrenNeeded::OffsetsAndValidity;
}
if is_root(expr) {
return ListChildrenNeeded::All;
}
expr.children()
.iter()
.map(get_necessary_list_children)
.max()
.unwrap_or(ListChildrenNeeded::Validity)
}
fn is_null_root(expr: &Expression) -> bool {
(expr.is::<IsNull>() || expr.is::<IsNotNull>())
&& expr.children().len() == 1
&& is_root(expr.child(0))
}
fn is_list_length_root(expr: &Expression) -> bool {
expr.is::<ListLength>() && expr.children().len() == 1 && is_root(expr.child(0))
}
pub(super) fn rewrite_validity_expr(expr: &Expression) -> VortexResult<Expression> {
if expr.is::<IsNotNull>() && expr.children().len() == 1 && is_root(expr.child(0)) {
return Ok(root());
}
if expr.is::<IsNull>() && expr.children().len() == 1 && is_root(expr.child(0)) {
return Ok(not(root()));
}
let children = expr
.children()
.iter()
.map(rewrite_validity_expr)
.collect::<VortexResult<Vec<_>>>()?;
expr.clone().with_children(children)
}
pub(super) fn rewrite_offsets_expr(expr: &Expression) -> VortexResult<Expression> {
if is_list_length_root(expr) {
return Ok(root());
}
let children = expr
.children()
.iter()
.map(rewrite_offsets_expr)
.collect::<VortexResult<Vec<_>>>()?;
expr.clone().with_children(children)
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use vortex_array::dtype::DType;
use vortex_array::dtype::Nullability;
use vortex_array::dtype::PType;
use vortex_array::expr::cast;
use vortex_array::expr::eq;
use vortex_array::expr::gt;
use vortex_array::expr::is_not_null;
use vortex_array::expr::is_null;
use vortex_array::expr::list_length;
use vortex_array::expr::lit;
use vortex_array::expr::not;
use vortex_array::expr::root;
use super::*;
#[rstest]
#[case::is_null(is_null(root()), ListChildrenNeeded::Validity)]
#[case::is_not_null(is_not_null(root()), ListChildrenNeeded::Validity)]
#[case::not_is_null(not(is_null(root())), ListChildrenNeeded::Validity)]
#[case::constant(lit(5), ListChildrenNeeded::Validity)]
#[case::list_length(list_length(root()), ListChildrenNeeded::OffsetsAndValidity)]
#[case::list_length_filter(
gt(list_length(root()), lit(1u64)),
ListChildrenNeeded::OffsetsAndValidity
)]
#[case::cast_list_length(
cast(
list_length(root()),
DType::Primitive(PType::I64, Nullability::Nullable),
),
ListChildrenNeeded::OffsetsAndValidity
)]
#[case::bare_root(root(), ListChildrenNeeded::All)]
#[case::not_root(not(root()), ListChildrenNeeded::All)]
#[case::is_null_of_derived(is_null(not(root())), ListChildrenNeeded::All)]
#[case::validity_and_elements(eq(is_null(root()), root()), ListChildrenNeeded::All)]
fn classify_expr_class(#[case] expr: Expression, #[case] expected: ListChildrenNeeded) {
assert_eq!(get_necessary_list_children(&expr), expected);
}
}