diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 416ef3ef07..67a1cfb438 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -264,6 +264,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -271,6 +272,7 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import org.jooq.Asterisk; import org.jooq.Clause; @@ -2983,19 +2985,6 @@ final class SelectQueryImpl extends AbstractResultQuery imp Table t0 = result.get(i); Table t1 = prependPathJoins(ctx, where, t0, CROSS_JOIN); - // [#15936] If join tree leaves contain paths, generate the path correlation predicate here - // [#15967] But only if they're in scope, and not in the current scope - traverseJoins(t1, where, null, l -> true, r -> true, null, - (w, t) -> { - if (TableImpl.path(t) != null && t instanceof TableImpl ti) { - if (ctx.inScope(ti.path) && !ctx.inCurrentScope(ti.path)) - w.addConditions(ti.pathCondition()); - } - - return w; - } - ); - if (t0 != t1) result.set(i, t1); } @@ -4556,31 +4545,49 @@ final class SelectQueryImpl extends AbstractResultQuery imp static final void addPathConditions(Context ctx, ConditionProviderImpl where0, TableList tablelist) { + // [#15936] Avoid duplicate conditions produced by join tree and path correlation + // TODO: This de-duplication shouldn't be necessary: find the cause. + Set set = new LinkedHashSet<>(); + // [#13639] Add JOIN predicates generated by path expressions in FROM clause. for (Table t : tablelist) - addPathConditions(ctx, where0, t); + addPathConditions(ctx, set, t); traverseJoins( - tablelist, null, null, + tablelist, set, null, t -> { if (t instanceof CrossJoin j) { - addPathConditions(ctx, where0, j.lhs); - addPathConditions(ctx, where0, j.rhs); + addPathConditions(ctx, set, j.lhs); + addPathConditions(ctx, set, j.rhs); } return true; }, - null, null, null + null, null, + + // [#15936] If join tree leaves contain paths, generate the path correlation predicate here + // [#15967] But only if they're in scope, and not in the current scope, in a subquery + !ctx.subquery() ? null : + (w, t) -> { + addPathConditions(ctx, w, t, ti -> !ctx.inCurrentScope(ti.path)); + return w; + } ); + + where0.addConditions(set); } - private static final void addPathConditions(Context ctx, ConditionProviderImpl result, Table t) { + private static final void addPathConditions(Context ctx, Collection result, Table t) { + addPathConditions(ctx, result, t, null); + } + + private static final void addPathConditions(Context ctx, Collection result, Table t, Predicate> predicate) { if (t instanceof TableImpl ti) { // [#14985] [#15967] In some edge cases, users may have chosen to use a path in the FROM // clause, whose path root isn't in scope. In that case, we must ignore // the path. - if (ti.path != null && ctx.inScope(ti.path)) - result.addConditions(ti.pathCondition()); + if (ti.path != null && ctx.inScope(ti.path) && (predicate == null || predicate.test(ti))) + result.add(ti.pathCondition()); } }