[#1502] Add support for implicit join over to-one relationships (WIP)
- [#7152] Add Context.scopeStart() and scopeEnd() - [#2791] Add a Context data map scoped to the outer scope of a subquery - [#1502] Add support for implicit join over to-one relationships
This commit is contained in:
parent
c67ed24a88
commit
87c49895c2
@ -149,6 +149,31 @@ public interface Context<C extends Context<C>> extends Scope {
|
||||
*/
|
||||
C subquery(boolean subquery);
|
||||
|
||||
/**
|
||||
* Start a new SELECT scope.
|
||||
*/
|
||||
C scopeStart();
|
||||
|
||||
/**
|
||||
* Mark the beginning of a scoped query part.
|
||||
*/
|
||||
C scopeMarkStart(QueryPart part);
|
||||
|
||||
/**
|
||||
* Register a "special" query part in the scope.
|
||||
*/
|
||||
C scopeRegister(QueryPart part);
|
||||
|
||||
/**
|
||||
* Mark the end of a scoped query part.
|
||||
*/
|
||||
C scopeMarkEnd(QueryPart part);
|
||||
|
||||
/**
|
||||
* End a previous SELECT scope.
|
||||
*/
|
||||
C scopeEnd();
|
||||
|
||||
/**
|
||||
* whether the current context is rendering a string literal.
|
||||
*/
|
||||
|
||||
@ -48,15 +48,22 @@ import static org.jooq.impl.Tools.DataKey.DATA_OMIT_CLAUSE_EVENT_EMISSION;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Clause;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.QueryPartInternal;
|
||||
import org.jooq.RenderContext;
|
||||
@ -94,6 +101,8 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
int stringLiteral;
|
||||
String stringLiteralEscapedApos = "'";
|
||||
int index;
|
||||
int scopeLevel = -1;
|
||||
final ScopeStack scopeStack;
|
||||
|
||||
// [#2665] VisitListener API
|
||||
final VisitListener[] visitListeners;
|
||||
@ -164,6 +173,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
? CastMode.NEVER
|
||||
: CastMode.DEFAULT;
|
||||
this.quote = settings().getRenderNameStyle() == RenderNameStyle.QUOTED;
|
||||
this.scopeStack = new ScopeStack();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -503,6 +513,41 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final C scopeStart() {
|
||||
scopeLevel++;
|
||||
scopeStart0();
|
||||
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public /* non-final */ C scopeMarkStart(QueryPart part) {
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public /* non-final */ C scopeRegister(QueryPart part) {
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public /* non-final */ C scopeMarkEnd(QueryPart part) {
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final C scopeEnd() {
|
||||
scopeEnd0();
|
||||
scopeLevel--;
|
||||
scopeStack.trim();
|
||||
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
void scopeStart0() {}
|
||||
void scopeEnd0() {}
|
||||
|
||||
@Override
|
||||
public final boolean stringLiteral() {
|
||||
return stringLiteral > 0;
|
||||
@ -668,4 +713,109 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
sb.append(subquery);
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
static class JoinNode {
|
||||
final Table<?> table;
|
||||
final Map<ForeignKey<?, ?>, JoinNode> children;
|
||||
|
||||
JoinNode(Table<?> table) {
|
||||
this.table = table;
|
||||
this.children = new LinkedHashMap<ForeignKey<?, ?>, JoinNode>();
|
||||
}
|
||||
|
||||
public Table<?> joinTree() {
|
||||
Table<?> result = table;
|
||||
|
||||
for (Entry<ForeignKey<?, ?>, JoinNode> e : children.entrySet())
|
||||
result = result.leftJoin(e.getValue().table).onKey(e.getKey());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class ScopeStackElement {
|
||||
int[] positions;
|
||||
JoinNode joinNode;
|
||||
}
|
||||
|
||||
class ScopeStack implements Iterable<ScopeStackElement> {
|
||||
private Map<QueryPart, List<ScopeStackElement>> stack;
|
||||
|
||||
private Map<QueryPart, List<ScopeStackElement>> stack() {
|
||||
if (stack == null)
|
||||
stack = new LinkedHashMap<QueryPart, List<ScopeStackElement>>();
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
final void trim() {
|
||||
if (scopeLevel > 0)
|
||||
for (List<ScopeStackElement> list : stack.values())
|
||||
while (list.size() > scopeLevel || list.size() > 0 && list.get(list.size() - 1) == null)
|
||||
list.remove(list.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<ScopeStackElement> iterator() {
|
||||
return new Iterator<ScopeStackElement>() {
|
||||
Iterator<List<ScopeStackElement>> it = stack().values().iterator();
|
||||
ScopeStackElement next;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return move() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopeStackElement next() {
|
||||
if (next == null) {
|
||||
return move();
|
||||
}
|
||||
else {
|
||||
ScopeStackElement result = next;
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private ScopeStackElement move() {
|
||||
while (it.hasNext()) {
|
||||
List<ScopeStackElement> list = it.next();
|
||||
|
||||
int size = scopeLevel + 1;
|
||||
if (list.size() >= size && (next = list.get(scopeLevel)) != null)
|
||||
break;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ScopeStackElement get(QueryPart key) {
|
||||
List<ScopeStackElement> list = stack().get(key);
|
||||
|
||||
if (list == null) {
|
||||
list = new ArrayList<ScopeStackElement>();
|
||||
stack().put(key, list);
|
||||
}
|
||||
|
||||
int size = scopeLevel + 1;
|
||||
if (list.size() < size)
|
||||
list.addAll(Collections.nCopies(size - list.size(), null));
|
||||
|
||||
ScopeStackElement result = list.get(scopeLevel);
|
||||
if (result == null) {
|
||||
result = new ScopeStackElement();
|
||||
list.set(scopeLevel, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,24 +95,26 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart {
|
||||
private static final EnumSet<SQLDialect> SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL2 = EnumSet.of(H2, MARIADB, MYSQL, SQLITE);
|
||||
|
||||
final Q wrapped;
|
||||
final Q wrapping;
|
||||
final Name alias;
|
||||
final Name[] fieldAliases;
|
||||
final boolean wrapInParentheses;
|
||||
|
||||
Alias(Q wrapped, Name alias) {
|
||||
this(wrapped, alias, null, false);
|
||||
Alias(Q wrapped, Q wrapping, Name alias) {
|
||||
this(wrapped, wrapping, alias, null, false);
|
||||
}
|
||||
|
||||
Alias(Q wrapped, Name alias, boolean wrapInParentheses) {
|
||||
this(wrapped, alias, null, wrapInParentheses);
|
||||
Alias(Q wrapped, Q wrapping, Name alias, boolean wrapInParentheses) {
|
||||
this(wrapped, wrapping, alias, null, wrapInParentheses);
|
||||
}
|
||||
|
||||
Alias(Q wrapped, Name alias, Name[] fieldAliases) {
|
||||
this(wrapped, alias, fieldAliases, false);
|
||||
Alias(Q wrapped, Q wrapping, Name alias, Name[] fieldAliases) {
|
||||
this(wrapped, wrapping, alias, fieldAliases, false);
|
||||
}
|
||||
|
||||
Alias(Q wrapped, Name alias, Name[] fieldAliases, boolean wrapInParentheses) {
|
||||
Alias(Q wrapped, Q wrapping, Name alias, Name[] fieldAliases, boolean wrapInParentheses) {
|
||||
this.wrapped = wrapped;
|
||||
this.wrapping = wrapping;
|
||||
this.alias = alias;
|
||||
this.fieldAliases = fieldAliases;
|
||||
this.wrapInParentheses = wrapInParentheses;
|
||||
@ -129,6 +131,9 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart {
|
||||
if (context.declareAliases() && (context.declareFields() || context.declareTables())) {
|
||||
context.declareAliases(false);
|
||||
|
||||
if (wrapped instanceof Table)
|
||||
context.scopeMarkStart(wrapping);
|
||||
|
||||
SQLDialect family = context.family();
|
||||
boolean emulatedDerivedColumnList = false;
|
||||
|
||||
@ -196,6 +201,9 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart {
|
||||
toSQLWrapped(context);
|
||||
}
|
||||
|
||||
if (wrapped instanceof Table)
|
||||
context.scopeMarkEnd(wrapping);
|
||||
|
||||
// [#291] some aliases cause trouble, if they are not explicitly marked using "as"
|
||||
toSQLAs(context);
|
||||
|
||||
|
||||
@ -50,9 +50,11 @@ import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER;
|
||||
import static org.jooq.impl.Tools.DataKey.DATA_COUNT_BIND_VALUES;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -60,11 +62,13 @@ import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Constants;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.QueryPartInternal;
|
||||
import org.jooq.RenderContext;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.conf.RenderFormatting;
|
||||
import org.jooq.conf.RenderKeywordStyle;
|
||||
import org.jooq.conf.RenderNameStyle;
|
||||
@ -162,6 +166,91 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
|
||||
// RenderContext API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public RenderContext scopeMarkStart(QueryPart part) {
|
||||
if (scopeLevel >= 0) {
|
||||
ScopeStackElement e = scopeStack.get(part);
|
||||
if (e.positions == null)
|
||||
e.positions = new int[2];
|
||||
|
||||
e.positions[0] = sql.length();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderContext scopeMarkEnd(QueryPart part) {
|
||||
if (scopeLevel >= 0) {
|
||||
ScopeStackElement e = scopeStack.get(part);
|
||||
e.positions[1] = sql.length();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderContext scopeRegister(QueryPart part) {
|
||||
if (scopeLevel >= 0) {
|
||||
if (part instanceof TableImpl) {
|
||||
Table<?> table = (Table<?>) part;
|
||||
Table<?> root = table;
|
||||
Table<?> child = root;
|
||||
List<ForeignKey<?, ?>> keys = new ArrayList<ForeignKey<?, ?>>();
|
||||
List<Table<?>> tables = new ArrayList<Table<?>>();
|
||||
|
||||
while (root instanceof TableImpl && (child = ((TableImpl<?>) root).child) != null) {
|
||||
keys.add(((TableImpl<?>) root).path);
|
||||
tables.add(root);
|
||||
root = child;
|
||||
}
|
||||
|
||||
ScopeStackElement e = scopeStack.get(root);
|
||||
if (e.joinNode == null)
|
||||
e.joinNode = new JoinNode(root);
|
||||
|
||||
JoinNode childNode = e.joinNode;
|
||||
for (int i = keys.size() - 1; i >= 0; i--) {
|
||||
ForeignKey<?, ?> k = keys.get(i);
|
||||
Table<?> t = tables.get(i);
|
||||
|
||||
JoinNode next = childNode.children.get(k);
|
||||
if (next == null) {
|
||||
next = new JoinNode(t);
|
||||
childNode.children.put(k, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void scopeEnd0() {
|
||||
outer:
|
||||
for (ScopeStackElement e1 : scopeStack) {
|
||||
if (e1.positions == null || e1.joinNode == null)
|
||||
continue outer;
|
||||
|
||||
String replaced = "(" + DSL.using(configuration).render(e1.joinNode.joinTree()) + ")";
|
||||
sql.replace(e1.positions[0], e1.positions[1], replaced);
|
||||
|
||||
int shift = replaced.length() - (e1.positions[1] - e1.positions[0]);
|
||||
|
||||
inner:
|
||||
for (ScopeStackElement e2 : scopeStack) {
|
||||
if (e2.positions == null)
|
||||
continue inner;
|
||||
|
||||
if (e2.positions[0] > e1.positions[0]) {
|
||||
e2.positions[0] = e2.positions[0] + shift;
|
||||
e2.positions[1] = e2.positions[1] + shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int peekSkipUpdateCounts() {
|
||||
return skipUpdateCounts;
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ final class FieldAlias<T> extends AbstractField<T> {
|
||||
FieldAlias(Field<T> field, Name alias) {
|
||||
super(alias, field.getDataType());
|
||||
|
||||
this.alias = new Alias<Field<T>>(field, alias, false);
|
||||
this.alias = new Alias<Field<T>>(field, this, alias, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -451,6 +451,8 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> context) {
|
||||
context.scopeStart();
|
||||
|
||||
SQLDialect dialect = context.dialect();
|
||||
SQLDialect family = context.family();
|
||||
|
||||
@ -764,6 +766,8 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
if (renderTrailingLimit != null)
|
||||
context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, renderTrailingLimit);
|
||||
}
|
||||
|
||||
context.scopeEnd();
|
||||
}
|
||||
|
||||
private final void pushWindow(Context<?> context) {
|
||||
|
||||
@ -78,7 +78,7 @@ final class TableAlias<R extends Record> extends AbstractTable<R> {
|
||||
TableAlias(Table<R> table, Name alias, Name[] fieldAliases, boolean wrapInParentheses) {
|
||||
super(alias, table.getSchema());
|
||||
|
||||
this.alias = new Alias<Table<R>>(table, alias, fieldAliases, wrapInParentheses);
|
||||
this.alias = new Alias<Table<R>>(table, this, alias, fieldAliases, wrapInParentheses);
|
||||
this.aliasedFields = init(fieldAliases);
|
||||
}
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ import org.jooq.Clause;
|
||||
import org.jooq.Comment;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.SQLDialect;
|
||||
@ -78,6 +79,8 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
final Alias<Table<R>> alias;
|
||||
|
||||
protected final Field<?>[] parameters;
|
||||
final Table<?> child;
|
||||
final ForeignKey<?, R> path;
|
||||
|
||||
/**
|
||||
* @deprecated - 3.10 - [#5996] - Use {@link #TableImpl(Name)} instead (or
|
||||
@ -125,19 +128,19 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
}
|
||||
|
||||
public TableImpl(Name name) {
|
||||
this(name, null, null, null, (Comment) null);
|
||||
this(name, null, null, null, null, null, (Comment) null);
|
||||
}
|
||||
|
||||
public TableImpl(Name name, Schema schema) {
|
||||
this(name, schema, null, null, (Comment) null);
|
||||
this(name, schema, null, null, null, null, (Comment) null);
|
||||
}
|
||||
|
||||
public TableImpl(Name name, Schema schema, Table<R> aliased) {
|
||||
this(name, schema, aliased, null, (Comment) null);
|
||||
this(name, schema, null, null, aliased, null, (Comment) null);
|
||||
}
|
||||
|
||||
public TableImpl(Name name, Schema schema, Table<R> aliased, Field<?>[] parameters) {
|
||||
this(name, schema, aliased, parameters, (Comment) null);
|
||||
this(name, schema, null, null, aliased, parameters, (Comment) null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,13 +148,23 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
*/
|
||||
@Deprecated
|
||||
public TableImpl(Name name, Schema schema, Table<R> aliased, Field<?>[] parameters, String comment) {
|
||||
this(name, schema, aliased, parameters, DSL.comment(comment));
|
||||
this(name, schema, null, null, aliased, parameters, DSL.comment(comment));
|
||||
}
|
||||
|
||||
public TableImpl(Name name, Schema schema, Table<R> aliased, Field<?>[] parameters, Comment comment) {
|
||||
this(name, schema, null, null, aliased, parameters, comment);
|
||||
}
|
||||
|
||||
public <O extends Record> TableImpl(Table<O> child, ForeignKey<O, R> path, Table<R> parent) {
|
||||
this(parent.getQualifiedName(), parent.getSchema(), child, path, null, null, DSL.comment(parent.getComment()));
|
||||
}
|
||||
|
||||
public <O extends Record> TableImpl(Name name, Schema schema, Table<O> child, ForeignKey<O, R> path, Table<R> aliased, Field<?>[] parameters, Comment comment) {
|
||||
super(name, schema, comment);
|
||||
|
||||
this.fields = new Fields<R>();
|
||||
this.child = child;
|
||||
this.path = path;
|
||||
|
||||
if (aliased != null) {
|
||||
|
||||
@ -160,9 +173,9 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
Alias<Table<R>> existingAlias = Tools.alias(aliased);
|
||||
|
||||
if (existingAlias != null)
|
||||
alias = new Alias<Table<R>>(existingAlias.wrapped, name, existingAlias.fieldAliases, existingAlias.wrapInParentheses);
|
||||
alias = new Alias<Table<R>>(existingAlias.wrapped, this, name, existingAlias.fieldAliases, existingAlias.wrapInParentheses);
|
||||
else
|
||||
alias = new Alias<Table<R>>(aliased, name);
|
||||
alias = new Alias<Table<R>>(aliased, this, name);
|
||||
}
|
||||
else
|
||||
alias = null;
|
||||
@ -174,9 +187,8 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
* Get the aliased table wrapped by this table
|
||||
*/
|
||||
Table<R> getAliasedTable() {
|
||||
if (alias != null) {
|
||||
if (alias != null)
|
||||
return alias.wrapped();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -193,6 +205,9 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
if (child != null)
|
||||
ctx.scopeRegister(this);
|
||||
|
||||
if (alias != null) {
|
||||
ctx.visit(alias);
|
||||
}
|
||||
@ -217,6 +232,9 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
}
|
||||
|
||||
private void accept0(Context<?> ctx) {
|
||||
if (ctx.declareTables())
|
||||
ctx.scopeMarkStart(this);
|
||||
|
||||
if (ctx.qualify() &&
|
||||
(!NO_SUPPORT_QUALIFIED_TVF_CALLS.contains(ctx.family()) || parameters == null || ctx.declareTables())) {
|
||||
Schema mappedSchema = Tools.getMappedSchema(ctx.configuration(), getSchema());
|
||||
@ -239,6 +257,9 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
.visit(new QueryPartList<Field<?>>(parameters))
|
||||
.sql(')');
|
||||
}
|
||||
|
||||
if (ctx.declareTables())
|
||||
ctx.scopeMarkEnd(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,12 +270,10 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
*/
|
||||
@Override
|
||||
public Table<R> as(Name as) {
|
||||
if (alias != null) {
|
||||
if (alias != null)
|
||||
return alias.wrapped().as(as);
|
||||
}
|
||||
else {
|
||||
else
|
||||
return new TableAlias<R>(this, as);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,12 +284,10 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
*/
|
||||
@Override
|
||||
public Table<R> as(Name as, Name... fieldAliases) {
|
||||
if (alias != null) {
|
||||
if (alias != null)
|
||||
return alias.wrapped().as(as, fieldAliases);
|
||||
}
|
||||
else {
|
||||
else
|
||||
return new TableAlias<R>(this, as, fieldAliases);
|
||||
}
|
||||
}
|
||||
|
||||
public Table<R> rename(String rename) {
|
||||
@ -295,7 +312,7 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
|
||||
|
||||
@Override
|
||||
public boolean declaresTables() {
|
||||
return (alias != null) || (parameters != null) || super.declaresTables();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
Loading…
Reference in New Issue
Block a user