Skip to content

Commit 732e840

Browse files
committed
fixes #1581
1 parent 2f4916d commit 732e840

File tree

8 files changed

+270
-122
lines changed

8 files changed

+270
-122
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Any requests for examples or any particular documentation will be most welcome.
5656

5757
## Extensions in the latest SNAPSHOT version 4.6
5858

59+
* support for named windows in window expressions: `SELECT sum(c) OVER winName FROM mytable WINDOW winName AS (PARTITION BY pcol)`
60+
5961
Additionally, we have fixed many errors and improved the code quality and the test coverage.
6062

6163
## Extensions of JSqlParser releases

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

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,14 @@
1616
import net.sf.jsqlparser.statement.select.OrderByElement;
1717

1818
/**
19-
* Analytic function. The name of the function is variable but the parameters following the special
20-
* analytic function path. e.g. row_number() over (order by test). Additional there can be an
21-
* expression for an analytical aggregate like sum(col) or the "all collumns" wildcard like
22-
* count(*).
19+
* Analytic function. The name of the function is variable but the parameters following the special analytic function
20+
* path. e.g. row_number() over (order by test). Additional there can be an expression for an analytical aggregate like
21+
* sum(col) or the "all collumns" wildcard like count(*).
2322
*
2423
* @author tw
2524
*/
2625
public class AnalyticExpression extends ASTNodeAccessImpl implements Expression {
2726

28-
private final OrderByClause orderBy = new OrderByClause();
29-
private final PartitionByClause partitionBy = new PartitionByClause();
3027
private String name;
3128
private Expression expression;
3229
private Expression offset;
@@ -39,8 +36,9 @@ public class AnalyticExpression extends ASTNodeAccessImpl implements Expression
3936
private boolean ignoreNulls = false; //IGNORE NULLS inside function parameters
4037
private boolean ignoreNullsOutside = false; //IGNORE NULLS outside function parameters
4138
private Expression filterExpression = null;
42-
private WindowElement windowElement = null;
4339
private List<OrderByElement> funcOrderBy = null;
40+
private String windowName = null; // refers to an external window definition (paritionBy, orderBy, windowElement)
41+
private WindowDefinition windowDef = new WindowDefinition();
4442

4543
public AnalyticExpression() {
4644
}
@@ -76,11 +74,11 @@ public void accept(ExpressionVisitor expressionVisitor) {
7674
}
7775

7876
public List<OrderByElement> getOrderByElements() {
79-
return orderBy.getOrderByElements();
77+
return windowDef.orderBy.getOrderByElements();
8078
}
8179

8280
public void setOrderByElements(List<OrderByElement> orderByElements) {
83-
orderBy.setOrderByElements(orderByElements);
81+
windowDef.orderBy.setOrderByElements(orderByElements);
8482
}
8583

8684
public KeepExpression getKeep() {
@@ -92,19 +90,19 @@ public void setKeep(KeepExpression keep) {
9290
}
9391

9492
public ExpressionList getPartitionExpressionList() {
95-
return partitionBy.getPartitionExpressionList();
93+
return windowDef.partitionBy.getPartitionExpressionList();
9694
}
9795

9896
public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
9997
setPartitionExpressionList(partitionExpressionList, false);
10098
}
10199

102100
public void setPartitionExpressionList(ExpressionList partitionExpressionList, boolean brackets) {
103-
partitionBy.setPartitionExpressionList(partitionExpressionList, brackets);
101+
windowDef.partitionBy.setPartitionExpressionList(partitionExpressionList, brackets);
104102
}
105103

106104
public boolean isPartitionByBrackets() {
107-
return partitionBy.isBrackets();
105+
return windowDef.partitionBy.isBrackets();
108106
}
109107

110108
public String getName() {
@@ -140,11 +138,11 @@ public void setDefaultValue(Expression defaultValue) {
140138
}
141139

142140
public WindowElement getWindowElement() {
143-
return windowElement;
141+
return windowDef.windowElement;
144142
}
145143

146144
public void setWindowElement(WindowElement windowElement) {
147-
this.windowElement = windowElement;
145+
windowDef.windowElement = windowElement;
148146
}
149147

150148
public AnalyticType getType() {
@@ -187,6 +185,22 @@ public void setIgnoreNullsOutside(boolean ignoreNullsOutside) {
187185
this.ignoreNullsOutside = ignoreNullsOutside;
188186
}
189187

188+
public String getWindowName() {
189+
return windowName;
190+
}
191+
192+
public void setWindowName(String windowName) {
193+
this.windowName = windowName;
194+
}
195+
196+
public WindowDefinition getWindowDefinition() {
197+
return windowDef;
198+
}
199+
200+
public void setWindowDefinition(WindowDefinition windowDef) {
201+
this.windowDef = windowDef;
202+
}
203+
190204
@Override
191205
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", "PMD.MissingBreakInSwitch"})
192206
public String toString() {
@@ -210,11 +224,11 @@ public String toString() {
210224
if (isIgnoreNulls()) {
211225
b.append(" IGNORE NULLS");
212226
}
213-
if (funcOrderBy!=null) {
227+
if (funcOrderBy != null) {
214228
b.append(" ORDER BY ");
215-
b.append( funcOrderBy.stream().map(OrderByElement::toString).collect(joining(", ")));
229+
b.append(funcOrderBy.stream().map(OrderByElement::toString).collect(joining(", ")));
216230
}
217-
231+
218232
b.append(") ");
219233
if (keep != null) {
220234
b.append(keep.toString()).append(" ");
@@ -232,7 +246,7 @@ public String toString() {
232246
if (isIgnoreNullsOutside()) {
233247
b.append("IGNORE NULLS ");
234248
}
235-
249+
236250
switch (type) {
237251
case FILTER_ONLY:
238252
return b.toString();
@@ -242,20 +256,14 @@ public String toString() {
242256
default:
243257
b.append("OVER");
244258
}
245-
b.append(" (");
246259

247-
partitionBy.toStringPartitionBy(b);
248-
orderBy.toStringOrderByElements(b);
249-
250-
if (windowElement != null) {
251-
if (orderBy.getOrderByElements() != null) {
252-
b.append(' ');
253-
}
254-
b.append(windowElement);
260+
if (windowName != null) {
261+
b.append(" ").append(windowName);
262+
} else {
263+
b.append(" ");
264+
b.append(windowDef.toString());
255265
}
256266

257-
b.append(")");
258-
259267
return b.toString();
260268
}
261269

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2022 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.expression;
11+
12+
import java.util.List;
13+
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
14+
import net.sf.jsqlparser.statement.select.OrderByElement;
15+
16+
public class WindowDefinition {
17+
18+
final OrderByClause orderBy = new OrderByClause();
19+
final PartitionByClause partitionBy = new PartitionByClause();
20+
WindowElement windowElement = null;
21+
private String windowName;
22+
23+
public WindowElement getWindowElement() {
24+
return windowElement;
25+
}
26+
27+
public void setWindowElement(WindowElement windowElement) {
28+
this.windowElement = windowElement;
29+
}
30+
31+
public List<OrderByElement> getOrderByElements() {
32+
return orderBy.getOrderByElements();
33+
}
34+
35+
public void setOrderByElements(List<OrderByElement> orderByElements) {
36+
orderBy.setOrderByElements(orderByElements);
37+
}
38+
39+
public ExpressionList getPartitionExpressionList() {
40+
return partitionBy.getPartitionExpressionList();
41+
}
42+
43+
public void setPartitionExpressionList(ExpressionList partitionExpressionList) {
44+
setPartitionExpressionList(partitionExpressionList, false);
45+
}
46+
47+
public void setPartitionExpressionList(ExpressionList partitionExpressionList, boolean brackets) {
48+
partitionBy.setPartitionExpressionList(partitionExpressionList, brackets);
49+
}
50+
51+
public String getWindowName() {
52+
return windowName;
53+
}
54+
55+
public void setWindowName(String windowName) {
56+
this.windowName = windowName;
57+
}
58+
59+
public WindowDefinition withWindowName(String windowName) {
60+
setWindowName(windowName);
61+
return this;
62+
}
63+
64+
@Override
65+
public String toString() {
66+
StringBuilder b = new StringBuilder();
67+
if (windowName != null) {
68+
b.append(windowName).append(" AS ");
69+
}
70+
b.append("(");
71+
partitionBy.toStringPartitionBy(b);
72+
orderBy.toStringOrderByElements(b);
73+
74+
if (windowElement != null) {
75+
if (orderBy.getOrderByElements() != null) {
76+
b.append(' ');
77+
}
78+
b.append(windowElement);
79+
}
80+
b.append(")");
81+
return b.toString();
82+
}
83+
}

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

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
import java.util.Iterator;
1616
import java.util.List;
1717
import java.util.Optional;
18+
import static java.util.stream.Collectors.joining;
1819
import net.sf.jsqlparser.expression.Expression;
1920
import net.sf.jsqlparser.expression.OracleHierarchicalExpression;
2021
import net.sf.jsqlparser.expression.OracleHint;
22+
import net.sf.jsqlparser.expression.WindowDefinition;
2123
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
2224
import net.sf.jsqlparser.schema.Table;
2325

@@ -55,6 +57,7 @@ public class PlainSelect extends ASTNodeAccessImpl implements SelectBody {
5557
private boolean noWait = false;
5658
private boolean emitChanges = false;
5759
private WithIsolation withIsolation;
60+
private List<WindowDefinition> windowDefinitions;
5861

5962
public boolean isUseBrackets() {
6063
return useBrackets;
@@ -230,8 +233,7 @@ public void setHaving(Expression expression) {
230233
}
231234

232235
/**
233-
* A list of {@link Expression}s of the GROUP BY clause. It is null in case
234-
* there is no GROUP BY clause
236+
* A list of {@link Expression}s of the GROUP BY clause. It is null in case there is no GROUP BY clause
235237
*
236238
* @return a list of {@link Expression}s
237239
*/
@@ -331,7 +333,6 @@ public boolean isEmitChanges() {
331333
return emitChanges;
332334
}
333335

334-
335336
public WithIsolation getWithIsolation() {
336337
return withIsolation;
337338
}
@@ -340,8 +341,16 @@ public void setWithIsolation(WithIsolation withIsolation) {
340341
this.withIsolation = withIsolation;
341342
}
342343

344+
public List<WindowDefinition> getWindowDefinitions() {
345+
return windowDefinitions;
346+
}
347+
348+
public void setWindowDefinitions(List<WindowDefinition> windowDefinitions) {
349+
this.windowDefinitions = windowDefinitions;
350+
}
351+
343352
@Override
344-
@SuppressWarnings({"PMD.CyclomaticComplexity" , "PMD.ExcessiveMethodLength", "PMD.NPathComplexity"})
353+
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ExcessiveMethodLength", "PMD.NPathComplexity"})
345354
public String toString() {
346355
StringBuilder sql = new StringBuilder();
347356
if (useBrackets) {
@@ -418,8 +427,14 @@ public String toString() {
418427
if (having != null) {
419428
sql.append(" HAVING ").append(having);
420429
}
430+
431+
if (windowDefinitions != null) {
432+
sql.append(" WINDOW ");
433+
sql.append(windowDefinitions.stream().map(WindowDefinition::toString).collect(joining(", ")));
434+
}
435+
421436
sql.append(orderByToString(oracleSiblings, orderByElements));
422-
if (emitChanges){
437+
if (emitChanges) {
423438
sql.append(" EMIT CHANGES");
424439
}
425440
if (limit != null) {
@@ -471,7 +486,7 @@ public String toString() {
471486
}
472487
if (withIsolation != null) {
473488
sql.append(withIsolation);
474-
}
489+
}
475490
}
476491
if (forXmlPath != null) {
477492
sql.append(" FOR XML PATH(").append(forXmlPath).append(")");
@@ -509,8 +524,8 @@ public static String getFormatedList(List<?> list, String expression, boolean us
509524
}
510525

511526
/**
512-
* List the toString out put of the objects in the List comma separated. If the
513-
* List is null or empty an empty string is returned.
527+
* List the toString out put of the objects in the List comma separated. If the List is null or empty an empty
528+
* string is returned.
514529
*
515530
* The same as getStringList(list, true, false)
516531
*
@@ -523,11 +538,11 @@ public static String getStringList(List<?> list) {
523538
}
524539

525540
/**
526-
* List the toString out put of the objects in the List that can be comma
527-
* separated. If the List is null or empty an empty string is returned.
541+
* List the toString out put of the objects in the List that can be comma separated. If the List is null or empty an
542+
* empty string is returned.
528543
*
529-
* @param list list of objects with toString methods
530-
* @param useComma true if the list has to be comma separated
544+
* @param list list of objects with toString methods
545+
* @param useComma true if the list has to be comma separated
531546
* @param useBrackets true if the list has to be enclosed in brackets
532547
* @return comma separated list of the elements in the list
533548
*/
@@ -536,11 +551,11 @@ public static String getStringList(List<?> list, boolean useComma, boolean useBr
536551
}
537552

538553
/**
539-
* Append the toString out put of the objects in the List (that can be comma
540-
* separated). If the List is null or empty an empty string is returned.
554+
* Append the toString out put of the objects in the List (that can be comma separated). If the List is null or
555+
* empty an empty string is returned.
541556
*
542-
* @param list list of objects with toString methods
543-
* @param useComma true if the list has to be comma separated
557+
* @param list list of objects with toString methods
558+
* @param useComma true if the list has to be comma separated
544559
* @param useBrackets true if the list has to be enclosed in brackets
545560
* @return comma separated list of the elements in the list
546561
*/
@@ -554,9 +569,9 @@ public static StringBuilder appendStringListTo(StringBuilder builder, List<?> li
554569

555570
int size = list.size();
556571
for (int i = 0; i < size; i++) {
557-
builder.append(list.get(i)).append( i < size - 1
572+
builder.append(list.get(i)).append(i < size - 1
558573
? comma + " "
559-
: "" );
574+
: "");
560575
}
561576

562577
if (useBrackets) {

0 commit comments

Comments
 (0)