use std::collections::HashSet;
use crate::optimizer::{
combine_with_and,
where_pushdown::{extract_referenced_tables_branch, flatten_conjuncts},
};
use super::predicates::{extract_table_names_from_from_clause, predicate_references_only_tables};
pub(super) fn extract_right_only_predicates(
right_from: &vibesql_ast::FromClause,
condition: &vibesql_ast::Expression,
database: &vibesql_storage::Database,
) -> Option<vibesql_ast::Expression> {
let right_tables = extract_table_names_from_from_clause(right_from);
if right_tables.is_empty() {
return None;
}
let mut right_table_set: HashSet<String> = HashSet::new();
let mut schema_builder = crate::schema::SchemaBuilder::new();
for table_name in &right_tables {
let normalized = table_name.to_lowercase();
if let Some(table) = database.get_table(&normalized) {
schema_builder.add_table(table_name.clone(), table.schema.clone());
right_table_set.insert(table_name.clone());
right_table_set.insert(normalized);
}
}
if right_table_set.is_empty() {
return None;
}
let right_schema = schema_builder.build();
let conjuncts = flatten_conjuncts(condition);
let right_only_predicates: Vec<vibesql_ast::Expression> = conjuncts
.into_iter()
.filter(|pred| {
match extract_referenced_tables_branch(pred, &right_schema) {
Some(ref tables) if !tables.is_empty() => tables.iter().all(|t| {
let t_lower = t.to_lowercase();
right_table_set.contains(t) || right_table_set.contains(&t_lower)
}),
_ => false,
}
})
.collect();
if right_only_predicates.is_empty() {
return None;
}
combine_with_and(right_only_predicates)
}
pub(super) fn filter_out_nullable_side_predicates(
nullable_side_from: &vibesql_ast::FromClause,
where_expr: &vibesql_ast::Expression,
database: &vibesql_storage::Database,
) -> Option<vibesql_ast::Expression> {
let nullable_tables = extract_table_names_from_from_clause(nullable_side_from);
if nullable_tables.is_empty() {
return Some(where_expr.clone());
}
let mut nullable_table_set: HashSet<String> = HashSet::new();
let mut schema_builder = crate::schema::SchemaBuilder::new();
for table_name in &nullable_tables {
let normalized = table_name.to_lowercase();
if let Some(table) = database.get_table(&normalized) {
schema_builder.add_table(table_name.clone(), table.schema.clone());
nullable_table_set.insert(table_name.clone());
nullable_table_set.insert(normalized);
} else {
nullable_table_set.insert(table_name.clone());
nullable_table_set.insert(normalized);
}
}
let nullable_schema = schema_builder.build();
let conjuncts = flatten_conjuncts(where_expr);
let kept_predicates: Vec<vibesql_ast::Expression> = conjuncts
.into_iter()
.filter(|pred| {
match extract_referenced_tables_branch(pred, &nullable_schema) {
Some(ref tables) if !tables.is_empty() => {
let all_nullable = tables.iter().all(|t| {
let t_lower = t.to_lowercase();
nullable_table_set.contains(t) || nullable_table_set.contains(&t_lower)
});
!all_nullable
}
Some(_) => true, None => {
!predicate_references_only_tables(pred, &nullable_table_set)
}
}
})
.collect();
combine_with_and(kept_predicates)
}