Skip to content

Commit a685e11

Browse files
git-hulkalamb
andauthored
Support parametric arguments to FUNCTION for ClickHouse dialect (#1315)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent 7a9793b commit a685e11

13 files changed

+119
-5
lines changed

src/ast/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4695,6 +4695,16 @@ impl fmt::Display for CloseCursor {
46954695
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
46964696
pub struct Function {
46974697
pub name: ObjectName,
4698+
/// The parameters to the function, including any options specified within the
4699+
/// delimiting parentheses.
4700+
///
4701+
/// Example:
4702+
/// ```plaintext
4703+
/// HISTOGRAM(0.5, 0.6)(x, y)
4704+
/// ```
4705+
///
4706+
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions)
4707+
pub parameters: FunctionArguments,
46984708
/// The arguments to the function, including any options specified within the
46994709
/// delimiting parentheses.
47004710
pub args: FunctionArguments,
@@ -4723,7 +4733,7 @@ pub struct Function {
47234733

47244734
impl fmt::Display for Function {
47254735
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4726-
write!(f, "{}{}", self.name, self.args)?;
4736+
write!(f, "{}{}{}", self.name, self.parameters, self.args)?;
47274737

47284738
if !self.within_group.is_empty() {
47294739
write!(

src/ast/visitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ where
533533
/// null_treatment: None,
534534
/// filter: None,
535535
/// over: None,
536+
/// parameters: FunctionArguments::None,
536537
/// within_group: vec![],
537538
/// });
538539
/// }

src/parser/mod.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use core::{
2727

2828
use log::debug;
2929

30+
use recursion::RecursionCounter;
3031
use IsLateral::*;
3132
use IsOptional::*;
3233

@@ -146,8 +147,6 @@ mod recursion {
146147
pub struct DepthGuard {}
147148
}
148149

149-
use recursion::RecursionCounter;
150-
151150
#[derive(PartialEq, Eq)]
152151
pub enum IsOptional {
153152
Optional,
@@ -1002,6 +1001,7 @@ impl<'a> Parser<'a> {
10021001
{
10031002
Ok(Expr::Function(Function {
10041003
name: ObjectName(vec![w.to_ident()]),
1004+
parameters: FunctionArguments::None,
10051005
args: FunctionArguments::None,
10061006
null_treatment: None,
10071007
filter: None,
@@ -1058,6 +1058,7 @@ impl<'a> Parser<'a> {
10581058
self.expect_token(&Token::RParen)?;
10591059
Ok(Expr::Function(Function {
10601060
name: ObjectName(vec![w.to_ident()]),
1061+
parameters: FunctionArguments::None,
10611062
args: FunctionArguments::Subquery(query),
10621063
filter: None,
10631064
null_treatment: None,
@@ -1293,6 +1294,7 @@ impl<'a> Parser<'a> {
12931294
self.expect_token(&Token::RParen)?;
12941295
return Ok(Expr::Function(Function {
12951296
name,
1297+
parameters: FunctionArguments::None,
12961298
args: FunctionArguments::Subquery(subquery),
12971299
filter: None,
12981300
null_treatment: None,
@@ -1301,7 +1303,16 @@ impl<'a> Parser<'a> {
13011303
}));
13021304
}
13031305

1304-
let args = self.parse_function_argument_list()?;
1306+
let mut args = self.parse_function_argument_list()?;
1307+
let mut parameters = FunctionArguments::None;
1308+
// ClickHouse aggregations support parametric functions like `HISTOGRAM(0.5, 0.6)(x, y)`
1309+
// which (0.5, 0.6) is a parameter to the function.
1310+
if dialect_of!(self is ClickHouseDialect | GenericDialect)
1311+
&& self.consume_token(&Token::LParen)
1312+
{
1313+
parameters = FunctionArguments::List(args);
1314+
args = self.parse_function_argument_list()?;
1315+
}
13051316

13061317
let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
13071318
self.expect_token(&Token::LParen)?;
@@ -1350,6 +1361,7 @@ impl<'a> Parser<'a> {
13501361

13511362
Ok(Expr::Function(Function {
13521363
name,
1364+
parameters,
13531365
args: FunctionArguments::List(args),
13541366
null_treatment,
13551367
filter,
@@ -1382,6 +1394,7 @@ impl<'a> Parser<'a> {
13821394
};
13831395
Ok(Expr::Function(Function {
13841396
name,
1397+
parameters: FunctionArguments::None,
13851398
args,
13861399
filter: None,
13871400
over: None,
@@ -6470,6 +6483,7 @@ impl<'a> Parser<'a> {
64706483
} else {
64716484
Ok(Statement::Call(Function {
64726485
name: object_name,
6486+
parameters: FunctionArguments::None,
64736487
args: FunctionArguments::None,
64746488
over: None,
64756489
filter: None,
@@ -8092,7 +8106,7 @@ impl<'a> Parser<'a> {
80928106
pub fn parse_query_body(&mut self, precedence: u8) -> Result<SetExpr, ParserError> {
80938107
// We parse the expression using a Pratt parser, as in `parse_expr()`.
80948108
// Start by parsing a restricted SELECT or a `(subquery)`:
8095-
let mut expr = if self.parse_keyword(Keyword::SELECT) {
8109+
let expr = if self.parse_keyword(Keyword::SELECT) {
80968110
SetExpr::Select(self.parse_select().map(Box::new)?)
80978111
} else if self.consume_token(&Token::LParen) {
80988112
// CTEs are not allowed here, but the parser currently accepts them
@@ -8111,6 +8125,17 @@ impl<'a> Parser<'a> {
81118125
);
81128126
};
81138127

8128+
self.parse_remaining_set_exprs(expr, precedence)
8129+
}
8130+
8131+
/// Parse any extra set expressions that may be present in a query body
8132+
///
8133+
/// (this is its own function to reduce required stack size in debug builds)
8134+
fn parse_remaining_set_exprs(
8135+
&mut self,
8136+
mut expr: SetExpr,
8137+
precedence: u8,
8138+
) -> Result<SetExpr, ParserError> {
81148139
loop {
81158140
// The query can be optionally followed by a set operator:
81168141
let op = self.parse_set_operator(&self.peek_token().token);

src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ pub fn join(relation: TableFactor) -> Join {
336336
pub fn call(function: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
337337
Expr::Function(Function {
338338
name: ObjectName(vec![Ident::new(function)]),
339+
parameters: FunctionArguments::None,
339340
args: FunctionArguments::List(FunctionArgumentList {
340341
duplicate_treatment: None,
341342
args: args

tests/sqlparser_clickhouse.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ fn parse_delimited_identifiers() {
183183
assert_eq!(
184184
&Expr::Function(Function {
185185
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
186+
parameters: FunctionArguments::None,
186187
args: FunctionArguments::List(FunctionArgumentList {
187188
duplicate_treatment: None,
188189
args: vec![],
@@ -553,6 +554,55 @@ fn parse_select_star_except() {
553554
clickhouse().verified_stmt("SELECT * EXCEPT (prev_status) FROM anomalies");
554555
}
555556

557+
#[test]
558+
fn parse_select_parametric_function() {
559+
match clickhouse_and_generic().verified_stmt("SELECT HISTOGRAM(0.5, 0.6)(x, y) FROM t") {
560+
Statement::Query(query) => {
561+
let projection: &Vec<SelectItem> = query.body.as_select().unwrap().projection.as_ref();
562+
assert_eq!(projection.len(), 1);
563+
match &projection[0] {
564+
UnnamedExpr(Expr::Function(f)) => {
565+
let args = match &f.args {
566+
FunctionArguments::List(ref args) => args,
567+
_ => unreachable!(),
568+
};
569+
assert_eq!(args.args.len(), 2);
570+
assert_eq!(
571+
args.args[0],
572+
FunctionArg::Unnamed(FunctionArgExpr::Expr(Identifier(Ident::from("x"))))
573+
);
574+
assert_eq!(
575+
args.args[1],
576+
FunctionArg::Unnamed(FunctionArgExpr::Expr(Identifier(Ident::from("y"))))
577+
);
578+
579+
let parameters = match f.parameters {
580+
FunctionArguments::List(ref args) => args,
581+
_ => unreachable!(),
582+
};
583+
assert_eq!(parameters.args.len(), 2);
584+
assert_eq!(
585+
parameters.args[0],
586+
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number(
587+
"0.5".parse().unwrap(),
588+
false
589+
))))
590+
);
591+
assert_eq!(
592+
parameters.args[1],
593+
FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number(
594+
"0.6".parse().unwrap(),
595+
false
596+
))))
597+
);
598+
}
599+
_ => unreachable!(),
600+
}
601+
}
602+
_ => unreachable!(),
603+
}
604+
}
605+
556606
#[test]
557607
fn parse_select_star_except_no_parens() {
558608
clickhouse().one_statement_parses_to(

tests/sqlparser_common.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ fn parse_select_count_wildcard() {
10451045
assert_eq!(
10461046
&Expr::Function(Function {
10471047
name: ObjectName(vec![Ident::new("COUNT")]),
1048+
parameters: FunctionArguments::None,
10481049
args: FunctionArguments::List(FunctionArgumentList {
10491050
duplicate_treatment: None,
10501051
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)],
@@ -1066,6 +1067,7 @@ fn parse_select_count_distinct() {
10661067
assert_eq!(
10671068
&Expr::Function(Function {
10681069
name: ObjectName(vec![Ident::new("COUNT")]),
1070+
parameters: FunctionArguments::None,
10691071
args: FunctionArguments::List(FunctionArgumentList {
10701072
duplicate_treatment: Some(DuplicateTreatment::Distinct),
10711073
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::UnaryOp {
@@ -2151,6 +2153,7 @@ fn parse_select_having() {
21512153
Some(Expr::BinaryOp {
21522154
left: Box::new(Expr::Function(Function {
21532155
name: ObjectName(vec![Ident::new("COUNT")]),
2156+
parameters: FunctionArguments::None,
21542157
args: FunctionArguments::List(FunctionArgumentList {
21552158
duplicate_treatment: None,
21562159
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Wildcard)],
@@ -2180,6 +2183,7 @@ fn parse_select_qualify() {
21802183
Some(Expr::BinaryOp {
21812184
left: Box::new(Expr::Function(Function {
21822185
name: ObjectName(vec![Ident::new("ROW_NUMBER")]),
2186+
parameters: FunctionArguments::None,
21832187
args: FunctionArguments::List(FunctionArgumentList {
21842188
duplicate_treatment: None,
21852189
args: vec![],
@@ -2523,6 +2527,7 @@ fn parse_listagg() {
25232527
assert_eq!(
25242528
&Expr::Function(Function {
25252529
name: ObjectName(vec![Ident::new("LISTAGG")]),
2530+
parameters: FunctionArguments::None,
25262531
args: FunctionArguments::List(FunctionArgumentList {
25272532
duplicate_treatment: Some(DuplicateTreatment::Distinct),
25282533
args: vec![
@@ -4227,6 +4232,7 @@ fn parse_named_argument_function() {
42274232
assert_eq!(
42284233
&Expr::Function(Function {
42294234
name: ObjectName(vec![Ident::new("FUN")]),
4235+
parameters: FunctionArguments::None,
42304236
args: FunctionArguments::List(FunctionArgumentList {
42314237
duplicate_treatment: None,
42324238
args: vec![
@@ -4265,6 +4271,7 @@ fn parse_named_argument_function_with_eq_operator() {
42654271
assert_eq!(
42664272
&Expr::Function(Function {
42674273
name: ObjectName(vec![Ident::new("FUN")]),
4274+
parameters: FunctionArguments::None,
42684275
args: FunctionArguments::List(FunctionArgumentList {
42694276
duplicate_treatment: None,
42704277
args: vec![
@@ -4337,6 +4344,7 @@ fn parse_window_functions() {
43374344
assert_eq!(
43384345
&Expr::Function(Function {
43394346
name: ObjectName(vec![Ident::new("row_number")]),
4347+
parameters: FunctionArguments::None,
43404348
args: FunctionArguments::List(FunctionArgumentList {
43414349
duplicate_treatment: None,
43424350
args: vec![],
@@ -4465,6 +4473,7 @@ fn test_parse_named_window() {
44654473
value: "MIN".to_string(),
44664474
quote_style: None,
44674475
}]),
4476+
parameters: FunctionArguments::None,
44684477
args: FunctionArguments::List(FunctionArgumentList {
44694478
duplicate_treatment: None,
44704479
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
@@ -4494,6 +4503,7 @@ fn test_parse_named_window() {
44944503
value: "MAX".to_string(),
44954504
quote_style: None,
44964505
}]),
4506+
parameters: FunctionArguments::None,
44974507
args: FunctionArguments::List(FunctionArgumentList {
44984508
duplicate_treatment: None,
44994509
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
@@ -8089,6 +8099,7 @@ fn parse_time_functions() {
80898099
let select = verified_only_select(&sql);
80908100
let select_localtime_func_call_ast = Function {
80918101
name: ObjectName(vec![Ident::new(func_name)]),
8102+
parameters: FunctionArguments::None,
80928103
args: FunctionArguments::List(FunctionArgumentList {
80938104
duplicate_treatment: None,
80948105
args: vec![],
@@ -9017,6 +9028,7 @@ fn parse_call() {
90179028
assert_eq!(
90189029
verified_stmt("CALL my_procedure('a')"),
90199030
Statement::Call(Function {
9031+
parameters: FunctionArguments::None,
90209032
args: FunctionArguments::List(FunctionArgumentList {
90219033
duplicate_treatment: None,
90229034
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
@@ -9418,6 +9430,7 @@ fn test_selective_aggregation() {
94189430
vec![
94199431
SelectItem::UnnamedExpr(Expr::Function(Function {
94209432
name: ObjectName(vec![Ident::new("ARRAY_AGG")]),
9433+
parameters: FunctionArguments::None,
94219434
args: FunctionArguments::List(FunctionArgumentList {
94229435
duplicate_treatment: None,
94239436
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
@@ -9435,6 +9448,7 @@ fn test_selective_aggregation() {
94359448
SelectItem::ExprWithAlias {
94369449
expr: Expr::Function(Function {
94379450
name: ObjectName(vec![Ident::new("ARRAY_AGG")]),
9451+
parameters: FunctionArguments::None,
94389452
args: FunctionArguments::List(FunctionArgumentList {
94399453
duplicate_treatment: None,
94409454
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(

tests/sqlparser_duckdb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@ fn test_duckdb_named_argument_function_with_assignment_operator() {
488488
assert_eq!(
489489
&Expr::Function(Function {
490490
name: ObjectName(vec![Ident::new("FUN")]),
491+
parameters: FunctionArguments::None,
491492
args: FunctionArguments::List(FunctionArgumentList {
492493
duplicate_treatment: None,
493494
args: vec![

tests/sqlparser_hive.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ fn parse_delimited_identifiers() {
381381
assert_eq!(
382382
&Expr::Function(Function {
383383
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
384+
parameters: FunctionArguments::None,
384385
args: FunctionArguments::List(FunctionArgumentList {
385386
duplicate_treatment: None,
386387
args: vec![],

tests/sqlparser_mssql.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ fn parse_delimited_identifiers() {
354354
assert_eq!(
355355
&Expr::Function(Function {
356356
name: ObjectName(vec![Ident::with_quote('"', "myfun")]),
357+
parameters: FunctionArguments::None,
357358
args: FunctionArguments::List(FunctionArgumentList {
358359
duplicate_treatment: None,
359360
args: vec![],

0 commit comments

Comments
 (0)