[jOOQ/jOOQ#11473] Use AliasedSelect instead of DerivedTable for MULTISET

A DerivedTable with table aliases produces a UNION subquery, and then we
run into this limitation in some dialects: [jOOQ/jOOQ#3791], where UNION
subqueries are not allowed to contain ORDER BY. The limitation can be
worked around by using AliasedSelect instead of a DerivedTable, though
again, there's a limitation that we can run into, though a less severe
one: [jOOQ/jOOQ#11473], where AliasedSelect doesn't work correctly yet
if original aliases (prior to re-aliasing) are referenced from ORDER BY
This commit is contained in:
Lukas Eder 2021-07-02 16:51:39 +02:00
parent 954e2acea5
commit 3d9e87f1c0
8 changed files with 32 additions and 25 deletions

View File

@ -63,17 +63,19 @@ final class AliasedSelect<R extends Record> extends AbstractTable<R> {
private final Select<R> query;
private final boolean subquery;
private final boolean ignoreOrderBy;
private final Name[] aliases;
AliasedSelect(Select<R> query, boolean subquery) {
this(query, subquery, Tools.fieldNames(Tools.degree(query)));
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy) {
this(query, subquery, ignoreOrderBy, Tools.fieldNames(Tools.degree(query)));
}
AliasedSelect(Select<R> query, boolean subquery, Name... aliases) {
AliasedSelect(Select<R> query, boolean subquery, boolean ignoreOrderBy, Name... aliases) {
super(TableOptions.expression(), N_SELECT);
this.query = query;
this.subquery = subquery;
this.ignoreOrderBy = ignoreOrderBy;
this.aliases = aliases;
}
@ -85,7 +87,9 @@ final class AliasedSelect<R extends Record> extends AbstractTable<R> {
public final Table<R> as(Name alias) {
SelectQueryImpl<R> q = selectQueryImpl(query);
if (q != null && (!q.getOrderBy().isEmpty() || Tools.hasEmbeddedFields(q.getSelect())))
// [#11473] In the presence of ORDER BY, AliasedSelect tends not to work
// correctly if ORDER BY references names available prior to the aliasing only
if (q != null && (ignoreOrderBy && !q.getOrderBy().isEmpty() || Tools.hasEmbeddedFields(q.getSelect())))
return query.asTable(alias, aliases);
else
return new TableAlias<>(this, alias, c -> true);

View File

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

View File

@ -65,6 +65,7 @@ import static org.jooq.impl.Tools.emulateMultiset;
import static org.jooq.impl.Tools.fieldName;
import static org.jooq.impl.Tools.fieldNameString;
import static org.jooq.impl.Tools.fieldNameStrings;
import static org.jooq.impl.Tools.fieldNames;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.visitSubquery;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONDITION;
@ -81,6 +82,7 @@ import org.jooq.JSONArrayAggOrderByStep;
import org.jooq.JSONArrayAggReturningStep;
import org.jooq.JSONArrayReturningStep;
import org.jooq.JSONB;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Result;
@ -96,8 +98,9 @@ import org.jooq.XMLAggOrderByStep;
*/
final class Multiset<R extends Record> extends AbstractField<Result<R>> {
static final Set<SQLDialect> NO_SUPPORT_JSON_COMPARE = SQLDialect.supportedBy(POSTGRES);
static final Set<SQLDialect> NO_SUPPORT_XML_COMPARE = SQLDialect.supportedBy(POSTGRES);
static final Set<SQLDialect> NO_SUPPORT_JSON_COMPARE = SQLDialect.supportedBy(POSTGRES);
static final Set<SQLDialect> NO_SUPPORT_JSONB_COMPARE = SQLDialect.supportedBy();
static final Set<SQLDialect> NO_SUPPORT_XML_COMPARE = SQLDialect.supportedBy(POSTGRES);
final Select<R> select;
@ -134,7 +137,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
private final void accept0(Context<?> ctx, boolean multisetCondition) {
switch (emulateMultiset(ctx.configuration())) {
case JSON: {
Table<?> t = select.asTable("t", fieldNameStrings(select.getSelect().size()));
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {
@ -177,7 +180,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
}
case JSONB: {
Table<?> t = select.asTable("t", fieldNameStrings(select.getSelect().size()));
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(select.getSelect().size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {
@ -207,7 +210,10 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
)).from(t)
);
visitSubquery(ctx, s, true);
if (multisetCondition && NO_SUPPORT_JSONB_COMPARE.contains(ctx.dialect()))
ctx.visit(DSL.field(s).cast(VARCHAR));
else
visitSubquery(ctx, s, true);
break;
}
}
@ -217,7 +223,7 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
case XML: {
List<Field<?>> fields = select.getSelect();
Table<?> t = select.asTable("t", fieldNameStrings(fields.size()));
Table<?> t = new AliasedSelect<>(select, true, false, fieldNames(fields.size())).as(DSL.name("t"), (Name[]) null);
switch (ctx.family()) {

View File

@ -39,12 +39,9 @@ package org.jooq.impl;
import static java.lang.Boolean.TRUE;
// ...
import static org.jooq.impl.DSL.jsonArrayAgg;
import static org.jooq.impl.DSL.jsonObject;
import static org.jooq.impl.DSL.jsonbArrayAgg;
import static org.jooq.impl.DSL.xmlagg;
import static org.jooq.impl.DSL.xmlelement;
import static org.jooq.impl.DSL.xmlserializeContent;
import static org.jooq.impl.Multiset.NO_SUPPORT_JSONB_COMPARE;
import static org.jooq.impl.Multiset.NO_SUPPORT_JSON_COMPARE;
import static org.jooq.impl.Multiset.NO_SUPPORT_XML_COMPARE;
import static org.jooq.impl.Multiset.jsonArrayaggEmulation;
@ -52,12 +49,9 @@ import static org.jooq.impl.Multiset.jsonbArrayaggEmulation;
import static org.jooq.impl.Multiset.returningClob;
import static org.jooq.impl.Multiset.xmlaggEmulation;
import static org.jooq.impl.Names.N_MULTISET_AGG;
import static org.jooq.impl.Names.N_RECORD;
import static org.jooq.impl.Names.N_RESULT;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.emulateMultiset;
import static org.jooq.impl.Tools.fieldName;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONDITION;
import org.jooq.Context;
@ -71,8 +65,6 @@ import org.jooq.SelectField;
import org.jooq.XML;
import org.jooq.XMLAggOrderByStep;
import org.jetbrains.annotations.NotNull;
/**
* @author Lukas Eder
*/
@ -134,7 +126,12 @@ final class MultisetAgg<R extends Record> extends DefaultAggregateFunction<Resul
? fo((AbstractAggregateFunction<?>) returningClob(ctx, order.orderBy(row.fields())))
: ofo((AbstractAggregateFunction<?>) returningClob(ctx, order));
ctx.visit(f);
if (multisetCondition && NO_SUPPORT_JSONB_COMPARE.contains(ctx.dialect()))
ctx.visit(f.cast(VARCHAR));
else
ctx.visit(f);
break;
}

View File

@ -212,7 +212,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, name("pattern")).as("t");
: new AliasedSelect<>(query.query, true, true, name("pattern")).as("t");
Select<Record1<Boolean>> select = select(DSL.field(cond)).from(t);
ctx.visit(lhs.eq(query.quantifier.apply(select)));
}

View File

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

View File

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

View File

@ -5551,7 +5551,7 @@ final class Tools {
List<Field<?>> result = collect(flattenCollection(select, false, false));
Name tableName = name("t");
Name[] fieldNames = fieldNames(result.size());
Table<?> t = new AliasedSelect<>(field.query, true, fieldNames).as("t");
Table<?> t = new AliasedSelect<>(field.query, true, true, 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)));