use std::collections::HashSet;
use linkrs_core::types::expr::contextual::ContextualExpression;
use crate::optimizer::factorization::flatten_resolver::{FlattenAll, FlattenAllButOne};
use crate::planning::plan::factorization::{FGroupPos, FactorizationError, FactorizedSchema};
fn key_groups(keys: &[ContextualExpression], schema: &FactorizedSchema) -> HashSet<FGroupPos> {
let mut set = HashSet::new();
for k in keys {
if let Some(pos) = schema.get_group_pos(k.id()) {
set.insert(pos);
}
}
set
}
fn flatten_keys(
schema: &mut FactorizedSchema,
keys: &[ContextualExpression],
keep_one: bool,
) -> Result<(), FactorizationError> {
let groups = key_groups(keys, schema);
let to_flatten = if keep_one {
FlattenAllButOne::get_groups_pos_to_flatten_for_groups(&groups, schema)
} else {
FlattenAll::get_groups_pos_to_flatten_for_groups(&groups, schema)
};
for pos in to_flatten {
schema.flatten_group(pos)?;
}
Ok(())
}
fn merge_flattened(
left: &FactorizedSchema,
right: &FactorizedSchema,
left_key_groups: &HashSet<FGroupPos>,
right_key_groups: &HashSet<FGroupPos>,
) -> Result<FactorizedSchema, FactorizationError> {
let mut merged = left.clone();
let mapping = merged.merge_groups_from(right);
for (expr_id, gpos) in right.expression_to_group_iter() {
let new_pos = mapping.get(gpos).copied().unwrap_or(*gpos);
merged.insert_to_scope_may_repeat(expr_id.clone(), new_pos)?;
}
for pos in left_key_groups {
debug_assert!(
merged.get_group(*pos).is_none_or(|g| g.is_flat()),
"left join-key group {} must be flat before merge",
pos
);
}
for (old_pos, new_pos) in &mapping {
if right_key_groups.contains(old_pos) {
debug_assert!(
merged.get_group(*new_pos).is_none_or(|g| g.is_flat()),
"right join-key group {} must be flat before merge",
old_pos
);
}
}
if merged.has_unflat_group() {
let unflat_count = merged.groups().iter().filter(|g| !g.is_flat()).count();
if unflat_count > 1 {
let mut first = true;
for i in 0..merged.num_groups() {
let pos = i as FGroupPos;
if let Some(g) = merged.get_group(pos) {
if !g.is_flat() {
if first {
first = false;
} else {
merged.flatten_group(pos)?;
}
}
}
}
}
}
merged.validate_at_most_one_unflat()?;
Ok(merged)
}
pub(super) fn binary_join_inner(
left: &FactorizedSchema,
right: &FactorizedSchema,
hash_keys: &[ContextualExpression],
probe_keys: &[ContextualExpression],
) -> Result<FactorizedSchema, FactorizationError> {
let left_keys = key_groups(hash_keys, left);
let right_keys = key_groups(probe_keys, right);
let mut left = left.clone();
let mut right = right.clone();
flatten_keys(&mut left, hash_keys, false)?;
flatten_keys(&mut right, probe_keys, true)?;
merge_flattened(&left, &right, &left_keys, &right_keys)
}
pub(super) fn binary_join_right(
left: &FactorizedSchema,
right: &FactorizedSchema,
hash_keys: &[ContextualExpression],
probe_keys: &[ContextualExpression],
) -> Result<FactorizedSchema, FactorizationError> {
let left_keys = key_groups(hash_keys, left);
let right_keys = key_groups(probe_keys, right);
let mut left = left.clone();
let mut right = right.clone();
flatten_keys(&mut left, hash_keys, true)?;
flatten_keys(&mut right, probe_keys, false)?;
merge_flattened(&left, &right, &left_keys, &right_keys)
}
pub(super) fn binary_join_full_outer(
left: &FactorizedSchema,
right: &FactorizedSchema,
hash_keys: &[ContextualExpression],
probe_keys: &[ContextualExpression],
) -> Result<FactorizedSchema, FactorizationError> {
let left_keys = key_groups(hash_keys, left);
let right_keys = key_groups(probe_keys, right);
let mut left = left.clone();
let mut right = right.clone();
flatten_keys(&mut left, hash_keys, false)?;
flatten_keys(&mut right, probe_keys, false)?;
merge_flattened(&left, &right, &left_keys, &right_keys)
}
pub(super) fn cross_join_no_keys(
left: &FactorizedSchema,
right: &FactorizedSchema,
) -> Result<FactorizedSchema, FactorizationError> {
let empty = HashSet::new();
merge_flattened(left, right, &empty, &empty)
}