Skip to content

PostgreSQL INSERT ... ON CONFLICT Issue #1551 #1552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jul 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de805ab
Adjust Gradle to JUnit 5
manticore-projects Nov 22, 2021
d5a6dca
Do not mark SpeedTest for concurrent execution
manticore-projects Nov 24, 2021
a9d0503
Remove unused imports
manticore-projects Nov 28, 2021
6dfa05f
Adjust Gradle to JUnit 5
manticore-projects Nov 22, 2021
8f0bfe6
Do not mark SpeedTest for concurrent execution
manticore-projects Nov 24, 2021
5cd0974
Remove unused imports
manticore-projects Nov 28, 2021
5f11d3f
Merge remote-tracking branch 'origin/master'
manticore-projects Nov 28, 2021
3ab04b0
Merge https://github.com/JSQLParser/JSqlParser
manticore-projects Dec 21, 2021
de94651
Merge branch 'JSQLParser:master' into master
manticore-projects Jan 24, 2022
c76ae00
Adjust Gradle to JUnit 5
manticore-projects Nov 22, 2021
b4d111e
Do not mark SpeedTest for concurrent execution
manticore-projects Nov 24, 2021
0a01f32
Remove unused imports
manticore-projects Nov 28, 2021
f55ab13
Adjust Gradle to JUnit 5
manticore-projects Nov 22, 2021
884583d
Do not mark SpeedTest for concurrent execution
manticore-projects Nov 24, 2021
50848ff
Remove unused imports
manticore-projects Nov 28, 2021
608a6ef
Merge branch 'master' of github.com:JSQLParser/JSqlParser
manticore-projects May 15, 2022
bb3a9f1
Merge github.com:JSQLParser/JSqlParser
manticore-projects May 16, 2022
15e713b
Merge remote-tracking branch 'manticore/master'
manticore-projects May 24, 2022
92c8937
Support Postgres INSERT ... ON CONFLICT
manticore-projects Jun 3, 2022
44809d4
Allow KEEP keyword
manticore-projects Jun 3, 2022
80e3f07
Sanitize before push
manticore-projects Jun 3, 2022
50a8d26
Merge remote-tracking branch 'origin/master' into InsertOnConflict
manticore-projects Jun 3, 2022
5f7da94
Tweak Grammar in order to survive the Maven Build
manticore-projects Jun 3, 2022
35ccf0b
Move Plugin configuration files to the CONFIG folder (hoping, that Co…
manticore-projects Jun 4, 2022
203076f
Update PMD in the Maven and Gradle configuration
manticore-projects Jun 4, 2022
fadd055
Merge remote-tracking branch 'origin/master' into InsertOnConflict
manticore-projects Jul 18, 2022
07f1950
Appease Codacy
manticore-projects Jul 18, 2022
760045e
Merge branch 'master' into InsertOnConflict
wumpz Jul 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Additionally, we have fixed many errors and improved the code quality and the te
* support table option **character set** and **index** options
* support Postgresql optional **TABLE** in **TRUNCATE**
* support for `ANALYZE mytable`
* PostgreSQL `INSERT INTO ... ON CONFLICT ... DO ...` statements
* Implement Parser Timeout Feature, e. g. `CCJSqlParserUtil.parse(sqlStr, parser -> parser.withTimeOut(60000));`
* extended support Postgres' `Extract( field FROM source)` where `field` is a String instead of a Keyword

Expand Down
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ dependencies {
testImplementation 'org.mockito:mockito-junit-jupiter:4.+'

// enforce latest version of JavaCC
javacc 'net.java.dev.javacc:javacc:7.0.10'
javacc 'net.java.dev.javacc:javacc:7.0.11'
}

compileJavacc {
Expand Down Expand Up @@ -114,7 +114,7 @@ jacocoTestCoverageVerification {
limit {
counter = 'LINE'
value = 'MISSEDCOUNT'
maximum = 5513
maximum = 5700
}
excludes = [
'net.sf.jsqlparser.util.validation.*',
Expand Down Expand Up @@ -164,15 +164,15 @@ spotbugsMain {

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

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

pmd {
consoleOutput = false
toolVersion = "6.41.0"
toolVersion = "6.46.0"

sourceSets = [sourceSets.main]

Expand All @@ -181,7 +181,7 @@ pmd {

//rulesMinimumPriority = 1

ruleSetFiles = files("ruleset.xml")
ruleSetFiles = files("config/pmd/ruleset.xml")

pmdMain {
excludes = [
Expand Down
13 changes: 11 additions & 2 deletions ruleset.xml → config/pmd/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ under the License.
<rule ref="category/java/design.xml/SimplifiedTernary" />
<rule ref="category/java/design.xml/UselessOverridingMethod" />
<rule ref="category/java/design.xml/AvoidThrowingNullPointerException" />

<!-- for Codazy -->

<!--
<rule ref="category/java/design.xml/NPathComplexity">
<properties>
<property name="reportLevel" value="200" />
</properties>
</rule>
-->


<!-- for Codazy -->
<rule ref="category/java/design.xml/CyclomaticComplexity" />
<rule ref="category/java/design.xml/ExcessiveMethodLength" />
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault" />
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
<configuration>
<rulesets>
<!-- Custom local file system rule set -->
<ruleset>${project.basedir}/ruleset.xml</ruleset>
<ruleset>${project.basedir}/config/pmd/ruleset.xml</ruleset>
</rulesets>
<excludes>
<exclude>**/*Bean.java</exclude>
Expand Down Expand Up @@ -625,7 +625,7 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pmdVersion>6.36.0</pmdVersion>
<pmdVersion>6.46.0</pmdVersion>
</properties>

<description>JSqlParser parses an SQL statement and translate it into a hierarchy of Java classes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2022 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.insert;

public enum ConflictActionType {
DO_NOTHING, DO_UPDATE
}
38 changes: 37 additions & 1 deletion src/main/java/net/sf/jsqlparser/statement/insert/Insert.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public class Insert implements Statement {
private List<WithItem> withItemsList;

private OutputClause outputClause;
private InsertConflictTarget conflictTarget;
private InsertConflictAction conflictAction;

public OutputClause getOutputClause() {
return outputClause;
}

public void setOutputClause(OutputClause outputClause) {
this.outputClause = outputClause;
}
Expand Down Expand Up @@ -228,6 +229,32 @@ public void setWithItemsList(List<WithItem> withItemsList) {
this.withItemsList = withItemsList;
}

public InsertConflictTarget getConflictTarget() {
return conflictTarget;
}

public void setConflictTarget(InsertConflictTarget conflictTarget) {
this.conflictTarget = conflictTarget;
}

public Insert withConflictTarget(InsertConflictTarget conflictTarget) {
setConflictTarget(conflictTarget);
return this;
}

public InsertConflictAction getConflictAction() {
return conflictAction;
}

public void setConflictAction(InsertConflictAction conflictAction) {
this.conflictAction = conflictAction;
}

public Insert withConflictAction(InsertConflictAction conflictAction) {
setConflictAction(conflictAction);
return this;
}

@Override
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
public String toString() {
Expand Down Expand Up @@ -286,6 +313,15 @@ public String toString() {
}
}

if (conflictAction!=null) {
sql.append(" ON CONFLICT");

if (conflictTarget!=null) {
conflictTarget.appendTo(sql);
}
conflictAction.appendTo(sql);
}

if (getReturningExpressionList() != null) {
sql.append(" RETURNING ").append(PlainSelect.
getStringList(getReturningExpressionList(), true, false));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2022 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.insert;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.update.UpdateSet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

/**
* https://www.postgresql.org/docs/current/sql-insert.html
* <pre>
* conflict_action is one of:
*
* DO NOTHING
* DO UPDATE SET { column_name = { expression | DEFAULT } |
* ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
* ( column_name [, ...] ) = ( sub-SELECT )
* } [, ...]
* [ WHERE condition ]
* </pre>
*/

public class InsertConflictAction {
ConflictActionType conflictActionType;

private final ArrayList<UpdateSet> updateSets = new ArrayList<>();
Expression whereExpression;

public InsertConflictAction(ConflictActionType conflictActionType) {
this.conflictActionType = Objects.requireNonNull(conflictActionType, "The Conflict Action Type is mandatory and must not be Null.");
}

public ConflictActionType getConflictActionType() {
return conflictActionType;
}

public void setConflictActionType(ConflictActionType conflictActionType) {
this.conflictActionType = Objects.requireNonNull(conflictActionType, "The Conflict Action Type is mandatory and must not be Null.");
}

public InsertConflictAction withConflictActionType(ConflictActionType conflictActionType) {
setConflictActionType(conflictActionType);
return this;
}

public InsertConflictAction addUpdateSet(Column column, Expression expression) {
this.updateSets.add(new UpdateSet(column, expression));
return this;
}

public InsertConflictAction addUpdateSet(UpdateSet updateSet) {
this.updateSets.add(updateSet);
return this;
}

public InsertConflictAction withUpdateSets(Collection<UpdateSet> updateSets) {
this.updateSets.clear();
this.updateSets.addAll(updateSets);
return this;
}

public Expression getWhereExpression() {
return whereExpression;
}

public void setWhereExpression(Expression whereExpression) {
this.whereExpression = whereExpression;
}

public InsertConflictAction withWhereExpression(Expression whereExpression) {
setWhereExpression(whereExpression);
return this;
}

@SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault")
public StringBuilder appendTo(StringBuilder builder) {
switch (conflictActionType) {
case DO_NOTHING:
builder.append(" DO NOTHING");
break;
case DO_UPDATE:
builder.append(" DO UPDATE ");
UpdateSet.appendUpdateSetsTo(builder, updateSets);

if (whereExpression!=null) {
builder.append(" WHERE ").append(whereExpression);
}
break;
}
return builder;
}

@Override
public String toString() {
return appendTo(new StringBuilder()).toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2022 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.insert;

import net.sf.jsqlparser.expression.Expression;

/**
* https://www.postgresql.org/docs/current/sql-insert.html
* <pre>
* conflict_target can be one of:
*
* ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
* ON CONSTRAINT constraint_name
* </pre>
* Currently, COLLATE is not supported yet.
*/
public class InsertConflictTarget {

String indexColumnName;
Expression indexExpression;
Expression whereExpression;
String constraintName;

public InsertConflictTarget(String indexColumnName, Expression indexExpression, Expression whereExpression, String constraintName) {
this.indexColumnName = indexColumnName;
this.indexExpression = indexExpression;

this.whereExpression = whereExpression;
this.constraintName = constraintName;
}

public String getIndexColumnName() {
return indexColumnName;
}

public void setIndexColumnName(String indexColumnName) {
this.indexColumnName = indexColumnName;
this.indexExpression = null;
}

public InsertConflictTarget withIndexColumnName(String indexColumnName) {
setIndexColumnName(indexColumnName);
return this;
}

public Expression getIndexExpression() {
return indexExpression;
}

public void setIndexExpression(Expression indexExpression) {
this.indexExpression = indexExpression;
this.indexColumnName = null;
}

public InsertConflictTarget withIndexExpression(Expression indexExpression) {
setIndexExpression(indexExpression);
return this;
}

public Expression getWhereExpression() {
return whereExpression;
}

public void setWhereExpression(Expression whereExpression) {
this.whereExpression = whereExpression;
}

public InsertConflictTarget withWhereExpression(Expression whereExpression) {
setWhereExpression(whereExpression);
return this;
}

public String getConstraintName() {
return constraintName;
}

public void setConstraintName(String constraintName) {
this.constraintName = constraintName;
}

public InsertConflictTarget withConstraintName(String constraintName) {
setConstraintName(constraintName);
return this;
}

public StringBuilder appendTo(StringBuilder builder) {
if (constraintName==null) {
builder.append(" ( ");

//@todo: Index Expression is not supported yet
//if (indexColumnName != null) {
builder.append(indexColumnName);
//} else {
// builder.append(" ( ").append(indexExpression).append(" )");
//}
builder.append(" ");

//@todo: Collate is not supported yet

builder.append(") ");

if (whereExpression != null) {
builder.append(" WHERE ").append(whereExpression);
}
} else {
builder.append(" ON CONSTRAINT ").append(constraintName);
}
return builder;
}

public String toString() {
return appendTo(new StringBuilder()).toString();
}
}
Loading