pub(super) fn extract_correlation_values(
subquery: &vibesql_ast::SelectStmt,
row: &vibesql_storage::Row,
outer_schema: &crate::schema::CombinedSchema,
) -> Option<Vec<(String, vibesql_types::SqlValue)>> {
let subquery_tables = extract_table_names_from_from_clause(subquery.from.as_ref());
let mut correlation_refs = std::collections::BTreeSet::new(); collect_correlation_refs(subquery, outer_schema, &subquery_tables, &mut correlation_refs);
let mut correlation_values = Vec::new();
for (table, column) in correlation_refs {
match outer_schema.get_column_index(table.as_deref(), &column) {
Some(idx) => {
if let Some(value) = row.values.get(idx) {
let full_name = if let Some(t) = &table {
format!("{}.{}", t, column)
} else {
column.clone()
};
correlation_values.push((full_name, value.clone()));
} else {
return None; }
}
None => return None, }
}
Some(correlation_values)
}
fn extract_table_names_from_from_clause(from: Option<&vibesql_ast::FromClause>) -> Vec<String> {
let mut tables = Vec::new();
if let Some(from_clause) = from {
extract_table_names_recursive(from_clause, &mut tables);
}
tables
}
fn extract_table_names_recursive(from: &vibesql_ast::FromClause, tables: &mut Vec<String>) {
match from {
vibesql_ast::FromClause::Table { name, alias, .. } => {
tables.push(alias.clone().unwrap_or_else(|| name.clone()));
}
vibesql_ast::FromClause::Join { left, right, .. } => {
extract_table_names_recursive(left, tables);
extract_table_names_recursive(right, tables);
}
vibesql_ast::FromClause::Subquery { alias, .. } => {
tables.push(alias.clone());
}
vibesql_ast::FromClause::Values { alias, .. } => {
tables.push(alias.clone());
}
}
}
fn collect_correlation_refs(
subquery: &vibesql_ast::SelectStmt,
outer_schema: &crate::schema::CombinedSchema,
subquery_tables: &[String],
refs: &mut std::collections::BTreeSet<(Option<String>, String)>,
) {
if let Some(where_clause) = &subquery.where_clause {
collect_correlation_refs_from_expr(where_clause, outer_schema, subquery_tables, refs);
}
for item in &subquery.select_list {
if let vibesql_ast::SelectItem::Expression { expr, .. } = item {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
}
if let Some(having) = &subquery.having {
collect_correlation_refs_from_expr(having, outer_schema, subquery_tables, refs);
}
if let Some(group_by) = &subquery.group_by {
for expr in group_by.all_expressions() {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
}
if let Some(from) = &subquery.from {
collect_correlation_refs_from_from_clause(from, outer_schema, subquery_tables, refs);
}
if let Some(set_op) = &subquery.set_operation {
let mut all_subquery_tables = subquery_tables.to_vec();
extract_table_names_recursive_from_select(&set_op.right, &mut all_subquery_tables);
collect_correlation_refs(&set_op.right, outer_schema, &all_subquery_tables, refs);
}
}
fn collect_correlation_refs_from_from_clause(
from: &vibesql_ast::FromClause,
outer_schema: &crate::schema::CombinedSchema,
subquery_tables: &[String],
refs: &mut std::collections::BTreeSet<(Option<String>, String)>,
) {
match from {
vibesql_ast::FromClause::Table { .. } => {
}
vibesql_ast::FromClause::Subquery { query, alias, .. } => {
let mut nested_tables = subquery_tables.to_vec();
nested_tables.push(alias.clone());
if let Some(nested_from) = &query.from {
extract_table_names_recursive(nested_from, &mut nested_tables);
}
collect_correlation_refs(query, outer_schema, &nested_tables, refs);
}
vibesql_ast::FromClause::Join { left, right, condition, .. } => {
collect_correlation_refs_from_from_clause(left, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_from_clause(right, outer_schema, subquery_tables, refs);
if let Some(cond) = condition {
collect_correlation_refs_from_expr(cond, outer_schema, subquery_tables, refs);
}
}
vibesql_ast::FromClause::Values { rows, .. } => {
for row in rows {
for expr in row {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
}
}
}
}
fn extract_table_names_recursive_from_select(
stmt: &vibesql_ast::SelectStmt,
tables: &mut Vec<String>,
) {
if let Some(from_clause) = &stmt.from {
extract_table_names_recursive(from_clause, tables);
}
if let Some(set_op) = &stmt.set_operation {
extract_table_names_recursive_from_select(&set_op.right, tables);
}
}
fn collect_correlation_refs_from_expr(
expr: &vibesql_ast::Expression,
outer_schema: &crate::schema::CombinedSchema,
subquery_tables: &[String],
refs: &mut std::collections::BTreeSet<(Option<String>, String)>,
) {
match expr {
vibesql_ast::Expression::ColumnRef(col_id) => {
let table = col_id.table_canonical();
let column = col_id.column_canonical();
if let Some(table_name) = table {
let table_lower = table_name.to_lowercase();
if !subquery_tables.iter().any(|t| t.to_lowercase() == table_lower) {
if outer_schema.get_column_index(Some(table_name), column).is_some() {
refs.insert((Some(table_name.to_string()), column.to_string()));
}
}
} else {
let column_exists_in_subquery_tables = subquery_tables.iter().any(|t| {
outer_schema.get_column_index(Some(t), column).is_some()
});
if !column_exists_in_subquery_tables {
if outer_schema.get_column_index(None, column).is_some() {
refs.insert((None, column.to_string()));
}
}
}
}
vibesql_ast::Expression::BinaryOp { left, right, .. } => {
collect_correlation_refs_from_expr(left, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_expr(right, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::UnaryOp { expr, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Function { args, .. }
| vibesql_ast::Expression::AggregateFunction { args, .. } => {
for arg in args {
collect_correlation_refs_from_expr(arg, outer_schema, subquery_tables, refs);
}
}
vibesql_ast::Expression::IsNull { expr, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Case { operand, when_clauses, else_result } => {
if let Some(op) = operand {
collect_correlation_refs_from_expr(op, outer_schema, subquery_tables, refs);
}
for when in when_clauses {
for condition in &when.conditions {
collect_correlation_refs_from_expr(
condition,
outer_schema,
subquery_tables,
refs,
);
}
collect_correlation_refs_from_expr(
&when.result,
outer_schema,
subquery_tables,
refs,
);
}
if let Some(else_expr) = else_result {
collect_correlation_refs_from_expr(else_expr, outer_schema, subquery_tables, refs);
}
}
vibesql_ast::Expression::InList { expr, values, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
for val in values {
collect_correlation_refs_from_expr(val, outer_schema, subquery_tables, refs);
}
}
vibesql_ast::Expression::Between { expr, low, high, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_expr(low, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_expr(high, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Like { expr, pattern, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_expr(pattern, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Cast { expr, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Position { substring, string, .. } => {
collect_correlation_refs_from_expr(substring, outer_schema, subquery_tables, refs);
collect_correlation_refs_from_expr(string, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Trim { removal_char, string, .. } => {
if let Some(c) = removal_char {
collect_correlation_refs_from_expr(c, outer_schema, subquery_tables, refs);
}
collect_correlation_refs_from_expr(string, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::Interval { value, .. } => {
collect_correlation_refs_from_expr(value, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::ScalarSubquery(subq)
| vibesql_ast::Expression::Exists { subquery: subq, .. } => {
collect_correlation_refs(subq, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::In { subquery: subq, expr, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
collect_correlation_refs(subq, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::QuantifiedComparison { expr, subquery, .. } => {
collect_correlation_refs_from_expr(expr, outer_schema, subquery_tables, refs);
collect_correlation_refs(subquery, outer_schema, subquery_tables, refs);
}
vibesql_ast::Expression::WindowFunction { function, over } => {
let args = match function {
vibesql_ast::WindowFunctionSpec::Aggregate { args, .. }
| vibesql_ast::WindowFunctionSpec::Ranking { args, .. }
| vibesql_ast::WindowFunctionSpec::Value { args, .. } => args,
};
for arg in args {
collect_correlation_refs_from_expr(arg, outer_schema, subquery_tables, refs);
}
if let Some(partition_by) = &over.partition_by {
for p in partition_by {
collect_correlation_refs_from_expr(p, outer_schema, subquery_tables, refs);
}
}
if let Some(order_by) = &over.order_by {
for ob_item in order_by {
collect_correlation_refs_from_expr(
&ob_item.expr,
outer_schema,
subquery_tables,
refs,
);
}
}
}
_ => {}
}
}