diff --git a/jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java b/jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java index 97af19b623..b124599717 100644 --- a/jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java +++ b/jOOQ/src/main/java/org/jooq/MergeMatchedDeleteStep.java @@ -81,7 +81,7 @@ import org.jooq.impl.DSL; * * @author Lukas Eder */ -public interface MergeMatchedDeleteStep extends MergeNotMatchedStep { +public interface MergeMatchedDeleteStep extends MergeMatchedStep { /** * Add an additional DELETE WHERE clause to the preceding diff --git a/jOOQ/src/main/java/org/jooq/MergeMatchedStep.java b/jOOQ/src/main/java/org/jooq/MergeMatchedStep.java index 174864dc21..8a5edc5072 100644 --- a/jOOQ/src/main/java/org/jooq/MergeMatchedStep.java +++ b/jOOQ/src/main/java/org/jooq/MergeMatchedStep.java @@ -49,6 +49,8 @@ import static org.jooq.SQLDialect.HSQLDB; // ... // ... +import org.jooq.impl.DSL; + /** * This type is used for the {@link Merge}'s DSL API. *

@@ -90,8 +92,89 @@ public interface MergeMatchedStep extends MergeNotMatchedStep< /** * Add the WHEN MATCHED THEN UPDATE clause to the - * MERGE statement + * MERGE statement. */ @Support({ CUBRID, DERBY, FIREBIRD, H2, HSQLDB }) MergeMatchedSetStep whenMatchedThenUpdate(); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + */ + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(Condition condition); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + */ + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(Field condition); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#condition(SQL) + * @see SQL + */ + @PlainSQL + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(SQL sql); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#condition(String) + * @see SQL + */ + @PlainSQL + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(String sql); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#condition(String, Object...) + * @see DSL#sql(String, Object...) + * @see SQL + */ + @PlainSQL + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(String sql, Object... bindings); + + /** + * Add the WHEN MATCHED AND .. THEN UPDATE clause to the + * MERGE statement. + *

+ * NOTE: When inserting plain SQL into jOOQ objects, you must + * guarantee syntax integrity. You may also create the possibility of + * malicious SQL injection. Be sure to properly use bind variables and/or + * escape literals when concatenated into SQL clauses! + * + * @see DSL#condition(String, QueryPart...) + * @see DSL#sql(String, QueryPart...) + * @see SQL + */ + @PlainSQL + @Support({ DERBY, H2, HSQLDB }) + MergeMatchedThenStep whenMatchedAnd(String sql, QueryPart... parts); + } diff --git a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java index b0c8cc23b5..6c597476a7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java @@ -119,6 +119,7 @@ import org.jooq.MergeKeyStep8; import org.jooq.MergeKeyStep9; import org.jooq.MergeMatchedDeleteStep; import org.jooq.MergeMatchedSetMoreStep; +import org.jooq.MergeMatchedThenStep; import org.jooq.MergeNotMatchedSetMoreStep; import org.jooq.MergeNotMatchedValuesStep1; import org.jooq.MergeNotMatchedValuesStep10; @@ -203,6 +204,7 @@ implements MergeOnStep, MergeOnConditionStep, MergeMatchedSetMoreStep, + MergeMatchedThenStep, MergeNotMatchedSetMoreStep, @@ -254,7 +256,8 @@ implements // Flags to keep track of DSL object creation state private boolean matchedClause; - private FieldMapForUpdate matchedUpdate; + private List matchedUpdate; + private List matchedUpdateAnd; private boolean notMatchedClause; private FieldMapsForInsert notMatchedInsert; @@ -275,6 +278,8 @@ implements this.with = with; this.table = table; this.on = new ConditionProviderImpl(); + this.matchedUpdate = new ArrayList<>(); + this.matchedUpdateAnd = new ArrayList<>(); if (fields != null) columns(fields); @@ -305,6 +310,10 @@ implements return upsertValues; } + FieldMapForUpdate getLastMatchedUpdate() { + return matchedUpdate.get(matchedUpdate.size() - 1); + } + @Override public final MergeImpl columns(Field... fields) { return columns(Arrays.asList(fields)); @@ -917,12 +926,50 @@ implements @Override public final MergeImpl whenMatchedThenUpdate() { matchedClause = true; - matchedUpdate = new FieldMapForUpdate(table, MERGE_SET_ASSIGNMENT); + matchedUpdate.add(new FieldMapForUpdate(table, MERGE_SET_ASSIGNMENT)); + matchedUpdateAnd.add(null); notMatchedClause = false; return this; } + @Override + public final MergeImpl whenMatchedAnd(Condition condition) { + whenMatchedThenUpdate(); + matchedUpdateAnd.set(matchedUpdateAnd.size() - 1, condition); + return this; + } + + @Override + public final MergeImpl whenMatchedAnd(Field condition) { + return whenMatchedAnd(condition(condition)); + } + + @Override + public final MergeImpl whenMatchedAnd(SQL sql) { + return whenMatchedAnd(condition(sql)); + } + + @Override + public final MergeImpl whenMatchedAnd(String sql) { + return whenMatchedAnd(condition(sql)); + } + + @Override + public final MergeImpl whenMatchedAnd(String sql, Object... bindings) { + return whenMatchedAnd(condition(sql, bindings)); + } + + @Override + public final MergeImpl whenMatchedAnd(String sql, QueryPart... parts) { + return whenMatchedAnd(condition(sql, parts)); + } + + @Override + public final MergeImpl thenUpdate() { + return this; + } + @Override public final MergeImpl set(Field field, T value) { return set(field, Tools.field(value, field)); @@ -931,7 +978,7 @@ implements @Override public final MergeImpl set(Field field, Field value) { if (matchedClause) - matchedUpdate.put(field, nullSafe(value)); + getLastMatchedUpdate().put(field, nullSafe(value)); else if (notMatchedClause) notMatchedInsert.set(field, nullSafe(value)); else @@ -956,7 +1003,7 @@ implements @Override public final MergeImpl set(Map map) { if (matchedClause) - matchedUpdate.set(map); + getLastMatchedUpdate().set(map); else if (notMatchedClause) notMatchedInsert.set(map); else @@ -1525,18 +1572,25 @@ implements .start(MERGE_SET); // [#999] WHEN MATCHED clause is optional - if (matchedUpdate != null) { + for (int i = 0; i < matchedUpdate.size(); i++) { + FieldMapForUpdate map = matchedUpdate.get(i); + Condition condition = matchedUpdateAnd.get(i); + ctx.formatSeparator() .visit(K_WHEN).sql(' ').visit(K_MATCHED); - // [#5110] Standard SQL "WHERE" clause in updates - if (matchedWhere != null && NO_SUPPORT_WHERE.contains(ctx.family())) + // [#7291] Standard SQL AND clause in updates + if (condition != null) + ctx.sql(' ').visit(K_AND).sql(' ').visit(condition); + + // [#5110] Oracle style "WHERE" clause in updates + else if (matchedWhere != null && NO_SUPPORT_WHERE.contains(ctx.family())) ctx.sql(' ').visit(K_AND).sql(' ').visit(matchedWhere); ctx.sql(' ').visit(K_THEN).sql(' ').visit(K_UPDATE).sql(' ').visit(K_SET) .formatIndentStart() .formatSeparator() - .visit(matchedUpdate) + .visit(map) .formatIndentEnd(); }