use crate::ast::Span;
use crate::ast::expr::{Expr, ExprKind};
use crate::catalog::{Catalog, TableMetadata};
use crate::planner::error::PlannerError;
use crate::planner::types::ResolvedType;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedColumn {
pub table_name: String,
pub column_name: String,
pub column_index: usize,
pub resolved_type: ResolvedType,
}
pub struct NameResolver<'a, C: Catalog + ?Sized> {
catalog: &'a C,
}
impl<'a, C: Catalog + ?Sized> NameResolver<'a, C> {
pub fn new(catalog: &'a C) -> Self {
Self { catalog }
}
pub fn resolve_table(&self, name: &str, span: Span) -> Result<&TableMetadata, PlannerError> {
self.catalog
.get_table(name)
.filter(|table| table.catalog_name == "default" && table.namespace_name == "default")
.ok_or_else(|| PlannerError::TableNotFound {
name: name.to_string(),
line: span.start.line,
column: span.start.column,
})
}
pub fn resolve_column<'t>(
&self,
table: &'t TableMetadata,
column: &str,
span: Span,
) -> Result<&'t crate::catalog::ColumnMetadata, PlannerError> {
table
.get_column(column)
.ok_or_else(|| PlannerError::ColumnNotFound {
column: column.to_string(),
table: table.name.clone(),
line: span.start.line,
col: span.start.column,
})
}
pub fn resolve_column_with_scope(
&self,
tables: &[&TableMetadata],
table_qualifier: Option<&str>,
column: &str,
span: Span,
) -> Result<ResolvedColumn, PlannerError> {
if let Some(qualifier) = table_qualifier {
let table = tables.iter().find(|t| t.name == qualifier).ok_or_else(|| {
PlannerError::TableNotFound {
name: qualifier.to_string(),
line: span.start.line,
column: span.start.column,
}
})?;
let column_index =
table
.get_column_index(column)
.ok_or_else(|| PlannerError::ColumnNotFound {
column: column.to_string(),
table: table.name.clone(),
line: span.start.line,
col: span.start.column,
})?;
let column_meta = &table.columns[column_index];
Ok(ResolvedColumn {
table_name: table.name.clone(),
column_name: column.to_string(),
column_index,
resolved_type: column_meta.data_type.clone(),
})
} else {
let mut matches: Vec<(&TableMetadata, usize)> = Vec::new();
for table in tables {
if let Some(idx) = table.get_column_index(column) {
matches.push((table, idx));
}
}
match matches.len() {
0 => {
let table_name = tables.first().map(|t| t.name.as_str()).unwrap_or("unknown");
Err(PlannerError::ColumnNotFound {
column: column.to_string(),
table: table_name.to_string(),
line: span.start.line,
col: span.start.column,
})
}
1 => {
let (table, column_index) = matches[0];
let column_meta = &table.columns[column_index];
Ok(ResolvedColumn {
table_name: table.name.clone(),
column_name: column.to_string(),
column_index,
resolved_type: column_meta.data_type.clone(),
})
}
_ => {
let table_names: Vec<String> =
matches.iter().map(|(t, _)| t.name.clone()).collect();
Err(PlannerError::AmbiguousColumn {
column: column.to_string(),
tables: table_names,
line: span.start.line,
col: span.start.column,
})
}
}
}
}
pub fn expand_wildcard(&self, table: &TableMetadata) -> Vec<String> {
table.column_names().into_iter().map(String::from).collect()
}
pub fn resolve_expr(&self, expr: &Expr, table: &TableMetadata) -> Result<(), PlannerError> {
self.resolve_expr_recursive(expr, table)
}
fn resolve_expr_recursive(
&self,
expr: &Expr,
table: &TableMetadata,
) -> Result<(), PlannerError> {
match &expr.kind {
ExprKind::ColumnRef {
table: table_qualifier,
column,
} => {
if let Some(qualifier) = table_qualifier
&& qualifier != &table.name
{
return Err(PlannerError::TableNotFound {
name: qualifier.clone(),
line: expr.span.start.line,
column: expr.span.start.column,
});
}
self.resolve_column(table, column, expr.span)?;
Ok(())
}
ExprKind::Literal { .. } | ExprKind::VectorLiteral { .. } => {
Ok(())
}
ExprKind::BinaryOp { left, right, .. } => {
self.resolve_expr_recursive(left, table)?;
self.resolve_expr_recursive(right, table)?;
Ok(())
}
ExprKind::UnaryOp { operand, .. } => self.resolve_expr_recursive(operand, table),
ExprKind::FunctionCall { args, .. } => {
for arg in args {
self.resolve_expr_recursive(arg, table)?;
}
Ok(())
}
ExprKind::Between {
expr: e, low, high, ..
} => {
self.resolve_expr_recursive(e, table)?;
self.resolve_expr_recursive(low, table)?;
self.resolve_expr_recursive(high, table)?;
Ok(())
}
ExprKind::Like {
expr: e,
pattern,
escape,
..
} => {
self.resolve_expr_recursive(e, table)?;
self.resolve_expr_recursive(pattern, table)?;
if let Some(esc) = escape {
self.resolve_expr_recursive(esc, table)?;
}
Ok(())
}
ExprKind::InList { expr: e, list, .. } => {
self.resolve_expr_recursive(e, table)?;
for item in list {
self.resolve_expr_recursive(item, table)?;
}
Ok(())
}
ExprKind::IsNull { expr: e, .. } => self.resolve_expr_recursive(e, table),
ExprKind::ScalarSubquery { .. }
| ExprKind::InSubquery { .. }
| ExprKind::Exists { .. }
| ExprKind::Quantified { .. } => Err(PlannerError::unsupported_feature(
"subquery name resolution",
"v0.6.0-subquery Phase 6",
expr.span,
)),
}
}
}
#[cfg(test)]
mod tests;