Skip to content

Commit

Permalink
Merge branch 'main' into filter-in-over-clause
Browse files Browse the repository at this point in the history
  • Loading branch information
lovasoa committed Oct 18, 2023
2 parents 9af63ba + 83cb734 commit c8cf584
Show file tree
Hide file tree
Showing 18 changed files with 789 additions and 70 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ $ cargo run --features json_example --example cli FILENAME.sql [--dialectname]
## Users

This parser is currently being used by the [DataFusion] query engine,
[LocustDB], [Ballista], [GlueSQL], and [Opteryx].
[LocustDB], [Ballista], [GlueSQL], [Opteryx], and [JumpWire].

If your project is using sqlparser-rs feel free to make a PR to add it
to this list.
Expand Down Expand Up @@ -179,6 +179,7 @@ licensed as above, without any additional terms or conditions.
[Ballista]: https://github.com/apache/arrow-ballista
[GlueSQL]: https://github.com/gluesql/gluesql
[Opteryx]: https://github.com/mabel-dev/opteryx
[JumpWire]: https://github.com/extragoodlabs/jumpwire
[Pratt Parser]: https://tdop.github.io/
[sql-2016-grammar]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html
[sql-standard]: https://en.wikipedia.org/wiki/ISO/IEC_9075
43 changes: 38 additions & 5 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl fmt::Display for Ident {
let escaped = value::escape_quoted_string(&self.value, q);
write!(f, "{q}{escaped}{q}")
}
Some(q) if q == '[' => write!(f, "[{}]", self.value),
Some('[') => write!(f, "[{}]", self.value),
None => f.write_str(&self.value),
_ => panic!("unexpected quote style"),
}
Expand Down Expand Up @@ -496,12 +496,14 @@ pub enum Expr {
/// ```sql
/// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)
/// TRIM(<expr>)
/// TRIM(<expr>, [, characters]) -- only Snowflake or Bigquery
/// ```
Trim {
expr: Box<Expr>,
// ([BOTH | LEADING | TRAILING]
trim_where: Option<TrimWhereField>,
trim_what: Option<Box<Expr>>,
trim_characters: Option<Vec<Expr>>,
},
/// ```sql
/// OVERLAY(<expr> PLACING <expr> FROM <expr>[ FOR <expr> ]
Expand Down Expand Up @@ -577,7 +579,7 @@ pub enum Expr {
///
/// Syntax:
/// ```sql
/// MARCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
/// MATCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
///
/// <col> = CompoundIdentifier
/// <expr> = String literal
Expand Down Expand Up @@ -895,6 +897,7 @@ impl fmt::Display for Expr {
expr,
trim_where,
trim_what,
trim_characters,
} => {
write!(f, "TRIM(")?;
if let Some(ident) = trim_where {
Expand All @@ -905,6 +908,9 @@ impl fmt::Display for Expr {
} else {
write!(f, "{expr}")?;
}
if let Some(characters) = trim_characters {
write!(f, ", {}", display_comma_separated(characters))?;
}

write!(f, ")")
}
Expand Down Expand Up @@ -1304,6 +1310,10 @@ pub enum Statement {
selection: Option<Expr>,
/// RETURNING
returning: Option<Vec<SelectItem>>,
/// ORDER BY (MySQL)
order_by: Vec<OrderByExpr>,
/// LIMIT (MySQL)
limit: Option<Expr>,
},
/// CREATE VIEW
CreateView {
Expand All @@ -1315,6 +1325,12 @@ pub enum Statement {
query: Box<Query>,
with_options: Vec<SqlOption>,
cluster_by: Vec<Ident>,
/// if true, has RedShift [`WITH NO SCHEMA BINDING`] clause <https://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_VIEW.html>
with_no_schema_binding: bool,
/// if true, has SQLite `IF NOT EXISTS` clause <https://www.sqlite.org/lang_createview.html>
if_not_exists: bool,
/// if true, has SQLite `TEMP` or `TEMPORARY` clause <https://www.sqlite.org/lang_createview.html>
temporary: bool,
},
/// CREATE TABLE
CreateTable {
Expand Down Expand Up @@ -2144,6 +2160,8 @@ impl fmt::Display for Statement {
using,
selection,
returning,
order_by,
limit,
} => {
write!(f, "DELETE ")?;
if !tables.is_empty() {
Expand All @@ -2159,6 +2177,12 @@ impl fmt::Display for Statement {
if let Some(returning) = returning {
write!(f, " RETURNING {}", display_comma_separated(returning))?;
}
if !order_by.is_empty() {
write!(f, " ORDER BY {}", display_comma_separated(order_by))?;
}
if let Some(limit) = limit {
write!(f, " LIMIT {limit}")?;
}
Ok(())
}
Statement::Close { cursor } => {
Expand Down Expand Up @@ -2262,13 +2286,18 @@ impl fmt::Display for Statement {
materialized,
with_options,
cluster_by,
with_no_schema_binding,
if_not_exists,
temporary,
} => {
write!(
f,
"CREATE {or_replace}{materialized}VIEW {name}",
"CREATE {or_replace}{materialized}{temporary}VIEW {if_not_exists}{name}",
or_replace = if *or_replace { "OR REPLACE " } else { "" },
materialized = if *materialized { "MATERIALIZED " } else { "" },
name = name
name = name,
temporary = if *temporary { "TEMPORARY " } else { "" },
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }
)?;
if !with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_options))?;
Expand All @@ -2279,7 +2308,11 @@ impl fmt::Display for Statement {
if !cluster_by.is_empty() {
write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?;
}
write!(f, " AS {query}")
write!(f, " AS {query}")?;
if *with_no_schema_binding {
write!(f, " WITH NO SCHEMA BINDING")?;
}
Ok(())
}
Statement::CreateTable {
name,
Expand Down
74 changes: 53 additions & 21 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ pub struct Query {
pub order_by: Vec<OrderByExpr>,
/// `LIMIT { <N> | ALL }`
pub limit: Option<Expr>,

/// `LIMIT { <N> } BY { <expr>,<expr>,... } }`
pub limit_by: Vec<Expr>,

/// `OFFSET <N> [ { ROW | ROWS } ]`
pub offset: Option<Offset>,
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
Expand All @@ -58,6 +62,9 @@ impl fmt::Display for Query {
if let Some(ref offset) = self.offset {
write!(f, " {offset}")?;
}
if !self.limit_by.is_empty() {
write!(f, " BY {}", display_separated(&self.limit_by, ", "))?;
}
if let Some(ref fetch) = self.fetch {
write!(f, " {fetch}")?;
}
Expand Down Expand Up @@ -713,13 +720,28 @@ pub enum TableFactor {
/// For example `FROM monthly_sales PIVOT(sum(amount) FOR MONTH IN ('JAN', 'FEB'))`
/// See <https://docs.snowflake.com/en/sql-reference/constructs/pivot>
Pivot {
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
name: ObjectName,
table_alias: Option<TableAlias>,
#[cfg_attr(feature = "visitor", visit(with = "visit_table_factor"))]
table: Box<TableFactor>,
aggregate_function: Expr, // Function expression
value_column: Vec<Ident>,
pivot_values: Vec<Value>,
pivot_alias: Option<TableAlias>,
alias: Option<TableAlias>,
},
/// An UNPIVOT operation on a table.
///
/// Syntax:
/// ```sql
/// table UNPIVOT(value FOR name IN (column1, [ column2, ... ])) [ alias ]
/// ```
///
/// See <https://docs.snowflake.com/en/sql-reference/constructs/unpivot>.
Unpivot {
#[cfg_attr(feature = "visitor", visit(with = "visit_table_factor"))]
table: Box<TableFactor>,
value: Ident,
name: Ident,
columns: Vec<Ident>,
alias: Option<TableAlias>,
},
}

Expand Down Expand Up @@ -803,32 +825,42 @@ impl fmt::Display for TableFactor {
Ok(())
}
TableFactor::Pivot {
name,
table_alias,
table,
aggregate_function,
value_column,
pivot_values,
pivot_alias,
alias,
} => {
write!(f, "{}", name)?;
if table_alias.is_some() {
write!(f, " AS {}", table_alias.as_ref().unwrap())?;
}
write!(
f,
" PIVOT({} FOR {} IN (",
"{} PIVOT({} FOR {} IN ({}))",
table,
aggregate_function,
Expr::CompoundIdentifier(value_column.to_vec())
Expr::CompoundIdentifier(value_column.to_vec()),
display_comma_separated(pivot_values)
)?;
for value in pivot_values {
write!(f, "{}", value)?;
if !value.eq(pivot_values.last().unwrap()) {
write!(f, ", ")?;
}
if alias.is_some() {
write!(f, " AS {}", alias.as_ref().unwrap())?;
}
write!(f, "))")?;
if pivot_alias.is_some() {
write!(f, " AS {}", pivot_alias.as_ref().unwrap())?;
Ok(())
}
TableFactor::Unpivot {
table,
value,
name,
columns,
alias,
} => {
write!(
f,
"{} UNPIVOT({} FOR {} IN ({}))",
table,
value,
name,
display_comma_separated(columns)
)?;
if alias.is_some() {
write!(f, " AS {}", alias.as_ref().unwrap())?;
}
Ok(())
}
Expand Down
8 changes: 8 additions & 0 deletions src/ast/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ pub enum DateTimeField {
Month,
Week,
Day,
DayOfWeek,
DayOfYear,
Date,
Hour,
Minute,
Expand All @@ -127,6 +129,7 @@ pub enum DateTimeField {
Doy,
Epoch,
Isodow,
IsoWeek,
Isoyear,
Julian,
Microsecond,
Expand All @@ -138,6 +141,7 @@ pub enum DateTimeField {
Nanosecond,
Nanoseconds,
Quarter,
Time,
Timezone,
TimezoneHour,
TimezoneMinute,
Expand All @@ -151,6 +155,8 @@ impl fmt::Display for DateTimeField {
DateTimeField::Month => "MONTH",
DateTimeField::Week => "WEEK",
DateTimeField::Day => "DAY",
DateTimeField::DayOfWeek => "DAYOFWEEK",
DateTimeField::DayOfYear => "DAYOFYEAR",
DateTimeField::Date => "DATE",
DateTimeField::Hour => "HOUR",
DateTimeField::Minute => "MINUTE",
Expand All @@ -162,6 +168,7 @@ impl fmt::Display for DateTimeField {
DateTimeField::Epoch => "EPOCH",
DateTimeField::Isodow => "ISODOW",
DateTimeField::Isoyear => "ISOYEAR",
DateTimeField::IsoWeek => "ISOWEEK",
DateTimeField::Julian => "JULIAN",
DateTimeField::Microsecond => "MICROSECOND",
DateTimeField::Microseconds => "MICROSECONDS",
Expand All @@ -172,6 +179,7 @@ impl fmt::Display for DateTimeField {
DateTimeField::Nanosecond => "NANOSECOND",
DateTimeField::Nanoseconds => "NANOSECONDS",
DateTimeField::Quarter => "QUARTER",
DateTimeField::Time => "TIME",
DateTimeField::Timezone => "TIMEZONE",
DateTimeField::TimezoneHour => "TIMEZONE_HOUR",
DateTimeField::TimezoneMinute => "TIMEZONE_MINUTE",
Expand Down
2 changes: 1 addition & 1 deletion src/ast/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ where
///
/// This demonstrates how to effectively replace an expression with another more complicated one
/// that references the original. This example avoids unnecessary allocations by using the
/// [`std::mem`](std::mem) family of functions.
/// [`std::mem`] family of functions.
///
/// ```
/// # use sqlparser::parser::Parser;
Expand Down
6 changes: 6 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ define_keywords!(
BIGINT,
BIGNUMERIC,
BINARY,
BINDING,
BLOB,
BLOOMFILTER,
BOOL,
Expand Down Expand Up @@ -196,6 +197,8 @@ define_keywords!(
DATE,
DATETIME,
DAY,
DAYOFWEEK,
DAYOFYEAR,
DEALLOCATE,
DEC,
DECADE,
Expand Down Expand Up @@ -334,6 +337,7 @@ define_keywords!(
IS,
ISODOW,
ISOLATION,
ISOWEEK,
ISOYEAR,
JAR,
JOIN,
Expand Down Expand Up @@ -632,6 +636,7 @@ define_keywords!(
UNKNOWN,
UNLOGGED,
UNNEST,
UNPIVOT,
UNSIGNED,
UNTIL,
UPDATE,
Expand Down Expand Up @@ -690,6 +695,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::HAVING,
Keyword::ORDER,
Keyword::PIVOT,
Keyword::UNPIVOT,
Keyword::TOP,
Keyword::LATERAL,
Keyword::VIEW,
Expand Down
Loading

0 comments on commit c8cf584

Please sign in to comment.