You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/01/13 20:50:30 UTC

(datafusion-sqlparser-rs) branch gh-readonly-queue/main/pr-2149-ee3b6223e673b9c0f737d649a7f934de0186bc64 created (now 3880a933)

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

github-bot pushed a change to branch gh-readonly-queue/main/pr-2149-ee3b6223e673b9c0f737d649a7f934de0186bc64
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at 3880a933 Add support for DuckDB `LAMBDA` keyword syntax (#2149)

This branch includes the following new commits:

     new 3880a933 Add support for DuckDB `LAMBDA` keyword syntax (#2149)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org


(datafusion-sqlparser-rs) 01/01: Add support for DuckDB `LAMBDA` keyword syntax (#2149)

Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3880a933e4c2d1e639c1b8bcb48bea34c37801fd
Author: Ophir LOJKINE <co...@ophir.dev>
AuthorDate: Tue Jan 13 21:50:08 2026 +0100

    Add support for DuckDB `LAMBDA` keyword syntax (#2149)
    
    Co-authored-by: Claude Opus 4.5 <no...@anthropic.com>
    Co-authored-by: Ifeanyi Ubah <if...@yahoo.com>
---
 src/ast/mod.rs                | 35 ++++++++++++++++++++++++++++++++++-
 src/dialect/generic.rs        |  4 ++++
 src/keywords.rs               |  1 +
 src/parser/mod.rs             | 41 +++++++++++++++++++++++++++++++++++++++++
 tests/sqlparser_common.rs     |  3 ++-
 tests/sqlparser_databricks.rs |  6 ++++--
 tests/sqlparser_duckdb.rs     | 19 +++++++++++++++++++
 7 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 7e0f1a10..35a62ab7 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -1415,14 +1415,47 @@ pub struct LambdaFunction {
     pub params: OneOrManyWithParens<Ident>,
     /// The body of the lambda function.
     pub body: Box<Expr>,
+    /// The syntax style used to write the lambda function.
+    pub syntax: LambdaSyntax,
 }
 
 impl fmt::Display for LambdaFunction {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{} -> {}", self.params, self.body)
+        match self.syntax {
+            LambdaSyntax::Arrow => write!(f, "{} -> {}", self.params, self.body),
+            LambdaSyntax::LambdaKeyword => {
+                // For lambda keyword syntax, display params without parentheses
+                // e.g., `lambda x, y : expr` not `lambda (x, y) : expr`
+                write!(f, "lambda ")?;
+                match &self.params {
+                    OneOrManyWithParens::One(p) => write!(f, "{p}")?,
+                    OneOrManyWithParens::Many(ps) => write!(f, "{}", display_comma_separated(ps))?,
+                };
+                write!(f, " : {}", self.body)
+            }
+        }
     }
 }
 
+/// The syntax style for a lambda function.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum LambdaSyntax {
+    /// Arrow syntax: `param -> expr` or `(param1, param2) -> expr`
+    ///
+    /// <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-lambda-functions>
+    ///
+    /// Supported, but deprecated in DuckDB:
+    /// <https://duckdb.org/docs/stable/sql/functions/lambda>
+    Arrow,
+    /// Lambda keyword syntax: `lambda param : expr` or `lambda param1, param2 : expr`
+    ///
+    /// Recommended in DuckDB:
+    /// <https://duckdb.org/docs/stable/sql/functions/lambda>
+    LambdaKeyword,
+}
+
 /// Encapsulates the common pattern in SQL where either one unparenthesized item
 /// such as an identifier or expression is permitted, or multiple of the same
 /// item in a parenthesized list. For accessing items regardless of the form,
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index f3a0903a..da57253d 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -203,4 +203,8 @@ impl Dialect for GenericDialect {
     fn supports_quote_delimited_string(&self) -> bool {
         true
     }
+
+    fn supports_lambda_functions(&self) -> bool {
+        true
+    }
 }
diff --git a/src/keywords.rs b/src/keywords.rs
index 77207283..964e4b38 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -555,6 +555,7 @@ define_keywords!(
     KEY_BLOCK_SIZE,
     KILL,
     LAG,
+    LAMBDA,
     LANGUAGE,
     LARGE,
     LAST,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 8001611e..64b65391 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -1548,6 +1548,9 @@ impl<'a> Parser<'a> {
             Keyword::MAP if *self.peek_token_ref() == Token::LBrace && self.dialect.support_map_literal_syntax() => {
                 Ok(Some(self.parse_duckdb_map_literal()?))
             }
+            Keyword::LAMBDA if self.dialect.supports_lambda_functions() => {
+                Ok(Some(self.parse_lambda_expr()?))
+            }
             _ if self.dialect.supports_geometric_types() => match w.keyword {
                 Keyword::CIRCLE => Ok(Some(self.parse_geometric_type(GeometricTypeKind::Circle)?)),
                 Keyword::BOX => Ok(Some(self.parse_geometric_type(GeometricTypeKind::GeometricBox)?)),
@@ -1600,6 +1603,7 @@ impl<'a> Parser<'a> {
                 Ok(Expr::Lambda(LambdaFunction {
                     params: OneOrManyWithParens::One(w.clone().into_ident(w_span)),
                     body: Box::new(self.parse_expr()?),
+                    syntax: LambdaSyntax::Arrow,
                 }))
             }
             _ => Ok(Expr::Identifier(w.clone().into_ident(w_span))),
@@ -2141,10 +2145,47 @@ impl<'a> Parser<'a> {
             Ok(Expr::Lambda(LambdaFunction {
                 params: OneOrManyWithParens::Many(params),
                 body: Box::new(expr),
+                syntax: LambdaSyntax::Arrow,
             }))
         })
     }
 
+    /// Parses a lambda expression using the `LAMBDA` keyword syntax.
+    ///
+    /// Syntax: `LAMBDA <params> : <expr>`
+    ///
+    /// Examples:
+    /// - `LAMBDA x : x + 1`
+    /// - `LAMBDA x, i : x > i`
+    ///
+    /// 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 = if self.consume_token(&Token::LParen) {
+            // Parenthesized parameters: (x, y)
+            let params = self.parse_comma_separated(|p| p.parse_identifier())?;
+            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())?;
+            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,
+        }))
+    }
+
     /// Tries to parse the body of an [ODBC escaping sequence]
     /// i.e. without the enclosing braces
     /// Currently implemented:
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 208a56e2..c7a1981e 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -15678,7 +15678,8 @@ fn test_lambdas() {
                             },
                         ],
                         else_result: Some(Box::new(Expr::value(number("1")))),
-                    })
+                    }),
+                    syntax: LambdaSyntax::Arrow,
                 })
             ]
         )),
diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs
index 9a9a73fe..9064c8dc 100644
--- a/tests/sqlparser_databricks.rs
+++ b/tests/sqlparser_databricks.rs
@@ -73,7 +73,8 @@ fn test_databricks_exists() {
                 ),
                 Expr::Lambda(LambdaFunction {
                     params: OneOrManyWithParens::One(Ident::new("x")),
-                    body: Box::new(Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x")))))
+                    body: Box::new(Expr::IsNull(Box::new(Expr::Identifier(Ident::new("x"))))),
+                    syntax: LambdaSyntax::Arrow,
                 })
             ]
         ),
@@ -141,7 +142,8 @@ fn test_databricks_lambdas() {
                             },
                         ],
                         else_result: Some(Box::new(Expr::value(number("1"))))
-                    })
+                    }),
+                    syntax: LambdaSyntax::Arrow,
                 })
             ]
         )),
diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs
index 4a2f29e1..80a15eb1 100644
--- a/tests/sqlparser_duckdb.rs
+++ b/tests/sqlparser_duckdb.rs
@@ -872,3 +872,22 @@ fn parse_extract_single_quotes() {
     let sql = "SELECT EXTRACT('month' FROM my_timestamp) FROM my_table";
     duckdb().verified_stmt(sql);
 }
+
+#[test]
+fn test_duckdb_lambda_function() {
+    // Test basic lambda with list_filter
+    let sql = "SELECT [3, 4, 5, 6].list_filter(lambda x : x > 4)";
+    duckdb_and_generic().verified_stmt(sql);
+
+    // Test lambda with arrow syntax (also supported by DuckDB)
+    let sql_arrow = "SELECT list_filter([1, 2, 3], x -> x > 1)";
+    duckdb_and_generic().verified_stmt(sql_arrow);
+
+    // Test lambda with multiple parameters (with index)
+    let sql_multi = "SELECT list_filter([1, 3, 1, 5], lambda x, i : x > i)";
+    duckdb_and_generic().verified_stmt(sql_multi);
+
+    // Test lambda in list_transform
+    let sql_transform = "SELECT list_transform([1, 2, 3], lambda x : x * 2)";
+    duckdb_and_generic().verified_stmt(sql_transform);
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org