use inillucent_value::Collation;
use super::{path_ordering, walk_key_of, Levers, OrderedBy, PathOrdering, PlannedSource};
use crate::ast::JoinKind;
use crate::bind::BoundSelect;
pub(super) fn walk_keeps_rows_together(
select: &BoundSelect,
sources: &[PlannedSource],
levers: Levers,
) -> bool {
let joined_in_order = sources.iter().skip(1).all(|term| {
matches!(
term.join,
JoinKind::Inner | JoinKind::Comma | JoinKind::Cross | JoinKind::Left
)
});
levers.has(Levers::STREAMING_GROUP)
&& joined_in_order
&& select.windows.is_empty()
&& select.compounds.is_empty()
}
pub(super) fn grouped_by_walk(select: &BoundSelect, outer: &PlannedSource) -> bool {
if select.group_by.is_empty() {
return false;
}
let Some(key) = path_ordering(&outer.table, &outer.path) else {
return false;
};
let mut wanted: Vec<(OrderedBy, Collation)> = Vec::new();
for expr in &select.group_by {
let Some(named) = walk_key_of(expr, outer.id, &outer.table) else {
return false;
};
let collation = crate::bind::result_collation(expr);
if !wanted.iter().any(|(held, _)| *held == named) {
wanted.push((named, collation));
}
}
covers_prefix(&key, &wanted)
}
pub(super) fn distinct_by_walk(select: &BoundSelect, outer: &PlannedSource) -> bool {
if !select.distinct || !select.group_by.is_empty() || !select.aggregates.is_empty() {
return false;
}
let Some(key) = path_ordering(&outer.table, &outer.path) else {
return false;
};
let mut wanted: Vec<(OrderedBy, Collation)> = Vec::new();
for column in &select.columns {
let Some(named) = walk_key_of(&column.expr, outer.id, &outer.table) else {
return false;
};
let collation = crate::bind::result_collation(&column.expr);
if !wanted.iter().any(|(held, _)| *held == named) {
wanted.push((named, collation));
}
}
covers_prefix(&key, &wanted)
}
fn covers_prefix(key: &PathOrdering, wanted: &[(OrderedBy, Collation)]) -> bool {
let free: Vec<&(OrderedBy, Collation)> = wanted
.iter()
.filter(|(named, _)| !key.pinned.contains(named))
.collect();
if free.len() > key.columns.len() {
return false;
}
let prefix = match key.columns.get(..free.len()) {
Some(prefix) => prefix,
None => return false,
};
free.iter().all(|(named, collation)| {
prefix
.iter()
.any(|(held, _, held_collation)| held == named && held_collation == collation)
})
}