diff --git a/src/main/java/net/sf/jsqlparser/statement/select/Join.java b/src/main/java/net/sf/jsqlparser/statement/select/Join.java index f60bdaf46..6b774e201 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/Join.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/Join.java @@ -37,6 +37,7 @@ public class Join extends ASTNodeAccessImpl { private boolean semi = false; private boolean straight = false; private boolean apply = false; + private boolean fetch = false; private FromItem fromItem; private KSQLJoinWindow joinWindow; @@ -149,6 +150,24 @@ public Join withApply(boolean apply) { return this; } + /** + * Whether is a "FETCH" join (JPQL/HQL) + * + * @return true if is a "FETCH" join + */ + public boolean isFetch() { + return fetch; + } + + public void setFetch(boolean b) { + fetch = b; + } + + public Join withFetch(boolean b) { + this.setFetch(b); + return this; + } + /** * Whether is a "SEMI" join * @@ -429,6 +448,9 @@ public String toString() { builder.append(joinHint).append(" "); } builder.append("JOIN "); + if (fetch) { + builder.append("FETCH "); + } } builder.append(fromItem).append((joinWindow != null) ? " WITHIN " + joinWindow : ""); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index e36e1038a..5059f7cea 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -616,6 +616,9 @@ public void deparseJoin(Join join) { builder.append(" ").append(join.getJoinHint()); } builder.append(" JOIN "); + if (join.isFetch()) { + builder.append("FETCH "); + } } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 74d36e5a5..e6b70cae6 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -4971,7 +4971,11 @@ Join JoinerExpression() #JoinerExpression: ] ( - ( [ joinHint=JoinHint() {join.setJoinHint(joinHint); } ] ) + ( + [ joinHint=JoinHint() {join.setJoinHint(joinHint); } ] + + [ { join.setFetch(true); } ] + ) | "," { join.setSimple(true); } ( { join.setOuter(true); } )? | diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 9a361b525..82783e822 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -1210,6 +1210,12 @@ public void testJoin() throws JSQLParserException { assertEquals("b", plainSelect.getJoins().get(0).getFromItem().getAlias().getName()); } + @Test + public void testJoinFetch() throws JSQLParserException { + String statement = "SELECT c FROM Customer c LEFT JOIN FETCH c.orders o"; + assertSqlCanBeParsedAndDeparsed(statement, true); + } + @Test public void testFunctions() throws JSQLParserException { String statement = "SELECT MAX(id) AS max FROM mytable WHERE mytable.col = 9";