[#6583] Work around MySQL's self-reference-in-DML-subquery restriction
This commit is contained in:
parent
988142bc7b
commit
14a73800eb
@ -149,6 +149,12 @@ public interface Context<C extends Context<C>> 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.
|
||||
*/
|
||||
|
||||
@ -509,6 +509,11 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int subqueryLevel() {
|
||||
return subquery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean subquery() {
|
||||
return subquery > 0;
|
||||
|
||||
@ -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<R extends Record> 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<R extends Record> extends AbstractQuery {
|
||||
|
||||
|
||||
|
||||
|
||||
ctx.data().remove(DATA_DML_TARGET_TABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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<R extends Record> extends AbstractResultQuery<R> imp
|
||||
private static final EnumSet<SQLDialect> SUPPORT_SELECT_INTO_TABLE = EnumSet.of(HSQLDB, POSTGRES);
|
||||
static final EnumSet<SQLDialect> SUPPORT_WINDOW_CLAUSE = EnumSet.of(H2, MYSQL, POSTGRES /*, SQLITE -- See [#8279] */);
|
||||
private static final EnumSet<SQLDialect> REQUIRES_FROM_CLAUSE = EnumSet.of(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL);
|
||||
private static final EnumSet<SQLDialect> REQUIRES_DERIVED_TABLE_DML = EnumSet.of(MARIADB, MYSQL);
|
||||
private static final EnumSet<SQLDialect> EMULATE_EMPTY_GROUP_BY_OTHER = EnumSet.of(FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE);
|
||||
|
||||
|
||||
@ -491,6 +494,20 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> 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<R extends Record> extends AbstractResultQuery<R> 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<? extends R> getRecordType0() {
|
||||
|
||||
@ -531,6 +531,11 @@ final class Tools {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* [#6583] The target table on which a DML operation operates on.
|
||||
*/
|
||||
DATA_DML_TARGET_TABLE
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user