#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use super::display_utils::{SpaceOrNewline, indented_list};
use crate::dml::{MergeClause, OutputClause, Update};
use crate::*;
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Query {
pub with: Option<With>,
pub body: Box<SetExpr>,
pub order_by: Option<OrderBy>,
pub limit: Option<Expr>,
pub offset: Option<Expr>,
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref with) = self.with {
with.fmt(f)?;
SpaceOrNewline.fmt(f)?;
}
self.body.fmt(f)?;
if let Some(ref order_by) = self.order_by {
SpaceOrNewline.fmt(f)?;
order_by.fmt(f)?;
}
if let Some(ref offset) = self.offset {
SpaceOrNewline.fmt(f)?;
f.write_str("OFFSET ")?;
offset.fmt(f)?;
}
if let Some(ref limit) = self.limit {
SpaceOrNewline.fmt(f)?;
f.write_str("LIMIT ")?;
limit.fmt(f)?;
}
Ok(())
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum SetExpr {
Select(Box<Select>),
Query(Box<Query>),
SetOperation {
op: SetOperator,
set_quantifier: SetQuantifier,
left: Box<SetExpr>,
right: Box<SetExpr>,
},
Values(Values),
Insert(Insert),
Update(Update),
Delete(Delete),
Merge {
into: bool,
table: RelNamed,
source: RelNamed,
on: Box<Expr>,
clauses: Vec<MergeClause>,
output: Option<OutputClause>,
},
Source(String),
Copy(Box<Copy>),
}
impl SetExpr {
pub fn as_select(&self) -> Option<&Select> {
if let Self::Select(select) = self {
Some(&**select)
} else {
None
}
}
}
impl fmt::Display for SetExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SetExpr::Select(s) => s.fmt(f),
SetExpr::Query(q) => {
f.write_str("(")?;
q.fmt(f)?;
f.write_str(")")
}
SetExpr::Values(v) => v.fmt(f),
SetExpr::Insert(v) => v.fmt(f),
SetExpr::Update(v) => v.fmt(f),
SetExpr::Delete(v) => v.fmt(f),
SetExpr::Merge {
into,
table,
source,
on,
clauses,
output,
} => {
write!(
f,
"MERGE{int} {table} USING {source} ",
int = if *into { " INTO" } else { "" }
)?;
write!(f, "ON {on} ")?;
write!(f, "{}", display_separated(clauses, " "))?;
if let Some(output) = output {
write!(f, " {output}")?;
}
Ok(())
}
SetExpr::SetOperation {
left,
right,
op,
set_quantifier,
} => {
Indent(left).fmt(f)?;
SpaceOrNewline.fmt(f)?;
op.fmt(f)?;
f.write_str(" ")?;
set_quantifier.fmt(f)?;
SpaceOrNewline.fmt(f)?;
Indent(right).fmt(f)?;
Ok(())
}
SetExpr::Source(s) => f.write_str(s),
SetExpr::Copy(c) => c.fmt(f),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Copy {
pub source: SetExpr,
pub target: Expr,
pub options: String,
}
impl fmt::Display for Copy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("COPY(")?;
self.source.fmt(f)?;
f.write_str(") TO ")?;
self.target.fmt(f)?;
f.write_str(" (")?;
f.write_str(&self.options)?;
f.write_str(")")?;
Ok(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum SetOperator {
Union,
Except,
Intersect,
Minus,
}
impl fmt::Display for SetOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
SetOperator::Union => "UNION",
SetOperator::Except => "EXCEPT",
SetOperator::Intersect => "INTERSECT",
SetOperator::Minus => "MINUS",
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum SetQuantifier {
All,
Distinct,
ByName,
AllByName,
DistinctByName,
}
impl fmt::Display for SetQuantifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SetQuantifier::All => write!(f, "ALL"),
SetQuantifier::Distinct => write!(f, "DISTINCT"),
SetQuantifier::ByName => write!(f, "BY NAME"),
SetQuantifier::AllByName => write!(f, "ALL BY NAME"),
SetQuantifier::DistinctByName => write!(f, "DISTINCT BY NAME"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Select {
pub from: Vec<RelNamed>,
pub selection: Option<Expr>,
pub group_by: Vec<Expr>,
pub distinct: Option<Distinct>,
pub projection: Vec<SelectItem>,
pub having: Option<Expr>,
}
impl fmt::Display for Select {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut is_first = true;
fn space_or_nl(f: &mut fmt::Formatter<'_>, is_first: &mut bool) -> fmt::Result {
if *is_first {
*is_first = false;
return Ok(());
}
SpaceOrNewline.fmt(f)
}
if !f.alternate() {
space_or_nl(f, &mut is_first)?;
f.write_str("SELECT")?;
if let Some(ref distinct) = self.distinct {
f.write_str(" ")?;
distinct.fmt(f)?;
}
if !self.projection.is_empty() {
indented_list(f, &self.projection)?;
}
}
if !self.from.is_empty() {
space_or_nl(f, &mut is_first)?;
f.write_str("FROM")?;
indented_list(f, &self.from)?;
}
if let Some(ref selection) = self.selection {
space_or_nl(f, &mut is_first)?;
f.write_str("WHERE ")?;
selection.fmt(f)?;
}
if !self.group_by.is_empty() {
space_or_nl(f, &mut is_first)?;
f.write_str("GROUP BY ")?;
display_comma_separated(&self.group_by).fmt(f)?;
}
if f.alternate() {
space_or_nl(f, &mut is_first)?;
write!(f, "SELECT")?;
if let Some(ref distinct) = self.distinct {
f.write_str(" ")?;
distinct.fmt(f)?;
}
if !self.projection.is_empty() {
indented_list(f, &self.projection)?;
}
}
if let Some(ref having) = self.having {
space_or_nl(f, &mut is_first)?;
f.write_str("HAVING")?;
Indent(having).fmt(f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct LateralView {
pub lateral_view: Expr,
pub lateral_view_name: ObjectName,
pub lateral_col_alias: Vec<Ident>,
pub outer: bool,
}
impl fmt::Display for LateralView {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
" LATERAL VIEW{outer} {} {}",
self.lateral_view,
self.lateral_view_name,
outer = if self.outer { " OUTER" } else { "" }
)?;
if !self.lateral_col_alias.is_empty() {
write!(
f,
" AS {}",
display_comma_separated(&self.lateral_col_alias)
)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct With {
pub recursive: bool,
pub cte_tables: Vec<Cte>,
}
impl fmt::Display for With {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("WITH ")?;
if self.recursive {
f.write_str("RECURSIVE ")?;
}
display_comma_separated(&self.cte_tables).fmt(f)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum CteAsMaterialized {
Materialized,
NotMaterialized,
}
impl fmt::Display for CteAsMaterialized {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CteAsMaterialized::Materialized => {
write!(f, "MATERIALIZED")?;
}
CteAsMaterialized::NotMaterialized => {
write!(f, "NOT MATERIALIZED")?;
}
};
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Cte {
pub alias: TableAlias,
pub query: Box<Query>,
pub materialized: Option<CteAsMaterialized>,
}
impl fmt::Display for Cte {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.alias.fmt(f)?;
f.write_str(" AS")?;
if let Some(materialized) = self.materialized.as_ref() {
f.write_str(" ")?;
materialized.fmt(f)?;
};
f.write_str(" (")?;
NewLine.fmt(f)?;
Indent(&self.query).fmt(f)?;
NewLine.fmt(f)?;
f.write_str(")")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct SelectItem {
pub expr: Expr,
pub alias: Option<Ident>,
}
impl SelectItem {
pub fn unnamed(expr: Expr) -> Self {
SelectItem { expr, alias: None }
}
}
impl fmt::Display for SelectItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate()
&& let Some(alias) = &self.alias
{
write!(f, "{:05} = ", &alias.value)?;
}
self.expr.fmt(f)?;
if !f.alternate()
&& let Some(alias) = &self.alias
{
f.write_str(" AS ")?;
alias.fmt(f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct ExprWithAlias {
pub expr: Expr,
pub alias: Option<Ident>,
}
impl fmt::Display for ExprWithAlias {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ExprWithAlias { expr, alias } = self;
write!(f, "{expr}")?;
if let Some(alias) = alias {
write!(f, " AS {alias}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct RelNamed {
pub lateral: bool,
pub alias: Option<TableAlias>,
pub expr: RelExpr,
}
impl RelNamed {
pub fn unnamed(expr: RelExpr) -> Self {
Self {
expr,
alias: None,
lateral: false,
}
}
}
impl fmt::Display for RelNamed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.lateral && !matches!(self.expr, RelExpr::Table(..)) {
write!(f, "LATERAL ")?;
}
if f.alternate()
&& let Some(alias) = &self.alias
{
write!(f, "{alias} = ")?;
}
self.expr.fmt(f)?;
if !f.alternate()
&& let Some(alias) = &self.alias
{
write!(f, " AS {alias}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum RelExpr {
Table(ObjectName),
Subquery(Box<Query>),
Function {
name: ObjectName,
args: Vec<Expr>,
ordinality: bool,
},
}
impl RelExpr {
pub fn unnamed(self) -> RelNamed {
RelNamed::unnamed(self)
}
pub fn alias(self, alias: Ident) -> RelNamed {
RelNamed {
expr: self,
alias: Some(TableAlias::simple(alias)),
lateral: false,
}
}
pub fn alias_cols(self, alias: Ident, cols: Vec<Ident>) -> RelNamed {
RelNamed {
expr: self,
alias: Some(TableAlias::new(alias, cols)),
lateral: false,
}
}
pub fn subquery(query: Query) -> Self {
RelExpr::Subquery(Box::new(query))
}
pub fn function(name: Ident, args: Vec<Expr>) -> Self {
RelExpr::Function {
name: ObjectName(vec![name]),
args: args.into_iter().collect(),
ordinality: false,
}
}
}
impl fmt::Display for RelExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RelExpr::Table(name) => {
name.fmt(f)?;
}
RelExpr::Subquery(subquery) => {
f.write_str("(")?;
NewLine.fmt(f)?;
Indent(subquery).fmt(f)?;
NewLine.fmt(f)?;
f.write_str(")")?;
}
RelExpr::Function {
name,
args,
ordinality,
} => {
write!(f, "{name}({})", display_comma_separated(args))?;
if *ordinality {
write!(f, " WITH ORDINALITY")?;
}
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct TableAlias {
pub name: Ident,
pub columns: Vec<Ident>,
}
impl TableAlias {
fn new(name: Ident, columns: Vec<Ident>) -> Self {
Self { name, columns }
}
pub fn simple(name: Ident) -> Self {
Self::new(name, vec![])
}
}
impl fmt::Display for TableAlias {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
if !self.columns.is_empty() {
write!(f, " ({})", display_comma_separated(&self.columns))?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum TableVersion {
ForSystemTimeAsOf(Expr),
Function(Expr),
}
impl Display for TableVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableVersion::ForSystemTimeAsOf(e) => write!(f, "FOR SYSTEM_TIME AS OF {e}")?,
TableVersion::Function(func) => write!(f, "{func}")?,
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Join {
pub relation: RelNamed,
pub join_operator: JoinOperator,
}
impl fmt::Display for Join {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn prefix(constraint: &JoinConstraint) -> &'static str {
match constraint {
JoinConstraint::Natural => "NATURAL ",
_ => "",
}
}
fn suffix(constraint: &'_ JoinConstraint) -> impl fmt::Display + '_ {
struct Suffix<'a>(&'a JoinConstraint);
impl fmt::Display for Suffix<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
JoinConstraint::On(expr) => write!(f, " ON {expr}"),
JoinConstraint::Using(attrs) => {
write!(f, " USING({})", display_comma_separated(attrs))
}
_ => Ok(()),
}
}
}
Suffix(constraint)
}
match &self.join_operator {
JoinOperator::Join(constraint) => f.write_fmt(format_args!(
"{}JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::Inner(constraint) => f.write_fmt(format_args!(
"{}INNER JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::Left(constraint) => f.write_fmt(format_args!(
"{}LEFT JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::LeftOuter(constraint) => f.write_fmt(format_args!(
"{}LEFT OUTER JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::Right(constraint) => f.write_fmt(format_args!(
"{}RIGHT JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::RightOuter(constraint) => f.write_fmt(format_args!(
"{}RIGHT OUTER JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::FullOuter(constraint) => f.write_fmt(format_args!(
"{}FULL JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::CrossJoin => f.write_fmt(format_args!("CROSS JOIN {}", self.relation)),
JoinOperator::Semi(constraint) => f.write_fmt(format_args!(
"{}SEMI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::LeftSemi(constraint) => f.write_fmt(format_args!(
"{}LEFT SEMI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::RightSemi(constraint) => f.write_fmt(format_args!(
"{}RIGHT SEMI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::Anti(constraint) => f.write_fmt(format_args!(
"{}ANTI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::LeftAnti(constraint) => f.write_fmt(format_args!(
"{}LEFT ANTI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::RightAnti(constraint) => f.write_fmt(format_args!(
"{}RIGHT ANTI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
)),
JoinOperator::CrossApply => f.write_fmt(format_args!("CROSS APPLY {}", self.relation)),
JoinOperator::OuterApply => f.write_fmt(format_args!("OUTER APPLY {}", self.relation)),
JoinOperator::AsOf {
match_condition,
constraint,
} => f.write_fmt(format_args!(
"ASOF JOIN {} MATCH_CONDITION ({match_condition}){}",
self.relation,
suffix(constraint)
)),
JoinOperator::StraightJoin(constraint) => f.write_fmt(format_args!(
"STRAIGHT_JOIN {}{}",
self.relation,
suffix(constraint)
)),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum JoinOperator {
Join(JoinConstraint),
Inner(JoinConstraint),
Left(JoinConstraint),
LeftOuter(JoinConstraint),
Right(JoinConstraint),
RightOuter(JoinConstraint),
FullOuter(JoinConstraint),
CrossJoin,
Semi(JoinConstraint),
LeftSemi(JoinConstraint),
RightSemi(JoinConstraint),
Anti(JoinConstraint),
LeftAnti(JoinConstraint),
RightAnti(JoinConstraint),
CrossApply,
OuterApply,
AsOf {
match_condition: Expr,
constraint: JoinConstraint,
},
StraightJoin(JoinConstraint),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum JoinConstraint {
On(Expr),
Using(Vec<ObjectName>),
Natural,
None,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum OrderByKind {
All(OrderByOptions),
Expressions(Vec<OrderByExpr>),
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct OrderBy {
pub exprs: Vec<OrderByExpr>,
}
impl fmt::Display for OrderBy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ORDER BY")?;
write!(f, " {}", display_comma_separated(&self.exprs))?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct OrderByExpr {
pub expr: Expr,
pub options: OrderByOptions,
}
impl fmt::Display for OrderByExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.expr, self.options)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct OrderByOptions {
pub asc: Option<bool>,
pub nulls_first: Option<bool>,
}
impl fmt::Display for OrderByOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.asc {
Some(true) => write!(f, " ASC")?,
Some(false) => write!(f, " DESC")?,
None => (),
}
match self.nulls_first {
Some(true) => write!(f, " NULLS FIRST")?,
Some(false) => write!(f, " NULLS LAST")?,
None => (),
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Distinct {
Distinct,
On(Vec<Expr>),
}
impl fmt::Display for Distinct {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Distinct::Distinct => write!(f, "DISTINCT"),
Distinct::On(col_names) => {
let col_names = display_comma_separated(col_names);
write!(f, "DISTINCT ON ({col_names})")
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Values {
pub explicit_row: bool,
pub rows: Vec<Vec<Expr>>,
}
impl fmt::Display for Values {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("VALUES")?;
let prefix = if self.explicit_row { "ROW" } else { "" };
let mut delim = "";
for row in &self.rows {
f.write_str(delim)?;
delim = ",";
SpaceOrNewline.fmt(f)?;
Indent(format_args!("{prefix}({})", display_comma_separated(row))).fmt(f)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct SelectInto {
pub temporary: bool,
pub unlogged: bool,
pub table: bool,
pub name: ObjectName,
}
impl fmt::Display for SelectInto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let temporary = if self.temporary { " TEMPORARY" } else { "" };
let unlogged = if self.unlogged { " UNLOGGED" } else { "" };
let table = if self.table { " TABLE" } else { "" };
write!(f, "INTO{}{}{} {}", temporary, unlogged, table, self.name)
}
}