This includes:
- [jOOQ/jOOQ#13640] Adding QOM.Decode
- [jOOQ/jOOQ#14155] Render correct empty Decode content
- Refactoring CaseSimple and Decode to have a common base impl
This commit is contained in:
Lukas Eder 2022-11-04 14:25:39 +01:00
parent b0de1c595c
commit eadd4e885d
4 changed files with 321 additions and 127 deletions

View File

@ -0,0 +1,189 @@
/*
* 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
*
* https://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: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;
import static org.jooq.impl.Names.NQ_CASE;
import static org.jooq.impl.QOM.tuple;
import java.util.List;
import java.util.Map;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Function3;
import org.jooq.Name;
import org.jooq.impl.QOM.Tuple2;
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
abstract class AbstractCaseSimple<V, T, CS extends AbstractCaseSimple<V, T, CS>>
extends
AbstractField<T>
{
final Field<V> value;
final List<Tuple2<Field<V>, Field<T>>> when;
Field<T> else_;
AbstractCaseSimple(Name name, Field<V> value, Field<V> compareValue, Field<T> result) {
this(name, value, result.getDataType());
when(compareValue, result);
}
AbstractCaseSimple(Name name, Field<V> value, Map<? extends Field<V>, ? extends Field<T>> map) {
this(name, value, dataType(map));
mapFields(map);
}
AbstractCaseSimple(Name name, Field<V> value, DataType<T> type) {
super(name, type);
this.value = value;
this.when = new QueryPartList<>();
}
@SuppressWarnings("unchecked")
private static final <T> DataType<T> dataType(Map<? extends Field<?>, ? extends Field<T>> map) {
if (map.isEmpty())
return (DataType<T>) SQLDataType.OTHER;
else
return map.entrySet().iterator().next().getValue().getDataType();
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
// @Override
public final CS when(V compareValue, T result) {
return when(Tools.field(compareValue, value), Tools.field(result));
}
// @Override
public final CS when(V compareValue, Field<T> result) {
return when(Tools.field(compareValue, value), result);
}
// @Override
public final CS when(Field<V> compareValue, T result) {
return when(compareValue, Tools.field(result));
}
// @Override
public final CS when(Field<V> compareValue, Field<T> result) {
when.add(tuple(compareValue, result));
return (CS) this;
}
// @Override
@SuppressWarnings("unchecked")
public final CS mapValues(Map<V, T> values) {
values.forEach((k, v) -> when(k, v));
return (CS) this;
}
// @Override
@SuppressWarnings("unchecked")
public final CS mapFields(Map<? extends Field<V>, ? extends Field<T>> fields) {
fields.forEach((k, v) -> when(k, v));
return (CS) this;
}
// @Override
public final Field<T> else_(T result) {
return else_(Tools.field(result));
}
// @Override
public final Field<T> else_(Field<T> result) {
this.else_ = result;
return this;
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
abstract CS construct(Field<V> v, DataType<T> t);
// @Override
public final Function3<? super Field<V>, ? super UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>>, ? super Field<T>, ? extends CS> $constructor() {
return (v, w, e) -> {
CS r = construct(v, getDataType());
w.forEach(t -> r.when(t.$1(), t.$2()));
r.else_(e);
return r;
};
}
// @Override
public final Field<V> $arg1() {
return value;
}
// @Override
public final CS $arg1(Field<V> newArg1) {
return $constructor().apply(newArg1, $arg2(), $arg3());
}
// @Override
public final UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> $arg2() {
return QOM.unmodifiable(when);
}
// @Override
public final CS $arg2(UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> w) {
return $constructor().apply($arg1(), w, $arg3());
}
// @Override
public final Field<T> $arg3() {
return else_;
}
// @Override
public final CS $arg3(Field<T> e) {
return $constructor().apply($arg1(), $arg2(), e);
}
}

View File

@ -48,10 +48,8 @@ import static org.jooq.impl.Keywords.K_THEN;
import static org.jooq.impl.Keywords.K_TRUE;
import static org.jooq.impl.Keywords.K_WHEN;
import static org.jooq.impl.Names.NQ_CASE;
import static org.jooq.impl.QOM.tuple;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_CASE_ELSE_NULL;
import java.util.List;
import java.util.Map;
import org.jooq.CaseConditionStep;
@ -59,51 +57,33 @@ import org.jooq.CaseWhenStep;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Function3;
// ...
import org.jooq.QueryPart;
// ...
// ...
import org.jooq.impl.QOM.Tuple2;
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
final class CaseSimple<V, T>
extends
AbstractField<T>
AbstractCaseSimple<V, T, CaseSimple<V, T>>
implements
CaseWhenStep<V, T>,
QOM.CaseSimple<V, T>
{
private final Field<V> value;
private final List<Tuple2<Field<V>, Field<T>>> when;
private Field<T> else_;
CaseSimple(Field<V> value, Field<V> compareValue, Field<T> result) {
this(value, result.getDataType());
when(compareValue, result);
super(NQ_CASE, value, compareValue, result);
}
CaseSimple(Field<V> value, Map<? extends Field<V>, ? extends Field<T>> map) {
this(value, dataType(map));
mapFields(map);
super(NQ_CASE, value, map);
}
CaseSimple(Field<V> value, DataType<T> type) {
super(NQ_CASE, type);
this.value = value;
this.when = new QueryPartList<>();
}
@SuppressWarnings("unchecked")
private static final <T> DataType<T> dataType(Map<? extends Field<?>, ? extends Field<T>> map) {
if (map.isEmpty())
return (DataType<T>) SQLDataType.OTHER;
else
return map.entrySet().iterator().next().getValue().getDataType();
super(NQ_CASE, value, type);
}
// -------------------------------------------------------------------------
@ -120,52 +100,6 @@ implements
return else_(result);
}
@Override
public final Field<T> else_(T result) {
return else_(Tools.field(result));
}
@Override
public final Field<T> else_(Field<T> result) {
this.else_ = result;
return this;
}
@Override
public final CaseWhenStep<V, T> when(V compareValue, T result) {
return when(Tools.field(compareValue, value), Tools.field(result));
}
@Override
public final CaseWhenStep<V, T> when(V compareValue, Field<T> result) {
return when(Tools.field(compareValue, value), result);
}
@Override
public final CaseWhenStep<V, T> when(Field<V> compareValue, T result) {
return when(compareValue, Tools.field(result));
}
@Override
public final CaseWhenStep<V, T> when(Field<V> compareValue, Field<T> result) {
when.add(tuple(compareValue, result));
return this;
}
@Override
public final CaseWhenStep<V, T> mapValues(Map<V, T> values) {
values.forEach((k, v) -> when(k, v));
return this;
}
@Override
public final CaseWhenStep<V, T> mapFields(Map<? extends Field<V>, ? extends Field<T>> fields) {
fields.forEach((k, v) -> when(k, v));
return this;
}
@Override
public final void accept(Context<?> ctx) {
if (when.isEmpty()) {
@ -290,42 +224,21 @@ implements
// -------------------------------------------------------------------------
@Override
public final Function3<? super Field<V>, ? super UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>>, ? super Field<T>, ? extends CaseSimple<V, T>> $constructor() {
return (v, w, e) -> {
CaseSimple<V, T> r = new CaseSimple<>(v, getDataType());
w.forEach(t -> r.when(t.$1(), t.$2()));
r.else_(e);
return r;
};
final CaseSimple<V, T> construct(Field<V> v, DataType<T> t) {
return new CaseSimple<>(v, t);
}
@Override
public final Field<V> $arg1() {
return value;
}
@Override
public final CaseSimple<V, T> $arg1(Field<V> newArg1) {
return $constructor().apply(newArg1, $when(), $else());
}
@Override
public final UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> $arg2() {
return QOM.unmodifiable(when);
}
@Override
public final CaseSimple<V, T> $arg2(UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> w) {
return $constructor().apply($value(), w, $else());
}
@Override
public final Field<T> $arg3() {
return else_;
}
@Override
public final CaseSimple<V, T> $arg3(Field<T> e) {
return $constructor().apply($value(), $when(), e);
}
}

View File

@ -37,58 +37,99 @@
*/
package org.jooq.impl;
import static org.jooq.SQLDialect.*;
// ...
// ...
// ...
// ...
// ...
// ...
import static org.jooq.SQLDialect.CUBRID;
import static org.jooq.SQLDialect.DERBY;
import static org.jooq.SQLDialect.FIREBIRD;
// ...
import static org.jooq.SQLDialect.HSQLDB;
// ...
// ...
import static org.jooq.SQLDialect.MARIADB;
// ...
import static org.jooq.SQLDialect.MYSQL;
import static org.jooq.SQLDialect.POSTGRES;
// ...
// ...
// ...
import static org.jooq.SQLDialect.SQLITE;
// ...
// ...
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.Names.N_DECODE;
import static org.jooq.impl.Names.N_MAP;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.jooq.CaseConditionStep;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
// ...
import org.jooq.QueryPart;
// ...
import org.jooq.SQLDialect;
import org.jooq.impl.QOM.UNotYetImplemented;
// ...
/**
* @author Lukas Eder
*/
final class Decode<T, Z> extends AbstractField<Z> implements UNotYetImplemented {
final class Decode<V, T>
extends
AbstractCaseSimple<V, T, Decode<V, T>>
implements
QOM.Decode<V, T>
{
private static final Set<SQLDialect> EMULATE_DISTINCT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE, YUGABYTEDB);
private final Field<T> field;
private final Field<T> search;
private final Field<Z> result;
private final Field<?>[] more;
@SuppressWarnings("unchecked")
Decode(Field<V> field, Field<V> search, Field<T> result, Field<?>[] more) {
super(N_DECODE, field, result.getDataType());
public Decode(Field<T> field, Field<T> search, Field<Z> result, Field<?>[] more) {
super(N_DECODE, result.getDataType());
when(search, result);
if (more.length > 1)
for (int i = 0; i + 1 < more.length; i += 2)
when((Field<V>) more[i], (Field<T>) more[i + 1]);
this.field = field;
this.search = search;
this.result = result;
this.more = more;
if (more.length % 2 != 0)
else_((Field<T>) more[more.length - 1]);
}
@SuppressWarnings("unchecked")
Decode(Field<V> field, DataType<T> type) {
super(N_DECODE, field, type);
}
// -------------------------------------------------------------------------
// XXX: QueryPart API
// -------------------------------------------------------------------------
@Override
public final void accept(Context<?> ctx) {
if (EMULATE_DISTINCT.contains(ctx.dialect())) {
ctx.visit(Tools.derivedTableIf(ctx, more.length > 1, field, f -> {
CaseConditionStep<Z> when = DSL.choose().when(f.isNotDistinctFrom(search), result);
ctx.visit(Tools.derivedTableIf(ctx, !when.isEmpty(), value, f -> {
CaseSearched<T> c = new CaseSearched<>(getDataType());
for (int i = 0; i + 1 < more.length; i += 2)
when = when.when(f.isNotDistinctFrom((Field<T>) more[i]), (Field<Z>) more[i + 1]);
// .when(f.isNotDistinctFrom(search), result)
when.forEach(t -> {
c.when(f.isNotDistinctFrom(t.$1()), t.$2());
});
if (more.length % 2 == 0)
return when;
if (else_ == null)
return c;
else
return when.otherwise((Field<Z>) more[more.length - 1]);
return c.else_(else_);
}));
}
@ -98,6 +139,44 @@ final class Decode<T, Z> extends AbstractField<Z> implements UNotYetImplemented
else
ctx.visit(function(N_DECODE, getDataType(), Tools.combine(field, search, result, more)));
ctx.visit(function(N_DECODE, getDataType(), args()));
}
final Field<?>[] args() {
List<Field<?>> result = new ArrayList<>();
result.add(value);
when.forEach(t -> {
result.add(t.$1());
result.add(t.$2());
});
if (else_ != null)
result.add(else_);
return result.toArray(EMPTY_FIELD);
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------
@Override
final Decode<V, T> construct(Field<V> v, DataType<T> t) {
return new Decode<>(v, t);
}
}

View File

@ -1242,6 +1242,19 @@ public final class QOM {
@NotNull default CaseSearched<T> $else(Field<T> else_) { return $arg2(else_); }
}
public /*sealed*/ interface Decode<V, T>
extends
Field<T>,
UOperator3<Field<V>, UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>>, Field<T>, Decode<V, T>>
{
@NotNull default Field<V> $value() { return $arg1(); }
@NotNull default Decode<V, T> $value(Field<V> value) { return $arg1(value); }
@NotNull default UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> $when() { return $arg2(); }
@NotNull default Decode<V, T> $when(UnmodifiableList<? extends Tuple2<Field<V>, Field<T>>> when) { return $arg2(when); }
@Nullable default Field<T> $else() { return $arg3(); }
@NotNull default Decode<V, T> $else(Field<T> else_) { return $arg3(else_); }
}
public /*sealed*/ interface Concat
extends
Field<String>,