[jOOQ/jOOQ#15637] Emulate DELETE .. USING with MERGE, where available
This commit is contained in:
parent
310f3cb266
commit
d0bf1d180f
@ -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<R extends Record> extends ConditionProvider, Delete
|
||||
/**
|
||||
* Add tables to the <code>USING</code> clause.
|
||||
*/
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
void addUsing(TableLike<?> table);
|
||||
|
||||
/**
|
||||
* Add tables to the <code>USING</code> clause.
|
||||
*/
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
void addUsing(TableLike<?>... tables);
|
||||
|
||||
/**
|
||||
* Add tables to the <code>USING</code> clause.
|
||||
*/
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
void addUsing(Collection<? extends TableLike<?>> tables);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@ -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<R extends Record> extends DeleteWhereStep<R> {
|
||||
* Add a <code>USING</code> clause to the query.
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
DeleteWhereStep<R> using(TableLike<?> table);
|
||||
|
||||
/**
|
||||
* Add a <code>USING</code> clause to the query.
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
DeleteWhereStep<R> using(TableLike<?>... tables);
|
||||
|
||||
/**
|
||||
* Add a <code>USING</code> clause to the query.
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
DeleteWhereStep<R> using(Collection<? extends TableLike<?>> tables);
|
||||
|
||||
/**
|
||||
@ -125,7 +132,7 @@ public interface DeleteUsingStep<R extends Record> extends DeleteWhereStep<R> {
|
||||
* @see SQL
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
@PlainSQL
|
||||
DeleteWhereStep<R> using(SQL sql);
|
||||
|
||||
@ -141,7 +148,7 @@ public interface DeleteUsingStep<R extends Record> extends DeleteWhereStep<R> {
|
||||
* @see SQL
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
@PlainSQL
|
||||
DeleteWhereStep<R> using(String sql);
|
||||
|
||||
@ -158,7 +165,7 @@ public interface DeleteUsingStep<R extends Record> extends DeleteWhereStep<R> {
|
||||
* @see SQL
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
@PlainSQL
|
||||
DeleteWhereStep<R> using(String sql, Object... bindings);
|
||||
|
||||
@ -175,7 +182,7 @@ public interface DeleteUsingStep<R extends Record> extends DeleteWhereStep<R> {
|
||||
* @see SQL
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
@PlainSQL
|
||||
DeleteWhereStep<R> using(String sql, QueryPart... parts);
|
||||
|
||||
@ -185,6 +192,6 @@ public interface DeleteUsingStep<R extends Record> extends DeleteWhereStep<R> {
|
||||
* @see DSL#table(Name)
|
||||
*/
|
||||
@NotNull @CheckReturnValue
|
||||
@Support({ DUCKDB, MARIADB, MYSQL, POSTGRES })
|
||||
@Support({ DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES })
|
||||
DeleteWhereStep<R> using(Name name);
|
||||
}
|
||||
|
||||
@ -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<SQLDialect> REQUIRE_REPEAT_FROM_IN_USING = SQLDialect.supportedBy(MARIADB, MYSQL);
|
||||
static final Set<SQLDialect> NO_SUPPORT_REPEAT_FROM_IN_USING = SQLDialect.supportedBy(DUCKDB, POSTGRES, YUGABYTEDB);
|
||||
static final Set<SQLDialect> REQUIRES_WHERE = SQLDialect.supportedBy(CLICKHOUSE);
|
||||
static final Set<SQLDialect> 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<? extends Number> 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<? extends Number> limit) {
|
||||
if (limit != null)
|
||||
if (ctx.family() == FIREBIRD)
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user