use std::fmt::{self, Debug};
use std::hash::Hash;
use std::mem;
use crate::ast::display::{self, AstDisplay, AstFormatter, WithOptionName};
use crate::ast::{AstInfo, Expr, Function, Ident, ShowStatement, WithOptionValue};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Query<T: AstInfo> {
pub ctes: CteBlock<T>,
pub body: SetExpr<T>,
pub order_by: Vec<OrderByExpr<T>>,
pub limit: Option<Limit<T>>,
pub offset: Option<Expr<T>>,
}
impl<T: AstInfo> AstDisplay for Query<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.ctes);
f.write_node(&self.body);
if !self.order_by.is_empty() {
f.write_str(" ORDER BY ");
f.write_node(&display::comma_separated(&self.order_by));
}
let write_offset = |f: &mut AstFormatter<W>| {
if let Some(offset) = &self.offset {
f.write_str(" OFFSET ");
f.write_node(offset);
}
};
if let Some(limit) = &self.limit {
if limit.with_ties {
write_offset(f);
f.write_str(" FETCH FIRST ");
f.write_node(&limit.quantity);
f.write_str(" ROWS WITH TIES");
} else {
f.write_str(" LIMIT ");
f.write_node(&limit.quantity);
write_offset(f);
}
} else {
write_offset(f);
}
}
}
impl_display_t!(Query);
impl<T: AstInfo> Query<T> {
pub fn select(select: Select<T>) -> Query<T> {
Query {
ctes: CteBlock::empty(),
body: SetExpr::Select(Box::new(select)),
order_by: vec![],
limit: None,
offset: None,
}
}
pub fn query(query: Query<T>) -> Query<T> {
Query {
ctes: CteBlock::empty(),
body: SetExpr::Query(Box::new(query)),
order_by: vec![],
limit: None,
offset: None,
}
}
pub fn take(&mut self) -> Query<T> {
mem::replace(
self,
Query::<T> {
ctes: CteBlock::empty(),
order_by: vec![],
body: SetExpr::Values(Values(vec![])),
limit: None,
offset: None,
},
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SetExpr<T: AstInfo> {
Select(Box<Select<T>>),
Query(Box<Query<T>>),
SetOperation {
op: SetOperator,
all: bool,
left: Box<SetExpr<T>>,
right: Box<SetExpr<T>>,
},
Values(Values<T>),
Show(ShowStatement<T>),
Table(T::ItemName),
}
impl<T: AstInfo> AstDisplay for SetExpr<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
match self {
SetExpr::Select(s) => f.write_node(s),
SetExpr::Query(q) => {
f.write_str("(");
f.write_node(q);
f.write_str(")")
}
SetExpr::Values(v) => f.write_node(v),
SetExpr::Show(v) => f.write_node(v),
SetExpr::Table(t) => {
f.write_str("TABLE ");
f.write_node(t)
}
SetExpr::SetOperation {
left,
right,
op,
all,
} => {
f.write_node(left);
f.write_str(" ");
f.write_node(op);
f.write_str(" ");
if *all {
f.write_str("ALL ");
}
f.write_node(right);
}
}
}
}
impl_display_t!(SetExpr);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SetOperator {
Union,
Except,
Intersect,
}
impl AstDisplay for SetOperator {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_str(match self {
SetOperator::Union => "UNION",
SetOperator::Except => "EXCEPT",
SetOperator::Intersect => "INTERSECT",
})
}
}
impl_display!(SetOperator);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SelectOptionName {
ExpectedGroupSize,
AggregateInputGroupSize,
DistinctOnInputGroupSize,
LimitInputGroupSize,
}
impl AstDisplay for SelectOptionName {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_str(match self {
SelectOptionName::ExpectedGroupSize => "EXPECTED GROUP SIZE",
SelectOptionName::AggregateInputGroupSize => "AGGREGATE INPUT GROUP SIZE",
SelectOptionName::DistinctOnInputGroupSize => "DISTINCT ON INPUT GROUP SIZE",
SelectOptionName::LimitInputGroupSize => "LIMIT INPUT GROUP SIZE",
})
}
}
impl_display!(SelectOptionName);
impl WithOptionName for SelectOptionName {
fn redact_value(&self) -> bool {
match self {
SelectOptionName::ExpectedGroupSize
| SelectOptionName::AggregateInputGroupSize
| SelectOptionName::DistinctOnInputGroupSize
| SelectOptionName::LimitInputGroupSize => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SelectOption<T: AstInfo> {
pub name: SelectOptionName,
pub value: Option<WithOptionValue<T>>,
}
impl_display_for_with_option!(SelectOption);
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Select<T: AstInfo> {
pub distinct: Option<Distinct<T>>,
pub projection: Vec<SelectItem<T>>,
pub from: Vec<TableWithJoins<T>>,
pub selection: Option<Expr<T>>,
pub group_by: Vec<Expr<T>>,
pub having: Option<Expr<T>>,
pub options: Vec<SelectOption<T>>,
}
impl<T: AstInfo> AstDisplay for Select<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_str("SELECT");
if let Some(distinct) = &self.distinct {
f.write_str(" ");
f.write_node(distinct);
}
if !self.projection.is_empty() {
f.write_str(" ");
f.write_node(&display::comma_separated(&self.projection));
}
if !self.from.is_empty() {
f.write_str(" FROM ");
f.write_node(&display::comma_separated(&self.from));
}
if let Some(ref selection) = self.selection {
f.write_str(" WHERE ");
f.write_node(selection);
}
if !self.group_by.is_empty() {
f.write_str(" GROUP BY ");
f.write_node(&display::comma_separated(&self.group_by));
}
if let Some(ref having) = self.having {
f.write_str(" HAVING ");
f.write_node(having);
}
if !self.options.is_empty() {
f.write_str(" OPTIONS (");
f.write_node(&display::comma_separated(&self.options));
f.write_str(")");
}
}
}
impl_display_t!(Select);
impl<T: AstInfo> Select<T> {
pub fn from(mut self, twj: TableWithJoins<T>) -> Select<T> {
self.from.push(twj);
self
}
pub fn project(mut self, select_item: SelectItem<T>) -> Select<T> {
self.projection.push(select_item);
self
}
pub fn selection(mut self, selection: Option<Expr<T>>) -> Select<T> {
self.selection = selection;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Distinct<T: AstInfo> {
EntireRow,
On(Vec<Expr<T>>),
}
impl_display_t!(Distinct);
impl<T: AstInfo> AstDisplay for Distinct<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
match self {
Distinct::EntireRow => f.write_str("DISTINCT"),
Distinct::On(cols) => {
f.write_str("DISTINCT ON (");
f.write_node(&display::comma_separated(cols));
f.write_str(")");
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CteBlock<T: AstInfo> {
Simple(Vec<Cte<T>>),
MutuallyRecursive(MutRecBlock<T>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MutRecBlock<T: AstInfo> {
pub options: Vec<MutRecBlockOption<T>>,
pub ctes: Vec<CteMutRec<T>>,
}
impl<T: AstInfo> CteBlock<T> {
pub fn empty() -> Self {
CteBlock::Simple(Vec::new())
}
pub fn is_empty(&self) -> bool {
match self {
CteBlock::Simple(list) => list.is_empty(),
CteBlock::MutuallyRecursive(list) => list.ctes.is_empty(),
}
}
pub fn bound_identifiers(&self) -> impl Iterator<Item = &Ident> {
let mut names = Vec::new();
match self {
CteBlock::Simple(list) => {
for cte in list.iter() {
names.push(&cte.alias.name);
}
}
CteBlock::MutuallyRecursive(MutRecBlock { options: _, ctes }) => {
for cte in ctes.iter() {
names.push(&cte.name);
}
}
}
names.into_iter()
}
}
impl<T: AstInfo> AstDisplay for CteBlock<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
if !self.is_empty() {
match self {
CteBlock::Simple(list) => {
f.write_str("WITH ");
f.write_node(&display::comma_separated(list));
}
CteBlock::MutuallyRecursive(MutRecBlock { options, ctes }) => {
f.write_str("WITH MUTUALLY RECURSIVE ");
if !options.is_empty() {
f.write_str("(");
f.write_node(&display::comma_separated(options));
f.write_str(") ");
}
f.write_node(&display::comma_separated(ctes));
}
}
f.write_str(" ");
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Cte<T: AstInfo> {
pub alias: TableAlias,
pub id: T::CteId,
pub query: Query<T>,
}
impl<T: AstInfo> AstDisplay for Cte<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.alias);
f.write_str(" AS (");
f.write_node(&self.query);
f.write_str(")");
}
}
impl_display_t!(Cte);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CteMutRec<T: AstInfo> {
pub name: Ident,
pub columns: Vec<CteMutRecColumnDef<T>>,
pub id: T::CteId,
pub query: Query<T>,
}
impl<T: AstInfo> AstDisplay for CteMutRec<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.name);
if !self.columns.is_empty() {
f.write_str(" (");
f.write_node(&display::comma_separated(&self.columns));
f.write_str(")");
}
f.write_str(" AS (");
f.write_node(&self.query);
f.write_str(")");
}
}
impl_display_t!(CteMutRec);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CteMutRecColumnDef<T: AstInfo> {
pub name: Ident,
pub data_type: T::DataType,
}
impl<T: AstInfo> AstDisplay for CteMutRecColumnDef<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.name);
f.write_str(" ");
f.write_node(&self.data_type);
}
}
impl_display_t!(CteMutRecColumnDef);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum MutRecBlockOptionName {
RecursionLimit,
ErrorAtRecursionLimit,
ReturnAtRecursionLimit,
}
impl AstDisplay for MutRecBlockOptionName {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_str(match self {
MutRecBlockOptionName::RecursionLimit => "RECURSION LIMIT",
MutRecBlockOptionName::ErrorAtRecursionLimit => "ERROR AT RECURSION LIMIT",
MutRecBlockOptionName::ReturnAtRecursionLimit => "RETURN AT RECURSION LIMIT",
})
}
}
impl_display!(MutRecBlockOptionName);
impl WithOptionName for MutRecBlockOptionName {
fn redact_value(&self) -> bool {
match self {
MutRecBlockOptionName::RecursionLimit
| MutRecBlockOptionName::ErrorAtRecursionLimit
| MutRecBlockOptionName::ReturnAtRecursionLimit => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct MutRecBlockOption<T: AstInfo> {
pub name: MutRecBlockOptionName,
pub value: Option<WithOptionValue<T>>,
}
impl_display_for_with_option!(MutRecBlockOption);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SelectItem<T: AstInfo> {
Expr { expr: Expr<T>, alias: Option<Ident> },
Wildcard,
}
impl<T: AstInfo> AstDisplay for SelectItem<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
match &self {
SelectItem::Expr { expr, alias } => {
f.write_node(expr);
if let Some(alias) = alias {
f.write_str(" AS ");
f.write_node(alias);
}
}
SelectItem::Wildcard => f.write_str("*"),
}
}
}
impl_display_t!(SelectItem);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TableWithJoins<T: AstInfo> {
pub relation: TableFactor<T>,
pub joins: Vec<Join<T>>,
}
impl<T: AstInfo> AstDisplay for TableWithJoins<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.relation);
for join in &self.joins {
f.write_node(join)
}
}
}
impl_display_t!(TableWithJoins);
impl<T: AstInfo> TableWithJoins<T> {
pub fn subquery(query: Query<T>, alias: TableAlias) -> TableWithJoins<T> {
TableWithJoins {
relation: TableFactor::Derived {
lateral: false,
subquery: Box::new(query),
alias: Some(alias),
},
joins: vec![],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum TableFactor<T: AstInfo> {
Table {
name: T::ItemName,
alias: Option<TableAlias>,
},
Function {
function: Function<T>,
alias: Option<TableAlias>,
with_ordinality: bool,
},
RowsFrom {
functions: Vec<Function<T>>,
alias: Option<TableAlias>,
with_ordinality: bool,
},
Derived {
lateral: bool,
subquery: Box<Query<T>>,
alias: Option<TableAlias>,
},
NestedJoin {
join: Box<TableWithJoins<T>>,
alias: Option<TableAlias>,
},
}
impl<T: AstInfo> AstDisplay for TableFactor<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
match self {
TableFactor::Table { name, alias } => {
f.write_node(name);
if let Some(alias) = alias {
f.write_str(" AS ");
f.write_node(alias);
}
}
TableFactor::Function {
function,
alias,
with_ordinality,
} => {
f.write_node(function);
if let Some(alias) = &alias {
f.write_str(" AS ");
f.write_node(alias);
}
if *with_ordinality {
f.write_str(" WITH ORDINALITY");
}
}
TableFactor::RowsFrom {
functions,
alias,
with_ordinality,
} => {
f.write_str("ROWS FROM (");
f.write_node(&display::comma_separated(functions));
f.write_str(")");
if let Some(alias) = alias {
f.write_str(" AS ");
f.write_node(alias);
}
if *with_ordinality {
f.write_str(" WITH ORDINALITY");
}
}
TableFactor::Derived {
lateral,
subquery,
alias,
} => {
if *lateral {
f.write_str("LATERAL ");
}
f.write_str("(");
f.write_node(subquery);
f.write_str(")");
if let Some(alias) = alias {
f.write_str(" AS ");
f.write_node(alias);
}
}
TableFactor::NestedJoin { join, alias } => {
f.write_str("(");
f.write_node(join);
f.write_str(")");
if let Some(alias) = alias {
f.write_str(" AS ");
f.write_node(alias);
}
}
}
}
}
impl_display_t!(TableFactor);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TableAlias {
pub name: Ident,
pub columns: Vec<Ident>,
pub strict: bool,
}
impl AstDisplay for TableAlias {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.name);
if !self.columns.is_empty() {
f.write_str(" (");
f.write_node(&display::comma_separated(&self.columns));
f.write_str(")");
}
}
}
impl_display!(TableAlias);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Join<T: AstInfo> {
pub relation: TableFactor<T>,
pub join_operator: JoinOperator<T>,
}
impl<T: AstInfo> AstDisplay for Join<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
fn prefix<T: AstInfo>(constraint: &JoinConstraint<T>) -> &'static str {
match constraint {
JoinConstraint::Natural => "NATURAL ",
_ => "",
}
}
fn suffix<'a, T: AstInfo>(constraint: &'a JoinConstraint<T>) -> impl AstDisplay + 'a {
struct Suffix<'a, T: AstInfo>(&'a JoinConstraint<T>);
impl<'a, T: AstInfo> AstDisplay for Suffix<'a, T> {
fn fmt<W>(&self, f: &mut AstFormatter<W>)
where
W: fmt::Write,
{
match self.0 {
JoinConstraint::On(expr) => {
f.write_str(" ON ");
f.write_node(expr);
}
JoinConstraint::Using { columns, alias } => {
f.write_str(" USING (");
f.write_node(&display::comma_separated(columns));
f.write_str(")");
if let Some(join_using_alias) = alias {
f.write_str(" AS ");
f.write_node(join_using_alias);
}
}
_ => {}
}
}
}
Suffix(constraint)
}
match &self.join_operator {
JoinOperator::Inner(constraint) => {
f.write_str(" ");
f.write_str(prefix(constraint));
f.write_str("JOIN ");
f.write_node(&self.relation);
f.write_node(&suffix(constraint));
}
JoinOperator::LeftOuter(constraint) => {
f.write_str(" ");
f.write_str(prefix(constraint));
f.write_str("LEFT JOIN ");
f.write_node(&self.relation);
f.write_node(&suffix(constraint));
}
JoinOperator::RightOuter(constraint) => {
f.write_str(" ");
f.write_str(prefix(constraint));
f.write_str("RIGHT JOIN ");
f.write_node(&self.relation);
f.write_node(&suffix(constraint));
}
JoinOperator::FullOuter(constraint) => {
f.write_str(" ");
f.write_str(prefix(constraint));
f.write_str("FULL JOIN ");
f.write_node(&self.relation);
f.write_node(&suffix(constraint));
}
JoinOperator::CrossJoin => {
f.write_str(" CROSS JOIN ");
f.write_node(&self.relation);
}
}
}
}
impl_display_t!(Join);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum JoinOperator<T: AstInfo> {
Inner(JoinConstraint<T>),
LeftOuter(JoinConstraint<T>),
RightOuter(JoinConstraint<T>),
FullOuter(JoinConstraint<T>),
CrossJoin,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum JoinConstraint<T: AstInfo> {
On(Expr<T>),
Using {
columns: Vec<Ident>,
alias: Option<Ident>,
},
Natural,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct OrderByExpr<T: AstInfo> {
pub expr: Expr<T>,
pub asc: Option<bool>,
pub nulls_last: Option<bool>,
}
impl<T: AstInfo> AstDisplay for OrderByExpr<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_node(&self.expr);
match self.asc {
Some(true) => f.write_str(" ASC"),
Some(false) => f.write_str(" DESC"),
None => {}
}
match self.nulls_last {
Some(true) => f.write_str(" NULLS LAST"),
Some(false) => f.write_str(" NULLS FIRST"),
None => {}
}
}
}
impl_display_t!(OrderByExpr);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Limit<T: AstInfo> {
pub with_ties: bool,
pub quantity: Expr<T>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Values<T: AstInfo>(pub Vec<Vec<Expr<T>>>);
impl<T: AstInfo> AstDisplay for Values<T> {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
f.write_str("VALUES ");
let mut delim = "";
for (i, row) in self.0.iter().enumerate() {
if f.redacted() && i == 20 {
f.write_str("/* ");
f.write_str(&(self.0.len().saturating_sub(20)).to_string());
f.write_str(" more rows */");
break;
}
f.write_str(delim);
delim = ", ";
f.write_str("(");
f.write_node(&display::comma_separated(row));
f.write_str(")");
}
}
}
impl_display_t!(Values);