mod classification;
mod or_conditions;
mod table_refs;
#[cfg(test)]
mod tests;
use std::collections::{HashMap, HashSet};
pub(crate) use classification::classify_predicate_branch;
pub(crate) use or_conditions::{
combine_predicates_with_and, extract_implied_filters_from_or_predicates,
};
pub(crate) use table_refs::{extract_referenced_tables_branch, flatten_conjuncts};
use vibesql_ast::Expression;
use crate::schema::CombinedSchema;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum PredicateCategory {
TableLocal {
table_name: String,
predicate: vibesql_ast::Expression,
},
EquiJoin {
left_table: String,
left_column: String,
right_table: String,
right_column: String,
predicate: vibesql_ast::Expression,
},
Complex {
predicate: vibesql_ast::Expression,
referenced_tables: HashSet<String>,
},
}
#[derive(Debug, Clone)]
pub struct PredicateDecomposition {
pub table_local_predicates: HashMap<String, Vec<vibesql_ast::Expression>>,
pub equijoin_conditions: Vec<(String, String, String, String, vibesql_ast::Expression)>,
pub complex_predicates: Vec<vibesql_ast::Expression>,
pub always_false: bool,
}
impl PredicateDecomposition {
pub fn empty() -> Self {
Self {
table_local_predicates: HashMap::new(),
equijoin_conditions: Vec::new(),
complex_predicates: Vec::new(),
always_false: false,
}
}
pub fn is_empty(&self) -> bool {
self.table_local_predicates.is_empty()
&& self.equijoin_conditions.is_empty()
&& self.complex_predicates.is_empty()
}
#[allow(dead_code)]
pub fn rebuild_where_clause(&self) -> Option<vibesql_ast::Expression> {
let mut all_predicates = Vec::new();
for predicates in self.table_local_predicates.values() {
all_predicates.extend(predicates.clone());
}
for (_, _, _, _, expr) in &self.equijoin_conditions {
all_predicates.push(expr.clone());
}
all_predicates.extend(self.complex_predicates.clone());
if all_predicates.is_empty() {
return None;
}
Some(combine_predicates_with_and(all_predicates))
}
}
pub fn decompose_where_clause(
where_expr: Option<&vibesql_ast::Expression>,
from_schema: &CombinedSchema,
) -> Result<PredicateDecomposition, String> {
let mut decomposition = PredicateDecomposition::empty();
let Some(expr) = where_expr else {
return Ok(decomposition);
};
let conjuncts = flatten_conjuncts(expr);
for conjunct in conjuncts {
classify_predicate_branch(&conjunct, from_schema, &mut decomposition)?;
}
extract_implied_filters_from_or_predicates(&mut decomposition, from_schema);
Ok(decomposition)
}
pub fn combine_with_and(expressions: Vec<Expression>) -> Option<Expression> {
match expressions.len() {
0 => None,
_ => Some(combine_predicates_with_and(expressions)),
}
}