Skip to content

Commit e0ad7c8

Browse files
fix: Multi-Variable LambdaExpression
- fixes #2030 - fixes #2032 Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>
1 parent d8207aa commit e0ad7c8

File tree

4 files changed

+75
-41
lines changed

4 files changed

+75
-41
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
*/
1010
package net.sf.jsqlparser.expression;
1111

12+
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
1213
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
1314

15+
import java.util.ArrayList;
1416
import java.util.Collections;
1517
import java.util.List;
1618

@@ -28,6 +30,15 @@ public LambdaExpression(List<String> identifiers, Expression expression) {
2830
this.expression = expression;
2931
}
3032

33+
public static LambdaExpression from(ExpressionList<? extends Expression> expressionList,
34+
Expression expression) {
35+
List<String> identifiers = new ArrayList<>(expressionList.size());
36+
for (Expression variable : expressionList) {
37+
identifiers.add(variable.toString());
38+
}
39+
return new LambdaExpression(identifiers, expression);
40+
}
41+
3142
public List<String> getIdentifiers() {
3243
return identifiers;
3344
}

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

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ Statements Statements() #Statements: {
912912

913913
)
914914

915-
(
915+
( LOOKAHEAD(2)
916916
( <ST_SEMICOLON> )*
917917
try {
918918
(
@@ -1932,7 +1932,7 @@ Column Column() #Column :
19321932
data = RelObjectNames()
19331933
[ LOOKAHEAD(2) <K_COMMENT> tk=<S_CHAR_LITERAL> ]
19341934
// @todo: we better should return a SEQUENCE instead of a COLUMN
1935-
[ "." <K_NEXTVAL> { data.getNames().add("nextval"); } ]
1935+
[ LOOKAHEAD(2) "." <K_NEXTVAL> { data.getNames().add("nextval"); } ]
19361936

19371937
[ LOOKAHEAD(2) arrayConstructor = ArrayConstructor(false) ]
19381938
{
@@ -4081,12 +4081,24 @@ ParenthesedExpressionList ParenthesedExpressionList():
40814081

40824082
ExpressionList SimpleExpressionList():
40834083
{
4084+
ExpressionList<Column> columns;
40844085
ExpressionList expressions = new ExpressionList();
40854086
Expression expr;
40864087
}
40874088
{
40884089
expr=SimpleExpression() { expressions.add(expr); }
4089-
( LOOKAHEAD(2, {!interrupted} ) "," ( LOOKAHEAD(2) expr=LambdaExpression() | expr=SimpleExpression()) { expressions.add(expr); } )*
4090+
(
4091+
LOOKAHEAD(2, {!interrupted} ) ","
4092+
(
4093+
// @todo: Check hot to avoid this expensive look ahead
4094+
LOOKAHEAD( LambdaExpression() ) expr=LambdaExpression()
4095+
|
4096+
expr=SimpleExpression()
4097+
)
4098+
{
4099+
expressions.add(expr);
4100+
}
4101+
)*
40904102
{
40914103
return expressions;
40924104
}
@@ -4119,6 +4131,7 @@ ParenthesedExpressionList<Column> ParenthesedColumnList():
41194131

41204132
ExpressionList ComplexExpressionList():
41214133
{
4134+
ExpressionList<Column> columns;
41224135
ExpressionList expressions = new ExpressionList();
41234136
Expression expr;
41244137
}
@@ -4135,7 +4148,9 @@ ExpressionList ComplexExpressionList():
41354148
LOOKAHEAD(2, {!interrupted}) ","
41364149
(
41374150
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
4138-
| LOOKAHEAD(2) expr=LambdaExpression()
4151+
|
4152+
// @todo: Check hot to avoid this expensive look ahead
4153+
LOOKAHEAD( LambdaExpression() ) expr=LambdaExpression()
41394154
| expr=Expression()
41404155
) { expressions.add(expr); }
41414156
)*
@@ -4378,7 +4393,7 @@ Expression BitwiseXor():
43784393
}
43794394
{
43804395
leftExpression=PrimaryExpression() { result = leftExpression; }
4381-
(
4396+
( LOOKAHEAD(2)
43824397
"^"
43834398
rightExpression=PrimaryExpression()
43844399
{
@@ -4514,14 +4529,27 @@ Expression PrimaryExpression() #PrimaryExpression:
45144529
|
45154530
(
45164531
list=ParenthesedExpressionList()
4532+
// Mutli-Variable Lambda Expression, e. g.
4533+
// SELECT map_filter(my_column, (k,v) -> v.my_inner_column = 'some_value')
4534+
[ LOOKAHEAD(2) "->"
4535+
retval = Expression()
4536+
{
4537+
retval = LambdaExpression.from(list, retval);
4538+
}
4539+
]
4540+
4541+
45174542
{
45184543
if (list.size() == 1) {
45194544
retval = new ParenthesedExpressionList( (Expression) list.getExpressions().get(0));
45204545
} else {
45214546
retval = list;
45224547
}
45234548
}
4524-
["." tmp=RelObjectNameExt() { retval = new RowGetExpression(retval, tmp); }]
4549+
4550+
4551+
// RowGet Expression
4552+
[ LOOKAHEAD(2) "." tmp=RelObjectNameExt() { retval = new RowGetExpression(retval, tmp); }]
45254553
)
45264554
)
45274555

@@ -4535,7 +4563,7 @@ Expression PrimaryExpression() #PrimaryExpression:
45354563

45364564
[ LOOKAHEAD(2) retval = ArrayExpression(retval) ]
45374565

4538-
( "::" type=ColDataType() {
4566+
( LOOKAHEAD(2) "::" type=ColDataType() {
45394567
castExpr = new CastExpression();
45404568
castExpr.setUseCastKeyword(false);
45414569
castExpr.setLeftExpression(retval);
@@ -4546,8 +4574,8 @@ Expression PrimaryExpression() #PrimaryExpression:
45464574

45474575
// Check for JSON operands
45484576
[
4549-
LOOKAHEAD(2) (
4550-
"->" (token=<S_CHAR_LITERAL> | token=<S_LONG>) { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"->")); }
4577+
LOOKAHEAD(2) (
4578+
"->" ( token=<S_CHAR_LITERAL> | token=<S_LONG>) { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"->")); }
45514579
|
45524580
"->>" (token=<S_CHAR_LITERAL> | token=<S_LONG>) { jsonIdents.add(new AbstractMap.SimpleEntry<String, String>(token.image,"->>")); }
45534581
|
@@ -4784,17 +4812,6 @@ StructType StructType() #StruckType:
47844812
}
47854813
}
47864814

4787-
Expression ParenthesedExpression():
4788-
{
4789-
Expression expression;
4790-
}
4791-
{
4792-
"(" expression = PrimaryExpression() ")"
4793-
{
4794-
return new ParenthesedExpressionList(expression);
4795-
}
4796-
}
4797-
47984815
JsonExpression JsonExpression(Expression expr, List<Map.Entry<String, String>> idents) : {
47994816
JsonExpression result = new JsonExpression(expr, idents);
48004817
Token token;
@@ -5453,28 +5470,22 @@ FullTextSearch FullTextSearch() : {
54535470

54545471
LambdaExpression LambdaExpression() #LambdaExpression:
54555472
{
5473+
ExpressionList<Column> columns;
54565474
String s;
5457-
ArrayList<String> identifiers = new ArrayList<String>();
54585475
Expression expression;
54595476
LambdaExpression lambdaExpression;
54605477
}
54615478
{
5462-
// wip, right now the Grammar works but collides with Multi Value Lists
5463-
// (
5464-
// LOOKAHEAD(3) "("
5465-
// s = RelObjectName() { identifiers.add(s); }
5466-
// ( "," s = RelObjectName() { identifiers.add(s); } )*
5467-
// ")"
5468-
// )
5469-
// |
54705479
(
5471-
s = RelObjectName() { identifiers.add(s); }
5480+
columns = ParenthesedColumnList()
5481+
|
5482+
s = RelObjectName() { columns = new ExpressionList<Column>(new Column(s)); }
54725483
)
54735484

54745485
"->"
54755486
expression = Expression()
54765487
{
5477-
lambdaExpression = new LambdaExpression(identifiers, expression);
5488+
lambdaExpression = LambdaExpression.from(columns, expression);
54785489
linkAST(lambdaExpression,jjtThis);
54795490
return lambdaExpression;
54805491
}
@@ -5559,7 +5570,7 @@ Function SimpleFunction():
55595570
}
55605571
}
55615572

5562-
[ "." (
5573+
[ LOOKAHEAD(2) "." (
55635574
// tricky lookahead since we do need to support the following constructs
55645575
// schema.f1().f2() - Function with Function Column
55655576
// schema.f1().f2.f3 - Function with Attribute Column
@@ -5632,7 +5643,7 @@ Function InternalFunction(boolean escaped):
56325643
")"
56335644

56345645

5635-
[ "." (
5646+
[ LOOKAHEAD(2) "." (
56365647
// tricky lookahead since we do need to support the following constructs
56375648
// schema.f1().f2() - Function with Function Column
56385649
// schema.f1().f2.f3 - Function with Attribute Column
@@ -5862,7 +5873,8 @@ CreateSchema CreateSchema():
58625873
table.getTable().setSchemaName(schema.getSchemaName());
58635874
schema.addStatement(table);
58645875
}
5865-
| view = CreateView(false)
5876+
|
5877+
view = CreateView(false)
58665878
{
58675879
view.getView().setSchemaName(schema.getSchemaName());
58685880
schema.addStatement(view);
@@ -6209,7 +6221,7 @@ ColDataType ColDataType():
62096221
| tk=<K_DATA>
62106222
) { schema = tk.image; }
62116223

6212-
[ "." arrayType = ColDataType() { schema += "." + arrayType.toString(); } ]
6224+
[ LOOKAHEAD(2) "." arrayType = ColDataType() { schema += "." + arrayType.toString(); } ]
62136225
{ colDataType.setDataType(schema); }
62146226
)
62156227

@@ -7753,7 +7765,7 @@ String IdentifierChain():
77537765
}
77547766
{
77557767
identifierChain=RelObjectNameExt2()
7756-
( "." part=RelObjectNameExt2() { identifierChain += "." + part; } )*
7768+
( LOOKAHEAD(2) "." part=RelObjectNameExt2() { identifierChain += "." + part; } )*
77577769

77587770
{
77597771
return identifierChain;

src/test/java/net/sf/jsqlparser/expression/LambdaExpressionTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import net.sf.jsqlparser.JSQLParserException;
1313
import net.sf.jsqlparser.test.TestUtils;
14-
import org.junit.jupiter.api.Disabled;
1514
import org.junit.jupiter.api.Test;
1615

1716
import static org.junit.jupiter.api.Assertions.*;
@@ -24,15 +23,26 @@ void testLambdaFunctionSingleParameter() throws JSQLParserException {
2423
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
2524
}
2625

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

35+
@Test
36+
void testLambdaMultiParameterIssue2030() throws JSQLParserException {
37+
String sqlStr = "SELECT map_filter(my_column, v -> v.my_inner_column = 'some_value')\n" +
38+
"FROM my_table";
39+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
40+
}
41+
42+
@Test
43+
void testLambdaMultiParameterIssue2032() throws JSQLParserException {
44+
String sqlStr = "SELECT array_sort(array_agg(named_struct('depth', events_union.depth, 'eventtime',events_union.eventtime)), (left, right) -> case when(left.eventtime, left.depth) <(right.eventtime, right.depth) then -1 when(left.eventtime, left.depth) >(right.eventtime, right.depth) then 1 else 0 end) as col1 FROM your_table;";
45+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
46+
}
47+
3848
}

src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/lexer01.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010
select * from dual where 1 < > 2 and 1 ! = 2 and 1 ^ /*aaa */ = 2
1111

1212

13-
--@FAILURE: Encountered unexpected token: "=" "=" recorded first on Aug 3, 2021, 7:20:08 AM
13+
--@FAILURE: Encountered unexpected token: "=" "=" recorded first on Aug 3, 2021, 7:20:08 AM
14+
--@FAILURE: Encountered unexpected token: "^" "^" recorded first on Jul 11, 2024, 9:09:49 AM

0 commit comments

Comments
 (0)