[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.
This commit is contained in:
Lukas Eder 2024-08-20 16:17:12 +02:00
parent 70187dbee8
commit 7ce935bbb5

View File

@ -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<R extends Record> extends AbstractResultQuery<R> 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<R extends Record> extends AbstractResultQuery<R> 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<Condition> 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<Condition> result, Table<?> t) {
addPathConditions(ctx, result, t, null);
}
private static final void addPathConditions(Context<?> ctx, Collection<Condition> result, Table<?> t, Predicate<? super TableImpl<?>> 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());
}
}