diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index b32489e67c..e13ac8c5d2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -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> retain = new ArrayList<>(); for (FieldProxy lookup : lookupFields) { - Field found = null; + Value> 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> tables, Name lookupName, FieldProxy lookup, Field found) { + private final Value> resolveInTableScope(Iterable>> tables, Name lookupName, FieldProxy lookup, Value> found) { tableScopeLoop: - for (Table t : tables) { - Field f; + for (Value> t : tables) { + Value> 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; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java b/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java index ecf5be7b71..00bdbeadd2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java +++ b/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java @@ -94,45 +94,91 @@ final class ScopeStack implements Iterable { return !iterator().hasNext(); } - @Override - public final Iterator iterator() { - return new Iterator() { - Iterator> it = stack().values().iterator(); - V next; - + final Iterable> valueIterable() { + return new Iterable>() { @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 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> iterator() { + return new ScopeStackIterator>(new F.F1, Value>() { + @Override + public Value apply(List list) { + return Value.lastOf(list); + } + }); } }; } + @Override + public final Iterator iterator() { + return new ScopeStackIterator<>(new F.F1, V>() { + @Override + public V apply(List list) { + return list.get(list.size() - 1); + } + }); + } + + static final class Value { + final int scopeLevel; + final V value; + + Value(int scopeLevel, V value) { + this.scopeLevel = scopeLevel; + this.value = value; + } + + static Value of(int scopeLevel, V value) { + return value == null ? null : new Value<>(scopeLevel, value); + } + + static Value lastOf(List list) { + int size = list.size(); + V value = list.get(size - 1); + return of(size - 1, value); + } + } + + private final class ScopeStackIterator implements Iterator { + final Iterator> it = stack().values().iterator(); + final F.F1, U> valueExtractor; + U next; + + ScopeStackIterator(F.F1, 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 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);