| // 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(()) |
| } |
| } |