Skip to content

Commit 3267aec

Browse files
committed
Snowflake: Add support for Lambda functions
1 parent 2e5532c commit 3267aec

3 files changed

Lines changed: 90 additions & 42 deletions

File tree

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ define_keywords!(
827827
RECEIVE,
828828
RECLUSTER,
829829
RECURSIVE,
830+
REDUCE,
830831
REF,
831832
REFERENCES,
832833
REFERENCING,
@@ -1051,6 +1052,7 @@ define_keywords!(
10511052
TRACE,
10521053
TRAILING,
10531054
TRANSACTION,
1055+
TRANSFORM,
10541056
TRANSIENT,
10551057
TRANSLATE,
10561058
TRANSLATE_REGEX,

src/parser/mod.rs

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,43 @@ impl<'a> Parser<'a> {
15561556
Keyword::LAMBDA if self.dialect.supports_lambda_functions() => {
15571557
Ok(Some(self.parse_lambda_expr()?))
15581558
}
1559+
Keyword::FILTER
1560+
| Keyword::TRANSFORM
1561+
| Keyword::REDUCE if self.dialect.supports_lambda_functions() => {
1562+
self.expect_token(&Token::LParen)?;
1563+
let array = self.parse_expr()?;
1564+
self.expect_token(&Token::Comma)?;
1565+
let initial_value = if w.keyword == Keyword::REDUCE {
1566+
let initial_value = self.parse_expr()?;
1567+
self.expect_token(&Token::Comma)?;
1568+
Some(initial_value)
1569+
} else {
1570+
None
1571+
};
1572+
let lambda = self.parse_lambda_expr_with_typed_args()?;
1573+
let mut args = vec![
1574+
FunctionArg::Unnamed(FunctionArgExpr::Expr(array)),
1575+
FunctionArg::Unnamed(FunctionArgExpr::Expr(lambda)),
1576+
];
1577+
if let Some(initial_value) = initial_value {
1578+
args.insert(1, FunctionArg::Unnamed(FunctionArgExpr::Expr(initial_value)));
1579+
}
1580+
self.expect_token(&Token::RParen)?;
1581+
Ok(Some(Expr::Function(Function {
1582+
name: ObjectName::from(vec![w.to_ident(w_span)]),
1583+
uses_odbc_syntax: false,
1584+
parameters: FunctionArguments::None,
1585+
args: FunctionArguments::List(FunctionArgumentList {
1586+
duplicate_treatment: None,
1587+
clauses: vec![],
1588+
args,
1589+
}),
1590+
filter: None,
1591+
null_treatment: None,
1592+
over: None,
1593+
within_group: vec![],
1594+
})))
1595+
}
15591596
_ if self.dialect.supports_geometric_types() => match w.keyword {
15601597
Keyword::CIRCLE => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Circle)?)),
15611598
Keyword::BOX => Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricBox)?)),
@@ -1614,21 +1651,6 @@ impl<'a> Parser<'a> {
16141651
syntax: LambdaSyntax::Arrow,
16151652
}))
16161653
}
1617-
Token::Word(_)
1618-
if self.peek_nth_token(1).token == Token::Arrow
1619-
&& self.dialect.supports_lambda_functions() =>
1620-
{
1621-
let first_param = LambdaFunctionParameter {
1622-
name: w.to_ident(self.peek_nth_token_ref(1).span),
1623-
data_type: self.maybe_parse(|parser| parser.parse_data_type())?,
1624-
};
1625-
self.expect_token(&Token::Arrow)?;
1626-
Ok(Expr::Lambda(LambdaFunction {
1627-
params: OneOrManyWithParens::One(first_param),
1628-
body: Box::new(self.parse_expr()?),
1629-
syntax: LambdaSyntax::Arrow,
1630-
}))
1631-
}
16321654
_ => Ok(Expr::Identifier(w.to_ident(w_span))),
16331655
}
16341656
}
@@ -2210,12 +2232,7 @@ impl<'a> Parser<'a> {
22102232
return Ok(None);
22112233
}
22122234
self.maybe_parse(|p| {
2213-
let params = p.parse_comma_separated(|p| {
2214-
Ok(LambdaFunctionParameter {
2215-
name: p.parse_identifier()?,
2216-
data_type: None,
2217-
})
2218-
})?;
2235+
let params = p.parse_comma_separated(|p| p.parse_lambda_function_parameter(false))?;
22192236
p.expect_token(&Token::RParen)?;
22202237
p.expect_token(&Token::Arrow)?;
22212238
let expr = p.parse_expr()?;
@@ -2227,6 +2244,18 @@ impl<'a> Parser<'a> {
22272244
})
22282245
}
22292246

2247+
/// Parses a lambda expression using the arrow syntax with optionally typed arguments.
2248+
fn parse_lambda_expr_with_typed_args(&mut self) -> Result<Expr, ParserError> {
2249+
let params = self.parse_lambda_function_parameters(true)?;
2250+
self.expect_token(&Token::Arrow)?;
2251+
let body = self.parse_expr()?;
2252+
Ok(Expr::Lambda(LambdaFunction {
2253+
params,
2254+
body: Box::new(body),
2255+
syntax: LambdaSyntax::Arrow,
2256+
}))
2257+
}
2258+
22302259
/// Parses a lambda expression using the `LAMBDA` keyword syntax.
22312260
///
22322261
/// Syntax: `LAMBDA <params> : <expr>`
@@ -2237,40 +2266,55 @@ impl<'a> Parser<'a> {
22372266
///
22382267
/// See <https://duckdb.org/docs/stable/sql/functions/lambda>
22392268
fn parse_lambda_expr(&mut self) -> Result<Expr, ParserError> {
2269+
// Parse the parameters: either a single identifier or comma-separated identifiers
2270+
let params = self.parse_lambda_function_parameters(false)?;
2271+
// Expect the colon separator
2272+
self.expect_token(&Token::Colon)?;
2273+
// Parse the body expression
2274+
let body = self.parse_expr()?;
2275+
Ok(Expr::Lambda(LambdaFunction {
2276+
params,
2277+
body: Box::new(body),
2278+
syntax: LambdaSyntax::LambdaKeyword,
2279+
}))
2280+
}
2281+
2282+
/// Parses the parameters of a lambda function with optional typing.
2283+
fn parse_lambda_function_parameters(
2284+
&mut self,
2285+
typed: bool,
2286+
) -> Result<OneOrManyWithParens<LambdaFunctionParameter>, ParserError> {
22402287
// Parse the parameters: either a single identifier or comma-separated identifiers
22412288
let params = if self.consume_token(&Token::LParen) {
22422289
// Parenthesized parameters: (x, y)
2243-
let params = self.parse_comma_separated(|p| {
2244-
Ok(LambdaFunctionParameter {
2245-
name: p.parse_identifier()?,
2246-
data_type: None,
2247-
})
2248-
})?;
2290+
let params =
2291+
self.parse_comma_separated(|p| p.parse_lambda_function_parameter(typed))?;
22492292
self.expect_token(&Token::RParen)?;
22502293
OneOrManyWithParens::Many(params)
22512294
} else {
22522295
// Unparenthesized parameters: x or x, y
2253-
let params = self.parse_comma_separated(|p| {
2254-
Ok(LambdaFunctionParameter {
2255-
name: p.parse_identifier()?,
2256-
data_type: None,
2257-
})
2258-
})?;
2296+
let params =
2297+
self.parse_comma_separated(|p| p.parse_lambda_function_parameter(typed))?;
22592298
if params.len() == 1 {
22602299
OneOrManyWithParens::One(params.into_iter().next().unwrap())
22612300
} else {
22622301
OneOrManyWithParens::Many(params)
22632302
}
22642303
};
2265-
// Expect the colon separator
2266-
self.expect_token(&Token::Colon)?;
2267-
// Parse the body expression
2268-
let body = self.parse_expr()?;
2269-
Ok(Expr::Lambda(LambdaFunction {
2270-
params,
2271-
body: Box::new(body),
2272-
syntax: LambdaSyntax::LambdaKeyword,
2273-
}))
2304+
Ok(params)
2305+
}
2306+
2307+
/// Parses a single parameter of a lambda function, with optional typing.
2308+
fn parse_lambda_function_parameter(
2309+
&mut self,
2310+
typed: bool,
2311+
) -> Result<LambdaFunctionParameter, ParserError> {
2312+
let name = self.parse_identifier()?;
2313+
let data_type = match self.peek_token().token {
2314+
Token::Word(_) if typed => self.maybe_parse(|p| p.parse_data_type())?,
2315+
_ => None,
2316+
};
2317+
Ok(LambdaFunctionParameter { name, data_type })
22742318
}
22752319

22762320
/// Tries to parse the body of an [ODBC escaping sequence]

tests/sqlparser_snowflake.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4562,4 +4562,6 @@ fn test_truncate_table_if_exists() {
45624562
fn test_snowflake_lambda() {
45634563
snowflake().verified_expr("TRANSFORM([1, 2, 3], a -> a * 2)");
45644564
snowflake().verified_expr("TRANSFORM([1, 2, 3], a INT -> a * 2)");
4565+
snowflake().verified_expr("TRANSFORM([1, 2, 3], (x INT, y INT) -> (x + y))");
4566+
snowflake().verified_expr("REDUCE([1, 2, 3], 0, (acc, val) -> acc + val)");
45654567
}

0 commit comments

Comments
 (0)