diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index a93efedb5..fc6c323c8 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -64,6 +64,8 @@ import net.sf.jsqlparser.statement.select.UnPivot; import net.sf.jsqlparser.statement.select.WithItem; +import java.util.Optional; + @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.UncommentedEmptyMethodBody"}) public class ExpressionVisitorAdapter implements ExpressionVisitor, PivotVisitor, SelectItemVisitor { @@ -382,11 +384,19 @@ public void visit(AnalyticExpression expr) { element.getExpression().accept(this); } } - if (expr.getWindowElement() != null) { - expr.getWindowElement().getRange().getStart().getExpression().accept(this); - expr.getWindowElement().getRange().getEnd().getExpression().accept(this); - expr.getWindowElement().getOffset().getExpression().accept(this); + /* + * Visit expressions from the range and offset of the window element. Do this using + * optional chains, because several things down the tree can be null e.g. the + * expression. So, null-safe versions of e.g.: + * expr.getWindowElement().getOffset().getExpression().accept(this); + */ + Optional.ofNullable(expr.getWindowElement().getRange()).map(WindowRange::getStart) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this)); + Optional.ofNullable(expr.getWindowElement().getRange()).map(WindowRange::getEnd) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this)); + Optional.ofNullable(expr.getWindowElement().getOffset()) + .map(WindowOffset::getExpression).ifPresent(e -> e.accept(this)); } } diff --git a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java index 52e71b8bf..14ae67fed 100644 --- a/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java @@ -259,4 +259,13 @@ public void visit(AllTableColumns all) { assertNotNull(holder[0]); assertEquals("a.*", holder[0].toString()); } + + @Test + public void testAnalyticExpressionWithPartialWindowElement() throws JSQLParserException { + ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter(); + Expression expression = CCJSqlParserUtil.parseExpression( + "SUM(\"Spent\") OVER (PARTITION BY \"ID\" ORDER BY \"Name\" ASC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)"); + + expression.accept(adapter); + } }