use std::collections::{HashMap, HashSet};
use vibesql_ast::{BinaryOperator, Expression, FromClause};
use super::select_analysis::{
collect_unqualified_columns_from_expr_single, extract_tables_from_expr,
has_unqualified_column_ref,
};
pub(super) fn extract_equijoin_tables_from_joins(
from: &FromClause,
table_names: &HashSet<String>,
table_prefixes: &HashMap<String, String>,
) -> HashSet<String> {
let mut tables = HashSet::new();
extract_join_on_tables(from, &mut tables, table_names, table_prefixes);
tables
}
fn extract_join_on_tables(
from: &FromClause,
tables: &mut HashSet<String>,
table_names: &HashSet<String>,
table_prefixes: &HashMap<String, String>,
) {
match from {
FromClause::Table { .. } => {
}
FromClause::Join { left, right, condition, .. } => {
extract_join_on_tables(left, tables, table_names, table_prefixes);
extract_join_on_tables(right, tables, table_names, table_prefixes);
if let Some(cond) = condition {
let on_tables = extract_equijoin_tables(cond, table_names, table_prefixes);
tables.extend(on_tables);
}
}
FromClause::Subquery { .. } => {
}
FromClause::Values { .. } => {
}
}
}
pub(super) fn extract_equijoin_tables(
expr: &Expression,
table_names: &HashSet<String>,
table_prefixes: &HashMap<String, String>,
) -> HashSet<String> {
let mut tables = HashSet::new();
find_equijoin_tables(expr, &mut tables, table_names, table_prefixes);
tables
}
fn find_equijoin_tables(
expr: &Expression,
tables: &mut HashSet<String>,
_table_names: &HashSet<String>,
table_prefixes: &HashMap<String, String>,
) {
match expr {
Expression::BinaryOp { op: BinaryOperator::And, left, right } => {
find_equijoin_tables(left, tables, _table_names, table_prefixes);
find_equijoin_tables(right, tables, _table_names, table_prefixes);
}
Expression::BinaryOp { op: BinaryOperator::Or, left, right } => {
find_equijoin_tables(left, tables, _table_names, table_prefixes);
find_equijoin_tables(right, tables, _table_names, table_prefixes);
}
Expression::BinaryOp { op: BinaryOperator::Equal, left, right } => {
let mut left_tables = HashSet::new();
let mut right_tables = HashSet::new();
extract_tables_from_expr(left, &mut left_tables);
extract_tables_from_expr(right, &mut right_tables);
let left_has_unqualified = has_unqualified_column_ref(left);
let right_has_unqualified = has_unqualified_column_ref(right);
if left_has_unqualified && left_tables.is_empty() {
let left_cols = collect_unqualified_columns_from_expr_single(left);
for col in left_cols {
let col_lower = col.to_lowercase();
for (table, prefix) in table_prefixes {
if col_lower.starts_with(prefix) {
left_tables.insert(table.clone());
}
}
}
}
if right_has_unqualified && right_tables.is_empty() {
let right_cols = collect_unqualified_columns_from_expr_single(right);
for col in right_cols {
let col_lower = col.to_lowercase();
for (table, prefix) in table_prefixes {
if col_lower.starts_with(prefix) {
right_tables.insert(table.clone());
}
}
}
}
if !left_tables.is_empty()
&& !right_tables.is_empty()
&& left_tables.is_disjoint(&right_tables)
{
tables.extend(left_tables);
tables.extend(right_tables);
}
}
_ => {}
}
}