diff --git a/jOOQ/src/main/java/org/jooq/Context.java b/jOOQ/src/main/java/org/jooq/Context.java index 0b6e95f0b7..bfa16ce75c 100644 --- a/jOOQ/src/main/java/org/jooq/Context.java +++ b/jOOQ/src/main/java/org/jooq/Context.java @@ -149,6 +149,12 @@ public interface Context> extends Scope { */ C subquery(boolean subquery); + /** + * Which level of subqueries we're currently in, starting with 0 for the top + * level query. + */ + int subqueryLevel(); + /** * Start a new SELECT scope. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index 6ebe9a286f..d035120da1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -509,6 +509,11 @@ abstract class AbstractContext> extends AbstractScope imple return (C) this; } + @Override + public final int subqueryLevel() { + return subquery; + } + @Override public final boolean subquery() { return subquery > 0; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index a3c452b337..c0d6f362bb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -67,6 +67,7 @@ import static org.jooq.impl.Keywords.K_SQL; import static org.jooq.impl.Keywords.K_TABLE; import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.BooleanDataKey.DATA_EMULATE_BULK_INSERT_RETURNING; +import static org.jooq.impl.Tools.DataKey.DATA_DML_TARGET_TABLE; import static org.jooq.util.sqlite.SQLiteDSL.rowid; import java.sql.CallableStatement; @@ -240,6 +241,8 @@ abstract class AbstractDMLQuery extends AbstractQuery { public final void accept(Context ctx) { WithImpl w = with; + ctx.data(DATA_DML_TARGET_TABLE, table); + @@ -440,6 +443,8 @@ abstract class AbstractDMLQuery extends AbstractQuery { + + ctx.data().remove(DATA_DML_TARGET_TABLE); } /** diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 4bad0d52ce..b51e943f8e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -93,6 +93,7 @@ import static org.jooq.impl.CombineOperator.INTERSECT; import static org.jooq.impl.CombineOperator.INTERSECT_ALL; import static org.jooq.impl.CombineOperator.UNION; import static org.jooq.impl.CombineOperator.UNION_ALL; +import static org.jooq.impl.DSL.asterisk; import static org.jooq.impl.DSL.falseCondition; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.name; @@ -137,6 +138,7 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_ROW_VALUE_EXPRESSION_PREDI import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASES_IN_ORDER_BY; import static org.jooq.impl.Tools.BooleanDataKey.DATA_WRAP_DERIVED_TABLES_IN_PARENTHESES; import static org.jooq.impl.Tools.DataKey.DATA_COLLECTED_SEMI_ANTI_JOIN; +import static org.jooq.impl.Tools.DataKey.DATA_DML_TARGET_TABLE; import static org.jooq.impl.Tools.DataKey.DATA_OVERRIDE_ALIASES_IN_ORDER_BY; import static org.jooq.impl.Tools.DataKey.DATA_SELECT_INTO_TABLE; import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS; @@ -207,6 +209,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp private static final EnumSet SUPPORT_SELECT_INTO_TABLE = EnumSet.of(HSQLDB, POSTGRES); static final EnumSet SUPPORT_WINDOW_CLAUSE = EnumSet.of(H2, MYSQL, POSTGRES /*, SQLITE -- See [#8279] */); private static final EnumSet REQUIRES_FROM_CLAUSE = EnumSet.of(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL); + private static final EnumSet REQUIRES_DERIVED_TABLE_DML = EnumSet.of(MARIADB, MYSQL); private static final EnumSet EMULATE_EMPTY_GROUP_BY_OTHER = EnumSet.of(FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE); @@ -491,6 +494,20 @@ final class SelectQueryImpl extends AbstractResultQuery imp @Override public final void accept(Context context) { + Table dmlTable; + + // [#6583] Work around MySQL's self-reference-in-DML-subquery restriction + if (context.subqueryLevel() == 1 + && REQUIRES_DERIVED_TABLE_DML.contains(context.family()) + && (dmlTable = (Table) context.data(DATA_DML_TARGET_TABLE)) != null + && containsTable(dmlTable)) { + context.visit(DSL.select(asterisk()).from(DSL.table(this).as("t"))); + } + else + accept0(context); + } + + public final void accept0(Context context) { context.scopeStart(); for (Table table : getFrom()) registerTable(context, table); @@ -2071,6 +2088,22 @@ final class SelectQueryImpl extends AbstractResultQuery imp return table.fieldsRow().size() > 0; } + private final boolean containsTable(Table table) { + for (Table t : getFrom()) + if (containsTable(t, table)) + return true; + + return false; + } + + private final boolean containsTable(Table table, Table contained) { + if (table instanceof JoinTable) + return containsTable(((JoinTable) table).lhs, contained) + || containsTable(((JoinTable) table).rhs, contained); + else + return contained.equals(table); + } + @SuppressWarnings("unchecked") @Override final Class getRecordType0() { diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index d9d274e84e..b65e4081bb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -531,6 +531,11 @@ final class Tools { + + /** + * [#6583] The target table on which a DML operation operates on. + */ + DATA_DML_TARGET_TABLE } /**