From 701ab65dfc6c5a794de0c040b2619e9eec10c514 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 15 May 2018 13:25:16 +0200 Subject: [PATCH] [#7491] Emulate ON CONFLICT .. DO UPDATE .. WHERE

on SQL Server using WHEN MATCHED AND

--- .../org/jooq/InsertOnConflictWhereStep.java | 1 + .../java/org/jooq/MergeMatchedWhereStep.java | 49 +++++++----- .../src/main/java/org/jooq/impl/Keywords.java | 3 +- .../main/java/org/jooq/impl/MergeImpl.java | 77 ++++++++++++------- 4 files changed, 84 insertions(+), 46 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/InsertOnConflictWhereStep.java b/jOOQ/src/main/java/org/jooq/InsertOnConflictWhereStep.java index 52a76d48d2..96578a2ca0 100644 --- a/jOOQ/src/main/java/org/jooq/InsertOnConflictWhereStep.java +++ b/jOOQ/src/main/java/org/jooq/InsertOnConflictWhereStep.java @@ -40,6 +40,7 @@ package org.jooq; import static org.jooq.SQLDialect.CUBRID; // ... import static org.jooq.SQLDialect.POSTGRES_9_5; +// ... import java.util.Collection; diff --git a/jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java b/jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java index 16fe637015..4b0357d574 100644 --- a/jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java +++ b/jOOQ/src/main/java/org/jooq/MergeMatchedWhereStep.java @@ -39,6 +39,7 @@ package org.jooq; import static org.jooq.SQLDialect.CUBRID; // ... +// ... /** * This type is used for the {@link Merge}'s DSL API. @@ -83,13 +84,17 @@ public interface MergeMatchedWhereStep extends MergeNotMatched * Add an additional WHERE clause to the preceding * WHEN MATCHED THEN UPDATE clause. *

- * Note: This syntax is only available for the - * {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases! + *

In Oracle, this will produce:

*

- * See http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016. - * htm for a full definition of the Oracle MERGE statement + *

+     * WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
+     * 
+ *

+ *

In SQL Server, this will produce:

+ *

+ *

+     * WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
+     * 
*/ @Support({ CUBRID }) MergeMatchedDeleteStep where(Condition condition); @@ -98,13 +103,17 @@ public interface MergeMatchedWhereStep extends MergeNotMatched * Add an additional WHERE clause to the preceding * WHEN MATCHED THEN UPDATE clause. *

- * Note: This syntax is only available for the - * {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases! + *

In Oracle, this will produce:

*

- * See http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016. - * htm for a full definition of the Oracle MERGE statement + *

+     * WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
+     * 
+ *

+ *

In SQL Server, this will produce:

+ *

+ *

+     * WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
+     * 
*/ @Support({ CUBRID }) MergeMatchedDeleteStep where(Field condition); @@ -113,13 +122,17 @@ public interface MergeMatchedWhereStep extends MergeNotMatched * Add an additional WHERE clause to the preceding * WHEN MATCHED THEN UPDATE clause. *

- * Note: This syntax is only available for the - * {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases! + *

In Oracle, this will produce:

*

- * See http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016. - * htm for a full definition of the Oracle MERGE statement + *

+     * WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
+     * 
+ *

+ *

In SQL Server, this will produce:

+ *

+ *

+     * WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
+     * 
* * @deprecated - 3.8.0 - [#4763] - Use {@link #where(Condition)} or * {@link #where(Field)} instead. Due to ambiguity between diff --git a/jOOQ/src/main/java/org/jooq/impl/Keywords.java b/jOOQ/src/main/java/org/jooq/impl/Keywords.java index 650a814834..ad7d2e69eb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Keywords.java +++ b/jOOQ/src/main/java/org/jooq/impl/Keywords.java @@ -175,6 +175,7 @@ final class Keywords { static final Keyword K_LIMIT = keyword("limit"); static final Keyword K_LOCK_IN_SHARE_MODE = keyword("lock in share mode"); static final Keyword K_LOOP = keyword("loop"); + static final Keyword K_MATCHED = keyword("matched"); static final Keyword K_MERGE_INTO = keyword("merge into"); static final Keyword K_MINUS = keyword("minus"); static final Keyword K_MODIFY = keyword("modify"); @@ -289,8 +290,6 @@ final class Keywords { static final Keyword K_VERSIONS_BETWEEN = keyword("versions between"); static final Keyword K_VIEW = keyword("view"); static final Keyword K_WHEN = keyword("when"); - static final Keyword K_WHEN_MATCHED_THEN_UPDATE_SET = keyword("when matched then update set"); - static final Keyword K_WHEN_NOT_MATCHED_THEN_INSERT = keyword("when not matched then insert"); static final Keyword K_WHERE = keyword("where"); static final Keyword K_WINDOW = keyword("window"); static final Keyword K_WITH = keyword("with"); diff --git a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java index 573d2907dd..5b96302b71 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java @@ -49,21 +49,28 @@ import static org.jooq.Clause.MERGE_WHEN_MATCHED_THEN_UPDATE; import static org.jooq.Clause.MERGE_WHEN_NOT_MATCHED_THEN_INSERT; import static org.jooq.Clause.MERGE_WHERE; // ... +// ... import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.exists; import static org.jooq.impl.DSL.insertInto; import static org.jooq.impl.DSL.notExists; import static org.jooq.impl.DSL.nullSafe; +import static org.jooq.impl.Keywords.K_AND; import static org.jooq.impl.Keywords.K_AS; import static org.jooq.impl.Keywords.K_DELETE_WHERE; +import static org.jooq.impl.Keywords.K_INSERT; import static org.jooq.impl.Keywords.K_KEY; +import static org.jooq.impl.Keywords.K_MATCHED; import static org.jooq.impl.Keywords.K_MERGE_INTO; +import static org.jooq.impl.Keywords.K_NOT; import static org.jooq.impl.Keywords.K_ON; +import static org.jooq.impl.Keywords.K_SET; +import static org.jooq.impl.Keywords.K_THEN; +import static org.jooq.impl.Keywords.K_UPDATE; import static org.jooq.impl.Keywords.K_UPSERT; import static org.jooq.impl.Keywords.K_USING; import static org.jooq.impl.Keywords.K_VALUES; -import static org.jooq.impl.Keywords.K_WHEN_MATCHED_THEN_UPDATE_SET; -import static org.jooq.impl.Keywords.K_WHEN_NOT_MATCHED_THEN_INSERT; +import static org.jooq.impl.Keywords.K_WHEN; import static org.jooq.impl.Keywords.K_WHERE; import static org.jooq.impl.Keywords.K_WITH_PRIMARY_KEY; import static org.jooq.impl.Tools.EMPTY_FIELD; @@ -73,6 +80,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -143,6 +151,7 @@ import org.jooq.Record; import org.jooq.Record1; import org.jooq.Row; import org.jooq.SQL; +import org.jooq.SQLDialect; import org.jooq.Select; import org.jooq.Table; import org.jooq.TableLike; @@ -226,31 +235,35 @@ implements /** * Generated UID */ - private static final long serialVersionUID = -8835479296876774391L; - private static final Clause[] CLAUSES = { MERGE }; + private static final long serialVersionUID = -8835479296876774391L; + private static final Clause[] CLAUSES = { MERGE }; - private final WithImpl with; - private final Table table; - private final ConditionProviderImpl on; - private TableLike using; + + + + + private final WithImpl with; + private final Table table; + private final ConditionProviderImpl on; + private TableLike using; // [#998] Oracle extensions to the MERGE statement - private Condition matchedWhere; - private Condition matchedDeleteWhere; - private Condition notMatchedWhere; + private Condition matchedWhere; + private Condition matchedDeleteWhere; + private Condition notMatchedWhere; // Flags to keep track of DSL object creation state - private boolean matchedClause; - private FieldMapForUpdate matchedUpdate; - private boolean notMatchedClause; - private FieldMapsForInsert notMatchedInsert; + private boolean matchedClause; + private FieldMapForUpdate matchedUpdate; + private boolean notMatchedClause; + private FieldMapsForInsert notMatchedInsert; // Objects for the UPSERT syntax (including H2 MERGE, HANA UPSERT, etc.) - private boolean upsertStyle; - private QueryPartList> upsertFields; - private QueryPartList> upsertKeys; - private QueryPartList> upsertValues; - private Select upsertSelect; + private boolean upsertStyle; + private QueryPartList> upsertFields; + private QueryPartList> upsertKeys; + private QueryPartList> upsertValues; + private Select upsertSelect; MergeImpl(Configuration configuration, WithImpl with, Table table) { this(configuration, with, table, null); @@ -1503,7 +1516,14 @@ implements // [#999] WHEN MATCHED clause is optional if (matchedUpdate != null) { ctx.formatSeparator() - .visit(K_WHEN_MATCHED_THEN_UPDATE_SET) + .visit(K_WHEN).sql(' ').visit(K_MATCHED); + + + + + + + ctx.sql(' ').visit(K_THEN).sql(' ').visit(K_UPDATE).sql(' ').visit(K_SET) .formatIndentStart() .formatSeparator() .visit(matchedUpdate) @@ -1514,21 +1534,22 @@ implements .start(MERGE_WHERE); // [#998] Oracle MERGE extension: WHEN MATCHED THEN UPDATE .. WHERE - if (matchedWhere != null) { + if (matchedWhere != null) + + + ctx.formatSeparator() .visit(K_WHERE).sql(' ') .visit(matchedWhere); - } ctx.end(MERGE_WHERE) .start(MERGE_DELETE_WHERE); // [#998] Oracle MERGE extension: WHEN MATCHED THEN UPDATE .. DELETE WHERE - if (matchedDeleteWhere != null) { + if (matchedDeleteWhere != null) ctx.formatSeparator() .visit(K_DELETE_WHERE).sql(' ') .visit(matchedDeleteWhere); - } ctx.end(MERGE_DELETE_WHERE) .end(MERGE_WHEN_MATCHED_THEN_UPDATE) @@ -1537,7 +1558,11 @@ implements // [#999] WHEN NOT MATCHED clause is optional if (notMatchedInsert != null) { ctx.formatSeparator() - .visit(K_WHEN_NOT_MATCHED_THEN_INSERT); + .visit(K_WHEN).sql(' ') + .visit(K_NOT).sql(' ') + .visit(K_MATCHED).sql(' ') + .visit(K_THEN).sql(' ') + .visit(K_INSERT); notMatchedInsert.toSQLReferenceKeys(ctx); ctx.formatSeparator() .start(MERGE_VALUES)