Skip to content

Commit 5c2a453

Browse files
feat: More lenient FromQuery allowing for Join and WithItem
- fixes starlake-ai/jsqltranspiler#73 - fixes starlake-ai/jsqltranspiler#72 Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>
1 parent 1d59430 commit 5c2a453

29 files changed

+215
-25
lines changed

src/main/java/net/sf/jsqlparser/statement/piped/FromQuery.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,19 @@
44
import net.sf.jsqlparser.expression.ExpressionVisitor;
55
import net.sf.jsqlparser.statement.select.FromItem;
66
import net.sf.jsqlparser.statement.select.FromItemVisitor;
7+
import net.sf.jsqlparser.statement.select.Join;
8+
import net.sf.jsqlparser.statement.select.LateralView;
79
import net.sf.jsqlparser.statement.select.Pivot;
810
import net.sf.jsqlparser.statement.select.SampleClause;
911
import net.sf.jsqlparser.statement.select.Select;
1012
import net.sf.jsqlparser.statement.select.SelectVisitor;
1113
import net.sf.jsqlparser.statement.select.UnPivot;
14+
import net.sf.jsqlparser.statement.select.WithItem;
1215

1316
import java.util.ArrayList;
17+
import java.util.Arrays;
1418
import java.util.Collection;
19+
import java.util.Iterator;
1520
import java.util.List;
1621
import java.util.Spliterator;
1722
import java.util.function.Consumer;
@@ -21,6 +26,8 @@
2126
public class FromQuery extends Select {
2227
private boolean usingFromKeyword = true;
2328
private FromItem fromItem;
29+
private List<LateralView> lateralViews = null;
30+
private List<Join> joins = null;
2431
private final ArrayList<PipeOperator> pipeOperators = new ArrayList<>();
2532

2633
public FromQuery(FromItem fromItem) {
@@ -49,6 +56,51 @@ public boolean isUsingFromKeyword() {
4956
return usingFromKeyword;
5057
}
5158

59+
60+
public List<LateralView> getLateralViews() {
61+
return lateralViews;
62+
}
63+
64+
public FromQuery setLateralViews(List<LateralView> lateralViews) {
65+
this.lateralViews = lateralViews;
66+
return this;
67+
}
68+
69+
public FromQuery addLateralViews(Collection<LateralView> lateralViews) {
70+
if (this.lateralViews == null) {
71+
this.lateralViews = new ArrayList<>(lateralViews);
72+
} else {
73+
this.lateralViews.addAll(lateralViews);
74+
}
75+
return this;
76+
}
77+
78+
public FromQuery addLateralViews(LateralView... lateralViews) {
79+
return this.addLateralViews(Arrays.asList(lateralViews));
80+
}
81+
82+
public List<Join> getJoins() {
83+
return joins;
84+
}
85+
86+
public FromQuery setJoins(List<Join> joins) {
87+
this.joins = joins;
88+
return this;
89+
}
90+
91+
public FromQuery addJoins(Collection<Join> joins) {
92+
if (this.joins == null) {
93+
this.joins = new ArrayList<>(joins);
94+
} else {
95+
this.joins.addAll(joins);
96+
}
97+
return this;
98+
}
99+
100+
public FromQuery addJoins(Join... joins) {
101+
return addJoins(Arrays.asList(joins));
102+
}
103+
52104
public FromQuery setUsingFromKeyword(boolean usingFromKeyword) {
53105
this.usingFromKeyword = usingFromKeyword;
54106
return this;
@@ -220,10 +272,36 @@ public <T, S> T accept(FromQueryVisitor<T, S> fromQueryVisitor, S context) {
220272

221273
@Override
222274
public StringBuilder appendTo(StringBuilder builder) {
275+
if (withItemsList != null && !withItemsList.isEmpty()) {
276+
builder.append("WITH ");
277+
for (Iterator<WithItem<?>> iter = withItemsList.iterator(); iter.hasNext();) {
278+
WithItem withItem = iter.next();
279+
builder.append(withItem);
280+
if (iter.hasNext()) {
281+
builder.append(",");
282+
}
283+
builder.append(" ");
284+
}
285+
}
286+
223287
if (usingFromKeyword) {
224288
builder.append("FROM ");
225289
}
226290
builder.append(fromItem).append("\n");
291+
if (lateralViews != null) {
292+
for (LateralView lateralView : lateralViews) {
293+
builder.append(" ").append(lateralView);
294+
}
295+
}
296+
if (joins != null) {
297+
for (Join join : joins) {
298+
if (join.isSimple()) {
299+
builder.append(", ").append(join);
300+
} else {
301+
builder.append(" ").append(join);
302+
}
303+
}
304+
}
227305
for (PipeOperator operator : pipeOperators) {
228306
operator.appendTo(builder);
229307
}

src/main/java/net/sf/jsqlparser/statement/select/Select.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
public abstract class Select extends ASTNodeAccessImpl implements Statement, Expression, FromItem {
2828
protected Table forUpdateTable = null;
29-
List<WithItem<?>> withItemsList;
29+
protected List<WithItem<?>> withItemsList;
3030
Limit limitBy;
3131
Limit limit;
3232
Offset offset;

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,11 +840,36 @@ public void visit(TableStatement tableStatement) {
840840

841841
@Override
842842
public <S> StringBuilder visit(FromQuery fromQuery, S context) {
843+
List<WithItem<?>> withItemsList = fromQuery.getWithItemsList();
844+
if (withItemsList != null && !withItemsList.isEmpty()) {
845+
builder.append("WITH ");
846+
for (Iterator<WithItem<?>> iter = withItemsList.iterator(); iter.hasNext();) {
847+
iter.next().accept((SelectVisitor<?>) this, context);
848+
if (iter.hasNext()) {
849+
builder.append(",");
850+
}
851+
builder.append(" ");
852+
}
853+
}
854+
843855
if (fromQuery.isUsingFromKeyword()) {
844856
builder.append("FROM ");
845857
}
846858
fromQuery.getFromItem().accept(this, context);
847859
builder.append("\n");
860+
861+
if (fromQuery.getLateralViews() != null) {
862+
for (LateralView lateralView : fromQuery.getLateralViews()) {
863+
deparseLateralView(lateralView);
864+
}
865+
}
866+
867+
if (fromQuery.getJoins() != null) {
868+
for (Join join : fromQuery.getJoins()) {
869+
deparseJoin(join);
870+
}
871+
}
872+
848873
for (PipeOperator operator : fromQuery.getPipeOperators()) {
849874
operator.accept(this, null);
850875
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2345,10 +2345,14 @@ FromQuery FromQuery() #FromQuery:
23452345
{
23462346
FromQuery fromQuery;
23472347
FromItem fromItem;
2348+
List<LateralView> lateralViews = null;
2349+
List<Join> joins = null;
23482350
PipeOperator pipeOperator;
23492351
}
23502352
{
23512353
<K_FROM> fromItem = FromItem() { fromQuery = new FromQuery(fromItem); }
2354+
[ LOOKAHEAD(2) lateralViews=LateralViews() { fromQuery.setLateralViews(lateralViews); } ]
2355+
[ LOOKAHEAD(2) joins=JoinsList() { fromQuery.setJoins(joins); } ]
23522356
(
23532357
LOOKAHEAD(2) "|>" pipeOperator = PipeOperator() { fromQuery.add(pipeOperator); }
23542358
)*

src/test/java/net/sf/jsqlparser/statement/piped/FromQueryTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,76 @@
88
public class FromQueryTest {
99
@Test
1010
void testParseAndDeparse() throws JSQLParserException {
11+
// formatter:off
1112
String sqlStr = "FROM Produce\n"
1213
+ "|> WHERE\n"
1314
+ " item != 'bananas'\n"
1415
+ " AND category IN ('fruit', 'nut')\n"
1516
+ "|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales\n"
1617
+ " GROUP BY item\n"
1718
+ "|> ORDER BY item DESC;";
19+
// formatter:on
1820
FromQuery fromQuery = (FromQuery) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
1921

2022
Assertions.assertInstanceOf(WherePipeOperator.class, fromQuery.get(0));
2123
Assertions.assertInstanceOf(AggregatePipeOperator.class, fromQuery.get(1));
2224
Assertions.assertInstanceOf(OrderByPipeOperator.class, fromQuery.get(2));
2325
}
26+
27+
@Test
28+
void testParseAndDeparseJoin() throws JSQLParserException {
29+
// formatter:off
30+
String sqlStr =
31+
"FROM Produce INNER JOIN Price USING(id_product) \n"
32+
+ "|> WHERE\n"
33+
+ " item != 'bananas'\n"
34+
+ " AND category IN ('fruit', 'nut')\n"
35+
+ "|> AGGREGATE COUNT(*) AS num_items, SUM(sales) AS total_sales\n"
36+
+ " GROUP BY item\n"
37+
+ "|> ORDER BY item DESC;";
38+
// formatter:on
39+
40+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
41+
}
42+
43+
@Test
44+
void testParseAndDeparseWith() throws JSQLParserException {
45+
// formatter:off
46+
String sqlStr =
47+
"with client_info as (\n" +
48+
" with client as (\n" +
49+
" select 1 as client_id\n" +
50+
" |> UNION ALL\n" +
51+
" (select 2),\n" +
52+
" (select 3)\n" +
53+
" ), basket as (\n" +
54+
" select 1 as basket_id, 1 as client_id\n" +
55+
" |> UNION ALL\n" +
56+
" (select 2, 2)\n" +
57+
" ), basket_item as (\n" +
58+
" select 1 as item_id, 1 as basket_id\n" +
59+
" |> UNION ALL\n" +
60+
" (select 2, 1),\n" +
61+
" (select 3, 1),\n" +
62+
" (select 4, 2)\n" +
63+
" ), item as (\n" +
64+
" select 1 as item_id, 'milk' as name\n" +
65+
" |> UNION ALL\n" +
66+
" (select 2, \"chocolate\"),\n" +
67+
" (select 3, \"donut\"),\n" +
68+
" (select 4, \"croissant\")\n" +
69+
" ), wrapper as (\n" +
70+
" FROM client c\n" +
71+
" |> aggregate count(i.item_id) as bought_item\n" +
72+
" group by c.client_id, i.item_id, i.name\n" +
73+
" |> aggregate array_agg((select as struct item_id, name, bought_item)) as items_info\n"
74+
+
75+
" group by client_id\n" +
76+
" )\n" +
77+
" select * from wrapper\n" +
78+
")\n" +
79+
"select * from client_info";
80+
// formatter:on
81+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
82+
}
2483
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ from
2121
)
2222

2323

24-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
24+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
25+
--@FAILURE: Encountered unexpected token: "(" "(" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ select deptno
1616
group by
1717
deptno
1818

19-
--@FAILURE: Encountered unexpected token: "varchar2_ntt" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
19+
--@FAILURE: Encountered unexpected token: "varchar2_ntt" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
20+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ select owner
1818
owner
1919
, object_type
2020

21-
--@FAILURE: Encountered unexpected token: "varchar2_ntt" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
21+
--@FAILURE: Encountered unexpected token: "varchar2_ntt" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
22+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ where
1919
and "timestamp" <= 1298505600000
2020

2121

22-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
22+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
23+
--@FAILURE: Encountered unexpected token: "not" "NOT" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ from o
2727
connect by nocycle obj=prior link
2828
start with obj='a'
2929

30-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 14, 2021 9:00:57 PM
30+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 14, 2021 9:00:57 PM
31+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ where (nvl(su.up,'n')='n' and su.ttype=:b0)
1313
for update of su.up
1414
order by su.d
1515

16-
--@FAILURE: select su.ttype,su.cid,su.s_id,sessiontimezone from sku su where(nvl(su.up,'n')='n' and su.ttype=:b0)order by su.d for update of su.up recorded first on 20 Apr 2024, 15:59:32
16+
--@FAILURE: select su.ttype,su.cid,su.s_id,sessiontimezone from sku su where(nvl(su.up,'n')='n' and su.ttype=:b0)order by su.d for update of su.up recorded first on 20 Apr 2024, 15:59:32
17+
--@FAILURE: Encountered unexpected token: "order" "ORDER" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ union all
1313
(select null keep, null keep_until
1414
from v$backup_piece bp)
1515

16-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on 3 Jun 2022, 18:48:09
16+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on 3 Jun 2022, 18:48:09
17+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ where r.c1 = a.c2
2424
order by reportlevel, eid
2525

2626

27-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
27+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
28+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ from reports_to_101
2626
order by reportlevel, eid
2727

2828

29-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
29+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
30+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ where reportlevel <= 1
2424
order by reportlevel, eid
2525

2626

27-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
27+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
28+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ order by order1
2626

2727

2828

29-
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:07 AM
29+
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:07 AM
30+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ select root,lev,obj,link,path,cycle,
4141
from t
4242

4343

44-
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
44+
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
45+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ select lpad(' ',2*reportlevel)||emp_last emp_name, eid, mgr_id, hire_date, job_i
2222
from dup_hiredate
2323
order by order1
2424

25-
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
25+
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
26+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ group by emp_last, eid, mgr_id, salary
2222
having max(mgrlevel) > 0
2323
order by mgr_id nulls first, emp_last
2424

25-
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
25+
--@FAILURE: Encountered unexpected token: "search" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
26+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
(select 'e', 'e' from dual)
1616

1717

18-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
18+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
19+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ union all
1212
(select distinct job_id from hr.job_history)
1313

1414

15-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
15+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Aug 3, 2021, 7:20:08 AM
16+
--@FAILURE: Encountered unexpected token: "union" "UNION" recorded first on Feb 13, 2025, 10:16:06 AM

0 commit comments

Comments
 (0)