use crate::{
ast,
visitor::{self, Acceptor, Dependencies, Visited},
};
use itertools::Itertools;
use std::iter::Iterator;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
enum TableWithAlias<'a> {
Query(&'a ast::Query, Option<&'a ast::TableAlias>),
Name(&'a ast::ObjectName, Option<&'a ast::TableAlias>),
}
pub struct TableWithJoins<'a>(&'a ast::TableWithJoins);
impl<'a> TableWithJoins<'a> {
pub fn new(table_with_joins: &'a ast::TableWithJoins) -> Self {
TableWithJoins(table_with_joins)
}
fn tables_with_aliases(self) -> impl Iterator<Item = TableWithAlias<'a>> {
match &self.0.relation {
ast::TableFactor::Derived {
subquery, alias, ..
} => Some(TableWithAlias::Query(subquery.as_ref(), alias.as_ref())),
ast::TableFactor::Table { name, alias, .. } => {
Some(TableWithAlias::Name(name, alias.as_ref()))
}
_ => None,
}
.into_iter()
.chain(
self.0.joins.iter().filter_map(|join| match &join.relation {
ast::TableFactor::Derived {
subquery, alias, ..
} => Some(TableWithAlias::Query(subquery.as_ref(), alias.as_ref())),
ast::TableFactor::Table { name, alias, .. } => {
Some(TableWithAlias::Name(name, alias.as_ref()))
}
_ => None,
}),
)
}
pub fn queries(self) -> impl Iterator<Item = &'a ast::Query> {
self.tables_with_aliases().filter_map(|t| match t {
TableWithAlias::Query(q, _) => Some(q),
_ => None,
})
}
pub fn names(self) -> impl Iterator<Item = &'a ast::ObjectName> {
self.tables_with_aliases().filter_map(|t| match t {
TableWithAlias::Name(n, _) => Some(n),
_ => None,
})
}
}
fn queries_from_set_expr<'a>(set_expr: &'a ast::SetExpr) -> Vec<&'a ast::Query> {
match set_expr {
ast::SetExpr::Select(select) => select
.from
.iter()
.flat_map(|table_with_joins| TableWithJoins(table_with_joins).queries())
.collect(),
ast::SetExpr::SetOperation { .. } => vec![],
ast::SetExpr::Values(values) => todo!(),
_ => todo!(), }
}
impl<'a> Acceptor<'a> for ast::Query {
fn dependencies(&'a self) -> Dependencies<'a, Self> {
let mut dependencies = Dependencies::empty();
dependencies.extend(
self.with
.iter()
.flat_map(|with| with.cte_tables.iter().map(|cte| cte.query.as_ref())),
);
dependencies.extend(queries_from_set_expr(self.body.as_ref()));
dependencies
}
}
pub trait Visitor<'a, T: Clone> {
fn dependencies(&self, acceptor: &'a ast::Query) -> Dependencies<'a, ast::Query> {
acceptor.dependencies()
}
fn query(&self, query: &'a ast::Query, dependencies: Visited<'a, ast::Query, T>) -> T;
}
impl<'a, T: Clone, V: Visitor<'a, T>> visitor::Visitor<'a, ast::Query, T> for V {
fn visit(&self, acceptor: &'a ast::Query, dependencies: Visited<'a, ast::Query, T>) -> T {
self.query(acceptor, dependencies)
}
fn dependencies(&self, acceptor: &'a ast::Query) -> Dependencies<'a, ast::Query> {
self.dependencies(acceptor)
}
}