use vibesql_ast::{FromClause, IndexHint, SelectStmt};
use crate::errors::ExecutorError;
pub fn validate_index_hints(
stmt: &SelectStmt,
database: &vibesql_storage::Database,
) -> Result<(), ExecutorError> {
validate_select(stmt, &[], database)
}
fn validate_select(
stmt: &SelectStmt,
outer_cte_names: &[String],
database: &vibesql_storage::Database,
) -> Result<(), ExecutorError> {
let mut cte_names: Vec<String> = outer_cte_names.to_vec();
if let Some(ctes) = &stmt.with_clause {
for cte in ctes {
cte_names.push(cte.name.to_lowercase());
validate_select(&cte.query, &cte_names, database)?;
}
}
if let Some(from) = &stmt.from {
validate_from_clause(from, &cte_names, database)?;
}
if let Some(set_op) = &stmt.set_operation {
validate_select(&set_op.right, &cte_names, database)?;
}
Ok(())
}
fn validate_from_clause(
from: &FromClause,
cte_names: &[String],
database: &vibesql_storage::Database,
) -> Result<(), ExecutorError> {
match from {
FromClause::Table { name, index_hint, .. } => {
if let Some(IndexHint::IndexedBy(index_name)) = index_hint {
validate_indexed_by(name, index_name, cte_names, database)?;
}
Ok(())
}
FromClause::Join { left, right, .. } => {
validate_from_clause(left, cte_names, database)?;
validate_from_clause(right, cte_names, database)
}
FromClause::Subquery { query, .. } => validate_select(query, cte_names, database),
FromClause::Values { .. } => Ok(()),
}
}
fn validate_indexed_by(
table_name: &str,
index_name: &str,
cte_names: &[String],
database: &vibesql_storage::Database,
) -> Result<(), ExecutorError> {
let no_such_index = || ExecutorError::NoSuchIndex { index_name: index_name.to_string() };
let table_lower = table_name.to_lowercase();
if cte_names.contains(&table_lower) {
return Err(no_such_index());
}
let metadata = database.get_index(index_name).ok_or_else(no_such_index)?;
let index_table_lower = metadata.table_name.to_lowercase();
let unqualified_lower = table_lower.rsplit('.').next().unwrap_or(&table_lower);
if index_table_lower != table_lower && index_table_lower != unqualified_lower {
return Err(no_such_index());
}
Ok(())
}