Skip to content

Commit aeaafbe

Browse files
authored
Extend lambda support for ClickHouse and DuckDB dialects (apache#1686)
1 parent 94b2ff7 commit aeaafbe

File tree

5 files changed

+78
-64
lines changed

5 files changed

+78
-64
lines changed

src/ast/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,9 @@ pub enum Expr {
10451045
/// param -> expr | (param1, ...) -> expr
10461046
/// ```
10471047
///
1048-
/// See <https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html>.
1048+
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function)
1049+
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
1050+
/// [DuckDb](https://duckdb.org/docs/sql/functions/lambda.html)
10491051
Lambda(LambdaFunction),
10501052
}
10511053

src/dialect/clickhouse.rs

+5
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,9 @@ impl Dialect for ClickHouseDialect {
7070
fn supports_dictionary_syntax(&self) -> bool {
7171
true
7272
}
73+
74+
/// See <https://clickhouse.com/docs/en/sql-reference/functions#higher-order-functions---operator-and-lambdaparams-expr-function>
75+
fn supports_lambda_functions(&self) -> bool {
76+
true
77+
}
7378
}

src/dialect/duckdb.rs

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ impl Dialect for DuckDbDialect {
6565
true
6666
}
6767

68+
/// See <https://duckdb.org/docs/sql/functions/lambda.html>
69+
fn supports_lambda_functions(&self) -> bool {
70+
true
71+
}
72+
6873
// DuckDB is compatible with PostgreSQL syntax for this statement,
6974
// although not all features may be implemented.
7075
fn supports_explain_with_utility_options(&self) -> bool {

tests/sqlparser_common.rs

+65
Original file line numberDiff line numberDiff line change
@@ -13332,3 +13332,68 @@ fn test_trailing_commas_in_from() {
1333213332
"SELECT 1, 2 FROM (SELECT * FROM t1), (SELECT * FROM t2)",
1333313333
);
1333413334
}
13335+
13336+
#[test]
13337+
fn test_lambdas() {
13338+
let dialects = all_dialects_where(|d| d.supports_lambda_functions());
13339+
13340+
#[rustfmt::skip]
13341+
let sql = concat!(
13342+
"SELECT array_sort(array('Hello', 'World'), ",
13343+
"(p1, p2) -> CASE WHEN p1 = p2 THEN 0 ",
13344+
"WHEN reverse(p1) < reverse(p2) THEN -1 ",
13345+
"ELSE 1 END)",
13346+
);
13347+
pretty_assertions::assert_eq!(
13348+
SelectItem::UnnamedExpr(call(
13349+
"array_sort",
13350+
[
13351+
call(
13352+
"array",
13353+
[
13354+
Expr::Value(Value::SingleQuotedString("Hello".to_owned())),
13355+
Expr::Value(Value::SingleQuotedString("World".to_owned()))
13356+
]
13357+
),
13358+
Expr::Lambda(LambdaFunction {
13359+
params: OneOrManyWithParens::Many(vec![Ident::new("p1"), Ident::new("p2")]),
13360+
body: Box::new(Expr::Case {
13361+
operand: None,
13362+
conditions: vec![
13363+
Expr::BinaryOp {
13364+
left: Box::new(Expr::Identifier(Ident::new("p1"))),
13365+
op: BinaryOperator::Eq,
13366+
right: Box::new(Expr::Identifier(Ident::new("p2")))
13367+
},
13368+
Expr::BinaryOp {
13369+
left: Box::new(call(
13370+
"reverse",
13371+
[Expr::Identifier(Ident::new("p1"))]
13372+
)),
13373+
op: BinaryOperator::Lt,
13374+
right: Box::new(call(
13375+
"reverse",
13376+
[Expr::Identifier(Ident::new("p2"))]
13377+
))
13378+
}
13379+
],
13380+
results: vec![
13381+
Expr::Value(number("0")),
13382+
Expr::UnaryOp {
13383+
op: UnaryOperator::Minus,
13384+
expr: Box::new(Expr::Value(number("1")))
13385+
}
13386+
],
13387+
else_result: Some(Box::new(Expr::Value(number("1"))))
13388+
})
13389+
})
13390+
]
13391+
)),
13392+
dialects.verified_only_select(sql).projection[0]
13393+
);
13394+
13395+
dialects.verified_expr(
13396+
"map_zip_with(map(1, 'a', 2, 'b'), map(1, 'x', 2, 'y'), (k, v1, v2) -> concat(v1, v2))",
13397+
);
13398+
dialects.verified_expr("transform(array(1, 2, 3), x -> x + 1)");
13399+
}

tests/sqlparser_databricks.rs

-63
Original file line numberDiff line numberDiff line change
@@ -83,69 +83,6 @@ fn test_databricks_exists() {
8383
);
8484
}
8585

86-
#[test]
87-
fn test_databricks_lambdas() {
88-
#[rustfmt::skip]
89-
let sql = concat!(
90-
"SELECT array_sort(array('Hello', 'World'), ",
91-
"(p1, p2) -> CASE WHEN p1 = p2 THEN 0 ",
92-
"WHEN reverse(p1) < reverse(p2) THEN -1 ",
93-
"ELSE 1 END)",
94-
);
95-
pretty_assertions::assert_eq!(
96-
SelectItem::UnnamedExpr(call(
97-
"array_sort",
98-
[
99-
call(
100-
"array",
101-
[
102-
Expr::Value(Value::SingleQuotedString("Hello".to_owned())),
103-
Expr::Value(Value::SingleQuotedString("World".to_owned()))
104-
]
105-
),
106-
Expr::Lambda(LambdaFunction {
107-
params: OneOrManyWithParens::Many(vec![Ident::new("p1"), Ident::new("p2")]),
108-
body: Box::new(Expr::Case {
109-
operand: None,
110-
conditions: vec![
111-
Expr::BinaryOp {
112-
left: Box::new(Expr::Identifier(Ident::new("p1"))),
113-
op: BinaryOperator::Eq,
114-
right: Box::new(Expr::Identifier(Ident::new("p2")))
115-
},
116-
Expr::BinaryOp {
117-
left: Box::new(call(
118-
"reverse",
119-
[Expr::Identifier(Ident::new("p1"))]
120-
)),
121-
op: BinaryOperator::Lt,
122-
right: Box::new(call(
123-
"reverse",
124-
[Expr::Identifier(Ident::new("p2"))]
125-
))
126-
}
127-
],
128-
results: vec![
129-
Expr::Value(number("0")),
130-
Expr::UnaryOp {
131-
op: UnaryOperator::Minus,
132-
expr: Box::new(Expr::Value(number("1")))
133-
}
134-
],
135-
else_result: Some(Box::new(Expr::Value(number("1"))))
136-
})
137-
})
138-
]
139-
)),
140-
databricks().verified_only_select(sql).projection[0]
141-
);
142-
143-
databricks().verified_expr(
144-
"map_zip_with(map(1, 'a', 2, 'b'), map(1, 'x', 2, 'y'), (k, v1, v2) -> concat(v1, v2))",
145-
);
146-
databricks().verified_expr("transform(array(1, 2, 3), x -> x + 1)");
147-
}
148-
14986
#[test]
15087
fn test_values_clause() {
15188
let values = Values {

0 commit comments

Comments
 (0)