From 89b9c5a46422850ffd3900db9fa9651c515bd112 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 25 Mar 2024 10:58:25 +0100 Subject: [PATCH] [jOOQ/jOOQ#16499] Prototype API and implement ARRAY_FILTER --- jOOQ/src/main/java/org/jooq/Lambda1.java | 59 +++++ .../main/java/org/jooq/impl/ArrayFilter.java | 203 ++++++++++++++++++ jOOQ/src/main/java/org/jooq/impl/DSL.java | 70 ++++++ .../main/java/org/jooq/impl/LambdaImpl1.java | 109 ++++++++++ jOOQ/src/main/java/org/jooq/impl/QOM.java | 64 ++++++ 5 files changed, 505 insertions(+) create mode 100644 jOOQ/src/main/java/org/jooq/Lambda1.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/ArrayFilter.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/LambdaImpl1.java diff --git a/jOOQ/src/main/java/org/jooq/Lambda1.java b/jOOQ/src/main/java/org/jooq/Lambda1.java new file mode 100644 index 0000000000..3e11a2590e --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Lambda1.java @@ -0,0 +1,59 @@ +/* + * 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; + +import org.jetbrains.annotations.NotNull; + +/** + * A lambda expression of degree 1. + * + * @author Lukas Eder + */ +public interface Lambda1 extends QueryPart { + + /** + * The first argument of the lambda. + */ + @NotNull Field $arg1(); + + /** + * The lambda result. + */ + @NotNull Field $result(); + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayFilter.java b/jOOQ/src/main/java/org/jooq/impl/ArrayFilter.java new file mode 100644 index 0000000000..467935e174 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayFilter.java @@ -0,0 +1,203 @@ +/* + * 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.DSL.*; +import static org.jooq.impl.Internal.*; +import static org.jooq.impl.Keywords.*; +import static org.jooq.impl.Names.*; +import static org.jooq.impl.SQLDataType.*; +import static org.jooq.impl.Tools.*; +import static org.jooq.impl.Tools.BooleanDataKey.*; +import static org.jooq.impl.Tools.ExtendedDataKey.*; +import static org.jooq.impl.Tools.SimpleDataKey.*; +import static org.jooq.SQLDialect.*; + +import org.jooq.*; +import org.jooq.Function1; +import org.jooq.Record; +import org.jooq.conf.*; +import org.jooq.impl.*; +import org.jooq.impl.QOM.*; +import org.jooq.tools.*; + +import java.util.*; +import java.util.function.*; +import java.util.stream.*; + + +/** + * The ARRAY FILTER statement. + */ +@SuppressWarnings({ "rawtypes", "unchecked", "unused" }) +final class ArrayFilter +extends + AbstractField +implements + QOM.ArrayFilter +{ + + final Field array; + final Lambda1 filter; + + ArrayFilter( + Field array, + Lambda1 filter + ) { + super( + N_ARRAY_FILTER, + allNotNull((DataType) dataType(((DataType) OTHER).array(), array, false), array) + ); + + this.array = nullSafeNotNull(array, ((DataType) OTHER).array()); + this.filter = filter; + } + + // ------------------------------------------------------------------------- + // XXX: QueryPart API + // ------------------------------------------------------------------------- + + @Override + final boolean parenthesised(Context ctx) { + switch (ctx.family()) { + + + case H2: + case HSQLDB: + case POSTGRES: + case YUGABYTEDB: + return false; + + case DUCKDB: + return true; + + default: + return true; + } + } + + @Override + public final void accept(Context ctx) { + switch (ctx.family()) { + + + + + + + + + case H2: + case HSQLDB: + case POSTGRES: + case YUGABYTEDB: + ctx.visit(DSL.field( + select(DSL.coalesce(arrayAgg(filter.$arg1()), when(array.isNotNull(), DSL.cast(array(), getDataType())))) + .from(unnest(array).as(N_T, filter.$arg1().getUnqualifiedName())) + .where(filter.$result()) + )); + break; + + case DUCKDB: + ctx.visit(function(N_ARRAY_FILTER, getDataType(), array, DSL.field("{0}", OTHER, filter))); + break; + + default: + ctx.visit(function(N_ARRAY_FILTER, getDataType(), array, DSL.field("{0}", OTHER, filter))); + break; + } + } + + + + + + + + + + + + + + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + @Override + public final Field $arg1() { + return array; + } + + @Override + public final Lambda1 $arg2() { + return filter; + } + + @Override + public final QOM.ArrayFilter $arg1(Field newValue) { + return $constructor().apply(newValue, $arg2()); + } + + @Override + public final QOM.ArrayFilter $arg2(Lambda1 newValue) { + return $constructor().apply($arg1(), newValue); + } + + @Override + public final Function2, ? super Lambda1, ? extends QOM.ArrayFilter> $constructor() { + return (a1, a2) -> new ArrayFilter<>(a1, a2); + } + + // ------------------------------------------------------------------------- + // XXX: The Object API + // ------------------------------------------------------------------------- + + @Override + public boolean equals(Object that) { + if (that instanceof QOM.ArrayFilter o) { + return + StringUtils.equals($array(), o.$array()) && + StringUtils.equals($filter(), o.$filter()) + ; + } + else + return super.equals(that); + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 3ace9a109b..002ba605d5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -110,6 +110,7 @@ import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.Names.N_COUNT; import static org.jooq.impl.Names.N_CUME_DIST; import static org.jooq.impl.Names.N_DENSE_RANK; +import static org.jooq.impl.Names.N_E; import static org.jooq.impl.Names.N_IF; import static org.jooq.impl.Names.N_IIF; import static org.jooq.impl.Names.N_PERCENTILE_CONT; @@ -223,6 +224,7 @@ import org.jooq.False; import org.jooq.Field; import org.jooq.FieldOrRow; // ... +import org.jooq.Function1; import org.jooq.Geography; import org.jooq.Geometry; import org.jooq.GroupConcatOrderByStep; @@ -269,6 +271,7 @@ import org.jooq.JSONTableColumnsFirstStep; import org.jooq.JSONValueOnStep; import org.jooq.Keyword; // ... +import org.jooq.Lambda1; // ... // ... import org.jooq.Merge; @@ -14029,6 +14032,29 @@ public class DSL { + + // ------------------------------------------------------------------------- + // XXX: Lambda constructors + // ------------------------------------------------------------------------- + + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @NotNull + static Lambda1 lambda(T1[] field, org.jooq.Function1, ? extends Field> function) { + return lambda(Tools.field(field), function); + } + + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @NotNull + static Lambda1 lambda(Field field, org.jooq.Function1, ? extends Field> function) { + Field e = (Field) DSL.field(N_E, field.getDataType().getArrayBaseDataType()); + return lambda(e, function.apply(e)); + } + + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @NotNull + public static Lambda1 lambda(Field arg1, Field result) { + return new org.jooq.impl.LambdaImpl1<>(arg1, result); + } // ------------------------------------------------------------------------- // XXX: Routine parameter constructors @@ -22394,6 +22420,50 @@ public class DSL { return new ArrayReplace<>(arg1, arg2, arg3); } + /** + * The ARRAY_FILTER function. + *

+ * Filter elements out of an array. + */ + @NotNull + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + public static Field arrayFilter(T[] array, Function1, ? extends Field> filter) { + return new ArrayFilter<>(Tools.field(array), DSL.lambda(array, filter)); + } + + /** + * The ARRAY_FILTER function. + *

+ * Filter elements out of an array. + */ + @NotNull + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + public static Field arrayFilter(T[] array, Lambda1 filter) { + return new ArrayFilter<>(Tools.field(array), filter); + } + + /** + * The ARRAY_FILTER function. + *

+ * Filter elements out of an array. + */ + @NotNull + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + public static Field arrayFilter(Field array, Function1, ? extends Field> filter) { + return new ArrayFilter<>(array, DSL.lambda(array, filter)); + } + + /** + * The ARRAY_FILTER function. + *

+ * Filter elements out of an array. + */ + @NotNull + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + public static Field arrayFilter(Field array, Lambda1 filter) { + return new ArrayFilter<>(array, filter); + } + // ------------------------------------------------------------------------- // Utility functions // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/LambdaImpl1.java b/jOOQ/src/main/java/org/jooq/impl/LambdaImpl1.java new file mode 100644 index 0000000000..5c61bd1c50 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/LambdaImpl1.java @@ -0,0 +1,109 @@ +/* + * 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.N_E; + +import org.jooq.Context; +import org.jooq.Field; +import org.jooq.Function1; +import org.jooq.Lambda1; +import org.jooq.QueryPart; +// ... +// ... + +/** + * @author Lukas Eder + */ +final class LambdaImpl1 +extends + AbstractQueryPart +implements + Lambda1 +{ + + final Field arg1; + final Field result; + + LambdaImpl1(Field arg1, Field result) { + this.arg1 = arg1; + this.result = result; + } + + // ------------------------------------------------------------------------- + // XXX: QueryPart API + // ------------------------------------------------------------------------- + + @Override + public final void accept(Context ctx) { + ctx.visit(arg1).sql(" -> ").visit(result); + } + + // ------------------------------------------------------------------------- + // XXX: Query Object Model + // ------------------------------------------------------------------------- + + @Override + public final Field $arg1() { + return arg1; + } + + @Override + public final Field $result() { + return result; + } + + + + + + + + + + + + + + + + // ------------------------------------------------------------------------- + // XXX: Object API + // ------------------------------------------------------------------------- + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/QOM.java b/jOOQ/src/main/java/org/jooq/impl/QOM.java index ecd623cfe9..0c8c949949 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QOM.java +++ b/jOOQ/src/main/java/org/jooq/impl/QOM.java @@ -103,6 +103,7 @@ import org.jooq.JSON; import org.jooq.JSONB; import org.jooq.JSONEntry; import org.jooq.Keyword; +import org.jooq.Lambda1; // ... import org.jooq.Name; import org.jooq.Operator; @@ -228,6 +229,22 @@ public final class QOM { // XXX: Model // ------------------------------------------------------------------------- +// public interface Lambda1 +// extends +// org.jooq.QueryPart +// { +// +// /** +// * The first argument of the lambda. +// */ +// @NotNull Field $arg1(); +// +// /** +// * The lambda result. +// */ +// @NotNull Field $result(); +// } + // This class uses a lot of fully qualified types, because of some javac bug // In Java 1.8.0_302, which hasn't been analysed and reproduced yet in a more // minimal example. Without the qualification, the types cannot be found @@ -11757,6 +11774,53 @@ public final class QOM { // ArrayReplace {} + /** + * The ARRAY FILTER function. + *

+ * Filter elements out of an array. + */ + public static final ArrayFilter ArrayFilter() { + return new org.jooq.impl.ArrayFilter<>( + null, + null + ); + } + + /** + * The ARRAY FILTER function. + *

+ * Filter elements out of an array. + */ + public static final ArrayFilter ArrayFilter( + Field array, + Lambda1 filter + ) { + return new org.jooq.impl.ArrayFilter<>( + array, + filter + ); + } + + /** + * The ARRAY FILTER function. + *

+ * Filter elements out of an array. + */ + public /*sealed*/ interface ArrayFilter + extends + UOperator2, Lambda1, ArrayFilter>, + org.jooq.Field + //permits + // ArrayFilter + { + @NotNull default Field $array() { return $arg1(); } + @CheckReturnValue + @NotNull default ArrayFilter $array(Field newArray) { return $arg1(newArray); } + @NotNull default Lambda1 $filter() { return $arg2(); } + @CheckReturnValue + @NotNull default ArrayFilter $filter(Lambda1 newFilter) { return $arg2(newFilter); } + } + /** * The NVL function. *