Skip to content

Commit 6cd7b55

Browse files
committed
bigquery,redshift,clickhouse: add support for IGNORE/RESPECT NULLS
1 parent cd3496d commit 6cd7b55

12 files changed

+104
-8
lines changed

src/ast/mod.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ pub enum Expr {
562562
///
563563
/// Syntax:
564564
/// ```sql
565-
/// MARCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
565+
/// MATCH (<col>, <col>, ...) AGAINST (<expr> [<search modifier>])
566566
///
567567
/// <col> = CompoundIdentifier
568568
/// <expr> = String literal
@@ -3409,6 +3409,16 @@ pub struct Function {
34093409
pub special: bool,
34103410
// Required ordering for the function (if empty, there is no requirement).
34113411
pub order_by: Vec<OrderByExpr>,
3412+
// IGNORE NULLS or RESPECT NULLS
3413+
pub null_treatment: Option<NullTreatment>,
3414+
}
3415+
3416+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3417+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3418+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
3419+
pub enum NullTreatment {
3420+
IGNORE,
3421+
RESPECT,
34123422
}
34133423

34143424
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
@@ -3440,13 +3450,23 @@ impl fmt::Display for Function {
34403450
} else {
34413451
""
34423452
};
3453+
3454+
let null_treatment = if let Some(null_treatment) = self.null_treatment {
3455+
match null_treatment {
3456+
NullTreatment::IGNORE => " IGNORE NULLS",
3457+
NullTreatment::RESPECT => " RESPECT NULLS",
3458+
}
3459+
} else {
3460+
""
3461+
};
34433462
write!(
34443463
f,
3445-
"{}({}{}{order_by}{})",
3464+
"{}({}{}{order_by}{}){}",
34463465
self.name,
34473466
if self.distinct { "DISTINCT " } else { "" },
34483467
display_comma_separated(&self.args),
34493468
display_comma_separated(&self.order_by),
3469+
null_treatment
34503470
)?;
34513471

34523472
if let Some(o) = &self.over {

src/keywords.rs

+1
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ define_keywords!(
490490
REPEATABLE,
491491
REPLACE,
492492
REPLICATION,
493+
RESPECT,
493494
RESTRICT,
494495
RESULT,
495496
RETURN,

src/parser.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ impl<'a> Parser<'a> {
705705
distinct: false,
706706
special: true,
707707
order_by: vec![],
708+
null_treatment: None,
708709
}))
709710
}
710711
Keyword::CURRENT_TIMESTAMP
@@ -880,7 +881,14 @@ impl<'a> Parser<'a> {
880881
pub fn parse_function(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
881882
self.expect_token(&Token::LParen)?;
882883
let distinct = self.parse_all_or_distinct()?.is_some();
883-
let (args, order_by) = self.parse_optional_args_with_orderby()?;
884+
let (args, order_by, mut null_treatment) = self.parse_optional_args_with_orderby()?;
885+
886+
if self.parse_keywords(&[Keyword::IGNORE, Keyword::NULLS]) {
887+
null_treatment = Some(NullTreatment::IGNORE)
888+
} else if self.parse_keywords(&[Keyword::RESPECT, Keyword::NULLS]) {
889+
null_treatment = Some(NullTreatment::RESPECT)
890+
}
891+
884892
let over = if self.parse_keyword(Keyword::OVER) {
885893
if self.consume_token(&Token::LParen) {
886894
let window_spec = self.parse_window_spec()?;
@@ -898,14 +906,15 @@ impl<'a> Parser<'a> {
898906
distinct,
899907
special: false,
900908
order_by,
909+
null_treatment,
901910
}))
902911
}
903912

904913
pub fn parse_time_functions(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
905-
let (args, order_by) = if self.consume_token(&Token::LParen) {
914+
let (args, order_by, null_treatment) = if self.consume_token(&Token::LParen) {
906915
self.parse_optional_args_with_orderby()?
907916
} else {
908-
(vec![], vec![])
917+
(vec![], vec![], None)
909918
};
910919
Ok(Expr::Function(Function {
911920
name,
@@ -914,6 +923,7 @@ impl<'a> Parser<'a> {
914923
distinct: false,
915924
special: false,
916925
order_by,
926+
null_treatment,
917927
}))
918928
}
919929

@@ -6485,18 +6495,25 @@ impl<'a> Parser<'a> {
64856495

64866496
pub fn parse_optional_args_with_orderby(
64876497
&mut self,
6488-
) -> Result<(Vec<FunctionArg>, Vec<OrderByExpr>), ParserError> {
6498+
) -> Result<(Vec<FunctionArg>, Vec<OrderByExpr>, Option<NullTreatment>), ParserError> {
64896499
if self.consume_token(&Token::RParen) {
6490-
Ok((vec![], vec![]))
6500+
Ok((vec![], vec![], None))
64916501
} else {
64926502
let args = self.parse_comma_separated(Parser::parse_function_args)?;
64936503
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
64946504
self.parse_comma_separated(Parser::parse_order_by_expr)?
64956505
} else {
64966506
vec![]
64976507
};
6508+
let null_treatment = if self.parse_keywords(&[Keyword::IGNORE, Keyword::NULLS]) {
6509+
Some(NullTreatment::IGNORE)
6510+
} else if self.parse_keywords(&[Keyword::RESPECT, Keyword::NULLS]) {
6511+
Some(NullTreatment::RESPECT)
6512+
}else {
6513+
None
6514+
};
64986515
self.expect_token(&Token::RParen)?;
6499-
Ok((args, order_by))
6516+
Ok((args, order_by, null_treatment))
65006517
}
65016518
}
65026519

tests/sqlparser_bigquery.rs

+10
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,15 @@ fn test_select_wildcard_with_except() {
351351
);
352352
}
353353

354+
355+
#[test]
356+
fn test_select_ignore_nulls() {
357+
bigquery().one_statement_parses_to(
358+
"SELECT last_value(user_id IGNORE NULLS) OVER (PARTITION BY anonymous_id ORDER BY tstamp ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS user_id FROM table1",
359+
"SELECT last_value(user_id) IGNORE NULLS OVER (PARTITION BY anonymous_id ORDER BY tstamp ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS user_id FROM table1"
360+
);
361+
}
362+
354363
#[test]
355364
fn test_select_wildcard_with_replace() {
356365
let select = bigquery_and_generic()
@@ -438,6 +447,7 @@ fn parse_map_access_offset() {
438447
distinct: false,
439448
special: false,
440449
order_by: vec![],
450+
null_treatment: None,
441451
})],
442452
})
443453
);

tests/sqlparser_clickhouse.rs

+9
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn parse_array_access_expr() {
5656
distinct: false,
5757
special: false,
5858
order_by: vec![],
59+
null_treatment: None,
5960
})],
6061
})],
6162
into: None,
@@ -93,6 +94,7 @@ fn parse_array_access_expr() {
9394
distinct: false,
9495
special: false,
9596
order_by: vec![],
97+
null_treatment: None,
9698
})]
9799
}),
98100
op: BinaryOperator::NotEq,
@@ -142,6 +144,7 @@ fn parse_array_fn() {
142144
distinct: false,
143145
special: false,
144146
order_by: vec![],
147+
null_treatment: None,
145148
}),
146149
expr_from_projection(only(&select.projection))
147150
);
@@ -197,6 +200,7 @@ fn parse_delimited_identifiers() {
197200
distinct: false,
198201
special: false,
199202
order_by: vec![],
203+
null_treatment: None,
200204
}),
201205
expr_from_projection(&select.projection[1]),
202206
);
@@ -378,6 +382,11 @@ fn parse_double_equal() {
378382
);
379383
}
380384

385+
#[test]
386+
fn parse_select_ignore_nulls(){
387+
clickhouse().verified_stmt("SELECT last_value(b) IGNORE NULLS FROM test_data");
388+
}
389+
381390
fn clickhouse() -> TestedDialects {
382391
TestedDialects {
383392
dialects: vec![Box::new(ClickHouseDialect {})],

tests/sqlparser_common.rs

+18
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ fn parse_select_count_wildcard() {
842842
distinct: false,
843843
special: false,
844844
order_by: vec![],
845+
null_treatment: None,
845846
}),
846847
expr_from_projection(only(&select.projection))
847848
);
@@ -862,6 +863,7 @@ fn parse_select_count_distinct() {
862863
distinct: true,
863864
special: false,
864865
order_by: vec![],
866+
null_treatment: None,
865867
}),
866868
expr_from_projection(only(&select.projection))
867869
);
@@ -1700,6 +1702,7 @@ fn parse_select_having() {
17001702
distinct: false,
17011703
special: false,
17021704
order_by: vec![],
1705+
null_treatment: None,
17031706
})),
17041707
op: BinaryOperator::Gt,
17051708
right: Box::new(Expr::Value(number("1"))),
@@ -3152,6 +3155,7 @@ fn parse_scalar_function_in_projection() {
31523155
distinct: false,
31533156
special: false,
31543157
order_by: vec![],
3158+
null_treatment: None,
31553159
}),
31563160
expr_from_projection(only(&select.projection))
31573161
);
@@ -3271,6 +3275,7 @@ fn parse_named_argument_function() {
32713275
distinct: false,
32723276
special: false,
32733277
order_by: vec![],
3278+
null_treatment: None,
32743279
}),
32753280
expr_from_projection(only(&select.projection))
32763281
);
@@ -3310,6 +3315,7 @@ fn parse_window_functions() {
33103315
distinct: false,
33113316
special: false,
33123317
order_by: vec![],
3318+
null_treatment: None,
33133319
}),
33143320
expr_from_projection(&select.projection[0])
33153321
);
@@ -3348,6 +3354,7 @@ fn test_parse_named_window() {
33483354
distinct: false,
33493355
special: false,
33503356
order_by: vec![],
3357+
null_treatment: None,
33513358
}),
33523359
alias: Ident {
33533360
value: "min1".to_string(),
@@ -3373,6 +3380,7 @@ fn test_parse_named_window() {
33733380
distinct: false,
33743381
special: false,
33753382
order_by: vec![],
3383+
null_treatment: None,
33763384
}),
33773385
alias: Ident {
33783386
value: "max1".to_string(),
@@ -3835,6 +3843,7 @@ fn parse_at_timezone() {
38353843
distinct: false,
38363844
special: false,
38373845
order_by: vec![],
3846+
null_treatment: None,
38383847
})),
38393848
time_zone: "UTC-06:00".to_string(),
38403849
},
@@ -3862,6 +3871,7 @@ fn parse_at_timezone() {
38623871
distinct: false,
38633872
special: false,
38643873
order_by: vec![],
3874+
null_treatment: None,
38653875
},)),
38663876
time_zone: "UTC-06:00".to_string(),
38673877
},),),
@@ -3873,6 +3883,7 @@ fn parse_at_timezone() {
38733883
distinct: false,
38743884
special: false,
38753885
order_by: vec![],
3886+
null_treatment: None,
38763887
},),
38773888
alias: Ident {
38783889
value: "hour".to_string(),
@@ -4031,6 +4042,7 @@ fn parse_table_function() {
40314042
distinct: false,
40324043
special: false,
40334044
order_by: vec![],
4045+
null_treatment: None,
40344046
});
40354047
assert_eq!(expr, expected_expr);
40364048
assert_eq!(alias, table_alias("a"))
@@ -6482,6 +6494,7 @@ fn parse_time_functions() {
64826494
distinct: false,
64836495
special: false,
64846496
order_by: vec![],
6497+
null_treatment: None,
64856498
}),
64866499
expr_from_projection(&select.projection[0])
64876500
);
@@ -6499,6 +6512,7 @@ fn parse_time_functions() {
64996512
distinct: false,
65006513
special: false,
65016514
order_by: vec![],
6515+
null_treatment: None,
65026516
}),
65036517
expr_from_projection(&select.projection[0])
65046518
);
@@ -6516,6 +6530,7 @@ fn parse_time_functions() {
65166530
distinct: false,
65176531
special: false,
65186532
order_by: vec![],
6533+
null_treatment: None,
65196534
}),
65206535
expr_from_projection(&select.projection[0])
65216536
);
@@ -6533,6 +6548,7 @@ fn parse_time_functions() {
65336548
distinct: false,
65346549
special: false,
65356550
order_by: vec![],
6551+
null_treatment: None,
65366552
}),
65376553
expr_from_projection(&select.projection[0])
65386554
);
@@ -6550,6 +6566,7 @@ fn parse_time_functions() {
65506566
distinct: false,
65516567
special: false,
65526568
order_by: vec![],
6569+
null_treatment: None,
65536570
}),
65546571
expr_from_projection(&select.projection[0])
65556572
);
@@ -7015,6 +7032,7 @@ fn parse_pivot_table() {
70157032
distinct: false,
70167033
special: false,
70177034
order_by: vec![],
7035+
null_treatment: None,
70187036
}),
70197037
value_column: vec![Ident::new("a"), Ident::new("MONTH")],
70207038
pivot_values: vec![

tests/sqlparser_hive.rs

+1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ fn parse_delimited_identifiers() {
347347
distinct: false,
348348
special: false,
349349
order_by: vec![],
350+
null_treatment: None,
350351
}),
351352
expr_from_projection(&select.projection[1]),
352353
);

tests/sqlparser_mssql.rs

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ fn parse_delimited_identifiers() {
178178
distinct: false,
179179
special: false,
180180
order_by: vec![],
181+
null_treatment: None,
181182
}),
182183
expr_from_projection(&select.projection[1]),
183184
);

0 commit comments

Comments
 (0)