Skip to content

Commit 6f9b80b

Browse files
committed
Snowflake: Add support for Lambda functions
1 parent 5f90cef commit 6f9b80b

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,
@@ -1052,6 +1053,7 @@ define_keywords!(
10521053
TRACE,
10531054
TRAILING,
10541055
TRANSACTION,
1056+
TRANSFORM,
10551057
TRANSIENT,
10561058
TRANSLATE,
10571059
TRANSLATE_REGEX,

src/parser/mod.rs

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,43 @@ impl<'a> Parser<'a> {
15591559
Keyword::LAMBDA if self.dialect.supports_lambda_functions() => {
15601560
Ok(Some(self.parse_lambda_expr()?))
15611561
}
1562+
Keyword::FILTER
1563+
| Keyword::TRANSFORM
1564+
| Keyword::REDUCE if self.dialect.supports_lambda_functions() => {
1565+
self.expect_token(&Token::LParen)?;
1566+
let array = self.parse_expr()?;
1567+
self.expect_token(&Token::Comma)?;
1568+
let initial_value = if w.keyword == Keyword::REDUCE {
1569+
let initial_value = self.parse_expr()?;
1570+
self.expect_token(&Token::Comma)?;
1571+
Some(initial_value)
1572+
} else {
1573+
None
1574+
};
1575+
let lambda = self.parse_lambda_expr_with_typed_args()?;
1576+
let mut args = vec![
1577+
FunctionArg::Unnamed(FunctionArgExpr::Expr(array)),
1578+
FunctionArg::Unnamed(FunctionArgExpr::Expr(lambda)),
1579+
];
1580+
if let Some(initial_value) = initial_value {
1581+
args.insert(1, FunctionArg::Unnamed(FunctionArgExpr::Expr(initial_value)));
1582+
}
1583+
self.expect_token(&Token::RParen)?;
1584+
Ok(Some(Expr::Function(Function {
1585+
name: ObjectName::from(vec![w.to_ident(w_span)]),
1586+
uses_odbc_syntax: false,
1587+
parameters: FunctionArguments::None,
1588+
args: FunctionArguments::List(FunctionArgumentList {
1589+
duplicate_treatment: None,
1590+
clauses: vec![],
1591+
args,
1592+
}),
1593+
filter: None,
1594+
null_treatment: None,
1595+
over: None,
1596+
within_group: vec![],
1597+
})))
1598+
}
15621599
_ if self.dialect.supports_geometric_types() => match w.keyword {
15631600
Keyword::CIRCLE => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Circle)?)),
15641601
Keyword::BOX => Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricBox)?)),
@@ -1617,21 +1654,6 @@ impl<'a> Parser<'a> {
16171654
syntax: LambdaSyntax::Arrow,
16181655
}))
16191656
}
1620-
Token::Word(_)
1621-
if self.peek_nth_token(1).token == Token::Arrow
1622-
&& self.dialect.supports_lambda_functions() =>
1623-
{
1624-
let first_param = LambdaFunctionParameter {
1625-
name: w.to_ident(self.peek_nth_token_ref(1).span),
1626-
data_type: self.maybe_parse(|parser| parser.parse_data_type())?,
1627-
};
1628-
self.expect_token(&Token::Arrow)?;
1629-
Ok(Expr::Lambda(LambdaFunction {
1630-
params: OneOrManyWithParens::One(first_param),
1631-
body: Box::new(self.parse_expr()?),
1632-
syntax: LambdaSyntax::Arrow,
1633-
}))
1634-
}
16351657
_ => Ok(Expr::Identifier(w.to_ident(w_span))),
16361658
}
16371659
}
@@ -2213,12 +2235,7 @@ impl<'a> Parser<'a> {
22132235
return Ok(None);
22142236
}
22152237
self.maybe_parse(|p| {
2216-
let params = p.parse_comma_separated(|p| {
2217-
Ok(LambdaFunctionParameter {
2218-
name: p.parse_identifier()?,
2219-
data_type: None,
2220-
})
2221-
})?;
2238+
let params = p.parse_comma_separated(|p| p.parse_lambda_function_parameter(false))?;
22222239
p.expect_token(&Token::RParen)?;
22232240
p.expect_token(&Token::Arrow)?;
22242241
let expr = p.parse_expr()?;
@@ -2230,6 +2247,18 @@ impl<'a> Parser<'a> {
22302247
})
22312248
}
22322249

2250+
/// Parses a lambda expression using the arrow syntax with optionally typed arguments.
2251+
fn parse_lambda_expr_with_typed_args(&mut self) -> Result<Expr, ParserError> {
2252+
let params = self.parse_lambda_function_parameters(true)?;
2253+
self.expect_token(&Token::Arrow)?;
2254+
let body = self.parse_expr()?;
2255+
Ok(Expr::Lambda(LambdaFunction {
2256+
params,
2257+
body: Box::new(body),
2258+
syntax: LambdaSyntax::Arrow,
2259+
}))
2260+
}
2261+
22332262
/// Parses a lambda expression using the `LAMBDA` keyword syntax.
22342263
///
22352264
/// Syntax: `LAMBDA <params> : <expr>`
@@ -2240,40 +2269,55 @@ impl<'a> Parser<'a> {
22402269
///
22412270
/// See <https://duckdb.org/docs/stable/sql/functions/lambda>
22422271
fn parse_lambda_expr(&mut self) -> Result<Expr, ParserError> {
2272+
// Parse the parameters: either a single identifier or comma-separated identifiers
2273+
let params = self.parse_lambda_function_parameters(false)?;
2274+
// Expect the colon separator
2275+
self.expect_token(&Token::Colon)?;
2276+
// Parse the body expression
2277+
let body = self.parse_expr()?;
2278+
Ok(Expr::Lambda(LambdaFunction {
2279+
params,
2280+
body: Box::new(body),
2281+
syntax: LambdaSyntax::LambdaKeyword,
2282+
}))
2283+
}
2284+
2285+
/// Parses the parameters of a lambda function with optional typing.
2286+
fn parse_lambda_function_parameters(
2287+
&mut self,
2288+
typed: bool,
2289+
) -> Result<OneOrManyWithParens<LambdaFunctionParameter>, ParserError> {
22432290
// Parse the parameters: either a single identifier or comma-separated identifiers
22442291
let params = if self.consume_token(&Token::LParen) {
22452292
// Parenthesized parameters: (x, y)
2246-
let params = self.parse_comma_separated(|p| {
2247-
Ok(LambdaFunctionParameter {
2248-
name: p.parse_identifier()?,
2249-
data_type: None,
2250-
})
2251-
})?;
2293+
let params =
2294+
self.parse_comma_separated(|p| p.parse_lambda_function_parameter(typed))?;
22522295
self.expect_token(&Token::RParen)?;
22532296
OneOrManyWithParens::Many(params)
22542297
} else {
22552298
// Unparenthesized parameters: x or x, y
2256-
let params = self.parse_comma_separated(|p| {
2257-
Ok(LambdaFunctionParameter {
2258-
name: p.parse_identifier()?,
2259-
data_type: None,
2260-
})
2261-
})?;
2299+
let params =
2300+
self.parse_comma_separated(|p| p.parse_lambda_function_parameter(typed))?;
22622301
if params.len() == 1 {
22632302
OneOrManyWithParens::One(params.into_iter().next().unwrap())
22642303
} else {
22652304
OneOrManyWithParens::Many(params)
22662305
}
22672306
};
2268-
// Expect the colon separator
2269-
self.expect_token(&Token::Colon)?;
2270-
// Parse the body expression
2271-
let body = self.parse_expr()?;
2272-
Ok(Expr::Lambda(LambdaFunction {
2273-
params,
2274-
body: Box::new(body),
2275-
syntax: LambdaSyntax::LambdaKeyword,
2276-
}))
2307+
Ok(params)
2308+
}
2309+
2310+
/// Parses a single parameter of a lambda function, with optional typing.
2311+
fn parse_lambda_function_parameter(
2312+
&mut self,
2313+
typed: bool,
2314+
) -> Result<LambdaFunctionParameter, ParserError> {
2315+
let name = self.parse_identifier()?;
2316+
let data_type = match self.peek_token().token {
2317+
Token::Word(_) if typed => self.maybe_parse(|p| p.parse_data_type())?,
2318+
_ => None,
2319+
};
2320+
Ok(LambdaFunctionParameter { name, data_type })
22772321
}
22782322

22792323
/// 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
@@ -4883,4 +4883,6 @@ fn test_truncate_table_if_exists() {
48834883
fn test_snowflake_lambda() {
48844884
snowflake().verified_expr("TRANSFORM([1, 2, 3], a -> a * 2)");
48854885
snowflake().verified_expr("TRANSFORM([1, 2, 3], a INT -> a * 2)");
4886+
snowflake().verified_expr("TRANSFORM([1, 2, 3], (x INT, y INT) -> (x + y))");
4887+
snowflake().verified_expr("REDUCE([1, 2, 3], 0, (acc, val) -> acc + val)");
48864888
}

0 commit comments

Comments
 (0)