Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ pub use self::query::{
ExcludeSelectItem, Fetch, ForClause, ForJson, ForXml, GroupByExpr, IdentWithAlias,
IlikeSelectItem, Join, JoinConstraint, JoinOperator, JsonTableColumn,
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, MatchRecognizePattern,
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NonBlock, Offset, OffsetRows,
OrderByExpr, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
OffsetRows, OrderByExpr, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
ReplaceSelectItem, RowsPerMatch, Select, SelectInto, SelectItem, SetExpr, SetOperator,
SetQuantifier, SymbolDefinition, Table, TableAlias, TableFactor, TableVersion, TableWithJoins,
Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
Expand Down
46 changes: 44 additions & 2 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,14 +353,56 @@ impl fmt::Display for LateralView {
}
}

/// An expression used in a named window declaration.
///
/// ```sql
/// WINDOW mywindow AS [named_window_expr]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum NamedWindowExpr {
/// A direct reference to another named window definition.
/// [BigQuery]
///
/// Example:
/// ```sql
/// WINDOW mywindow AS prev_window
/// ```
///
/// [BigQuery]: https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls#ref_named_window
NamedWindow(Ident),
/// A window expression.
///
/// Example:
/// ```sql
/// WINDOW mywindow AS (ORDER BY 1)
/// ```
WindowSpec(WindowSpec),
}

impl fmt::Display for NamedWindowExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
NamedWindowExpr::NamedWindow(named_window) => {
write!(f, "{named_window}")?;
}
NamedWindowExpr::WindowSpec(window_spec) => {
write!(f, "({window_spec})")?;
}
};
Ok(())
}
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct NamedWindowDefinition(pub Ident, pub WindowSpec);
pub struct NamedWindowDefinition(pub Ident, pub NamedWindowExpr);

impl fmt::Display for NamedWindowDefinition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} AS ({})", self.0, self.1)
write!(f, "{} AS {}", self.0, self.1)
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/dialect/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ impl Dialect for BigQueryDialect {
fn supports_string_literal_backslash_escape(&self) -> bool {
true
}

/// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls#ref_named_window)
fn supports_window_clause_named_window_reference(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ impl Dialect for GenericDialect {
fn supports_dictionary_syntax(&self) -> bool {
true
}

fn supports_window_clause_named_window_reference(&self) -> bool {
true
}
}
11 changes: 11 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,17 @@ pub trait Dialect: Debug + Any {
fn supports_filter_during_aggregation(&self) -> bool {
false
}
/// Returns true if the dialect supports referencing another named window
/// within a window clause declaration.
///
/// Example
/// ```sql
/// SELECT * FROM mytable
/// WINDOW mynamed_window AS another_named_window
/// ```
fn supports_window_clause_named_window_reference(&self) -> bool {
false
}
/// Returns true if the dialect supports `ARRAY_AGG() [WITHIN GROUP (ORDER BY)]` expressions.
/// Otherwise, the dialect should expect an `ORDER BY` without the `WITHIN GROUP` clause, e.g. [`ANSI`]
///
Expand Down
13 changes: 10 additions & 3 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10055,9 +10055,16 @@ impl<'a> Parser<'a> {
pub fn parse_named_window(&mut self) -> Result<NamedWindowDefinition, ParserError> {
let ident = self.parse_identifier(false)?;
self.expect_keyword(Keyword::AS)?;
self.expect_token(&Token::LParen)?;
let window_spec = self.parse_window_spec()?;
Ok(NamedWindowDefinition(ident, window_spec))

let window_expr = if self.consume_token(&Token::LParen) {
NamedWindowExpr::WindowSpec(self.parse_window_spec()?)
} else if self.dialect.supports_window_clause_named_window_reference() {
NamedWindowExpr::NamedWindow(self.parse_identifier(false)?)
} else {
return self.expected("(", self.peek_token());
};

Ok(NamedWindowDefinition(ident, window_expr))
}

pub fn parse_create_procedure(&mut self, or_alter: bool) -> Result<Statement, ParserError> {
Expand Down
48 changes: 44 additions & 4 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4340,6 +4340,31 @@ fn parse_named_window_functions() {
supported_dialects.verified_stmt(sql);
}

#[test]
fn parse_window_clause() {
let sql = "SELECT * \
FROM mytable \
WINDOW \
window1 AS (ORDER BY 1 ASC, 2 DESC, 3 NULLS FIRST), \
window2 AS (window1), \
window3 AS (PARTITION BY a, b, c), \
window4 AS (ROWS UNBOUNDED PRECEDING), \
window5 AS (window1 PARTITION BY a), \
window6 AS (window1 ORDER BY a), \
window7 AS (window1 ROWS UNBOUNDED PRECEDING), \
window8 AS (window1 PARTITION BY a ORDER BY b ROWS UNBOUNDED PRECEDING) \
ORDER BY C3";
verified_only_select(sql);

let sql = "SELECT from mytable WINDOW window1 AS window2";
let dialects = all_dialects_except(|d| d.is::<BigQueryDialect>() || d.is::<GenericDialect>());
let res = dialects.parse_sql_statements(sql);
assert_eq!(
ParserError::ParserError("Expected (, found: window2".to_string()),
res.unwrap_err()
);
}

#[test]
fn test_parse_named_window() {
let sql = "SELECT \
Expand Down Expand Up @@ -4437,7 +4462,7 @@ fn test_parse_named_window() {
value: "window1".to_string(),
quote_style: None,
},
WindowSpec {
NamedWindowExpr::WindowSpec(WindowSpec {
window_name: None,
partition_by: vec![],
order_by: vec![OrderByExpr {
Expand All @@ -4449,22 +4474,22 @@ fn test_parse_named_window() {
nulls_first: None,
}],
window_frame: None,
},
}),
),
NamedWindowDefinition(
Ident {
value: "window2".to_string(),
quote_style: None,
},
WindowSpec {
NamedWindowExpr::WindowSpec(WindowSpec {
window_name: None,
partition_by: vec![Expr::Identifier(Ident {
value: "C11".to_string(),
quote_style: None,
})],
order_by: vec![],
window_frame: None,
},
}),
),
],
qualify: None,
Expand All @@ -4473,6 +4498,21 @@ fn test_parse_named_window() {
assert_eq!(actual_select_only, expected);
}

#[test]
fn parse_window_clause_named_window() {
let sql = "SELECT * FROM mytable WINDOW window1 AS window2";
let Select { named_window, .. } =
all_dialects_where(|d| d.supports_window_clause_named_window_reference())
.verified_only_select(sql);
assert_eq!(
vec![NamedWindowDefinition(
Ident::new("window1"),
NamedWindowExpr::NamedWindow(Ident::new("window2"))
)],
named_window
);
}

#[test]
fn parse_aggregate_with_group_by() {
let sql = "SELECT a, COUNT(1), MIN(b), MAX(b) FROM foo GROUP BY a";
Expand Down