diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index 38193bf5ee..20e299de12 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -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 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 extends AbstractRowCountQuery + ctx.data().remove(DATA_DML_USING_TABLES); ctx.data().remove(DATA_DML_TARGET_TABLE); ctx.scopeEnd(); } diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java index a461067363..bb61ba8b11 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java @@ -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> 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; diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 8a5d3d28d9..8cbcc067b6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -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 extends AbstractResultQuery imp @Override public final void accept(Context ctx) { Table dmlTable; + List> 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 extends AbstractResultQuery 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>) 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()); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 99131002ed..c6ed87b905 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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> 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