From eadd4e885d52689b98cc4472d1e4da152648fd68 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Fri, 4 Nov 2022 14:25:39 +0100 Subject: [PATCH] [jOOQ/jOOQ#13640] [jOOQ/jOOQ#14155] Add QOM.Decode 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 --- .../org/jooq/impl/AbstractCaseSimple.java | 189 ++++++++++++++++++ .../main/java/org/jooq/impl/CaseSimple.java | 121 ++--------- jOOQ/src/main/java/org/jooq/impl/Decode.java | 125 +++++++++--- jOOQ/src/main/java/org/jooq/impl/QOM.java | 13 ++ 4 files changed, 321 insertions(+), 127 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/impl/AbstractCaseSimple.java diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractCaseSimple.java b/jOOQ/src/main/java/org/jooq/impl/AbstractCaseSimple.java new file mode 100644 index 0000000000..9f2aed6dfa --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractCaseSimple.java @@ -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> +extends + AbstractField +{ + + final Field value; + final List, Field>> when; + Field else_; + + AbstractCaseSimple(Name name, Field value, Field compareValue, Field result) { + this(name, value, result.getDataType()); + + when(compareValue, result); + } + + AbstractCaseSimple(Name name, Field value, Map, ? extends Field> map) { + this(name, value, dataType(map)); + + mapFields(map); + } + + AbstractCaseSimple(Name name, Field value, DataType type) { + super(name, type); + + this.value = value; + this.when = new QueryPartList<>(); + } + + @SuppressWarnings("unchecked") + private static final DataType dataType(Map, ? extends Field> map) { + if (map.isEmpty()) + return (DataType) 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 result) { + return when(Tools.field(compareValue, value), result); + } + + // @Override + public final CS when(Field compareValue, T result) { + return when(compareValue, Tools.field(result)); + } + + // @Override + public final CS when(Field compareValue, Field result) { + when.add(tuple(compareValue, result)); + + return (CS) this; + } + + // @Override + @SuppressWarnings("unchecked") + public final CS mapValues(Map values) { + values.forEach((k, v) -> when(k, v)); + return (CS) this; + } + + // @Override + @SuppressWarnings("unchecked") + public final CS mapFields(Map, ? extends Field> fields) { + fields.forEach((k, v) -> when(k, v)); + return (CS) this; + } + + // @Override + public final Field else_(T result) { + return else_(Tools.field(result)); + } + + // @Override + public final Field else_(Field result) { + this.else_ = result; + + return this; + } + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + abstract CS construct(Field v, DataType t); + + // @Override + public final Function3, ? super UnmodifiableList, Field>>, ? super Field, ? 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 $arg1() { + return value; + } + + // @Override + public final CS $arg1(Field newArg1) { + return $constructor().apply(newArg1, $arg2(), $arg3()); + } + + // @Override + public final UnmodifiableList, Field>> $arg2() { + return QOM.unmodifiable(when); + } + + // @Override + public final CS $arg2(UnmodifiableList, Field>> w) { + return $constructor().apply($arg1(), w, $arg3()); + } + + // @Override + public final Field $arg3() { + return else_; + } + + // @Override + public final CS $arg3(Field e) { + return $constructor().apply($arg1(), $arg2(), e); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/CaseSimple.java b/jOOQ/src/main/java/org/jooq/impl/CaseSimple.java index edd995f384..f1d8df8396 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CaseSimple.java +++ b/jOOQ/src/main/java/org/jooq/impl/CaseSimple.java @@ -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 extends - AbstractField + AbstractCaseSimple> implements CaseWhenStep, QOM.CaseSimple { - private final Field value; - private final List, Field>> when; - private Field else_; - CaseSimple(Field value, Field compareValue, Field result) { - this(value, result.getDataType()); - - when(compareValue, result); + super(NQ_CASE, value, compareValue, result); } CaseSimple(Field value, Map, ? extends Field> map) { - this(value, dataType(map)); - - mapFields(map); + super(NQ_CASE, value, map); } CaseSimple(Field value, DataType type) { - super(NQ_CASE, type); - - this.value = value; - this.when = new QueryPartList<>(); - } - - @SuppressWarnings("unchecked") - private static final DataType dataType(Map, ? extends Field> map) { - if (map.isEmpty()) - return (DataType) 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 else_(T result) { - return else_(Tools.field(result)); - } - - @Override - public final Field else_(Field result) { - this.else_ = result; - - return this; - } - - @Override - public final CaseWhenStep when(V compareValue, T result) { - return when(Tools.field(compareValue, value), Tools.field(result)); - } - - @Override - public final CaseWhenStep when(V compareValue, Field result) { - return when(Tools.field(compareValue, value), result); - } - - @Override - public final CaseWhenStep when(Field compareValue, T result) { - return when(compareValue, Tools.field(result)); - } - - @Override - public final CaseWhenStep when(Field compareValue, Field result) { - when.add(tuple(compareValue, result)); - - return this; - } - - @Override - public final CaseWhenStep mapValues(Map values) { - values.forEach((k, v) -> when(k, v)); - return this; - } - - @Override - public final CaseWhenStep mapFields(Map, ? extends Field> 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 UnmodifiableList, Field>>, ? super Field, ? extends CaseSimple> $constructor() { - return (v, w, e) -> { - CaseSimple r = new CaseSimple<>(v, getDataType()); - w.forEach(t -> r.when(t.$1(), t.$2())); - r.else_(e); - return r; - }; + final CaseSimple construct(Field v, DataType t) { + return new CaseSimple<>(v, t); } - @Override - public final Field $arg1() { - return value; - } - @Override - public final CaseSimple $arg1(Field newArg1) { - return $constructor().apply(newArg1, $when(), $else()); - } - @Override - public final UnmodifiableList, Field>> $arg2() { - return QOM.unmodifiable(when); - } - @Override - public final CaseSimple $arg2(UnmodifiableList, Field>> w) { - return $constructor().apply($value(), w, $else()); - } - @Override - public final Field $arg3() { - return else_; - } - @Override - public final CaseSimple $arg3(Field e) { - return $constructor().apply($value(), $when(), e); - } + + + + + + + + } diff --git a/jOOQ/src/main/java/org/jooq/impl/Decode.java b/jOOQ/src/main/java/org/jooq/impl/Decode.java index b109775344..f2b522c75d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Decode.java +++ b/jOOQ/src/main/java/org/jooq/impl/Decode.java @@ -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 extends AbstractField implements UNotYetImplemented { +final class Decode +extends + AbstractCaseSimple> +implements + QOM.Decode +{ private static final Set EMULATE_DISTINCT = SQLDialect.supportedBy(CUBRID, DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE, YUGABYTEDB); - private final Field field; - private final Field search; - private final Field result; - private final Field[] more; + @SuppressWarnings("unchecked") + Decode(Field field, Field search, Field result, Field[] more) { + super(N_DECODE, field, result.getDataType()); - public Decode(Field field, Field search, Field 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) more[i], (Field) more[i + 1]); - this.field = field; - this.search = search; - this.result = result; - this.more = more; + if (more.length % 2 != 0) + else_((Field) more[more.length - 1]); } - @SuppressWarnings("unchecked") + Decode(Field field, DataType 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 when = DSL.choose().when(f.isNotDistinctFrom(search), result); + ctx.visit(Tools.derivedTableIf(ctx, !when.isEmpty(), value, f -> { + CaseSearched c = new CaseSearched<>(getDataType()); - for (int i = 0; i + 1 < more.length; i += 2) - when = when.when(f.isNotDistinctFrom((Field) more[i]), (Field) 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) more[more.length - 1]); + return c.else_(else_); })); } @@ -98,6 +139,44 @@ final class Decode extends AbstractField 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> 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 construct(Field v, DataType t) { + return new Decode<>(v, t); + } + + + + + + + + + + + + + + } diff --git a/jOOQ/src/main/java/org/jooq/impl/QOM.java b/jOOQ/src/main/java/org/jooq/impl/QOM.java index 4015d7bece..a4d0e5bc4d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QOM.java +++ b/jOOQ/src/main/java/org/jooq/impl/QOM.java @@ -1242,6 +1242,19 @@ public final class QOM { @NotNull default CaseSearched $else(Field else_) { return $arg2(else_); } } + public /*sealed*/ interface Decode + extends + Field, + UOperator3, UnmodifiableList, Field>>, Field, Decode> + { + @NotNull default Field $value() { return $arg1(); } + @NotNull default Decode $value(Field value) { return $arg1(value); } + @NotNull default UnmodifiableList, Field>> $when() { return $arg2(); } + @NotNull default Decode $when(UnmodifiableList, Field>> when) { return $arg2(when); } + @Nullable default Field $else() { return $arg3(); } + @NotNull default Decode $else(Field else_) { return $arg3(else_); } + } + public /*sealed*/ interface Concat extends Field,