This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 4b4a9d74 Snowflake: Lambda functions (#2192)
4b4a9d74 is described below

commit 4b4a9d7411f7a855999069e6a0d10202cdcab7b9
Author: Yoav Cohen <[email protected]>
AuthorDate: Fri Feb 13 09:39:31 2026 +0100

    Snowflake: Lambda functions (#2192)
---
 src/ast/mod.rs                | 23 +++++++++++++-
 src/dialect/snowflake.rs      |  5 +++
 src/parser/mod.rs             | 71 ++++++++++++++++++++++++++++++++++---------
 tests/sqlparser_common.rs     | 17 ++++++++++-
 tests/sqlparser_databricks.rs | 16 ++++++++--
 5 files changed, 114 insertions(+), 18 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 601af1bd..4d8c536a 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -1423,7 +1423,7 @@ impl fmt::Display for AccessExpr {
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct LambdaFunction {
     /// The parameters to the lambda function.
-    pub params: OneOrManyWithParens<Ident>,
+    pub params: OneOrManyWithParens<LambdaFunctionParameter>,
     /// The body of the lambda function.
     pub body: Box<Expr>,
     /// The syntax style used to write the lambda function.
@@ -1448,6 +1448,27 @@ impl fmt::Display for LambdaFunction {
     }
 }
 
+/// A parameter to a lambda function, optionally with a data type.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct LambdaFunctionParameter {
+    /// The name of the parameter
+    pub name: Ident,
+    /// The optional data type of the parameter
+    /// [Snowflake 
Syntax](https://docs.snowflake.com/en/sql-reference/functions/filter#arguments)
+    pub data_type: Option<DataType>,
+}
+
+impl fmt::Display for LambdaFunctionParameter {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self.data_type {
+            Some(dt) => write!(f, "{} {}", self.name, dt),
+            None => write!(f, "{}", self.name),
+        }
+    }
+}
+
 /// The syntax style for a lambda function.
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index 14e4ad45..d6470916 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -662,6 +662,11 @@ impl Dialect for SnowflakeDialect {
     fn supports_select_wildcard_rename(&self) -> bool {
         true
     }
+
+    /// See 
<https://docs.snowflake.com/en/user-guide/querying-semistructured#label-higher-order-functions>
+    fn supports_lambda_functions(&self) -> bool {
+        true
+    }
 }
 
 // Peeks ahead to identify tokens that are expected after
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 7a2bda8a..7dc75815 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1606,10 +1606,34 @@ impl<'a> Parser<'a> {
                     value: self.parse_introduced_string_expr()?.into(),
                 })
             }
+            // An unreserved word (likely an identifier) is followed by an 
arrow,
+            // which indicates a lambda function with a single, untyped 
parameter.
+            // For example: `a -> a * 2`.
             Token::Arrow if self.dialect.supports_lambda_functions() => {
                 self.expect_token(&Token::Arrow)?;
                 Ok(Expr::Lambda(LambdaFunction {
-                    params: OneOrManyWithParens::One(w.to_ident(w_span)),
+                    params: OneOrManyWithParens::One(LambdaFunctionParameter {
+                        name: w.to_ident(w_span),
+                        data_type: None,
+                    }),
+                    body: Box::new(self.parse_expr()?),
+                    syntax: LambdaSyntax::Arrow,
+                }))
+            }
+            // An unreserved word (likely an identifier) that is followed by 
another word (likley a data type)
+            // which is then followed by an arrow, which indicates a lambda 
function with a single, typed parameter.
+            // For example: `a INT -> a * 2`.
+            Token::Word(_)
+                if self.dialect.supports_lambda_functions()
+                    && self.peek_nth_token_ref(1).token == Token::Arrow =>
+            {
+                let data_type = self.parse_data_type()?;
+                self.expect_token(&Token::Arrow)?;
+                Ok(Expr::Lambda(LambdaFunction {
+                    params: OneOrManyWithParens::One(LambdaFunctionParameter {
+                        name: w.to_ident(w_span),
+                        data_type: Some(data_type),
+                    }),
                     body: Box::new(self.parse_expr()?),
                     syntax: LambdaSyntax::Arrow,
                 }))
@@ -2195,7 +2219,7 @@ impl<'a> Parser<'a> {
             return Ok(None);
         }
         self.maybe_parse(|p| {
-            let params = p.parse_comma_separated(|p| p.parse_identifier())?;
+            let params = p.parse_comma_separated(|p| 
p.parse_lambda_function_parameter())?;
             p.expect_token(&Token::RParen)?;
             p.expect_token(&Token::Arrow)?;
             let expr = p.parse_expr()?;
@@ -2207,7 +2231,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Parses a lambda expression using the `LAMBDA` keyword syntax.
+    /// Parses a lambda expression following the `LAMBDA` keyword syntax.
     ///
     /// Syntax: `LAMBDA <params> : <expr>`
     ///
@@ -2217,30 +2241,49 @@ impl<'a> Parser<'a> {
     ///
     /// See <https://duckdb.org/docs/stable/sql/functions/lambda>
     fn parse_lambda_expr(&mut self) -> Result<Expr, ParserError> {
+        // Parse the parameters: either a single identifier or comma-separated 
identifiers
+        let params = self.parse_lambda_function_parameters()?;
+        // Expect the colon separator
+        self.expect_token(&Token::Colon)?;
+        // Parse the body expression
+        let body = self.parse_expr()?;
+        Ok(Expr::Lambda(LambdaFunction {
+            params,
+            body: Box::new(body),
+            syntax: LambdaSyntax::LambdaKeyword,
+        }))
+    }
+
+    /// Parses the parameters of a lambda function with optional typing.
+    fn parse_lambda_function_parameters(
+        &mut self,
+    ) -> Result<OneOrManyWithParens<LambdaFunctionParameter>, ParserError> {
         // Parse the parameters: either a single identifier or comma-separated 
identifiers
         let params = if self.consume_token(&Token::LParen) {
             // Parenthesized parameters: (x, y)
-            let params = self.parse_comma_separated(|p| p.parse_identifier())?;
+            let params = self.parse_comma_separated(|p| 
p.parse_lambda_function_parameter())?;
             self.expect_token(&Token::RParen)?;
             OneOrManyWithParens::Many(params)
         } else {
             // Unparenthesized parameters: x or x, y
-            let params = self.parse_comma_separated(|p| p.parse_identifier())?;
+            let params = self.parse_comma_separated(|p| 
p.parse_lambda_function_parameter())?;
             if params.len() == 1 {
                 OneOrManyWithParens::One(params.into_iter().next().unwrap())
             } else {
                 OneOrManyWithParens::Many(params)
             }
         };
-        // Expect the colon separator
-        self.expect_token(&Token::Colon)?;
-        // Parse the body expression
-        let body = self.parse_expr()?;
-        Ok(Expr::Lambda(LambdaFunction {
-            params,
-            body: Box::new(body),
-            syntax: LambdaSyntax::LambdaKeyword,
-        }))
+        Ok(params)
+    }
+
+    /// Parses a single parameter of a lambda function, with optional typing.
+    fn parse_lambda_function_parameter(&mut self) -> 
Result<LambdaFunctionParameter, ParserError> {
+        let name = self.parse_identifier()?;
+        let data_type = match self.peek_token().token {
+            Token::Word(_) => self.maybe_parse(|p| p.parse_data_type())?,
+            _ => None,
+        };
+        Ok(LambdaFunctionParameter { name, data_type })
     }
 
     /// Tries to parse the body of an [ODBC escaping sequence]
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 899dba8d..5822153a 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -15925,7 +15925,16 @@ fn test_lambdas() {
                     ]
                 ),
                 Expr::Lambda(LambdaFunction {
-                    params: OneOrManyWithParens::Many(vec![Ident::new("p1"), 
Ident::new("p2")]),
+                    params: OneOrManyWithParens::Many(vec![
+                        LambdaFunctionParameter {
+                            name: Ident::new("p1"),
+                            data_type: None
+                        },
+                        LambdaFunctionParameter {
+                            name: Ident::new("p2"),
+                            data_type: None
+                        }
+                    ]),
                     body: Box::new(Expr::Case {
                         case_token: AttachedToken::empty(),
                         end_token: AttachedToken::empty(),
@@ -15970,6 +15979,12 @@ fn test_lambdas() {
         "map_zip_with(map(1, 'a', 2, 'b'), map(1, 'x', 2, 'y'), (k, v1, v2) -> 
concat(v1, v2))",
     );
     dialects.verified_expr("transform(array(1, 2, 3), x -> x + 1)");
+
+    // Ensure all lambda variants are parsed correctly
+    dialects.verified_expr("a -> a * 2"); // Single parameter without type
+    dialects.verified_expr("a INT -> a * 2"); // Single parameter with type
+    dialects.verified_expr("(a, b) -> a * b"); // Multiple parameters without 
types
+    dialects.verified_expr("(a INT, b FLOAT) -> a * b"); // Multiple 
parameters with types
 }
 
 #[test]
diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs
index 6a7534ad..24d06ef2 100644
--- a/tests/sqlparser_databricks.rs
+++ b/tests/sqlparser_databricks.rs
@@ -72,7 +72,10 @@ fn test_databricks_exists() {
                     ]
                 ),
                 Expr::Lambda(LambdaFunction {
-                    params: OneOrManyWithParens::One(Ident::new("x")),
+                    params: OneOrManyWithParens::One(LambdaFunctionParameter {
+                        name: Ident::new("x"),
+                        data_type: None
+                    }),
                     body: 
Box::new(Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x"))))),
                     syntax: LambdaSyntax::Arrow,
                 })
@@ -109,7 +112,16 @@ fn test_databricks_lambdas() {
                     ]
                 ),
                 Expr::Lambda(LambdaFunction {
-                    params: OneOrManyWithParens::Many(vec![Ident::new("p1"), 
Ident::new("p2")]),
+                    params: OneOrManyWithParens::Many(vec![
+                        LambdaFunctionParameter {
+                            name: Ident::new("p1"),
+                            data_type: None
+                        },
+                        LambdaFunctionParameter {
+                            name: Ident::new("p2"),
+                            data_type: None
+                        }
+                    ]),
                     body: Box::new(Expr::Case {
                         case_token: AttachedToken::empty(),
                         end_token: AttachedToken::empty(),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to