[jOOQ/jOOQ#9061] Support looking up unqualified field names
This commit is contained in:
parent
61e13c13d7
commit
1a15bfbf29
@ -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);
|
||||
}
|
||||
|
||||
|
||||
1826
jOOQ/src/main/java/org/jooq/impl/FieldProxy.java
Normal file
1826
jOOQ/src/main/java/org/jooq/impl/FieldProxy.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user