diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index 885ce3a614..81d77f85f4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -61,6 +61,7 @@ import static org.jooq.impl.DSL.sql; import static org.jooq.impl.DSL.table; import static org.jooq.impl.DSL.val; import static org.jooq.impl.Tools.EMPTY_FIELD; +import static org.jooq.impl.Tools.traverseJoins; import java.sql.Timestamp; import java.util.ArrayList; @@ -590,7 +591,7 @@ abstract class AbstractTable extends AbstractNamed implements List> result = new ArrayList<>(); for (ForeignKey reference : getReferences()) { - for (Table o : flattenJoins(other)) { + traverseJoins(other, o -> { if (o.equals(reference.getKey().getTable())) { result.add((ForeignKey) reference); } @@ -602,27 +603,12 @@ abstract class AbstractTable extends AbstractNamed implements if (aliased != null && aliased.equals(reference.getKey().getTable())) result.add((ForeignKey) reference); } - } + }); } return Collections.unmodifiableList(result); } - private final List> flattenJoins(Table other) { - return flattenJoins(other, new ArrayList<>()); - } - - private final List> flattenJoins(Table other, List> list) { - if (other instanceof JoinTable) { - flattenJoins(((JoinTable) other).lhs, list); - flattenJoins(((JoinTable) other).rhs, list); - } - else - list.add(other); - - return list; - } - /** * {@inheritDoc} *

diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index c28c9190d5..df17df1cc2 100755 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -102,12 +102,13 @@ import static org.jooq.impl.Keywords.K_PARTITION_BY; import static org.jooq.impl.Keywords.K_USING; import static org.jooq.impl.Names.N_JOIN; import static org.jooq.impl.QueryPartListView.wrap; +import static org.jooq.impl.Tools.search; +import static org.jooq.impl.Tools.traverseJoins; import static org.jooq.impl.Tools.BooleanDataKey.DATA_COLLECT_SEMI_ANTI_JOIN; import static org.jooq.impl.Tools.DataKey.DATA_COLLECTED_SEMI_ANTI_JOIN; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -665,7 +666,7 @@ implements unaliased.set(i, (TableField) alias.wrapped().field(f)); } - if (search(lhs, keyFields[0].getTable()) != null) { + if (traverseJoins(lhs, false, r -> r, search(keyFields[0].getTable()))) { for (ForeignKey key : lhs.getReferences()) if (key.getFields().containsAll(unaliased) && unaliased.containsAll(key.getFields())) return onKey(key); @@ -674,7 +675,7 @@ implements if (key.getFields().containsAll(unaliased)) return onKey(key); } - else if (search(rhs, keyFields[0].getTable()) != null) { + else if (traverseJoins(rhs, false, r -> r, search(keyFields[0].getTable()))) { for (ForeignKey key : rhs.getReferences()) if (key.getFields().containsAll(unaliased) && unaliased.containsAll(key.getFields())) return onKey(key); @@ -690,40 +691,14 @@ implements @Override public final JoinTable onKey(ForeignKey key) { - if (search(lhs, key.getTable()) != null) + if (traverseJoins(lhs, false, r -> r, search(key.getTable()))) return onKey(key, lhs, rhs); - else if (search(rhs, key.getTable()) != null) + else if (traverseJoins(rhs, false, r -> r, search(key.getTable()))) return onKey(key, rhs, lhs); throw onKeyException(OnKeyExceptionReason.NOT_FOUND, null, null); } - private final Table search(Table tree, Table search) { - - // [#6304] [#7626] Improved alias discovery - Alias> treeAlias = Tools.alias(tree); - Alias> searchAlias = Tools.alias(search); - - if (treeAlias != null || searchAlias != null) { - return search( - treeAlias != null ? treeAlias.wrapped() : tree, - searchAlias != null ? searchAlias.wrapped() : search - ); - } - else if (tree instanceof JoinTable) { - JoinTable t = (JoinTable) tree; - - Table r = search(t.lhs, search); - if (r == null) - r = search(t.rhs, search); - - return r; - } - else { - return search.equals(tree) ? tree : null; - } - } - @SuppressWarnings({ "unchecked", "rawtypes" }) private final JoinTable onKey(ForeignKey key, Table fk, Table pk) { JoinTable result = this; diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 4580fcb7bb..ed78d32ced 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -176,6 +176,7 @@ import static org.jooq.impl.Tools.hasAmbiguousNames; import static org.jooq.impl.Tools.isNotEmpty; import static org.jooq.impl.Tools.qualify; import static org.jooq.impl.Tools.recordType; +import static org.jooq.impl.Tools.search; import static org.jooq.impl.Tools.selectQueryImpl; import static org.jooq.impl.Tools.traverseConditions; import static org.jooq.impl.Tools.traverseJoins; @@ -199,6 +200,7 @@ import static org.jooq.impl.Tools.DataKey.DATA_SELECT_ALIASES; import static org.jooq.impl.Tools.DataKey.DATA_SELECT_INTO_TABLE; import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_CTE; import static org.jooq.impl.Tools.DataKey.DATA_WINDOW_DEFINITIONS; +import static org.jooq.tools.StringUtils.defaultIfNull; import java.sql.ResultSetMetaData; import java.util.ArrayDeque; @@ -606,7 +608,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp private final SelectQueryImpl nest(SelectQueryImpl result, SelectQueryImpl nested, Function, ? extends SelectQueryImpl> nestedFinisher) { - // [#10716] TODO: Qualify all fields with c1, c2, to avoid ambiguous + // [#10716] TODO: Qualify all fields with c1, c2, to avoid ambiguous fields in derived tables nested = nestedFinisher.apply(nested); Table t = nested.asTable("t"); traverseJoins(from, t0 -> result.localQueryPartMapping.put(t0, t)); @@ -1351,7 +1353,6 @@ final class SelectQueryImpl extends AbstractResultQuery imp - @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -3656,40 +3657,11 @@ final class SelectQueryImpl extends AbstractResultQuery imp } private final boolean knownTableSource() { - for (Table table : getFrom()) - if (!knownTable(table)) - return false; - - return true; - } - - private final boolean knownTable(Table table) { - if (table instanceof JoinTable) - return knownTable(((JoinTable) table).lhs) && knownTable(((JoinTable) table).rhs); - else - return table.fieldsRow().size() > 0; + return traverseJoins(getFrom(), true, r -> !r, (r, t) -> r && t.fieldsRow().size() > 0); } private final boolean containsTable(Table table) { - for (Table t : getFrom()) - if (containsTable(t, table)) - return true; - - return false; - } - - private final boolean containsTable(Table table, Table contained) { - Table alias; - - if ((alias = Tools.aliased(table)) != null) - return containsTable(alias, contained); - else if ((alias = Tools.aliased(contained)) != null) - return containsTable(table, alias); - else if (table instanceof JoinTable) - return containsTable(((JoinTable) table).lhs, contained) - || containsTable(((JoinTable) table).rhs, contained); - else - return contained.equals(table); + return traverseJoins(getFrom(), false, r -> r, search(table)); } @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 2b94c9eb9d..ed55741104 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -105,6 +105,7 @@ import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.val; import static org.jooq.impl.DefaultExecuteContext.localConnection; +import static org.jooq.impl.DefaultParseContext.SUPPORTS_HASH_COMMENT_SYNTAX; import static org.jooq.impl.Identifiers.QUOTES; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED; @@ -154,7 +155,6 @@ import static org.jooq.impl.Keywords.K_START_WITH; import static org.jooq.impl.Keywords.K_THEN; import static org.jooq.impl.Keywords.K_THROW; import static org.jooq.impl.Keywords.K_WHEN; -import static org.jooq.impl.DefaultParseContext.SUPPORTS_HASH_COMMENT_SYNTAX; import static org.jooq.impl.SQLDataType.BLOB; import static org.jooq.impl.SQLDataType.CLOB; import static org.jooq.impl.SQLDataType.JSON; @@ -217,7 +217,9 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ManagedBlocker; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -6093,18 +6095,62 @@ final class Tools { // TODO: In a new expression tree model, we'll support generic visitors of some sort // --------------------------------------------------------------------------------- + static final BiFunction, Boolean> search(Table table) { + return (r, t) -> { + + // [#6304] [#7626] Improved alias discovery + Table t1 = defaultIfNull(Tools.aliased(table), table); + Table t2 = defaultIfNull(Tools.aliased(t), t); + + return r || t1.equals(t2); + }; + } + static final void traverseJoins(Iterable> i, Consumer> consumer) { for (Table t : i) traverseJoins(t, consumer); } static final void traverseJoins(Table t, Consumer> consumer) { + traverseJoins(t, null, x -> false, (result, x) -> { consumer.accept(x); return result; }); + } + + static final T traverseJoins( + Iterable> i, + T result, + Predicate abort, + BiFunction, ? extends T> function + ) { + for (Table t : i) + if (abort.test(result)) + return result; + else + result = traverseJoins(t, result, abort, function); + + return result; + } + + static final T traverseJoins( + Table t, + T result, + Predicate abort, + BiFunction, ? extends T> function + ) { + if (abort.test(result)) + return result; + if (t instanceof JoinTable) { - traverseJoins(((JoinTable) t).lhs, consumer); - traverseJoins(((JoinTable) t).rhs, consumer); + result = traverseJoins(((JoinTable) t).lhs, result, abort, function); + + if (abort.test(result)) + return result; + + result = traverseJoins(((JoinTable) t).rhs, result, abort, function); } else - consumer.accept(t); + result = function.apply(result, t); + + return result; } static final void traverseSelectList(SelectFieldList list, Consumer> consumer) {