From e523b43b7e394950126b379f87914ce57afed154 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 18 Aug 2021 16:36:03 +0200 Subject: [PATCH] [jOOQ/jOOQ#12328] Empty select() should not project asterisk if unknown table is used with leftSemiJoin() or leftAntiJoin() --- .../java/org/jooq/impl/DeleteQueryImpl.java | 2 +- .../java/org/jooq/impl/SelectQueryImpl.java | 15 +++++- jOOQ/src/main/java/org/jooq/impl/Tools.java | 49 ++++++++++++++----- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java index f32b20c391..c91153fa8e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java @@ -237,7 +237,7 @@ final class DeleteQueryImpl extends AbstractDMLQuery implem if (multiTableJoin) // No table declarations in this case, but references - ctx.visit(K_FROM).sql(' ').visit(traverseJoins(t, new TableList(), x -> false, (r, x) -> { r.add(x); return r; })).formatSeparator(); + ctx.visit(K_FROM).sql(' ').visit(traverseJoins(t, new TableList(), null, (r, x) -> { r.add(x); return r; })).formatSeparator(); diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index bf4360ac5e..c91c510ffb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -3815,7 +3815,20 @@ final class SelectQueryImpl extends AbstractResultQuery imp } private final boolean knownTableSource() { - return traverseJoins(getFrom(), true, r -> !r, (r, t) -> r && t.fieldsRow().size() > 0); + return traverseJoins( + getFrom(), + true, + r -> !r, + null, + + // [#12328] Don't recurse into the RHS if the join does not affect the projection + j -> j.type != JoinType.LEFT_ANTI_JOIN && j.type != JoinType.LEFT_SEMI_JOIN, + null, + + // TODO: PostgreSQL supports tables without columns, see e.g. + // https://blog.jooq.org/creating-tables-dum-and-dee-in-postgresql/ + (r, t) -> r && t.fieldsRow().size() > 0 + ); } @SuppressWarnings("unchecked") diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 7ebd27f092..f849c2f752 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -6260,7 +6260,7 @@ final class Tools { } static final void traverseJoins(Table t, Consumer> consumer) { - traverseJoins(t, null, x -> false, (result, x) -> { consumer.accept(x); return result; }); + traverseJoins(t, null, null, (result, x) -> { consumer.accept(x); return result; }); } static final T traverseJoins( @@ -6270,7 +6270,7 @@ final class Tools { BiFunction, ? extends T> function ) { for (Table t : i) - if (abort.test(result)) + if (abort != null && abort.test(result)) return result; else result = traverseJoins(t, result, abort, function); @@ -6284,33 +6284,58 @@ final class Tools { Predicate abort, BiFunction, ? extends T> function ) { - return traverseJoins(t, result, abort, null, function); + return traverseJoins(t, result, abort, null, null, null, function); + } + + static final T traverseJoins( + Iterable> i, + T result, + Predicate abort, + Predicate recurseLhs, + Predicate recurseRhs, + BiFunction joinTypeFunction, + BiFunction, ? extends T> tableFunction + ) { + for (Table t : i) + if (abort != null && abort.test(result)) + return result; + else + result = traverseJoins(t, result, abort, recurseLhs, recurseRhs, joinTypeFunction, tableFunction); + + return result; } static final T traverseJoins( Table t, T result, Predicate abort, + Predicate recurseLhs, + Predicate recurseRhs, BiFunction joinTypeFunction, BiFunction, ? extends T> tableFunction ) { - if (abort.test(result)) + if (abort != null && abort.test(result)) return result; if (t instanceof JoinTable) { - result = traverseJoins(((JoinTable) t).lhs, result, abort, joinTypeFunction, tableFunction); + JoinTable j = (JoinTable) t; - if (abort.test(result)) - return result; + if (recurseLhs == null || recurseLhs.test(j)) { + result = traverseJoins(j.lhs, result, abort, recurseLhs, recurseRhs, joinTypeFunction, tableFunction); - if (joinTypeFunction != null) { - result = joinTypeFunction.apply(result, ((JoinTable) t).type); - - if (abort.test(result)) + if (abort != null && abort.test(result)) return result; } - result = traverseJoins(((JoinTable) t).rhs, result, abort, joinTypeFunction, tableFunction); + if (joinTypeFunction != null) { + result = joinTypeFunction.apply(result, j.type); + + if (abort != null && abort.test(result)) + return result; + } + + if (recurseRhs == null || recurseRhs.test(j)) + result = traverseJoins(j.rhs, result, abort, recurseLhs, recurseRhs, joinTypeFunction, tableFunction); } else if (tableFunction != null) result = tableFunction.apply(result, t);