use vibesql_ast::{Expression, FromClause};
pub(super) fn collect_table_names(from: &FromClause, names: &mut Vec<String>) {
match from {
FromClause::Table { name, alias, .. } => {
names.push(alias.clone().unwrap_or_else(|| name.clone()));
names.push(name.clone()); }
FromClause::Join { left, right, .. } => {
collect_table_names(left, names);
collect_table_names(right, names);
}
FromClause::Subquery { alias, .. } => {
names.push(alias.clone());
}
FromClause::Values { alias, .. } => {
names.push(alias.clone());
}
}
}
pub(super) fn is_self_join(
from: &FromClause,
table_name: &str,
table_alias: &Option<String>,
) -> bool {
let mut existing_names = Vec::new();
collect_table_names(from, &mut existing_names);
let effective_name = table_alias.as_deref().unwrap_or(table_name);
existing_names
.iter()
.any(|n| n.eq_ignore_ascii_case(effective_name) || n.eq_ignore_ascii_case(table_name))
}
pub(super) fn is_simple_single_table_self_join(
from: &FromClause,
table_name: &str,
table_alias: &Option<String>,
) -> bool {
match from {
FromClause::Table { name, alias, .. } => {
let outer_effective_name = alias.as_deref().unwrap_or(name);
let subquery_effective_name = table_alias.as_deref().unwrap_or(table_name);
outer_effective_name.eq_ignore_ascii_case(subquery_effective_name)
|| name.eq_ignore_ascii_case(table_name)
}
_ => false,
}
}
pub(super) fn get_outer_table_name(from: &FromClause) -> Option<String> {
match from {
FromClause::Table { name, alias, .. } => {
Some(alias.clone().unwrap_or_else(|| name.clone()))
}
_ => None,
}
}
pub(super) fn rewrite_column_refs_with_alias(
expr: &Expression,
old_table: &str,
new_alias: &str,
) -> Expression {
match expr {
Expression::ColumnRef(col_id) => {
let should_rewrite = match col_id.table_canonical() {
None => true, Some(t) => t.eq_ignore_ascii_case(old_table),
};
if should_rewrite {
Expression::ColumnRef(vibesql_ast::ColumnIdentifier::qualified(
new_alias,
false,
col_id.column_canonical(),
false,
))
} else {
expr.clone()
}
}
Expression::BinaryOp { left, op, right } => Expression::BinaryOp {
left: Box::new(rewrite_column_refs_with_alias(left, old_table, new_alias)),
op: op.clone(),
right: Box::new(rewrite_column_refs_with_alias(right, old_table, new_alias)),
},
Expression::UnaryOp { op, expr: inner } => Expression::UnaryOp {
op: op.clone(),
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
},
Expression::IsNull { expr: inner, negated } => Expression::IsNull {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
negated: *negated,
},
Expression::Between { expr: inner, low, high, negated, symmetric } => Expression::Between {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
low: Box::new(rewrite_column_refs_with_alias(low, old_table, new_alias)),
high: Box::new(rewrite_column_refs_with_alias(high, old_table, new_alias)),
negated: *negated,
symmetric: *symmetric,
},
Expression::InList { expr: inner, values, negated } => Expression::InList {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
values: values
.iter()
.map(|v| rewrite_column_refs_with_alias(v, old_table, new_alias))
.collect(),
negated: *negated,
},
Expression::Like { expr: inner, pattern, negated, escape } => Expression::Like {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
pattern: Box::new(rewrite_column_refs_with_alias(pattern, old_table, new_alias)),
negated: *negated,
escape: escape
.as_ref()
.map(|e| Box::new(rewrite_column_refs_with_alias(e, old_table, new_alias))),
},
Expression::Function { name, args, character_unit } => Expression::Function {
name: name.clone(),
args: args
.iter()
.map(|a| rewrite_column_refs_with_alias(a, old_table, new_alias))
.collect(),
character_unit: character_unit.clone(),
},
Expression::Case { operand, when_clauses, else_result } => Expression::Case {
operand: operand
.as_ref()
.map(|o| Box::new(rewrite_column_refs_with_alias(o, old_table, new_alias))),
when_clauses: when_clauses
.iter()
.map(|case_when| vibesql_ast::CaseWhen {
conditions: case_when
.conditions
.iter()
.map(|c| rewrite_column_refs_with_alias(c, old_table, new_alias))
.collect(),
result: rewrite_column_refs_with_alias(&case_when.result, old_table, new_alias),
})
.collect(),
else_result: else_result
.as_ref()
.map(|e| Box::new(rewrite_column_refs_with_alias(e, old_table, new_alias))),
},
Expression::Cast { expr: inner, data_type } => Expression::Cast {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
data_type: data_type.clone(),
},
Expression::In { expr: inner, subquery, negated } => Expression::In {
expr: Box::new(rewrite_column_refs_with_alias(inner, old_table, new_alias)),
subquery: subquery.clone(), negated: *negated,
},
Expression::Exists { subquery, negated } => Expression::Exists {
subquery: subquery.clone(), negated: *negated,
},
Expression::ScalarSubquery(subquery) => Expression::ScalarSubquery(subquery.clone()),
_ => expr.clone(),
}
}