mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-27 16:09:09 +00:00
BigQuery: Support window clause using named window (#1237)
This commit is contained in:
parent
0b5722afbf
commit
6fcf8c9abe
7 changed files with 120 additions and 11 deletions
|
@ -44,8 +44,8 @@ pub use self::query::{
|
||||||
ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause, ForJson, ForXml, GroupByExpr,
|
ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause, ForJson, ForXml, GroupByExpr,
|
||||||
IdentWithAlias, IlikeSelectItem, Join, JoinConstraint, JoinOperator, JsonTableColumn,
|
IdentWithAlias, IlikeSelectItem, Join, JoinConstraint, JoinOperator, JsonTableColumn,
|
||||||
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, MatchRecognizePattern,
|
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, MatchRecognizePattern,
|
||||||
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NonBlock, Offset, OffsetRows,
|
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
|
||||||
OrderByExpr, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
|
OffsetRows, OrderByExpr, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
|
||||||
ReplaceSelectItem, RowsPerMatch, Select, SelectInto, SelectItem, SetExpr, SetOperator,
|
ReplaceSelectItem, RowsPerMatch, Select, SelectInto, SelectItem, SetExpr, SetOperator,
|
||||||
SetQuantifier, SymbolDefinition, Table, TableAlias, TableFactor, TableVersion, TableWithJoins,
|
SetQuantifier, SymbolDefinition, Table, TableAlias, TableFactor, TableVersion, TableWithJoins,
|
||||||
Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
|
Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
|
||||||
|
|
|
@ -358,14 +358,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)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
pub struct NamedWindowDefinition(pub Ident, pub WindowSpec);
|
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 NamedWindowExpr);
|
||||||
|
|
||||||
impl fmt::Display for NamedWindowDefinition {
|
impl fmt::Display for NamedWindowDefinition {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} AS ({})", self.0, self.1)
|
write!(f, "{} AS {}", self.0, self.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,9 @@ impl Dialect for BigQueryDialect {
|
||||||
fn supports_string_literal_backslash_escape(&self) -> bool {
|
fn supports_string_literal_backslash_escape(&self) -> bool {
|
||||||
true
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,4 +54,8 @@ impl Dialect for GenericDialect {
|
||||||
fn supports_dictionary_syntax(&self) -> bool {
|
fn supports_dictionary_syntax(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_window_clause_named_window_reference(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,17 @@ pub trait Dialect: Debug + Any {
|
||||||
fn supports_filter_during_aggregation(&self) -> bool {
|
fn supports_filter_during_aggregation(&self) -> bool {
|
||||||
false
|
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.
|
/// 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`]
|
/// Otherwise, the dialect should expect an `ORDER BY` without the `WITHIN GROUP` clause, e.g. [`ANSI`]
|
||||||
///
|
///
|
||||||
|
|
|
@ -10121,9 +10121,16 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse_named_window(&mut self) -> Result<NamedWindowDefinition, ParserError> {
|
pub fn parse_named_window(&mut self) -> Result<NamedWindowDefinition, ParserError> {
|
||||||
let ident = self.parse_identifier(false)?;
|
let ident = self.parse_identifier(false)?;
|
||||||
self.expect_keyword(Keyword::AS)?;
|
self.expect_keyword(Keyword::AS)?;
|
||||||
self.expect_token(&Token::LParen)?;
|
|
||||||
let window_spec = self.parse_window_spec()?;
|
let window_expr = if self.consume_token(&Token::LParen) {
|
||||||
Ok(NamedWindowDefinition(ident, window_spec))
|
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> {
|
pub fn parse_create_procedure(&mut self, or_alter: bool) -> Result<Statement, ParserError> {
|
||||||
|
|
|
@ -4341,6 +4341,31 @@ fn parse_named_window_functions() {
|
||||||
supported_dialects.verified_stmt(sql);
|
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]
|
#[test]
|
||||||
fn test_parse_named_window() {
|
fn test_parse_named_window() {
|
||||||
let sql = "SELECT \
|
let sql = "SELECT \
|
||||||
|
@ -4438,7 +4463,7 @@ fn test_parse_named_window() {
|
||||||
value: "window1".to_string(),
|
value: "window1".to_string(),
|
||||||
quote_style: None,
|
quote_style: None,
|
||||||
},
|
},
|
||||||
WindowSpec {
|
NamedWindowExpr::WindowSpec(WindowSpec {
|
||||||
window_name: None,
|
window_name: None,
|
||||||
partition_by: vec![],
|
partition_by: vec![],
|
||||||
order_by: vec![OrderByExpr {
|
order_by: vec![OrderByExpr {
|
||||||
|
@ -4450,14 +4475,14 @@ fn test_parse_named_window() {
|
||||||
nulls_first: None,
|
nulls_first: None,
|
||||||
}],
|
}],
|
||||||
window_frame: None,
|
window_frame: None,
|
||||||
},
|
}),
|
||||||
),
|
),
|
||||||
NamedWindowDefinition(
|
NamedWindowDefinition(
|
||||||
Ident {
|
Ident {
|
||||||
value: "window2".to_string(),
|
value: "window2".to_string(),
|
||||||
quote_style: None,
|
quote_style: None,
|
||||||
},
|
},
|
||||||
WindowSpec {
|
NamedWindowExpr::WindowSpec(WindowSpec {
|
||||||
window_name: None,
|
window_name: None,
|
||||||
partition_by: vec![Expr::Identifier(Ident {
|
partition_by: vec![Expr::Identifier(Ident {
|
||||||
value: "C11".to_string(),
|
value: "C11".to_string(),
|
||||||
|
@ -4465,7 +4490,7 @@ fn test_parse_named_window() {
|
||||||
})],
|
})],
|
||||||
order_by: vec![],
|
order_by: vec![],
|
||||||
window_frame: None,
|
window_frame: None,
|
||||||
},
|
}),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
qualify: None,
|
qualify: None,
|
||||||
|
@ -4475,6 +4500,21 @@ fn test_parse_named_window() {
|
||||||
assert_eq!(actual_select_only, expected);
|
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]
|
#[test]
|
||||||
fn parse_aggregate_with_group_by() {
|
fn parse_aggregate_with_group_by() {
|
||||||
let sql = "SELECT a, COUNT(1), MIN(b), MAX(b) FROM foo GROUP BY a";
|
let sql = "SELECT a, COUNT(1), MIN(b), MAX(b) FROM foo GROUP BY a";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue