use crate::{ast::FunctionDispatch, ScalarExpr};
use uqa_core::Value;
pub struct MembershipOperands<'a> {
pub value: &'a ScalarExpr,
pub items: MembershipItems<'a>,
pub negated: bool,
}
#[derive(Clone, Copy)]
pub enum MembershipItems<'a> {
Expressions(&'a [ScalarExpr]),
Constants(&'a [Value]),
}
pub enum MembershipItem<'a> {
Expression(&'a ScalarExpr),
Constant(&'a Value),
}
impl<'a> MembershipItems<'a> {
#[must_use]
pub fn len(self) -> usize {
match self {
Self::Expressions(items) => items.len(),
Self::Constants(items) => items.len(),
}
}
#[must_use]
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn iter(self) -> impl Iterator<Item = MembershipItem<'a>> {
let (expressions, constants): (&[ScalarExpr], &[Value]) = match self {
Self::Expressions(items) => (items, &[]),
Self::Constants(items) => (&[], items),
};
expressions
.iter()
.map(MembershipItem::Expression)
.chain(constants.iter().map(MembershipItem::Constant))
}
}
#[must_use]
pub fn membership_operands(expression: &ScalarExpr) -> Option<MembershipOperands<'_>> {
match expression {
ScalarExpr::InList {
expr,
list,
negated,
} => Some(MembershipOperands {
value: expr,
items: MembershipItems::Expressions(list),
negated: *negated,
}),
ScalarExpr::Func {
binding: Some(binding),
args,
..
} => {
let [value, array, ScalarExpr::Literal(Value::Str(operator))] = args.as_slice() else {
return None;
};
let items = match array {
ScalarExpr::Array(items) => MembershipItems::Expressions(items),
ScalarExpr::Literal(Value::Array(array))
| ScalarExpr::TypedLiteral {
value: Value::Array(array),
..
} if array.dimensions().len() <= 1 => MembershipItems::Constants(array.elements()),
_ => return None,
};
let negated = match (binding.dispatch, operator.as_str()) {
(Some(FunctionDispatch::AnyOperator), "=") => false,
(Some(FunctionDispatch::AllOperator), "<>") if !items.is_empty() => true,
_ => return None,
};
Some(MembershipOperands {
value,
items,
negated,
})
}
_ => None,
}
}
pub(crate) fn references_current_row(
expression: &ScalarExpr,
schema: Option<&crate::RowSchema>,
) -> bool {
let mut found = false;
expression.visit(&mut |node| {
found |= match node {
ScalarExpr::Column(column) => {
schema.is_none_or(|schema| schema.resolves_local_column(None, column))
}
ScalarExpr::QualifiedColumn { qualifier, column } => {
schema.is_none_or(|schema| schema.resolves_local_column(Some(qualifier), column))
}
ScalarExpr::Position(position) => schema.is_none_or(|schema| {
schema
.slot(*position)
.is_some_and(|slot| schema.slot_is_local(slot))
}),
ScalarExpr::InternalColumn(column) => schema.is_none_or(|schema| {
schema
.internal_slot(*column)
.is_some_and(|slot| schema.slot_is_local(slot))
}),
ScalarExpr::QualifiedStar(qualifier) => schema.is_none_or(|schema| {
schema
.identities()
.iter()
.any(|identity| identity.qualifier() == Some(qualifier.as_str()))
}),
_ => false,
};
});
found
}