use std::fmt::Write;
use lutra_bin::ir;
use lutra_sql as sa;
use crate::sql::utils::{self, RelCols};
#[derive(Clone, strum::AsRefStr)]
pub enum Node {
Column {
expr: Box<sa::Expr>,
rels: Vec<sa::RelNamed>,
},
Columns {
exprs: Vec<sa::Expr>,
rels: Vec<sa::RelNamed>,
},
Rel(sa::RelNamed),
RelVar(String),
Select(sa::Select),
Query(sa::Query),
Source(String),
}
impl Node {
pub fn dummy() -> Self {
Self::RelVar(String::new())
}
pub fn as_columns(&self) -> Option<&[sa::SelectItem]> {
match self {
Node::Select(select)
if select.distinct.is_none()
&& select.from.is_empty()
&& select.having.is_none()
&& select.selection.is_none() =>
{
Some(select.projection.as_slice())
}
_ => None,
}
}
}
impl From<sa::Expr> for Node {
fn from(expr: sa::Expr) -> Self {
Node::Column {
expr: Box::new(expr),
rels: vec![],
}
}
}
impl std::fmt::Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_ref())?;
f.write_str("(")?;
fn fmt_rels(
f: &mut std::fmt::Formatter<'_>,
rels: &Vec<sa::RelNamed>,
) -> Result<(), std::fmt::Error> {
for rvar in rels {
f.write_str("WITH {")?;
f.write_str(&rvar.to_string())?;
f.write_str("}")?;
if f.alternate() {
f.write_char('\n')?;
} else {
f.write_char(' ')?;
}
}
Ok(())
}
match self {
Node::Column { expr, rels } => {
fmt_rels(f, rels)?;
f.write_str(&expr.to_string())?;
}
Node::Columns { exprs, rels } => {
fmt_rels(f, rels)?;
f.write_char('[')?;
for e in exprs {
f.write_str(&e.to_string())?;
if f.alternate() {
f.write_str(",\n")?;
} else {
f.write_str(", ")?;
}
}
f.write_char(']')?;
}
Node::Rel(rel_var) => f.write_str(&rel_var.to_string())?,
Node::RelVar(rel_var) => f.write_str(rel_var)?,
Node::Select(select) => f.write_str(&select.to_string())?,
Node::Query(query) => f.write_str(&query.to_string())?,
Node::Source(source) => f.write_str(source)?,
}
f.write_char(')')
}
}
impl<'a> crate::sql::queries::Context<'a> {
pub fn query_into_rel(&mut self, query: sa::Query) -> sa::RelNamed {
let rel_var = self.rel_name_gen.next();
sa::RelExpr::subquery(query).alias(utils::new_ident(rel_var))
}
pub fn rel_into_select(&mut self, rel: sa::RelNamed, ty: &ir::Ty) -> sa::Select {
let rel_var = utils::get_rel_alias(&rel);
let mut select = utils::select_empty();
select.projection = self.projection_noop(rel_var, ty);
select.from = vec![rel];
select
}
pub fn rel_var_into_columns(&mut self, rel_var: Option<&str>, ty: &ir::Ty) -> Vec<sa::Expr> {
self.rel_cols(ty)
.map(|c_name| utils::identifier(rel_var, c_name))
.collect()
}
pub fn rel_var_into_select(&mut self, rel_var: &str, ty: &ir::Ty) -> sa::Select {
let mut select = utils::select_empty();
select.projection = self.projection_noop(Some(rel_var), ty);
select
}
pub fn wrap_node(&mut self, rel: Node, rel_ty: &ir::Ty) -> Node {
let select = self.node_into_select(rel, rel_ty);
Node::Query(utils::query_select(select))
}
pub fn wrap_query(&mut self, query: sa::Query, ty: &ir::Ty) -> sa::Query {
let rel = self.query_into_rel(query);
let select = self.rel_into_select(rel, ty);
utils::query_select(select)
}
pub fn node_into_column_and_rels(
&mut self,
scoped: Node,
ty: &ir::Ty,
) -> (sa::Expr, Vec<sa::RelNamed>) {
match scoped {
Node::Column {
expr,
rels: rel_vars,
} => (*expr, rel_vars),
Node::Columns {
exprs,
rels: rel_vars,
} if exprs.len() == 1 => (exprs.into_iter().next().unwrap(), rel_vars),
Node::Source(source) => (sa::Expr::Source(source), vec![]),
_ => {
let rel = self.node_into_rel(scoped, ty);
let rel_var = utils::get_rel_alias(&rel).unwrap();
let c_name = self.rel_cols(ty).next().unwrap();
let expr = utils::identifier(Some(rel_var), c_name);
(expr, vec![rel])
}
}
}
pub fn node_into_column(&mut self, scoped: Node, ty: &ir::Ty) -> sa::Expr {
match scoped {
Node::Column { expr, rels } if rels.is_empty() => *expr,
Node::Columns { exprs, rels } if exprs.len() == 1 && rels.is_empty() => {
exprs.into_iter().next().unwrap()
}
Node::Source(source) => sa::Expr::Source(source),
Node::RelVar(ref rel_name) => {
let cols = self.rel_var_into_columns(Some(rel_name), ty);
if cols.len() == 1 {
cols.into_iter().next().unwrap()
} else {
sa::Expr::Subquery(Box::new(self.node_into_query(scoped, ty)))
}
}
_ => sa::Expr::Subquery(Box::new(self.node_into_query(scoped, ty))),
}
}
pub fn node_into_columns(&mut self, scoped: Node, ty: &ir::Ty) -> Vec<sa::Expr> {
match scoped {
Node::Column { expr, rels } if rels.is_empty() => vec![*expr],
Node::Columns { exprs, rels } if rels.is_empty() => exprs,
Node::Source(source) => vec![sa::Expr::Source(source)],
Node::RelVar(rel_name) => self.rel_var_into_columns(Some(&rel_name), ty),
_ => {
vec![sa::Expr::Subquery(Box::new(
self.node_into_query(scoped, ty),
))]
}
}
}
pub fn node_into_columns_and_rels(
&mut self,
scoped: Node,
ty: &ir::Ty,
) -> (Vec<sa::Expr>, Vec<sa::RelNamed>) {
match scoped {
Node::Column { expr, rels } => (vec![*expr], rels),
Node::Columns { exprs, rels } => (exprs, rels),
Node::Source(source) => (vec![sa::Expr::Source(source)], vec![]),
Node::RelVar(rel_name) => (self.rel_var_into_columns(Some(&rel_name), ty), vec![]),
_ => {
let rel = self.node_into_rel(scoped, ty);
let rel_var = utils::get_rel_alias(&rel);
(self.rel_var_into_columns(rel_var, ty), vec![rel])
}
}
}
pub fn node_into_rel(&mut self, node: Node, ty: &ir::Ty) -> sa::RelNamed {
match node {
Node::Rel(rel) => rel,
Node::Query(query) => self.query_into_rel(query),
Node::Source(source) => {
self.query_into_rel(utils::query_new(sa::SetExpr::Source(source)))
}
_ => {
let rel_name = self.rel_name_gen.next();
let select = self.node_into_select(node, ty);
sa::RelExpr::subquery(utils::query_select(select)).alias(utils::new_ident(rel_name))
}
}
}
pub fn node_into_rel_var(&mut self, node: Node, ty: &ir::Ty) -> (String, Option<sa::RelNamed>) {
match node {
Node::RelVar(name) => (name, None),
_ => {
let mut rel = self.node_into_rel(node, ty);
if rel.alias.is_none() {
rel.alias = Some(utils::new_table_alias(self.rel_name_gen.next()));
}
let name = utils::get_rel_alias(&rel).unwrap().to_string();
(name, Some(rel))
}
}
}
pub fn node_into_select(&mut self, node: Node, ty: &ir::Ty) -> sa::Select {
match node {
Node::Column {
expr,
rels: rel_vars,
} => {
let mut select = utils::select_empty();
select.projection = self.projection(ty, [*expr]);
select.from = rel_vars;
select
}
Node::Columns {
exprs,
rels: rel_vars,
} => {
let mut select = utils::select_empty();
select.projection = self.projection(ty, exprs);
select.from = rel_vars;
select
}
Node::Rel(rel) => self.rel_into_select(rel, ty),
Node::RelVar(rel_var) => self.rel_var_into_select(&rel_var, ty),
Node::Select(select) => select,
Node::Query(query) => {
if query.limit.is_none()
&& query.offset.is_none()
&& query.order_by.is_none()
&& query.with.is_none()
&& let sa::SetExpr::Select(select) = *query.body
{
return *select;
}
let rel = self.query_into_rel(query);
self.rel_into_select(rel, ty)
}
Node::Source(_) => {
let rel = self.node_into_rel(node, ty);
self.rel_into_select(rel, ty)
}
}
}
pub fn node_into_query(&mut self, node: Node, ty: &ir::Ty) -> sa::Query {
match node {
Node::Query(query) => query,
_ => utils::query_select(self.node_into_select(node, ty)),
}
}
}