pub mod bind_from;
pub mod bind_group_by;
pub mod bind_having;
pub mod bind_modifier;
pub mod bind_select;
pub mod bind_select_list;
pub mod bind_setop;
pub mod bind_values;
pub mod select_expr_expander;
pub mod select_list;
use bind_select::{BoundSelect, SelectBinder};
use bind_setop::{BoundSetOp, SetOpBinder};
use bind_values::{BoundValues, ValuesBinder};
use glaredb_error::{DbError, Result, not_implemented};
use glaredb_parser::ast;
use super::bind_context::{BindContext, BindScopeRef};
use super::ident::BinderIdent;
use super::table_list::TableRef;
use crate::logical::binder::bind_context::BoundCte;
use crate::logical::resolver::ResolvedMeta;
use crate::logical::resolver::resolve_context::ResolveContext;
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(clippy::large_enum_variant)] pub enum BoundQuery {
Select(BoundSelect),
Setop(BoundSetOp),
Values(BoundValues),
}
impl BoundQuery {
pub fn output_table_ref(&self) -> TableRef {
match self {
BoundQuery::Select(select) => match &select.select_list.output {
Some(pruned) => pruned.table,
None => select.select_list.projections_table,
},
Self::Setop(setop) => setop.setop_table,
BoundQuery::Values(values) => values.expressions_table,
}
}
}
#[derive(Debug)]
pub struct QueryBinder<'a> {
pub current: BindScopeRef,
pub resolve_context: &'a ResolveContext,
}
impl<'a> QueryBinder<'a> {
pub fn new(current: BindScopeRef, resolve_context: &'a ResolveContext) -> Self {
QueryBinder {
current,
resolve_context,
}
}
pub fn bind(
&self,
bind_context: &mut BindContext,
query: ast::QueryNode<ResolvedMeta>,
) -> Result<BoundQuery> {
if let Some(ctes) = query.ctes {
self.bind_ctes(bind_context, ctes)?;
}
let body = self.bind_body(bind_context, query.body, query.order_by, query.limit)?;
Ok(body)
}
pub fn bind_body(
&self,
bind_context: &mut BindContext,
body: ast::QueryNodeBody<ResolvedMeta>,
order_by: Option<ast::OrderByModifier<ResolvedMeta>>,
limit: ast::LimitModifier<ResolvedMeta>,
) -> Result<BoundQuery> {
match body {
ast::QueryNodeBody::Select(select) => {
let binder = SelectBinder::new(self.current, self.resolve_context);
let select = binder.bind(bind_context, *select, order_by, limit)?;
Ok(BoundQuery::Select(select))
}
ast::QueryNodeBody::Nested(query) => self.bind(bind_context, *query),
ast::QueryNodeBody::Values(values) => {
let binder = ValuesBinder::new(self.current, self.resolve_context);
let values = binder.bind(bind_context, values, order_by, limit)?;
Ok(BoundQuery::Values(values))
}
ast::QueryNodeBody::Set(setop) => {
let binder = SetOpBinder::new(self.current, self.resolve_context);
let setop = binder.bind(bind_context, setop, order_by, limit)?;
Ok(BoundQuery::Setop(setop))
}
}
}
fn bind_ctes(
&self,
bind_context: &mut BindContext,
ctes: ast::CommonTableExprs<ResolvedMeta>,
) -> Result<()> {
if ctes.recursive {
not_implemented!("recursive CTEs");
}
for cte in ctes.ctes {
self.bind_cte(bind_context, cte)?
}
Ok(())
}
fn bind_cte(
&self,
bind_context: &mut BindContext,
cte: ast::CommonTableExpr<ResolvedMeta>,
) -> Result<()> {
let nested = bind_context.new_child_scope(self.current);
let binder = QueryBinder::new(nested, self.resolve_context);
let bound = binder.bind(bind_context, *cte.body)?;
let mut names = Vec::new();
let mut types = Vec::new();
for table in bind_context.iter_tables_in_scope(nested)? {
types.extend(table.column_types.iter().cloned());
names.extend(table.column_names.iter().cloned());
}
if let Some(col_aliases) = &cte.column_aliases {
if col_aliases.len() > names.len() {
return Err(DbError::new(format!(
"Expected at most {} column aliases, received {}",
names.len(),
col_aliases.len()
)));
}
for (idx, col_alias) in col_aliases.iter().enumerate() {
names[idx] = BinderIdent::from(col_alias.clone());
}
}
let cte = BoundCte {
bind_scope: nested,
materialized: cte.materialized,
name: cte.alias.into(),
column_names: names,
column_types: types,
bound: Box::new(bound),
mat_ref: None,
};
bind_context.add_cte(self.current, cte)?;
Ok(())
}
}