[jOOQ/jOOQ#16928] Bad OFFSET emulation in UNION queries when OFFSET
contains an expression
This commit is contained in:
parent
81f9c081d0
commit
77a0f967a8
@ -40,27 +40,23 @@ package org.jooq.impl;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.jooq.JoinType.JOIN;
|
||||
import static org.jooq.JoinType.LEFT_OUTER_JOIN;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.conf.InvocationOrder.REVERSE;
|
||||
import static org.jooq.conf.ParamType.INDEXED;
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.JoinTable.onKey0;
|
||||
import static org.jooq.impl.TableFieldImpl.implicitJoinAsScalarSubquery;
|
||||
import static org.jooq.impl.Tools.DATAKEY_RESET_IN_SUBQUERY_SCOPE;
|
||||
import static org.jooq.impl.Tools.EMPTY_CLAUSE;
|
||||
import static org.jooq.impl.Tools.EMPTY_QUERYPART;
|
||||
import static org.jooq.impl.Tools.lazy;
|
||||
import static org.jooq.impl.Tools.traverseJoins;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONDITION;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_NESTED_SET_OPERATIONS;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_OMIT_CLAUSE_EVENT_EMISSION;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNQUALIFY_LOCAL_SCOPE;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_RENDER_IMPLICIT_JOIN;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASED_EXPRESSIONS;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNQUALIFY_LOCAL_SCOPE;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_OVERRIDE_ALIASES_IN_ORDER_BY;
|
||||
import static org.jooq.tools.StringUtils.defaultIfNull;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
@ -76,7 +72,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jooq.BindContext;
|
||||
@ -110,7 +105,6 @@ import org.jooq.conf.SettingsTools;
|
||||
import org.jooq.conf.StatementType;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.impl.QOM.UEmpty;
|
||||
import org.jooq.impl.Tools.BooleanDataKey;
|
||||
import org.jooq.impl.Tools.DataKey;
|
||||
|
||||
|
||||
@ -120,11 +114,6 @@ import org.jooq.impl.Tools.DataKey;
|
||||
@SuppressWarnings("unchecked")
|
||||
abstract class AbstractContext<C extends Context<C>> extends AbstractScope implements Context<C> {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
final ExecuteContext ctx;
|
||||
final PreparedStatement stmt;
|
||||
|
||||
@ -181,28 +170,15 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
|
||||
VisitListenerProvider[] providers = configuration.visitListenerProviders();
|
||||
|
||||
// [#2080] [#3935] Currently, the InternalVisitListener is not used everywhere
|
||||
boolean useInternalVisitListener =
|
||||
false
|
||||
|
||||
|
||||
|
||||
;
|
||||
|
||||
// [#6758] Avoid this allocation if unneeded
|
||||
VisitListener[] visitListeners = providers.length > 0 || useInternalVisitListener
|
||||
? new VisitListener[providers.length + (useInternalVisitListener ? 1 : 0)]
|
||||
VisitListener[] visitListeners = providers.length > 0
|
||||
? new VisitListener[providers.length]
|
||||
: null;
|
||||
|
||||
if (visitListeners != null) {
|
||||
for (int i = 0; i < providers.length; i++)
|
||||
visitListeners[i] = providers[i].provide();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
this.visitContext = new DefaultVisitContext();
|
||||
this.visitParts = new ArrayDeque<>();
|
||||
this.visitClauses = new ArrayDeque<>();
|
||||
@ -310,6 +286,10 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
}
|
||||
}
|
||||
|
||||
// [#16928] Apply type specific replacements that can't be implemented in individual types,
|
||||
// and for which an internal VisitListener is overkill
|
||||
part = typeSpecificReplacements(part);
|
||||
|
||||
// Issue start clause events
|
||||
// -----------------------------------------------------------------
|
||||
Clause[] clauses = Tools.isNotEmpty(visitListenersStart) ? clause(part) : null;
|
||||
@ -396,6 +376,26 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
static final record AliasOverride(List<Field<?>> originalFields, List<Field<?>> aliasedFields) {}
|
||||
|
||||
private final QueryPart typeSpecificReplacements(QueryPart part) {
|
||||
if (!declareFields() && part instanceof Field) {
|
||||
|
||||
// [#2080] Override the actual alias in case a synthetic alias is generated
|
||||
// in the SELECT clause
|
||||
AliasOverride override = (AliasOverride) data(DATA_OVERRIDE_ALIASES_IN_ORDER_BY);
|
||||
|
||||
// Don't combine the effects of DATA_OVERRIDE_ALIASES_IN_ORDER_BY with DATA_UNALIAS_ALIASES_IN_ORDER_BY
|
||||
if (override != null && !TRUE.equals(data(DATA_UNALIAS_ALIASED_EXPRESSIONS))) {
|
||||
for (int i = 0; i < override.originalFields().size(); i++)
|
||||
if (part.equals(override.originalFields().get(i)))
|
||||
part = field(name(override.aliasedFields().get(i).getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final C visitSubquery(QueryPart part) {
|
||||
Tools.visitSubquery(this, part);
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASED_EXPRESSIONS;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_OVERRIDE_ALIASES_IN_ORDER_BY;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Field;
|
||||
// ...
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.VisitContext;
|
||||
import org.jooq.VisitListener;
|
||||
|
||||
/**
|
||||
* A {@link VisitListener} used by jOOQ internally, to implement some useful
|
||||
* features.
|
||||
* <p>
|
||||
* Features implemented are:
|
||||
* <h3>[#2790] Keep a locally scoped data map, scoped for the current subquery</h3>
|
||||
* <p>
|
||||
* Sometimes, it is useful to have some information only available while
|
||||
* visiting QueryParts in the same context of the current subquery, e.g. when
|
||||
* communicating between SELECT and WINDOW clause, as is required to emulate
|
||||
* [#531].
|
||||
* </p>
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class InternalVisitListener implements VisitListener {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -324,6 +324,7 @@ import org.jooq.WindowDefinition;
|
||||
import org.jooq.XML;
|
||||
import org.jooq.conf.AutoAliasExpressions;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.impl.AbstractContext.AliasOverride;
|
||||
import org.jooq.impl.ForLock.ForLockMode;
|
||||
import org.jooq.impl.ForLock.ForLockWaitMode;
|
||||
import org.jooq.impl.QOM.CompareCondition;
|
||||
@ -1798,10 +1799,12 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
try {
|
||||
List<Field<?>> originalFields = null;
|
||||
List<Field<?>> alternativeFields = null;
|
||||
AliasOverride aliasOverride = null;
|
||||
|
||||
if (selectAliases != null) {
|
||||
List<Field<?>> originalFields = null;
|
||||
List<Field<?>> alternativeFields = null;
|
||||
|
||||
context.data().remove(DATA_SELECT_ALIASES);
|
||||
|
||||
|
||||
@ -1810,6 +1813,8 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
alternativeFields = map(originalFields = getSelect(),
|
||||
(f, i) -> i < a.length && a[i] != null ? f.as(a[i]) : f
|
||||
);
|
||||
|
||||
aliasOverride = new AliasOverride(originalFields, alternativeFields);
|
||||
}
|
||||
|
||||
if (TRUE.equals(renderTrailingLimit))
|
||||
@ -1950,13 +1955,13 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
case MARIADB: {
|
||||
if (getLimit().isApplicable() && getLimit().isExpression())
|
||||
toSQLReferenceLimitWithWindowFunctions(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitWithWindowFunctions(context, aliasOverride);
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1966,7 +1971,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1977,18 +1982,18 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
case FIREBIRD:
|
||||
case MYSQL: {
|
||||
if (getLimit().isApplicable() && (getLimit().withTies() || getLimit().isExpression()))
|
||||
toSQLReferenceLimitWithWindowFunctions(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitWithWindowFunctions(context, aliasOverride);
|
||||
else
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TRINO: {
|
||||
if (getLimit().isApplicable() && getLimit().isExpression())
|
||||
toSQLReferenceLimitWithWindowFunctions(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitWithWindowFunctions(context, aliasOverride);
|
||||
else
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -2000,16 +2005,16 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
case DUCKDB:
|
||||
case YUGABYTEDB: {
|
||||
if (getLimit().isApplicable() && getLimit().withTies())
|
||||
toSQLReferenceLimitWithWindowFunctions(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitWithWindowFunctions(context, aliasOverride);
|
||||
else
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// By default, render the dialect's limit clause
|
||||
default: {
|
||||
toSQLReferenceLimitDefault(context, originalFields, alternativeFields);
|
||||
toSQLReferenceLimitDefault(context, aliasOverride);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2113,26 +2118,26 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
/**
|
||||
* The default LIMIT / OFFSET clause in most dialects
|
||||
*/
|
||||
private final void toSQLReferenceLimitDefault(Context<?> context, List<Field<?>> originalFields, List<Field<?>> alternativeFields) {
|
||||
context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, true, c -> toSQLReference0(context, originalFields, alternativeFields, null));
|
||||
private final void toSQLReferenceLimitDefault(Context<?> context, AliasOverride aliasOverride) {
|
||||
context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, true, c -> toSQLReference0(context, aliasOverride, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Omit rendering any limit clause
|
||||
*/
|
||||
private final void toSQLReferenceQualifyInsteadOfLimit(Context<?> context, List<Field<?>> originalFields, List<Field<?>> alternativeFields) {
|
||||
context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, false, c -> toSQLReference0(context, originalFields, alternativeFields, limitWindowFunctionCondition(limitWindowFunction(context))));
|
||||
private final void toSQLReferenceQualifyInsteadOfLimit(Context<?> context, AliasOverride aliasOverride) {
|
||||
context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, false, c -> toSQLReference0(context, aliasOverride, limitWindowFunctionCondition(limitWindowFunction(context))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulate the LIMIT / OFFSET clause using window functions, specifically
|
||||
* when the WITH TIES clause is specified.
|
||||
*/
|
||||
private final void toSQLReferenceLimitWithWindowFunctions(Context<?> ctx, List<Field<?>> originalFields, List<Field<?>> alternativeFields) {
|
||||
private final void toSQLReferenceLimitWithWindowFunctions(Context<?> ctx, AliasOverride aliasOverride) {
|
||||
if (Transformations.EMULATE_QUALIFY.contains(ctx.dialect()) || getQualify().hasWhere())
|
||||
toSQLReferenceLimitWithWindowFunctions0(ctx);
|
||||
else
|
||||
toSQLReferenceQualifyInsteadOfLimit(ctx, originalFields, alternativeFields);
|
||||
toSQLReferenceQualifyInsteadOfLimit(ctx, aliasOverride);
|
||||
}
|
||||
|
||||
|
||||
@ -2143,15 +2148,15 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
// AUTHOR.ID as v1, BOOK.ID as v2, BOOK.TITLE as v3
|
||||
// Enforce x.* or just * if we have no known field names (e.g. when plain SQL tables are involved)
|
||||
final List<Field<?>> alternativeFields = new ArrayList<>(originalFields.size());
|
||||
final List<Field<?>> aliasedFields = new ArrayList<>(originalFields.size());
|
||||
|
||||
if (originalFields.isEmpty())
|
||||
alternativeFields.add(DSL.field("*"));
|
||||
aliasedFields.add(DSL.field("*"));
|
||||
else
|
||||
alternativeFields.addAll(aliasedFields(originalFields));
|
||||
aliasedFields.addAll(aliasedFields(originalFields));
|
||||
|
||||
alternativeFields.add(CustomField.of("rn", SQLDataType.INTEGER, c -> {
|
||||
boolean wrapQueryExpressionBodyInDerivedTable = wrapQueryExpressionBodyInDerivedTable(c);
|
||||
aliasedFields.add(CustomField.of("rn", SQLDataType.INTEGER, c -> {
|
||||
boolean wrapQueryExpressionBodyInDerivedTable = wrapQueryExpressionBodyInDerivedTable(c, true);
|
||||
|
||||
// [#3575] Ensure that no column aliases from the surrounding SELECT clause
|
||||
// are referenced from the below ranking functions' ORDER BY clause.
|
||||
@ -2159,7 +2164,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
boolean q = c.qualify();
|
||||
|
||||
c.data(DATA_OVERRIDE_ALIASES_IN_ORDER_BY, new Object[] { originalFields, alternativeFields });
|
||||
c.data(DATA_OVERRIDE_ALIASES_IN_ORDER_BY, new AliasOverride(originalFields, aliasedFields));
|
||||
if (wrapQueryExpressionBodyInDerivedTable)
|
||||
c.qualify(false);
|
||||
|
||||
@ -2190,7 +2195,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
.visit(K_FROM).sqlIndentStart(" (")
|
||||
.subquery(true);
|
||||
|
||||
toSQLReference0(ctx, originalFields, alternativeFields, null);
|
||||
toSQLReference0(ctx, new AliasOverride(originalFields, aliasedFields), null);
|
||||
|
||||
ctx.subquery(false)
|
||||
.sqlIndentEnd(") ")
|
||||
@ -2307,8 +2312,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
@SuppressWarnings("unchecked")
|
||||
private final void toSQLReference0(
|
||||
Context<?> context,
|
||||
List<Field<?>> originalFields,
|
||||
List<Field<?>> alternativeFields,
|
||||
AliasOverride aliasOverride,
|
||||
Condition additionalQualify
|
||||
) {
|
||||
SQLDialect family = context.family();
|
||||
@ -2362,7 +2366,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|| wrapQueryExpressionBodyInDerivedTable(context, aliasOverride != null)
|
||||
|
||||
// [#7459] In the presence of UNIONs and other set operations, the SEEK
|
||||
// predicate must be applied on a derived table, not on the individual subqueries
|
||||
@ -2379,10 +2383,10 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
.formatNewLine()
|
||||
.sql("t.*");
|
||||
|
||||
if (alternativeFields != null && originalFields.size() < alternativeFields.size())
|
||||
if (aliasOverride != null && aliasOverride.originalFields().size() < aliasOverride.aliasedFields().size())
|
||||
context.sql(", ")
|
||||
.formatSeparator()
|
||||
.declareFields(true, c -> c.visit(alternativeFields.get(alternativeFields.size() - 1)));
|
||||
.declareFields(true, c -> c.visit(aliasOverride.aliasedFields().get(aliasOverride.aliasedFields().size() - 1)));
|
||||
|
||||
context.formatIndentEnd()
|
||||
.formatSeparator()
|
||||
@ -2413,7 +2417,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
unionParenthesis(
|
||||
context,
|
||||
'(',
|
||||
alternativeFields != null ? alternativeFields : getSelect(),
|
||||
aliasOverride != null ? aliasOverride.aliasedFields() : getSelect(),
|
||||
derivedTableRequired(context, this),
|
||||
unionParensRequired = unionOpNesting || unionParensRequired(context),
|
||||
null
|
||||
@ -2488,11 +2492,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
// [#2335] When emulating LIMIT .. OFFSET, the SELECT clause needs to generate
|
||||
// non-ambiguous column names as ambiguous column names are not allowed in subqueries
|
||||
if (alternativeFields != null)
|
||||
if (wrapQueryExpressionBodyInDerivedTable && originalFields.size() < alternativeFields.size())
|
||||
context.visit(new SelectFieldList<>(alternativeFields.subList(0, originalFields.size())));
|
||||
if (aliasOverride != null)
|
||||
if (wrapQueryExpressionBodyInDerivedTable && aliasOverride.originalFields().size() < aliasOverride.aliasedFields().size())
|
||||
context.visit(new SelectFieldList<>(aliasOverride.aliasedFields().subList(0, aliasOverride.originalFields().size())));
|
||||
else
|
||||
context.visit(new SelectFieldList<>(alternativeFields));
|
||||
context.visit(new SelectFieldList<>(aliasOverride.aliasedFields()));
|
||||
|
||||
// The default behaviour
|
||||
else
|
||||
@ -2762,8 +2766,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
// ORDER BY clause for local subselect
|
||||
// -----------------------------------
|
||||
toSQLOrderBy(
|
||||
context,
|
||||
originalFields, alternativeFields,
|
||||
context, aliasOverride,
|
||||
false, wrapQueryExpressionBodyInDerivedTable,
|
||||
false, orderBy, limit
|
||||
);
|
||||
@ -2827,8 +2830,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
// ORDER BY clause for UNION
|
||||
// -------------------------
|
||||
context.qualify(false, c -> toSQLOrderBy(
|
||||
context,
|
||||
originalFields, alternativeFields,
|
||||
context, aliasOverride,
|
||||
wrapQueryExpressionInDerivedTable, wrapQueryExpressionBodyInDerivedTable,
|
||||
true, unionOrderBy, unionLimit
|
||||
));
|
||||
@ -3425,8 +3427,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
@SuppressWarnings("unchecked")
|
||||
private final void toSQLOrderBy(
|
||||
final Context<?> ctx,
|
||||
final List<Field<?>> originalFields,
|
||||
final List<Field<?>> alternativeFields,
|
||||
final AliasOverride aliasOverride,
|
||||
final boolean wrapQueryExpressionInDerivedTable,
|
||||
final boolean wrapQueryExpressionBodyInDerivedTable,
|
||||
final boolean isUnionOrderBy,
|
||||
@ -3608,11 +3609,16 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
return !getSeek().isEmpty() && !getOrderBy().isEmpty() && !unionOp.isEmpty();
|
||||
}
|
||||
|
||||
private final boolean wrapQueryExpressionBodyInDerivedTable(Context<?> ctx) {
|
||||
private final boolean wrapQueryExpressionBodyInDerivedTable(Context<?> ctx, boolean hasAlternativeFields) {
|
||||
|
||||
// [#2059] [#7539] Some dialects require query in derived table when using ORDER BY
|
||||
return !unionOp.isEmpty() && (
|
||||
WRAP_EXP_BODY_IN_DERIVED_TABLE_LIMIT.contains(ctx.dialect()) && getLimit().isApplicable()
|
||||
|
||||
// [#16928] "Alternative fields" such as ROW_NUMBER() calculations only work with UNIONs when the
|
||||
// UNION is nested.
|
||||
hasAlternativeFields
|
||||
|| WRAP_EXP_BODY_IN_DERIVED_TABLE_LIMIT.contains(ctx.dialect()) && getLimit().isApplicable()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user