From 1e5e4f5bf043e8488306ded12d0e99dfc0c552b0 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 2 Aug 2013 19:40:15 +0200 Subject: [PATCH] [#2665] [#2667] [#2676] First API and implementation draft * [#2665] Implement SPI for RenderContext and BindContext listening to allow for custom SQL transformation * [#2667] Add org.jooq.Clause and let org.jooq.Context listen on start(Clause) and end(Clause) events - First API and implementation draft * [#2676] Add QueryPartInternal.clause() to allow for QueryParts to return Clause information to org.jooq.Context --- jOOQ/src/main/java/org/jooq/Clause.java | 252 ++++++++++++++++++ .../src/main/java/org/jooq/Configuration.java | 27 ++ jOOQ/src/main/java/org/jooq/Context.java | 9 + .../main/java/org/jooq/QueryPartInternal.java | 15 ++ .../main/java/org/jooq/RecordListener.java | 4 +- .../java/org/jooq/RecordListenerProvider.java | 4 +- jOOQ/src/main/java/org/jooq/VisitContext.java | 128 +++++++++ .../src/main/java/org/jooq/VisitListener.java | 111 ++++++++ .../java/org/jooq/VisitListenerProvider.java | 72 +++++ .../java/org/jooq/impl/AbstractContext.java | 168 +++++++++++- .../jooq/impl/AbstractDelegatingQuery.java | 11 + .../java/org/jooq/impl/AbstractField.java | 7 + .../java/org/jooq/impl/AbstractFunction.java | 8 + .../java/org/jooq/impl/AbstractRoutine.java | 7 + .../org/jooq/impl/AbstractStoreQuery.java | 13 +- jOOQ/src/main/java/org/jooq/impl/Alias.java | 7 + .../main/java/org/jooq/impl/ArrayTable.java | 12 + .../org/jooq/impl/ArrayTableSimulation.java | 7 + .../java/org/jooq/impl/BetweenCondition.java | 12 + .../org/jooq/impl/CaseConditionStepImpl.java | 8 + .../main/java/org/jooq/impl/CatalogImpl.java | 8 + .../java/org/jooq/impl/CombinedCondition.java | 9 + .../java/org/jooq/impl/CompareCondition.java | 7 + .../org/jooq/impl/ConditionProviderImpl.java | 6 + .../src/main/java/org/jooq/impl/Contains.java | 12 + .../java/org/jooq/impl/CustomCondition.java | 15 ++ .../main/java/org/jooq/impl/CustomField.java | 13 +- .../java/org/jooq/impl/CustomQueryPart.java | 15 ++ jOOQ/src/main/java/org/jooq/impl/DSL.java | 3 +- .../org/jooq/impl/DefaultConfiguration.java | 151 ++++++++--- .../java/org/jooq/impl/DefaultDSLContext.java | 8 +- .../impl/DefaultVisitListenerProvider.java | 102 +++++++ .../java/org/jooq/impl/DeleteQueryImpl.java | 7 + .../src/main/java/org/jooq/impl/DivideBy.java | 3 +- jOOQ/src/main/java/org/jooq/impl/Dual.java | 8 + .../main/java/org/jooq/impl/Expression.java | 3 +- .../java/org/jooq/impl/FalseCondition.java | 8 + .../java/org/jooq/impl/FieldCondition.java | 7 + .../java/org/jooq/impl/FieldMapForInsert.java | 7 + .../java/org/jooq/impl/FieldMapForUpdate.java | 14 +- .../org/jooq/impl/FieldMapsForInsert.java | 7 + jOOQ/src/main/java/org/jooq/impl/Fields.java | 14 +- .../java/org/jooq/impl/FlashbackTable.java | 7 + .../src/main/java/org/jooq/impl/Function.java | 20 +- .../java/org/jooq/impl/FunctionTable.java | 8 + .../main/java/org/jooq/impl/InCondition.java | 15 +- .../java/org/jooq/impl/InsertQueryImpl.java | 16 +- .../org/jooq/impl/InsertSelectQueryImpl.java | 10 +- .../java/org/jooq/impl/IsDistinctFrom.java | 7 + jOOQ/src/main/java/org/jooq/impl/IsNull.java | 11 +- .../main/java/org/jooq/impl/JoinTable.java | 114 ++++++-- .../main/java/org/jooq/impl/KeywordImpl.java | 6 + jOOQ/src/main/java/org/jooq/impl/Limit.java | 7 + .../main/java/org/jooq/impl/MergeImpl.java | 16 +- .../src/main/java/org/jooq/impl/NameImpl.java | 8 + .../main/java/org/jooq/impl/NotCondition.java | 8 + .../main/java/org/jooq/impl/PackageImpl.java | 8 + .../java/org/jooq/impl/ParameterImpl.java | 12 +- jOOQ/src/main/java/org/jooq/impl/Pivot.java | 17 ++ .../java/org/jooq/impl/QualifiedTable.java | 8 + .../impl/QuantifiedComparisonCondition.java | 8 + .../org/jooq/impl/QuantifiedSelectImpl.java | 7 + .../java/org/jooq/impl/QueryPartList.java | 36 ++- .../main/java/org/jooq/impl/RegexpLike.java | 16 +- jOOQ/src/main/java/org/jooq/impl/Rollup.java | 3 +- .../org/jooq/impl/RowBetweenCondition.java | 27 +- .../main/java/org/jooq/impl/RowCondition.java | 13 + jOOQ/src/main/java/org/jooq/impl/RowImpl.java | 11 +- .../java/org/jooq/impl/RowInCondition.java | 15 ++ .../main/java/org/jooq/impl/RowIsNull.java | 14 + .../org/jooq/impl/RowOverlapsCondition.java | 13 + .../org/jooq/impl/RowSubqueryCondition.java | 13 + .../main/java/org/jooq/impl/SQLCondition.java | 13 + .../src/main/java/org/jooq/impl/SQLField.java | 13 + .../src/main/java/org/jooq/impl/SQLQuery.java | 11 + .../java/org/jooq/impl/SQLResultQuery.java | 11 + .../src/main/java/org/jooq/impl/SQLTable.java | 13 + .../main/java/org/jooq/impl/SQLTemplate.java | 6 + .../main/java/org/jooq/impl/SchemaImpl.java | 12 +- .../java/org/jooq/impl/SelectFieldList.java | 8 +- .../impl/SelectQueryAsExistsCondition.java | 10 + .../org/jooq/impl/SelectQueryAsField.java | 6 + .../impl/SelectQueryAsSubQueryCondition.java | 8 + .../org/jooq/impl/SelectQueryAsTable.java | 6 + .../java/org/jooq/impl/SelectQueryImpl.java | 135 +++++++--- .../java/org/jooq/impl/SortFieldImpl.java | 11 +- .../java/org/jooq/impl/SortFieldList.java | 4 +- .../main/java/org/jooq/impl/TableAlias.java | 8 + .../java/org/jooq/impl/TableFieldImpl.java | 8 + .../main/java/org/jooq/impl/TableImpl.java | 18 +- .../main/java/org/jooq/impl/TableList.java | 6 +- .../java/org/jooq/impl/TrueCondition.java | 8 + .../main/java/org/jooq/impl/TruncateImpl.java | 8 + jOOQ/src/main/java/org/jooq/impl/UDTImpl.java | 8 + jOOQ/src/main/java/org/jooq/impl/Union.java | 18 +- .../java/org/jooq/impl/UpdateQueryImpl.java | 36 ++- jOOQ/src/main/java/org/jooq/impl/Values.java | 7 + .../main/java/org/jooq/impl/WithTable.java | 8 + .../main/java/org/jooq/impl/WrappedList.java | 6 + .../java/org/jooq/test/VisitContextTest.java | 133 +++++++++ 100 files changed, 2134 insertions(+), 188 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/Clause.java create mode 100644 jOOQ/src/main/java/org/jooq/VisitContext.java create mode 100644 jOOQ/src/main/java/org/jooq/VisitListener.java create mode 100644 jOOQ/src/main/java/org/jooq/VisitListenerProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultVisitListenerProvider.java create mode 100644 jOOQ/src/test/java/org/jooq/test/VisitContextTest.java diff --git a/jOOQ/src/main/java/org/jooq/Clause.java b/jOOQ/src/main/java/org/jooq/Clause.java new file mode 100644 index 0000000000..6dc1f382c6 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Clause.java @@ -0,0 +1,252 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq; + +/** + * TODO [#2667] + */ +public enum Clause { + + /** + * A placeholder clause for cases where the behaviour was not yet specified. + * This will not go public, and is meant to be a placeholder during + * development. + */ + @Deprecated + DUMMY, + + // ------------------------------------------------------------------------- + // Clauses used in a any type of statement to model catalog references + // ------------------------------------------------------------------------- + + CATALOG, + + // ------------------------------------------------------------------------- + // Clauses used in a any type of statement to model schema references + // ------------------------------------------------------------------------- + + SCHEMA, + + // ------------------------------------------------------------------------- + // Clauses used in a any type of statement to model table references + // ------------------------------------------------------------------------- + + /** + * A complete table reference. + *

+ * This "clause" surrounds a complete table reference as it can be encountered + * in + *

+ */ + TABLE, + + /** + * + */ + TABLE_JOIN, + TABLE_JOIN_INNER, + TABLE_JOIN_CROSS, + TABLE_JOIN_NATURAL, + TABLE_JOIN_OUTER_LEFT, + TABLE_JOIN_OUTER_RIGHT, + TABLE_JOIN_OUTER_FULL, + TABLE_JOIN_NATURAL_OUTER_LEFT, + TABLE_JOIN_NATURAL_OUTER_RIGHT, + TABLE_JOIN_ON, + TABLE_JOIN_USING, + TABLE_JOIN_PARTITION_BY, + + TABLE_FLASHBACK, + TABLE_PIVOT, + + // ------------------------------------------------------------------------- + // Clauses used in a any type of statement to model column references + // ------------------------------------------------------------------------- + + FIELD, + FIELD_CASE, + FIELD_ROW, + + // ------------------------------------------------------------------------- + // Clauses used in a any type of statement to model condition references + // ------------------------------------------------------------------------- + + CONDITION, + + CONDITION_NULL, + CONDITION_NULL_NOT, + // TODO: Should operators be distinguished? + // - LIKE predicate + // - Subselect predicates + // - RVE predicates + // - Quantified predicates + CONDITION_COMPARISON, + CONDITION_BETWEEN, + CONDITION_DISTINCT, + CONDITION_DISTINCT_NOT, + CONDITION_OVERLAPS, + + CONDITION_AND, + CONDITION_OR, + CONDITION_NOT, + + CONDITION_IN, + CONDITION_IN_NOT, + CONDITION_EXISTS, + CONDITION_EXISTS_NOT, + + // ------------------------------------------------------------------------- + // Clauses that are used in a SELECT statement + // ------------------------------------------------------------------------- + + /** + * A complete SELECT statement or a subselect. + *

+ * This "clause" surrounds a complete SELECT statement, a + * subselect, or a set operation, such as + *

+ */ + SELECT, + + /** + * A UNION set operation. + *

+ * This clause surrounds two or more subselects (see {@link #SELECT}) + * concatenating them using a UNION set operation. + */ + SELECT_UNION, + + /** + * A UNION ALL set operation. + *

+ * This clause surrounds two or more subselects (see {@link #SELECT}) + * concatenating them using a UNION ALL set operation. + */ + SELECT_UNION_ALL, + + /** + * A INTERSECT set operation. + *

+ * This clause surrounds two or more subselects (see {@link #SELECT}) + * concatenating them using a INTERSECT set operation. + */ + SELECT_INTERSECT, + + /** + * A EXCEPT set operation. + *

+ * This clause surrounds two or more subselects (see {@link #SELECT}) + * concatenating them using a EXCEPT set operation. + */ + SELECT_EXCEPT, + + /** + * A SELECT clause within a {@link #SELECT} statement or + * subselect. + *

+ * This clause surrounds + *

+ */ + SELECT_SELECT, + + /** + * A FROM clause within a {@link #SELECT} statement or + * subselect. + *

+ * This clause surrounds + *

+ *

+ * See {@link #TABLE} and related clauses for possible table references. + * + * @see #TABLE + */ + SELECT_FROM, + SELECT_WHERE, + SELECT_START_WITH, + SELECT_CONNECT_BY, + SELECT_GROUP_BY, + SELECT_HAVING, + SELECT_ORDER_BY, + + + + INSERT, + INSERT_INSERT_INTO, + INSERT_RETURNING, + + + + UPDATE, + UPDATE_UPDATE, + UPDATE_SET, + UPDATE_SET_ASSIGNMENT, + UPDATE_WHERE, + UPDATE_RETURNING, + + + DELETE, + DELETE_DELETE, + DELETE_WHERE, + + + MERGE, + MERGE_MERGE_INTO, + + + + TRUNCATE, + TRUNCATE_TRUNCATE, +} \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/Configuration.java b/jOOQ/src/main/java/org/jooq/Configuration.java index 9bb4287c24..ae516cb2e9 100644 --- a/jOOQ/src/main/java/org/jooq/Configuration.java +++ b/jOOQ/src/main/java/org/jooq/Configuration.java @@ -194,6 +194,11 @@ public interface Configuration extends Serializable { */ ExecuteListenerProvider[] executeListenerProviders(); + /** + * TODO [#2667] + */ + VisitListenerProvider[] visitListenerProviders(); + /** * Retrieve the configured schema mapping. * @@ -264,6 +269,18 @@ public interface Configuration extends Serializable { */ Configuration set(ExecuteListenerProvider... newExecuteListenerProviders); + /** + * Change this configuration to hold a new visit listener providers. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newVisitListenerProviders The new visit listener providers to + * be contained in the changed configuration. + * @return The changed configuration. + */ + Configuration set(VisitListenerProvider... newVisitListenerProviders); + /** * Change this configuration to hold a new dialect. *

@@ -340,6 +357,16 @@ public interface Configuration extends Serializable { */ Configuration derive(ExecuteListenerProvider... newExecuteListenerProviders); + /** + * Create a derived configuration from this one, with new visit listener + * providers. + * + * @param newVisitListenerProviders The new visit listener providers to + * be contained in the derived configuration. + * @return The derived configuration. + */ + Configuration derive(VisitListenerProvider... newVisitListenerProviders); + /** * Create a derived configuration from this one, with a new dialect. * diff --git a/jOOQ/src/main/java/org/jooq/Context.java b/jOOQ/src/main/java/org/jooq/Context.java index 7a83e34131..3763c021f4 100644 --- a/jOOQ/src/main/java/org/jooq/Context.java +++ b/jOOQ/src/main/java/org/jooq/Context.java @@ -120,6 +120,15 @@ public interface Context> { */ C visit(QueryPart part) throws DataAccessException; + /** + * TODO [#2667] + * + * Properties of these methods: + * - A clause is always started / ended, even if it isn't rendered or if it's empty! + */ + C start(Clause clause); + C end(Clause clause); + /** * Whether the current context is rendering a SQL field declaration (e.g. a * {@link Field} in the SELECT clause of the query). diff --git a/jOOQ/src/main/java/org/jooq/QueryPartInternal.java b/jOOQ/src/main/java/org/jooq/QueryPartInternal.java index 9ce41b4008..f521c5c9db 100644 --- a/jOOQ/src/main/java/org/jooq/QueryPartInternal.java +++ b/jOOQ/src/main/java/org/jooq/QueryPartInternal.java @@ -69,6 +69,21 @@ public interface QueryPartInternal extends QueryPart { */ void bind(BindContext ctx) throws DataAccessException; + /** + * The {@link Clause} that is represented by this query part. + *

+ * {@link QueryPart}s can specify a Clause for which an event + * will be emitted {@link Context#start(Clause) before} and + * {@link Context#end(Clause) after} visiting the the query part through + * {@link Context#visit(QueryPart)} + *

+ * This method is for JOOQ INTERNAL USE only. Do not reference directly + * + * @return The Clause represented by this query part or + * null if this query part does not represent a clause. + */ + Clause clause(); + /** * Check whether this {@link QueryPart} is able to declare fields in a * SELECT clause. diff --git a/jOOQ/src/main/java/org/jooq/RecordListener.java b/jOOQ/src/main/java/org/jooq/RecordListener.java index ed2e849463..f74cab20cf 100644 --- a/jOOQ/src/main/java/org/jooq/RecordListener.java +++ b/jOOQ/src/main/java/org/jooq/RecordListener.java @@ -35,6 +35,8 @@ */ package org.jooq; +import java.util.EventListener; + /** * A listener for manipulation events on {@link UpdatableRecord}s. *

@@ -52,7 +54,7 @@ package org.jooq; * * @author Lukas Eder */ -public interface RecordListener { +public interface RecordListener extends EventListener { /** * Called before storing an UpdatableRecord. diff --git a/jOOQ/src/main/java/org/jooq/RecordListenerProvider.java b/jOOQ/src/main/java/org/jooq/RecordListenerProvider.java index c290fbebfb..d839832573 100644 --- a/jOOQ/src/main/java/org/jooq/RecordListenerProvider.java +++ b/jOOQ/src/main/java/org/jooq/RecordListenerProvider.java @@ -43,8 +43,8 @@ import org.jooq.impl.DefaultRecordListenerProvider; * In order to facilitate the lifecycle management of * RecordListener instances that are provided to a jOOQ * {@link Configuration}, clients can implement this API. To jOOQ, it is thus - * irrelevant, if execute listeners are stateful or stateless, local to a - * single record or record manipulation, or global to an application. + * irrelevant, if execute listeners are stateful or stateless, local to a single + * record or record manipulation, or global to an application. * * @author Lukas Eder * @see RecordListener diff --git a/jOOQ/src/main/java/org/jooq/VisitContext.java b/jOOQ/src/main/java/org/jooq/VisitContext.java new file mode 100644 index 0000000000..ab44546eb1 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/VisitContext.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq; + +import java.util.Map; + +/** + * A context object for {@link QueryPart} traversal passed to registered + * {@link VisitListener}'s. + * + * @author Lukas Eder + * @see VisitListener + */ +public interface VisitContext { + + /** + * Get all custom data from this VisitContext. + *

+ * This corresponds to {@link Context#data()} returned from + * {@link #context()}. + * + * @return The custom data. This is never null + * @see VisitListener + */ + Map data(); + + /** + * Get some custom data from this VisitContext. + *

+ * This corresponds to {@link Context#data(Object)} returned from + * {@link #context()}. + * + * @param key A key to identify the custom data + * @return The custom data or null if no such data is contained + * in this VisitListener + * @see VisitListener + */ + Object data(Object key); + + /** + * Set some custom data to this VisitContext. + *

+ * This corresponds to {@link Context#data(Object, Object)} returned from + * {@link #context()}. + * + * @param key A key to identify the custom data + * @param value The custom data or null to unset the custom + * data + * @return The previously set custom data or null if no data + * was previously set for the given key + * @see VisitContext + */ + Object data(Object key, Object value); + + /** + * The configuration wrapped by this context. + */ + Configuration configuration(); + + /** + * The most recent clause that was encountered through + * {@link Context#start(Clause)}. + */ + Clause clause(); + + /** + * All previous clauses. + *

+ * This returns all previous clauses that were encountered through + * {@link Context#start(Clause)} and that haven't been removed yet through + * {@link Context#end(Clause)}. In other words, VisitContext + * contains a stack of clauses. + */ + Clause[] clauses(); + + /** + * The {@link QueryPart} that is being visited. + */ + QueryPart visiting(); + + /** + * The underlying {@link RenderContext} or {@link BindContext} object. + */ + Context context(); + + /** + * The underlying {@link RenderContext} or null, if the underlying context is a {@link BindContext}. + */ + RenderContext renderContext(); + + /** + * The underlying {@link BindContext} or null, if the underlying context is a {@link RenderContext}. + */ + BindContext bindContext(); +} diff --git a/jOOQ/src/main/java/org/jooq/VisitListener.java b/jOOQ/src/main/java/org/jooq/VisitListener.java new file mode 100644 index 0000000000..6ba375f5f4 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/VisitListener.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq; + +import java.sql.PreparedStatement; +import java.util.EventListener; + +/** + * A listener for {@link QueryPart} traversal events. + *

+ * Users may want to centrally inject custom behaviour when rendering their + * {@link QueryPart} objects or when binding values to {@link PreparedStatement} + * s. This service provider allows to hook in callback method implementations + * before or after these events: + *

+ *

+ * The following rules apply to visiting clauses and query parts: + *

+ *

+ * An example is given here: + *

SELECT 1 FROM [A CROSS JOIN B]
+ *

+ * The above example will create the following set of events: + * + *

+ * {@link Clause#SELECT}
+ * +-{@link Clause#SELECT_SELECT}
+ * | +-{@link Clause#FIELD}
+ * |   +-val(1)
+ * +-{@link Clause#SELECT_FROM}
+ *   +-{@link Clause#TABLE_JOIN}
+ *     +-{@link Clause#TABLE}
+ *     | +-table("A")
+ *     +-{@link Clause#TABLE_JOIN_CROSS}
+ *       +-{@link Clause#TABLE}
+ *         +-table("B")
+ * 
+ *

+ * Whatever is not a {@link Clause} in the above example is a {@link QueryPart}. + * + * @author Lukas Eder + */ +public interface VisitListener extends EventListener { + + /** + * Called before entering a {@link Clause}. + * + * @see Context#start(Clause) + */ + void clauseStart(VisitContext context); + + /** + * Called after leaving a {@link Clause}. + * + * @see Context#end(Clause) + */ + void clauseEnd(VisitContext context); + + /** + * Called before visiting a {@link QueryPart}. + * + * @see Context#visit(QueryPart) + */ + void visitStart(VisitContext context); + + /** + * Called after visiting a {@link QueryPart}. + * + * @see Context#visit(QueryPart) + */ + void visitEnd(VisitContext context); +} diff --git a/jOOQ/src/main/java/org/jooq/VisitListenerProvider.java b/jOOQ/src/main/java/org/jooq/VisitListenerProvider.java new file mode 100644 index 0000000000..223dcfee3b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/VisitListenerProvider.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq; + +import org.jooq.impl.DefaultVisitListenerProvider; + +/** + * A provider for {@link VisitListener} instances. + *

+ * In order to facilitate the lifecycle management of VisitListener + * instances that are provided to a jOOQ {@link Configuration}, clients can + * implement this API. To jOOQ, it is thus irrelevant, if execute listeners are + * stateful or stateless, local to a single record or record manipulation, or + * global to an application. + * + * @author Lukas Eder + * @see VisitListener + * @see Configuration + */ +public interface VisitListenerProvider { + + /** + * Provide a VisitListener instance. + *

+ * Implementations are free to choose whether this method returns new + * instances at every call or whether the same instance is returned + * repetitively. + *

+ * A VisitListener shall be provided exactly once per + * Context traversal, i.e. per RenderContext or + * BindContext. + * + * @return A VisitListener instance. + * @see VisitListener + * @see VisitContext + * @see DefaultVisitListenerProvider + */ + VisitListener provide(); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index eed465cf16..df0ef2e9c0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -35,13 +35,22 @@ */ package org.jooq.impl; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.HashMap; import java.util.Map; +import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Context; import org.jooq.QueryPart; import org.jooq.QueryPartInternal; +import org.jooq.RenderContext; +import org.jooq.Table; +import org.jooq.VisitContext; +import org.jooq.VisitListener; +import org.jooq.VisitListenerProvider; /** * @author Lukas Eder @@ -49,16 +58,32 @@ import org.jooq.QueryPartInternal; @SuppressWarnings("unchecked") abstract class AbstractContext> implements Context { - final Configuration configuration; - final Map data; - boolean declareFields; - boolean declareTables; - boolean subquery; - int index; + final Configuration configuration; + final Map data; + final VisitListener[] visitListeners; + + private final DefaultVisitContext visitContext; + private final Deque visitClauses; + private final Deque visitParts; + + boolean declareFields; + boolean declareTables; + boolean subquery; + int index; AbstractContext(Configuration configuration) { + VisitListenerProvider[] providers = configuration.visitListenerProviders(); + this.configuration = configuration; this.data = new HashMap(); + this.visitListeners = new VisitListener[providers.length]; + this.visitContext = new DefaultVisitContext(); + this.visitClauses = new ArrayDeque(); + this.visitParts = new ArrayDeque(); + + for (int i = 0; i < providers.length; i++) { + this.visitListeners[i] = providers[i].provide(); + } } // ------------------------------------------------------------------------ @@ -85,9 +110,112 @@ abstract class AbstractContext> implements Context { return data.put(key, value); } + /** + * TODO [#2667] This is a draft implementation. The actual implementation + * may change + */ + private class DefaultVisitContext implements VisitContext { + + @Override + public final Map data() { + return AbstractContext.this.data(); + } + + @Override + public final Object data(Object key) { + return AbstractContext.this.data(key); + } + + @Override + public final Object data(Object key, Object value) { + return AbstractContext.this.data(key, value); + } + + @Override + public final Configuration configuration() { + return AbstractContext.this.configuration(); + } + + @Override + public final Clause clause() { + return visitClauses.peekLast(); + } + + @Override + public final Clause[] clauses() { + return visitClauses.toArray(new Clause[visitClauses.size()]); + } + + @Override + public final QueryPart visiting() { + return visitParts.peekLast(); + } + + @Override + public final Context context() { + return AbstractContext.this; + } + + @Override + public final RenderContext renderContext() { + return (RenderContext) (AbstractContext.this instanceof RenderContext ? AbstractContext.this : null); + } + + @Override + public final BindContext bindContext() { + return (BindContext) (AbstractContext.this instanceof BindContext ? AbstractContext.this : null); + } + } + + @Override + public final C start(Clause clause) { + visitClauses.addLast(clause); + + for (VisitListener listener : visitListeners) { + listener.clauseStart(visitContext); + } + + return (C) this; + } + + @Override + public final C end(Clause clause) { + for (VisitListener listener : visitListeners) { + listener.clauseEnd(visitContext); + } + + if (visitClauses.removeLast() != clause) + throw new IllegalStateException("Mismatch between visited clauses!"); + + return (C) this; + } + + private final void start(QueryPart part) { + visitParts.addLast(part); + + for (VisitListener listener : visitListeners) { + listener.visitStart(visitContext); + } + } + + private final void end(QueryPart part) { + for (VisitListener listener : visitListeners) { + listener.visitEnd(visitContext); + } + + if (visitParts.removeLast() != part) + throw new RuntimeException("Mismatch between visited query parts"); + } + @Override public final C visit(QueryPart part) { if (part != null) { + Clause clause = visitListeners.length > 0 ? clause(part) : null; + + if (clause != null) + start(clause); + + start(part); QueryPartInternal internal = (QueryPartInternal) part; // If this is supposed to be a declaration section and the part isn't @@ -111,11 +239,39 @@ abstract class AbstractContext> implements Context { else { visit0(internal); } + + end(part); + if (clause != null) + end(clause); } return (C) this; } + /** + * Emit a clause from a query part being visited. + *

+ * This method returns a clause to emit as a surrounding event before / + * after visiting a query part. This is needed for all reusable query parts, + * whose clause type is ambiguous at the container site. An example: + *

+ *

SELECT * FROM [A CROSS JOIN B]
+ *

+ * The type of the above JoinTable modelling + * A CROSS JOIN B is not known to the surrounding + * SELECT statement, which only knows {@link Table} types. The + * {@link Clause#TABLE_JOIN} event that is required to be emitted around the + * {@link Context#visit(QueryPart)} event has to be issued here in + * AbstractContext. + */ + private final Clause clause(QueryPart part) { + if (part instanceof QueryPartInternal) { + return ((QueryPartInternal) part).clause(); + } + + return null; + } + protected abstract void visit0(QueryPartInternal internal); @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java index 144b4a43d6..7fef0a418f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDelegatingQuery.java @@ -40,9 +40,11 @@ import java.util.Map; import org.jooq.AttachableInternal; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Param; import org.jooq.Query; +import org.jooq.QueryPartInternal; import org.jooq.RenderContext; import org.jooq.conf.ParamType; @@ -95,6 +97,15 @@ abstract class AbstractDelegatingQuery extends AbstractQueryPar context.visit(delegate); } + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return null; + } + @Override public final String getSQL() { return delegate.getSQL(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 3040bf0053..04c5106614 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.Comparator.EQUALS; import static org.jooq.Comparator.GREATER; import static org.jooq.Comparator.GREATER_OR_EQUAL; @@ -72,6 +73,7 @@ import org.jooq.BetweenAndStep; import org.jooq.BindContext; import org.jooq.CaseValueStep; import org.jooq.CaseWhenStep; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Configuration; @@ -119,6 +121,11 @@ abstract class AbstractField extends AbstractQueryPart implements Field { @Override public abstract void bind(BindContext context); + @Override + public /* non-final for now */ Clause clause() { + return DUMMY; + } + // ------------------------------------------------------------------------ // XXX: API // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractFunction.java b/jOOQ/src/main/java/org/jooq/impl/AbstractFunction.java index aaaef55fd6..9addece115 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractFunction.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractFunction.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.DataType; import org.jooq.Field; @@ -72,6 +75,11 @@ abstract class AbstractFunction extends AbstractField { ctx.visit(getFunction0(ctx.configuration())); } + @Override + public final Clause clause() { + return DUMMY; + } + final Field[] getArguments() { return arguments; } diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java index 38022decad..e6bd65b5e0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractRoutine.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.SQLDialect.SQLSERVER; import static org.jooq.impl.DSL.function; @@ -58,6 +59,7 @@ import org.jooq.AggregateFunction; import org.jooq.ArrayRecord; import org.jooq.AttachableInternal; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.DSLContext; import org.jooq.DataType; @@ -290,6 +292,11 @@ public abstract class AbstractRoutine extends AbstractQueryPart implements Ro } } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final void bind(BindContext context) { for (Parameter parameter : getParameters()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java index 3d5d79339c..f0efe207a0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractStoreQuery.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.Utils.fieldArray; import static org.jooq.util.sqlite.SQLiteDSL.rowid; @@ -49,6 +50,7 @@ import java.util.List; import java.util.Map; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.DSLContext; import org.jooq.ExecuteContext; @@ -83,7 +85,7 @@ abstract class AbstractStoreQuery extends AbstractQuery implem super(configuration); this.into = into; - this.returning = new QueryPartList>(); + this.returning = new QueryPartList>(DUMMY); } protected abstract Map, Field> getValues(); @@ -147,14 +149,17 @@ abstract class AbstractStoreQuery extends AbstractQuery implem return returned; } - final void toSQLReturning(RenderContext context) { + final void toSQLReturning(RenderContext context, Clause clause) { if (!returning.isEmpty()) { switch (context.configuration().dialect()) { case FIREBIRD: case POSTGRES: context.formatSeparator() - .keyword("returning ") - .visit(returning); + .start(clause) + .keyword("returning") + .sql(" ") + .visit(returning) + .end(clause); break; default: diff --git a/jOOQ/src/main/java/org/jooq/impl/Alias.java b/jOOQ/src/main/java/org/jooq/impl/Alias.java index b3f4691c81..d283795a3e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Alias.java +++ b/jOOQ/src/main/java/org/jooq/impl/Alias.java @@ -37,6 +37,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.FIREBIRD; @@ -55,6 +56,7 @@ import static org.jooq.impl.DSL.select; import static org.jooq.impl.Utils.list; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; import org.jooq.Record; import org.jooq.RenderContext; @@ -221,6 +223,11 @@ class Alias extends AbstractQueryPart { } } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final boolean declaresFields() { return true; diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java b/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java index 5ac6e60ce7..e936369861 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayTable.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.fieldByName; import java.util.ArrayList; @@ -42,6 +43,7 @@ import java.util.List; import org.jooq.ArrayRecord; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.Param; @@ -173,6 +175,11 @@ class ArrayTable extends AbstractTable { ctx.visit(table(ctx.configuration())); } + @Override + public final Clause clause() { + return DUMMY; + } + private final Table table(Configuration configuration) { switch (configuration.dialect().family()) { case ORACLE: { @@ -296,6 +303,11 @@ class ArrayTable extends AbstractTable { context.visit(array); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override final Fields fields0() { return ArrayTable.this.fields0(); diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java b/jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java index 62a72ca386..e7f3502851 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java @@ -35,12 +35,14 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.falseCondition; import static org.jooq.impl.DSL.fieldByName; import static org.jooq.impl.DSL.one; import static org.jooq.impl.DSL.using; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.Record; @@ -126,6 +128,11 @@ class ArrayTableSimulation extends AbstractTable { ctx.visit(table(ctx.configuration())); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override final Fields fields0() { return field; diff --git a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java index a51660a8aa..1256cd8143 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/BetweenCondition.java @@ -37,6 +37,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_BETWEEN; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -53,6 +54,7 @@ import static org.jooq.impl.DSL.val; import org.jooq.BetweenAndStep; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -106,6 +108,11 @@ class BetweenCondition extends AbstractCondition implements BetweenAndStep delegate(ctx.configuration()).toSQL(ctx); } + @Override + public final Clause clause() { + return CONDITION_BETWEEN; + } + private final QueryPartInternal delegate(Configuration configuration) { if (symmetric && asList(ASE, CUBRID, DB2, DERBY, FIREBIRD, H2, MARIADB, MYSQL, ORACLE, SQLSERVER, SQLITE, SYBASE).contains(configuration.dialect().family())) { if (not) { @@ -142,5 +149,10 @@ class BetweenCondition extends AbstractCondition implements BetweenAndStep public final void bind(BindContext context) { context.visit(field).visit(minValue).visit(maxValue); } + + @Override + public final Clause clause() { + return CONDITION_BETWEEN; + } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/CaseConditionStepImpl.java b/jOOQ/src/main/java/org/jooq/impl/CaseConditionStepImpl.java index d1d233a463..b0d5193836 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CaseConditionStepImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CaseConditionStepImpl.java @@ -35,11 +35,14 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; import org.jooq.CaseConditionStep; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Field; import org.jooq.RenderContext; @@ -137,4 +140,9 @@ class CaseConditionStepImpl extends AbstractField implements CaseCondition context.keyword("end") .formatIndentLockEnd(); } + + @Override + public final Clause clause() { + return DUMMY; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java b/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java index 21e37b18ab..3bee3beba7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java @@ -35,11 +35,14 @@ */ package org.jooq.impl; +import static org.jooq.Clause.CATALOG; + import java.util.Collections; import java.util.List; import org.jooq.BindContext; import org.jooq.Catalog; +import org.jooq.Clause; import org.jooq.RenderContext; import org.jooq.Schema; import org.jooq.tools.StringUtils; @@ -78,6 +81,11 @@ public class CatalogImpl extends AbstractQueryPart implements Catalog { context.literal(getName()); } + @Override + public final Clause clause() { + return CATALOG; + } + @Override public final Schema getSchema(String name) { for (Schema schema : getSchemas()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java index c2d9f05410..dcc25a34c8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java @@ -36,6 +36,9 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_AND; +import static org.jooq.Clause.CONDITION_OR; +import static org.jooq.Operator.AND; import static org.jooq.impl.DSL.trueCondition; import static org.jooq.impl.Utils.visitAll; @@ -44,6 +47,7 @@ import java.util.Collection; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Operator; import org.jooq.RenderContext; @@ -94,6 +98,11 @@ class CombinedCondition extends AbstractCondition { } } + @Override + public final Clause clause() { + return operator == AND ? CONDITION_AND : CONDITION_OR; + } + @Override public final void bind(BindContext context) { visitAll(context, conditions); diff --git a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java index 455418f562..ed67513c4f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java @@ -37,6 +37,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_COMPARISON; import static org.jooq.Comparator.LIKE; import static org.jooq.Comparator.LIKE_IGNORE_CASE; import static org.jooq.Comparator.NOT_LIKE; @@ -47,6 +48,7 @@ import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.POSTGRES; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Field; import org.jooq.RenderContext; @@ -127,4 +129,9 @@ class CompareCondition extends AbstractCondition { .sql("'"); } } + + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java b/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java index 05437aa8ce..ba4a916d18 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConditionProviderImpl.java @@ -42,6 +42,7 @@ import java.util.Arrays; import java.util.Collection; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.ConditionProvider; import org.jooq.Field; @@ -121,6 +122,11 @@ class ConditionProviderImpl extends AbstractQueryPart implements ConditionProvid context.visit(getWhere()); } + @Override + public final Clause clause() { + return null; + } + // ------------------------------------------------------------------------- // Condition API // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/Contains.java b/jOOQ/src/main/java/org/jooq/impl/Contains.java index 4804d11c52..5021c3b296 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Contains.java +++ b/jOOQ/src/main/java/org/jooq/impl/Contains.java @@ -35,10 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_COMPARISON; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.val; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Field; import org.jooq.RenderContext; @@ -82,6 +84,11 @@ class Contains extends AbstractCondition { context.visit(condition()); } + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } + private final Condition condition() { // [#1107] Some dialects support "contains" operations for ARRAYs @@ -124,6 +131,11 @@ class Contains extends AbstractCondition { context.visit(lhs).visit(rhs()); } + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } + private final Field rhs() { return (rhs == null) ? val(value, lhs) : rhs; } diff --git a/jOOQ/src/main/java/org/jooq/impl/CustomCondition.java b/jOOQ/src/main/java/org/jooq/impl/CustomCondition.java index 120777d6fd..c2c988eaa7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CustomCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CustomCondition.java @@ -36,6 +36,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.RenderContext; import org.jooq.exception.DataAccessException; @@ -86,6 +87,20 @@ public abstract class CustomCondition extends AbstractCondition { @Override public abstract void bind(BindContext context) throws DataAccessException; + // ------------------------------------------------------------------------- + // Implementation optional + // ------------------------------------------------------------------------- + + /** + * Subclasses may implement this method + *


+ * {@inheritDoc} + */ + @Override + public Clause clause() { + return null; + } + // ------------------------------------------------------------------------- // No further overrides allowed // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/CustomField.java b/jOOQ/src/main/java/org/jooq/impl/CustomField.java index 7b0e1082c7..3a75518dfc 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CustomField.java +++ b/jOOQ/src/main/java/org/jooq/impl/CustomField.java @@ -36,6 +36,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Field; import org.jooq.RenderContext; @@ -88,9 +89,19 @@ public abstract class CustomField extends AbstractField { public abstract void bind(BindContext context) throws DataAccessException; // ------------------------------------------------------------------------- - // Further overrides allowed + // Implementation optional // ------------------------------------------------------------------------- + /** + * Subclasses may implement this method + *
+ * {@inheritDoc} + */ + @Override + public Clause clause() { + return null; + } + // ------------------------------------------------------------------------- // No further overrides allowed // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/CustomQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/CustomQueryPart.java index 3cb00b2a77..2de5c6f521 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CustomQueryPart.java +++ b/jOOQ/src/main/java/org/jooq/impl/CustomQueryPart.java @@ -36,6 +36,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; import org.jooq.RenderContext; import org.jooq.exception.DataAccessException; @@ -97,6 +98,20 @@ public abstract class CustomQueryPart extends AbstractQueryPart { @Override public abstract void bind(BindContext context) throws DataAccessException; + // ------------------------------------------------------------------------- + // Implementation optional + // ------------------------------------------------------------------------- + + /** + * Subclasses may implement this method + *
+ * {@inheritDoc} + */ + @Override + public Clause clause() { + return null; + } + // ------------------------------------------------------------------------- // No further overrides allowed // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index eb55965e89..5a1946fb02 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -7766,7 +7767,7 @@ public class DSL { WrappedList[] array = new WrappedList[fieldSets.length]; for (int i = 0; i < fieldSets.length; i++) { - array[i] = new WrappedList(new QueryPartList>(fieldSets[i])); + array[i] = new WrappedList(new QueryPartList>(DUMMY, fieldSets[i])); } return new Function("grouping sets", SQLDataType.OTHER, array); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java index fc8f2291ae..99a75c05fe 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java @@ -55,6 +55,7 @@ import org.jooq.RecordListenerProvider; import org.jooq.RecordMapperProvider; import org.jooq.SQLDialect; import org.jooq.SchemaMapping; +import org.jooq.VisitListenerProvider; import org.jooq.conf.Settings; import org.jooq.conf.SettingsTools; @@ -84,6 +85,7 @@ public class DefaultConfiguration implements Configuration { private transient RecordMapperProvider recordMapperProvider; private transient RecordListenerProvider[] recordListenerProviders; private transient ExecuteListenerProvider[] executeListenerProviders; + private transient VisitListenerProvider[] visitListenerProviders; // Derived objects private org.jooq.SchemaMapping mapping; @@ -100,14 +102,29 @@ public class DefaultConfiguration implements Configuration { * through the various derive() or set() methods. */ public DefaultConfiguration() { + this(SQL99); + } + + /** + * Create a new "empty" configuration object given a {@link SQLDialect}. + *

+ * This can be used as is, as a "dummy" configuration object, or as a base + * implementation for creating more sophisticated "derived" configurations + * through the various derive() or set() methods. + * + * @param dialect The pre-existing {@link SQLDialect}. + */ + DefaultConfiguration(SQLDialect dialect) { this( new NoConnectionProvider(), new DefaultRecordMapperProvider(), new RecordListenerProvider[0], new ExecuteListenerProvider[0], - SQL99, + new VisitListenerProvider[0], + dialect, SettingsTools.defaultSettings(), - null); + null + ); } /** @@ -124,6 +141,7 @@ public class DefaultConfiguration implements Configuration { configuration.recordMapperProvider(), configuration.recordListenerProviders(), configuration.executeListenerProviders(), + configuration.visitListenerProviders(), configuration.dialect(), configuration.settings(), configuration.data() @@ -143,6 +161,7 @@ public class DefaultConfiguration implements Configuration { RecordMapperProvider recordMapperProvider, RecordListenerProvider[] recordListenerProviders, ExecuteListenerProvider[] executeListenerProviders, + VisitListenerProvider[] visitListenerProviders, SQLDialect dialect, Settings settings, Map data) @@ -151,6 +170,7 @@ public class DefaultConfiguration implements Configuration { set(recordMapperProvider); set(recordListenerProviders); set(executeListenerProviders); + set(visitListenerProviders); set(dialect); set(settings); @@ -181,6 +201,7 @@ public class DefaultConfiguration implements Configuration { recordMapperProvider, recordListenerProviders, executeListenerProviders, + visitListenerProviders, dialect, settings, data @@ -197,6 +218,7 @@ public class DefaultConfiguration implements Configuration { newRecordMapperProvider, recordListenerProviders, executeListenerProviders, + visitListenerProviders, dialect, settings, data @@ -207,12 +229,13 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - public Configuration derive(RecordListenerProvider... newRecordListenerProviders) { + public final Configuration derive(RecordListenerProvider... newRecordListenerProviders) { return new DefaultConfiguration( connectionProvider, recordMapperProvider, newRecordListenerProviders, executeListenerProviders, + visitListenerProviders, dialect, settings, data @@ -229,6 +252,24 @@ public class DefaultConfiguration implements Configuration { recordMapperProvider, recordListenerProviders, newExecuteListenerProviders, + visitListenerProviders, + dialect, + settings, + data + ); + } + + /** + * {@inheritDoc} + */ + @Override + public final Configuration derive(VisitListenerProvider... newVisitListenerProviders) { + return new DefaultConfiguration( + connectionProvider, + recordMapperProvider, + recordListenerProviders, + executeListenerProviders, + newVisitListenerProviders, dialect, settings, data @@ -245,6 +286,7 @@ public class DefaultConfiguration implements Configuration { recordMapperProvider, recordListenerProviders, executeListenerProviders, + visitListenerProviders, newDialect, settings, data @@ -261,6 +303,7 @@ public class DefaultConfiguration implements Configuration { recordMapperProvider, recordListenerProviders, executeListenerProviders, + visitListenerProviders, dialect, newSettings, data @@ -271,15 +314,6 @@ public class DefaultConfiguration implements Configuration { // XXX: Changing configurations // ------------------------------------------------------------------------- - /** - * {@inheritDoc} - */ - @Override - public final Configuration set(SQLDialect newDialect) { - this.dialect = newDialect; - return this; - } - /** * {@inheritDoc} */ @@ -305,12 +339,11 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - public final Configuration set(Settings newSettings) { - this.settings = newSettings != null - ? SettingsTools.clone(newSettings) - : SettingsTools.defaultSettings(); + public final Configuration set(RecordListenerProvider... newRecordListenerProviders) { + this.recordListenerProviders = newRecordListenerProviders != null + ? newRecordListenerProviders + : new RecordListenerProvider[0]; - this.mapping = new SchemaMapping(this); return this; } @@ -330,26 +363,40 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - public final Configuration set(RecordListenerProvider... newRecordListenerProviders) { - this.recordListenerProviders = newRecordListenerProviders != null - ? newRecordListenerProviders - : new RecordListenerProvider[0]; + public final Configuration set(VisitListenerProvider... newVisitListenerProviders) { + this.visitListenerProviders = newVisitListenerProviders != null + ? newVisitListenerProviders + : new VisitListenerProvider[0]; return this; } + /** + * {@inheritDoc} + */ + @Override + public final Configuration set(SQLDialect newDialect) { + this.dialect = newDialect; + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public final Configuration set(Settings newSettings) { + this.settings = newSettings != null + ? SettingsTools.clone(newSettings) + : SettingsTools.defaultSettings(); + + this.mapping = new SchemaMapping(this); + return this; + } + // ------------------------------------------------------------------------- // XXX: Getters // ------------------------------------------------------------------------- - /** - * {@inheritDoc} - */ - @Override - public final SQLDialect dialect() { - return dialect; - } - /** * {@inheritDoc} */ @@ -362,7 +409,7 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - public RecordMapperProvider recordMapperProvider() { + public final RecordMapperProvider recordMapperProvider() { return recordMapperProvider; } @@ -370,9 +417,32 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - @Deprecated - public final org.jooq.SchemaMapping schemaMapping() { - return mapping; + public final RecordListenerProvider[] recordListenerProviders() { + return recordListenerProviders; + } + + /** + * {@inheritDoc} + */ + @Override + public final ExecuteListenerProvider[] executeListenerProviders() { + return executeListenerProviders; + } + + /** + * {@inheritDoc} + */ + @Override + public final VisitListenerProvider[] visitListenerProviders() { + return visitListenerProviders; + } + + /** + * {@inheritDoc} + */ + @Override + public final SQLDialect dialect() { + return dialect; } /** @@ -411,16 +481,9 @@ public class DefaultConfiguration implements Configuration { * {@inheritDoc} */ @Override - public final ExecuteListenerProvider[] executeListenerProviders() { - return executeListenerProviders; - } - - /** - * {@inheritDoc} - */ - @Override - public RecordListenerProvider[] recordListenerProviders() { - return recordListenerProviders; + @Deprecated + public final org.jooq.SchemaMapping schemaMapping() { + return mapping; } @Override @@ -452,6 +515,7 @@ public class DefaultConfiguration implements Configuration { oos.writeObject(cloneSerializables(executeListenerProviders)); oos.writeObject(cloneSerializables(recordListenerProviders)); + oos.writeObject(cloneSerializables(visitListenerProviders)); } private E[] cloneSerializables(E[] array) { @@ -473,5 +537,6 @@ public class DefaultConfiguration implements Configuration { recordMapperProvider = (RecordMapperProvider) ois.readObject(); executeListenerProviders = (ExecuteListenerProvider[]) ois.readObject(); recordListenerProviders = (RecordListenerProvider[]) ois.readObject(); + visitListenerProviders = (VisitListenerProvider[]) ois.readObject(); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index e69e65b9a9..cd7df82898 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -202,7 +202,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { } public DefaultDSLContext(SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new NoConnectionProvider(), null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new NoConnectionProvider(), null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(Connection connection, SQLDialect dialect) { @@ -210,7 +210,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { } public DefaultDSLContext(Connection connection, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new DefaultConnectionProvider(connection), null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new DefaultConnectionProvider(connection), null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(DataSource datasource, SQLDialect dialect) { @@ -218,7 +218,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { } public DefaultDSLContext(DataSource datasource, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(new DataSourceConnectionProvider(datasource), null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(new DataSourceConnectionProvider(datasource), null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(ConnectionProvider connectionProvider, SQLDialect dialect) { @@ -226,7 +226,7 @@ public class DefaultDSLContext implements DSLContext, Serializable { } public DefaultDSLContext(ConnectionProvider connectionProvider, SQLDialect dialect, Settings settings) { - this(new DefaultConfiguration(connectionProvider, null, null, null, dialect, settings, null)); + this(new DefaultConfiguration(connectionProvider, null, null, null, null, dialect, settings, null)); } public DefaultDSLContext(Configuration configuration) { diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultVisitListenerProvider.java b/jOOQ/src/main/java/org/jooq/impl/DefaultVisitListenerProvider.java new file mode 100644 index 0000000000..b50171581b --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultVisitListenerProvider.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.impl; + +import java.io.Serializable; + +import org.jooq.VisitListener; +import org.jooq.VisitListenerProvider; + +/** + * A default implementation for {@link VisitListenerProvider}. + *

+ * This implementation just wraps an instance of {@link VisitListener}, always + * providing the same. + * + * @author Lukas Eder + */ +public class DefaultVisitListenerProvider implements VisitListenerProvider, Serializable { + + /** + * Generated UID. + */ + private static final long serialVersionUID = -2122007794302549679L; + + /** + * The delegate listener. + */ + private final VisitListener listener; + + /** + * Convenience method to construct an array of + * DefaultVisitListenerProvider from an array of + * VisitListener instances. + */ + public static VisitListenerProvider[] providers(VisitListener... listeners) { + VisitListenerProvider[] result = new VisitListenerProvider[listeners.length]; + + for (int i = 0; i < listeners.length; i++) { + result[i] = new DefaultVisitListenerProvider(listeners[i]); + } + + return result; + } + + /** + * Create a new provider instance from an argument listener. + * + * @param listener The argument listener. + */ + public DefaultVisitListenerProvider(VisitListener listener) { + this.listener = listener; + } + + /** + * {@inheritDoc} + */ + @Override + public final VisitListener provide() { + return listener; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return listener.toString(); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java index 0ad3a87aba..b7ca33137b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/DeleteQueryImpl.java @@ -37,12 +37,14 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DELETE; import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; import java.util.Collection; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.DeleteQuery; @@ -131,4 +133,9 @@ class DeleteQueryImpl extends AbstractQuery implements DeleteQ public final void bind(BindContext context) { context.visit(getFrom()).visit(getWhere()); } + + @Override + public final Clause clause() { + return DELETE; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/DivideBy.java b/jOOQ/src/main/java/org/jooq/impl/DivideBy.java index ffb4ecf423..5d99773eb7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DivideBy.java +++ b/jOOQ/src/main/java/org/jooq/impl/DivideBy.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.exists; import static org.jooq.impl.DSL.notExists; @@ -74,7 +75,7 @@ implements this.divisor = divisor; this.condition = new ConditionProviderImpl(); - this.returning = new QueryPartList>(); + this.returning = new QueryPartList>(DUMMY); } // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/Dual.java b/jOOQ/src/main/java/org/jooq/impl/Dual.java index 1ebe11f3eb..5dbe4b9355 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Dual.java +++ b/jOOQ/src/main/java/org/jooq/impl/Dual.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.TABLE; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Schema; @@ -124,6 +127,11 @@ class Dual extends AbstractTable { @Override public final void bind(BindContext context) {} + @Override + public final Clause clause() { + return TABLE; + } + @Override final Fields fields0() { return new Fields(); diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 24337b6472..08c888ddf0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -36,6 +36,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -99,7 +100,7 @@ class Expression extends AbstractFunction { this.operator = operator; this.lhs = lhs; - this.rhs = new QueryPartList>(rhs); + this.rhs = new QueryPartList>(DUMMY, rhs); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java b/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java index 96e0a01402..4bf2eac11a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_COMPARISON; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.RenderContext; /** @@ -54,5 +57,10 @@ class FalseCondition extends AbstractCondition { context.sql("1 = 0"); } + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } + FalseCondition() {} } diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java b/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java index 00c2e10217..f3034e4e2e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldCondition.java @@ -35,10 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.inline; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.QueryPartInternal; @@ -69,6 +71,11 @@ class FieldCondition extends AbstractCondition { delegate(ctx.configuration()).bind(ctx); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { switch (configuration.dialect().family()) { diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapForInsert.java index 786004bdf8..8881179cff 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapForInsert.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.Utils.visitAll; import java.util.Collection; @@ -42,6 +43,7 @@ import java.util.Iterator; import java.util.Map; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.RenderContext; @@ -136,6 +138,11 @@ class FieldMapForInsert extends AbstractQueryPartMap, Field> { visitAll(context, values()); } + @Override + public final Clause clause() { + return DUMMY; + } + final void putFields(Collection> fields) { for (Field field : fields) { put(field, null); diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java index a92062ca68..d959a06869 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapForUpdate.java @@ -36,12 +36,15 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; +import static org.jooq.Clause.UPDATE_SET_ASSIGNMENT; import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.SQLDialect.SQLITE; import java.util.Map; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.RenderContext; @@ -79,11 +82,13 @@ class FieldMapForUpdate extends AbstractQueryPartMap, Field> { context.formatNewLine(); } - context.qualify(supportsQualify) + context.start(UPDATE_SET_ASSIGNMENT) + .qualify(supportsQualify) .visit(entry.getKey()) .qualify(restoreQualify) .sql(" = ") - .visit(entry.getValue()); + .visit(entry.getValue()) + .end(UPDATE_SET_ASSIGNMENT); separator = ", "; } @@ -101,6 +106,11 @@ class FieldMapForUpdate extends AbstractQueryPartMap, Field> { } } + @Override + public final Clause clause() { + return DUMMY; + } + final void set(Map, ?> map) { for (Entry, ?> entry : map.entrySet()) { Field field = entry.getKey(); diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java index e4b0947502..12fa47f343 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldMapsForInsert.java @@ -35,12 +35,14 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.Utils.visitAll; import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Select; @@ -137,6 +139,11 @@ class FieldMapsForInsert extends AbstractQueryPart { visitAll(context, insertMaps); } + @Override + public final Clause clause() { + return DUMMY; + } + // ------------------------------------------------------------------------- // The FieldMapsForInsert API // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/Fields.java b/jOOQ/src/main/java/org/jooq/impl/Fields.java index 8aeb3c7264..f7be7f3bff 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Fields.java +++ b/jOOQ/src/main/java/org/jooq/impl/Fields.java @@ -36,14 +36,17 @@ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import java.util.Collection; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Record; -import org.jooq.RenderContext; import org.jooq.RecordType; +import org.jooq.RenderContext; /** * A simple wrapper for Field[], providing some useful lookup @@ -246,12 +249,17 @@ class Fields extends AbstractQueryPart implements RecordType>(fields).toSQL(context); + new QueryPartList>(DUMMY, fields).toSQL(context); } @Override public final void bind(BindContext context) { - new QueryPartList>(fields).bind(context); + new QueryPartList>(DUMMY, fields).bind(context); + } + + @Override + public final Clause clause() { + return DUMMY; } // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/FlashbackTable.java b/jOOQ/src/main/java/org/jooq/impl/FlashbackTable.java index 8762f03d5c..bc61dd1177 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FlashbackTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/FlashbackTable.java @@ -35,10 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.keyword; import static org.jooq.impl.DSL.val; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.QueryPart; import org.jooq.Record; @@ -143,6 +145,11 @@ implements VersionsBetweenAndStep { } } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final boolean declaresTables() { return true; diff --git a/jOOQ/src/main/java/org/jooq/impl/Function.java b/jOOQ/src/main/java/org/jooq/impl/Function.java index 552138bf4a..72dcb59f50 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Function.java +++ b/jOOQ/src/main/java/org/jooq/impl/Function.java @@ -37,6 +37,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; import static org.jooq.SQLDialect.H2; @@ -54,6 +55,7 @@ import java.util.Collection; import org.jooq.AggregateFunction; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Name; @@ -136,10 +138,10 @@ class Function extends AbstractField implements this.term = null; this.name = null; this.distinct = distinct; - this.arguments = new QueryPartList(arguments); + this.arguments = new QueryPartList(DUMMY, arguments); this.keepDenseRankOrderBy = new SortFieldList(); this.withinGroupOrderBy = new SortFieldList(); - this.partitionBy = new QueryPartList>(); + this.partitionBy = new QueryPartList>(DUMMY); this.orderBy = new SortFieldList(); } @@ -149,10 +151,10 @@ class Function extends AbstractField implements this.term = term; this.name = null; this.distinct = distinct; - this.arguments = new QueryPartList(arguments); + this.arguments = new QueryPartList(DUMMY, arguments); this.keepDenseRankOrderBy = new SortFieldList(); this.withinGroupOrderBy = new SortFieldList(); - this.partitionBy = new QueryPartList>(); + this.partitionBy = new QueryPartList>(DUMMY); this.orderBy = new SortFieldList(); } @@ -162,10 +164,10 @@ class Function extends AbstractField implements this.term = null; this.name = name; this.distinct = distinct; - this.arguments = new QueryPartList(arguments); + this.arguments = new QueryPartList(DUMMY, arguments); this.keepDenseRankOrderBy = new SortFieldList(); this.withinGroupOrderBy = new SortFieldList(); - this.partitionBy = new QueryPartList>(); + this.partitionBy = new QueryPartList>(DUMMY); this.orderBy = new SortFieldList(); } @@ -182,8 +184,12 @@ class Function extends AbstractField implements // ------------------------------------------------------------------------- @Override - public final void bind(BindContext context) { + public final Clause clause() { + return DUMMY; + } + @Override + public final void bind(BindContext context) { if (term == LIST_AGG && asList(CUBRID, H2, HSQLDB, MARIADB, MYSQL).contains(context.configuration().dialect())) { context.visit(arguments.get(0)); context.visit(withinGroupOrderBy); diff --git a/jOOQ/src/main/java/org/jooq/impl/FunctionTable.java b/jOOQ/src/main/java/org/jooq/impl/FunctionTable.java index 11e62c0a13..a7adc63f50 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FunctionTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/FunctionTable.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.Record; import org.jooq.RenderContext; @@ -101,6 +104,11 @@ class FunctionTable extends AbstractTable { } } + @Override + public final Clause clause() { + return DUMMY; + } + @Override final Fields fields0() { return new Fields(); diff --git a/jOOQ/src/main/java/org/jooq/impl/InCondition.java b/jOOQ/src/main/java/org/jooq/impl/InCondition.java index 5ab2a40f3d..24cce4e561 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/InCondition.java @@ -36,12 +36,16 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_IN; +import static org.jooq.Clause.CONDITION_IN_NOT; +import static org.jooq.Comparator.IN; import static org.jooq.impl.Utils.visitAll; import java.util.Arrays; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Field; import org.jooq.RenderContext; @@ -64,6 +68,11 @@ class InCondition extends AbstractCondition { this.comparator = comparator; } + @Override + public final Clause clause() { + return comparator == IN ? CONDITION_IN : CONDITION_IN_NOT; + } + @Override public final void bind(BindContext context) { context.visit(field); @@ -93,11 +102,13 @@ class InCondition extends AbstractCondition { // operator if (comparator == Comparator.IN) { context.formatSeparator() - .keyword("or "); + .keyword("or") + .sql(" "); } else { context.formatSeparator() - .keyword("and "); + .keyword("and") + .sql(" "); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java index d9d22da1f2..e559e45187 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertQueryImpl.java @@ -37,6 +37,8 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.INSERT; +import static org.jooq.Clause.INSERT_RETURNING; import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; @@ -45,6 +47,7 @@ import java.util.List; import java.util.Map; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -313,16 +316,23 @@ class InsertQueryImpl extends AbstractStoreQuery implements } } + @Override + public final Clause clause() { + return INSERT; + } + private final void toSQLInsert(RenderContext context) { - context.keyword("insert ") + context.keyword("insert") + .sql(" ") // [#1295] MySQL natively supports the IGNORE keyword .keyword((onDuplicateKeyIgnore && asList(MARIADB, MYSQL).contains(context.configuration().dialect())) ? "ignore " : "") - .keyword("into ") + .keyword("into") + .sql(" ") .visit(getInto()) .sql(" ") .visit(insertMaps); - toSQLReturning(context); + toSQLReturning(context, INSERT_RETURNING); } private final void bindInsert(BindContext context) { diff --git a/jOOQ/src/main/java/org/jooq/impl/InsertSelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/InsertSelectQueryImpl.java index 9a8449d719..c6a5f12abe 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InsertSelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InsertSelectQueryImpl.java @@ -35,9 +35,11 @@ */ package org.jooq.impl; +import static org.jooq.Clause.INSERT; import static org.jooq.impl.Utils.visitAll; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.Insert; @@ -70,7 +72,8 @@ class InsertSelectQueryImpl extends AbstractQuery implements I @Override public final void toSQL(RenderContext context) { - context.keyword("insert into ") + context.keyword("insert into") + .sql(" ") .visit(into) .sql(" ("); @@ -93,4 +96,9 @@ class InsertSelectQueryImpl extends AbstractQuery implements I visitAll(context, fields); context.visit(select); } + + @Override + public final Clause clause() { + return INSERT; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java index 23db495833..2e973fd9ea 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java @@ -36,6 +36,7 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -53,6 +54,7 @@ import static org.jooq.impl.DSL.one; import static org.jooq.impl.DSL.zero; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Configuration; import org.jooq.Field; @@ -94,6 +96,11 @@ class IsDistinctFrom extends AbstractCondition { delegate(context.configuration()).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + /** * Get a delegate CompareCondition, in case the context * {@link SQLDialect} natively supports the IS DISTINCT FROM diff --git a/jOOQ/src/main/java/org/jooq/impl/IsNull.java b/jOOQ/src/main/java/org/jooq/impl/IsNull.java index 18f496e702..7507ebbee6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsNull.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsNull.java @@ -36,7 +36,11 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_NULL; +import static org.jooq.Clause.CONDITION_NULL_NOT; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.RenderContext; @@ -62,6 +66,11 @@ class IsNull extends AbstractCondition { @Override public final void toSQL(RenderContext context) { - context.visit(field).keyword(isNull ? " is null" : " is not null"); + context.visit(field).sql(" ").keyword(isNull ? "is null" : "is not null"); + } + + @Override + public final Clause clause() { + return isNull ? CONDITION_NULL : CONDITION_NULL_NOT; } } diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index 85b8af04d7..a1e921894a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -36,6 +36,19 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; +import static org.jooq.Clause.TABLE_JOIN; +import static org.jooq.Clause.TABLE_JOIN_CROSS; +import static org.jooq.Clause.TABLE_JOIN_INNER; +import static org.jooq.Clause.TABLE_JOIN_NATURAL; +import static org.jooq.Clause.TABLE_JOIN_NATURAL_OUTER_LEFT; +import static org.jooq.Clause.TABLE_JOIN_NATURAL_OUTER_RIGHT; +import static org.jooq.Clause.TABLE_JOIN_ON; +import static org.jooq.Clause.TABLE_JOIN_OUTER_FULL; +import static org.jooq.Clause.TABLE_JOIN_OUTER_LEFT; +import static org.jooq.Clause.TABLE_JOIN_OUTER_RIGHT; +import static org.jooq.Clause.TABLE_JOIN_PARTITION_BY; +import static org.jooq.Clause.TABLE_JOIN_USING; import static org.jooq.JoinType.CROSS_JOIN; import static org.jooq.JoinType.JOIN; import static org.jooq.JoinType.LEFT_OUTER_JOIN; @@ -59,6 +72,7 @@ import java.util.Collection; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Field; import org.jooq.ForeignKey; @@ -101,11 +115,11 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta this.lhs = lhs.asTable(); this.rhs = rhs.asTable(); - this.rhsPartitionBy = new QueryPartList>(); + this.rhsPartitionBy = new QueryPartList>(DUMMY); this.type = type; this.condition = new ConditionProviderImpl(); - this.using = new QueryPartList>(); + this.using = new QueryPartList>(DUMMY); } // ------------------------------------------------------------------------ @@ -125,10 +139,14 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta @Override public final void toSQL(RenderContext context) { + JoinType translatedType = translateType(context); + Clause translatedClause = translateClause(translatedType); + context.visit(lhs) .formatIndentStart() .formatSeparator() - .keyword(translateType(context).toSQL()) + .start(translatedClause) + .keyword(translatedType.toSQL()) .sql(" "); // [#671] Some databases formally require nested JOINS to be @@ -151,20 +169,41 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta // OUTER JOINed table if (!rhsPartitionBy.isEmpty()) { context.formatSeparator() - .keyword("partition by (") + .start(TABLE_JOIN_PARTITION_BY) + .keyword("partition by") + .sql(" (") .visit(rhsPartitionBy) - .sql(")"); + .sql(")") + .end(TABLE_JOIN_PARTITION_BY); } // CROSS JOIN and NATURAL JOIN do not have any condition clauses if (!asList(CROSS_JOIN, NATURAL_JOIN, NATURAL_LEFT_OUTER_JOIN, - NATURAL_RIGHT_OUTER_JOIN).contains(translateType(context))) { + NATURAL_RIGHT_OUTER_JOIN).contains(translatedType)) { toSQLJoinCondition(context); } - context.formatIndentEnd(); + context.end(translatedClause) + .formatIndentEnd(); + } + + /** + * Translate the join type into a join clause + */ + final Clause translateClause(JoinType translatedType) { + switch (translatedType) { + case JOIN: return TABLE_JOIN_INNER; + case CROSS_JOIN: return TABLE_JOIN_CROSS; + case NATURAL_JOIN: return TABLE_JOIN_NATURAL; + case LEFT_OUTER_JOIN: return TABLE_JOIN_OUTER_LEFT; + case RIGHT_OUTER_JOIN: return TABLE_JOIN_OUTER_RIGHT; + case FULL_OUTER_JOIN: return TABLE_JOIN_OUTER_FULL; + case NATURAL_LEFT_OUTER_JOIN: return TABLE_JOIN_NATURAL_OUTER_LEFT; + case NATURAL_RIGHT_OUTER_JOIN: return TABLE_JOIN_NATURAL_OUTER_RIGHT; + default: throw new IllegalArgumentException("Bad join type: " + translatedType); + } } /** @@ -210,24 +249,38 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta // [#582] Some dialects don't explicitly support a JOIN .. USING // syntax. This can be simulated with JOIN .. ON if (asList(ASE, CUBRID, DB2, H2, SQLSERVER, SYBASE).contains(context.configuration().dialect().family())) { - String glue = "on "; + boolean first = true; for (Field field : using) { - context.formatSeparator() - .keyword(glue) + context.formatSeparator(); + + if (first) { + first = false; + + context.start(TABLE_JOIN_ON) + .keyword("on"); + } + else { + context.keyword("and"); + } + + context.sql(" ") .visit(lhs.field(field)) .sql(" = ") .visit(rhs.field(field)); - - glue = "and "; } + + context.end(TABLE_JOIN_ON); } // Native supporters of JOIN .. USING else { context.formatSeparator() - .keyword("using ("); + .start(TABLE_JOIN_USING) + .keyword("using") + .sql("( "); Utils.fieldNames(context, using); - context.sql(")"); + context.sql(")") + .end(TABLE_JOIN_USING); } } @@ -237,27 +290,41 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta simulateNaturalLeftOuterJoin(context) || simulateNaturalRightOuterJoin(context)) { - String glue = "on "; + boolean first = true; for (Field field : lhs.fields()) { Field other = rhs.field(field); if (other != null) { - context.formatSeparator() - .keyword(glue) + context.formatSeparator(); + + if (first) { + first = false; + + context.start(TABLE_JOIN_ON) + .keyword("on"); + } + else { + context.keyword("and"); + } + + context.sql(" ") .visit(field) .sql(" = ") .visit(other); - - glue = "and "; } } + + context.end(TABLE_JOIN_ON); } // Regular JOIN condition else { context.formatSeparator() - .keyword("on ") - .visit(condition); + .start(TABLE_JOIN_ON) + .keyword("on") + .sql(" ") + .visit(condition) + .end(TABLE_JOIN_ON); } } @@ -273,6 +340,11 @@ class JoinTable extends AbstractTable implements TableOptionalOnStep, Ta } } + @Override + public final Clause clause() { + return TABLE_JOIN; + } + @Override public final Table as(String alias) { return new TableAlias(this, alias, true); diff --git a/jOOQ/src/main/java/org/jooq/impl/KeywordImpl.java b/jOOQ/src/main/java/org/jooq/impl/KeywordImpl.java index de8cb92730..a8beaf03c0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/KeywordImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/KeywordImpl.java @@ -36,6 +36,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Keyword; import org.jooq.RenderContext; @@ -64,4 +65,9 @@ public class KeywordImpl extends AbstractQueryPart implements Keyword { @Override public final void bind(BindContext ctx) {} + + @Override + public final Clause clause() { + return null; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Limit.java b/jOOQ/src/main/java/org/jooq/impl/Limit.java index 810440a2e5..afeabb4cfa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Limit.java +++ b/jOOQ/src/main/java/org/jooq/impl/Limit.java @@ -35,12 +35,14 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.RenderContext.CastMode.NEVER; import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.val; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.Param; import org.jooq.RenderContext; @@ -320,6 +322,11 @@ class Limit extends AbstractQueryPart { } } + @Override + public final Clause clause() { + return DUMMY; + } + /** * Whether this limit has an offset of zero */ diff --git a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java index 63b3a9c626..2c86e64636 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/MergeImpl.java @@ -35,6 +35,8 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; +import static org.jooq.Clause.MERGE; import static org.jooq.SQLDialect.H2; import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.exists; @@ -52,6 +54,7 @@ import java.util.Map; import java.util.Set; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -230,7 +233,7 @@ implements if (fields != null) { h2Style = true; - h2Fields = new QueryPartList>(fields); + h2Fields = new QueryPartList>(DUMMY, fields); } } @@ -240,7 +243,7 @@ implements QueryPartList> getH2Fields() { if (h2Fields == null) { - h2Fields = new QueryPartList>(table.fields()); + h2Fields = new QueryPartList>(DUMMY, table.fields()); } return h2Fields; @@ -248,7 +251,7 @@ implements QueryPartList> getH2Keys() { if (h2Keys == null) { - h2Keys = new QueryPartList>(); + h2Keys = new QueryPartList>(DUMMY); } return h2Keys; @@ -256,7 +259,7 @@ implements QueryPartList> getH2Values() { if (h2Values == null) { - h2Values = new QueryPartList>(); + h2Values = new QueryPartList>(DUMMY); } return h2Values; @@ -1194,4 +1197,9 @@ implements .visit(notMatchedInsert) .visit(notMatchedWhere); } + + @Override + public final Clause clause() { + return MERGE; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/NameImpl.java b/jOOQ/src/main/java/org/jooq/impl/NameImpl.java index 265f1d9727..b3b10cdf04 100644 --- a/jOOQ/src/main/java/org/jooq/impl/NameImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/NameImpl.java @@ -35,9 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import java.util.Arrays; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Name; import org.jooq.RenderContext; @@ -72,6 +75,11 @@ class NameImpl extends AbstractQueryPart implements Name { @Override public final void bind(BindContext context) {} + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final String[] getName() { return qualifiedName; diff --git a/jOOQ/src/main/java/org/jooq/impl/NotCondition.java b/jOOQ/src/main/java/org/jooq/impl/NotCondition.java index 2922e2caf7..063f7e419d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/NotCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/NotCondition.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_NOT; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.RenderContext; @@ -58,4 +61,9 @@ class NotCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(condition); } + + @Override + public final Clause clause() { + return CONDITION_NOT; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/PackageImpl.java b/jOOQ/src/main/java/org/jooq/impl/PackageImpl.java index b7ae1bc71f..56ab174c80 100644 --- a/jOOQ/src/main/java/org/jooq/impl/PackageImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/PackageImpl.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Package; import org.jooq.RenderContext; import org.jooq.SQLDialect; @@ -85,6 +88,11 @@ public class PackageImpl extends AbstractQueryPart implements Package { @Override public final void bind(BindContext context) {} + @Override + public final Clause clause() { + return DUMMY; + } + // ------------------------------------------------------------------------ // XXX: Object API // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java index 63913eb432..177f2882dd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParameterImpl.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.DataType; import org.jooq.Parameter; @@ -82,12 +85,17 @@ class ParameterImpl extends AbstractQueryPart implements Parameter { return type.getType(); } + @Override + public final void toSQL(RenderContext context) { + context.literal(getName()); + } + @Override public final void bind(BindContext context) {} @Override - public final void toSQL(RenderContext context) { - context.literal(getName()); + public final Clause clause() { + return DUMMY; } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/Pivot.java b/jOOQ/src/main/java/org/jooq/impl/Pivot.java index 173469b123..a13ca8947a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Pivot.java +++ b/jOOQ/src/main/java/org/jooq/impl/Pivot.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.DSL.trueCondition; import static org.jooq.impl.DSL.using; @@ -44,6 +45,7 @@ import java.util.Collection; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -145,6 +147,11 @@ implements .declareTables(false); } + @Override + public final Clause clause() { + return DUMMY; + } + private Table select(Configuration configuration) { List> groupingFields = new ArrayList>(); List> aliasedGroupingFields = new ArrayList>(); @@ -261,6 +268,11 @@ implements .visit(table) .declareTables(declareTables); } + + @Override + public final Clause clause() { + return DUMMY; + } } /** @@ -317,6 +329,11 @@ implements context.visit(pivot(context.configuration())); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final Table as(String alias) { return new TableAlias(this, alias, true); diff --git a/jOOQ/src/main/java/org/jooq/impl/QualifiedTable.java b/jOOQ/src/main/java/org/jooq/impl/QualifiedTable.java index da5a510e50..491fa18bea 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QualifiedTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/QualifiedTable.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.TABLE; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Table; @@ -79,6 +82,11 @@ class QualifiedTable extends AbstractTable { @Override public final void bind(BindContext context) {} + @Override + public final Clause clause() { + return TABLE; + } + @Override public final Class getRecordType() { return RecordImpl.class; diff --git a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java index d18774442a..84fb63fb40 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/QuantifiedComparisonCondition.java @@ -37,7 +37,10 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_COMPARISON; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Field; import org.jooq.QuantifiedSelect; @@ -73,4 +76,9 @@ class QuantifiedComparisonCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(field).visit(query); } + + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java b/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java index ca6e5eca04..d832c416eb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/QuantifiedSelectImpl.java @@ -35,9 +35,11 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.table; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.QuantifiedSelect; @@ -114,6 +116,11 @@ class QuantifiedSelectImpl extends AbstractQueryPart implement } } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPart part(Configuration context) { if (query != null) { return query; diff --git a/jOOQ/src/main/java/org/jooq/impl/QueryPartList.java b/jOOQ/src/main/java/org/jooq/impl/QueryPartList.java index 136f7f42f9..e34740f2f7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QueryPartList.java +++ b/jOOQ/src/main/java/org/jooq/impl/QueryPartList.java @@ -36,6 +36,7 @@ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.Utils.visitAll; import java.util.ArrayList; @@ -46,6 +47,7 @@ import java.util.List; import java.util.ListIterator; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; import org.jooq.RenderContext; @@ -55,22 +57,26 @@ import org.jooq.RenderContext; class QueryPartList extends AbstractQueryPart implements List { private static final long serialVersionUID = -2936922742534009564L; - private final List wrappedList = new ArrayList(); + private final List wrappedList; + private final Clause clause; - QueryPartList() { - this((Collection) null); + QueryPartList(Clause clause) { + this(clause, (Collection) null); } - QueryPartList(Collection wrappedList) { + QueryPartList(Clause clause, Collection wrappedList) { super(); + this.clause = clause; + this.wrappedList = new ArrayList(); + if (wrappedList != null) { addAll(wrappedList); } } - QueryPartList(T... wrappedList) { - this(Arrays.asList(wrappedList)); + QueryPartList(Clause clause, T... wrappedList) { + this(clause, Arrays.asList(wrappedList)); } @Override @@ -78,7 +84,13 @@ class QueryPartList extends AbstractQueryPart implements Li // Some lists render different SQL when empty if (isEmpty()) { +// if (clause != null && clause != DUMMY) +// context.start(clause); + toSQLEmptyList(context); + +// if (clause != null && clause != DUMMY) +// context.end(clause); } else { @@ -94,7 +106,14 @@ class QueryPartList extends AbstractQueryPart implements Li if (indent) context.formatNewLine(); +// if (clause != null && clause != DUMMY) +// context.start(clause); + context.visit(queryPart); + +// if (clause != null && clause != DUMMY) +// context.end(clause); + separator = ", "; } @@ -108,6 +127,11 @@ class QueryPartList extends AbstractQueryPart implements Li visitAll(context, wrappedList); } + @Override + public final Clause clause() { + return DUMMY; + } + /** * Subclasses may override this method */ diff --git a/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java b/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java index 34d468e52f..2c6a08b246 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java +++ b/jOOQ/src/main/java/org/jooq/impl/RegexpLike.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.RenderContext; @@ -69,7 +72,9 @@ class RegexpLike extends AbstractCondition { case SQLITE: case SYBASE: { context.visit(search) - .keyword(" regexp ") + .sql(" ") + .keyword("regexp") + .sql(" ") .visit(pattern); break; @@ -109,7 +114,9 @@ class RegexpLike extends AbstractCondition { case SQLSERVER: default: { context.visit(search) - .keyword(" like_regex ") + .sql(" ") + .keyword("like_regex") + .sql(" ") .visit(pattern); break; @@ -121,4 +128,9 @@ class RegexpLike extends AbstractCondition { public final void bind(BindContext context) { context.visit(search).visit(pattern); } + + @Override + public final Clause clause() { + return DUMMY; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Rollup.java b/jOOQ/src/main/java/org/jooq/impl/Rollup.java index c66d359972..12a6824fcf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Rollup.java +++ b/jOOQ/src/main/java/org/jooq/impl/Rollup.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.field; import static org.jooq.impl.DSL.function; @@ -61,7 +62,7 @@ class Rollup extends AbstractFunction { case CUBRID: case MARIADB: case MYSQL: - return field("{0} {with rollup}", new QueryPartList>(getArguments())); + return field("{0} {with rollup}", new QueryPartList>(DUMMY, getArguments())); default: return function("rollup", Object.class, getArguments()); diff --git a/jOOQ/src/main/java/org/jooq/impl/RowBetweenCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowBetweenCondition.java index c9513223a7..c246ff632c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowBetweenCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowBetweenCondition.java @@ -36,6 +36,8 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_BETWEEN; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -76,6 +78,7 @@ import org.jooq.BetweenAndStep8; import org.jooq.BetweenAndStep9; import org.jooq.BetweenAndStepN; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -685,6 +688,11 @@ implements delegate(context.configuration()).toSQL(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { // These casts are safe for RowImpl RowN r = (RowN) row; @@ -727,11 +735,17 @@ implements @Override public final void toSQL(RenderContext context) { context.visit(row) - .keyword(not ? " not" : "") - .keyword(" between ") - .keyword(symmetric ? "symmetric " : "") + .sql(not ? " " : "") + .keyword(not ? "not" : "") + .sql(" ") + .keyword("between") + .sql(" ") + .keyword(symmetric ? "symmetric" : "") + .sql(symmetric ? " " : "") .visit(minValue) - .keyword(" and ") + .sql(" ") + .keyword("and") + .sql(" ") .visit(maxValue); } @@ -739,5 +753,10 @@ implements public final void bind(BindContext context) { context.visit(row).visit(minValue).visit(maxValue); } + + @Override + public final Clause clause() { + return CONDITION_BETWEEN; + } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/RowCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowCondition.java index 25e12b5687..3406029182 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowCondition.java @@ -36,6 +36,8 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_COMPARISON; +import static org.jooq.Clause.DUMMY; import static org.jooq.Comparator.EQUALS; import static org.jooq.Comparator.GREATER; import static org.jooq.Comparator.GREATER_OR_EQUAL; @@ -57,6 +59,7 @@ import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Configuration; @@ -98,6 +101,11 @@ class RowCondition extends AbstractCondition { delegate(context.configuration()).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { SQLDialect dialect = configuration.dialect(); @@ -206,5 +214,10 @@ class RowCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(left).visit(right); } + + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/RowImpl.java b/jOOQ/src/main/java/org/jooq/impl/RowImpl.java index 837975ebe9..ff6b82fd3d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowImpl.java @@ -35,6 +35,7 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.row; import java.util.Arrays; @@ -66,6 +67,7 @@ import org.jooq.BetweenAndStep8; import org.jooq.BetweenAndStep9; import org.jooq.BetweenAndStepN; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.DataType; @@ -200,6 +202,11 @@ implements context.visit(fields); } + @Override + public final Clause clause() { + return DUMMY; + } + // ------------------------------------------------------------------------ // XXX: Row accessor API // ------------------------------------------------------------------------ @@ -9910,13 +9917,13 @@ implements @Override public final Condition in(Collection rows) { - QueryPartList list = new QueryPartList(rows); + QueryPartList list = new QueryPartList(DUMMY, rows); return new RowInCondition(this, list, Comparator.IN); } @Override public final Condition notIn(Collection rows) { - QueryPartList list = new QueryPartList(rows); + QueryPartList list = new QueryPartList(DUMMY, rows); return new RowInCondition(this, list, Comparator.NOT_IN); } diff --git a/jOOQ/src/main/java/org/jooq/impl/RowInCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowInCondition.java index 5b5098e7a0..78b1cf5ff4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowInCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowInCondition.java @@ -36,7 +36,11 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_IN; +import static org.jooq.Clause.CONDITION_IN_NOT; +import static org.jooq.Clause.DUMMY; import static org.jooq.Comparator.EQUALS; +import static org.jooq.Comparator.IN; import static org.jooq.Comparator.NOT_IN; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.DB2; @@ -51,6 +55,7 @@ import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Configuration; @@ -89,6 +94,11 @@ class RowInCondition extends AbstractCondition { delegate(context.configuration()).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { if (asList(ASE, DB2, DERBY, FIREBIRD, INGRES, SQLSERVER, SQLITE, SYBASE).contains(configuration.dialect().family())) { List conditions = new ArrayList(); @@ -131,5 +141,10 @@ class RowInCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(left).visit(right); } + + @Override + public final Clause clause() { + return comparator == IN ? CONDITION_IN : CONDITION_IN_NOT; + } } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java b/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java index ab0f9c29e5..88a6e8ef24 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowIsNull.java @@ -36,6 +36,9 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_NULL; +import static org.jooq.Clause.CONDITION_NULL_NOT; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; import static org.jooq.SQLDialect.DERBY; @@ -53,6 +56,7 @@ import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -89,6 +93,11 @@ class RowIsNull extends AbstractCondition { delegate(context.configuration()).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { // CUBRID 9.0.0 and HSQLDB have buggy implementations of the NULL predicate. @@ -125,5 +134,10 @@ class RowIsNull extends AbstractCondition { public final void bind(BindContext context) { context.visit(row); } + + @Override + public final Clause clause() { + return isNull ? CONDITION_NULL : CONDITION_NULL_NOT; + } } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java index 52a2cc6c94..d4e80249ab 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowOverlapsCondition.java @@ -36,6 +36,8 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_OVERLAPS; +import static org.jooq.Clause.DUMMY; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DB2; @@ -51,6 +53,7 @@ import static org.jooq.SQLDialect.SQLSERVER; import static org.jooq.SQLDialect.SYBASE; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.DataType; import org.jooq.Field; @@ -86,6 +89,11 @@ class RowOverlapsCondition extends AbstractCondition { delegate(context.configuration()).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration) { Field left1 = left.field1(); Field left2 = left.field2(); @@ -152,5 +160,10 @@ class RowOverlapsCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(left).visit(right); } + + @Override + public final Clause clause() { + return CONDITION_OVERLAPS; + } } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java b/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java index 151e13a3f2..aa6c61e46d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowSubqueryCondition.java @@ -36,6 +36,8 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.CONDITION_COMPARISON; +import static org.jooq.Clause.DUMMY; import static org.jooq.Comparator.EQUALS; import static org.jooq.Comparator.IN; import static org.jooq.Comparator.NOT_EQUALS; @@ -58,6 +60,7 @@ import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Configuration; @@ -100,6 +103,11 @@ class RowSubqueryCondition extends AbstractCondition { delegate(context.configuration(), null).bind(context); } + @Override + public final Clause clause() { + return DUMMY; + } + private final QueryPartInternal delegate(Configuration configuration, RenderContext context) { SQLDialect family = configuration.dialect().family(); @@ -214,5 +222,10 @@ class RowSubqueryCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(left).visit(right); } + + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } } } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java b/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java index 363ca8d267..2f2e0fd0cf 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLCondition.java @@ -35,8 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.CONDITION; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; import org.jooq.RenderContext; class SQLCondition extends AbstractCondition { @@ -70,4 +74,13 @@ class SQLCondition extends AbstractCondition { public final void bind(BindContext context) { context.visit(delegate); } + + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return CONDITION; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLField.java b/jOOQ/src/main/java/org/jooq/impl/SQLField.java index 7c6e923201..89fcf8a315 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLField.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLField.java @@ -35,9 +35,13 @@ */ package org.jooq.impl; +import static org.jooq.Clause.FIELD; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; import org.jooq.RenderContext; class SQLField extends AbstractField { @@ -68,4 +72,13 @@ class SQLField extends AbstractField { public final void bind(BindContext context) { context.visit(delegate); } + + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return FIELD; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java b/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java index fb9f37a615..a0fc23bb5d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLQuery.java @@ -36,8 +36,10 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; import org.jooq.RenderContext; /** @@ -71,4 +73,13 @@ class SQLQuery extends AbstractQuery { public final void bind(BindContext context) { context.visit(delegate); } + + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return null; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java b/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java index 3b8a296f30..c13ef074b9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLResultQuery.java @@ -38,9 +38,11 @@ package org.jooq.impl; import java.sql.ResultSetMetaData; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; import org.jooq.Record; import org.jooq.RenderContext; @@ -78,6 +80,15 @@ class SQLResultQuery extends AbstractResultQuery { context.visit(delegate); } + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return null; + } + @Override public final Class getRecordType() { return RecordImpl.class; diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLTable.java b/jOOQ/src/main/java/org/jooq/impl/SQLTable.java index 9470d05499..88e116a14f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLTable.java @@ -35,8 +35,12 @@ */ package org.jooq.impl; +import static org.jooq.Clause.TABLE; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Table; @@ -85,6 +89,15 @@ class SQLTable extends AbstractTable { context.visit(delegate); } + @Override + public final Clause clause() { + if (delegate instanceof QueryPartInternal) { + return ((QueryPartInternal) delegate).clause(); + } + + return TABLE; + } + @Override final Fields fields0() { return new Fields(); diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLTemplate.java b/jOOQ/src/main/java/org/jooq/impl/SQLTemplate.java index a24616c2f2..b6e2286bc5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLTemplate.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLTemplate.java @@ -38,6 +38,7 @@ package org.jooq.impl; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.QueryPart; import org.jooq.RenderContext; import org.jooq.Template; @@ -79,5 +80,10 @@ class SQLTemplate implements Template { public final void bind(BindContext context) { Utils.renderAndBind(null, context, sql, substitutes); } + + @Override + public final Clause clause() { + return null; + } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java b/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java index 18a0fd43ec..b4569cef59 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java @@ -36,10 +36,13 @@ package org.jooq.impl; +import static org.jooq.Clause.SCHEMA; + import java.util.Collections; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.RenderContext; import org.jooq.Schema; import org.jooq.Sequence; @@ -70,12 +73,17 @@ public class SchemaImpl extends AbstractQueryPart implements Schema { return schemaName; } + @Override + public final void toSQL(RenderContext context) { + context.literal(getName()); + } + @Override public final void bind(BindContext context) {} @Override - public final void toSQL(RenderContext context) { - context.literal(getName()); + public final Clause clause() { + return SCHEMA; } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectFieldList.java b/jOOQ/src/main/java/org/jooq/impl/SelectFieldList.java index b2b551ab97..30959d1284 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectFieldList.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectFieldList.java @@ -36,6 +36,8 @@ package org.jooq.impl; +import static org.jooq.Clause.FIELD; + import java.util.Collection; import org.jooq.Field; @@ -49,15 +51,15 @@ class SelectFieldList extends QueryPartList> { private static final long serialVersionUID = 8850104968428500798L; SelectFieldList() { - super(); + super(FIELD); } SelectFieldList(Collection> wrappedList) { - super(wrappedList); + super(FIELD, wrappedList); } SelectFieldList(Field... wrappedList) { - super(wrappedList); + super(FIELD, wrappedList); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java index 75ab96f213..72e7a00d56 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsExistsCondition.java @@ -36,7 +36,12 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_EXISTS; +import static org.jooq.Clause.CONDITION_EXISTS_NOT; +import static org.jooq.impl.ExistsOperator.EXISTS; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.RenderContext; import org.jooq.Select; @@ -96,4 +101,9 @@ class SelectQueryAsExistsCondition extends AbstractCondition { .subquery(false); } } + + @Override + public final Clause clause() { + return operator == EXISTS ? CONDITION_EXISTS : CONDITION_EXISTS_NOT; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java index c9a3b8064a..3bf32885d1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsField.java @@ -37,6 +37,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Field; import org.jooq.RenderContext; @@ -101,4 +102,9 @@ class SelectQueryAsField extends AbstractField { .sql(")"); } } + + @Override + public final Clause clause() { + return null; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java index 0abd47b881..0f13cf1010 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsSubQueryCondition.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_COMPARISON; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Comparator; import org.jooq.Field; import org.jooq.RenderContext; @@ -105,4 +108,9 @@ class SelectQueryAsSubQueryCondition extends AbstractCondition { .subquery(false); } } + + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java index badae9c2b2..2b7b78c2f0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryAsTable.java @@ -37,6 +37,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Select; @@ -112,4 +113,9 @@ class SelectQueryAsTable extends AbstractTable { .subquery(false); } } + + @Override + public final Clause clause() { + return null; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index 2fb5f059bb..92ca04b8c4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -36,6 +36,16 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.DUMMY; +import static org.jooq.Clause.SELECT; +import static org.jooq.Clause.SELECT_CONNECT_BY; +import static org.jooq.Clause.SELECT_FROM; +import static org.jooq.Clause.SELECT_GROUP_BY; +import static org.jooq.Clause.SELECT_HAVING; +import static org.jooq.Clause.SELECT_ORDER_BY; +import static org.jooq.Clause.SELECT_SELECT; +import static org.jooq.Clause.SELECT_START_WITH; +import static org.jooq.Clause.SELECT_WHERE; import static org.jooq.SQLDialect.ASE; import static org.jooq.SQLDialect.CUBRID; import static org.jooq.SQLDialect.DERBY; @@ -62,6 +72,7 @@ import java.util.Collection; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -140,7 +151,7 @@ class SelectQueryImpl extends AbstractSelect implements Sel this.condition = new ConditionProviderImpl(); this.connectBy = new ConditionProviderImpl(); this.connectByStartWith = new ConditionProviderImpl(); - this.groupBy = new QueryPartList(); + this.groupBy = new QueryPartList(DUMMY); this.having = new ConditionProviderImpl(); this.orderBy = new SortFieldList(); this.limit = new Limit(); @@ -149,10 +160,15 @@ class SelectQueryImpl extends AbstractSelect implements Sel this.from.add(from.asTable()); } - this.forUpdateOf = new QueryPartList>(); + this.forUpdateOf = new QueryPartList>(DUMMY); this.forUpdateOfTables = new TableList(); } + @Override + public final Clause clause() { + return SELECT; + } + @Override public final void bind(BindContext context) { context.declareFields(true) @@ -323,7 +339,6 @@ class SelectQueryImpl extends AbstractSelect implements Sel context.formatSeparator() .sql(option); } - } /** @@ -434,7 +449,9 @@ class SelectQueryImpl extends AbstractSelect implements Sel // SELECT clause // ------------- - context.keyword("select "); + context.start(SELECT_SELECT) + .keyword("select") + .sql(" "); // [#1493] Oracle hints come directly after the SELECT keyword if (!StringUtils.isBlank(hint)) { @@ -442,7 +459,7 @@ class SelectQueryImpl extends AbstractSelect implements Sel } if (distinct) { - context.keyword("distinct "); + context.keyword("distinct").sql(" "); } // Sybase and SQL Server have leading TOP clauses @@ -527,15 +544,24 @@ class SelectQueryImpl extends AbstractSelect implements Sel .paramType(paramType); } - context.declareFields(false); + context.declareFields(false) + .end(SELECT_SELECT); // FROM and JOIN clauses // --------------------- - context.declareTables(true); + context.start(SELECT_FROM) + .declareTables(true); - if (!context.render(getFrom()).isEmpty()) { + // The simplest way to see if no FROM clause needs to be rendered is to + // render it. But use a new RenderContext (without any VisitListeners) + // for that purpose! + DefaultConfiguration c = new DefaultConfiguration(context.configuration().dialect()); + String renderedFrom = new DefaultRenderContext(c).render(getFrom()); + + if (!renderedFrom.isEmpty()) { context.formatSeparator() - .keyword("from ") + .keyword("from") + .sql(" ") .visit(getFrom()); // [#1681] Sybase ASE and Ingres need a cross-joined dummy table @@ -545,32 +571,61 @@ class SelectQueryImpl extends AbstractSelect implements Sel } } - context.declareTables(false); + context.declareTables(false) + .end(SELECT_FROM); // WHERE clause // ------------ + context.start(SELECT_WHERE); + if (!(getWhere().getWhere() instanceof TrueCondition)) { context.formatSeparator() - .keyword("where ") + .keyword("where") + .sql(" ") .visit(getWhere()); } + context.end(SELECT_WHERE); + // CONNECT BY clause // ----------------- - if (!(getConnectBy().getWhere() instanceof TrueCondition)) { - // CUBRID supports this clause only as [ START WITH .. ] CONNECT BY - // Oracle also knows the CONNECT BY .. [ START WITH ] alternative - // syntax - toSQLStartWith(context); - toSQLConnectBy(context); + // CUBRID supports this clause only as [ START WITH .. ] CONNECT BY + // Oracle also knows the CONNECT BY .. [ START WITH ] alternative + // syntax + context.start(SELECT_START_WITH); + + if (!(getConnectByStartWith().getWhere() instanceof TrueCondition)) { + context.formatSeparator() + .keyword("start with") + .sql(" ") + .visit(getConnectByStartWith()); } + context.end(SELECT_START_WITH); + context.start(SELECT_CONNECT_BY); + + if (!(getConnectBy().getWhere() instanceof TrueCondition)) { + context.formatSeparator() + .keyword("connect by"); + + if (connectByNoCycle) { + context.keyword(" nocycle"); + } + + context.sql(" ").visit(getConnectBy()); + } + + context.end(SELECT_CONNECT_BY); + // GROUP BY and HAVING clause // -------------------------- + context.start(SELECT_GROUP_BY); + if (grouping) { context.formatSeparator() - .keyword("group by "); + .keyword("group by") + .sql(" "); // [#1665] Empty GROUP BY () clauses need parentheses if (getGroupBy().isEmpty()) { @@ -595,19 +650,33 @@ class SelectQueryImpl extends AbstractSelect implements Sel } } + context.end(SELECT_GROUP_BY); + + // HAVING clause + // ------------- + context.start(SELECT_HAVING); + if (!(getHaving().getWhere() instanceof TrueCondition)) { context.formatSeparator() - .keyword("having ") + .keyword("having") + .sql(" ") .visit(getHaving()); } + context.end(SELECT_HAVING); + // ORDER BY clause // --------------- + context.start(SELECT_ORDER_BY); + if (!getOrderBy().isEmpty()) { context.formatSeparator() - .keyword("order ") - .keyword(orderBySiblings ? "siblings " : "") - .keyword("by ") + .keyword("order") + .sql(orderBySiblings ? " " : "") + .keyword(orderBySiblings ? "siblings" : "") + .sql(" ") + .keyword("by") + .sql(" ") .visit(getOrderBy()); } @@ -615,27 +684,11 @@ class SelectQueryImpl extends AbstractSelect implements Sel // OFFSET .. FETCH else if (getLimit().isApplicable() && asList(SQLSERVER, SQLSERVER2012).contains(dialect)){ context.formatSeparator() - .keyword("order by 1"); - } - } - - private void toSQLStartWith(RenderContext context) { - if (!(getConnectByStartWith().getWhere() instanceof TrueCondition)) { - context.formatSeparator() - .keyword("start with ") - .visit(getConnectByStartWith()); - } - } - - private void toSQLConnectBy(RenderContext context) { - context.formatSeparator() - .keyword("connect by"); - - if (connectByNoCycle) { - context.keyword(" nocycle"); + .keyword("order by") + .sql(" 1"); } - context.sql(" ").visit(getConnectBy()); + context.end(SELECT_ORDER_BY); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java index 92fbc5044d..a32593d94c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java @@ -35,11 +35,13 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.DSL.nvl2; import static org.jooq.impl.DSL.one; import static org.jooq.impl.DSL.zero; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.RenderContext; import org.jooq.SortField; @@ -122,10 +124,10 @@ class SortFieldImpl extends AbstractQueryPart implements SortField { .keyword(order.toSQL()); if (nullsFirst) { - context.keyword(" nulls first"); + context.sql(" ").keyword("nulls first"); } else { - context.keyword(" nulls last"); + context.sql(" ").keyword("nulls last"); } break; @@ -162,4 +164,9 @@ class SortFieldImpl extends AbstractQueryPart implements SortField { context.visit(field); } + + @Override + public final Clause clause() { + return DUMMY; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SortFieldList.java b/jOOQ/src/main/java/org/jooq/impl/SortFieldList.java index 9f05642e9a..7b56fb6586 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SortFieldList.java +++ b/jOOQ/src/main/java/org/jooq/impl/SortFieldList.java @@ -36,6 +36,8 @@ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -55,7 +57,7 @@ class SortFieldList extends QueryPartList> { } SortFieldList(List> wrappedList) { - super(wrappedList); + super(DUMMY, wrappedList); } void addAll(Field... fields) { diff --git a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java index bca3428e47..6480395833 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java @@ -36,10 +36,13 @@ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Field; import org.jooq.ForeignKey; import org.jooq.Record; @@ -125,6 +128,11 @@ class TableAlias extends AbstractTable { context.visit(alias); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final Table as(String as) { return alias.wrapped().as(as); diff --git a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java index 1a38fd22de..d3ae226db5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.FIELD; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Record; import org.jooq.RenderContext; @@ -79,6 +82,11 @@ class TableFieldImpl extends AbstractField implements Ta @Override public final void bind(BindContext context) {} + @Override + public final Clause clause() { + return FIELD; + } + // ------------------------------------------------------------------------ // XXX: Object API // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java index cab6fb07fe..f118e013e1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.TABLE; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Schema; @@ -89,6 +92,16 @@ public class TableImpl extends AbstractTable { return null; } + @Override + final Fields fields0() { + return fields; + } + + @Override + public final Clause clause() { + return TABLE; + } + @Override public final void bind(BindContext context) { if (alias != null) { @@ -96,11 +109,6 @@ public class TableImpl extends AbstractTable { } } - @Override - final Fields fields0() { - return fields; - } - @Override public final void toSQL(RenderContext context) { if (alias != null) { diff --git a/jOOQ/src/main/java/org/jooq/impl/TableList.java b/jOOQ/src/main/java/org/jooq/impl/TableList.java index 934339085f..21789634c0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableList.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableList.java @@ -36,6 +36,8 @@ package org.jooq.impl; +import static org.jooq.Clause.TABLE; + import java.util.List; import org.jooq.Field; @@ -50,11 +52,11 @@ class TableList extends QueryPartList> { private static final long serialVersionUID = -8545559185481762229L; TableList() { - super(); + super(TABLE); } TableList(List> wrappedList) { - super(wrappedList); + super(TABLE, wrappedList); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java b/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java index ae23b3b3be..1eb4a0758b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java @@ -36,7 +36,10 @@ package org.jooq.impl; +import static org.jooq.Clause.CONDITION_COMPARISON; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.RenderContext; /** @@ -54,5 +57,10 @@ class TrueCondition extends AbstractCondition { context.sql("1 = 1"); } + @Override + public final Clause clause() { + return CONDITION_COMPARISON; + } + TrueCondition() {} } diff --git a/jOOQ/src/main/java/org/jooq/impl/TruncateImpl.java b/jOOQ/src/main/java/org/jooq/impl/TruncateImpl.java index 95276eca6b..19730eb1fb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TruncateImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TruncateImpl.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.TRUNCATE; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Record; import org.jooq.RenderContext; @@ -132,4 +135,9 @@ class TruncateImpl extends AbstractQuery implements public final void bind(BindContext context) { context.visit(table); } + + @Override + public final Clause clause() { + return TRUNCATE; + } } diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java index 719ac3eaec..cb85b2c1fd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Record; @@ -136,6 +139,11 @@ public class UDTImpl> extends AbstractQueryPart implement context.visit(fields); } + @Override + public final Clause clause() { + return DUMMY; + } + /** * Subclasses may call this method to create {@link UDTField} objects that * are linked to this table. diff --git a/jOOQ/src/main/java/org/jooq/impl/Union.java b/jOOQ/src/main/java/org/jooq/impl/Union.java index 88afe182dd..0227bf4812 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Union.java +++ b/jOOQ/src/main/java/org/jooq/impl/Union.java @@ -35,12 +35,17 @@ */ package org.jooq.impl; +import static org.jooq.Clause.SELECT_EXCEPT; +import static org.jooq.Clause.SELECT_INTERSECT; +import static org.jooq.Clause.SELECT_UNION; +import static org.jooq.Clause.SELECT_UNION_ALL; import static org.jooq.impl.Utils.visitAll; import java.util.ArrayList; import java.util.List; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Configuration; import org.jooq.Field; import org.jooq.Record; @@ -63,10 +68,10 @@ class Union extends AbstractSelect { Union(Configuration configuration, Select query1, Select query2, CombineOperator operator) { super(configuration); - this.operator = operator; this.queries = new ArrayList>(); this.queries.add(query1); this.queries.add(query2); + this.operator = operator; } @Override @@ -128,6 +133,17 @@ class Union extends AbstractSelect { visitAll(context, queries); } + @Override + public final Clause clause() { + switch (operator) { + case EXCEPT: return SELECT_EXCEPT; + case INTERSECT: return SELECT_INTERSECT; + case UNION: return SELECT_UNION; + case UNION_ALL: return SELECT_UNION_ALL; + default: throw new IllegalArgumentException("Operator not supported : " + operator); + } + } + @Override final boolean isForUpdate() { return false; diff --git a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java index a693e017cc..d83aa05019 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UpdateQueryImpl.java @@ -37,6 +37,12 @@ package org.jooq.impl; import static java.util.Arrays.asList; +import static org.jooq.Clause.UPDATE; +import static org.jooq.Clause.UPDATE_RETURNING; +import static org.jooq.Clause.UPDATE_SET; +import static org.jooq.Clause.UPDATE_SET_ASSIGNMENT; +import static org.jooq.Clause.UPDATE_UPDATE; +import static org.jooq.Clause.UPDATE_WHERE; import static org.jooq.SQLDialect.INGRES; import static org.jooq.SQLDialect.ORACLE; import static org.jooq.impl.DSL.select; @@ -47,6 +53,7 @@ import java.util.Map; import javax.annotation.Generated; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Condition; import org.jooq.Configuration; import org.jooq.Field; @@ -445,18 +452,24 @@ class UpdateQueryImpl extends AbstractStoreQuery implements @Override public final void toSQL(RenderContext context) { - context.keyword("update ") + context.start(UPDATE_UPDATE) + .keyword("update") + .sql(" ") .declareTables(true) .visit(getInto()) .declareTables(false) + .end(UPDATE_UPDATE) .formatSeparator() - .keyword("set "); + .start(UPDATE_SET) + .keyword("set") + .sql(" "); // A multi-row update was specified if (multiRow != null) { boolean qualify = context.qualify(); - context.qualify(false) + context.start(UPDATE_SET_ASSIGNMENT) + .qualify(false) .visit(multiRow) .qualify(qualify) .sql(" = "); @@ -467,7 +480,7 @@ class UpdateQueryImpl extends AbstractStoreQuery implements context.visit(multiValue); } - // Subselects or subselect simulatinos of row value expressions + // Subselects or subselect simulations of row value expressions else { Select select = multiSelect; @@ -485,6 +498,8 @@ class UpdateQueryImpl extends AbstractStoreQuery implements .formatNewLine() .sql(")"); } + + context.end(UPDATE_SET_ASSIGNMENT); } // A regular (non-multi-row) update was specified @@ -494,13 +509,17 @@ class UpdateQueryImpl extends AbstractStoreQuery implements .formatIndentLockEnd(); } + context.end(UPDATE_SET); + if (!(getWhere() instanceof TrueCondition)) { context.formatSeparator() + .start(UPDATE_WHERE) .keyword("where ") - .visit(getWhere()); + .visit(getWhere()) + .end(UPDATE_WHERE); } - toSQLReturning(context); + toSQLReturning(context, UPDATE_RETURNING); } @Override @@ -530,6 +549,11 @@ class UpdateQueryImpl extends AbstractStoreQuery implements bindReturning(context); } + @Override + public final Clause clause() { + return UPDATE; + } + @Override public final boolean isExecutable() { return updateMap.size() > 0 || multiRow != null; diff --git a/jOOQ/src/main/java/org/jooq/impl/Values.java b/jOOQ/src/main/java/org/jooq/impl/Values.java index 22e1f26bfb..dd48eb3c9d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Values.java +++ b/jOOQ/src/main/java/org/jooq/impl/Values.java @@ -35,9 +35,11 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; import static org.jooq.impl.Utils.visitAll; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Row; @@ -158,6 +160,11 @@ class Values extends AbstractTable { visitAll(context, rows); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override final Fields fields0() { return new Fields(rows[0].fields()); diff --git a/jOOQ/src/main/java/org/jooq/impl/WithTable.java b/jOOQ/src/main/java/org/jooq/impl/WithTable.java index 4aaaf735f6..82016866cd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WithTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/WithTable.java @@ -35,7 +35,10 @@ */ package org.jooq.impl; +import static org.jooq.Clause.DUMMY; + import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.Record; import org.jooq.RenderContext; import org.jooq.Table; @@ -79,6 +82,11 @@ class WithTable extends AbstractTable { context.visit(delegate); } + @Override + public final Clause clause() { + return DUMMY; + } + @Override public final Class getRecordType() { return delegate.getRecordType(); diff --git a/jOOQ/src/main/java/org/jooq/impl/WrappedList.java b/jOOQ/src/main/java/org/jooq/impl/WrappedList.java index 2c1c8d4d43..96a7703a1c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WrappedList.java +++ b/jOOQ/src/main/java/org/jooq/impl/WrappedList.java @@ -36,6 +36,7 @@ package org.jooq.impl; import org.jooq.BindContext; +import org.jooq.Clause; import org.jooq.RenderContext; /** @@ -65,4 +66,9 @@ class WrappedList extends AbstractQueryPart { public final void bind(BindContext context) { context.visit(wrapped); } + + @Override + public final Clause clause() { + return null; + } } diff --git a/jOOQ/src/test/java/org/jooq/test/VisitContextTest.java b/jOOQ/src/test/java/org/jooq/test/VisitContextTest.java new file mode 100644 index 0000000000..78d72adf01 --- /dev/null +++ b/jOOQ/src/test/java/org/jooq/test/VisitContextTest.java @@ -0,0 +1,133 @@ +/** + * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.test; + +import static org.jooq.Clause.SELECT_WHERE; +import static org.jooq.impl.DSL.select; +import static org.jooq.impl.DSL.using; +import static org.jooq.impl.DefaultVisitListenerProvider.providers; +import static org.jooq.test.data.Table1.FIELD_ID1; +import static org.jooq.test.data.Table1.FIELD_NAME1; +import static org.jooq.test.data.Table1.TABLE1; +import static org.jooq.tools.StringUtils.leftPad; + +import org.jooq.Clause; +import org.jooq.Configuration; +import org.jooq.VisitContext; +import org.jooq.VisitListener; + +import org.junit.Test; + +/** + * Some common tests related to {@link VisitContext} + * + * @author Lukas Eder + */ +public class VisitContextTest extends AbstractTest { + + @Test + public void testClauses() { + Configuration c = create.configuration().derive(providers(new ClausesListener())); + +// String sql = +// using(c) +// .select(FIELD_ID1, FIELD_NAME1) +// .from(select(FIELD_NAME1).from( +// TABLE1, +// TABLE2.join(TABLE3).on("1 = 2") +// .leftOuterJoin(TABLE3).on("1 = 1"))) +// .where(FIELD_ID1.eq(1)) +// .and(FIELD_NAME1.ne("3")) +// .and("x = y") +// .having(FIELD_ID1.eq(1)) +// .getSQL(); + String sql = + using(c) + .select(FIELD_ID1, FIELD_NAME1) + .from(select(FIELD_NAME1).from(TABLE1)) + .getSQL(); + + System.out.println(); + System.out.println(sql); + } + + private static class ClausesListener implements VisitListener { + + Clause clause; + String where = "where"; + int indent = 0; + + @Override + public void clauseStart(VisitContext context) { + if (context.clause() == Clause.DUMMY) + return; + + clause = context.clause(); + indent += 2; + System.out.println(leftPad("+-", indent, "| ") + context.clause()); + } + + @Override + public void clauseEnd(VisitContext context) { + if (context.clause() == Clause.DUMMY) + return; + + if (clause == SELECT_WHERE) { + if (context.renderContext() != null) { + context.renderContext() + .sql(" ") + .keyword(where) + .sql(" ") + .sql("SecurityCode IN (1, 2, 3)"); + } + } + + clause = null; + indent -= 2; + } + + @Override + public void visitStart(VisitContext context) { + if (clause == SELECT_WHERE) { + where = "and"; + } + } + + @Override + public void visitEnd(VisitContext context) { + } + } +}