[jOOQ/jOOQ#11143] ParserWithMetaLookups reports ambiguous column in correlated subquery, when there is none

This commit is contained in:
Lukas Eder 2020-12-17 13:14:00 +01:00
parent f02fa963ba
commit 005f0ef426
2 changed files with 104 additions and 49 deletions

View File

@ -561,6 +561,7 @@ import org.jooq.conf.RenderQuotedNames;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.impl.JSONNull.JSONNullType;
import org.jooq.impl.ScopeStack.Value;
import org.jooq.impl.XMLParse.DocumentOrContent;
import org.jooq.tools.StringUtils;
import org.jooq.tools.reflect.Reflect;
@ -12527,7 +12528,7 @@ final class ParserContext {
List<FieldProxy<?>> retain = new ArrayList<>();
for (FieldProxy<?> lookup : lookupFields) {
Field<?> found = null;
Value<Field<?>> found = null;
for (Field<?> f : fieldScope) {
if (f.getName().equals(lookup.getName())) {
@ -12536,13 +12537,14 @@ final class ParserContext {
throw exception("Ambiguous field identifier");
}
found = f;
// TODO: Does this instance of "found" really interact with the one below?
found = new Value<>(0, f);
}
}
found = resolveInTableScope(tableScope, lookup.getQualifiedName(), lookup, found);
found = resolveInTableScope(tableScope.valueIterable(), lookup.getQualifiedName(), lookup, found);
if (found != null)
lookup.delegate((AbstractField) found);
lookup.delegate((AbstractField) found.value);
else
retain.add(lookup);
}
@ -12559,14 +12561,20 @@ final class ParserContext {
unknownField(r);
}
private final Field<?> resolveInTableScope(Iterable<Table<?>> tables, Name lookupName, FieldProxy<?> lookup, Field<?> found) {
private final Value<Field<?>> resolveInTableScope(Iterable<Value<Table<?>>> tables, Name lookupName, FieldProxy<?> lookup, Value<Field<?>> found) {
tableScopeLoop:
for (Table<?> t : tables) {
Field<?> f;
for (Value<Table<?>> t : tables) {
Value<Field<?>> f;
if (t instanceof JoinTable) {
found = resolveInTableScope(Arrays.asList(((JoinTable) t).lhs, ((JoinTable) t).rhs), lookupName, lookup, found);
if (t.value instanceof JoinTable) {
found = resolveInTableScope(
Arrays.asList(
new Value<>(t.scopeLevel, ((JoinTable) t.value).lhs),
new Value<>(t.scopeLevel, ((JoinTable) t.value).rhs)
),
lookupName, lookup, found
);
}
else if (lookupName.qualified()) {
@ -12576,17 +12584,18 @@ final class ParserContext {
// - Test fully qualified column names vs partially qualified column names
Name q = lookupName.qualifier();
boolean x = q.qualified();
if (x && q.equals(t.getQualifiedName()) || !x && q.last().equals(t.getName()))
if ((found = t.field(lookup.getName())) != null)
if (x && q.equals(t.value.getQualifiedName()) || !x && q.last().equals(t.value.getName()))
if ((found = Value.of(t.scopeLevel, t.value.field(lookup.getName()))) != null)
break tableScopeLoop;
}
else if ((f = t.field(lookup.getName())) != null) {
if (found != null) {
else if ((f = Value.of(t.scopeLevel, t.value.field(lookup.getName()))) != null) {
if (found == null || found.scopeLevel < f.scopeLevel) {
found = f;
}
else {
position(lookup.position());
throw exception("Ambiguous field identifier");
}
found = f;
}
}

View File

@ -94,45 +94,91 @@ final class ScopeStack<K, V> implements Iterable<V> {
return !iterator().hasNext();
}
@Override
public final Iterator<V> iterator() {
return new Iterator<V>() {
Iterator<List<V>> it = stack().values().iterator();
V next;
final Iterable<Value<V>> valueIterable() {
return new Iterable<Value<V>>() {
@Override
public boolean hasNext() {
return move() != null;
}
@Override
public V next() {
if (next == null) {
return move();
}
else {
V result = next;
next = null;
return result;
}
}
private V move() {
List<V> list;
while (it.hasNext())
if (!(list = it.next()).isEmpty() && (next = list.get(list.size() - 1)) != null)
break;
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
public Iterator<Value<V>> iterator() {
return new ScopeStackIterator<Value<V>>(new F.F1<List<V>, Value<V>>() {
@Override
public Value<V> apply(List<V> list) {
return Value.lastOf(list);
}
});
}
};
}
@Override
public final Iterator<V> iterator() {
return new ScopeStackIterator<>(new F.F1<List<V>, V>() {
@Override
public V apply(List<V> list) {
return list.get(list.size() - 1);
}
});
}
static final class Value<V> {
final int scopeLevel;
final V value;
Value(int scopeLevel, V value) {
this.scopeLevel = scopeLevel;
this.value = value;
}
static <V> Value<V> of(int scopeLevel, V value) {
return value == null ? null : new Value<>(scopeLevel, value);
}
static <V> Value<V> lastOf(List<V> list) {
int size = list.size();
V value = list.get(size - 1);
return of(size - 1, value);
}
}
private final class ScopeStackIterator<U> implements Iterator<U> {
final Iterator<List<V>> it = stack().values().iterator();
final F.F1<List<V>, U> valueExtractor;
U next;
ScopeStackIterator(F.F1<List<V>, U> valueExtractor) {
this.valueExtractor = valueExtractor;
}
@Override
public boolean hasNext() {
return move() != null;
}
@Override
public U next() {
if (next == null) {
return move();
}
else {
U result = next;
next = null;
return result;
}
}
private U move() {
List<V> list;
while (it.hasNext())
if (!(list = it.next()).isEmpty() && ((next = valueExtractor.apply(list)) != null))
break;
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
}
final void setAll(V value) {
for (K key : stack().keySet())
set(key, value);