use glaredb_error::{DbError, Result};
use glaredb_parser::ast;
use super::bind_context::{BindContext, BindScopeRef, CorrelatedColumn};
use super::expr_binder::RecursionContext;
use super::ident::BinderIdent;
use super::table_list::TableAlias;
use crate::expr::Expression;
use crate::expr::column_expr::{ColumnExpr, ColumnReference};
use crate::logical::resolver::ResolvedMeta;
use crate::util::fmt::displayable::IntoDisplayableSlice;
pub trait ExpressionColumnBinder {
fn bind_from_root_literal(
&mut self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
literal: &ast::Literal<ResolvedMeta>,
) -> Result<Option<Expression>>;
fn bind_from_ident(
&mut self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
ident: &BinderIdent,
recur: RecursionContext,
) -> Result<Option<Expression>>;
fn bind_from_idents(
&mut self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
idents: &[BinderIdent],
recur: RecursionContext,
) -> Result<Option<Expression>>;
}
#[derive(Debug, Clone, Copy)]
pub struct DefaultColumnBinder;
impl ExpressionColumnBinder for DefaultColumnBinder {
fn bind_from_root_literal(
&mut self,
_bind_scope: BindScopeRef,
_bind_context: &mut BindContext,
_literal: &ast::Literal<ResolvedMeta>,
) -> Result<Option<Expression>> {
Ok(None)
}
fn bind_from_ident(
&mut self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
ident: &BinderIdent,
_recur: RecursionContext,
) -> Result<Option<Expression>> {
self.bind_column(bind_scope, bind_context, None, ident)
}
fn bind_from_idents(
&mut self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
idents: &[BinderIdent],
_recur: RecursionContext,
) -> Result<Option<Expression>> {
let (alias, col) = idents_to_alias_and_column(idents)?;
self.bind_column(bind_scope, bind_context, alias, &col)
}
}
impl DefaultColumnBinder {
pub fn bind_column(
&self,
bind_scope: BindScopeRef,
bind_context: &mut BindContext,
alias: Option<TableAlias>,
col: &BinderIdent,
) -> Result<Option<Expression>> {
let mut current = bind_scope;
loop {
let table = bind_context.find_table_for_column(current, alias.as_ref(), col)?;
match table {
Some((table, col_idx)) => {
let is_correlated = current != bind_scope;
if is_correlated {
let correlated = CorrelatedColumn {
outer: current,
table,
col_idx,
};
bind_context.push_correlation(bind_scope, correlated)?;
}
let reference = ColumnReference {
table_scope: table,
column: col_idx,
};
let datatype = bind_context.get_column_type(reference)?;
return Ok(Some(Expression::Column(ColumnExpr {
reference,
datatype,
})));
}
None => {
match bind_context.get_parent_ref(current)? {
Some(parent) => current = parent,
None => {
return Ok(None);
}
}
}
}
}
}
}
fn idents_to_alias_and_column(idents: &[BinderIdent]) -> Result<(Option<TableAlias>, BinderIdent)> {
match idents.len() {
0 => Err(DbError::new("Empty identifier")),
1 => {
Ok((None, idents[0].clone()))
}
2..=4 => {
let mut idents = idents.to_vec();
let col = idents.pop().unwrap();
let alias = TableAlias {
table: idents.pop().unwrap(), schema: idents.pop(), database: idents.pop(), };
Ok((Some(alias), col))
}
_ => Err(DbError::new(format!(
"Too many identifier parts in {}",
idents.display_as_list(),
))), }
}
#[derive(Debug, Clone, Copy)]
pub struct ErroringColumnBinder;
impl ExpressionColumnBinder for ErroringColumnBinder {
fn bind_from_root_literal(
&mut self,
_bind_scope: BindScopeRef,
_bind_context: &mut BindContext,
_literal: &ast::Literal<ResolvedMeta>,
) -> Result<Option<Expression>> {
Ok(None)
}
fn bind_from_ident(
&mut self,
_bind_scope: BindScopeRef,
_bind_context: &mut BindContext,
_ident: &BinderIdent,
_recur: RecursionContext,
) -> Result<Option<Expression>> {
Err(DbError::new(
"Statement does not support binding to columns",
))
}
fn bind_from_idents(
&mut self,
_bind_scope: BindScopeRef,
_bind_context: &mut BindContext,
_idents: &[BinderIdent],
_recur: RecursionContext,
) -> Result<Option<Expression>> {
Err(DbError::new(
"Statement does not support binding to columns",
))
}
}