From 7ce935bbb5ef5f1a0e8783ec2aca75c0cf37e049 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 20 Aug 2024 16:17:12 +0200 Subject: [PATCH] [jOOQ/jOOQ#15936] De-duplicate join/path correlation predicates There are some edge cases where duplicate predicates are being generated from both the join tree (or table lists) and the path correlations. It's worth investigating why this happens rather than removing the duplicates with a Set, but for now this works. --- .../java/org/jooq/impl/SelectQueryImpl.java | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) 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()); } }