Skip to content

Commit f1c525a

Browse files
authored
feat: support any number/order of merge operations (#1938)
* feat: support any number/order of merge operations * fix: readd standard fields and add compatibility logic
1 parent 34561d8 commit f1c525a

File tree

12 files changed

+440
-146
lines changed

12 files changed

+440
-146
lines changed

src/main/java/net/sf/jsqlparser/statement/merge/Merge.java

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Iterator;
2727
import java.util.List;
2828
import java.util.Optional;
29+
import java.util.stream.Collectors;
2930

3031
public class Merge implements Statement {
3132

@@ -37,9 +38,43 @@ public class Merge implements Statement {
3738
private MergeInsert mergeInsert;
3839
private MergeUpdate mergeUpdate;
3940
private boolean insertFirst = false;
41+
private List<MergeOperation> operations;
4042

4143
private OutputClause outputClause;
4244

45+
private void deriveOperationsFromStandardClauses() {
46+
List<MergeOperation> operations = new ArrayList<>();
47+
if (insertFirst) {
48+
Optional.ofNullable(mergeInsert).ifPresent(operations::add);
49+
Optional.ofNullable(mergeUpdate).ifPresent(operations::add);
50+
} else {
51+
Optional.ofNullable(mergeUpdate).ifPresent(operations::add);
52+
Optional.ofNullable(mergeInsert).ifPresent(operations::add);
53+
}
54+
this.operations = operations;
55+
}
56+
57+
private void deriveStandardClausesFromOperations() {
58+
List<MergeOperation> applicableOperations =
59+
Optional.ofNullable(operations).orElse(Collections.emptyList()).stream()
60+
.filter(o -> o instanceof MergeUpdate || o instanceof MergeInsert)
61+
.collect(Collectors.toList());
62+
mergeUpdate = applicableOperations.stream()
63+
.filter(o -> o instanceof MergeUpdate)
64+
.map(MergeUpdate.class::cast)
65+
.findFirst()
66+
.orElse(null);
67+
mergeInsert = applicableOperations.stream()
68+
.filter(o -> o instanceof MergeInsert)
69+
.map(MergeInsert.class::cast)
70+
.findFirst()
71+
.orElse(null);
72+
insertFirst = applicableOperations.stream()
73+
.findFirst()
74+
.map(o -> o instanceof MergeInsert)
75+
.orElse(false);
76+
}
77+
4378
public List<WithItem> getWithItemsList() {
4479
return withItemsList;
4580
}
@@ -129,12 +164,22 @@ public void setOnCondition(Expression onCondition) {
129164
this.onCondition = onCondition;
130165
}
131166

167+
public List<MergeOperation> getOperations() {
168+
return operations;
169+
}
170+
171+
public void setOperations(List<MergeOperation> operations) {
172+
this.operations = operations;
173+
deriveStandardClausesFromOperations();
174+
}
175+
132176
public MergeInsert getMergeInsert() {
133177
return mergeInsert;
134178
}
135179

136-
public void setMergeInsert(MergeInsert insert) {
137-
this.mergeInsert = insert;
180+
public void setMergeInsert(MergeInsert mergeInsert) {
181+
this.mergeInsert = mergeInsert;
182+
deriveOperationsFromStandardClauses();
138183
}
139184

140185
public MergeUpdate getMergeUpdate() {
@@ -143,6 +188,7 @@ public MergeUpdate getMergeUpdate() {
143188

144189
public void setMergeUpdate(MergeUpdate mergeUpdate) {
145190
this.mergeUpdate = mergeUpdate;
191+
deriveOperationsFromStandardClauses();
146192
}
147193

148194
@Override
@@ -156,6 +202,7 @@ public boolean isInsertFirst() {
156202

157203
public void setInsertFirst(boolean insertFirst) {
158204
this.insertFirst = insertFirst;
205+
deriveOperationsFromStandardClauses();
159206
}
160207

161208
public OutputClause getOutputClause() {
@@ -193,16 +240,8 @@ public String toString() {
193240
b.append(" ON ");
194241
b.append(onCondition);
195242

196-
if (insertFirst && mergeInsert != null) {
197-
b.append(mergeInsert);
198-
}
199-
200-
if (mergeUpdate != null) {
201-
b.append(mergeUpdate);
202-
}
203-
204-
if (!insertFirst && mergeInsert != null) {
205-
b.append(mergeInsert);
243+
if (operations != null && !operations.isEmpty()) {
244+
operations.forEach(b::append);
206245
}
207246

208247
if (outputClause != null) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.merge;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
14+
import java.io.Serializable;
15+
16+
public class MergeDelete implements Serializable, MergeOperation {
17+
private Expression andPredicate;
18+
19+
public Expression getAndPredicate() {
20+
return andPredicate;
21+
}
22+
23+
public void setAndPredicate(Expression andPredicate) {
24+
this.andPredicate = andPredicate;
25+
}
26+
27+
public MergeDelete withAndPredicate(Expression andPredicate) {
28+
this.setAndPredicate(andPredicate);
29+
return this;
30+
}
31+
32+
@Override
33+
public void accept(MergeOperationVisitor mergeOperationVisitor) {
34+
mergeOperationVisitor.visit(this);
35+
}
36+
37+
@Override
38+
public String toString() {
39+
StringBuilder b = new StringBuilder();
40+
b.append(" WHEN MATCHED");
41+
if (andPredicate != null) {
42+
b.append(" AND ").append(andPredicate.toString());
43+
}
44+
b.append(" THEN DELETE");
45+
return b.toString();
46+
}
47+
}

src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import java.util.Collection;
1919
import java.util.Optional;
2020

21-
public class MergeInsert implements Serializable {
21+
public class MergeInsert implements Serializable, MergeOperation {
2222

2323
private Expression andPredicate;
2424
private ExpressionList<Column> columns;
@@ -57,6 +57,11 @@ public void setWhereCondition(Expression whereCondition) {
5757
this.whereCondition = whereCondition;
5858
}
5959

60+
@Override
61+
public void accept(MergeOperationVisitor mergeOperationVisitor) {
62+
mergeOperationVisitor.visit(this);
63+
}
64+
6065
@Override
6166
public String toString() {
6267
StringBuilder b = new StringBuilder();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.merge;
11+
12+
/**
13+
* Marker interface to cover {@link MergeDelete}, {@link MergeUpdate} and {@link MergeInsert}
14+
*/
15+
public interface MergeOperation {
16+
void accept(MergeOperationVisitor mergeOperationVisitor);
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.merge;
11+
12+
public interface MergeOperationVisitor {
13+
14+
void visit(MergeDelete mergeDelete);
15+
16+
void visit(MergeUpdate mergeUpdate);
17+
18+
void visit(MergeInsert mergeInsert);
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.merge;
11+
12+
@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
13+
public class MergeOperationVisitorAdapter implements MergeOperationVisitor {
14+
@Override
15+
public void visit(MergeDelete mergeDelete) {
16+
17+
}
18+
19+
@Override
20+
public void visit(MergeUpdate mergeUpdate) {
21+
22+
}
23+
24+
@Override
25+
public void visit(MergeInsert mergeInsert) {
26+
27+
}
28+
}

src/main/java/net/sf/jsqlparser/statement/merge/MergeUpdate.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import java.io.Serializable;
1616
import java.util.List;
1717

18-
public class MergeUpdate implements Serializable {
18+
public class MergeUpdate implements Serializable, MergeOperation {
1919

2020
private List<UpdateSet> updateSets;
2121
private Expression andPredicate;
@@ -61,6 +61,11 @@ public void setDeleteWhereCondition(Expression deleteWhereCondition) {
6161
this.deleteWhereCondition = deleteWhereCondition;
6262
}
6363

64+
@Override
65+
public void accept(MergeOperationVisitor mergeOperationVisitor) {
66+
mergeOperationVisitor.visit(this);
67+
}
68+
6469
@Override
6570
public String toString() {
6671
StringBuilder b = new StringBuilder();
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.util.deparser;
11+
12+
import net.sf.jsqlparser.statement.merge.*;
13+
import net.sf.jsqlparser.statement.select.WithItem;
14+
15+
import java.util.Iterator;
16+
import java.util.List;
17+
18+
public class MergeDeParser extends AbstractDeParser<Merge> implements MergeOperationVisitor {
19+
private final ExpressionDeParser expressionDeParser;
20+
21+
private final SelectDeParser selectDeParser;
22+
23+
public MergeDeParser(ExpressionDeParser expressionDeParser, SelectDeParser selectDeParser,
24+
StringBuilder buffer) {
25+
super(buffer);
26+
this.expressionDeParser = expressionDeParser;
27+
this.selectDeParser = selectDeParser;
28+
}
29+
30+
@Override
31+
void deParse(Merge merge) {
32+
List<WithItem> withItemsList = merge.getWithItemsList();
33+
if (withItemsList != null && !withItemsList.isEmpty()) {
34+
buffer.append("WITH ");
35+
for (Iterator<WithItem> iter = withItemsList.iterator(); iter.hasNext();) {
36+
iter.next().accept(expressionDeParser);
37+
if (iter.hasNext()) {
38+
buffer.append(",");
39+
}
40+
buffer.append(" ");
41+
}
42+
}
43+
44+
buffer.append("MERGE ");
45+
if (merge.getOracleHint() != null) {
46+
buffer.append(merge.getOracleHint()).append(" ");
47+
}
48+
buffer.append("INTO ");
49+
merge.getTable().accept(selectDeParser);
50+
51+
buffer.append(" USING ");
52+
merge.getFromItem().accept(selectDeParser);
53+
54+
buffer.append(" ON ");
55+
merge.getOnCondition().accept(expressionDeParser);
56+
57+
List<MergeOperation> operations = merge.getOperations();
58+
if (operations != null && !operations.isEmpty()) {
59+
operations.forEach(operation -> operation.accept(this));
60+
}
61+
62+
if (merge.getOutputClause() != null) {
63+
merge.getOutputClause().appendTo(buffer);
64+
}
65+
}
66+
67+
@Override
68+
public void visit(MergeDelete mergeDelete) {
69+
buffer.append(" WHEN MATCHED");
70+
if (mergeDelete.getAndPredicate() != null) {
71+
buffer.append(" AND ");
72+
mergeDelete.getAndPredicate().accept(expressionDeParser);
73+
}
74+
buffer.append(" THEN DELETE");
75+
}
76+
77+
@Override
78+
public void visit(MergeUpdate mergeUpdate) {
79+
buffer.append(" WHEN MATCHED");
80+
if (mergeUpdate.getAndPredicate() != null) {
81+
buffer.append(" AND ");
82+
mergeUpdate.getAndPredicate().accept(expressionDeParser);
83+
}
84+
buffer.append(" THEN UPDATE SET ");
85+
deparseUpdateSets(mergeUpdate.getUpdateSets(), buffer, expressionDeParser);
86+
87+
if (mergeUpdate.getWhereCondition() != null) {
88+
buffer.append(" WHERE ");
89+
mergeUpdate.getWhereCondition().accept(expressionDeParser);
90+
}
91+
92+
if (mergeUpdate.getDeleteWhereCondition() != null) {
93+
buffer.append(" DELETE WHERE ");
94+
mergeUpdate.getDeleteWhereCondition().accept(expressionDeParser);
95+
}
96+
}
97+
98+
@Override
99+
public void visit(MergeInsert mergeInsert) {
100+
buffer.append(" WHEN NOT MATCHED");
101+
if (mergeInsert.getAndPredicate() != null) {
102+
buffer.append(" AND ");
103+
mergeInsert.getAndPredicate().accept(expressionDeParser);
104+
}
105+
buffer.append(" THEN INSERT ");
106+
if (mergeInsert.getColumns() != null) {
107+
mergeInsert.getColumns().accept(expressionDeParser);
108+
}
109+
buffer.append(" VALUES ");
110+
mergeInsert.getValues().accept(expressionDeParser);
111+
112+
if (mergeInsert.getWhereCondition() != null) {
113+
buffer.append(" WHERE ");
114+
mergeInsert.getWhereCondition().accept(expressionDeParser);
115+
}
116+
}
117+
118+
}

0 commit comments

Comments
 (0)