[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
This commit is contained in:
Lukas Eder 2022-10-11 12:27:55 +02:00
parent 2449f09b9d
commit c48dd42de3
15 changed files with 159 additions and 45 deletions

View File

@ -112,7 +112,7 @@ import org.jooq.impl.QOM.UEmpty;
/**
* @author Lukas Eder
*/
final class Alias<Q extends QueryPart> extends AbstractQueryPart implements UEmpty {
final class Alias<Q extends QueryPart> 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<Q extends QueryPart> 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) {

View File

@ -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("");

View File

@ -79,15 +79,17 @@ final class Decode<T, Z> extends AbstractField<Z> implements UNotYetImplemented
@Override
public final void accept(Context<?> ctx) {
if (EMULATE_DISTINCT.contains(ctx.dialect())) {
CaseConditionStep<Z> when = DSL.choose().when(field.isNotDistinctFrom(search), result);
ctx.visit(Tools.derivedTableIf(ctx, more.length > 1, field, f -> {
CaseConditionStep<Z> when = DSL.choose().when(f.isNotDistinctFrom(search), result);
for (int i = 0; i + 1 < more.length; i += 2)
when = when.when(field.isNotDistinctFrom((Field<T>) more[i]), (Field<Z>) more[i + 1]);
for (int i = 0; i + 1 < more.length; i += 2)
when = when.when(f.isNotDistinctFrom((Field<T>) more[i]), (Field<Z>) more[i + 1]);
if (more.length % 2 == 0)
ctx.visit(when);
else
ctx.visit(when.otherwise((Field<Z>) more[more.length - 1]));
if (more.length % 2 == 0)
return when;
else
return when.otherwise((Field<Z>) more[more.length - 1]);
}));
}

View File

@ -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();

View File

@ -52,7 +52,7 @@ import org.jetbrains.annotations.NotNull;
/**
* @author Lukas Eder
*/
final class FieldAlias<T> extends AbstractField<T> implements QOM.FieldAlias<T> {
final class FieldAlias<T> extends AbstractField<T> implements QOM.FieldAlias<T>, SimpleCheckQueryPart {
private final Alias<Field<T>> alias;
@ -62,6 +62,11 @@ final class FieldAlias<T> extends AbstractField<T> implements QOM.FieldAlias<T>
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);

View File

@ -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();

View File

@ -76,7 +76,7 @@ extends
AbstractQueryPart
implements
UnmodifiableCollection<T>,
SimpleQueryPart,
SimpleCheckQueryPart,
SeparatedQueryPart
permits

View File

@ -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("");

View File

@ -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.
* <p>
* 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.
* <p>
* e.g. an {@link OrderField} can be simple if its contents are also simple.
*/
default boolean isSimple(Context<?> ctx) {
return true;
}
}

View File

@ -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.
* <p>
* Unlike {@link SimpleCheckQueryPart}, this is always simple.
*
* @author Lukas Eder
*/
interface SimpleQueryPart extends QueryPartInternal {
/**
* Whether the {@link QueryPart} really is simple.
* <p>
* e.g. an {@link OrderField} can be simple if its contents are also simple.
*/
default boolean isSimple(Context<?> ctx) {
return true;
}
}

View File

@ -77,7 +77,7 @@ import org.jooq.QueryPart;
import org.jooq.impl.QOM.NullOrdering;
final class SortFieldImpl<T> extends AbstractQueryPart implements SortField<T>, SimpleQueryPart {
final class SortFieldImpl<T> extends AbstractQueryPart implements SortField<T>, SimpleCheckQueryPart {
// DB2 supports NULLS FIRST/LAST only in OLAP (window) functions
private static final Set<SQLDialect> NO_SUPPORT_NULLS = SQLDialect.supportedUntil(CUBRID, MARIADB, MYSQL);

View File

@ -62,7 +62,7 @@ import org.jetbrains.annotations.NotNull;
/**
* @author Lukas Eder
*/
final class TableAlias<R extends Record> extends AbstractTable<R> implements QOM.TableAlias<R> {
final class TableAlias<R extends Record> extends AbstractTable<R> implements QOM.TableAlias<R>, SimpleCheckQueryPart {
final Alias<Table<R>> alias;
transient FieldsImpl<R> aliasedFields;
@ -85,6 +85,11 @@ final class TableAlias<R extends Record> extends AbstractTable<R> 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
*/

View File

@ -133,7 +133,7 @@ extends
implements
ScopeMappable,
ScopeNestable,
SimpleQueryPart,
SimpleCheckQueryPart,
UNotYetImplemented
{

View File

@ -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<T1> f1,
Function1<? super Field<T1>, ? extends Field<R>> 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<T2> f2,
Function2<? super Field<T1>, ? super Field<T2>, ? extends Field<R>> 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<T3> f3,
Function3<? super Field<T1>, ? super Field<T2>, ? super Field<T3>, ? extends Field<R>> 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 <T1, R> Field<R> derivedTableIf(
Context<?> ctx,
boolean condition,
Field<T1> f1,
Function1<? super Field<T1>, ? extends Field<R>> 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 <T1, T2, R> Field<R> derivedTableIf(
Context<?> ctx,
boolean condition,
Field<T1> f1,
Field<T2> f2,
Function2<? super Field<T1>, ? super Field<T2>, ? extends Field<R>> 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 <T1, T2, T3, R> Field<R> derivedTableIf(
Context<?> ctx,
boolean condition,
Field<T1> f1,
Field<T2> f2,
Field<T3> f3,
Function3<? super Field<T1>, ? super Field<T2>, ? super Field<T3>, ? extends Field<R>> 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);
}

View File

@ -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();