blob: 656f7f14b432ee24c49aa3f3171384d6ad763988 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::*;
/// The most complete variant of a `SELECT` query expression, optionally
/// including `WITH`, `UNION` / other set operations, and `ORDER BY`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Query {
/// WITH (common table expressions, or CTEs)
pub ctes: Vec<Cte>,
/// SELECT or UNION / EXCEPT / INTECEPT
pub body: SetExpr,
/// ORDER BY
pub order_by: Vec<OrderByExpr>,
/// `LIMIT { <N> | ALL }`
pub limit: Option<Expr>,
/// `OFFSET <N> { ROW | ROWS }`
pub offset: Option<Expr>,
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
pub fetch: Option<Fetch>,
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.ctes.is_empty() {
write!(f, "WITH {} ", display_comma_separated(&self.ctes))?;
}
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 {} ROWS", offset)?;
}
if let Some(ref fetch) = self.fetch {
write!(f, " {}", fetch)?;
}
Ok(())
}
}
/// A node in a tree, representing a "query body" expression, roughly:
/// `SELECT ... [ {UNION|EXCEPT|INTERSECT} SELECT ...]`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SetExpr {
/// Restricted SELECT .. FROM .. HAVING (no ORDER BY or set operations)
Select(Box<Select>),
/// Parenthesized SELECT subquery, which may include more set operations
/// in its body and an optional ORDER BY / LIMIT.
Query(Box<Query>),
/// UNION/EXCEPT/INTERSECT of two queries
SetOperation {
op: SetOperator,
all: bool,
left: Box<SetExpr>,
right: Box<SetExpr>,
},
Values(Values),
// TODO: ANSI SQL supports `TABLE` here.
}
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,
} => {
let all_str = if *all { " ALL" } else { "" };
write!(f, "{} {}{} {}", left, op, all_str, right)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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",
})
}
}
/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
/// appear either as the only body item of an `SQLQuery`, or as an operand
/// to a set operation like `UNION`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Select {
pub distinct: bool,
/// projection expressions
pub projection: Vec<SelectItem>,
/// FROM
pub from: Vec<TableWithJoins>,
/// WHERE
pub selection: Option<Expr>,
/// GROUP BY
pub group_by: Vec<Expr>,
/// HAVING
pub having: Option<Expr>,
}
impl fmt::Display for Select {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"SELECT{} {}",
if self.distinct { " DISTINCT" } else { "" },
display_comma_separated(&self.projection)
)?;
if !self.from.is_empty() {
write!(f, " FROM {}", display_comma_separated(&self.from))?;
}
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(())
}
}
/// A single CTE (used after `WITH`): `alias [(col1, col2, ...)] AS ( query )`
/// The names in the column list before `AS`, when specified, replace the names
/// of the columns returned by the query. The parser does not validate that the
/// number of columns in the query matches the number of columns in the query.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Cte {
pub alias: TableAlias,
pub query: Query,
}
impl fmt::Display for Cte {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} AS ({})", self.alias, self.query)
}
}
/// One item of the comma-separated list following `SELECT`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SelectItem {
/// Any expression, not followed by `[ AS ] alias`
UnnamedExpr(Expr),
/// An expression, followed by `[ AS ] alias`
ExprWithAlias { expr: Expr, alias: Ident },
/// `alias.*` or even `schema.table.*`
QualifiedWildcard(ObjectName),
/// An unqualified `*`
Wildcard,
}
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::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix),
SelectItem::Wildcard => write!(f, "*"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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(())
}
}
/// A table name or a parenthesized subquery with an optional alias
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TableFactor {
Table {
name: ObjectName,
alias: Option<TableAlias>,
/// Arguments of a table-valued function, as supported by Postgres
/// and MSSQL. Note that deprecated MSSQL `FROM foo (NOLOCK)` syntax
/// will also be parsed as `args`.
args: Vec<Expr>,
/// MSSQL-specific `WITH (...)` hints such as NOLOCK.
with_hints: Vec<Expr>,
},
Derived {
lateral: bool,
subquery: Box<Query>,
alias: Option<TableAlias>,
},
/// Represents a parenthesized join expression, such as
/// `(foo <JOIN> bar [ <JOIN> baz ... ])`.
/// The inner `TableWithJoins` can have no joins only if its
/// `relation` is itself a `TableFactor::NestedJoin`.
NestedJoin(Box<TableWithJoins>),
}
impl fmt::Display for TableFactor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableFactor::Table {
name,
alias,
args,
with_hints,
} => {
write!(f, "{}", name)?;
if !args.is_empty() {
write!(f, "({})", display_comma_separated(args))?;
}
if let Some(alias) = alias {
write!(f, " AS {}", alias)?;
}
if !with_hints.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_hints))?;
}
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::NestedJoin(table_reference) => write!(f, "({})", table_reference),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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)]
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<'a>(constraint: &'a JoinConstraint) -> impl fmt::Display + 'a {
struct Suffix<'a>(&'a JoinConstraint);
impl<'a> fmt::Display for Suffix<'a> {
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::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum JoinOperator {
Inner(JoinConstraint),
LeftOuter(JoinConstraint),
RightOuter(JoinConstraint),
FullOuter(JoinConstraint),
CrossJoin,
/// CROSS APPLY (non-standard)
CrossApply,
/// OUTER APPLY (non-standard)
OuterApply,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum JoinConstraint {
On(Expr),
Using(Vec<Ident>),
Natural,
}
/// SQL ORDER BY expression
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OrderByExpr {
pub expr: Expr,
pub asc: Option<bool>,
}
impl fmt::Display for OrderByExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.asc {
Some(true) => write!(f, "{} ASC", self.expr),
Some(false) => write!(f, "{} DESC", self.expr),
None => write!(f, "{}", self.expr),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Fetch {
pub with_ties: bool,
pub percent: bool,
pub quantity: Option<Expr>,
}
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 {
let percent = if self.percent { " PERCENT" } else { "" };
write!(f, "FETCH FIRST {}{} ROWS {}", quantity, percent, extension)
} else {
write!(f, "FETCH FIRST ROWS {}", extension)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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(())
}
}