[jOOQ/jOOQ#14742] Fix regressions. These still don't work in MariaDB:

- INSERT INTO t VALUES ((SELECT .. FROM t))
- DELETE FROM t USING t AS u WHERE u.x = (SELECT .. FROM t)
This commit is contained in:
Lukas Eder 2023-03-03 13:00:53 +01:00
parent 57204a93cc
commit 2b5cc36086
4 changed files with 57 additions and 14 deletions

View File

@ -103,6 +103,7 @@ import static org.jooq.impl.Tools.removeGenerator;
import static org.jooq.impl.Tools.unalias;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASED_EXPRESSIONS;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_TARGET_TABLE;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_USING_TABLES;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_RENDERING_DATA_CHANGE_DELTA_TABLE;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_TOP_LEVEL_CTE;
import static org.jooq.tools.StringUtils.defaultIfNull;
@ -340,8 +341,9 @@ abstract class AbstractDMLQuery<R extends Record> extends AbstractRowCountQuery
public final void accept(Context<?> ctx) {
WithImpl w = with;
ctx.scopeStart()
.data(DATA_DML_TARGET_TABLE, table);
ctx.scopeStart();
ctx.data(DATA_DML_TARGET_TABLE, table);
ctx.data(DATA_DML_USING_TABLES, this instanceof DeleteQueryImpl<?> d ? d.$using() : null);
@ -672,6 +674,7 @@ abstract class AbstractDMLQuery<R extends Record> extends AbstractRowCountQuery
ctx.data().remove(DATA_DML_USING_TABLES);
ctx.data().remove(DATA_DML_TARGET_TABLE);
ctx.scopeEnd();
}

View File

@ -162,12 +162,7 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
// Single record inserts can use the standard syntax in any dialect
else if (rows == 1 && supportsValues(ctx)) {
ctx.formatSeparator()
.start(INSERT_VALUES)
.visit(K_VALUES)
.sql(' ');
toSQL92Values(ctx);
ctx.end(INSERT_VALUES);
toSQLValues(ctx);
}
// True SQL92 multi-record inserts aren't always supported
@ -197,13 +192,14 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
case MARIADB: {
if (supportsValues(ctx))
toSQLValues(ctx);
else
toSQLInsertSelect(ctx, insertSelect(ctx, GeneratorStatementType.INSERT));
break;
}
@ -362,6 +358,20 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
// [#14742] MariaDB can't have (unaliased!) self-references of the INSERT
// target table in INSERT INTO t VALUES ((SELECT .. FROM t)),
// though other subqueries are possible
// [#6583] While MySQL also has this limitation, it is already covered
// for all DML statements, elsewhere
case MARIADB:
for (List<Field<?>> row : values.values())
for (Field<?> value : row)
if (value instanceof ScalarSubquery<?> s)
if (Tools.containsTable(s.query.$from(), table, false))
return false;
return true;
default:
return true;

View File

@ -227,6 +227,7 @@ import static org.jooq.impl.Tools.ExtendedDataKey.DATA_RENDER_TABLE;
import static org.jooq.impl.Tools.ExtendedDataKey.DATA_TRANSFORM_ROWNUM_TO_LIMIT;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_COLLECTED_SEMI_ANTI_JOIN;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_TARGET_TABLE;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_USING_TABLES;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_OVERRIDE_ALIASES_IN_ORDER_BY;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_RENDERING_DATA_CHANGE_DELTA_TABLE;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_SELECT_ALIASES;
@ -1423,6 +1424,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@Override
public final void accept(Context<?> ctx) {
Table<?> dmlTable;
List<Table<?>> dmlTables;
// [#6583] [#8609] [#14742] Work around MySQL's self-reference-in-DML-subquery restriction
if (ctx.subqueryLevel() == 1
@ -1433,6 +1435,23 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
ctx.visit(DSL.select(asterisk()).from(asTable("t")));
}
// [#14742] MariaDB still has this bug: https://jira.mariadb.org/browse/MDEV-17954
else if (ctx.subqueryLevel() == 1
&& ctx.family() == MARIADB
&& !TRUE.equals(ctx.data(DATA_INSERT_SELECT))
&& (
// A USING clause can be generated when the target table is aliased
(dmlTable = (Table<?>) ctx.data(DATA_DML_TARGET_TABLE)) != null
&& Tools.aliased(dmlTable) != null
&& containsUnaliasedTable(getFrom(), dmlTable)
// Or, explicitly
|| (dmlTables = (List<Table<?>>) ctx.data(DATA_DML_USING_TABLES)) != null
&& Tools.anyMatch(dmlTables, t -> containsUnaliasedTable(getFrom(), t)))) {
ctx.visit(DSL.select(asterisk()).from(asTable("t")));
}
// [#3564] Emulate DISTINCT ON queries at the top level
else if (Tools.isNotEmpty(distinctOn) && EMULATE_DISTINCT_ON.contains(ctx.dialect())) {
ctx.visit(distinctOnEmulation());

View File

@ -818,6 +818,11 @@ final class Tools {
*/
DATA_DML_TARGET_TABLE,
/**
* [#6583] [#14742] The target table on which a DML operation operates on.
*/
DATA_DML_USING_TABLES,
/**
* [#8479] There is a WHERE clause to be emulated for ON DUPLICATE KEY
*/
@ -7169,6 +7174,12 @@ final class Tools {
return traverseJoins(in, false, r -> r, search(search, t -> unwrap(t, unalias)));
}
static final boolean containsTable(Iterable<? extends Table<?>> in, Table<?> search, boolean unalias) {
// [#6304] [#7626] [#14668] Improved alias discovery
return traverseJoins(in, false, r -> r, search(search, t -> unwrap(t, unalias)));
}
static final boolean containsUnaliasedTable(Table<?> in, Table<?> search) {
// [#6304] [#7626] [#14668] Improved alias discovery