[jOOQ/jOOQ#13509] MySQL may ignore MULTISET subquery ORDER BY clause

This commit is contained in:
Lukas Eder 2022-05-03 11:29:32 +02:00
parent 86373d9897
commit 4f5ad3ce74
11 changed files with 44 additions and 18 deletions

View File

@ -299,7 +299,7 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart implements UEmp
// by pushing down projection aliases into the
// derived table
else
context.sql('(').visit(new AliasedSelect<>(wrappedAsSelect, true, false, fieldAliases)).sql(')');
context.sql('(').visit(new AliasedSelect<>(wrappedAsSelect, true, false, false, fieldAliases)).sql(')');
}
}
}

View File

@ -42,6 +42,7 @@ import static org.jooq.impl.DSL.selectFrom;
import static org.jooq.impl.Names.NQ_SELECT;
import static org.jooq.impl.Tools.selectQueryImpl;
import static org.jooq.impl.Tools.visitSubquery;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_LIMIT_WITH_ORDER_BY;
import static org.jooq.impl.Tools.SimpleDataKey.DATA_SELECT_ALIASES;
import org.jooq.Clause;
@ -65,18 +66,20 @@ final class AliasedSelect<R extends Record> extends AbstractTable<R> implements
private final Select<R> query;
private final boolean subquery;
private final boolean ignoreOrderBy;
private final boolean forceLimit;
private final Name[] aliases;
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy) {
this(query, subquery, ignoreOrderBy, Tools.fieldNames(Tools.degree(query)));
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy, boolean forceLimit) {
this(query, subquery, ignoreOrderBy, forceLimit, Tools.fieldNames(Tools.degree(query)));
}
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy, Name... aliases) {
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy, boolean forceLimit, Name... aliases) {
super(TableOptions.expression(), NQ_SELECT);
this.query = query;
this.subquery = subquery;
this.ignoreOrderBy = ignoreOrderBy;
this.forceLimit = forceLimit;
this.aliases = aliases;
}
@ -114,6 +117,13 @@ final class AliasedSelect<R extends Record> extends AbstractTable<R> implements
@Override
public final void accept(Context<?> ctx) {
if (forceLimit)
ctx.data(DATA_FORCE_LIMIT_WITH_ORDER_BY, true, c -> accept0(c));
else
accept0(ctx);
}
private final void accept0(Context<?> ctx) {
SelectQueryImpl<R> q = selectQueryImpl(query);
// [#3679] [#10540] Without standardised UNION subquery column names,

View File

@ -65,7 +65,7 @@ final class FetchCount extends AbstractResultQuery<Record1<Integer>> implements
@Override
public final void accept(Context<?> ctx) {
ctx.visit(select(count).from(new AliasedSelect<>(query, true, true).as("t")));
ctx.visit(select(count).from(new AliasedSelect<>(query, true, true, false).as("t")));
}
@SuppressWarnings({ "unchecked", "rawtypes" })

View File

@ -853,7 +853,7 @@ implements
// [#6375] INSERT .. VALUES and INSERT .. SELECT distinction also in MERGE
if (NO_SUPPORT_DERIVED_COLUMN_LIST_IN_MERGE_USING.contains(ctx.dialect()))
t = new AliasedSelect<Record>((Select<Record>) s, true, true, map(f, Field::getUnqualifiedName, Name[]::new)).as("t");
t = new AliasedSelect<Record>((Select<Record>) s, true, true, false, map(f, Field::getUnqualifiedName, Name[]::new)).as("t");
else
t = s.asTable("t", map(f, Field::getName, String[]::new));
}

View File

@ -40,16 +40,15 @@ package org.jooq.impl;
import static java.lang.Boolean.TRUE;
// ...
// ...
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.jsonArray;
import static org.jooq.impl.DSL.jsonEntry;
import static org.jooq.impl.DSL.jsonbArray;
import static org.jooq.impl.DSL.quotedName;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.selectFrom;
import static org.jooq.impl.DSL.when;
@ -154,7 +153,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
private final void accept0(Context<?> ctx, boolean multisetCondition) {
switch (emulateMultiset(ctx.configuration())) {
case JSON: {
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
Table<?> t = new AliasedSelect<>(select, true, false, ctx.family() == MYSQL, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {
@ -207,7 +206,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
}
case JSONB: {
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
Table<?> t = new AliasedSelect<>(select, true, false, ctx.family() == MYSQL, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {
@ -262,7 +261,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
case XML: {
List<Field<?>> fields = select.getSelect();
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(fields.size())).as(DSL.name("t"), (Name[]) null);
Table<?> t = new AliasedSelect<>(select, true, false, ctx.family() == MYSQL, fieldNames(fields.size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {

View File

@ -214,7 +214,7 @@ final class QuantifiedComparisonCondition extends AbstractCondition implements L
Table<?> t = query.array != null
? new ArrayTable(query.array).asTable("t", "pattern")
: new AliasedSelect<>(query.query, true, true, name("pattern")).as("t");
: new AliasedSelect<>(query.query, true, true, false, name("pattern")).as("t");
Select<Record1<Boolean>> select = select(DSL.field(cond)).from(t);
ctx.visit(lhs.eq(query.quantifier.apply(select)));
}

View File

@ -190,7 +190,7 @@ final class RowSubqueryCondition extends AbstractCondition implements UNotYetImp
Name[] names = fieldNames(l.size());
return select()
.from(new AliasedSelect<>(s, true, true, names).as(table))
.from(new AliasedSelect<>(s, true, true, false, names).as(table))
.where(c == null
? noCondition()
: new RowCondition(l, row(fieldsByName(table, names)), c));

View File

@ -81,7 +81,7 @@ final class SelectIsNotNull extends AbstractCondition implements QOM.SelectIsNot
acceptStandard(ctx);
}
else {
Table<?> t = new AliasedSelect<>(select, true, true).as("t");
Table<?> t = new AliasedSelect<>(select, true, true, false).as("t");
ctx.visit(inline(1).eq(selectCount().from(t).where(allNotNull(t.fields()))));
}
}

View File

@ -115,7 +115,7 @@ final class SelectIsNull extends AbstractCondition implements QOM.SelectIsNull {
acceptStandard(ctx);
}
else {
Table<?> t = new AliasedSelect<>(select, true, true).as("t");
Table<?> t = new AliasedSelect<>(select, true, true, false).as("t");
ctx.visit(inline(1).eq(selectCount().from(t).where(allNull(t.fields()))));
}
}

View File

@ -207,6 +207,7 @@ import static org.jooq.impl.Tools.traverseJoins;
import static org.jooq.impl.Tools.unalias;
import static org.jooq.impl.Tools.unqualified;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_COLLECT_SEMI_ANTI_JOIN;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_LIMIT_WITH_ORDER_BY;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_INSERT_SELECT;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_INSERT_SELECT_WITHOUT_INSERT_COLUMN_LIST;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_NESTED_SET_OPERATIONS;
@ -3039,8 +3040,16 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
.formatNewLine()
.sql(") x");
if (TRUE.equals(ctx.data().get(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE)) && actualLimit.isApplicable())
ctx.visit(actualLimit);
if (TRUE.equals(ctx.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE))) {
if (actualLimit.isApplicable()) {
ctx.visit(actualLimit);
}
else if (!actualOrderBy.isEmpty() && TRUE.equals(ctx.data(DATA_FORCE_LIMIT_WITH_ORDER_BY))) {
Limit l = new Limit();
l.setLimit(Integer.MAX_VALUE);
ctx.visit(l);
}
}
}
private final boolean applySeekOnDerivedTable() {

View File

@ -557,6 +557,14 @@ final class Tools {
*/
DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE,
/**
* [#13509] In some cases, it may be desirable to enforce appending a
* <code>LIMIT</code> clause, when there's an <code>ORDER BY</code>
* clause, e.g. to prevent the optimiser from removing the seemingly
* unnecessary sort.
*/
DATA_FORCE_LIMIT_WITH_ORDER_BY,
/**
* [#3886] Whether a list has already been indented.
*/
@ -6146,7 +6154,7 @@ final class Tools {
List<Field<?>> result = collect(flattenCollection(select));
Name tableName = name("t");
Name[] fieldNames = fieldNames(result.size());
Table<?> t = new AliasedSelect<>(field.query, true, true, fieldNames).as("t");
Table<?> t = new AliasedSelect<>(field.query, true, true, false, fieldNames).as("t");
for (int i = 0; i < result.size(); i++)
result.set(i, DSL.field(DSL.select(DSL.field(tableName.append(fieldNames[i]), result.get(i).getDataType())).from(t)));