[#8479] Emulate INSERT .. ON DUPLICATE KEY UPDATE .. WHERE on MySQL

MySQL doesn't support the useful WHERE clause on ON DUPLICATE KEY UPDATE like PostgreSQL does on ON CONFLICT. But it can easily be emulated using CASE if users do not rely on the effective update counts.
This commit is contained in:
lukaseder 2019-04-10 11:52:56 +02:00
parent 72eda66bdf
commit 3f84ccf58f
5 changed files with 40 additions and 9 deletions

View File

@ -317,7 +317,7 @@ insertStatement =
)
break
[
'ON DUPLICATE KEY UPDATE' 'SET' setClauses
'ON DUPLICATE KEY UPDATE' 'SET' setClauses [ 'WHERE' condition ]
| 'ON DUPLICATE KEY IGNORE'
| 'ON CONFLICT' ( 'ON CONSTRAINT' constraintName | '(' fieldNames ')' ) 'DO'
(

View File

@ -43,12 +43,15 @@ import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.Tools.flattenEntrySet;
import static org.jooq.impl.Tools.DataKey.DATA_ON_DUPLICATE_KEY_WHERE;
import java.util.EnumSet;
import java.util.Map;
import org.jooq.Clause;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.SQLDialect;
@ -74,6 +77,7 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> {
this.assignmentClause = assignmentClause;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public final void accept(Context<?> ctx) {
if (size() > 0) {
@ -98,9 +102,16 @@ final class FieldMapForUpdate extends AbstractQueryPartMap<Field<?>, Field<?>> {
.qualify(supportsQualify)
.visit(entry.getKey())
.qualify(restoreQualify)
.sql(" = ")
.visit(entry.getValue())
.end(assignmentClause);
.sql(" = ");
// [#8479] Emulate WHERE clause using CASE
Condition condition = (Condition) ctx.data(DATA_ON_DUPLICATE_KEY_WHERE);
if (condition != null)
ctx.visit(when(condition, (Field) entry.getValue()).else_(entry.getKey()));
else
ctx.visit(entry.getValue());
ctx.end(assignmentClause);
separator = ", ";
}

View File

@ -79,6 +79,7 @@ import static org.jooq.impl.Tools.fieldNameStrings;
import static org.jooq.impl.Tools.fieldNames;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_CONSTRAINT_REFERENCE;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_INSERT_SELECT_WITHOUT_INSERT_COLUMN_LIST;
import static org.jooq.impl.Tools.DataKey.DATA_ON_DUPLICATE_KEY_WHERE;
import java.util.ArrayList;
import java.util.Arrays;
@ -305,9 +306,18 @@ final class InsertQueryImpl<R extends Record> extends AbstractStoreQuery<R> impl
.visit(K_ON_DUPLICATE_KEY_UPDATE)
.formatIndentStart()
.formatSeparator()
.qualify(newQualify)
.visit(updateMap)
.qualify(oldQualify)
.qualify(newQualify);
// [#8479] Emulate WHERE clause using CASE
if (condition.hasWhere())
ctx.data(DATA_ON_DUPLICATE_KEY_WHERE, condition.getWhere());
ctx.visit(updateMap);
if (condition.hasWhere())
ctx.data().remove(DATA_ON_DUPLICATE_KEY_WHERE);
ctx.qualify(oldQualify)
.formatIndentEnd()
.end(INSERT_ON_DUPLICATE_KEY_UPDATE);

View File

@ -1700,7 +1700,12 @@ final class ParserImpl implements Parser {
if (parseKeywordIf(ctx, "ON")) {
if (parseKeywordIf(ctx, "DUPLICATE KEY UPDATE SET")) {
returning = onDuplicate.onDuplicateKeyUpdate().set(parseSetClauseList(ctx));
InsertOnConflictWhereStep<?> where = onDuplicate.onDuplicateKeyUpdate().set(parseSetClauseList(ctx));
if (parseKeywordIf(ctx, "WHERE"))
returning = where.where(parseCondition(ctx));
else
returning = where;
}
else if (parseKeywordIf(ctx, "DUPLICATE KEY IGNORE")) {
returning = onDuplicate.onDuplicateKeyIgnore();

View File

@ -549,7 +549,12 @@ final class Tools {
/**
* [#6583] The target table on which a DML operation operates on.
*/
DATA_DML_TARGET_TABLE
DATA_DML_TARGET_TABLE,
/**
* [#8479] There is a WHERE clause to be emulated for ON DUPLICATE KEY
*/
DATA_ON_DUPLICATE_KEY_WHERE
}
/**