From 3f84ccf58fc4e3350f8f6786273235aa5ce267af Mon Sep 17 00:00:00 2001 From: lukaseder Date: Wed, 10 Apr 2019 11:52:56 +0200 Subject: [PATCH] [#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. --- .../resources/org/jooq/web/grammar-3.12.txt | 2 +- .../java/org/jooq/impl/FieldMapForUpdate.java | 17 ++++++++++++++--- .../java/org/jooq/impl/InsertQueryImpl.java | 16 +++++++++++++--- .../src/main/java/org/jooq/impl/ParserImpl.java | 7 ++++++- jOOQ/src/main/java/org/jooq/impl/Tools.java | 7 ++++++- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt b/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt index ad0615f7cd..b6af32a32a 100644 --- a/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt +++ b/jOOQ-manual/src/main/resources/org/jooq/web/grammar-3.12.txt @@ -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' ( diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java index e905a1fcad..36eef99e18 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java @@ -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> { 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> { .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 = ", "; } diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index 119ee44f68..4dfb8035b4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -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 extends AbstractStoreQuery 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); diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 8cca34b587..26494752f9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -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(); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index e2fef18f4c..6b57258653 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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 } /**