Skip to content

Commit 8eb3d9a

Browse files
Nested with items (#1221)
* Nested WithItems, fixes issue #1186 * Remove redundant Test Avoid altering the nb-configuration * Mention Nested WITH CTEs in the readme * Eliminate dead/unused MultiExpression Code
1 parent 999db01 commit 8eb3d9a

File tree

8 files changed

+199
-98
lines changed

8 files changed

+199
-98
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ Also I would like to know about needed examples or documentation stuff.
5757

5858
## Extensions in the latest SNAPSHOT version 4.1
5959

60-
* support for **with** (ctl) for **delete**, **update** and **merge**
60+
* support for nested `WITH` CTEs
61+
* support for **with** (cte) for **delete**, **update** and **merge**
6162
* introduce a max depth to allow parsing complex expression lists without performance loss (thx to @manticore-projects)
6263
* allow all functions to have complex expressions as parameters (thx to @manticore-projects)
63-
** API change FunctionWithCondParams production removed
64+
* API change FunctionWithCondParams production removed
6465
* API change in ValuesStatement: the expression list is now hold as a ItemList and not as a List<Expression>
6566
* support for parser modification within **parseExpression** and **parseCondExpression**
6667
* support for table schema for foreign keys

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

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,64 @@
1414
import java.util.Collections;
1515
import java.util.List;
1616
import java.util.Optional;
17+
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
18+
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
1719

1820
public class WithItem implements SelectBody {
1921

2022
private String name;
2123
private List<SelectItem> withItemList;
22-
private SelectBody selectBody;
24+
private ItemsList itemsList;
25+
private boolean useValues = true;
26+
private boolean useBracketsForValues = false;
27+
28+
private SubSelect subSelect;
2329
private boolean recursive;
2430

31+
/**
32+
* Get the values (as VALUES (...) or SELECT)
33+
*
34+
* @return the values of the insert
35+
*/
36+
public ItemsList getItemsList() {
37+
return itemsList;
38+
}
39+
40+
public void setItemsList(ItemsList list) {
41+
itemsList = list;
42+
}
43+
44+
public boolean isUseValues() {
45+
return useValues;
46+
}
47+
48+
public void setUseValues(boolean useValues) {
49+
this.useValues = useValues;
50+
}
51+
52+
public WithItem withItemsList(ItemsList itemsList) {
53+
this.setItemsList(itemsList);
54+
return this;
55+
}
56+
57+
public WithItem withUseValues(boolean useValues) {
58+
this.setUseValues(useValues);
59+
return this;
60+
}
61+
62+
public boolean isUsingBracketsForValues() {
63+
return useBracketsForValues;
64+
}
65+
66+
public void setUseBracketsForValues(boolean useBracketsForValues) {
67+
this.useBracketsForValues = useBracketsForValues;
68+
}
69+
70+
public WithItem withUseBracketsForValues(boolean useBracketsForValues) {
71+
this.setUseBracketsForValues(useBracketsForValues);
72+
return this;
73+
}
74+
2575
public String getName() {
2676
return name;
2777
}
@@ -38,12 +88,12 @@ public void setRecursive(boolean recursive) {
3888
this.recursive = recursive;
3989
}
4090

41-
public SelectBody getSelectBody() {
42-
return selectBody;
91+
public SubSelect getSubSelect() {
92+
return subSelect.withUseBrackets(false);
4393
}
4494

45-
public void setSelectBody(SelectBody selectBody) {
46-
this.selectBody = selectBody;
95+
public void setSubSelect(SubSelect subSelect) {
96+
this.subSelect = subSelect.withUseBrackets(false);
4797
}
4898

4999
/**
@@ -62,9 +112,26 @@ public void setWithItemList(List<SelectItem> withItemList) {
62112
@Override
63113
@SuppressWarnings({"PMD.CyclomaticComplexity"})
64114
public String toString() {
65-
return (recursive ? "RECURSIVE " : "") + name + ((withItemList != null) ? " " + PlainSelect.
66-
getStringList(withItemList, true, true) : "")
67-
+ " AS (" + selectBody + ")";
115+
StringBuilder builder = new StringBuilder();
116+
builder.append(recursive ? "RECURSIVE " : "");
117+
builder.append(name);
118+
builder.append(
119+
(withItemList != null) ? " " + PlainSelect.getStringList(withItemList, true, true) : "");
120+
builder.append(" AS ");
121+
122+
if (useValues) {
123+
builder.append("(VALUES ");
124+
ExpressionList expressionList = (ExpressionList) itemsList;
125+
builder.append(
126+
PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues));
127+
builder.append(")");
128+
} else {
129+
builder.append(subSelect.isUseBrackets() ? "" : "(");
130+
builder.append(subSelect);
131+
132+
builder.append(subSelect.isUseBrackets() ? "" : ")");
133+
}
134+
return builder.toString();
68135
}
69136

70137
@Override
@@ -82,8 +149,8 @@ public WithItem withWithItemList(List<SelectItem> withItemList) {
82149
return this;
83150
}
84151

85-
public WithItem withSelectBody(SelectBody selectBody) {
86-
this.setSelectBody(selectBody);
152+
public WithItem withSubSelect(SubSelect subSelect) {
153+
this.setSubSelect(subSelect);
87154
return this;
88155
}
89156

@@ -103,8 +170,4 @@ public WithItem addWithItemList(Collection<? extends SelectItem> withItemList) {
103170
collection.addAll(withItemList);
104171
return this.withWithItemList(collection);
105172
}
106-
107-
public <E extends SelectBody> E getSelectBody(Class<E> type) {
108-
return type.cast(getSelectBody());
109-
}
110173
}

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public List<String> getTableList(Expression expr) {
147147
@Override
148148
public void visit(WithItem withItem) {
149149
otherItemNames.add(withItem.getName().toLowerCase());
150-
withItem.getSelectBody().accept(this);
150+
withItem.getSubSelect().accept((ItemsListVisitor) this);
151151
}
152152

153153
@Override

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import net.sf.jsqlparser.expression.OracleHint;
2020
import net.sf.jsqlparser.expression.SQLServerHints;
2121
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
22+
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
2223
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
2324
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
2425
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
@@ -240,7 +241,7 @@ public void visit(SelectExpressionItem selectExpressionItem) {
240241

241242
@Override
242243
public void visit(SubSelect subSelect) {
243-
buffer.append("(");
244+
buffer.append(subSelect.isUseBrackets() ? "(" : "");
244245
if (subSelect.getWithItemsList() != null && !subSelect.getWithItemsList().isEmpty()) {
245246
buffer.append("WITH ");
246247
for (Iterator<WithItem> iter = subSelect.getWithItemsList().iterator(); iter.hasNext();) {
@@ -253,7 +254,7 @@ public void visit(SubSelect subSelect) {
253254
}
254255
}
255256
subSelect.getSelectBody().accept(this);
256-
buffer.append(")");
257+
buffer.append(subSelect.isUseBrackets() ? ")" : "");
257258
Alias alias = subSelect.getAlias();
258259
if (alias != null) {
259260
buffer.append(alias.toString());
@@ -484,9 +485,27 @@ public void visit(WithItem withItem) {
484485
if (withItem.getWithItemList() != null) {
485486
buffer.append(" ").append(PlainSelect.getStringList(withItem.getWithItemList(), true, true));
486487
}
487-
buffer.append(" AS (");
488-
withItem.getSelectBody().accept(this);
489-
buffer.append(")");
488+
buffer.append(" AS ");
489+
490+
if (withItem.isUseValues()) {
491+
ItemsList itemsList = withItem.getItemsList();
492+
boolean useBracketsForValues = withItem.isUsingBracketsForValues();
493+
buffer.append("(VALUES ");
494+
495+
ExpressionList expressionList = (ExpressionList) itemsList;
496+
buffer.append(
497+
PlainSelect.getStringList(expressionList.getExpressions(), true, useBracketsForValues));
498+
buffer.append(")");
499+
} else {
500+
SubSelect subSelect = withItem.getSubSelect();
501+
if (!subSelect.isUseBrackets()) {
502+
buffer.append("(");
503+
}
504+
subSelect.accept((FromItemVisitor) this);
505+
if (!subSelect.isUseBrackets()) {
506+
buffer.append(")");
507+
}
508+
}
490509
}
491510

492511
@Override

src/main/java/net/sf/jsqlparser/util/validation/validator/SelectValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public void visit(WithItem withItem) {
287287
if (isNotEmpty(withItem.getWithItemList())) {
288288
withItem.getWithItemList().forEach(wi -> wi.accept(this));
289289
}
290-
withItem.getSelectBody().accept(this);
290+
withItem.getSubSelect().accept(this);
291291
}
292292

293293
@Override

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,13 +1691,24 @@ WithItem WithItem() #WithItem:
16911691
WithItem with = new WithItem();
16921692
String name = null;
16931693
List<SelectItem> selectItems = null;
1694-
SelectBody selectBody = null;
1694+
SubSelect select = null;
1695+
1696+
ExpressionList simpleExpressionList;
16951697
}
16961698
{
16971699
[ <K_RECURSIVE> { with.setRecursive(true); } ] name=RelObjectName() { with.setName(name); }
16981700
[ "(" selectItems=SelectItemsList() ")" { with.setWithItemList(selectItems); } ]
16991701
<K_AS>
1700-
"(" selectBody = SelectBody() { with.setSelectBody(selectBody); } ")"
1702+
// if the next block looks alike an ExpressionList without Brackets, then parse as List
1703+
( LOOKAHEAD( "(" <K_VALUES> SimpleExpressionList(true) ")" )
1704+
"(" <K_VALUES>
1705+
simpleExpressionList = SimpleExpressionList(true) { with.withUseBracketsForValues(false).setItemsList(simpleExpressionList); }
1706+
")"
1707+
1708+
// Otherwise parse it as a SubSelect
1709+
| "(" select = SubSelect() { with.setSubSelect(select.withUseBrackets(false)); with.setUseValues(false); } ")"
1710+
1711+
)
17011712
{ return with; }
17021713
}
17031714

0 commit comments

Comments
 (0)