use crate::parser::ast::ReturnItem;
use linkrs_core::types::semantic::AliasType;
use linkrs_core::DBError;
use linkrs_core::DBResult;
use crate::binder::bound::*;
use crate::binder::scope::BinderVariable;
use super::Binder;
impl Binder {
pub(crate) fn bind_return(
&mut self,
stmt: &crate::parser::ast::ReturnStmt,
) -> DBResult<BoundStatement> {
let items = stmt
.items
.iter()
.map(|item| match item {
ReturnItem::Expression { expression, alias } => {
self.bind_expr(expression).map(|be| BoundProjectionItem {
expression: be,
alias: alias.clone(),
})
}
})
.collect::<DBResult<Vec<_>>>()?;
let order_by = stmt
.order_by
.as_ref()
.map(|ob| {
ob.items
.iter()
.map(|item| {
self.bind_expr(&item.expression).map(|be| {
super::super::bound::BoundOrderByItem {
expression: be,
direction: item.direction,
}
})
})
.collect::<DBResult<Vec<_>>>()
})
.transpose()?;
Ok(BoundStatement::Return(BoundReturnStatement {
items,
distinct: stmt.distinct,
order_by,
skip: stmt.skip.clone(),
limit: stmt.limit.clone(),
}))
}
pub(crate) fn bind_with(
&mut self,
stmt: &crate::parser::ast::WithStmt,
) -> DBResult<BoundStatement> {
let items = stmt
.items
.iter()
.map(|item| match item {
ReturnItem::Expression { expression, alias } => {
self.bind_expr(expression).map(|be| BoundProjectionItem {
expression: be,
alias: alias.clone(),
})
}
})
.collect::<DBResult<Vec<_>>>()?;
for item in &items {
if let Some(alias) = &item.alias {
self.scope.define_variable(BinderVariable {
name: alias.clone(),
alias_type: AliasType::Expression,
tags: Vec::new(),
properties: std::collections::HashMap::new(),
is_defined: true,
});
}
}
let condition = stmt
.where_clause
.as_ref()
.map(|c| self.bind_expr(c))
.transpose()?;
let ctes = self.bind_cte_defs(stmt)?;
Ok(BoundStatement::With(BoundWithStatement {
items,
condition,
ctes,
}))
}
pub(crate) fn bind_cte_defs(
&mut self,
stmt: &crate::parser::ast::WithStmt,
) -> DBResult<Vec<crate::binder::bound::BoundCteDef>> {
use crate::parser::ast::stmt::SetOperationType;
if stmt.recursive && stmt.ctes.is_empty() {
return Err(DBError::from(
linkrs_core::error::QueryError::invalid_query(
"WITH RECURSIVE requires at least one CTE (`name AS (anchor UNION ALL step)`)"
.to_string(),
),
));
}
if stmt.ctes.len() > 1 {
return Err(DBError::from(
linkrs_core::error::QueryError::invalid_query(
"Only a single CTE per WITH statement is supported".to_string(),
),
));
}
let mut bound = Vec::with_capacity(stmt.ctes.len());
for cte in &stmt.ctes {
if cte.name.trim().is_empty() {
return Err(DBError::from(
linkrs_core::error::QueryError::invalid_query(
"CTE name must not be empty".to_string(),
),
));
}
if stmt.recursive {
let (anchor_stmt, step_stmt) = match cte.body.as_ref() {
crate::parser::ast::Stmt::SetOperation(setop)
if setop.op_type == SetOperationType::UnionAll =>
{
(setop.left.as_ref(), setop.right.as_ref())
}
_ => {
return Err(DBError::from(
linkrs_core::error::QueryError::invalid_query(format!(
"Recursive CTE '{}' body must be `anchor UNION ALL step`",
cte.name
)),
));
}
};
let anchor = self.bind_stmt(anchor_stmt)?;
self.cte_stack.push(cte.name.clone());
let step = self.bind_stmt(step_stmt);
self.cte_stack.pop();
let step = step?;
bound.push(crate::binder::bound::BoundCteDef {
name: cte.name.clone(),
anchor: Box::new(anchor),
step: Some(Box::new(step)),
});
} else {
let anchor = self.bind_stmt(cte.body.as_ref())?;
bound.push(crate::binder::bound::BoundCteDef {
name: cte.name.clone(),
anchor: Box::new(anchor),
step: None,
});
}
}
Ok(bound)
}
pub(crate) fn bind_unwind(
&mut self,
stmt: &crate::parser::ast::UnwindStmt,
) -> DBResult<BoundStatement> {
let expr = self.bind_expr(&stmt.expression)?;
Ok(BoundStatement::Unwind(BoundUnwindStatement {
expression: expr,
alias: stmt.variable.clone(),
}))
}
}