From 6033daafe091c3be95877eb05f00d0841ac5a5e9 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 28 Jan 2020 15:00:07 +0100 Subject: [PATCH] [jOOQ/jOOQ#8800] Add Settings.transformAnsiJoinToTableLists to support pre-ANSI join syntax --- .../src/main/java/org/jooq/conf/Settings.java | 50 ++++++++++ .../java/org/jooq/impl/CombinedCondition.java | 4 +- .../java/org/jooq/impl/CompareCondition.java | 14 +-- .../main/java/org/jooq/impl/JoinTable.java | 10 +- .../java/org/jooq/impl/SelectQueryImpl.java | 99 ++++++++++++++++++- .../main/java/org/jooq/impl/Transform.java | 98 ++++++++++++++++++ .../resources/xsd/jooq-runtime-3.13.0.xsd | 17 ++++ 7 files changed, 277 insertions(+), 15 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/Transform.java diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index b15fdead55..950d1c5ae4 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -82,6 +82,8 @@ public class Settings @XmlElement(defaultValue = "true") protected Boolean fetchTriggerValuesAfterSQLServerOutput = true; @XmlElement(defaultValue = "false") + protected Boolean transformAnsiJoinToTableLists = false; + @XmlElement(defaultValue = "false") protected Boolean transformTableListsToAnsiJoin = false; @XmlElement(defaultValue = "DEFAULT") @XmlSchemaType(name = "string") @@ -705,6 +707,38 @@ public class Settings this.fetchTriggerValuesAfterSQLServerOutput = value; } + /** + * Transform ANSI join to table lists if possible + *

+ * Historically, prior to ANSI join syntax, joins were implemented by listing tables in + * the FROM clause and providing join predicates in the WHERE clause, possibly using vendor specific + * operators like (+) (Oracle, DB2) or *= (SQL Server) for outer join + * support. For backwards compatibility with older RDBMS versions, ANSI joins in jOOQ code may be + * converted to equivalent table lists in generated SQL using this flag. + *

+ * This feature is available in the commercial distribution only. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isTransformAnsiJoinToTableLists() { + return transformAnsiJoinToTableLists; + } + + /** + * Sets the value of the transformAnsiJoinToTableLists property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setTransformAnsiJoinToTableLists(Boolean value) { + this.transformAnsiJoinToTableLists = value; + } + /** * Transform table lists to ANSI join if possible *

@@ -2123,6 +2157,11 @@ public class Settings return this; } + public Settings withTransformAnsiJoinToTableLists(Boolean value) { + setTransformAnsiJoinToTableLists(value); + return this; + } + public Settings withTransformTableListsToAnsiJoin(Boolean value) { setTransformTableListsToAnsiJoin(value); return this; @@ -2661,6 +2700,7 @@ public class Settings builder.append("renderOutputForSQLServerReturningClause", renderOutputForSQLServerReturningClause); builder.append("renderParenthesisAroundSetOperationQueries", renderParenthesisAroundSetOperationQueries); builder.append("fetchTriggerValuesAfterSQLServerOutput", fetchTriggerValuesAfterSQLServerOutput); + builder.append("transformAnsiJoinToTableLists", transformAnsiJoinToTableLists); builder.append("transformTableListsToAnsiJoin", transformTableListsToAnsiJoin); builder.append("backslashEscaping", backslashEscaping); builder.append("paramType", paramType); @@ -2916,6 +2956,15 @@ public class Settings return false; } } + if (transformAnsiJoinToTableLists == null) { + if (other.transformAnsiJoinToTableLists!= null) { + return false; + } + } else { + if (!transformAnsiJoinToTableLists.equals(other.transformAnsiJoinToTableLists)) { + return false; + } + } if (transformTableListsToAnsiJoin == null) { if (other.transformTableListsToAnsiJoin!= null) { return false; @@ -3509,6 +3558,7 @@ public class Settings result = ((prime*result)+((renderOutputForSQLServerReturningClause == null)? 0 :renderOutputForSQLServerReturningClause.hashCode())); result = ((prime*result)+((renderParenthesisAroundSetOperationQueries == null)? 0 :renderParenthesisAroundSetOperationQueries.hashCode())); result = ((prime*result)+((fetchTriggerValuesAfterSQLServerOutput == null)? 0 :fetchTriggerValuesAfterSQLServerOutput.hashCode())); + result = ((prime*result)+((transformAnsiJoinToTableLists == null)? 0 :transformAnsiJoinToTableLists.hashCode())); result = ((prime*result)+((transformTableListsToAnsiJoin == null)? 0 :transformTableListsToAnsiJoin.hashCode())); result = ((prime*result)+((backslashEscaping == null)? 0 :backslashEscaping.hashCode())); result = ((prime*result)+((paramType == null)? 0 :paramType.hashCode())); diff --git a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java index 410050775f..515e831272 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CombinedCondition.java @@ -66,8 +66,8 @@ final class CombinedCondition extends AbstractCondition { private static final Clause[] CLAUSES_AND = { CONDITION, CONDITION_AND }; private static final Clause[] CLAUSES_OR = { CONDITION, CONDITION_OR }; - private final Operator operator; - private final List conditions; + final Operator operator; + final List conditions; static Condition of(Operator operator, Condition left, Condition right) { if (left instanceof NoCondition) diff --git a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java index 3b4ee8c066..4a126a2fbc 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/CompareCondition.java @@ -81,14 +81,14 @@ import org.jooq.conf.ParamType; */ final class CompareCondition extends AbstractCondition implements LikeEscapeStep { - private static final long serialVersionUID = -747240442279619486L; - private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; - private static final Set REQUIRES_CAST_ON_LIKE = SQLDialect.supportedBy(DERBY, POSTGRES); + private static final long serialVersionUID = -747240442279619486L; + private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; + private static final Set REQUIRES_CAST_ON_LIKE = SQLDialect.supportedBy(DERBY, POSTGRES); - private final Field field1; - private final Field field2; - private final Comparator comparator; - private Character escape; + final Field field1; + final Field field2; + final Comparator comparator; + private Character escape; CompareCondition(Field field1, Field field2, Comparator comparator) { this.field1 = field1; diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index 2c3f8ce9ec..4ccdcca19a 100755 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -167,9 +167,9 @@ implements - private final JoinType type; - private final ConditionProviderImpl condition; - private final QueryPartList> using; + final JoinType type; + final ConditionProviderImpl condition; + final QueryPartList> using; JoinTable(TableLike lhs, TableLike rhs, JoinType type) { @@ -488,7 +488,7 @@ implements } @SuppressWarnings({ "rawtypes", "unchecked" }) - private final Condition naturalCondition() { + final Condition naturalCondition() { List conditions = new ArrayList<>(using.size()); for (Field field : lhs.fields()) { @@ -502,7 +502,7 @@ implements } @SuppressWarnings({ "rawtypes", "unchecked" }) - private final Condition usingCondition() { + final Condition usingCondition() { List conditions = new ArrayList<>(using.size()); for (Field field : using) diff --git a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java index d673a86df7..aba70125fb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SelectQueryImpl.java @@ -192,6 +192,8 @@ import org.jooq.WindowDefinition; import org.jooq.exception.DataAccessException; import org.jooq.impl.Tools.BooleanDataKey; import org.jooq.impl.Tools.DataKey; +import org.jooq.impl.Transform.Transformer; +import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; /** @@ -207,6 +209,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp * Generated UID */ private static final long serialVersionUID = 1646393178384872967L; + private static final JooqLogger log = JooqLogger.getLogger(SelectQueryImpl.class); private static final Clause[] CLAUSES = { SELECT }; private static final Set EMULATE_SELECT_INTO_AS_CTAS = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, H2, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE); private static final Set NO_SUPPORT_FOR_UPDATE = SQLDialect.supportedBy(CUBRID); @@ -911,7 +914,7 @@ final class SelectQueryImpl extends AbstractResultQuery imp /** * The default LIMIT / OFFSET clause in most dialects */ - private void toSQLReferenceLimitDefault(Context context) { + private final void toSQLReferenceLimitDefault(Context context) { Object data = context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE); context.data(DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE, true); @@ -1355,6 +1358,9 @@ final class SelectQueryImpl extends AbstractResultQuery imp + + + context.formatSeparator() .visit(K_FROM) .sql(' ') @@ -1591,6 +1597,97 @@ final class SelectQueryImpl extends AbstractResultQuery imp } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + private final void toSQLOrderBy( Context ctx, Field[] originalFields, diff --git a/jOOQ/src/main/java/org/jooq/impl/Transform.java b/jOOQ/src/main/java/org/jooq/impl/Transform.java new file mode 100644 index 0000000000..197b988392 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/Transform.java @@ -0,0 +1,98 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.13.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.13.0.xsd index 071276a4b2..53034f97d2 100644 --- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.13.0.xsd +++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.13.0.xsd @@ -177,6 +177,21 @@ included in the OUTPUT clause. For details, see https://github.com/jOOQ/jOOQ/issues/4498.]]> + + +Historically, prior to ANSI join syntax, joins were implemented by listing tables in +the FROM clause and providing join predicates in the WHERE clause, possibly using vendor specific +operators like (+) (Oracle, DB2) or *= (SQL Server) for outer join +support. For backwards compatibility with older RDBMS versions, ANSI joins in jOOQ code may be +converted to equivalent table lists in generated SQL using this flag. +

+This flag has a limited implementation that supports inner joins (in most cases) and outer joins +(only for simple comparison predicates). +

+This feature is available in the commercial distribution only.]]> + + @@ -186,6 +201,8 @@ operators like (+) (Oracle, DB2) or *= (SQL Server) fo support. Migrating such join syntax is tedious. The jOOQ parser can parse the old syntax and this flag enables the transformation to ANSI join syntax.

+This flag has not been implemented yet! +

This feature is available in the commercial distribution only.]]>