[#7491] Emulate ON CONFLICT .. DO UPDATE .. WHERE <p> on SQL Server using WHEN MATCHED AND <p>

This commit is contained in:
lukaseder 2018-05-15 13:25:16 +02:00
parent c16aafcac5
commit 701ab65dfc
4 changed files with 84 additions and 46 deletions

View File

@ -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;

View File

@ -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<R extends Record> extends MergeNotMatched
* Add an additional <code>WHERE</code> clause to the preceding
* <code>WHEN MATCHED THEN UPDATE</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases!
* <h3>In Oracle, this will produce:</h3>
* <p>
* See <a href=
* "http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm"
* >http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.
* htm</a> for a full definition of the Oracle <code>MERGE</code> statement
* <code><pre>
* WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
* </pre></code>
* <p>
* <h3>In SQL Server, this will produce:</h3>
* <p>
* <code><pre>
* WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
* </pre><code>
*/
@Support({ CUBRID })
MergeMatchedDeleteStep<R> where(Condition condition);
@ -98,13 +103,17 @@ public interface MergeMatchedWhereStep<R extends Record> extends MergeNotMatched
* Add an additional <code>WHERE</code> clause to the preceding
* <code>WHEN MATCHED THEN UPDATE</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases!
* <h3>In Oracle, this will produce:</h3>
* <p>
* See <a href=
* "http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm"
* >http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.
* htm</a> for a full definition of the Oracle <code>MERGE</code> statement
* <code><pre>
* WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
* </pre></code>
* <p>
* <h3>In SQL Server, this will produce:</h3>
* <p>
* <code><pre>
* WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
* </pre><code>
*/
@Support({ CUBRID })
MergeMatchedDeleteStep<R> where(Field<Boolean> condition);
@ -113,13 +122,17 @@ public interface MergeMatchedWhereStep<R extends Record> extends MergeNotMatched
* Add an additional <code>WHERE</code> clause to the preceding
* <code>WHEN MATCHED THEN UPDATE</code> clause.
* <p>
* <b>Note:</b> This syntax is only available for the
* {@link SQLDialect#CUBRID} and {@link SQLDialect#ORACLE} databases!
* <h3>In Oracle, this will produce:</h3>
* <p>
* See <a href=
* "http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.htm"
* >http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9016.
* htm</a> for a full definition of the Oracle <code>MERGE</code> statement
* <code><pre>
* WHEN MATCHED THEN UPDATE SET .. WHERE [ condition ]
* </pre></code>
* <p>
* <h3>In SQL Server, this will produce:</h3>
* <p>
* <code><pre>
* WHEN MATCHED AND [ condition ] THEN UPDATE SET ..
* </pre><code>
*
* @deprecated - 3.8.0 - [#4763] - Use {@link #where(Condition)} or
* {@link #where(Field)} instead. Due to ambiguity between

View File

@ -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");

View File

@ -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<R> table;
private final ConditionProviderImpl on;
private TableLike<?> using;
private final WithImpl with;
private final Table<R> 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<Field<?>> upsertFields;
private QueryPartList<Field<?>> upsertKeys;
private QueryPartList<Field<?>> upsertValues;
private Select<?> upsertSelect;
private boolean upsertStyle;
private QueryPartList<Field<?>> upsertFields;
private QueryPartList<Field<?>> upsertKeys;
private QueryPartList<Field<?>> upsertValues;
private Select<?> upsertSelect;
MergeImpl(Configuration configuration, WithImpl with, Table<R> 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)