#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::ast::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Query {
pub with: Option<With>,
pub body: SetExpr,
pub order_by: Vec<OrderByExpr>,
pub limit: Option<String>,
pub offset: Option<String>,
pub fetch: Option<Fetch>,
}
impl Query {
pub fn as_simple_values(&self) -> Option<&Values> {
match &self {
Query {
with: None,
body: SetExpr::Values(values),
order_by,
limit: None,
offset: None,
fetch: None,
} if order_by.is_empty() => Some(values),
_ => None,
}
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref with) = self.with {
write!(f, "{} ", with)?;
}
write!(f, "{}", self.body)?;
if !self.order_by.is_empty() {
write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
}
if let Some(ref limit) = self.limit {
write!(f, " LIMIT {}", limit)?;
}
if let Some(ref offset) = self.offset {
write!(f, " OFFSET {}", offset)?;
}
if let Some(ref fetch) = self.fetch {
write!(f, " {}", fetch)?;
}
Ok(())
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SetExpr {
Select(Box<Select>),
Query(Box<Query>),
SetOperation {
op: SetOperator,
all: bool,
corresponding: Corresponding,
left: Box<SetExpr>,
right: Box<SetExpr>,
},
Values(Values),
}
impl fmt::Display for SetExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SetExpr::Select(s) => write!(f, "{}", s),
SetExpr::Query(q) => write!(f, "({})", q),
SetExpr::Values(v) => write!(f, "{}", v),
SetExpr::SetOperation {
left,
right,
op,
all,
corresponding,
} => {
let all_str = if *all { " ALL" } else { "" };
write!(f, "{} {}{}{} {}", left, op, all_str, corresponding, right)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SetOperator {
Union,
Except,
Intersect,
}
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",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Corresponding {
pub corresponding: bool,
pub column_list: Option<Vec<Ident>>,
}
impl Corresponding {
pub fn with_column_list(column_list: Option<Vec<Ident>>) -> Self {
Self {
corresponding: true,
column_list,
}
}
pub fn none() -> Self {
Self {
corresponding: false,
column_list: None,
}
}
pub fn is_corresponding(&self) -> bool {
self.corresponding
}
pub fn column_list(&self) -> Option<&[Ident]> {
self.column_list.as_deref()
}
}
impl fmt::Display for Corresponding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.corresponding {
write!(f, " CORRESPONDING")?;
if let Some(column_list) = &self.column_list {
write!(f, " BY ({})", display_comma_separated(column_list))?;
}
}
Ok(())
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Select {
pub distinct: Distinct,
pub projection: Vec<SelectItem>,
pub from: Vec<TableWithJoins>,
pub lateral_views: Vec<LateralView>,
pub selection: Option<Expr>,
pub group_by: Vec<Expr>,
pub having: Option<Expr>,
}
impl fmt::Display for Select {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SELECT{}", &self.distinct)?;
write!(f, " {}", display_comma_separated(&self.projection))?;
if !self.from.is_empty() {
write!(f, " FROM {}", display_comma_separated(&self.from))?;
}
if !self.lateral_views.is_empty() {
for lv in &self.lateral_views {
write!(f, "{}", lv)?;
}
}
if let Some(ref selection) = self.selection {
write!(f, " WHERE {}", selection)?;
}
if !self.group_by.is_empty() {
write!(f, " GROUP BY {}", display_comma_separated(&self.group_by))?;
}
if let Some(ref having) = self.having {
write!(f, " HAVING {}", having)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[expect(clippy::enum_variant_names)]
pub enum Distinct {
#[default]
All,
Distinct,
DistinctOn(Vec<Expr>),
}
impl Distinct {
pub const fn is_all(&self) -> bool {
matches!(self, Distinct::All)
}
pub const fn is_distinct(&self) -> bool {
matches!(self, Distinct::Distinct)
}
}
impl fmt::Display for Distinct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Distinct::All => write!(f, ""),
Distinct::Distinct => write!(f, " DISTINCT"),
Distinct::DistinctOn(exprs) => {
write!(f, " DISTINCT ON ({})", display_comma_separated(exprs))
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
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, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
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 {
write!(
f,
"WITH {}{}",
if self.recursive { "RECURSIVE " } else { "" },
display_comma_separated(&self.cte_tables)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Cte {
pub alias: TableAlias,
pub cte_inner: CteInner,
}
impl fmt::Display for Cte {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.cte_inner {
CteInner::Query(query) => write!(f, "{} AS ({})", self.alias, query)?,
CteInner::ChangeLog(obj_name) => {
write!(f, "{} AS changelog from {}", self.alias, obj_name)?
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CteInner {
Query(Query),
ChangeLog(ObjectName),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum SelectItem {
UnnamedExpr(Expr),
ExprQualifiedWildcard(Expr, Vec<Ident>),
ExprWithAlias { expr: Expr, alias: Ident },
QualifiedWildcard(ObjectName, Option<Vec<Expr>>),
Wildcard(Option<Vec<Expr>>),
}
impl fmt::Display for SelectItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr),
SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias),
SelectItem::ExprQualifiedWildcard(expr, prefix) => write!(
f,
"({}){}.*",
expr,
prefix
.iter()
.format_with("", |i, f| f(&format_args!(".{i}")))
),
SelectItem::QualifiedWildcard(prefix, except) => match except {
Some(cols) => write!(
f,
"{}.* EXCEPT ({})",
prefix,
cols.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.as_slice()
.join(", ")
),
None => write!(f, "{}.*", prefix),
},
SelectItem::Wildcard(except) => match except {
Some(cols) => write!(
f,
"* EXCEPT ({})",
cols.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.as_slice()
.join(", ")
),
None => write!(f, "*"),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TableWithJoins {
pub relation: TableFactor,
pub joins: Vec<Join>,
}
impl fmt::Display for TableWithJoins {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.relation)?;
for join in &self.joins {
write!(f, "{}", join)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TableFactor {
Table {
name: ObjectName,
alias: Option<TableAlias>,
as_of: Option<AsOf>,
},
Derived {
lateral: bool,
subquery: Box<Query>,
alias: Option<TableAlias>,
},
TableFunction {
name: ObjectName,
alias: Option<TableAlias>,
args: Vec<FunctionArg>,
with_ordinality: bool,
},
NestedJoin(Box<TableWithJoins>),
}
impl fmt::Display for TableFactor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TableFactor::Table { name, alias, as_of } => {
write!(f, "{}", name)?;
if let Some(as_of) = as_of {
write!(f, "{}", as_of)?
}
if let Some(alias) = alias {
write!(f, " AS {}", alias)?;
}
Ok(())
}
TableFactor::Derived {
lateral,
subquery,
alias,
} => {
if *lateral {
write!(f, "LATERAL ")?;
}
write!(f, "({})", subquery)?;
if let Some(alias) = alias {
write!(f, " AS {}", alias)?;
}
Ok(())
}
TableFactor::TableFunction {
name,
alias,
args,
with_ordinality,
} => {
write!(f, "{}({})", name, display_comma_separated(args))?;
if *with_ordinality {
write!(f, " WITH ORDINALITY")?;
}
if let Some(alias) = alias {
write!(f, " AS {}", alias)?;
}
Ok(())
}
TableFactor::NestedJoin(table_reference) => write!(f, "({})", table_reference),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TableAlias {
pub name: Ident,
pub columns: Vec<Ident>,
}
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, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Join {
pub relation: TableFactor,
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::Inner(constraint) => write!(
f,
" {}JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::LeftOuter(constraint) => write!(
f,
" {}LEFT JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::RightOuter(constraint) => write!(
f,
" {}RIGHT JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::FullOuter(constraint) => write!(
f,
" {}FULL JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
JoinOperator::AsOfInner(constraint) => write!(
f,
" {}ASOF JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::AsOfLeft(constraint) => write!(
f,
" {}ASOF LEFT JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum JoinOperator {
Inner(JoinConstraint),
LeftOuter(JoinConstraint),
RightOuter(JoinConstraint),
FullOuter(JoinConstraint),
CrossJoin,
AsOfInner(JoinConstraint),
AsOfLeft(JoinConstraint),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum JoinConstraint {
On(Expr),
Using(Vec<Ident>),
Natural,
None,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct OrderByExpr {
pub expr: Expr,
pub asc: Option<bool>,
pub nulls_first: Option<bool>,
}
impl fmt::Display for OrderByExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.expr)?;
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, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Fetch {
pub with_ties: bool,
pub quantity: Option<String>,
}
impl fmt::Display for Fetch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let extension = if self.with_ties { "WITH TIES" } else { "ONLY" };
if let Some(ref quantity) = self.quantity {
write!(f, "FETCH FIRST {} ROWS {}", quantity, extension)
} else {
write!(f, "FETCH FIRST ROWS {}", extension)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Top {
pub with_ties: bool,
pub percent: bool,
pub quantity: Option<Expr>,
}
impl fmt::Display for Top {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let extension = if self.with_ties { " WITH TIES" } else { "" };
if let Some(ref quantity) = self.quantity {
let percent = if self.percent { " PERCENT" } else { "" };
write!(f, "TOP ({}){}{}", quantity, percent, extension)
} else {
write!(f, "TOP{}", extension)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Values(pub Vec<Vec<Expr>>);
impl fmt::Display for Values {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VALUES ")?;
let mut delim = "";
for row in &self.0 {
write!(f, "{}", delim)?;
delim = ", ";
write!(f, "({})", display_comma_separated(row))?;
}
Ok(())
}
}