diff --git a/jOOQ/src/main/java/org/jooq/DeleteQuery.java b/jOOQ/src/main/java/org/jooq/DeleteQuery.java index a141562b53..d9a4bf8afd 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteQuery.java +++ b/jOOQ/src/main/java/org/jooq/DeleteQuery.java @@ -44,9 +44,14 @@ package org.jooq; // ... // ... // ... +import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.DUCKDB; +// ... import static org.jooq.SQLDialect.FIREBIRD; import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +// ... import static org.jooq.SQLDialect.MARIADB; // ... // ... @@ -84,19 +89,19 @@ public interface DeleteQuery extends ConditionProvider, Delete /** * Add tables to the USING clause. */ - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) void addUsing(TableLike table); /** * Add tables to the USING clause. */ - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) void addUsing(TableLike... tables); /** * Add tables to the USING clause. */ - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) void addUsing(Collection> tables); // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/DeleteUsingStep.java b/jOOQ/src/main/java/org/jooq/DeleteUsingStep.java index 5a6274dd17..dabd863a49 100644 --- a/jOOQ/src/main/java/org/jooq/DeleteUsingStep.java +++ b/jOOQ/src/main/java/org/jooq/DeleteUsingStep.java @@ -41,7 +41,14 @@ package org.jooq; // ... // ... // ... +// ... +import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.DUCKDB; +// ... +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +import static org.jooq.SQLDialect.HSQLDB; +// ... import static org.jooq.SQLDialect.MARIADB; // ... import static org.jooq.SQLDialect.MYSQL; @@ -96,21 +103,21 @@ public interface DeleteUsingStep extends DeleteWhereStep { * Add a USING clause to the query. */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) DeleteWhereStep using(TableLike table); /** * Add a USING clause to the query. */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) DeleteWhereStep using(TableLike... tables); /** * Add a USING clause to the query. */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) DeleteWhereStep using(Collection> tables); /** @@ -125,7 +132,7 @@ public interface DeleteUsingStep extends DeleteWhereStep { * @see SQL */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) @PlainSQL DeleteWhereStep using(SQL sql); @@ -141,7 +148,7 @@ public interface DeleteUsingStep extends DeleteWhereStep { * @see SQL */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) @PlainSQL DeleteWhereStep using(String sql); @@ -158,7 +165,7 @@ public interface DeleteUsingStep extends DeleteWhereStep { * @see SQL */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) @PlainSQL DeleteWhereStep using(String sql, Object... bindings); @@ -175,7 +182,7 @@ public interface DeleteUsingStep extends DeleteWhereStep { * @see SQL */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) @PlainSQL DeleteWhereStep using(String sql, QueryPart... parts); @@ -185,6 +192,6 @@ public interface DeleteUsingStep extends DeleteWhereStep { * @see DSL#table(Name) */ @NotNull @CheckReturnValue - @Support({ DUCKDB, MARIADB, MYSQL, POSTGRES }) + @Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES }) DeleteWhereStep using(Name name); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java index c729ae93f9..da6876edd6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java @@ -51,6 +51,7 @@ import static org.jooq.SQLDialect.CLICKHOUSE; // ... import static org.jooq.SQLDialect.CUBRID; // ... +// ... import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.DUCKDB; // ... @@ -80,6 +81,7 @@ import static org.jooq.SQLDialect.YUGABYTEDB; import static org.jooq.conf.SettingsTools.getExecuteDeleteWithoutWhere; import static org.jooq.impl.ConditionProviderImpl.extractCondition; import static org.jooq.impl.DSL.field; +import static org.jooq.impl.DSL.mergeInto; import static org.jooq.impl.DSL.noCondition; import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; @@ -105,6 +107,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import org.jooq.Clause; import org.jooq.Condition; @@ -123,6 +126,7 @@ import org.jooq.SQLDialect; import org.jooq.Scope; import org.jooq.SortField; import org.jooq.Table; +import org.jooq.TableField; import org.jooq.TableLike; // ... import org.jooq.conf.ParamType; @@ -152,6 +156,8 @@ implements static final Set REQUIRE_REPEAT_FROM_IN_USING = SQLDialect.supportedBy(MARIADB, MYSQL); static final Set NO_SUPPORT_REPEAT_FROM_IN_USING = SQLDialect.supportedBy(DUCKDB, POSTGRES, YUGABYTEDB); static final Set REQUIRES_WHERE = SQLDialect.supportedBy(CLICKHOUSE); + static final Set EMULATE_USING_WITH_MERGE = SQLDialect.supportedBy(DERBY, FIREBIRD, H2, HSQLDB); + @@ -303,6 +309,11 @@ implements @SuppressWarnings({ "unchecked", "rawtypes" }) @Override final void accept1(Context ctx) { + if (!using.isEmpty() && EMULATE_USING_WITH_MERGE.contains(ctx.dialect())) { + acceptUsingAsMerge(ctx); + return; + } + ctx.start(DELETE_DELETE) .visit(K_DELETE).sql(' '); @@ -439,6 +450,78 @@ implements ctx.end(DELETE_RETURNING); } + static final record MergeUsing(Table table, boolean patchSource) { + + } + + static final MergeUsing mergeUsing( + TableList tables, + Table table, + Condition condition, + SortFieldList orderBy, + Field limit + ) { + boolean patchSource = true; + + if (orderBy.isEmpty() && limit == null) { + if (tables.size() == 1 && tables.get(0) instanceof TableImpl && !(patchSource = false)) + return new MergeUsing(tables.get(0), patchSource); + else + return new MergeUsing(select().from(tables).asTable("s"), patchSource); + } + + // TODO [#13326]: Avoid the JOIN if it isn't strictly necessary + // (i.e. if ORDER BY references only from, not table) + else + return new MergeUsing( + select(tables.fields()) + .from(tables) + .join(table).on(condition) + .orderBy(orderBy) + .limit(limit) + .asTable("s"), + patchSource + ); + + } + + private final void acceptUsingAsMerge(Context ctx) { + // TODO: What about RETURNING? + // TODO: What if there are multiple FROM tables? + // TODO: What if there are SET ROW = ROW assignment(s)? + // TODO: What if there are SET ROW = (SELECT ..) assignment(s)? + + Condition c = condition; + Table t = table(ctx); + TableList u; + + // [#15637] Same semantics as NO_SUPPORT_REPEAT_FROM_IN_USING + if (containsDeclaredTable(using, t)) { + u = new TableList(using); + u.remove(t); + } + else + u = using; + + MergeUsing mu = mergeUsing(u, t, c, orderBy, limit); + + if (mu.patchSource() && ctx.configuration().requireCommercial(() -> "The DELETE .. USING to MERGE transformation requires commercial only logic for non-trivial USING clauses. Please upgrade to the jOOQ Professional Edition or jOOQ Enterprise Edition")) { + + + + + + + + + + + + } + + ctx.visit(mergeInto(table).using(mu.table()).on(c).whenMatchedThenDelete()); + } + static final void acceptLimit(Context ctx, Field limit) { if (limit != null) if (ctx.family() == FIREBIRD) diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index a101f5af24..9174e0fc29 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -90,6 +90,7 @@ import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.selectFrom; import static org.jooq.impl.DSL.trueCondition; +import static org.jooq.impl.DeleteQueryImpl.mergeUsing; import static org.jooq.impl.InlineDerivedTable.hasInlineDerivedTables; import static org.jooq.impl.InlineDerivedTable.transformInlineDerivedTables; import static org.jooq.impl.InlineDerivedTable.transformInlineDerivedTables0; @@ -180,11 +181,11 @@ import org.jooq.TableField; import org.jooq.TableLike; // ... import org.jooq.UpdateQuery; +import org.jooq.impl.DeleteQueryImpl.MergeUsing; import org.jooq.impl.FieldMapForUpdate.SetClause; import org.jooq.impl.QOM.UnmodifiableList; import org.jooq.impl.QOM.UnmodifiableMap; import org.jooq.impl.QOM.Update; -import org.jooq.impl.Tools.BooleanDataKey; /** * @author Lukas Eder @@ -658,29 +659,12 @@ implements // TODO: What if there are SET ROW = ROW assignment(s)? // TODO: What if there are SET ROW = (SELECT ..) assignment(s)? - Table s; - boolean patchSource = true; Condition c = condition; + Table t = table(ctx); FieldMapForUpdate um = updateMap; + MergeUsing mu = mergeUsing(from, t, c, orderBy, limit); - if (orderBy.isEmpty() && limit == null) { - if (from.size() == 1 && from.get(0) instanceof TableImpl && !(patchSource = false)) - s = from.get(0); - else - s = select().from(from).asTable("s"); - } - - // TODO [#13326]: Avoid the JOIN if it isn't strictly necessary - // (i.e. if ORDER BY references only from, not table) - else - s = select(from.fields()) - .from(from) - .join(table).on(condition) - .orderBy(orderBy) - .limit(limit) - .asTable("s"); - - if (patchSource && ctx.configuration().requireCommercial(() -> "The UPDATE .. FROM to MERGE transformation requires commercial only logic for non-trivial FROM clauses. Please upgrade to the jOOQ Professional Edition or jOOQ Enterprise Edition")) { + if (mu.patchSource() && ctx.configuration().requireCommercial(() -> "The UPDATE .. FROM to MERGE transformation requires commercial only logic for non-trivial FROM clauses. Please upgrade to the jOOQ Professional Edition or jOOQ Enterprise Edition")) { @@ -698,7 +682,7 @@ implements } - ctx.visit(mergeInto(table).using(s).on(c).whenMatchedThenUpdate().set(um)); + ctx.visit(mergeInto(table).using(mu.table()).on(c).whenMatchedThenUpdate().set(um)); } final boolean updatesField(Field field) {