use std::collections::HashMap;
use vibesql_ast::Expression;
use super::{
table_refs::{extract_referenced_tables_branch, flatten_conjuncts},
PredicateDecomposition,
};
use crate::schema::CombinedSchema;
pub(crate) fn extract_implied_filters_from_or_predicates(
decomposition: &mut PredicateDecomposition,
schema: &CombinedSchema,
) {
for complex_pred in &decomposition.complex_predicates {
if let Some(implied_filters) = extract_table_filters_from_or(complex_pred, schema) {
for (table_name, filter_expr) in implied_filters {
decomposition
.table_local_predicates
.entry(table_name)
.or_default()
.push(filter_expr);
}
}
}
}
pub(super) fn extract_table_filters_from_or(
expr: &Expression,
schema: &CombinedSchema,
) -> Option<Vec<(String, Expression)>> {
let (left_branch, right_branch) = match expr {
Expression::BinaryOp { op: vibesql_ast::BinaryOperator::Or, left, right } => {
(left.as_ref(), right.as_ref())
}
_ => return None,
};
let left_filters = extract_table_predicates_from_branch(left_branch, schema);
let right_filters = extract_table_predicates_from_branch(right_branch, schema);
let mut result = Vec::new();
for (table_name, left_preds) in &left_filters {
if let Some(right_preds) = right_filters.get(table_name) {
let left_combined = combine_predicates_with_and(left_preds.clone());
let right_combined = combine_predicates_with_and(right_preds.clone());
let combined_filter = Expression::BinaryOp {
op: vibesql_ast::BinaryOperator::Or,
left: Box::new(left_combined),
right: Box::new(right_combined),
};
result.push((table_name.clone(), combined_filter));
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
fn extract_table_predicates_from_branch(
expr: &Expression,
schema: &CombinedSchema,
) -> HashMap<String, Vec<Expression>> {
let mut table_predicates: HashMap<String, Vec<Expression>> = HashMap::new();
let conjuncts = flatten_conjuncts(expr);
for conjunct in conjuncts {
if let Some(tables) = extract_referenced_tables_branch(&conjunct, schema) {
if tables.len() == 1 {
let table_name = tables.iter().next().unwrap().clone();
table_predicates.entry(table_name).or_default().push(conjunct);
}
}
}
table_predicates
}
pub(crate) fn combine_predicates_with_and(
mut predicates: Vec<vibesql_ast::Expression>,
) -> vibesql_ast::Expression {
if predicates.is_empty() {
vibesql_ast::Expression::Literal(vibesql_types::SqlValue::Boolean(true))
} else if predicates.len() == 1 {
predicates.pop().unwrap()
} else {
let mut result = predicates.remove(0);
for predicate in predicates {
result = vibesql_ast::Expression::BinaryOp {
op: vibesql_ast::BinaryOperator::And,
left: Box::new(result),
right: Box::new(predicate),
};
}
result
}
}