Skip to content

Commit

Permalink
feat: DuckDB Lambda Functions
Browse files Browse the repository at this point in the history
- at the moment, only one lambda parameter is supported, sorry

Signed-off-by: Andreas Reichel <[email protected]>
  • Loading branch information
manticore-projects committed Mar 30, 2024
1 parent 1cd576b commit 236793a
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,6 @@ public interface ExpressionVisitor {
void visit(TSQLRightJoin tsqlRightJoin);

void visit(StructType structType);

void visit(LambdaExpression lambdaExpression);
}
Original file line number Diff line number Diff line change
Expand Up @@ -702,4 +702,9 @@ public void visit(StructType structType) {
}
}

@Override
public void visit(LambdaExpression lambdaExpression) {
lambdaExpression.getExpression().accept(this);
}

}
57 changes: 57 additions & 0 deletions src/main/java/net/sf/jsqlparser/expression/LambdaExpression.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package net.sf.jsqlparser.expression;

import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

import java.util.List;

public class LambdaExpression extends ASTNodeAccessImpl implements Expression {
private List<String> identifiers;
private Expression expression;

public LambdaExpression(List<String> identifiers, Expression expression) {
this.identifiers = identifiers;
this.expression = expression;
}

public List<String> getIdentifiers() {
return identifiers;
}

public LambdaExpression setIdentifiers(List<String> identifiers) {
this.identifiers = identifiers;
return this;
}

public Expression getExpression() {
return expression;
}

public LambdaExpression setExpression(Expression expression) {
this.expression = expression;
return this;
}

public StringBuilder appendTo(StringBuilder builder) {
if (identifiers.size() == 1) {
builder.append(identifiers.get(0));
} else {
int i = 0;
builder.append("( ");
for (String s : identifiers) {
builder.append(i++ > 0 ? ", " : "").append(s);
}
builder.append(" )");
}
return builder.append(" -> ").append(expression);
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}

@Override
public void accept(ExpressionVisitor expressionVisitor) {
expressionVisitor.visit(this);
}
}
6 changes: 6 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import net.sf.jsqlparser.expression.JsonFunction;
import net.sf.jsqlparser.expression.JsonFunctionExpression;
import net.sf.jsqlparser.expression.KeepExpression;
import net.sf.jsqlparser.expression.LambdaExpression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.NextValExpression;
Expand Down Expand Up @@ -1187,6 +1188,11 @@ public void visit(StructType structType) {
}
}

@Override
public void visit(LambdaExpression lambdaExpression) {
lambdaExpression.getExpression().accept(this);
}

@Override
public void visit(VariableAssignment var) {
var.getVariable().accept(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.JsonFunction;
import net.sf.jsqlparser.expression.KeepExpression;
import net.sf.jsqlparser.expression.LambdaExpression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.NextValExpression;
Expand Down Expand Up @@ -1179,4 +1180,21 @@ public void visit(StructType structType) {
}
}

@Override
public void visit(LambdaExpression lambdaExpression) {
if (lambdaExpression.getIdentifiers().size() == 1) {
buffer.append(lambdaExpression.getIdentifiers().get(0));
} else {
int i = 0;
buffer.append("( ");
for (String s : lambdaExpression.getIdentifiers()) {
buffer.append(i++ > 0 ? ", " : "").append(s);
}
buffer.append(" )");
}

buffer.append(" -> ");
lambdaExpression.getExpression().accept(this);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.JsonFunction;
import net.sf.jsqlparser.expression.KeepExpression;
import net.sf.jsqlparser.expression.LambdaExpression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.NextValExpression;
Expand Down Expand Up @@ -719,4 +720,9 @@ public void visit(StructType structType) {
}
}
}

@Override
public void visit(LambdaExpression lambdaExpression) {
lambdaExpression.getExpression().accept(this);
}
}
32 changes: 31 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -3879,7 +3879,7 @@ ExpressionList SimpleExpressionList():
}
{
expr=SimpleExpression() { expressions.add(expr); }
( LOOKAHEAD(2, {!interrupted} ) "," expr=SimpleExpression() { expressions.add(expr); } )*
( LOOKAHEAD(2, {!interrupted} ) "," ( LOOKAHEAD(2) expr=LambdaExpression() | expr=SimpleExpression()) { expressions.add(expr); } )*
{
return expressions;
}
Expand Down Expand Up @@ -3928,6 +3928,7 @@ ExpressionList ComplexExpressionList():
LOOKAHEAD(2, {!interrupted}) ","
(
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
| LOOKAHEAD(2) expr=LambdaExpression()
| expr=Expression()
) { expressions.add(expr); }
)*
Expand Down Expand Up @@ -5202,6 +5203,35 @@ FullTextSearch FullTextSearch() : {
}
}

LambdaExpression LambdaExpression() #LambdaExpression:
{
String s;
ArrayList<String> identifiers = new ArrayList<String>();
Expression expression;
LambdaExpression lambdaExpression;
}
{
// wip, right now the Grammar works but collides with Multi Value Lists
// (
// LOOKAHEAD(3) "("
// s = RelObjectName() { identifiers.add(s); }
// ( "," s = RelObjectName() { identifiers.add(s); } )*
// ")"
// )
// |
(
s = RelObjectName() { identifiers.add(s); }
)

"->"
expression = Expression()
{
lambdaExpression = new LambdaExpression(identifiers, expression);
linkAST(lambdaExpression,jjtThis);
return lambdaExpression;
}
}

Function Function() #Function:
{
Function function;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.sf.jsqlparser.expression;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class LambdaExpressionTest {

@Test
void testLambdaFunctionSingleParameter() throws JSQLParserException {
String sqlStr = "select list_transform( split('test', ''), x -> unicode(x) )";
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

@Disabled
@Test
// wip, right now the Grammar works but collides with Multi Value Lists
void testLambdaFunctionMultipleParameter() throws JSQLParserException {
String sqlStr = "SELECT list_transform(\n" +
" [1, 2, 3],\n" +
" x -> list_reduce([4, 5, 6], (a, b) -> a + b) + x\n" +
" )";
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

}

0 comments on commit 236793a

Please sign in to comment.