From 4febe602d83cf721e1ede997e02253c6be14996c Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 17 Mar 2021 18:07:00 +0100 Subject: [PATCH] [jOOQ/jOOQ#5810] Emulate QUALIFY where not supported This includes: - [jOOQ/jOOQ#11663] Wrong order of WINDOW / QUALIFY clauses --- .../main/java/org/jooq/SelectQualifyStep.java | 35 ++++++-- jOOQ/src/main/java/org/jooq/SelectQuery.java | 12 +-- .../java/org/jooq/impl/AbstractContext.java | 5 +- .../org/jooq/impl/AbstractWindowFunction.java | 3 +- .../java/org/jooq/impl/BetweenCondition.java | 22 ++--- .../org/jooq/impl/DefaultRenderContext.java | 30 ++++--- .../main/java/org/jooq/impl/InCondition.java | 18 ++--- .../java/org/jooq/impl/IsDistinctFrom.java | 6 +- .../main/java/org/jooq/impl/NotCondition.java | 2 +- .../java/org/jooq/impl/ScopeMappable.java | 51 ++++++++++++ .../java/org/jooq/impl/ScopeNestable.java | 50 ++++++++++++ .../main/java/org/jooq/impl/ScopeStack.java | 16 +++- .../java/org/jooq/impl/SelectQueryImpl.java | 80 +++++++++++++++---- .../main/java/org/jooq/impl/TableImpl.java | 11 +-- jOOQ/src/main/java/org/jooq/impl/Tools.java | 52 ++++++++++++ 15 files changed, 312 insertions(+), 81 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/ScopeMappable.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/ScopeNestable.java diff --git a/jOOQ/src/main/java/org/jooq/SelectQualifyStep.java b/jOOQ/src/main/java/org/jooq/SelectQualifyStep.java index 2ff5d2893a..995ed12ddb 100644 --- a/jOOQ/src/main/java/org/jooq/SelectQualifyStep.java +++ b/jOOQ/src/main/java/org/jooq/SelectQualifyStep.java @@ -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 extends SelectOrderByStep qualify(Condition condition); /** @@ -123,7 +142,7 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(Condition... conditions); /** @@ -131,14 +150,14 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(Collection conditions); /** * Add a QUALIFY clause to the query. */ @NotNull - @Support({ H2 }) + @Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE }) SelectQualifyConditionStep qualify(Field condition); /** @@ -153,7 +172,7 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(SQL sql); @@ -169,7 +188,7 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(String sql); @@ -186,7 +205,7 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(String sql, Object... bindings); @@ -203,7 +222,7 @@ public interface SelectQualifyStep extends SelectOrderByStep qualify(String sql, QueryPart... parts); diff --git a/jOOQ/src/main/java/org/jooq/SelectQuery.java b/jOOQ/src/main/java/org/jooq/SelectQuery.java index 3d0b42d1ff..388afee120 100644 --- a/jOOQ/src/main/java/org/jooq/SelectQuery.java +++ b/jOOQ/src/main/java/org/jooq/SelectQuery.java @@ -423,7 +423,7 @@ public interface SelectQuery extends Select, 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 extends Select, 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 extends Select, ConditionProvi * * @param conditions The condition */ - @Support({ H2 }) + @Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE }) void addQualify(Collection conditions); /** @@ -452,7 +452,7 @@ public interface SelectQuery extends Select, 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 extends Select, 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 extends Select, ConditionProvi * conditions * @param conditions The condition */ - @Support({ H2 }) + @Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE }) void addQualify(Operator operator, Collection conditions); /** diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index a8daaf22be..3a1317f17e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -226,7 +226,7 @@ abstract class AbstractContext> 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> extends AbstractScope imple @Override public /* non-final */ QueryPart scopeMapping(QueryPart part) { - return null; + return part; } @Override @@ -1011,6 +1011,7 @@ abstract class AbstractContext> extends AbstractScope imple ScopeStackElement(QueryPart part, int scopeLevel) { this.part = part; + this.mapped = part; this.scopeLevel = scopeLevel; } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractWindowFunction.java b/jOOQ/src/main/java/org/jooq/impl/AbstractWindowFunction.java index 14eaa1bfa6..6367b8fff7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractWindowFunction.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractWindowFunction.java @@ -100,7 +100,8 @@ implements WindowPartitionByStep, WindowRowsStep, WindowRowsAndStep, - WindowExcludeStep + WindowExcludeStep, + ScopeMappable { /** diff --git a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java index f2bd18db97..486a20ff02 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java @@ -93,18 +93,18 @@ import org.jooq.SQLDialect; */ final class BetweenCondition extends AbstractCondition implements BetweenAndStep { - 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 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 NO_SUPPORT_SYMMETRIC = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, IGNITE, MARIADB, MYSQL, SQLITE); - private final boolean symmetric; - private final boolean not; - private final Field field; - private final Field minValue; - private Field maxValue; + private final boolean symmetric; + private final boolean not; + final Field field; + final Field minValue; + Field maxValue; BetweenCondition(Field field, Field minValue, boolean not, boolean symmetric) { this.field = nullableIf(false, nullSafe(field, minValue.getDataType())); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index 2311affbc2..15e1f63d80 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -195,21 +195,21 @@ class DefaultRenderContext extends AbstractContext 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 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 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(); diff --git a/jOOQ/src/main/java/org/jooq/impl/InCondition.java b/jOOQ/src/main/java/org/jooq/impl/InCondition.java index ac2e87591c..6c57441fb9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/InCondition.java @@ -99,16 +99,16 @@ import org.jooq.SQLDialect; */ final class InCondition 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 REQUIRES_IN_LIMIT = SQLDialect.supportedBy(FIREBIRD); - private static final Set 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 REQUIRES_IN_LIMIT = SQLDialect.supportedBy(FIREBIRD); + private static final Set NO_SUPPORT_EMPTY_LISTS = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES); - private final Field field; - private final List> values; - private final Comparator comparator; + final Field field; + final List> values; + final Comparator comparator; InCondition(Field field, List> values, Comparator comparator) { this.field = field; diff --git a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java index 967be19ad9..3b8627294b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java @@ -95,9 +95,9 @@ final class IsDistinctFrom extends AbstractCondition { - private final Field lhs; - private final Field rhs; - private final Comparator comparator; + final Field lhs; + final Field rhs; + final Comparator comparator; IsDistinctFrom(Field lhs, Field rhs, Comparator comparator) { this.lhs = lhs; diff --git a/jOOQ/src/main/java/org/jooq/impl/NotCondition.java b/jOOQ/src/main/java/org/jooq/impl/NotCondition.java index 215d328ea0..ddb4007c76 100644 --- a/jOOQ/src/main/java/org/jooq/impl/NotCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/NotCondition.java @@ -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; diff --git a/jOOQ/src/main/java/org/jooq/impl/ScopeMappable.java b/jOOQ/src/main/java/org/jooq/impl/ScopeMappable.java new file mode 100644 index 0000000000..0de4d8b37a --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/ScopeMappable.java @@ -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 { +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ScopeNestable.java b/jOOQ/src/main/java/org/jooq/impl/ScopeNestable.java new file mode 100644 index 0000000000..7c3dff795e --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/ScopeNestable.java @@ -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 { +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java b/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java index b60bc2906d..7e98413389 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java +++ b/jOOQ/src/main/java/org/jooq/impl/ScopeStack.java @@ -184,12 +184,18 @@ final class ScopeStack implements Iterable { } private final V get0(List 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 V getOrThrow(K key, Supplier exception) throws T { @@ -223,6 +229,10 @@ final class ScopeStack implements Iterable { list.set(scopeLevel, value); } + private List listOrNull(K key) { + return stack().get(key); + } + private List list(K key) { return stack().computeIfAbsent(key, k -> new ArrayList<>()); } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index ef4dd8ecd5..4580fcb7bb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -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 extends AbstractResultQuery imp + + @@ -379,7 +390,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp private final QueryPartList> unionSeek; private boolean unionSeekBefore; // [#3579] TODO private final Limit unionLimit; - private final Map, Table> localTableMapping; + private final Map localQueryPartMapping; SelectQueryImpl(Configuration configuration, WithImpl with) { this(configuration, with, null); @@ -419,7 +430,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp if (from != null) this.from.add(from.asTable()); - this.localTableMapping = new LinkedHashMap<>(); + this.localQueryPartMapping = new LinkedHashMap<>(); } /** @@ -596,10 +607,9 @@ final class SelectQueryImpl extends AbstractResultQuery imp private final SelectQueryImpl nest(SelectQueryImpl result, SelectQueryImpl nested, Function, ? extends SelectQueryImpl> nestedFinisher) { // [#10716] TODO: Qualify all fields with c1, c2, to avoid ambiguous - nested.select.add(DSL.asterisk()); nested = nestedFinisher.apply(nested); Table 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 extends AbstractResultQuery imp @SuppressWarnings({ "rawtypes", "unchecked" }) - final Select distinctOnEmulation() { + private final Select distinctOnEmulation() { // [#3564] TODO: Extract and merge this with getSelectResolveSomeAsterisks0() List> partitionBy = new ArrayList<>(distinctOn.size()); @@ -1378,6 +1388,39 @@ final class SelectQueryImpl extends AbstractResultQuery 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 extends AbstractResultQuery 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 extends AbstractResultQuery imp + + + + + @@ -2020,7 +2068,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp context.scopeRegister(t, true); }); - for (Entry, Table> entry : localTableMapping.entrySet()) + for (Entry entry : localQueryPartMapping.entrySet()) context.scopeRegister(entry.getKey(), true, entry.getValue()); // SELECT clause @@ -2317,15 +2365,6 @@ final class SelectQueryImpl extends AbstractResultQuery 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 extends AbstractResultQuery 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( diff --git a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java index 63fc408096..bf71b18555 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java @@ -84,7 +84,7 @@ import org.jooq.tools.StringUtils; * @author Lukas Eder */ @org.jooq.Internal -public class TableImpl extends AbstractTable { +public class TableImpl extends AbstractTable 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 extends AbstractTable { } private void accept0(Context ctx) { - - - - - - - - - if (ctx.declareTables()) ctx.scopeMarkStart(this); diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 43eddad8ac..2b94c9eb9d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -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> i, Consumer> 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> 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> consumer) { + + // TODO: Traverse expressions as well + for (SortField f : list) + consumer.accept(((SortFieldImpl) f).getField()); + } + + static final void traverseConditions(Condition condition, Consumer> 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 + } }