[jOOQ/jOOQ#9061] Support looking up unqualified field names

This commit is contained in:
Lukas Eder 2020-04-16 16:54:44 +02:00
parent 61e13c13d7
commit 1a15bfbf29
4 changed files with 1942 additions and 17 deletions

View File

@ -768,7 +768,7 @@ abstract class AbstractField<T> extends AbstractTypedNamed<T> implements Field<T
}
@Override
public LikeEscapeStep like(QuantifiedSelect<Record1<String>> query) {
public final LikeEscapeStep like(QuantifiedSelect<Record1<String>> query) {
return new QuantifiedComparisonCondition(query, this, LIKE);
}
@ -823,7 +823,7 @@ abstract class AbstractField<T> extends AbstractTypedNamed<T> implements Field<T
}
@Override
public LikeEscapeStep notLike(QuantifiedSelect<Record1<String>> query) {
public final LikeEscapeStep notLike(QuantifiedSelect<Record1<String>> query) {
return new QuantifiedComparisonCondition(query, this, NOT_LIKE);
}

File diff suppressed because it is too large Load Diff

View File

@ -873,6 +873,7 @@ final class ParserImpl implements Parser {
if (ctx.done())
return null;
ctx.scopeStart();
boolean metaLookupsForceIgnore = ctx.metaLookupsForceIgnore();
try {
switch (ctx.character()) {
@ -1002,7 +1003,15 @@ final class ParserImpl implements Parser {
throw ctx.exception("Unsupported query type");
}
catch (ParserException e) {
// [#9061] Don't hide this pre-existing exceptions in scopeResolve()
ctx.scopeClear();
throw e;
}
finally {
ctx.scopeEnd();
ctx.scopeResolve();
ctx.metaLookupsForceIgnore(metaLookupsForceIgnore);
}
}
@ -1085,6 +1094,7 @@ final class ParserImpl implements Parser {
}
private static final SelectQueryImpl<Record> parseSelect(ParserContext ctx, Integer degree, WithImpl with) {
ctx.scopeStart();
SelectQueryImpl<Record> result = parseQueryExpressionBody(ctx, degree, with, null);
List<SortField<?>> orderBy = null;
@ -1213,6 +1223,7 @@ final class ParserImpl implements Parser {
result.setForUpdateSkipLocked();
}
ctx.scopeEnd();
return result;
}
@ -1422,6 +1433,12 @@ final class ParserImpl implements Parser {
if (from != null && from.size() == 1 && from.get(0).getName().equalsIgnoreCase("dual"))
from = null;
// [#9061] Register tables in scope as early as possible
// TODO: Move this into parseTables() so lateral joins can profit from lookups (?)
if (from != null)
for (Table<?> table : from)
ctx.tableScope.set(table.getName(), table);
if (parseKeywordIf(ctx, "WHERE"))
where = parseCondition(ctx);
@ -11580,19 +11597,22 @@ final class ParserImpl implements Parser {
}
final class ParserContext {
private static final boolean PRO_EDITION = false ;
private static final boolean PRO_EDITION = false ;
final DSLContext dsl;
final Locale locale;
final Meta meta;
final char[] sql;
private final ParseWithMetaLookups metaLookups;
private boolean metaLookupsForceIgnore;
private int position = 0;
private boolean ignoreHints = true;
private final Object[] bindings;
private int bindIndex = 0;
private String delimiter = ";";
final DSLContext dsl;
final Locale locale;
final Meta meta;
final char[] sql;
private final ParseWithMetaLookups metaLookups;
private boolean metaLookupsForceIgnore;
private int position = 0;
private boolean ignoreHints = true;
private final Object[] bindings;
private int bindIndex = 0;
private String delimiter = ";";
final ScopeStack<String, Table<?>> tableScope = new ScopeStack<>(null);
final ScopeStack<String, FieldProxy<?>> lookupFields = new ScopeStack<>(null);
private boolean scopeClear = false;
@ -11860,6 +11880,66 @@ final class ParserContext {
+ (sql.length > position + 80 ? "..." : "");
}
void scopeStart() {
tableScope.scopeStart();
lookupFields.scopeStart();
lookupFields.setAll(null);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
void scopeEnd() {
List<FieldProxy<?>> retain = new ArrayList<>();
for (FieldProxy<?> f : lookupFields) {
Field<?> f1 = null;
for (Table<?> t : tableScope) {
Field<?> f2;
if ((f2 = t.field(f.getName())) != null) {
if (f1 != null) {
position(f.position);
throw exception("Ambiguous field identifier");
}
f1 = f2;
}
}
if (f1 != null) {
f.delegate = (AbstractField) f1;
f.sql = null;
}
else
retain.add(f);
}
lookupFields.scopeEnd();
tableScope.scopeEnd();
for (FieldProxy<?> r : retain)
if (lookupFields.get(r.getName()) == null)
if (lookupFields.inScope())
lookupFields.set(r.getName(), r);
else
unknownField(r);
}
void scopeClear() {
scopeClear = true;
}
void scopeResolve() {
if (!lookupFields.isEmpty())
unknownField(lookupFields.iterator().next());
}
void unknownField(FieldProxy<?> field) {
if (!scopeClear && !metaLookupsForceIgnore && metaLookups == THROW_ON_FAILURE) {
position(field.position);
throw exception("Unknown field identifier");
}
}
Table<?> lookupTable(Name name) {
if (meta != null) {
List<Table<?>> tables;
@ -11896,10 +11976,14 @@ final class ParserContext {
}
}
if (!metaLookupsForceIgnore && metaLookups == THROW_ON_FAILURE)
throw exception("Unknown field identifier");
if (metaLookups == ParseWithMetaLookups.OFF)
return field(name);
return field(name);
FieldProxy<?> field = lookupFields.get(name.last());
if (field == null)
lookupFields.set(name.last(), field = new FieldProxy<>((AbstractField<Object>) field(name), sql, position));
return field;
}
@Override

View File

@ -89,6 +89,11 @@ final class ScopeStack<K, V> implements Iterable<V> {
}
}
}
final boolean isEmpty() {
return !iterator().hasNext();
}
@Override
public final Iterator<V> iterator() {
return new Iterator<V>() {
@ -132,6 +137,11 @@ final class ScopeStack<K, V> implements Iterable<V> {
};
}
final void setAll(V value) {
for (K key : stack().keySet())
set(key, value);
}
final void set(K key, V value) {
set0(list(key), value);
}
@ -194,4 +204,9 @@ final class ScopeStack<K, V> implements Iterable<V> {
interface Constructor<V> {
V create(int scopeLevel);
}
@Override
public String toString() {
return stack().toString();
}
}