[jOOQ/jOOQ#5810] Emulate QUALIFY where not supported - WIP

This commit is contained in:
Lukas Eder 2021-03-18 10:33:30 +01:00
parent 4febe602d8
commit 66d875a922
4 changed files with 64 additions and 85 deletions

View File

@ -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<R extends Record> extends AbstractNamed implements
List<ForeignKey<R, O>> result = new ArrayList<>();
for (ForeignKey<R, ?> reference : getReferences()) {
for (Table<?> o : flattenJoins(other)) {
traverseJoins(other, o -> {
if (o.equals(reference.getKey().getTable())) {
result.add((ForeignKey<R, O>) reference);
}
@ -602,27 +603,12 @@ abstract class AbstractTable<R extends Record> extends AbstractNamed implements
if (aliased != null && aliased.equals(reference.getKey().getTable()))
result.add((ForeignKey<R, O>) reference);
}
}
});
}
return Collections.unmodifiableList(result);
}
private final List<Table<?>> flattenJoins(Table<?> other) {
return flattenJoins(other, new ArrayList<>());
}
private final List<Table<?>> flattenJoins(Table<?> other, List<Table<?>> list) {
if (other instanceof JoinTable) {
flattenJoins(((JoinTable) other).lhs, list);
flattenJoins(((JoinTable) other).rhs, list);
}
else
list.add(other);
return list;
}
/**
* {@inheritDoc}
* <p>

View File

@ -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<? extends Table<?>> treeAlias = Tools.alias(tree);
Alias<? extends Table<?>> 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;

View File

@ -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<R extends Record> extends AbstractResultQuery<R> imp
private final SelectQueryImpl<R> nest(SelectQueryImpl<R> result, SelectQueryImpl<R> nested, Function<? super SelectQueryImpl<R>, ? extends SelectQueryImpl<R>> 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<R> t = nested.asTable("t");
traverseJoins(from, t0 -> result.localQueryPartMapping.put(t0, t));
@ -1351,7 +1353,6 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@SuppressWarnings({ "rawtypes", "unchecked" })
@ -3656,40 +3657,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> 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")

View File

@ -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, Table<?>, 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<? extends Table<?>> i, Consumer<? super Table<?>> consumer) {
for (Table<?> t : i)
traverseJoins(t, consumer);
}
static final void traverseJoins(Table<?> t, Consumer<? super Table<?>> consumer) {
traverseJoins(t, null, x -> false, (result, x) -> { consumer.accept(x); return result; });
}
static final <T> T traverseJoins(
Iterable<? extends Table<?>> i,
T result,
Predicate<? super T> abort,
BiFunction<? super T, ? super Table<?>, ? 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> T traverseJoins(
Table<?> t,
T result,
Predicate<? super T> abort,
BiFunction<? super T, ? super Table<?>, ? 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<? super Field<?>> consumer) {