Skip to content

Commit 657443c

Browse files
committed
Support PostgreSQL conflict index elements, OVERRIDING USER and MERGE RETURNING
1 parent b2115ac commit 657443c

15 files changed

Lines changed: 498 additions & 89 deletions

File tree

src/main/java/net/sf/jsqlparser/statement/ReturningClause.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Locale;
1616
import java.util.Map;
1717
import java.util.Objects;
18+
import java.util.function.Consumer;
1819
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
1920
import net.sf.jsqlparser.schema.Column;
2021
import net.sf.jsqlparser.schema.MultiPartName;
@@ -86,6 +87,10 @@ public List<ReturningOutputAlias> getOutputAliases() {
8687
}
8788

8889
public StringBuilder appendTo(StringBuilder builder) {
90+
return appendTo(builder, item -> builder.append(item));
91+
}
92+
93+
public StringBuilder appendTo(StringBuilder builder, Consumer<SelectItem<?>> itemPrinter) {
8994
builder.append(" ").append(keyword).append(" ");
9095
if (outputAliases != null && !outputAliases.isEmpty()) {
9196
builder.append("WITH (");
@@ -101,7 +106,7 @@ public StringBuilder appendTo(StringBuilder builder) {
101106
if (i > 0) {
102107
builder.append(", ");
103108
}
104-
builder.append(get(i));
109+
itemPrinter.accept(get(i));
105110
}
106111

107112
if (dataItems != null && !dataItems.isEmpty()) {

src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ public <S> T visit(Insert insert, S context) {
296296
expressionVisitor.visitUpdateSets(conflictAction.getUpdateSets(), context);
297297
}
298298

299+
if (insert.getConflictTarget() != null) {
300+
insert.getConflictTarget().accept(expressionVisitor, context);
301+
}
299302
visitReturningClause(insert.getReturningClause(), context);
300303
return null;
301304
}
@@ -457,6 +460,7 @@ public <S> T visit(Merge merge, S context) {
457460
expressionVisitor.visitExpression(merge.getOnCondition(), context);
458461
mergeOperationVisitor.visit(merge.getOperations(), context);
459462
selectVisitor.visitOutputClause(merge.getOutputClause(), context);
463+
visitReturningClause(merge.getReturningClause(), context);
460464
return null;
461465
}
462466

src/main/java/net/sf/jsqlparser/statement/create/table/Index.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ public enum NullOrdering {
331331
public final String columnName;
332332
public final List<String> params;
333333
private final Expression expression;
334+
private boolean expressionParenthesized = true;
334335
private String collation;
335336
private String operatorClass;
336337
private List<Option> operatorClassParameters;
@@ -386,6 +387,19 @@ public boolean isExpression() {
386387
return expression != null;
387388
}
388389

390+
public boolean isExpressionParenthesized() {
391+
return expressionParenthesized;
392+
}
393+
394+
public void setExpressionParenthesized(boolean expressionParenthesized) {
395+
this.expressionParenthesized = expressionParenthesized;
396+
}
397+
398+
public ColumnParams withExpressionParenthesized(boolean expressionParenthesized) {
399+
setExpressionParenthesized(expressionParenthesized);
400+
return this;
401+
}
402+
389403
public String getCollation() {
390404
return collation;
391405
}
@@ -461,9 +475,13 @@ public String toString() {
461475
/** Renders expression keys through the caller's expression printer. */
462476
public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
463477
if (expression != null) {
464-
builder.append('(');
478+
if (expressionParenthesized) {
479+
builder.append('(');
480+
}
465481
expressionPrinter.accept(expression);
466-
builder.append(')');
482+
if (expressionParenthesized) {
483+
builder.append(')');
484+
}
467485
} else {
468486
builder.append(columnName);
469487
}

src/main/java/net/sf/jsqlparser/statement/insert/Insert.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ public class Insert implements Statement {
4040
private List<Partition> partitions;
4141
private Select select;
4242
private boolean onlyDefaultValues = false;
43-
private boolean overriding = false;
43+
44+
public enum OverridingMode {
45+
NONE, SYSTEM, USER
46+
}
47+
48+
private OverridingMode overridingMode = OverridingMode.NONE;
4449
private List<UpdateSet> duplicateUpdateSets = null;
4550
private InsertModifierPriority modifierPriority = null;
4651
private boolean modifierIgnore = false;
@@ -226,12 +231,25 @@ public void setWithItemsList(List<WithItem<?>> withItemsList) {
226231
this.withItemsList = withItemsList;
227232
}
228233

234+
public OverridingMode getOverridingMode() {
235+
return overridingMode;
236+
}
237+
238+
public void setOverridingMode(OverridingMode overridingMode) {
239+
this.overridingMode = java.util.Objects.requireNonNull(overridingMode);
240+
}
241+
242+
public Insert withOverridingMode(OverridingMode overridingMode) {
243+
setOverridingMode(overridingMode);
244+
return this;
245+
}
246+
229247
public boolean isOverriding() {
230-
return overriding;
248+
return overridingMode != OverridingMode.NONE;
231249
}
232250

233251
public void setOverriding(boolean overriding) {
234-
this.overriding = overriding;
252+
this.overridingMode = overriding ? OverridingMode.SYSTEM : OverridingMode.NONE;
235253
}
236254

237255
public Insert withOverriding(boolean overriding) {
@@ -388,8 +406,8 @@ private void appendInsertTargetAndValues(StringBuilder sql) {
388406
sql.append(") ");
389407
}
390408

391-
if (overriding) {
392-
sql.append("OVERRIDING SYSTEM VALUE ");
409+
if (isOverriding()) {
410+
sql.append("OVERRIDING ").append(overridingMode).append(" VALUE ");
393411
}
394412

395413
if (partitions != null) {

src/main/java/net/sf/jsqlparser/statement/insert/InsertConflictTarget.java

Lines changed: 119 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
package net.sf.jsqlparser.statement.insert;
1111

1212
import net.sf.jsqlparser.expression.Expression;
13+
import net.sf.jsqlparser.expression.ExpressionVisitor;
14+
import net.sf.jsqlparser.statement.create.table.Index;
15+
import java.util.function.Consumer;
1316

1417
import java.io.Serializable;
1518
import java.util.*;
@@ -23,75 +26,130 @@
2326
* ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
2427
* ON CONSTRAINT constraint_name
2528
* </pre>
26-
* <p>
27-
* Currently, COLLATE is not supported yet.
2829
*/
2930
public class InsertConflictTarget implements Serializable {
31+
private final List<Index.ColumnParams> indexElements = new ArrayList<>();
32+
private Expression whereExpression;
33+
private String constraintName;
3034

31-
ArrayList<String> indexColumnNames = new ArrayList<>();
32-
Expression indexExpression;
33-
Expression whereExpression;
34-
String constraintName;
35+
public InsertConflictTarget() {}
3536

3637
public InsertConflictTarget(String indexColumnName, Expression indexExpression,
3738
Expression whereExpression, String constraintName) {
38-
this.indexColumnNames.add(indexColumnName);
39-
this.indexExpression = indexExpression;
39+
this(indexColumnName == null ? Collections.emptyList()
40+
: Collections.singletonList(indexColumnName),
41+
indexExpression, whereExpression, constraintName);
42+
}
4043

44+
public InsertConflictTarget(Collection<String> indexColumnNames, Expression indexExpression,
45+
Expression whereExpression, String constraintName) {
46+
if (indexColumnNames != null && !indexColumnNames.isEmpty()) {
47+
addAllIndexColumnNames(indexColumnNames);
48+
} else if (indexExpression != null) {
49+
setIndexExpression(indexExpression);
50+
}
4151
this.whereExpression = whereExpression;
4252
this.constraintName = constraintName;
4353
}
4454

45-
public InsertConflictTarget(Collection<String> indexColumnName, Expression indexExpression,
46-
Expression whereExpression, String constraintName) {
47-
this.indexColumnNames.addAll(indexColumnName);
48-
this.indexExpression = indexExpression;
55+
/** Ordered column and expression keys, including their collation and operator class. */
56+
public List<Index.ColumnParams> getIndexElements() {
57+
return indexElements;
58+
}
4959

50-
this.whereExpression = whereExpression;
51-
this.constraintName = constraintName;
60+
public void setIndexElements(List<Index.ColumnParams> elements) {
61+
List<Index.ColumnParams> copy = new ArrayList<>(elements);
62+
indexElements.clear();
63+
indexElements.addAll(copy);
5264
}
5365

66+
public InsertConflictTarget withIndexElements(List<Index.ColumnParams> elements) {
67+
setIndexElements(elements);
68+
return this;
69+
}
70+
71+
/** A mutable view of the column keys; expression keys are available via getIndexElements(). */
5472
public List<String> getIndexColumnNames() {
55-
return indexColumnNames;
73+
return new AbstractList<String>() {
74+
private int elementIndex(int index) {
75+
int columnIndex = 0;
76+
for (int i = 0; i < indexElements.size(); i++) {
77+
if (!indexElements.get(i).isExpression() && columnIndex++ == index) {
78+
return i;
79+
}
80+
}
81+
throw new IndexOutOfBoundsException("Column index: " + index);
82+
}
83+
84+
@Override
85+
public String get(int index) {
86+
return indexElements.get(elementIndex(index)).getColumnName();
87+
}
88+
89+
@Override
90+
public int size() {
91+
return (int) indexElements.stream().filter(key -> !key.isExpression()).count();
92+
}
93+
94+
@Override
95+
public String set(int index, String name) {
96+
return indexElements.set(elementIndex(index), new Index.ColumnParams(name))
97+
.getColumnName();
98+
}
99+
100+
@Override
101+
public void add(int index, String name) {
102+
indexElements.add(index == size() ? indexElements.size() : elementIndex(index),
103+
new Index.ColumnParams(name));
104+
}
105+
106+
@Override
107+
public String remove(int index) {
108+
return indexElements.remove(elementIndex(index)).getColumnName();
109+
}
110+
};
56111
}
57112

58113
@Deprecated
59114
public String getIndexColumnName() {
60-
return indexColumnNames.isEmpty() ? null : indexColumnNames.get(0);
115+
return getIndexColumnName(0);
61116
}
62117

63118
public String getIndexColumnName(int index) {
64-
return indexColumnNames.size() > index ? indexColumnNames.get(index) : null;
119+
List<String> names = getIndexColumnNames();
120+
return names.size() > index ? names.get(index) : null;
65121
}
66122

67-
public boolean addIndexColumnName(String indexColumnName) {
68-
this.indexExpression = null;
69-
return this.indexColumnNames.add(indexColumnName);
123+
public boolean addIndexColumnName(String name) {
124+
indexElements.removeIf(Index.ColumnParams::isExpression);
125+
return indexElements.add(new Index.ColumnParams(name));
70126
}
71127

72-
public InsertConflictTarget withIndexColumnName(String indexColumnName) {
73-
this.indexExpression = null;
74-
this.indexColumnNames.add(indexColumnName);
128+
public InsertConflictTarget withIndexColumnName(String name) {
129+
addIndexColumnName(name);
75130
return this;
76131
}
77132

78-
public boolean addAllIndexColumnNames(Collection<String> indexColumnName) {
79-
this.indexExpression = null;
80-
return this.indexColumnNames.addAll(indexColumnName);
133+
public boolean addAllIndexColumnNames(Collection<String> names) {
134+
indexElements.removeIf(Index.ColumnParams::isExpression);
135+
return getIndexColumnNames().addAll(names);
81136
}
82137

83-
138+
/** Returns the first expression key, or null for a column-only target. */
84139
public Expression getIndexExpression() {
85-
return indexExpression;
140+
return indexElements.stream().filter(Index.ColumnParams::isExpression)
141+
.map(Index.ColumnParams::getExpression).findFirst().orElse(null);
86142
}
87143

88-
public void setIndexExpression(Expression indexExpression) {
89-
this.indexExpression = indexExpression;
90-
this.indexColumnNames.clear();
144+
public void setIndexExpression(Expression expression) {
145+
indexElements.clear();
146+
if (expression != null) {
147+
indexElements.add(new Index.ColumnParams(expression));
148+
}
91149
}
92150

93-
public InsertConflictTarget withIndexExpression(Expression indexExpression) {
94-
setIndexExpression(indexExpression);
151+
public InsertConflictTarget withIndexExpression(Expression expression) {
152+
setIndexExpression(expression);
95153
return this;
96154
}
97155

@@ -121,35 +179,42 @@ public InsertConflictTarget withConstraintName(String constraintName) {
121179
return this;
122180
}
123181

124-
public StringBuilder appendTo(StringBuilder builder) {
125-
if (constraintName == null) {
126-
builder.append(" ( ");
127-
128-
// @todo: Index Expression is not supported yet
129-
if (!indexColumnNames.isEmpty()) {
130-
boolean insertComma = false;
131-
for (String s : indexColumnNames) {
132-
builder.append(insertComma ? ", " : " ").append(s);
133-
insertComma |= true;
134-
}
135-
} else {
136-
builder.append(" ( ").append(indexExpression).append(" )");
182+
/** Visits expression keys and the optional index predicate. */
183+
public <S> void accept(ExpressionVisitor<?> visitor, S context) {
184+
for (Index.ColumnParams element : indexElements) {
185+
if (element.getExpression() != null) {
186+
element.getExpression().accept(visitor, context);
137187
}
138-
builder.append(" ");
139-
140-
// @todo: Collate is not supported yet
188+
}
189+
if (whereExpression != null) {
190+
whereExpression.accept(visitor, context);
191+
}
192+
}
141193

142-
builder.append(") ");
194+
public StringBuilder appendTo(StringBuilder builder) {
195+
return appendTo(builder, expression -> builder.append(expression));
196+
}
143197

144-
if (whereExpression != null) {
145-
builder.append(" WHERE ").append(whereExpression);
198+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
199+
if (constraintName != null) {
200+
return builder.append(" ON CONSTRAINT ").append(constraintName);
201+
}
202+
builder.append(" (");
203+
for (int i = 0; i < indexElements.size(); i++) {
204+
if (i > 0) {
205+
builder.append(", ");
146206
}
147-
} else {
148-
builder.append(" ON CONSTRAINT ").append(constraintName);
207+
indexElements.get(i).appendTo(builder, expressionPrinter);
208+
}
209+
builder.append(")");
210+
if (whereExpression != null) {
211+
builder.append(" WHERE ");
212+
expressionPrinter.accept(whereExpression);
149213
}
150214
return builder;
151215
}
152216

217+
@Override
153218
public String toString() {
154219
return appendTo(new StringBuilder()).toString();
155220
}

0 commit comments

Comments
 (0)