[jOOQ/jOOQ#11969] Let Condition extend Field<Boolean>

This commit is contained in:
Lukas Eder 2022-05-17 10:38:28 +02:00
parent 5b5a57eab8
commit 088571bbcc
21 changed files with 89 additions and 40 deletions

View File

@ -372,7 +372,7 @@ public class DerbyDatabase extends AbstractDatabase implements ResultQueryDataba
.when(inline("INTEGER"), inline((long) Integer.MAX_VALUE))
.when(inline("BIGINT"), inline(Long.MAX_VALUE))
).coerce(NUMERIC).as(SYSSEQUENCES.MAXIMUMVALUE),
field(SYSSEQUENCES.CYCLEOPTION.eq(inline("Y"))).as(SYSSEQUENCES.CYCLEOPTION),
SYSSEQUENCES.CYCLEOPTION.eq(inline("Y")).as(SYSSEQUENCES.CYCLEOPTION),
inline(null, BIGINT).cast(BIGINT).as("cache")
)
.from(SYSSEQUENCES)

View File

@ -46,6 +46,7 @@ import static org.jooq.impl.DSL.arrayAgg;
import static org.jooq.impl.DSL.coalesce;
import static org.jooq.impl.DSL.concat;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.exists;
import static org.jooq.impl.DSL.falseCondition;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.inline;
@ -241,7 +242,7 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
INDEXES.TABLE_SCHEMA,
INDEXES.TABLE_NAME,
INDEXES.INDEX_NAME,
field(field(INDEXES.getQualifiedName().append("INDEX_TYPE_NAME")).ne(inline("UNIQUE INDEX"))).as(INDEXES.NON_UNIQUE),
field(INDEXES.getQualifiedName().append("INDEX_TYPE_NAME")).ne(inline("UNIQUE INDEX")).as(INDEXES.NON_UNIQUE),
field("i.column_name", INDEXES.COLUMN_NAME).as(INDEXES.COLUMN_NAME),
field("i.ordinal_position", INDEXES.ORDINAL_POSITION).as(INDEXES.ORDINAL_POSITION),
field("i.ordering_specification", INDEXES.ASC_OR_DESC).as(INDEXES.ASC_OR_DESC))
@ -599,7 +600,7 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
: SEQUENCES.MAX_VALUE;
Field<Boolean> isCycle = is2_0_202()
? field(field(SEQUENCES.getQualifiedName().append("CYCLE_OPTION")).eq(inline("YES")))
? field(SEQUENCES.getQualifiedName().append("CYCLE_OPTION")).eq(inline("YES"))
: SEQUENCES.IS_CYCLE;
return create()
@ -748,13 +749,13 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
private List<RoutineDefinition> getRoutines1_4() {
List<RoutineDefinition> result = new ArrayList<>();
Field<Boolean> overloaded = field(select(field(DSL.exists(
Field<Boolean> overloaded = field(select(DSL.exists(
select(one())
.from(FUNCTION_ALIASES.as("a"))
.where(field(name("a", FUNCTION_ALIASES.ALIAS_SCHEMA.getName())).eq(FUNCTION_ALIASES.ALIAS_SCHEMA))
.and(field(name("a", FUNCTION_ALIASES.ALIAS_NAME.getName())).eq(FUNCTION_ALIASES.ALIAS_NAME))
.and(field(name("a", FUNCTION_ALIASES.COLUMN_COUNT.getName())).ne(FUNCTION_ALIASES.COLUMN_COUNT))
)))).as("overloaded");
))).as("overloaded");
for (Record record : create()
.select(

View File

@ -124,11 +124,11 @@ public class H2TableDefinition extends AbstractTableDefinition {
COLUMNS.NUMERIC_PRECISION).as(COLUMNS.NUMERIC_PRECISION),
nvl(ELEMENT_TYPES.NUMERIC_SCALE, COLUMNS.NUMERIC_SCALE).as(COLUMNS.NUMERIC_SCALE),
COLUMNS.IS_NULLABLE,
field(Tables.COLUMNS.IS_GENERATED.eq(inline("ALWAYS"))).as(COLUMNS.IS_COMPUTED),
Tables.COLUMNS.IS_GENERATED.eq(inline("ALWAYS")).as(COLUMNS.IS_COMPUTED),
Tables.COLUMNS.GENERATION_EXPRESSION,
COLUMNS.COLUMN_DEFAULT,
COLUMNS.REMARKS,
field(Tables.COLUMNS.IS_IDENTITY.eq(inline("YES"))).as(Tables.COLUMNS.IS_IDENTITY),
Tables.COLUMNS.IS_IDENTITY.eq(inline("YES")).as(Tables.COLUMNS.IS_IDENTITY),
COLUMNS.DOMAIN_SCHEMA,
COLUMNS.DOMAIN_NAME
)
@ -220,7 +220,7 @@ public class H2TableDefinition extends AbstractTableDefinition {
COLUMNS.COLUMN_DEFAULT.as("GENERATION_EXPRESSION"),
COLUMNS.COLUMN_DEFAULT,
COLUMNS.REMARKS,
field(COLUMNS.SEQUENCE_NAME.isNotNull()).as("IS_IDENTITY"),
COLUMNS.SEQUENCE_NAME.isNotNull().as("IS_IDENTITY"),
db.is1_4_198() ? COLUMNS.DOMAIN_SCHEMA : inline("").as(COLUMNS.DOMAIN_SCHEMA),
db.is1_4_198() ? COLUMNS.DOMAIN_NAME : inline("").as(COLUMNS.DOMAIN_NAME)
)

View File

@ -566,7 +566,7 @@ public class HSQLDBDatabase extends AbstractDatabase implements ResultQueryDatab
nvl(ELEMENT_TYPES.COLLECTION_TYPE_IDENTIFIER, ROUTINES.DATA_TYPE).as("datatype"),
ROUTINES.NUMERIC_PRECISION,
ROUTINES.NUMERIC_SCALE,
field(ROUTINES.ROUTINE_DEFINITION.likeRegex(".*(?i:(\\w+\\s+)+aggregate\\s+function).*")).as("aggregate"))
ROUTINES.ROUTINE_DEFINITION.likeRegex(".*(?i:(\\w+\\s+)+aggregate\\s+function).*").as("aggregate"))
.from(ROUTINES)
.leftOuterJoin(ELEMENT_TYPES)
.on(ROUTINES.ROUTINE_SCHEMA.equal(ELEMENT_TYPES.OBJECT_SCHEMA))

View File

@ -935,7 +935,7 @@ public class PostgresDatabase extends AbstractDatabase implements ResultQueryDat
// [#7785] The pg_proc.proisagg column has been replaced incompatibly in PostgreSQL 11
Field<Boolean> isAgg = (is11()
? field(PG_PROC.PROKIND.eq(inline("a")))
? PG_PROC.PROKIND.eq(inline("a"))
: field("{0}.proisagg", SQLDataType.BOOLEAN, PG_PROC)
).as("is_agg");

View File

@ -81,7 +81,7 @@ import org.jetbrains.annotations.NotNull;
*
* @author Lukas Eder
*/
public interface Condition extends QueryPart {
public interface Condition extends Field<Boolean> {
/**
* Combine this condition with another one using the {@link Operator#AND}

View File

@ -90,6 +90,30 @@ public interface Context<C extends Context<C>> extends Scope {
@NotNull
C visit(QueryPart part) throws DataAccessException;
/**
* Visit a {@link Condition} as an ordinary {@link QueryPart}.
*/
@NotNull
C visit(Condition part) throws DataAccessException;
/**
* Visit a {@link Field} as a {@link DSL#field(Condition)}, if it is a
* {@link Condition}, or as an ordinery {@link QueryPart}, otherwise.
* <p>
* [#11969] Not all RDBMS support {@link Condition} in the form of
* {@link Field} of type {@link Boolean}, natively. As such, we must wrap
* any such {@link Condition} using {@link DSL#field(Condition)} to make
* sure the appropriate emulations are implemented. This applies to
* conditions that are declared as {@link Field}, e.g. the arguments of a
* function like {@link DSL#nvl(Field, Field)}.
* <p>
* If a {@link Condition} is declared as a condition, then this doesn't
* apply and {@link #visit(Condition)} is invoked, instead, e.g. the
* arguments of {@link AggregateFunction#filterWhere(Condition)}.
*/
@NotNull
C visit(Field<?> part) throws DataAccessException;
/**
* Visit a <code>QueryPart</code> as a subquery in the current
* <code>Context</code>.

View File

@ -57,11 +57,13 @@ import org.jooq.Select;
/**
* @author Lukas Eder
*/
abstract class AbstractCondition extends AbstractQueryPart implements Condition {
abstract class AbstractCondition extends AbstractField<Boolean> implements Condition {
private static final Clause[] CLAUSES = { CONDITION };
AbstractCondition() {}
AbstractCondition() {
super(DSL.name("condition"), SQLDataType.BOOLEAN);
}
/**
* [#10179] Subclasses may override this method to indicate that the

View File

@ -75,9 +75,11 @@ import java.util.function.Supplier;
import org.jooq.BindContext;
import org.jooq.Clause;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.Context;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.JoinType;
import org.jooq.LanguageContext;
@ -97,13 +99,10 @@ import org.jooq.conf.RenderImplicitJoinType;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.conf.StatementType;
import org.jooq.impl.AbstractContext.ScopeStackElement;
import org.jooq.impl.Tools.DataKey;
import org.jooq.impl.Tools.DataKeyScopeStackPart;
import org.jooq.tools.StringUtils;
import org.jetbrains.annotations.NotNull;
/**
* @author Lukas Eder
@ -230,6 +229,16 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
// VisitListener API
// ------------------------------------------------------------------------
@Override
public final C visit(Condition part) {
return visit((QueryPart) part);
}
@Override
public final C visit(Field<?> part) {
return part instanceof Condition ? visit((QueryPart) DSL.field((Condition) part)) : visit((QueryPart) part);
}
@Override
public final C visit(QueryPart part) {
if (part != null) {

View File

@ -265,11 +265,11 @@ abstract class AbstractInList<T> extends AbstractCondition {
private final int padSize;
PaddedList(List<T> delegate, int maxPadding, int padBase) {
int b = max(2, padBase);
int b = Math.max(2, padBase);
this.delegate = delegate;
this.realSize = delegate.size();
this.padSize = min(maxPadding, (int) round(pow(b, ceil(log(realSize) / log(b)))));
this.padSize = Math.min(maxPadding, (int) Math.round(Math.pow(b, Math.ceil(Math.log(realSize) / Math.log(b)))));
}
@Override

View File

@ -61,12 +61,17 @@ import org.jetbrains.annotations.Nullable;
/**
* @author Lukas Eder
*/
final class ConditionProviderImpl extends AbstractQueryPart implements ConditionProvider, Condition, UProxy<Condition> {
final class ConditionProviderImpl extends AbstractField<Boolean> implements ConditionProvider, Condition, UProxy<Condition> {
private Condition condition;
ConditionProviderImpl() {}
ConditionProviderImpl() {
this(null);
}
ConditionProviderImpl(Condition condition) {
super(DSL.name("condition"), SQLDataType.BOOLEAN);
this.condition = condition;
}

View File

@ -20232,7 +20232,7 @@ public class DSL {
@NotNull
@Support
public static Condition condition(Field<Boolean> field) {
return field instanceof NoField ? noCondition() : field instanceof ConditionAsField ? ((ConditionAsField) field).condition : new FieldCondition(field);
return field instanceof Condition ? (Condition) field : field instanceof NoField ? noCondition() : field instanceof ConditionAsField ? ((ConditionAsField) field).condition : new FieldCondition(field);
}
// -------------------------------------------------------------------------

View File

@ -4744,7 +4744,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri
@Override
public boolean fetchExists(Select<?> query) {
return fetchValue(field(exists(query)));
return fetchValue(exists(query));
}
@Override

View File

@ -64,6 +64,7 @@ import static org.jooq.impl.DSL.emptyGroupingSet;
import java.util.Set;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.GroupField;

View File

@ -7524,8 +7524,6 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return null;
else if (part instanceof Field)
return (Field<?>) part;
else if (part instanceof Condition)
return field((Condition) part);
else if (part instanceof Row)
return (Row) part;
else
@ -7537,8 +7535,6 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
return null;
else if (part instanceof Field)
return (Field<?>) part;
else if (part instanceof Condition)
return field((Condition) part);
else
throw expected("Field");
}
@ -8317,7 +8313,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
else if (parseFunctionNameIf("IFNULL"))
return parseFunctionArgs2((f1, f2) -> ifnull((Field<?>) f1, (Field<?>) f2));
else if (parseFunctionNameIf("ISNULL"))
return parseFunctionArgs2(f -> field(f.isNull()), (f1, f2) -> isnull((Field<?>) f1, (Field<?>) f2));
return parseFunctionArgs2(f -> f.isNull(), (f1, f2) -> isnull((Field<?>) f1, (Field<?>) f2));
else if ((field = parseFieldIfIf()) != null)
return field;
else

View File

@ -193,19 +193,19 @@ final class QuantifiedComparisonCondition extends AbstractCondition implements L
}
else if ((query.array != null || query.query != null) && emulateOperator) {
Field<String> pattern = DSL.field(name("pattern"), VARCHAR);
Condition cond;
Condition condition;
Field<Boolean> lhs;
switch (comparator) {
case NOT_LIKE:
case NOT_SIMILAR_TO:
case NOT_LIKE_IGNORE_CASE:
cond = comparisonCondition(inverse(comparator), pattern);
condition = comparisonCondition(inverse(comparator), pattern);
lhs = inline(false);
break;
case LIKE:
case SIMILAR_TO:
case LIKE_IGNORE_CASE:
cond = comparisonCondition(comparator, pattern);
condition = comparisonCondition(comparator, pattern);
lhs = inline(true);
break;
default:
@ -215,7 +215,7 @@ final class QuantifiedComparisonCondition extends AbstractCondition implements L
Table<?> t = query.array != null
? new ArrayTable(query.array).asTable("t", "pattern")
: new AliasedSelect<>(query.query, true, true, false, name("pattern")).as("t");
Select<Record1<Boolean>> select = select(DSL.field(cond)).from(t);
Select<Record1<Boolean>> select = select(condition).from(t);
ctx.visit(lhs.eq(query.quantifier.apply(select)));
}
else {
@ -296,22 +296,22 @@ final class QuantifiedComparisonCondition extends AbstractCondition implements L
private Condition comparisonCondition(Comparator operator, Object value) {
switch (operator) {
case LIKE:
return escape != null ? field.like(convert(value, String.class), escape) : field.like(convert(value, String.class));
return escape != null ? field.like(Convert.convert(value, String.class), escape) : field.like(Convert.convert(value, String.class));
case NOT_LIKE:
return escape != null ? field.notLike(convert(value, String.class), escape) : field.notLike(convert(value, String.class));
return escape != null ? field.notLike(Convert.convert(value, String.class), escape) : field.notLike(Convert.convert(value, String.class));
case SIMILAR_TO:
return escape != null ? field.similarTo(convert(value, String.class), escape) : field.similarTo(convert(value, String.class));
return escape != null ? field.similarTo(Convert.convert(value, String.class), escape) : field.similarTo(Convert.convert(value, String.class));
case NOT_SIMILAR_TO:
return escape != null ? field.notSimilarTo(convert(value, String.class), escape) : field.notSimilarTo(convert(value, String.class));
return escape != null ? field.notSimilarTo(Convert.convert(value, String.class), escape) : field.notSimilarTo(Convert.convert(value, String.class));
case LIKE_IGNORE_CASE:
return escape != null ? field.likeIgnoreCase(convert(value, String.class), escape) : field.likeIgnoreCase(convert(value, String.class));
return escape != null ? field.likeIgnoreCase(Convert.convert(value, String.class), escape) : field.likeIgnoreCase(Convert.convert(value, String.class));
case NOT_LIKE_IGNORE_CASE:
return escape != null ? field.notLikeIgnoreCase(convert(value, String.class), escape) : field.notLikeIgnoreCase(convert(value, String.class));
return escape != null ? field.notLikeIgnoreCase(Convert.convert(value, String.class), escape) : field.notLikeIgnoreCase(Convert.convert(value, String.class));
default:
return ((Field) field).compare(operator, value);

View File

@ -56,6 +56,7 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.Function1;
import org.jooq.QueryPart;
@ -240,7 +241,15 @@ implements
* Subclasses may override this method
*/
protected void acceptElement(Context<?> ctx, T part) {
ctx.visit(part);
// [#11969] As of jOOQ 3.17, *all* QueryPartCollectionView<Field<?>> are
// declared as such, there are no QueryPartCollectionView<Condition>
// declarations. Hence, it is currently safe to assume that
// if (part instanceof Condition), then it must have been passed
// as a subtype of Field<?> and must be wrapped
if (part instanceof Condition)
ctx.visit((QueryPart) DSL.field((Condition) part));
else
ctx.visit(part);
}
/**

View File

@ -38,6 +38,7 @@
package org.jooq.impl;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.SelectFieldOrAsterisk;

View File

@ -38,7 +38,6 @@
package org.jooq.impl;
import static java.util.Collections.emptyList;
import static org.jooq.SortOrder.DESC;
import static org.jooq.impl.Tools.anyMatch;

View File

@ -6751,6 +6751,8 @@ final class Tools {
static final <T> Field<T> nullSafe(Field<T> field, DataType<?> type) {
return field == null
? (Field<T>) DSL.val((T) null, type)
: field instanceof Condition
? (Field<T>) DSL.field((Condition) field)
: convertVal(field, type);
}

View File

@ -113,7 +113,7 @@ implements
.from(queryTable)
.where(row(queryFields).isNotNull())
.groupBy(queryFields)
.having(count().gt(one()));
.having(DSL.count().gt(one()));
// TODO: [#7362] [#10304] Find a better way to prevent double negation and unnecessary parentheses
ctx.visit(notExists(subquery));