Skip to content

Commit 5ae09ad

Browse files
PostgreSQL INSERT ... ON CONFLICT Issue #1551 (#1552)
* Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Adjust Gradle to JUnit 5 Parallel Test execution Gradle Caching Explicitly request for latest JavaCC 7.0.10 * Do not mark SpeedTest for concurrent execution * Remove unused imports * Support Postgres INSERT ... ON CONFLICT Fixes #1551 Refactor UpdateSet.toString(), which is used by Insert and Update * Allow KEEP keyword Enables special Oracle Test keywordasidentifier04.sql, now 191 tests succeed * Sanitize before push * Tweak Grammar in order to survive the Maven Build Ammend the README * Move Plugin configuration files to the CONFIG folder (hoping, that Codacy will find it there) Update PMD in the Maven configuration * Update PMD in the Maven and Gradle configuration * Appease Codacy Co-authored-by: Tobias <t.warneke@gmx.net>
1 parent 7400013 commit 5ae09ad

File tree

17 files changed

+587
-52
lines changed

17 files changed

+587
-52
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Additionally, we have fixed many errors and improved the code quality and the te
7373
* support table option **character set** and **index** options
7474
* support Postgresql optional **TABLE** in **TRUNCATE**
7575
* support for `ANALYZE mytable`
76+
* PostgreSQL `INSERT INTO ... ON CONFLICT ... DO ...` statements
7677
* Implement Parser Timeout Feature, e. g. `CCJSqlParserUtil.parse(sqlStr, parser -> parser.withTimeOut(60000));`
7778
* extended support Postgres' `Extract( field FROM source)` where `field` is a String instead of a Keyword
7879

build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ dependencies {
4545
testImplementation 'org.mockito:mockito-junit-jupiter:4.+'
4646

4747
// enforce latest version of JavaCC
48-
javacc 'net.java.dev.javacc:javacc:7.0.10'
48+
javacc 'net.java.dev.javacc:javacc:7.0.11'
4949
}
5050

5151
compileJavacc {
@@ -114,7 +114,7 @@ jacocoTestCoverageVerification {
114114
limit {
115115
counter = 'LINE'
116116
value = 'MISSEDCOUNT'
117-
maximum = 5513
117+
maximum = 5700
118118
}
119119
excludes = [
120120
'net.sf.jsqlparser.util.validation.*',
@@ -164,15 +164,15 @@ spotbugsMain {
164164

165165
spotbugs {
166166
// fail only on P1 and without the net.sf.jsqlparser.parser.*
167-
excludeFilter = file("spotBugsExcludeFilter.xml")
167+
excludeFilter = file("config/spotbugs/spotBugsExcludeFilter.xml")
168168

169169
// do not run over the test, although we should do that eventually
170170
spotbugsTest.enabled = false
171171
}
172172

173173
pmd {
174174
consoleOutput = false
175-
toolVersion = "6.41.0"
175+
toolVersion = "6.46.0"
176176

177177
sourceSets = [sourceSets.main]
178178

@@ -181,7 +181,7 @@ pmd {
181181

182182
//rulesMinimumPriority = 1
183183

184-
ruleSetFiles = files("ruleset.xml")
184+
ruleSetFiles = files("config/pmd/ruleset.xml")
185185

186186
pmdMain {
187187
excludes = [

ruleset.xml renamed to config/pmd/ruleset.xml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,17 @@ under the License.
6868
<rule ref="category/java/design.xml/SimplifiedTernary" />
6969
<rule ref="category/java/design.xml/UselessOverridingMethod" />
7070
<rule ref="category/java/design.xml/AvoidThrowingNullPointerException" />
71-
72-
<!-- for Codazy -->
71+
72+
<!--
73+
<rule ref="category/java/design.xml/NPathComplexity">
74+
<properties>
75+
<property name="reportLevel" value="200" />
76+
</properties>
77+
</rule>
78+
-->
79+
80+
81+
<!-- for Codazy -->
7382
<rule ref="category/java/design.xml/CyclomaticComplexity" />
7483
<rule ref="category/java/design.xml/ExcessiveMethodLength" />
7584
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault" />
File renamed without changes.

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@
114114
<configuration>
115115
<rulesets>
116116
<!-- Custom local file system rule set -->
117-
<ruleset>${project.basedir}/ruleset.xml</ruleset>
117+
<ruleset>${project.basedir}/config/pmd/ruleset.xml</ruleset>
118118
</rulesets>
119119
<excludes>
120120
<exclude>**/*Bean.java</exclude>
@@ -625,7 +625,7 @@
625625

626626
<properties>
627627
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
628-
<pmdVersion>6.36.0</pmdVersion>
628+
<pmdVersion>6.46.0</pmdVersion>
629629
</properties>
630630

631631
<description>JSqlParser parses an SQL statement and translate it into a hierarchy of Java classes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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.statement.insert;
11+
12+
public enum ConflictActionType {
13+
DO_NOTHING, DO_UPDATE
14+
}

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ public class Insert implements Statement {
5555
private List<WithItem> withItemsList;
5656

5757
private OutputClause outputClause;
58+
private InsertConflictTarget conflictTarget;
59+
private InsertConflictAction conflictAction;
5860

5961
public OutputClause getOutputClause() {
6062
return outputClause;
6163
}
62-
6364
public void setOutputClause(OutputClause outputClause) {
6465
this.outputClause = outputClause;
6566
}
@@ -228,6 +229,32 @@ public void setWithItemsList(List<WithItem> withItemsList) {
228229
this.withItemsList = withItemsList;
229230
}
230231

232+
public InsertConflictTarget getConflictTarget() {
233+
return conflictTarget;
234+
}
235+
236+
public void setConflictTarget(InsertConflictTarget conflictTarget) {
237+
this.conflictTarget = conflictTarget;
238+
}
239+
240+
public Insert withConflictTarget(InsertConflictTarget conflictTarget) {
241+
setConflictTarget(conflictTarget);
242+
return this;
243+
}
244+
245+
public InsertConflictAction getConflictAction() {
246+
return conflictAction;
247+
}
248+
249+
public void setConflictAction(InsertConflictAction conflictAction) {
250+
this.conflictAction = conflictAction;
251+
}
252+
253+
public Insert withConflictAction(InsertConflictAction conflictAction) {
254+
setConflictAction(conflictAction);
255+
return this;
256+
}
257+
231258
@Override
232259
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
233260
public String toString() {
@@ -286,6 +313,15 @@ public String toString() {
286313
}
287314
}
288315

316+
if (conflictAction!=null) {
317+
sql.append(" ON CONFLICT");
318+
319+
if (conflictTarget!=null) {
320+
conflictTarget.appendTo(sql);
321+
}
322+
conflictAction.appendTo(sql);
323+
}
324+
289325
if (getReturningExpressionList() != null) {
290326
sql.append(" RETURNING ").append(PlainSelect.
291327
getStringList(getReturningExpressionList(), true, false));
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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.statement.insert;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
import net.sf.jsqlparser.schema.Column;
14+
import net.sf.jsqlparser.statement.update.UpdateSet;
15+
16+
import java.util.ArrayList;
17+
import java.util.Collection;
18+
import java.util.Objects;
19+
20+
/**
21+
* https://www.postgresql.org/docs/current/sql-insert.html
22+
* <pre>
23+
* conflict_action is one of:
24+
*
25+
* DO NOTHING
26+
* DO UPDATE SET { column_name = { expression | DEFAULT } |
27+
* ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
28+
* ( column_name [, ...] ) = ( sub-SELECT )
29+
* } [, ...]
30+
* [ WHERE condition ]
31+
* </pre>
32+
*/
33+
34+
public class InsertConflictAction {
35+
ConflictActionType conflictActionType;
36+
37+
private final ArrayList<UpdateSet> updateSets = new ArrayList<>();
38+
Expression whereExpression;
39+
40+
public InsertConflictAction(ConflictActionType conflictActionType) {
41+
this.conflictActionType = Objects.requireNonNull(conflictActionType, "The Conflict Action Type is mandatory and must not be Null.");
42+
}
43+
44+
public ConflictActionType getConflictActionType() {
45+
return conflictActionType;
46+
}
47+
48+
public void setConflictActionType(ConflictActionType conflictActionType) {
49+
this.conflictActionType = Objects.requireNonNull(conflictActionType, "The Conflict Action Type is mandatory and must not be Null.");
50+
}
51+
52+
public InsertConflictAction withConflictActionType(ConflictActionType conflictActionType) {
53+
setConflictActionType(conflictActionType);
54+
return this;
55+
}
56+
57+
public InsertConflictAction addUpdateSet(Column column, Expression expression) {
58+
this.updateSets.add(new UpdateSet(column, expression));
59+
return this;
60+
}
61+
62+
public InsertConflictAction addUpdateSet(UpdateSet updateSet) {
63+
this.updateSets.add(updateSet);
64+
return this;
65+
}
66+
67+
public InsertConflictAction withUpdateSets(Collection<UpdateSet> updateSets) {
68+
this.updateSets.clear();
69+
this.updateSets.addAll(updateSets);
70+
return this;
71+
}
72+
73+
public Expression getWhereExpression() {
74+
return whereExpression;
75+
}
76+
77+
public void setWhereExpression(Expression whereExpression) {
78+
this.whereExpression = whereExpression;
79+
}
80+
81+
public InsertConflictAction withWhereExpression(Expression whereExpression) {
82+
setWhereExpression(whereExpression);
83+
return this;
84+
}
85+
86+
@SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault")
87+
public StringBuilder appendTo(StringBuilder builder) {
88+
switch (conflictActionType) {
89+
case DO_NOTHING:
90+
builder.append(" DO NOTHING");
91+
break;
92+
case DO_UPDATE:
93+
builder.append(" DO UPDATE ");
94+
UpdateSet.appendUpdateSetsTo(builder, updateSets);
95+
96+
if (whereExpression!=null) {
97+
builder.append(" WHERE ").append(whereExpression);
98+
}
99+
break;
100+
}
101+
return builder;
102+
}
103+
104+
@Override
105+
public String toString() {
106+
return appendTo(new StringBuilder()).toString();
107+
}
108+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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.statement.insert;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
14+
/**
15+
* https://www.postgresql.org/docs/current/sql-insert.html
16+
* <pre>
17+
* conflict_target can be one of:
18+
*
19+
* ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
20+
* ON CONSTRAINT constraint_name
21+
* </pre>
22+
* Currently, COLLATE is not supported yet.
23+
*/
24+
public class InsertConflictTarget {
25+
26+
String indexColumnName;
27+
Expression indexExpression;
28+
Expression whereExpression;
29+
String constraintName;
30+
31+
public InsertConflictTarget(String indexColumnName, Expression indexExpression, Expression whereExpression, String constraintName) {
32+
this.indexColumnName = indexColumnName;
33+
this.indexExpression = indexExpression;
34+
35+
this.whereExpression = whereExpression;
36+
this.constraintName = constraintName;
37+
}
38+
39+
public String getIndexColumnName() {
40+
return indexColumnName;
41+
}
42+
43+
public void setIndexColumnName(String indexColumnName) {
44+
this.indexColumnName = indexColumnName;
45+
this.indexExpression = null;
46+
}
47+
48+
public InsertConflictTarget withIndexColumnName(String indexColumnName) {
49+
setIndexColumnName(indexColumnName);
50+
return this;
51+
}
52+
53+
public Expression getIndexExpression() {
54+
return indexExpression;
55+
}
56+
57+
public void setIndexExpression(Expression indexExpression) {
58+
this.indexExpression = indexExpression;
59+
this.indexColumnName = null;
60+
}
61+
62+
public InsertConflictTarget withIndexExpression(Expression indexExpression) {
63+
setIndexExpression(indexExpression);
64+
return this;
65+
}
66+
67+
public Expression getWhereExpression() {
68+
return whereExpression;
69+
}
70+
71+
public void setWhereExpression(Expression whereExpression) {
72+
this.whereExpression = whereExpression;
73+
}
74+
75+
public InsertConflictTarget withWhereExpression(Expression whereExpression) {
76+
setWhereExpression(whereExpression);
77+
return this;
78+
}
79+
80+
public String getConstraintName() {
81+
return constraintName;
82+
}
83+
84+
public void setConstraintName(String constraintName) {
85+
this.constraintName = constraintName;
86+
}
87+
88+
public InsertConflictTarget withConstraintName(String constraintName) {
89+
setConstraintName(constraintName);
90+
return this;
91+
}
92+
93+
public StringBuilder appendTo(StringBuilder builder) {
94+
if (constraintName==null) {
95+
builder.append(" ( ");
96+
97+
//@todo: Index Expression is not supported yet
98+
//if (indexColumnName != null) {
99+
builder.append(indexColumnName);
100+
//} else {
101+
// builder.append(" ( ").append(indexExpression).append(" )");
102+
//}
103+
builder.append(" ");
104+
105+
//@todo: Collate is not supported yet
106+
107+
builder.append(") ");
108+
109+
if (whereExpression != null) {
110+
builder.append(" WHERE ").append(whereExpression);
111+
}
112+
} else {
113+
builder.append(" ON CONSTRAINT ").append(constraintName);
114+
}
115+
return builder;
116+
}
117+
118+
public String toString() {
119+
return appendTo(new StringBuilder()).toString();
120+
}
121+
}

0 commit comments

Comments
 (0)