From 3d9e87f1c05695f8587efcd829685efa38243622 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 2 Jul 2021 16:51:39 +0200 Subject: [PATCH] [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 --- .../main/java/org/jooq/impl/AliasedSelect.java | 12 ++++++++---- .../main/java/org/jooq/impl/FetchCount.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Multiset.java | 18 ++++++++++++------ .../main/java/org/jooq/impl/MultisetAgg.java | 17 +++++++---------- .../impl/QuantifiedComparisonCondition.java | 2 +- .../src/main/java/org/jooq/impl/RowIsNull.java | 2 +- .../org/jooq/impl/RowSubqueryCondition.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Tools.java | 2 +- 8 files changed, 32 insertions(+), 25 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/AliasedSelect.java b/jOOQ/src/main/java/org/jooq/impl/AliasedSelect.java index 255e13aa08..7fd47654d1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AliasedSelect.java +++ b/jOOQ/src/main/java/org/jooq/impl/AliasedSelect.java @@ -63,17 +63,19 @@ final class AliasedSelect extends AbstractTable { private final Select query; private final boolean subquery; + private final boolean ignoreOrderBy; private final Name[] aliases; - AliasedSelect(Select query, boolean subquery) { - this(query, subquery, Tools.fieldNames(Tools.degree(query))); + AliasedSelect(Select query, boolean subquery, boolean ignoreOrderBy) { + this(query, subquery, ignoreOrderBy, Tools.fieldNames(Tools.degree(query))); } - AliasedSelect(Select query, boolean subquery, Name... aliases) { + AliasedSelect(Select 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 extends AbstractTable { public final Table as(Name alias) { SelectQueryImpl 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); diff --git a/jOOQ/src/main/java/org/jooq/impl/FetchCount.java b/jOOQ/src/main/java/org/jooq/impl/FetchCount.java index 7ed812c3e7..4db1483b7e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FetchCount.java +++ b/jOOQ/src/main/java/org/jooq/impl/FetchCount.java @@ -65,7 +65,7 @@ final class FetchCount extends AbstractResultQuery> { @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" }) diff --git a/jOOQ/src/main/java/org/jooq/impl/Multiset.java b/jOOQ/src/main/java/org/jooq/impl/Multiset.java index 93ab298717..4369a3a1cd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Multiset.java +++ b/jOOQ/src/main/java/org/jooq/impl/Multiset.java @@ -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 extends AbstractField> { - static final Set NO_SUPPORT_JSON_COMPARE = SQLDialect.supportedBy(POSTGRES); - static final Set NO_SUPPORT_XML_COMPARE = SQLDialect.supportedBy(POSTGRES); + static final Set NO_SUPPORT_JSON_COMPARE = SQLDialect.supportedBy(POSTGRES); + static final Set NO_SUPPORT_JSONB_COMPARE = SQLDialect.supportedBy(); + static final Set NO_SUPPORT_XML_COMPARE = SQLDialect.supportedBy(POSTGRES); final Select select; @@ -134,7 +137,7 @@ final class Multiset extends AbstractField> { 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 extends AbstractField> { } 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 extends AbstractField> { )).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 extends AbstractField> { case XML: { List> 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()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java b/jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java index 77be2286bc..42f26dee46 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java +++ b/jOOQ/src/main/java/org/jooq/impl/MultisetAgg.java @@ -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 extends DefaultAggregateFunction) 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; } diff --git a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java index f39f52dc0a..117906ad28 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java @@ -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> select = select(DSL.field(cond)).from(t); ctx.visit(lhs.eq(query.quantifier.apply(select))); } diff --git a/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java b/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java index 36b47131ed..0b7727614e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java @@ -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())))); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java index 3382368a4b..a6033c6d95 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java @@ -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)); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index b724040b98..da47e21835 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -5551,7 +5551,7 @@ final class Tools { List> 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)));