[jOOQ/jOOQ#5810] Emulate QUALIFY where not supported

This includes:
- [jOOQ/jOOQ#11663] Wrong order of WINDOW / QUALIFY clauses
This commit is contained in:
Lukas Eder 2021-03-17 18:07:00 +01:00
parent 71f26063df
commit 4febe602d8
15 changed files with 312 additions and 81 deletions

View File

@ -38,9 +38,28 @@
package org.jooq;
// ...
// ...
// ...
import static org.jooq.SQLDialect.CUBRID;
// ...
// ...
import static org.jooq.SQLDialect.FIREBIRD;
import static org.jooq.SQLDialect.H2;
// ...
// ...
import static org.jooq.SQLDialect.MARIADB;
// ...
import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
// ...
// ...
import java.util.Collection;
@ -115,7 +134,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* other with {@link Operator#AND}.
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
SelectQualifyConditionStep<R> qualify(Condition condition);
/**
@ -123,7 +142,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* other with {@link Operator#AND}.
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
SelectQualifyConditionStep<R> qualify(Condition... conditions);
/**
@ -131,14 +150,14 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* other with {@link Operator#AND}.
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
SelectQualifyConditionStep<R> qualify(Collection<? extends Condition> conditions);
/**
* Add a <code>QUALIFY</code> clause to the query.
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
SelectQualifyConditionStep<R> qualify(Field<Boolean> condition);
/**
@ -153,7 +172,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* @see SQL
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
@PlainSQL
SelectQualifyConditionStep<R> qualify(SQL sql);
@ -169,7 +188,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* @see SQL
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
@PlainSQL
SelectQualifyConditionStep<R> qualify(String sql);
@ -186,7 +205,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* @see SQL
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
@PlainSQL
SelectQualifyConditionStep<R> qualify(String sql, Object... bindings);
@ -203,7 +222,7 @@ public interface SelectQualifyStep<R extends Record> extends SelectOrderByStep<R
* @see SQL
*/
@NotNull
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
@PlainSQL
SelectQualifyConditionStep<R> qualify(String sql, QueryPart... parts);

View File

@ -423,7 +423,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
*
* @param condition The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Condition condition);
/**
@ -432,7 +432,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
*
* @param conditions The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Condition... conditions);
/**
@ -441,7 +441,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
*
* @param conditions The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Collection<? extends Condition> conditions);
/**
@ -452,7 +452,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
* conditions
* @param condition The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Operator operator, Condition condition);
/**
@ -463,7 +463,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
* conditions
* @param conditions The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Operator operator, Condition... conditions);
/**
@ -474,7 +474,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
* conditions
* @param conditions The condition
*/
@Support({ H2 })
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
void addQualify(Operator operator, Collection<? extends Condition> conditions);
/**

View File

@ -226,7 +226,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
QueryPart replacement = start(part);
if (replacement != null) {
QueryPartInternal internal = (QueryPartInternal) replacement;
QueryPartInternal internal = (QueryPartInternal) scopeMapping(replacement);
// If this is supposed to be a declaration section and the part isn't
// able to declare anything, then disable declaration temporarily
@ -701,7 +701,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
@Override
public /* non-final */ QueryPart scopeMapping(QueryPart part) {
return null;
return part;
}
@Override
@ -1011,6 +1011,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
ScopeStackElement(QueryPart part, int scopeLevel) {
this.part = part;
this.mapped = part;
this.scopeLevel = scopeLevel;
}

View File

@ -100,7 +100,8 @@ implements
WindowPartitionByStep<T>,
WindowRowsStep<T>,
WindowRowsAndStep<T>,
WindowExcludeStep<T>
WindowExcludeStep<T>,
ScopeMappable
{
/**

View File

@ -93,18 +93,18 @@ import org.jooq.SQLDialect;
*/
final class BetweenCondition<T> extends AbstractCondition implements BetweenAndStep<T> {
private static final long serialVersionUID = -4666251100802237878L;
private static final Clause[] CLAUSES_BETWEEN = { CONDITION, CONDITION_BETWEEN };
private static final Clause[] CLAUSES_BETWEEN_SYMMETRIC = { CONDITION, CONDITION_BETWEEN_SYMMETRIC };
private static final Clause[] CLAUSES_NOT_BETWEEN = { CONDITION, CONDITION_NOT_BETWEEN };
private static final Clause[] CLAUSES_NOT_BETWEEN_SYMMETRIC = { CONDITION, CONDITION_NOT_BETWEEN_SYMMETRIC };
private static final Set<SQLDialect> NO_SUPPORT_SYMMETRIC = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, IGNITE, MARIADB, MYSQL, SQLITE);
private static final long serialVersionUID = -4666251100802237878L;
private static final Clause[] CLAUSES_BETWEEN = { CONDITION, CONDITION_BETWEEN };
private static final Clause[] CLAUSES_BETWEEN_SYMMETRIC = { CONDITION, CONDITION_BETWEEN_SYMMETRIC };
private static final Clause[] CLAUSES_NOT_BETWEEN = { CONDITION, CONDITION_NOT_BETWEEN };
private static final Clause[] CLAUSES_NOT_BETWEEN_SYMMETRIC = { CONDITION, CONDITION_NOT_BETWEEN_SYMMETRIC };
private static final Set<SQLDialect> NO_SUPPORT_SYMMETRIC = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, IGNITE, MARIADB, MYSQL, SQLITE);
private final boolean symmetric;
private final boolean not;
private final Field<T> field;
private final Field<T> minValue;
private Field<T> maxValue;
private final boolean symmetric;
private final boolean not;
final Field<T> field;
final Field<T> minValue;
Field<T> maxValue;
BetweenCondition(Field<T> field, Field<T> minValue, boolean not, boolean symmetric) {
this.field = nullableIf(false, nullSafe(field, minValue.getDataType()));

View File

@ -195,21 +195,21 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
@Override
public QueryPart scopeMapping(QueryPart part) {
if (scopeStack.inScope()) {
if (part instanceof TableImpl) {
ScopeStackElement e = scopeStack.get(part);
if (scopeStack.inScope() && part instanceof ScopeMappable) {
ScopeStackElement e = scopeStack.get(part);
if (e != null)
return e.mapped;
}
if (e != null && e.mapped != null)
return e.mapped;
}
return null;
return part;
}
@Override
public RenderContext scopeRegister(QueryPart part, boolean forceNew, QueryPart mapped) {
if (scopeStack.inScope()) {
ScopeStackElement e;
if (part instanceof TableImpl) {
Table<?> root = (Table<?>) part;
Table<?> child = root;
@ -220,11 +220,10 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
root = child;
}
ScopeStackElement e = forceNew
e = forceNew
? scopeStack.create(root)
: scopeStack.getOrCreate(root);
e.mapped = mapped;
if (e.joinNode == null)
e.joinNode = new JoinNode(configuration(), root);
@ -243,15 +242,24 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
}
}
else if (forceNew)
scopeStack.create(part);
e = scopeStack.create(part);
else
scopeStack.getOrCreate(part);
e = scopeStack.getOrCreate(part);
e.mapped = mapped;
}
return this;
}
@Override
void scopeStart0() {
for (ScopeStackElement e : scopeStack) {
if (e.part != e.mapped && !(e.part instanceof ScopeNestable))
scopeStack.set(e.part, null);
}
}
@Override
void scopeEnd0() {
ScopeMarker[] markers = ScopeMarker.values();

View File

@ -99,16 +99,16 @@ import org.jooq.SQLDialect;
*/
final class InCondition<T> extends AbstractCondition {
private static final long serialVersionUID = -1653924248576930761L;
private static final int IN_LIMIT = 1000;
private static final Clause[] CLAUSES_IN = { CONDITION, CONDITION_IN };
private static final Clause[] CLAUSES_IN_NOT = { CONDITION, CONDITION_NOT_IN };
private static final Set<SQLDialect> REQUIRES_IN_LIMIT = SQLDialect.supportedBy(FIREBIRD);
private static final Set<SQLDialect> NO_SUPPORT_EMPTY_LISTS = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES);
private static final long serialVersionUID = -1653924248576930761L;
private static final int IN_LIMIT = 1000;
private static final Clause[] CLAUSES_IN = { CONDITION, CONDITION_IN };
private static final Clause[] CLAUSES_IN_NOT = { CONDITION, CONDITION_NOT_IN };
private static final Set<SQLDialect> REQUIRES_IN_LIMIT = SQLDialect.supportedBy(FIREBIRD);
private static final Set<SQLDialect> NO_SUPPORT_EMPTY_LISTS = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES);
private final Field<T> field;
private final List<? extends Field<?>> values;
private final Comparator comparator;
final Field<T> field;
final List<? extends Field<?>> values;
final Comparator comparator;
InCondition(Field<T> field, List<? extends Field<?>> values, Comparator comparator) {
this.field = field;

View File

@ -95,9 +95,9 @@ final class IsDistinctFrom<T> extends AbstractCondition {
private final Field<T> lhs;
private final Field<T> rhs;
private final Comparator comparator;
final Field<T> lhs;
final Field<T> rhs;
final Comparator comparator;
IsDistinctFrom(Field<T> lhs, Field<T> rhs, Comparator comparator) {
this.lhs = lhs;

View File

@ -50,7 +50,7 @@ final class NotCondition extends AbstractCondition {
private static final long serialVersionUID = 2921001862882237932L;
private static final Clause[] CLAUSES = { CONDITION, CONDITION_NOT };
private final Condition condition;
final Condition condition;
NotCondition(Condition condition) {
this.condition = condition;

View File

@ -0,0 +1,51 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import org.jooq.Context;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
/**
* A marker interface for all query parts that can be mapped via
* {@link Context#scopeMapping(QueryPart)}
*
* @author Lukas Eder
*/
interface ScopeMappable extends QueryPartInternal {
}

View File

@ -0,0 +1,50 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import org.jooq.Context;
import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
/**
* A marker interface for all query parts that are visible from nested scopes.
*
* @author Lukas Eder
*/
interface ScopeNestable extends QueryPartInternal {
}

View File

@ -184,12 +184,18 @@ final class ScopeStack<K, V> implements Iterable<V> {
}
private final V get0(List<V> list) {
int i = list.size() - 1;
return i == -1 ? null : list.get(i);
int i;
if (list == null)
return null;
else if ((i = list.size()) == 0)
return null;
else
return list.get(i - 1);
}
final V get(K key) {
return get0(list(key));
return get0(listOrNull(key));
}
final <T extends Throwable> V getOrThrow(K key, Supplier<T> exception) throws T {
@ -223,6 +229,10 @@ final class ScopeStack<K, V> implements Iterable<V> {
list.set(scopeLevel, value);
}
private List<V> listOrNull(K key) {
return stack().get(key);
}
private List<V> list(K key) {
return stack().computeIfAbsent(key, k -> new ArrayList<>());
}

View File

@ -74,6 +74,7 @@ import static org.jooq.SQLDialect.DEFAULT;
import static org.jooq.SQLDialect.DERBY;
// ...
import static org.jooq.SQLDialect.FIREBIRD;
// ...
import static org.jooq.SQLDialect.H2;
// ...
import static org.jooq.SQLDialect.HSQLDB;
@ -170,12 +171,16 @@ import static org.jooq.impl.SQLDataType.XML;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
import static org.jooq.impl.Tools.fieldArray;
import static org.jooq.impl.Tools.fieldName;
import static org.jooq.impl.Tools.hasAmbiguousNames;
import static org.jooq.impl.Tools.isNotEmpty;
import static org.jooq.impl.Tools.qualify;
import static org.jooq.impl.Tools.recordType;
import static org.jooq.impl.Tools.selectQueryImpl;
import static org.jooq.impl.Tools.traverseConditions;
import static org.jooq.impl.Tools.traverseJoins;
import static org.jooq.impl.Tools.traverseOrderBy;
import static org.jooq.impl.Tools.traverseSelectList;
import static org.jooq.impl.Tools.unalias;
import static org.jooq.impl.Tools.unqualified;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_COLLECT_SEMI_ANTI_JOIN;
@ -203,12 +208,15 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
@ -244,6 +252,7 @@ import org.jooq.SelectOffsetStep;
import org.jooq.SelectQuery;
import org.jooq.SelectWithTiesStep;
import org.jooq.SortField;
import org.jooq.Support;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableLike;
@ -325,6 +334,8 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@ -379,7 +390,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
private final QueryPartList<Field<?>> unionSeek;
private boolean unionSeekBefore; // [#3579] TODO
private final Limit unionLimit;
private final Map<Table<?>, Table<?>> localTableMapping;
private final Map<QueryPart, QueryPart> localQueryPartMapping;
SelectQueryImpl(Configuration configuration, WithImpl with) {
this(configuration, with, null);
@ -419,7 +430,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
if (from != null)
this.from.add(from.asTable());
this.localTableMapping = new LinkedHashMap<>();
this.localQueryPartMapping = new LinkedHashMap<>();
}
/**
@ -596,10 +607,9 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
private final SelectQueryImpl<R> nest(SelectQueryImpl<R> result, SelectQueryImpl<R> nested, Function<? super SelectQueryImpl<R>, ? extends SelectQueryImpl<R>> nestedFinisher) {
// [#10716] TODO: Qualify all fields with c1, c2, to avoid ambiguous
nested.select.add(DSL.asterisk());
nested = nestedFinisher.apply(nested);
Table<R> t = nested.asTable("t");
traverseJoins(from, t0 -> result.localTableMapping.put(t0, t));
traverseJoins(from, t0 -> result.localQueryPartMapping.put(t0, t));
result.from.add(t);
return result;
@ -1345,7 +1355,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@SuppressWarnings({ "rawtypes", "unchecked" })
final Select<?> distinctOnEmulation() {
private final Select<?> distinctOnEmulation() {
// [#3564] TODO: Extract and merge this with getSelectResolveSomeAsterisks0()
List<Field<?>> partitionBy = new ArrayList<>(distinctOn.size());
@ -1378,6 +1388,39 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
return limit.offset != null ? s1.offset((Param) limit.offset) : s1;
}
@Override
public final void accept(Context<?> ctx) {
Table<?> dmlTable;
@ -1391,7 +1434,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
ctx.visit(DSL.select(asterisk()).from(asTable("t")));
}
// [#3564] Emulate DISINTCT ON queries at the top level
// [#3564] Emulate DISTINCT ON queries at the top level
else if (Tools.isNotEmpty(distinctOn) && EMULATE_DISTINCT_ON.contains(ctx.dialect())) {
ctx.visit(distinctOnEmulation());
}
@ -1418,6 +1461,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
@ -2020,7 +2068,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
context.scopeRegister(t, true);
});
for (Entry<Table<?>, Table<?>> entry : localTableMapping.entrySet())
for (Entry<QueryPart, QueryPart> entry : localQueryPartMapping.entrySet())
context.scopeRegister(entry.getKey(), true, entry.getValue());
// SELECT clause
@ -2317,15 +2365,6 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
context.end(SELECT_HAVING);
// QUALIFY clause
// -------------
if (getQualify().hasWhere())
context.formatSeparator()
.visit(K_QUALIFY)
.sql(' ')
.visit(getQualify());
// WINDOW clause
// -------------
context.start(SELECT_WINDOW);
@ -2338,6 +2377,15 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
context.end(SELECT_WINDOW);
// QUALIFY clause
// -------------
if (getQualify().hasWhere())
context.formatSeparator()
.visit(K_QUALIFY)
.sql(' ')
.visit(getQualify());
// ORDER BY clause for local subselect
// -----------------------------------
toSQLOrderBy(

View File

@ -84,7 +84,7 @@ import org.jooq.tools.StringUtils;
* @author Lukas Eder
*/
@org.jooq.Internal
public class TableImpl<R extends Record> extends AbstractTable<R> {
public class TableImpl<R extends Record> extends AbstractTable<R> implements ScopeMappable, ScopeNestable {
private static final long serialVersionUID = 261033315221985068L;
private static final Clause[] CLAUSES_TABLE_REFERENCE = { TABLE, TABLE_REFERENCE };
@ -305,15 +305,6 @@ public class TableImpl<R extends Record> extends AbstractTable<R> {
}
private void accept0(Context<?> ctx) {
if (ctx.declareTables())
ctx.scopeMarkStart(this);

View File

@ -6090,6 +6090,9 @@ final class Tools {
: field;
}
// TODO: In a new expression tree model, we'll support generic visitors of some sort
// ---------------------------------------------------------------------------------
static final void traverseJoins(Iterable<? extends Table<?>> i, Consumer<? super Table<?>> consumer) {
for (Table<?> t : i)
traverseJoins(t, consumer);
@ -6103,4 +6106,53 @@ final class Tools {
else
consumer.accept(t);
}
static final void traverseSelectList(SelectFieldList<?> list, Consumer<? super Field<?>> consumer) {
// TODO: Traverse expressions as well
for (SelectFieldOrAsterisk f : list)
if (f instanceof Field)
consumer.accept((Field<?>) f);
}
static final void traverseOrderBy(SortFieldList list, Consumer<? super Field<?>> consumer) {
// TODO: Traverse expressions as well
for (SortField<?> f : list)
consumer.accept(((SortFieldImpl<?>) f).getField());
}
static final void traverseConditions(Condition condition, Consumer<? super Field<?>> consumer) {
if (condition instanceof CombinedCondition) {
for (Condition c : ((CombinedCondition) condition).conditions)
traverseConditions(c, consumer);
}
else if (condition instanceof NotCondition) {
traverseConditions(((NotCondition) condition).condition, consumer);
}
else if (condition instanceof BetweenCondition) {
consumer.accept(((BetweenCondition<?>) condition).field);
consumer.accept(((BetweenCondition<?>) condition).minValue);
consumer.accept(((BetweenCondition<?>) condition).maxValue);
}
else if (condition instanceof CompareCondition) {
consumer.accept(((CompareCondition) condition).field1);
consumer.accept(((CompareCondition) condition).field2);
}
else if (condition instanceof FieldCondition) {
consumer.accept(((FieldCondition) condition).field);
}
else if (condition instanceof InCondition) {
consumer.accept(((InCondition<?>) condition).field);
for (Field<?> f : ((InCondition<?>) condition).values)
consumer.accept(f);
}
else if (condition instanceof IsDistinctFrom) {
consumer.accept(((IsDistinctFrom<?>) condition).lhs);
consumer.accept(((IsDistinctFrom<?>) condition).rhs);
}
// TODO: Other conditions
}
}