Skip to content

Commit 236793a

Browse files
feat: DuckDB Lambda Functions
- at the moment, only one lambda parameter is supported, sorry Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>
1 parent 1cd576b commit 236793a

File tree

8 files changed

+154
-1
lines changed

8 files changed

+154
-1
lines changed

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,6 @@ public interface ExpressionVisitor {
247247
void visit(TSQLRightJoin tsqlRightJoin);
248248

249249
void visit(StructType structType);
250+
251+
void visit(LambdaExpression lambdaExpression);
250252
}

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,4 +702,9 @@ public void visit(StructType structType) {
702702
}
703703
}
704704

705+
@Override
706+
public void visit(LambdaExpression lambdaExpression) {
707+
lambdaExpression.getExpression().accept(this);
708+
}
709+
705710
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package net.sf.jsqlparser.expression;
2+
3+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
4+
5+
import java.util.List;
6+
7+
public class LambdaExpression extends ASTNodeAccessImpl implements Expression {
8+
private List<String> identifiers;
9+
private Expression expression;
10+
11+
public LambdaExpression(List<String> identifiers, Expression expression) {
12+
this.identifiers = identifiers;
13+
this.expression = expression;
14+
}
15+
16+
public List<String> getIdentifiers() {
17+
return identifiers;
18+
}
19+
20+
public LambdaExpression setIdentifiers(List<String> identifiers) {
21+
this.identifiers = identifiers;
22+
return this;
23+
}
24+
25+
public Expression getExpression() {
26+
return expression;
27+
}
28+
29+
public LambdaExpression setExpression(Expression expression) {
30+
this.expression = expression;
31+
return this;
32+
}
33+
34+
public StringBuilder appendTo(StringBuilder builder) {
35+
if (identifiers.size() == 1) {
36+
builder.append(identifiers.get(0));
37+
} else {
38+
int i = 0;
39+
builder.append("( ");
40+
for (String s : identifiers) {
41+
builder.append(i++ > 0 ? ", " : "").append(s);
42+
}
43+
builder.append(" )");
44+
}
45+
return builder.append(" -> ").append(expression);
46+
}
47+
48+
@Override
49+
public String toString() {
50+
return appendTo(new StringBuilder()).toString();
51+
}
52+
53+
@Override
54+
public void accept(ExpressionVisitor expressionVisitor) {
55+
expressionVisitor.visit(this);
56+
}
57+
}

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import net.sf.jsqlparser.expression.JsonFunction;
4242
import net.sf.jsqlparser.expression.JsonFunctionExpression;
4343
import net.sf.jsqlparser.expression.KeepExpression;
44+
import net.sf.jsqlparser.expression.LambdaExpression;
4445
import net.sf.jsqlparser.expression.LongValue;
4546
import net.sf.jsqlparser.expression.MySQLGroupConcat;
4647
import net.sf.jsqlparser.expression.NextValExpression;
@@ -1187,6 +1188,11 @@ public void visit(StructType structType) {
11871188
}
11881189
}
11891190

1191+
@Override
1192+
public void visit(LambdaExpression lambdaExpression) {
1193+
lambdaExpression.getExpression().accept(this);
1194+
}
1195+
11901196
@Override
11911197
public void visit(VariableAssignment var) {
11921198
var.getVariable().accept(this);

src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import net.sf.jsqlparser.expression.JsonExpression;
3636
import net.sf.jsqlparser.expression.JsonFunction;
3737
import net.sf.jsqlparser.expression.KeepExpression;
38+
import net.sf.jsqlparser.expression.LambdaExpression;
3839
import net.sf.jsqlparser.expression.LongValue;
3940
import net.sf.jsqlparser.expression.MySQLGroupConcat;
4041
import net.sf.jsqlparser.expression.NextValExpression;
@@ -1179,4 +1180,21 @@ public void visit(StructType structType) {
11791180
}
11801181
}
11811182

1183+
@Override
1184+
public void visit(LambdaExpression lambdaExpression) {
1185+
if (lambdaExpression.getIdentifiers().size() == 1) {
1186+
buffer.append(lambdaExpression.getIdentifiers().get(0));
1187+
} else {
1188+
int i = 0;
1189+
buffer.append("( ");
1190+
for (String s : lambdaExpression.getIdentifiers()) {
1191+
buffer.append(i++ > 0 ? ", " : "").append(s);
1192+
}
1193+
buffer.append(" )");
1194+
}
1195+
1196+
buffer.append(" -> ");
1197+
lambdaExpression.getExpression().accept(this);
1198+
}
1199+
11821200
}

src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import net.sf.jsqlparser.expression.JsonExpression;
3535
import net.sf.jsqlparser.expression.JsonFunction;
3636
import net.sf.jsqlparser.expression.KeepExpression;
37+
import net.sf.jsqlparser.expression.LambdaExpression;
3738
import net.sf.jsqlparser.expression.LongValue;
3839
import net.sf.jsqlparser.expression.MySQLGroupConcat;
3940
import net.sf.jsqlparser.expression.NextValExpression;
@@ -719,4 +720,9 @@ public void visit(StructType structType) {
719720
}
720721
}
721722
}
723+
724+
@Override
725+
public void visit(LambdaExpression lambdaExpression) {
726+
lambdaExpression.getExpression().accept(this);
727+
}
722728
}

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3879,7 +3879,7 @@ ExpressionList SimpleExpressionList():
38793879
}
38803880
{
38813881
expr=SimpleExpression() { expressions.add(expr); }
3882-
( LOOKAHEAD(2, {!interrupted} ) "," expr=SimpleExpression() { expressions.add(expr); } )*
3882+
( LOOKAHEAD(2, {!interrupted} ) "," ( LOOKAHEAD(2) expr=LambdaExpression() | expr=SimpleExpression()) { expressions.add(expr); } )*
38833883
{
38843884
return expressions;
38853885
}
@@ -3928,6 +3928,7 @@ ExpressionList ComplexExpressionList():
39283928
LOOKAHEAD(2, {!interrupted}) ","
39293929
(
39303930
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
3931+
| LOOKAHEAD(2) expr=LambdaExpression()
39313932
| expr=Expression()
39323933
) { expressions.add(expr); }
39333934
)*
@@ -5202,6 +5203,35 @@ FullTextSearch FullTextSearch() : {
52025203
}
52035204
}
52045205

5206+
LambdaExpression LambdaExpression() #LambdaExpression:
5207+
{
5208+
String s;
5209+
ArrayList<String> identifiers = new ArrayList<String>();
5210+
Expression expression;
5211+
LambdaExpression lambdaExpression;
5212+
}
5213+
{
5214+
// wip, right now the Grammar works but collides with Multi Value Lists
5215+
// (
5216+
// LOOKAHEAD(3) "("
5217+
// s = RelObjectName() { identifiers.add(s); }
5218+
// ( "," s = RelObjectName() { identifiers.add(s); } )*
5219+
// ")"
5220+
// )
5221+
// |
5222+
(
5223+
s = RelObjectName() { identifiers.add(s); }
5224+
)
5225+
5226+
"->"
5227+
expression = Expression()
5228+
{
5229+
lambdaExpression = new LambdaExpression(identifiers, expression);
5230+
linkAST(lambdaExpression,jjtThis);
5231+
return lambdaExpression;
5232+
}
5233+
}
5234+
52055235
Function Function() #Function:
52065236
{
52075237
Function function;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.sf.jsqlparser.expression;
2+
3+
import net.sf.jsqlparser.JSQLParserException;
4+
import net.sf.jsqlparser.test.TestUtils;
5+
import org.junit.jupiter.api.Disabled;
6+
import org.junit.jupiter.api.Test;
7+
8+
import static org.junit.jupiter.api.Assertions.*;
9+
10+
class LambdaExpressionTest {
11+
12+
@Test
13+
void testLambdaFunctionSingleParameter() throws JSQLParserException {
14+
String sqlStr = "select list_transform( split('test', ''), x -> unicode(x) )";
15+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
16+
}
17+
18+
@Disabled
19+
@Test
20+
// wip, right now the Grammar works but collides with Multi Value Lists
21+
void testLambdaFunctionMultipleParameter() throws JSQLParserException {
22+
String sqlStr = "SELECT list_transform(\n" +
23+
" [1, 2, 3],\n" +
24+
" x -> list_reduce([4, 5, 6], (a, b) -> a + b) + x\n" +
25+
" )";
26+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
27+
}
28+
29+
}

0 commit comments

Comments
 (0)