Skip to content

Commit 2bf39af

Browse files
alambiffyio
authored andcommitted
Accept WINDOW clause after QUALIFY when parsing (apache#1248)
Co-authored-by: ifeanyi <[email protected]>
1 parent a4995b5 commit 2bf39af

8 files changed

+86
-14
lines changed

src/ast/query.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ pub struct Select {
245245
pub named_window: Vec<NamedWindowDefinition>,
246246
/// QUALIFY (Snowflake)
247247
pub qualify: Option<Expr>,
248+
/// The positioning of QUALIFY and WINDOW clauses differ between dialects.
249+
/// e.g. BigQuery requires that WINDOW comes after QUALIFY, while DUCKDB accepts
250+
/// WINDOW before QUALIFY.
251+
/// We accept either positioning and flag the accepted variant.
252+
pub window_before_qualify: bool,
248253
/// BigQuery syntax: `SELECT AS VALUE | SELECT AS STRUCT`
249254
pub value_table_mode: Option<ValueTableMode>,
250255
/// STARTING WITH .. CONNECT BY
@@ -310,11 +315,20 @@ impl fmt::Display for Select {
310315
if let Some(ref having) = self.having {
311316
write!(f, " HAVING {having}")?;
312317
}
313-
if !self.named_window.is_empty() {
314-
write!(f, " WINDOW {}", display_comma_separated(&self.named_window))?;
315-
}
316-
if let Some(ref qualify) = self.qualify {
317-
write!(f, " QUALIFY {qualify}")?;
318+
if self.window_before_qualify {
319+
if !self.named_window.is_empty() {
320+
write!(f, " WINDOW {}", display_comma_separated(&self.named_window))?;
321+
}
322+
if let Some(ref qualify) = self.qualify {
323+
write!(f, " QUALIFY {qualify}")?;
324+
}
325+
} else {
326+
if let Some(ref qualify) = self.qualify {
327+
write!(f, " QUALIFY {qualify}")?;
328+
}
329+
if !self.named_window.is_empty() {
330+
write!(f, " WINDOW {}", display_comma_separated(&self.named_window))?;
331+
}
318332
}
319333
if let Some(ref connect_by) = self.connect_by {
320334
write!(f, " {connect_by}")?;

src/parser/mod.rs

+22-9
Original file line numberDiff line numberDiff line change
@@ -7854,16 +7854,28 @@ impl<'a> Parser<'a> {
78547854
None
78557855
};
78567856

7857-
let named_windows = if self.parse_keyword(Keyword::WINDOW) {
7858-
self.parse_comma_separated(Parser::parse_named_window)?
7859-
} else {
7860-
vec![]
7861-
};
7862-
7863-
let qualify = if self.parse_keyword(Keyword::QUALIFY) {
7864-
Some(self.parse_expr()?)
7857+
// Accept QUALIFY and WINDOW in any order and flag accordingly.
7858+
let (named_windows, qualify, window_before_qualify) = if self.parse_keyword(Keyword::WINDOW)
7859+
{
7860+
let named_windows = self.parse_comma_separated(Parser::parse_named_window)?;
7861+
if self.parse_keyword(Keyword::QUALIFY) {
7862+
(named_windows, Some(self.parse_expr()?), true)
7863+
} else {
7864+
(named_windows, None, true)
7865+
}
7866+
} else if self.parse_keyword(Keyword::QUALIFY) {
7867+
let qualify = Some(self.parse_expr()?);
7868+
if self.parse_keyword(Keyword::WINDOW) {
7869+
(
7870+
self.parse_comma_separated(Parser::parse_named_window)?,
7871+
qualify,
7872+
false,
7873+
)
7874+
} else {
7875+
(Default::default(), qualify, false)
7876+
}
78657877
} else {
7866-
None
7878+
Default::default()
78677879
};
78687880

78697881
let connect_by = if self.dialect.supports_connect_by()
@@ -7891,6 +7903,7 @@ impl<'a> Parser<'a> {
78917903
sort_by,
78927904
having,
78937905
named_window: named_windows,
7906+
window_before_qualify,
78947907
qualify,
78957908
value_table_mode,
78967909
connect_by,

tests/sqlparser_clickhouse.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ fn parse_map_access_expr() {
114114
sort_by: vec![],
115115
having: None,
116116
named_window: vec![],
117+
window_before_qualify: false,
117118
qualify: None,
118119
value_table_mode: None,
119120
connect_by: None,

tests/sqlparser_common.rs

+29
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ fn parse_update_set_from() {
401401
having: None,
402402
named_window: vec![],
403403
qualify: None,
404+
window_before_qualify: false,
404405
value_table_mode: None,
405406
connect_by: None,
406407
}))),
@@ -4546,12 +4547,34 @@ fn test_parse_named_window() {
45464547
),
45474548
],
45484549
qualify: None,
4550+
window_before_qualify: true,
45494551
value_table_mode: None,
45504552
connect_by: None,
45514553
};
45524554
assert_eq!(actual_select_only, expected);
45534555
}
45544556

4557+
#[test]
4558+
fn parse_window_and_qualify_clause() {
4559+
let sql = "SELECT \
4560+
MIN(c12) OVER window1 AS min1 \
4561+
FROM aggregate_test_100 \
4562+
QUALIFY ROW_NUMBER() OVER my_window \
4563+
WINDOW window1 AS (ORDER BY C12), \
4564+
window2 AS (PARTITION BY C11) \
4565+
ORDER BY C3";
4566+
verified_only_select(sql);
4567+
4568+
let sql = "SELECT \
4569+
MIN(c12) OVER window1 AS min1 \
4570+
FROM aggregate_test_100 \
4571+
WINDOW window1 AS (ORDER BY C12), \
4572+
window2 AS (PARTITION BY C11) \
4573+
QUALIFY ROW_NUMBER() OVER my_window \
4574+
ORDER BY C3";
4575+
verified_only_select(sql);
4576+
}
4577+
45554578
#[test]
45564579
fn parse_window_clause_named_window() {
45574580
let sql = "SELECT * FROM mytable WINDOW window1 AS window2";
@@ -4918,6 +4941,7 @@ fn parse_interval_and_or_xor() {
49184941
having: None,
49194942
named_window: vec![],
49204943
qualify: None,
4944+
window_before_qualify: false,
49214945
value_table_mode: None,
49224946
connect_by: None,
49234947
}))),
@@ -6872,6 +6896,7 @@ fn lateral_function() {
68726896
having: None,
68736897
named_window: vec![],
68746898
qualify: None,
6899+
window_before_qualify: false,
68756900
value_table_mode: None,
68766901
connect_by: None,
68776902
};
@@ -7516,6 +7541,7 @@ fn parse_merge() {
75167541
sort_by: vec![],
75177542
having: None,
75187543
named_window: vec![],
7544+
window_before_qualify: false,
75197545
qualify: None,
75207546
value_table_mode: None,
75217547
connect_by: None,
@@ -8958,6 +8984,7 @@ fn parse_unload() {
89588984
sort_by: vec![],
89598985
having: None,
89608986
named_window: vec![],
8987+
window_before_qualify: false,
89618988
qualify: None,
89628989
value_table_mode: None,
89638990
connect_by: None,
@@ -9112,6 +9139,7 @@ fn parse_connect_by() {
91129139
having: None,
91139140
named_window: vec![],
91149141
qualify: None,
9142+
window_before_qualify: false,
91159143
value_table_mode: None,
91169144
connect_by: Some(ConnectBy {
91179145
condition: Expr::BinaryOp {
@@ -9199,6 +9227,7 @@ fn parse_connect_by() {
91999227
having: None,
92009228
named_window: vec![],
92019229
qualify: None,
9230+
window_before_qualify: false,
92029231
value_table_mode: None,
92039232
connect_by: Some(ConnectBy {
92049233
condition: Expr::BinaryOp {

tests/sqlparser_duckdb.rs

+2
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ fn test_select_union_by_name() {
177177
sort_by: vec![],
178178
having: None,
179179
named_window: vec![],
180+
window_before_qualify: false,
180181
qualify: None,
181182
value_table_mode: None,
182183
connect_by: None,
@@ -214,6 +215,7 @@ fn test_select_union_by_name() {
214215
sort_by: vec![],
215216
having: None,
216217
named_window: vec![],
218+
window_before_qualify: false,
217219
qualify: None,
218220
value_table_mode: None,
219221
connect_by: None,

tests/sqlparser_mssql.rs

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ fn parse_create_procedure() {
117117
sort_by: vec![],
118118
having: None,
119119
named_window: vec![],
120+
window_before_qualify: false,
120121
qualify: None,
121122
value_table_mode: None,
122123
connect_by: None,
@@ -525,6 +526,7 @@ fn parse_substring_in_select() {
525526
having: None,
526527
named_window: vec![],
527528
qualify: None,
529+
window_before_qualify: false,
528530
value_table_mode: None,
529531
connect_by: None,
530532
}))),

tests/sqlparser_mysql.rs

+8
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
908908
having: None,
909909
named_window: vec![],
910910
qualify: None,
911+
window_before_qualify: false,
911912
value_table_mode: None,
912913
connect_by: None,
913914
}))),
@@ -954,6 +955,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
954955
having: None,
955956
named_window: vec![],
956957
qualify: None,
958+
window_before_qualify: false,
957959
value_table_mode: None,
958960
connect_by: None,
959961
}))),
@@ -997,6 +999,7 @@ fn parse_escaped_backticks_with_escape() {
997999
having: None,
9981000
named_window: vec![],
9991001
qualify: None,
1002+
window_before_qualify: false,
10001003
value_table_mode: None,
10011004
connect_by: None,
10021005
}))),
@@ -1040,6 +1043,7 @@ fn parse_escaped_backticks_with_no_escape() {
10401043
having: None,
10411044
named_window: vec![],
10421045
qualify: None,
1046+
window_before_qualify: false,
10431047
value_table_mode: None,
10441048
connect_by: None,
10451049
}))),
@@ -1745,6 +1749,7 @@ fn parse_select_with_numeric_prefix_column_name() {
17451749
having: None,
17461750
named_window: vec![],
17471751
qualify: None,
1752+
window_before_qualify: false,
17481753
value_table_mode: None,
17491754
connect_by: None,
17501755
})))
@@ -1797,6 +1802,7 @@ fn parse_select_with_concatenation_of_exp_number_and_numeric_prefix_column() {
17971802
having: None,
17981803
named_window: vec![],
17991804
qualify: None,
1805+
window_before_qualify: false,
18001806
value_table_mode: None,
18011807
connect_by: None,
18021808
})))
@@ -2291,6 +2297,7 @@ fn parse_substring_in_select() {
22912297
sort_by: vec![],
22922298
having: None,
22932299
named_window: vec![],
2300+
window_before_qualify: false,
22942301
qualify: None,
22952302
value_table_mode: None,
22962303
connect_by: None,
@@ -2603,6 +2610,7 @@ fn parse_hex_string_introducer() {
26032610
sort_by: vec![],
26042611
having: None,
26052612
named_window: vec![],
2613+
window_before_qualify: false,
26062614
qualify: None,
26072615
value_table_mode: None,
26082616
into: None,

tests/sqlparser_postgres.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,7 @@ fn parse_copy_to() {
10781078
group_by: GroupByExpr::Expressions(vec![]),
10791079
having: None,
10801080
named_window: vec![],
1081+
window_before_qualify: false,
10811082
cluster_by: vec![],
10821083
distribute_by: vec![],
10831084
sort_by: vec![],
@@ -2170,6 +2171,7 @@ fn parse_array_subquery_expr() {
21702171
having: None,
21712172
named_window: vec![],
21722173
qualify: None,
2174+
window_before_qualify: false,
21732175
value_table_mode: None,
21742176
connect_by: None,
21752177
}))),
@@ -2188,6 +2190,7 @@ fn parse_array_subquery_expr() {
21882190
having: None,
21892191
named_window: vec![],
21902192
qualify: None,
2193+
window_before_qualify: false,
21912194
value_table_mode: None,
21922195
connect_by: None,
21932196
}))),

0 commit comments

Comments
 (0)