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 } /**