From c48dd42de379cc38c12aedc86758b75d9f8b86a7 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 11 Oct 2022 12:27:55 +0200 Subject: [PATCH] [jOOQ/jOOQ#14065] More expression repetition avoidance This includes: - Additional tests to detect repetition - [jOOQ/jOOQ#14066] Split SimpleQueryPart into two, allowing for marking types that are always simple - Turn more QueryPart types into SimpleQueryPart --- jOOQ/src/main/java/org/jooq/impl/Alias.java | 8 ++- .../main/java/org/jooq/impl/CatalogImpl.java | 9 +-- jOOQ/src/main/java/org/jooq/impl/Decode.java | 16 +++-- .../java/org/jooq/impl/FalseCondition.java | 2 +- .../main/java/org/jooq/impl/FieldAlias.java | 7 +- .../java/org/jooq/impl/NullCondition.java | 2 +- .../jooq/impl/QueryPartCollectionView.java | 2 +- .../main/java/org/jooq/impl/SchemaImpl.java | 2 +- .../org/jooq/impl/SimpleCheckQueryPart.java | 65 +++++++++++++++++++ .../java/org/jooq/impl/SimpleQueryPart.java | 13 +--- .../java/org/jooq/impl/SortFieldImpl.java | 2 +- .../main/java/org/jooq/impl/TableAlias.java | 7 +- .../main/java/org/jooq/impl/TableImpl.java | 2 +- jOOQ/src/main/java/org/jooq/impl/Tools.java | 65 +++++++++++++++---- .../java/org/jooq/impl/TrueCondition.java | 2 +- 15 files changed, 159 insertions(+), 45 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/SimpleCheckQueryPart.java diff --git a/jOOQ/src/main/java/org/jooq/impl/Alias.java b/jOOQ/src/main/java/org/jooq/impl/Alias.java index d9cb90e33f..4ab23182e8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Alias.java +++ b/jOOQ/src/main/java/org/jooq/impl/Alias.java @@ -112,7 +112,7 @@ import org.jooq.impl.QOM.UEmpty; /** * @author Lukas Eder */ -final class Alias extends AbstractQueryPart implements UEmpty { +final class Alias extends AbstractQueryPart implements UEmpty, SimpleCheckQueryPart { private static final Clause[] CLAUSES_TABLE_REFERENCE = { TABLE, TABLE_REFERENCE }; private static final Clause[] CLAUSES_TABLE_ALIAS = { TABLE, TABLE_ALIAS }; @@ -148,6 +148,12 @@ final class Alias extends AbstractQueryPart implements UEmp return wrapped; } + @Override + public final boolean isSimple(Context ctx) { + return wrapped instanceof Table && !ctx.declareTables() + || wrapped instanceof Field && !ctx.declareFields(); + } + @Override public final void accept(Context ctx) { diff --git a/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java b/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java index f424b7609b..eed7aeda52 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CatalogImpl.java @@ -43,20 +43,17 @@ import static org.jooq.impl.Tools.getMappedCatalog; import java.util.Collections; import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Predicate; import java.util.stream.Stream; import org.jooq.Catalog; import org.jooq.Clause; import org.jooq.Comment; import org.jooq.Context; -import org.jooq.Function1; import org.jooq.Name; -import org.jooq.Schema; -// ... import org.jooq.QueryPart; // ... +import org.jooq.Schema; +// ... import org.jooq.tools.StringUtils; /** @@ -67,7 +64,7 @@ import org.jooq.tools.StringUtils; * @author Lukas Eder */ @org.jooq.Internal -public class CatalogImpl extends AbstractNamed implements Catalog { +public class CatalogImpl extends AbstractNamed implements Catalog, SimpleQueryPart { private static final Clause[] CLAUSES = { CATALOG, CATALOG_REFERENCE }; static final Catalog DEFAULT_CATALOG = new CatalogImpl(""); diff --git a/jOOQ/src/main/java/org/jooq/impl/Decode.java b/jOOQ/src/main/java/org/jooq/impl/Decode.java index e78afbb8f8..8b611ff1d1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Decode.java +++ b/jOOQ/src/main/java/org/jooq/impl/Decode.java @@ -79,15 +79,17 @@ final class Decode extends AbstractField implements UNotYetImplemented @Override public final void accept(Context ctx) { if (EMULATE_DISTINCT.contains(ctx.dialect())) { - CaseConditionStep when = DSL.choose().when(field.isNotDistinctFrom(search), result); + ctx.visit(Tools.derivedTableIf(ctx, more.length > 1, field, f -> { + CaseConditionStep when = DSL.choose().when(f.isNotDistinctFrom(search), result); - for (int i = 0; i + 1 < more.length; i += 2) - when = when.when(field.isNotDistinctFrom((Field) more[i]), (Field) more[i + 1]); + for (int i = 0; i + 1 < more.length; i += 2) + when = when.when(f.isNotDistinctFrom((Field) more[i]), (Field) more[i + 1]); - if (more.length % 2 == 0) - ctx.visit(when); - else - ctx.visit(when.otherwise((Field) more[more.length - 1])); + if (more.length % 2 == 0) + return when; + else + return when.otherwise((Field) more[more.length - 1]); + })); } diff --git a/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java b/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java index f1a4c2ae76..89ed25e404 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/FalseCondition.java @@ -51,7 +51,7 @@ import org.jooq.impl.QOM.UEmpty; /** * @author Lukas Eder */ -final class FalseCondition extends AbstractCondition implements False, UEmpty { +final class FalseCondition extends AbstractCondition implements False, UEmpty, SimpleQueryPart { private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; static final FalseCondition INSTANCE = new FalseCondition(); diff --git a/jOOQ/src/main/java/org/jooq/impl/FieldAlias.java b/jOOQ/src/main/java/org/jooq/impl/FieldAlias.java index 18824f3a72..22f933ab34 100644 --- a/jOOQ/src/main/java/org/jooq/impl/FieldAlias.java +++ b/jOOQ/src/main/java/org/jooq/impl/FieldAlias.java @@ -52,7 +52,7 @@ import org.jetbrains.annotations.NotNull; /** * @author Lukas Eder */ -final class FieldAlias extends AbstractField implements QOM.FieldAlias { +final class FieldAlias extends AbstractField implements QOM.FieldAlias, SimpleCheckQueryPart { private final Alias> alias; @@ -62,6 +62,11 @@ final class FieldAlias extends AbstractField implements QOM.FieldAlias this.alias = new Alias<>(field, this, alias); } + @Override + public final boolean isSimple(Context ctx) { + return !ctx.declareFields(); + } + @Override public final void accept(Context ctx) { ctx.visit(alias); diff --git a/jOOQ/src/main/java/org/jooq/impl/NullCondition.java b/jOOQ/src/main/java/org/jooq/impl/NullCondition.java index 4b2cdb3c12..42cbd699d7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/NullCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/NullCondition.java @@ -57,7 +57,7 @@ import org.jooq.impl.QOM.UEmpty; /** * @author Lukas Eder */ -final class NullCondition extends AbstractCondition implements Null, UEmpty { +final class NullCondition extends AbstractCondition implements Null, UEmpty, SimpleQueryPart { private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; static final NullCondition INSTANCE = new NullCondition(); diff --git a/jOOQ/src/main/java/org/jooq/impl/QueryPartCollectionView.java b/jOOQ/src/main/java/org/jooq/impl/QueryPartCollectionView.java index 6128b94283..3a840e4643 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QueryPartCollectionView.java +++ b/jOOQ/src/main/java/org/jooq/impl/QueryPartCollectionView.java @@ -76,7 +76,7 @@ extends AbstractQueryPart implements UnmodifiableCollection, - SimpleQueryPart, + SimpleCheckQueryPart, SeparatedQueryPart permits diff --git a/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java b/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java index 2fce0ebf75..c671aceb6f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SchemaImpl.java @@ -80,7 +80,7 @@ import org.jooq.tools.StringUtils; * @author Lukas Eder */ @org.jooq.Internal -public class SchemaImpl extends AbstractNamed implements Schema { +public class SchemaImpl extends AbstractNamed implements Schema, SimpleQueryPart { private static final Clause[] CLAUSES = { SCHEMA, SCHEMA_REFERENCE }; static final Schema DEFAULT_SCHEMA = new SchemaImpl(""); diff --git a/jOOQ/src/main/java/org/jooq/impl/SimpleCheckQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/SimpleCheckQueryPart.java new file mode 100644 index 0000000000..6d4f9163ea --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/SimpleCheckQueryPart.java @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import org.jooq.Context; +import org.jooq.OrderField; +import org.jooq.QueryPart; +import org.jooq.QueryPartInternal; + +/** + * A marker interface for all query parts that are capable of generating + * "simple" SQL. This information is used mainly for formatting decisions. + *

+ * Unlike {@link SimpleQueryPart}, which marks a {@link QueryPart} that is + * unconditionally simple, this allows for checking whether a {@link QueryPart} + * is {@link #isSimple(Context)} + * + * @author Lukas Eder + */ +interface SimpleCheckQueryPart extends QueryPartInternal { + + /** + * Whether the {@link QueryPart} really is simple. + *

+ * e.g. an {@link OrderField} can be simple if its contents are also simple. + */ + default boolean isSimple(Context ctx) { + return true; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/SimpleQueryPart.java b/jOOQ/src/main/java/org/jooq/impl/SimpleQueryPart.java index dc6fbbbf72..7c6a72aa30 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SimpleQueryPart.java +++ b/jOOQ/src/main/java/org/jooq/impl/SimpleQueryPart.java @@ -37,25 +37,16 @@ */ package org.jooq.impl; -import org.jooq.Context; -import org.jooq.OrderField; -import org.jooq.QueryPart; import org.jooq.QueryPartInternal; /** * A marker interface for all query parts that are capable of generating * "simple" SQL. This information is used mainly for formatting decisions. + *

+ * Unlike {@link SimpleCheckQueryPart}, this is always simple. * * @author Lukas Eder */ interface SimpleQueryPart extends QueryPartInternal { - /** - * Whether the {@link QueryPart} really is simple. - *

- * e.g. an {@link OrderField} can be simple if its contents are also simple. - */ - default boolean isSimple(Context ctx) { - return true; - } } diff --git a/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java index da963eb7a3..a7c03beae8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/SortFieldImpl.java @@ -77,7 +77,7 @@ import org.jooq.QueryPart; import org.jooq.impl.QOM.NullOrdering; -final class SortFieldImpl extends AbstractQueryPart implements SortField, SimpleQueryPart { +final class SortFieldImpl extends AbstractQueryPart implements SortField, SimpleCheckQueryPart { // DB2 supports NULLS FIRST/LAST only in OLAP (window) functions private static final Set NO_SUPPORT_NULLS = SQLDialect.supportedUntil(CUBRID, MARIADB, MYSQL); diff --git a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java index 7967344968..9f06f3789a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java @@ -62,7 +62,7 @@ import org.jetbrains.annotations.NotNull; /** * @author Lukas Eder */ -final class TableAlias extends AbstractTable implements QOM.TableAlias { +final class TableAlias extends AbstractTable implements QOM.TableAlias, SimpleCheckQueryPart { final Alias> alias; transient FieldsImpl aliasedFields; @@ -85,6 +85,11 @@ final class TableAlias extends AbstractTable implements QOM this.alias = new Alias<>(table, this, alias, fieldAliases, wrapInParentheses); } + @Override + public final boolean isSimple(Context ctx) { + return !ctx.declareTables(); + } + /** * Register fields for this table alias */ diff --git a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java index d231490be0..263649c866 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java @@ -133,7 +133,7 @@ extends implements ScopeMappable, ScopeNestable, - SimpleQueryPart, + SimpleCheckQueryPart, UNotYetImplemented { diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index 7369b81027..4b69a6dce1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -3721,7 +3721,8 @@ final class Tools { } static final boolean isSimple(Context ctx, QueryPart part) { - return part instanceof SimpleQueryPart && ((SimpleQueryPart) part).isSimple(ctx); + return part instanceof SimpleQueryPart + || part instanceof SimpleCheckQueryPart && ((SimpleCheckQueryPart) part).isSimple(ctx); } static final boolean isSimple(Context ctx, QueryPart... parts) { @@ -7176,10 +7177,7 @@ final class Tools { Field f1, Function1, ? extends Field> f ) { - if (derivedTableEnabled(ctx) && !isSimple(ctx, f1)) - return DSL.field(select(f.apply(f1.as("f1"))).from(select(f1.as("f1")).asTable("t"))); - else - return f.apply(f1); + return derivedTableIf(ctx, true, f1, f); } /** @@ -7192,10 +7190,7 @@ final class Tools { Field f2, Function2, ? super Field, ? extends Field> f ) { - if (derivedTableEnabled(ctx) && !isSimple(ctx, f1) && !isSimple(ctx, f2)) - return DSL.field(select(f.apply(f1.as("f1"), f2.as("f2"))).from(select(f1.as("f1"), f2.as("f2")).asTable("t"))); - else - return f.apply(f1, f2); + return derivedTableIf(ctx, true, f1, f2, f); } /** @@ -7209,8 +7204,56 @@ final class Tools { Field f3, Function3, ? super Field, ? super Field, ? extends Field> f ) { - if (derivedTableEnabled(ctx) && !isSimple(ctx, f1) && !isSimple(ctx, f2) && !isSimple(ctx, f3)) - return DSL.field(select(f.apply(f1.as("f1"), f2.as("f2"), f3.as("f3"))).from(select(f1.as("f1"), f2.as("f2"), f3.as("f3")).asTable("t"))); + return derivedTableIf(ctx, true, f1, f2, f3, f); + } + + /** + * Wrap an expression in a derived table to allow for simplifying + * referencing it. + */ + static final Field derivedTableIf( + Context ctx, + boolean condition, + Field f1, + Function1, ? extends Field> f + ) { + if (condition && derivedTableEnabled(ctx) && !isSimple(ctx, f1)) + return DSL.field(select(f.apply(DSL.field(name("f1"), f1.getDataType()))).from(select(f1.as("f1")).asTable("t"))); + else + return f.apply(f1); + } + + /** + * Wrap expressions in a derived table to allow for simplifying referencing + * them. + */ + static final Field derivedTableIf( + Context ctx, + boolean condition, + Field f1, + Field f2, + Function2, ? super Field, ? extends Field> f + ) { + if (condition && derivedTableEnabled(ctx) && !isSimple(ctx, f1) && !isSimple(ctx, f2)) + return DSL.field(select(f.apply(DSL.field(name("f1"), f1.getDataType()), DSL.field(name("f2"), f2.getDataType()))).from(select(f1.as("f1"), f2.as("f2")).asTable("t"))); + else + return f.apply(f1, f2); + } + + /** + * Wrap expressions in a derived table to allow for simplifying referencing + * them. + */ + static final Field derivedTableIf( + Context ctx, + boolean condition, + Field f1, + Field f2, + Field f3, + Function3, ? super Field, ? super Field, ? extends Field> f + ) { + if (condition && derivedTableEnabled(ctx) && !isSimple(ctx, f1) && !isSimple(ctx, f2) && !isSimple(ctx, f3)) + return DSL.field(select(f.apply(DSL.field(name("f1"), f1.getDataType()), DSL.field(name("f2"), f2.getDataType()), DSL.field(name("f3"), f3.getDataType()))).from(select(f1.as("f1"), f2.as("f2"), f3.as("f3")).asTable("t"))); else return f.apply(f1, f2, f3); } diff --git a/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java b/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java index fc43bafb6d..2c90314d51 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java +++ b/jOOQ/src/main/java/org/jooq/impl/TrueCondition.java @@ -71,7 +71,7 @@ import org.jooq.impl.QOM.UEmpty; /** * @author Lukas Eder */ -final class TrueCondition extends AbstractCondition implements True, UEmpty { +final class TrueCondition extends AbstractCondition implements True, UEmpty, SimpleQueryPart { private static final Clause[] CLAUSES = { CONDITION, CONDITION_COMPARISON }; static final TrueCondition INSTANCE = new TrueCondition();