This includes:

- [jOOQ/jOOQ#16121] Add Context.scopeParts(Class<Q>): Iterable<Q> to
allow for iterating over the values that are currently in scope
- [jOOQ/jOOQ#15998] RenderTable.WHEN_AMBIGUOUS_COLUMNS doesn't cover all
ambiguous column name cases
- [jOOQ/jOOQ#15996] RenderTable.WHEN_MULTIPLE_TABLES doesn't work
correctly in correlated subqueries
This commit is contained in:
Lukas Eder 2024-01-22 17:49:59 +01:00
parent e6aaf25bbd
commit 1f07cdfe6d
5 changed files with 108 additions and 15 deletions

View File

@ -428,6 +428,12 @@ public interface Context<C extends Context<C>> extends ExecuteScope {
@NotNull
C scopeRegister(QueryPart part, boolean forceNew, QueryPart mapped);
/**
* Get all values of a type that are in the current scope or higher.
*/
@NotNull
<Q extends QueryPart> Iterable<Q> scopeParts(Class<? extends Q> type);
/**
* Check whether a query part is registered in the current scope or higher.
*/

View File

@ -870,6 +870,11 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
return (C) this;
}
@Override
public final <Q extends QueryPart> Iterable<Q> scopeParts(Class<? extends Q> type) {
return (Iterable<Q>) scopeStack.keyIterable(k -> type.isInstance(k));
}
@Override
public final boolean inScope(QueryPart part) {
return scopeStack.get(part) != null;

View File

@ -44,6 +44,8 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -98,7 +100,7 @@ final class ScopeStack<K, V> implements Iterable<V> {
}
final Iterable<Value<V>> valueIterable() {
return () -> new ScopeStackIterator<Value<V>>(Value::lastOf, e -> true);
return () -> new ScopeStackIterator<Value<V>>(e -> Value.lastOf(e.getValue()), e -> true);
}
@Override
@ -107,11 +109,19 @@ final class ScopeStack<K, V> implements Iterable<V> {
}
final Iterable<V> iterableAtScopeLevel() {
return () -> new ScopeStackIterator<>(list -> list.size() == scopeLevel + 1 ? list.get(scopeLevel) : null, e -> true);
return () -> new ScopeStackIterator<>((k, v) -> v.size() == scopeLevel + 1 ? v.get(scopeLevel) : null, e -> true);
}
final Iterable<V> iterable(Predicate<? super V> filter) {
return () -> new ScopeStackIterator<>(list -> list.get(list.size() - 1), filter);
return () -> new ScopeStackIterator<>((k, v) -> v.get(v.size() - 1), filter);
}
final Iterable<K> keyIterableAtScopeLevel() {
return () -> new ScopeStackIterator<>((k, v) -> v.size() == scopeLevel + 1 ? k : null, e -> true);
}
final Iterable<K> keyIterable(Predicate<? super K> filter) {
return () -> new ScopeStackIterator<>((k, v) -> k, filter);
}
static final record Value<V>(int scopeLevel, V value) {
@ -127,12 +137,16 @@ final class ScopeStack<K, V> implements Iterable<V> {
}
private final class ScopeStackIterator<U> implements Iterator<U> {
final Iterator<List<V>> it = stack().values().iterator();
final Function<List<V>, U> valueExtractor;
final Predicate<? super U> filter;
U next;
final Iterator<Entry<K, List<V>>> it = stack().entrySet().iterator();
final Function<Entry<K, List<V>>, U> valueExtractor;
final Predicate<? super U> filter;
U next;
ScopeStackIterator(Function<List<V>, U> valueExtractor, Predicate<? super U> filter) {
ScopeStackIterator(BiFunction<K, List<V>, U> valueExtractor, Predicate<? super U> filter) {
this(e -> valueExtractor.apply(e.getKey(), e.getValue()), filter);
}
ScopeStackIterator(Function<Entry<K, List<V>>, U> valueExtractor, Predicate<? super U> filter) {
this.valueExtractor = valueExtractor;
this.filter = filter;
}
@ -156,8 +170,8 @@ final class ScopeStack<K, V> implements Iterable<V> {
private U move() {
for (
List<V> list;
it.hasNext() && ((list = it.next()).isEmpty() || ((next = valueExtractor.apply(list)) == null) || !filter.test(next));
Entry<K, List<V>> e;
it.hasNext() && ((e = it.next()).getValue().isEmpty() || ((next = valueExtractor.apply(e)) == null) || !filter.test(next));
next = null
);

View File

@ -206,12 +206,15 @@ import static org.jooq.impl.Tools.allMatch;
import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.autoAlias;
import static org.jooq.impl.Tools.camelCase;
import static org.jooq.impl.Tools.concat;
import static org.jooq.impl.Tools.containsUnaliasedTable;
import static org.jooq.impl.Tools.fieldArray;
import static org.jooq.impl.Tools.hasAmbiguousNames;
import static org.jooq.impl.Tools.hasAmbiguousNamesInTables;
import static org.jooq.impl.Tools.isEmpty;
import static org.jooq.impl.Tools.isNotEmpty;
import static org.jooq.impl.Tools.isWindow;
import static org.jooq.impl.Tools.joinedTables;
import static org.jooq.impl.Tools.map;
import static org.jooq.impl.Tools.qualify;
import static org.jooq.impl.Tools.recordType;
@ -257,6 +260,7 @@ import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -1681,6 +1685,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@SuppressWarnings({ "unchecked", "rawtypes" })
final void accept0(Context<?> context) {
@ -1708,16 +1713,25 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
}
case WHEN_MULTIPLE_TABLES: {
int s = getFrom().size();
if (knownTableSource()) {
Iterator<Table<?>> it = concat(
joinedTables(getFrom()),
context.scopeParts((Class<Table<?>>) (Class) Table.class)
).iterator();
if (knownTableSource() && (s == 0 || s == 1 && !(getFrom().get(0) instanceof JoinTable)))
context.data(DATA_RENDER_TABLE, false);
// [#15996] At least 2 tables are in scope
if (!it.hasNext() || it.next() != null && !it.hasNext())
context.data(DATA_RENDER_TABLE, false);
}
break;
}
case WHEN_AMBIGUOUS_COLUMNS: {
if (knownTableSource() && !hasAmbiguousNames(getSelect()))
if (knownTableSource() && !hasAmbiguousNamesInTables(concat(
joinedTables(getFrom()),
context.scopeParts((Class<Table<?>>) (Class) Table.class)
)))
context.data(DATA_RENDER_TABLE, false);
break;

View File

@ -6278,7 +6278,15 @@ final class Tools {
return t == Date.class || t == LocalDate.class;
}
static final boolean hasAmbiguousNames(Collection<? extends Field<?>> fields) {
static final boolean hasAmbiguousNamesInTables(Iterable<? extends Table<?>> tables) {
if (tables == null)
return false;
Set<String> names = new HashSet<>();
return anyMatch(tables, t -> anyMatch(t.fields(), f -> !names.add(f.getName())));
}
static final boolean hasAmbiguousNames(Iterable<? extends Field<?>> fields) {
if (fields == null)
return false;
@ -6659,6 +6667,40 @@ final class Tools {
return anyMatch(fields, f -> f.getDataType().isEmbeddable());
}
static final <E> Iterable<E> concat(Iterable<E> i1, Iterable<E> i2) {
return () -> concat(i1.iterator(), i2.iterator());
}
static final <E> Iterator<E> concat(Iterator<E> i1, Iterator<E> i2) {
return new Iterator<E>() {
boolean first = true;
@Override
public boolean hasNext() {
if (first)
if (i1.hasNext())
return true;
else
first = false;
return i2.hasNext();
}
@Override
public E next() {
return first ? i1.next() : i2.next();
}
@Override
public void remove() {
if (first)
i1.remove();
else
i2.remove();
}
};
}
static final <E> List<E> collect(Iterable<E> iterable) {
if (iterable instanceof List<E> l)
return l;
@ -7360,6 +7402,18 @@ final class Tools {
return traverseJoins(in, false, r -> r, search(search, Tools::unwrap));
}
static final List<Table<?>> joinedTables(Iterable<? extends Table<?>> i) {
List<Table<?>> result = new ArrayList<>();
traverseJoins(i, result::add);
return result;
}
static final List<Table<?>> joinedTables(Table<?> t) {
List<Table<?>> result = new ArrayList<>();
traverseJoins(t, result::add);
return result;
}
static final void traverseJoins(Iterable<? extends Table<?>> i, Consumer<? super Table<?>> consumer) {
for (Table<?> t : i)
traverseJoins(t, consumer);